# 歩行方策を自分で学習する 第13章の四脚を歩かせている方策は、[配布済みのもの](walk.md)をそのまま使えば 学習しなくても動きます。ここでは**それを自分で学習し直す**手順をまとめます。 報酬を変えたい、速度の上限を上げてみたい、別の歩容を出したい——そういう ときの入口です。 学習の入口は 2 つあります。NVIDIA GPU があれば **mjlab 版**、無ければ **CPU 版**です。CPU 版は同じタスクを素の MuJoCo で回すもので、6 コアのノート PC で 2048 環境 × 2300 iteration が約 69 分です(環境は並列に動かす 1 台ぶんのシミュレーション、iteration は方策を 1 回更新するまでの区切り)。GPU が無くても、Google Colab (無料枠の T4)で mjlab 版を通す道もあります。どれで学習してもチェックポイント(途中で保存した学習結果(重み))の 形式は同じで、同じ手順で実機用のヘッダに書き出せます。 ## 学習コードの構成 学習は [unitree_rl_mjlab](https://github.com/unitreerobotics/unitree_rl_mjlab) ([mjlab](https://github.com/mujocolab/mjlab) + rsl_rl の PPO 学習基盤)の上で 行います。`docs/os-on-arduino/code/13_quadruped/rl/` はその上流に**被せる overlay** で、 それ自体は単体で動くプログラムではありません。 | ファイル | 中身 | |---|---| | `robot_cfg.py` | 機体定義(アクチュエータ、初期姿勢、`home` 角) | | `env_cfgs.py` | 観測・コマンド・ドメインランダマイゼーション(摩擦や質量などを学習中にランダムに変えること。{ref}`walk-training-code`) | | `rewards.py` | 報酬項(速度追従、歩容、姿勢、エネルギー等) | | `rl_cfg.py` | PPO のハイパーパラメータとネットワーク構成 [96, 64] | | `runner.py` | ERFI(extended random force injection。トルク外乱)を足した学習ランナー | | `scripts/` | 環境構築・学習・再生・動画書き出し | | `cpu/` | 同じタスクを mjlab なしで学習する CPU 版(下の「GPU なしで学習する(CPU 版)」) | `setup.sh` が上流を `.upstream/unitree_rl_mjlab` に clone し、`rl/` を `src/tasks/velocity/config/arduino_quad` として**シンボリックリンク**で 置きます。上流は `src/tasks/` 配下を自動 import するので、これだけで 次の 4 タスクが登録されます。 | タスク ID | 用途 | |---|---| | `ArduinoQuad-Flat` | 平地・素の velocity レシピ(立ち上げ確認用) | | `ArduinoQuad-Walk` | 平地 + 歩容シェーピング(**本命。トロット歩行はこれ**) | | `ArduinoQuad-Robust` | Walk と同じだが初期状態を崩す(実機前の頑健化) | | `ArduinoQuad-Recovery` | 転倒姿勢から立ち上がる | ## Google Colab で学習する(GPU が無い人向け) ブラウザだけで、環境構築から Arduino 用ヘッダの書き出しまで通せる ノートブックを用意しました。 ```{raw} html

Open In Colab

