【角速度FB制御を使ったライントレースの実装】
マイクロマウス Advent Calendar 2025 – 4日目
昨日はあこちゃんの[誰でも作れる、シリコンタイヤ !]でした。
0. まえがき
「どうやって角速度フィードバック(FB)でライントレースしていますか?」
という質問をもらうことが多々ありました。
難しい制御理論は置いておいて、
「どの関数が何をして、どの順番で実行され、最終的にどうやってモータ duty(L/R)になるか」
だけをシンプルに解説します。
この記事を読めば、まだこの制御を取り入れていない選手や、制御面で悩んでいる選手、そして競技を始めたばかりの選手にとって一つの道筋になるはずです。また、中級者・上級者の方でも、今よりもう一段階上の安定性に近づくきっかけ
それでは解説していきます。
1. なぜ角速度FB制御なのか
まず前提として、ロボトレースに取り組んでいる多くの競技者は、
マシンの動きを 「並進(前後速度)」と「旋回(角度変化)」 の2種類に切り分けて管理しています。
この 2 つは干渉させず 完全に独立したフィードバック(FB)ループ として扱う、という構造が一般的です。
多くの入門〜中級レベルでは、
「ラインセンサで検知したライン偏差(中心からのずれ) → モータduty」
という 偏差に直接比例させる制御 を行っていると思います。
しかし、マシンが高速化し、モータの余力など ハードウェアの限界に近づくと、
特に高速ターン(シケインなど)で 想定どおりの角速度が得られなくなる ことがあります。
つまり、センサ上は「曲がれ」と指示していても、
実際のマシンは 曲がりきれていない(=角速度が不足している) 状況が発生します。
そこで活用するのが 角速度フィードバック(FB)制御 です。
IMU(ジャイロセンサ)でマシンの実際の角速度をリアルタイムに計測し、
「いま本当に曲がれているか?」、「指示値に対して角速度は合っているか?」
を常に監視します。
もし思ったより曲がれていなければ、左右モータの電圧(duty)を増減して補正して、モータの逆起電力や車体の慣性といった外的要因も同時に打ち消しながら、意図した旋回量に近づけていきます。
この理想の旋回性能に近づけられる点が、角速度FB制御の大きなメリットです。
角速度FBを入れることで高速域でも追従性が安定し、
曲がり不足によるコースアウトや、
ゲインを上げすぎて duty がハンチングする調整 を避けることができます。
さらに、必要な分だけ電圧を出すようになるため
無駄な電流が減り、モータの発熱やバッテリ消費も抑えられます。
特に大会本番の残電圧を確保したい場面では、この効果は効いてきます。
2. 角速度FBライントレースを行う4つの処理
では、実際にマシン内部ではどのように処理が流れているのか。
自分のマシンでは、ラインセンサ入力 → モータ duty 出力 までを
次の4つの段階に分けて考えています。
これはあくまで「正解」ではなく、一例ですが、
制御構造を整理する上ではうまく分けられていると思います。
下記が処理の全体像です。
line_trace(in: line_offset, speed → out: ω_set) // ライン偏差+速度から「目標角速度」を生成 omega_controller(in: ω_set, gyro_z → out: V_turn) // 角速度PI制御で旋回成分の電圧を出力 center_speed_controller(in: target_speed, now_speed → out: V_forward) // 並進方向の速度をPI制御し推進電圧を出力 voltage_controller(in: V_forward, V_turn, Battery_V → out: L_duty, R_duty) // 左右電圧を合成し、バッテリ電圧で正規化してPWM dutyへ変換
この「処理を段階ごとに分けて理解する」ことが、
角速度FB制御を正しく扱ううえで最も重要なポイントになります。
ここで示す4ステップは、あくまで 角速度FBライントレースの構造を説明するためのもの です。
実際の走行では各種補正式など、別の要素もこのフローに組み合わせて使うので、実装の際は注意して下さい。
では1つ1つ順番に説明していきます。
3. line_trace:ライン偏差から「目標角速度」を作る
line_trace(in: line_offset, speed → out: ω_set) // ライン偏差+速度から「目標角速度」を生成
ラインセンサの複数チャンネルの情報は、
最終的に line_offset(ライン中心からのずれ量) という1つの値に集約されます。
この line_offset に現在の並進速度を掛け合わせることで、
「今この速度なら、どれくらいの角速度で曲がるべきか?」
という 目標角速度(ω_set) を生成します。
ここは “センサ情報 → 1つの角速度指示値” という変換を行う段階です。
line_trace関数 の出力は ω_set のみです。
センサ情報をこの 1 つの値に集約することで、
制御構造がシンプルになり、結果として走行の安定性も高まります。
4. omega_controller:角速度 PI制御で旋回成分を作る
omega_controller(in: ω_set, gyro_z → out: V_turn) // 角速度PI制御で旋回成分の電圧を出力
先ほど計算した ω_set(指示値)と、
ジャイロから取得した実角速度 gyro_z の誤差を用いて
PI制御を行うのがこの段階です。
ここで出てくる出力 V_turn は、
左右モータへ割り振る「旋回電圧」に相当します。
特徴としては、
-
P成分は速い誤差を抑え、
-
I成分は“曲がり不足”をゆっくり補正していく
という役割を持っています。
この「V_turn」が後段の電圧合成に回され、
右回転なら右輪を強く、左輪を弱く…というように、
物理的な旋回力として使われます。
5. center_speed_controller:並進方向の速度を制御する
center_speed_controller(in: target_speed, now_speed → out: V_forward) // 並進方向の速度をPI制御し推進電圧を出力
これは角速度とは独立した 並進(前後方向)の速度制御 です。
-
target_speed …… 走りたい速度(速度計画)
-
now_speed …… 実際の車速(エンコーダで推定)
この2つの差を制御し、
前後方向の推進電圧 V_forward を出力します。
このように、
「角速度」と「並進速度」それぞれを別々のFB系に分けることで、
高速域でもマシンの挙動がブレにくくなります。
特に高速コースでは、
旋回中に並進速度が落ちすぎたり、逆に出すぎたりすると不安定につながりますが、
この2系統構造によってそれを回避できます。
6. voltage_controller:左右電圧を合成し、モータ duty を作る
voltage_controller(in: V_forward, V_turn, Battery_V → out: L_duty, R_duty) // 左右電圧を合成し、バッテリ電圧で正規化してPWM dutyへ変換
ここまでで、
-
旋回成分(V_turn)
-
前進成分(V_forward)
という2種類の電圧が得られています。
これらを左右モータに次のように割り振ります:
-
左モータ:V_forward − V_turn
-
右モータ:V_forward + V_turn
この合成により、
直線では左右同じ電圧が出て、
旋回中は片方が強く・もう片方が弱くなる —
という直感的な挙動が実現できます。
最後に、これらの電圧をバッテリ電圧で割って PWM duty(L/R) に変換します。
ここで初めて “モータに実際に送る値” が完成します。
7. おわりに
以上、現状私のマシンで動いている 「ライン入力 → モータ出力」 までの制御の流れを紹介しました。
今回の記事では、具体的なゲイン値や内部の細かい式についてはあえて触れていません。
それらはマシンごとに最適解が大きく異なり、実際にデータを取りながら調整していく“自分の味付け”の領域だと思っています。
ただ、この 制御構造(流れ) さえ理解できれば、
あとは自分のマシンに合ったゲインや補正式を作り込むだけで、確実に一段上の安定性に近づくはずです。
正直なところ、自分と同じように 1 からこの辺りのアルゴリズムを考え、実装してきた選手からは
「ここまで書くのはやりすぎだよ」
と怒られてしまうかもしれません。
しかし、ここで紹介した内容は多くの上位勢が既に確立している部分でもあり、
これを “1つの道筋” として公開することで、
初心者・中級者がストレスなく(脱落することなく)次のステップに進める手助けになればと考えました。
そして、基本構造を早く理解できれば、
空いた開発リソースを 新しいアルゴリズムの研究開発 に割けるようになります。
競技全体のレベルアップにもつながるはずだと思っています。
この記事が、次のステップへ進みたい選手にとって、
少しでもヒントやきっかけになれば幸いです。
コメント
コメントを投稿