sim2real のレシピ
最終更新
シミュレーションではきれいに歩けるのに実機に載せた途端に転ぶ——この種のプロジェクトで最もよくある失敗の形です。microduck_rl は sim2real のレシピ一式を誰かの経験の中ではなく、リポジトリそのものに書き込んでいます。
このページでは、それが何を正しくやっているのかを整理します。パラメータを丸写しするより、ここを理解するほうが役に立ちます。
一、アクチュエータの物理を理想モデルで済ませない
Dynamixel XL330 サーボのモデリングには BAM の M6 モデルが使われており、次を含みます。
- 電圧制御則
- 逆起電力(back-EMF)
- クーロン摩擦、ストライベック摩擦、負荷依存の摩擦
この層が sim2real の土台です。サーボを理想的なトルク源として扱うと、シミュレーションで学んだ歩容は実機でほぼ確実に破綻します。実物のサーボの摩擦と電圧特性が、ポリシーの当てにしていた応答をちょうど食い潰すからです。
二、ランダム化するのは物理パラメータであって、ノイズではない
ドメインランダム化は環境ごとに、次の 4 つの量に対して適用されます。
| ランダム化の対象 | なぜそれなのか |
|---|---|
| バッテリー電圧 | 満充電のアヒルと残量わずかのアヒルでは、サーボが出せるトルクが違います |
| 負荷時の電圧降下 | 複数のサーボが同時に力を出すと電圧が落ち、これが歩容に直接効きます |
| 指令の遅延 | 実システムの制御経路には遅延があり、ポリシーはそれに頑健でなければなりません |
| 摩擦の大きさ | 個体差と摩耗 |
実装は src/mjlab_microduck/actuator/ の FrictionDRBamActuator です。
この 4 つがいずれも物理パラメータであって、観測にガウスノイズを足したものではない点に注意してください。両者はまったく別種のランダム化です。前者はポリシーに現実のパラメータ分布への対処を学ばせ、後者は観測ノイズへの鈍感さを与えるだけです。
三、バックラッシュは正しい側にモデル化しなければならない
ここが最も間違えやすく、microduck_rl の実装で最も学ぶ価値のある部分です。
主要タスクにはそれぞれ Backlash 版があり、14 個のサーボ関節に ±1°(合計 2°)の歯車のガタが直列に入ります。
鍵はエンコーダの位置です。
実機のエンコーダは、バックラッシュの出力側に付いている。
したがってシミュレーション側もこの構造に合わせる必要があります。
- 各サーボに非駆動の
passive_<joint>_backlashヒンジを追加する。 - ファームウェアの PD エミュレーション(
BacklashEncoderBamActuator)とjoint_pos/joint_velの観測を、バックラッシュを通して読み取る。つまりqpos[servo] + qpos[backlash]です。
エンコーダをバックラッシュの入力側にモデル化してしまうと、ポリシーは関節位置を正確に把握しているつもりになりますが、実機で読めるのはガタを経た後の値です。この差は静止しているうちは見えず、反転の瞬間に一気に表面化します。
工学的な利点もあります。観測次元も行動次元も変わらないため、ONNX の書き出しと実機ランタイムにはまったく変更が要りません。バックラッシュ付きで学習したポリシーと、そうでないポリシーはそのまま入れ替えられます。
四、複数のポリシーが 1 つの観測契約を共有する
実機ではランタイムが歩行 / 復帰 / トリックのポリシー間をホットスワップします。その背後にあるのが、共有された 61 次元の観測契約です。
この設計の意味は、どのポリシーもいつでもロボットを引き受けられるということです。転倒復帰のポリシーは歩行ポリシーからの「引き継ぎ」を待つ必要がなく、必要になった瞬間に介入できます。
シミュレーションでこれを予行演習します。
uv run scripts/infer_policy.py --walking walk.onnx --standing stand.onnx \
--sitstand sitstand.onnx --roulade roulade.onnx --new-cmd-obs
このスクリプトが予行演習しているのは、まさに実機ランタイムの切り替えロジックです。--debug、--save-csv、--record に対応しており、これらはsim2real の比較のために用意されています。同じ指令列をシミュレーションと実機でそれぞれ流し、曲線を突き合わせます。
実機に載せた後、どこを見るか
ポリシーを実機に載せた後の主な観察窓は robotctl monitor です。クライアントが何を要求したかと実際に何が実行されたかを並べて表示し、両者が食い違うときはその理由を示します。
sim2real の段階で最もよくあるのは、安全側の制限がポリシー出力をクランプしているケースです。スティックを目一杯倒しているのにロボットが動かない、という状況で、理由は次のように明示されます。
deadman — no intent arrived recently, velocity zeroed
下端の枠には、現在読み込まれている .onnx が表示されます。これはポリシーを差し替えながらデバッグするときに重要です。walk はモード名であり、歩容がまったく違う 2 つの版もどちらも walk として報告されます。
オフライン解析のために状態を丸ごと書き出すには:
robotctl monitor --json --hz 50 > run.jsonl
infer_policy.py --save-csv のシミュレーションデータと突き合わせれば、関節ごとの sim2real 比較ができます。