``` ノートブックの流れ: 1. GPU の確認(**ランタイム → ランタイムのタイプを変更 → T4 GPU**) 2. コードを取ってきて、学習環境(mjlab + MuJoCo Warp + rsl_rl)を入れる 3. **配布済みの学習済み方策を再生して動画で見る** — ここまでで 「シミュレータ・方策・エクスポート」が揃って動いていることを確認できます 4. 割り当てられた GPU で 30 iteration 回して**学習速度を実測**し、 所要時間を見積もる 5. 学習する(チェックポイントは Google Drive に保存) 6. セッションが切れたら続きから再開する 7. 学習した方策を動画で確認して、`arduino_quad_policy.h` に書き出す ```{note} Colab の無料枠はセッションが数時間で切れます。学習ログを Google Drive に 逃がしておけば、`--agent.resume True` で何度でも続きから再開できます。 ノートブックの 6 章がその設定です。 ``` ### 無料枠の T4 でどれくらいかかるか 2026-08-31 に Colab の無料枠(Tesla T4 15 GB、Python 3.13)で、環境構築から `.h` の書き出しまで実際に通しました。そのときの実測です。 | | | |---|---| | 環境構築(`setup.sh`) | 約 1 分 | | 学習(2048 環境) | **1.42 秒/iteration、34,900 steps/s** | | 書籍と同じ 4,500 iteration に必要な時間 | **約 1.8 時間** | | 方策の再生(300 ステップの動画) | 初回 5〜6 分(GPU カーネルの生成を含む)、2 回目以降 3〜4 分 | | `.h` の書き出し | 約 1 分 | 参考までに、同じ設定を RTX 4090 で回すと 0.77 秒/iteration・63,700 steps/s でした。**T4 は 4090 の半分強の速度**で、無料枠でも書籍と同じ規模の学習が 現実的な時間で終わります。 ### 何 iteration で歩くようになるか ノートブック既定の設定(`ArduinoQuad-Walk`、2048 環境)で 1500 iteration 回し、100 iteration ごとのチェックポイントを**指令 0.09 m/s で 6 秒ずつ 再生した実測**です(RTX 4090、1 シード。シードは乱数の初期値)。 | iteration | 実測 前進 [m/s] | iteration | 実測 前進 [m/s] | |---|---|---|---| | 0 | −0.000 | 800 | 0.086 | | 100 | **−0.219** | 900 | 0.071 | | 200 | −0.194 | 1000 | 0.072 | | 300 | 0.005 | 1100 | 0.096 | | 400 | 0.014 | 1200 | 0.072 | | 500 | 0.046 | 1300 | 0.086 | | 600 | 0.046 | 1400 | 0.101 | | 700 | 0.058 | 1499 | **0.089** | **100〜200 iteration では後ろに進みます。** 前進報酬を拾うより先に 「転ばない」を覚えるためで、失敗ではありません。前進に転じるのが 300 付近、 指令の速度に届くのが 800 前後、そこから先は 0.07〜0.10 の範囲で揺れながら 追従します。各点は 1 エピソード(ロボットを置いてから倒れるか時間切れになるまでの 1 回)の測定なので、隣り合う点の ±0.02 m/s は 誤差の範囲です。 ```{note} これは**既定の設定で学習した別の方策**であって、[配布済み方策](walk.md) (home 高さ 0.11 m、歩容周期 0.32 s、3999 iteration、指令 0.12 m/s で 0.120 m/s)の再現では ありません。エクスポートされるヘッダには学習時の home 角が書き込まれるので、 実機側の整合はそのまま取れます。 ``` ## 手元の GPU で学習する 推奨環境は **Ubuntu** です。Windows でも WSL(Windows の上で Linux を動かす仕組み)を 入れれば動くはずですが、まだ確かめていません。どちらも用意できなければ、下の {ref}`train-cpu` を使ってください。 ```bash cd 13_quadruped # docs/os-on-arduino/code から ./rl/scripts/setup.sh # 上流を clone して overlay を置き、依存を入れる source rl/scripts/env.sh # 機体の MJCF の場所を教える ./rl/scripts/train.sh ArduinoQuad-Walk --env.scene.num-envs=4096 ``` `setup.sh --no-install` にすると、clone と配置だけ行って依存の導入を飛ばせます。 ### 版の固定について `setup.sh` は依存を**版で固定して**入れます。上流 (unitree_rl_mjlab) の `setup.py` が指すままでは、この機体の設定が読み込めません。 | | | なぜ固定するか | |---|---|---| | mjlab | 1.3.0 | サーボの逆起電力 (`viscous_damping`) と 27 ms の指令遅れ (`delay_min/max_lag`) が 1.2.0 に無い。後者は sim2real の要なので外せない | | mujoco-warp | 3.7.0.1 | mjlab 1.3.0 が要求する版 | | mujoco | 3.7.0 | 最新版が入ると mujoco-warp の import が落ちる | | warp-lang | 1.14.0 | 1.16 では学習開始時のカーネル生成が落ちる | | scipy | 1.15 以上 | mjlab 1.3.0 が使っているのに宣言していない | `setup.sh` は加えて、上流のロボット定義が使う `update_assets`(mjlab 1.3.0 で 削除された)を補う小さな互換パッチを、clone したチェックアウトに当てます。 当てないと `src/assets/robots` 全体が ImportError になり、`ArduinoQuad-*` の 登録まで巻き添えになります。 この組み合わせは **2026-08-31 に RTX 4090 で、環境構築から `.h` の書き出しまで 通して確認**しています(2048 環境で 0.77 秒/iteration、63,700 steps/s)。 学習した方策を見るには: ```bash ./rl/scripts/play.sh ArduinoQuad-Walk # ビューアを開く(画面のある環境) ``` 画面の無いサーバや Colab では、ビューアではなく mp4 に落とします: ```bash source rl/scripts/env.sh cd "$ARDUINO_QUAD_UPSTREAM" python "$ARDUINO_QUAD_ROOT/rl/scripts/record_video.py" \ --ckpt logs/rsl_rl/arduino_quad_velocity//model_1500.pt \ --vx 0.09 --steps 300 --out walk.mp4 ``` `` は、学習を始めるたびに `logs/rsl_rl/arduino_quad_velocity/` の下に作られる、 日時の付いたフォルダの名前に置き換えてください。 `--bundle` を渡すと、チェックポイントの代わりに**エクスポート済みの方策** (`13_quadruped/` の `.npz` + `.json`。`--bundle "$ARDUINO_QUAD_ROOT"` のように `13_quadruped` のディレクトリを渡す)を、実機と同じ numpy 実装で再生します。 (train-cpu)= ## GPU なしで学習する(CPU 版) `rl/cpu/` は、同じタスク(`ArduinoQuad-Walk` / `-Robust`)を mjlab を使わずに **素の MuJoCo** で学習するものです。物理は CPU の `mujoco.rollout`(C++ の スレッドプール)で回し、PPO は mjlab 版と同じ rsl-rl-lib 5.0.1 を使います。 ネットワークの更新だけを GPU に置くこともできます(`--device cuda`。CUDA は NVIDIA の GPU で計算するための仕組み)。 ```bash cd 13_quadruped/rl/cpu # docs/os-on-arduino/code から uv sync # mujoco 3.7.0 + rsl-rl-lib 5.0.1 + torch uv run train.py # ArduinoQuad-Walk、2048 環境、CPU uv run train.py --device cuda # 物理は CPU のまま、更新だけ GPU uv run evaluate.py --checkpoint logs/rsl_rl/arduino_quad_velocity//model_2299.pt ``` mjlab 版(`rl/scripts/train.sh`)はそのまま残っています。両者は同じタスクで、 チェックポイントの形式も同じなので、**どちらで学習したものもどちらで続きから 学習でき、同じエクスポータでヘッダに書き出せます**。CPU 版のチェックポイントを 書き出すときは `--backend cpu` を付けます。 ```bash uv run ../../host/export_quad_policy.py \ logs/rsl_rl/arduino_quad_velocity//model_2299.pt 出力先 --backend cpu ``` ```{note} 画面の無いサーバで `evaluate.py --video` を使うときは `MUJOCO_GL=egl` を付けて ください。付けないと MuJoCo が GL のコンテキストを作れず、`Renderer` の生成で 落ちます。 ``` ### どれくらいかかるか ノート PC(Core i9-8950HK、6 コア 12 スレッド、2018 年)での実測です。 | 環境数 | 物理(24 ステップ分) | 1 iteration(PPO 更新込み、CPU) | |---|---|---| | 2048 | 0.78 秒 | 1.55 秒 | | 4096 | 1.50 秒 | 3.13 秒 | 約 63,000 env-steps/s で、mjlab 版を RTX 4090 で回したとき(63,700 steps/s)と 同じくらいです。同じノート PC で 2048 環境 × 2300 iteration を通しで回すと **68.9 分**でした(1.80 秒/iteration)。上の表の 1.55 秒はベンチだけを測った値で、 実際の学習ではログとチェックポイントの書き出しが乗ります。 ### 何 iteration で歩くようになるか 上の通し(`ARDUINO_QUAD_HOME_HEIGHT=0.11 ARDUINO_QUAD_GAIT_PERIOD=0.32`、2048 環境、 1 シード)の 100 iteration ごとのチェックポイントを、`evaluate.py` で**指令 0.09 m/s・32 環境・12 秒**ずつ再生した実測です。接地率は 4 本の足それぞれが 接地している時間の割合(最小〜最大)、対角の同期は対角 2 本が同時に接地/離地して いる割合、滑りは接地中の足先の水平速度です。 | iteration | 前進 [m/s] | 接地率 | 対角の同期 | 滑り [mm/s] | 転倒 | |---|---|---|---|---|---| | 0 | 0.000 | 0.00–1.00 | 0.00 | 0 | 0 | | 100 | — | — | — | — | 全機 | | 200 | — | — | — | — | 全機 | | 300 | −0.003 | 0.00–0.99 | 0.33 | 19 | 0 | | 400 | 0.036 | 0.21–0.86 | 0.44 | 75 | 0 | | 600 | 0.026 | 0.28–0.82 | 0.31 | 79 | 0 | | 800 | 0.088 | 0.38–0.78 | 0.53 | 73 | 0 | | 1000 | 0.089 | 0.38–0.78 | 0.56 | 68 | 0 | | 1500 | 0.088 | 0.36–0.77 | 0.55 | 62 | 0 | | 1800 | 0.092 | 0.46–0.72 | 0.65 | 45 | 0 | | 2000 | 0.090 | 0.46–0.67 | 0.69 | 41 | 0 | | 2299 | 0.089 | 0.47–0.68 | 0.70 | 41 | 0 | **100〜200 iteration では全機が転びます。** まだ立っていられないので、 「転ばない」を覚えるまでの区間です。300 で転ばなくなり、400〜600 は前に出ようと して横に流れ(このとき旋回速度が 0.36 rad/s まで出ます)、指令の速度に届くのが 800 前後です。 **速さは 1000 iteration で頭打ちですが、歩容はそこから 2300 まで良くなり続けます。** 接地率が 4 本そろい(0.38–0.78 → 0.47–0.68)、対角の同期が 0.53 → 0.70 に上がり、 滑りが 73 → 41 mm/s に減ります。1500〜1800 でひと段落良くなるのは、`trot_guidance` (歩容を教える報酬)の重みが 1400 iteration で 0 になった直後です。前進の速さだけを 見て学習を止めると、この区間を捨てることになります。 ```{note} CPU 版が mjlab 版と同じタスクになっていることは `rl/cpu/parity_check.py` で 確認しています(同じ初期状態・同じ行動列で両方を回し、関節角・観測・報酬 26 項を ステップごとに比較)。検査のやり方と、残っている差は `rl/cpu/README.md` にあります。 ``` ## Genesis で学習する 同じタスクを物理シミュレータ Genesis(GPU)で学習するコードが `rl/genesis/` に あります。学習した方策は MuJoCo でも歩きます。手順と、2 つのシミュレータでの 比較は {doc}`sim2sim` にまとめました。Genesis 版にも Google Colab で学習から 書き出しまで通すノートブックがあります({doc}`sim2sim` の「Google Colab で学習する」)。 ## 実機に持っていく チェックポイントから Arduino 用のヘッダを書き出します。 ```bash cd "$ARDUINO_QUAD_UPSTREAM" python "$ARDUINO_QUAD_ROOT/host/export_quad_policy.py" \ logs/rsl_rl/arduino_quad_velocity//model_1500.pt \ 出力先ディレクトリ ArduinoQuad-Walk ``` 出てくるのは 3 つです。 | ファイル | 使うところ | |---|---| | `arduino_quad_policy.h` | ファームウェア(`13_quadruped/include/` に置き換える) | | `arduino_quad_policy.npz` | PC 直結版(`13_quadruped/host/quad_host.py`) | | `arduino_quad_policy.json` | 同じ情報の機械可読版 | 重みだけでなく、**観測の並び・履歴の順序・正規化の統計・home 角・action の スケール**まで、学習時の環境から読み出して書き込みます。ここを人間が 書き写すと、エラーも出ないまま「震えるだけのロボット」になるためです。 書き出したあとで numpy 実装と PyTorch の出力を突き合わせて検証します。 実機で動かす手順は {doc}`walk` の「実機で歩かせる」を読んでください。 **最初は必ず機体を吊るしてから**動かすこと。 ## つまずきやすいポイント | 症状 | 原因と対処 | |---|---| | wandb のログインを求められて止まる | mjlab の既定ロガーが wandb(Weights & Biases。学習の経過を記録するクラウドサービス)です。`--agent.logger tensorboard` を付けると、TensorBoard(学習の経過をグラフで見るローカルのツール)に記録します | | `MJCF が見つかりません` | `source rl/scripts/env.sh` を忘れています(`$ARDUINO_QUAD_XML`) | | 画面が無い環境で `play.sh` が止まる | ビューアを開こうとしています。`record_video.py` を使ってください | | `home 姿勢が環境と方策で違います` | 方策と環境の学習設定が食い違っています。メッセージが出す `ARDUINO_QUAD_HOME_HEIGHT=...` を付けて実行し直す | | 学習が「その場で止まる」方策に収束する | 到達できない速度を指令しています。この機体でサーボの余裕の中に収まるのは 0.05〜0.09 m/s です | (読者からの質問に応じて随時追記します。質問は [GitHub Issues](https://github.com/iory/build-your-own-arduino-rtos/issues) へどうぞ)