佐倉です。
日曜日なのでマイクロマウスやってない人には何を言っているのかわからないような話を書こうと思います。
さて、本日は非同期部分(迷路探索部分)の構成とか、同期部分(制御部分)との通信について書いて行きます。
まずはいきなり非同期部分の構成案を出してみます。このあたりは基本的に皆さんが工夫を凝らす部分だと思うので、非常に抽象的な表現やコードが出てきますが、自身の具体例に置き換えながら見ていただければ幸いです。
※この記事で紹介するコードはすべて動作確認等、行なっておりません。擬似コードだと思ってお読みください。
まずは探索の入り口、壁情報の取得から。
// 迷路データを更新する関数 void UpdateMazeData() { while(IsWallReadPosition() == false); // 迷路の情報を更新する場所に到達するまで待つ. ClearWallReadPositionFlag(); // 上で使う事になるであろうフラグをクリアする. UpdateMaze(GetWallData(), GetMyPosition()); // 迷路を更新する。引数は 壁情報,自機位置 と想定. }
これまで紹介してきたような作り方でシステムを作ると、探索のための壁の情報をどのタイミングで読み出せばよいのかが分かりません。なので、同期を取るために関数の一行目のようなwhileループで待っています。他のやり方として、制御ループのフィルタ部でしっかりとしたオドメトリを行なっている場合には、座標を見てソフトウェア割り込みを発生させるというやり方もキレイだと思います。
さて、次は探索のループです。
// 探索のループを行う関数 void SearchLoop(void (*InitSearchAlgorithm)(void), MoveType (*SearchAlgorithm)(Position pos)) { InitSearchAlgorithm(); // 探索アルゴリズムの初期化を行う StartMove(); // 最初の一区画の走りだしを行う // 探索を行うループ do { UpdateMazeData(); // 迷路データを更新する AppendMove(SearchAlgorithm(GetMyPosition())); // 探索アルゴリズムが返す動きを実行キューに入れる }while(IsGoal() == false); StopMove(); // ゴールで停止する時に必要な動きを実行キューに入れる }
関数ポインタを使ってまとめると、一つの関数で様々な探索アルゴリズムのループに対応できて便利です。構造体にまとめると更にいいかもしれません。
探索アルゴリズムは位置を受け取り、現在の迷路データから[前進、右ターン、左ターン、後退、停止]などの行うべき行動を返します。コード上ではゴールにたどり着くまで繰り返しとなっていますが、実際には壁にぶつかってスタックする事もあるでしょうから、異常があった場合に抜けるような条件も書いておくと良いでしょう。
次は動きを追加している部分の説明をします。まずはコードから。
// 動きを追加してキューに入れる void AppendMove(MoveType move) { addMove(move); // 動きを管理させる OptimizeMotion(); // 動きを最適化する PushMotion(GetMotion()); // 具体的な動きをキューに入れる }
このあたりはアプリケーションと関わってくるので参考程度に見てみてください。一番重要なのが最後のPushMotionの部分で、モーションデータをリングバッファに入れます。
ここで想定しているOptimizeMoveは、たとえば直線が連続していた場合には長い直線として再構築するような物です。
同期部分で動きを取りだす
// 動きを取り出す void GetMotionData { ............. ............. motion = PopMotion(); // 具体的な動きをキューから取り出す ............. ............. }
どんどん抽象化が進んでしまっています。動きに関するデータを取り出して実際に使う形(加速度や角加速度)に加工します。Motionの粒度などは工夫すると楽しい所ですね。GoFのCommandパターンのようにしておくと差し込みや差し替え、取り消しが直感的になるのでオススメです。
ここまで例として、探索での非同期部分と同期部分の合わせ方を書いてみました。最短走行であれば、動きのデータを突っ込むだけ突っ込んでおいて、あとはゴールに着くまで放置という感じになるかと思います。
この方式の最大のメリットは、探索ルーチンにかけられる時間が非常に長い事です。(恐らく)主流のやり方の、加減速などのを非同期部分で書くとそこで待ちが生じてしまい、使える時間が少なくなっていますが、この方式であればコンスタントに制御ループが長くなりますが、平行してずっと処理を続けることができます。なので、探索のアルゴリズムを工夫したかったり、探索中の既探索区間を斜め走行したりしたい方にオススメの方法です。
デメリットは、制御ループが重くなる事と、危ういバランスの上に成り立っているので、ちょっとミスをすると同期が外れて帰ってこなくなる事です。
考察というタイトルを付けておきながら、一つの形態の紹介になってしまいました。ちなみに、ほぼ同じ構成で私のマウスが動いています。私はシステムを作るのが好きで、マイクロマウスを1台作るたびにソフトを書き直し続けていますが、今の所この方式が一番気に入っています。
私のマウスの動画
マイクロマウスは簡単なハードウェアで構成されているので、とりあえず動かすのは非常に簡単ですが、綺麗なシステムを構築するという観点から見ると奥深く楽しめます。マウスをやっている方は、自分のやりたい事にあったシステム構築を、やっていない方はこういう視点の楽しみ方もあるので、ぜひ手を出してみてください。
今回で、システムについての話はひとまずお終いにしようと思います。来週から何を書くかは未定ですが、マイクロマウス関連の話を出していこうと思います。