milkcocoa0902 / ai Goto Github PK
View Code? Open in Web Editor NEWRobocupの戦略システム
Robocupの戦略システム
Boost.Testを導入し、各種driverがしっかりと動作をするかどうか確認する
ssl-visionから受け取ったデータをもとにフィールド情報を更新するためのクラスが必要です
Serial通信を行うclassを作成する
ロボットを目標地点まで到達させるための経路生成機構が必要です.
RRT*を用いた経路生成機能を実装してください.
チームの情報を格納するclassを作成する
格納する情報
Visionから受信したデータなどを加工する部分(Filter)のベースクラスが必要です.
SSL-Visionは、時折ボールを見失ったり、そもそも誤差を含んでいたりします。
それを解決するために状態オブザーバを実装してください。
参考資料に示すような状態変数を持つボールの状態オブザーバを実装してください。
// (1)
class ball : public base<model::ball, timing::OnUpdated> {
public:
// (2)
using base<model::ball, timing::manual>::base;
// (3)
model::ball update(
const model::ball& _ball,
std::chrono::high_resolution_clock::time_point _time) override;
};
filter::base<model::ball, filter::timing::OnUpdated>
から派生したものとしてください.
必要に応じて実装してください.
必要な値がなければ ↑↑のようにすれば良いです
update
メンバ関数const model::ball& _ball
: Visionから観測された値std::chrono::high_resolution_clock::time_point _time
: Visionがフレームをキャプチャした時刻src
└── ai
└── filter
└── observer
├── ball.cpp
└── ball.hpp
ai::filter::observer:
名前空間にball
として実装してください。Multicastを行うclassを作成する
ssl-visionからしたデータをもとにballの情報を更新するためのクラスが必要です
#63 でコントローラを作成したが、位置制御に向いていないのか、思ったほどの速度を出すことができない(30[mm/s]程度)。
そこで、新たなコントローラの実装を行う
refereeBoxから送られてくる情報を格納するclassを作成してください
仕様
class refbox {
public:
// (1)
enum class stageName { };
// (2)
enum class gameCommand { };
// (3)
refbox();
// (4)
util::timePointType packetTmestamp() const;
uint32_t stageTmeLeft() const;
stageName stage() const;
gameCommand command() const;
teamInfo teamYellow() const;
teamInfo teamBlue() const;
std::tuple<double, double> ballPlacementPosition() const;
// (5)
void packetTmestamp(util::timePointType _timePoint);
void stageTmeLeft(uint32_t _timeLeft);
void stage(stageName _name);
void command(gameCommand _command);
void teamYellow(const teamInfo& _info);
void teamBlue(const teamInfo& _info);
void ballPlacementPosition(std::tuple<double, double> _position);
private:
util::timePointType packetTmestamp_;
uint32_t stageTmeLeft_;
stageName stage_;
gameCommand command_;
teamInfo teamYellow_;
teamInfo teamBlue_;
std::tuple<double, double> ballPlacementPosition_;
};
ゲームの状態を表す列挙隊を作成してください。ゲーム名はssl-protos/refbox/referee.pb.h
の中に定義されているのでそれを参照してください
ゲームコマンドを表す列挙体を作成してください。コマンド名はl-protos/refbox/referee.pb.h
の中に定義されているのでそれを参照してください
メンバを初期化するためのコンストラクタを設定してください。
各メンバの値を取得するためのgetterを設定してください。
各メンバに値を格納するためのsetterを設定してください。
ai-
├── src
│ └── ai
│ └── model
│ ├── refbox.cpp
│ └── refbox.hpp
└── test
└── model
└── refbox.cpp
クラスはai::model
名前空間にrefbox
として実装してください
ロボットと戦略システムを通信させる上での手段(パケットフォーマット, プロトコル, 機材.....)に関して議論する.
みたいな、こんなのどう?みたいなことあげてって議論する感じの
指定した位置にボールを蹴るためのactionを実装したい.
最低限の仕様は以下の通り
class kick : public base{
public:
using base::base; // (1)
void kickTo(double _x, double _y); // (2)
model::command execute(); /// (3)
private:
// (4)
double x_;
double y_;
};
特に必要でない場合は指定のものを使用してください.action::base::base()
で受け取っている以外のパラメータが必要な場合は適宜実装してください.
kickTo()
ボールを蹴る位置を指定するための関数です.
execute()
実際に処理を行うための関数です.
double x_;
: ボールの目標位置のx座標
double y_;
: ボールの目標位置のy座標
src
└── ai
└── game
└── action
├── kick.cpp
└── kick.hpp
ai::game::action
名前空間に実装してくださいfinished_
に true
をセットするようにしてくださいgrSim上のロボットへ命令を送るためのクラスが必要です.
クラスは少なくとも以下に示すメンバ関数, データメンバを持つ.
// (1)
class grsim final : public base {
public:
// (2)
grsim(boost::asio::io_service& _ioService, const std::string& _grsimAddr, uint16_t _port);
// (3)
void sendCommand(const model::command& _command) override;
private:
// (4)
util::multicast::sender udpSender_;
};
grsim
クラスクラスは #62 のsender::base
から派生したものとする. また, このクラスが他のクラスから継承されないようにfinal
指定子を付けておく.
boost::asio::io_service& _ioService
udpSender_
のコンストラクタで必要になる<boost/asio.hpp>
をincludeすると使えるconst std::string& _grsimAddr
udpSender_
のコンストラクタで必要になるuint16_t _port
udpSender_
のコンストラクタで必要になるmodel::command
を送信するメンバ関数引数で受け取ったcommand
を"ssl-protos/grsim/packet.pb.h"
をincludeすると使えるようになるssl_protos::grsim::Packet
クラスでシリアライズし, udpSender_
を使って送信する.
送信は以下のようなコードで行うことができる.
ssl_protos::grsim::Packet packet{};
// ここでpacketにデータをセットする
// 送信バッファと, それに書き込むstreamクラスのオブジェクトを作成
boost::asio::streambuf buf;
std::ostream os(&buf);
// packetをシリアライズし, 結果をos経由でbufに書き込む
packet.SerializeToOstream(&os);
// bufを送信
udpSender_.send(buf.data());
コンストラクタの初期化リストで_ioService
と_grsimAddr
, _port
を渡して初期化してください.
ai
├── src
│ └── ai
│ └── sender
│ ├── grsim.cpp
│ └── grsim.hpp
└── test
└── sender
└── grsim.cpp
ai::sender
名前空間内にgrsim
で実装してください0
だったらfalse
, それ以外だったらtrue
という感じで大丈夫ですgrsim
クラスの動作確認をやってみると楽しいかもしれません (みんなより一歩早くロボット動かせるぞ!!!)Slackで話したとおりイエローの0がブルーの0に向かってく挙動をします。
Masterから変更したのはmain.cppの以下の関数のみです
void mainLoop() {
std::cout << "game start!!" << std::endl;
ai::util::TimePointType prevTime{};
game::action::move move(world_, false, 0);
while (running_) {
try {
const auto currentTime = util::ClockType::now();
if (currentTime - prevTime < cycle) {
std::this_thread::sleep_for(1ms);
} else {
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
const auto prevCmd = refbox_.command();
world_ = updaterWorld_.value();
refbox_ = updaterRefbox_.value();
const auto currentCmd = refbox_.command();
const auto penaltyKick = teamColor_ == model::teamColor::Yellow
? model::refbox::gameCommand::PreparePenaltyYellow
: model::refbox::gameCommand::PreparePenaltyBlue;
// stopgameとpenalty_kickでは速度を落として安定制御
if (currentCmd != prevCmd) {
if (currentCmd == model::refbox::gameCommand::Stop ||
/*current_cmd == model::refbox::game_command::ball_placement_yellow ||
current_cmd == model::refbox::game_command::ball_placement_blue ||*/
(prevCmd == penaltyKick &&
currentCmd == model::refbox::gameCommand::NormalStart) ||
currentCmd == model::refbox::gameCommand::PreparePenaltyYellow ||
currentCmd == model::refbox::gameCommand::PreparePenaltyBlue) {
driver_.velocityLimit(500);
} else if (currentCmd == model::refbox::gameCommand::BallPlacementYellow ||
currentCmd == model::refbox::gameCommand::BallPlacementBlue) {
driver_.velocityLimit(1000);
} else {
driver_.velocityLimit(std::numeric_limits<double>::max());
}
}
const auto visibleRobots =
static_cast<bool>(teamColor_) ? world_.robotsYellow() : world_.robotsBlue();
prevTime = currentTime;
// witten by suiheki20181104
move.moveTo(3000, 3000, 0);
auto model_tmp = move.execute();
driver_.updateCommand(model_tmp);
}
} catch (const std::exception& e) {
std::cout << boost::format("exception at game_thread\n\t%1%") % e.what() << std::endl;
} catch (...) {
std::cout << "unknown exception at game_thread" << std::endl;
}
}
}
gtkmmを使用して, aiのguiクライアントを作成する
ai::controller::base
にて、 boost::variant
が使用されたままになっている
これを、 std::variant
に変更したい
プロジェクト内の時間に関係した関数や型を作成する
ボールを処理するためのactionを作成してください。
最低限、以下を実装してください。
class getBall: public base{
private:
// (1)
position target_;
public:
using base::base;
setTarget(cosnt position _target); // (2)
model::command execute() override; // (3)
};
position target_
... 狙う位置
setTarget()
狙う位置を設定する
execute()
動作処理
src
└── ai
└── game
└── action
├── getBall.cpp
└── getBall.hpp
ai::game::action
にgetBall
として実装してくださいディフェンスエリア手前にロボットを数台置いて壁を作るのがrobocupでは主流となっている。
そこで、壁用のactionを作成する必要がある。
基本的には move
と同様の構成のactionとなるが、ボールを蹴るという点が両者の最大の違いとなる。
最低限の仕様は以下の通り
class clear : public base{
private:
// (1)
model::command::position pos_;
model::command::KickFlag kick_;
public:
using base::base;
void moveTo(model::command::position _pos); // (2)
void kickType(model::command::kickType _kick, double _power); // (3)
model::command execute() override; // (4)
};
model::command::position pos_
: 移動先の座標
model::command::KickFlag kick_
: キックタイプとパワーのtuple. キックタイプは model::command::kickType
を参照
引数に移動先の座標を受け取るので、それをメンバに登録してください。
引数にキックタイプとパワーを受け取るので、それをtuple化してメンバに格納してください
実際にactionを定義する関数です。
基本的な処理内容は action::move::execute()
と変わりませんが、commandにキック指令を登録するようにしてください。
また、向く方向はボールの方向です。ただし、ゴールの方向には向かないものと考えてください。(ひとまずはこれは気にしなくてもいいかも)
src
└── ai
└── game
└── action
├── clear.cpp
└── clear.hpp
ai::game::action
名前空間に登録してくださいai/game/action/move.(hpp | cpp)
ai/model/ball.(hpp | cpp)
ai/model/robot.(hpp | cpp)
ai/model/world.(hpp | cpp)
ai/model/command.(hpp | cpp)
ai/model/teamColor.hpp
refboxから受信したしたデータをいい感じに更新するクラスが必要です
チームカラーを表現する型を作成する.
/// チームカラーを表現する列挙型
enum class teamColor {
blue,
yellow,
};
メンバはblue
, yellow
の各チーム色から構成されます
以下の階層にそれぞれ列挙型とテストケースを実装してください
ai
├──src
│ └──ai
│ └──model
│ └──teamColor.hpp
│
└test
└──model
└──teamColor.cpp
namespaceはai::model
に設置してください.
ロボットのデータ更新するためのクラスが必要です
フィールドの情報を表すためのclassを実装してください
class field {
private:
uint32_t length_;
uint32_t width_;
uint32_t centerRadius_;
uint32_t goalWidth_;
uint32_t penaltyLength_;
uint32_t penaltyWidth_;
public:
// (1)
field();
// (2)
uint32_t length() const;
uint32_t width() const;
uint32_t centerRadius() const;
uint32_t goalWidth() const;
uint32_t penaltyLength() const;
uint32_t penaltyWidth() const;
// (3)
void length(uint32_t _length);
void width(uint32_t _width);
void centerRadius(uint32_t _centerRadius);
void goalWidth(uint32_t _goalWidth);
void penaltyLength(uint32_t _penaltyLength);
void penaltyWidth(uint32_t _penaltyWidth);
// (4)
double xMax() const;
double xMin() const;
double yMax() const;
double yMin() const;
};
各データメンバを初期化するためのコンストラクタを実装してください
各データメンバの値を取得するためのGetterメソッドを実装してください
対応するデータメンバに対してデータを格納するためのSetterメソッドを実装してください
フィールドの中心を原点とした時に, フィールド端の座標を取得するためのメソッドを実装してください.
例えばxMax()
は次のように実装されます.
double xMax()const{
return (length / 2);
}
ai
├── src
│ └── ai
│ └── model
│ ├── field.cpp
│ └── field.hpp
└── test
└── model
└── field.cpp
ai::model
名前空間にfield
を実装してくださいWerckerを使用してpush時に自動でbuildテストするようにする
controllerのbaseクラスを実装してください 関連: #63
コマンドを送信したり、ssl-visionからデータを受信したりするためのdriverが必要です
(詳細仕様は省く)
ABP(=Autonomous Ball Placement )に対応したい
ABPとは,セットプレイに移行する際にロボットによって指定座標にボールを配置するような行為を指す.
配置座標が与えられるため, その座標にボールを持っていけるようなActionを作成してほしい
class AutonomousBallPlacement : public base {
public:
// (1)
AutonomousBallPlacement(const model::world& _world, bool _isYellow, uint32_t _id, Eigen::Vector2d _target);
// (2)
enum class runningState {
Move, // ボール前まで移動
Hold, // ボールを持つ
Place, // ボールを指定位置まで運ぶ
Wait, // ボールの回転を止めるためドリブルバーを止めて停止
Leave, // ボールから離れる
Finished // 動作終了(停止)
};
// (3)
enum class placeMode {
Pull,
Push
};
// (4)
model::command execute() override;
private:
// (5)
runningState state_;
placeMode mode_;
bool waitFlag_;
bool roundFlag_;
util::TimePointType begin_;
util::TimePointType now_;
Eigen::Vector2d target_;
Eigen::Vector2d abpTarget_;
double firstBallx_;
double firstBally_;
};
_target
には,ボールを配置する座標を指定してください
処理の進行具合を表すステートマシンです.
配置する際にpull(引っ張る)かpush(押していく)かを指定するための列挙体です.
実際の処理を記述してください
runningState state_;
現在の進行具合です
placeMode mode_;
どうやって目的地まで持っていくか
bool waitFlag_;
待つ?
bool roundFlag_;
回り込む?
util::TimePointType begin_;
開始時刻
util::TimePointType now_;
現在時刻
Eigen::Vector2d target_;
移動目標位置
Eigen::Vector2d abpTarget_;
最終的な目的地
double firstBallx_;
ボールが最初にあったx座標
double firstBally_;
ボールが最初にあったy座標
src
└── ai
└── game
└── action
├── abp.cpp
└── abp.hpp
敵ロボットをマーキングするためのアクションを作成してください
最低限、以下を実装してください
class marking: public base{
private:
// (1)
uint32_t id_;
public:
using base::base;
markRobot(cosnt uint32_t _id); // (2)
model::command execute() override; // (3)
};
uint32_t id_
... マーキング対象のロボットのid
markRobot(cosnt uint32_t _id);
マークするロボットをセットするための処理
execute()
動作処理
src
└── ai
└── game
└── action
├── marking.cpp
└── marking.hpp
SSL-Visionから得られたデータを格納するclassを作成してください
class world {
// (1)
mutable std::mutex mutex_;
// (2)
model::field field_;
model::ball ball_;
std::unordered_map<unsigned int, model::robot> robots_blue_;
std::unordered_map<unsigned int, model::robot> robots_yellow_;
public:
// (3)
model::field field() const;
model::ball ball() const;
std::unordered_map<unsigned int, model::robot> robots_blue() const;
std::unordered_map<unsigned int, model::robot> robots_yellow() const;
// (4)
void update(const ssl_protos::vision::Packet& packet);
};
std::mutex
std::mutex
及びstd::lock_guard
を使い, 内部リソース(2)
へのアクセスを排他的にしてください.
mutex_
にはmutable
指定子を付け, const
なメンバ関数からも変更できるようにしてください.
以下の4つのパラメータを保持してください.
model::field field_;
: フィールドのサイス等
model::ball ball_;
: フィールド上のボール
std::unordered_map<unsigned int, model::robot> robots_blue_;
: フィールド上の青ロボット
std::unordered_map<unsigned int, model::robot> robots_yellow_;
: フィールド上の黃ロボット
(2)で挙げた値を取得するためのgetter
を実装してください.
値をreturn
する前に, std::lock_guard
を使ってmutex_
をロックしてください.
(2)で挙げた内部のパラメータを更新するためのメンバ関数update()
を実装してください.
簡単な処理の流れは以下のとおりです.
detection
が含まれていればball_
, robots_blue_
, robots_yellow_
を更新するgeometry
が含まれていればfield_
を更新する(3)と同様に, 内部の値を更新する前にstd::lock_guard
を使ってmutex_
をロックしてください.
├── src
│ └── ai
│ └── model
│ ├── world.cpp
│ └── world.hpp
└── test
└── model
└── world.cpp
ai::model
名前空間内にworld
で実装してください環境を構築する上で「ここんとこちょっとうまくいかないみたい」なことがあったらこの板に書き込んでください
(Issueに慣れるのも兼れればいいな)
参考資料: #71 を参照のこと
// (1)
class robot : public base<model::robot, timing::Manual> {
public:
// (2)
using base<model::robot, timing::Manual>::base;
// (3)
void observe(const model::command::velocity _u,
std::chrono::high_resolution_clock::time_point _time);
};
#45 のfilter::base<model::robot, filter::timing::Manual>から派生したものとしてください.
必要に応じて実装してください.
必要な値がなければ ↑↑↑のようにすれば良いです.
const model::command::velocity _u
: 制御入力量
std::chrono::high_resolution_clock::time_point _time
: Controller
が命令を生成した時刻
状態オブザーバを更新するメンバ関数です.
src
└── ai
└── filter
└── observer
├── robot.cpp
└── robot.hpp
クラスはai::filter::observer
名前空間内に実装してください
ここでの状態は (位置, 速度, 加速度) です
RRTでの経路生成か速度生成器での生成に問題があるのか、とてもオーバーシュートしている。
もう少しちゃんと目標位置にたどり着きたいのでこれを修正したい
src/ai/controller/decision/velGen.cpp
ロボットへ送るデータを表現するclassを実装してください.
なお, #11 の結果に依存しない一般的な部分を実装します.
class command {
public:
// (1)
enum class kickType { none, straight, tip };
struct position {
double x;
double y;
double theta;
};
struct velocity {
double vx;
double vy;
double omega;
};
using Setpoint = std::variant<position, velocity>;
using KickFlag = std::tuple<kickType, double>;
// (2)
explicit command(uint32_t _id);
// (3)
uint32_t id() const;
int32_t dribble() const;
KickFlag kick() const;
const Setpoint& setpoint() const;
void dribble(int dribble);
void kick(const KickFlag& _kick);
void position(const position& _position);
void velocity(const velocity& _velocity);
private:
// (4)
uint32_t id_;
int32_t dribble_;
KickFlag kick_;
Setpoint_t setpoint_;
};
位置と速度に関する処理を型レベルで別々に扱いたいので, それぞれを表現する構造体を宣言します.
また, 位置または速度という状態を表現するために, 型安全共用体ライブラリのstd::variant
を使用します.
クラスのデータメンバid_
はコンストラクタからのみ設定できるようにします.
値を一つだけ受け取るので, command c = 1;
のような初期化ができないようにexplicit
をつけてください.
各データメンバの値を取得するためのgetterと, id_
を除くデータメンバに値を設定するためのsetterを実装してください.
uint32_t id_;
: ロボットのID
int32_t dribble_;
ドリブルバーのパワー
KickFlag kick;
: キックフラグ
Setpoint setpoint_;
: 目標位置または目標速度
ai-
├── src
│ └── ai
│ └── model
│ ├── command.cpp
│ └── command.hpp
└── test
└── model
└── command.cpp
クラスはai::model
名前空間内にcommand
で実装してください
World Modelを更新するためのクラスが必要です
必要なもの
ロボットの状態を保管するモデルを作成する。
保存する状態は、 (位置、速度、加速度) に加えて (移動方向、回転(角)速度)
ballの状態を表すclassを作成する
状態は (位置, 速度, 加速度)
ロボットの制御を行うクラスが必要です.
// (1)
class very_cool_name : public base {
public:
// (2)
very_cool_name(double interval, ...);
// (3)
velocity_t update(const model::robot& _robot, const position& _setpoint) override;
velocity_t update(const model::robot& _robot, const velocity& _setpoint) override;
};
何かかっこいい名前を付けてください.
クラスは後述するcontroller::base
から派生したものとしてください.
double interval
[s]
?その他必要なパラメータがあれば, 2つ目の引数以降で受け取ってください.
目標値setpoint
から操作量を計算するメンバ関数.
目標値として位置が渡された場合と速度が渡された場合の2種類を実装してください.
第1引数のrobot
は, そのループが実行されるときのロボットの状態です.
ai
└── src
└── ai
└── controller
├── base.cpp
├── base.hpp
├── very_cool_name.cpp
└── very_cool_name.hpp
ai::controller
名前空間内に実装してくださいA declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.