二足歩行ロボットを作ってみよう(前編)
ABEJA の研究員が、Dynamixel モーターと IsaacSim を活用して強化学習ベースの二足歩行ロボットをゼロから開発するまでのシミュレーション環境構築プロセスを詳細に報告している。
キーポイント
ハードウェア選定と設計
コストを抑えつつ機能性を確保するため、Dynamixel モーター(XC-330, XL-330)を採用し、OpenRB-150 と WitMotion IMU を制御基板および姿勢推定に使用した。
シミュレーション環境の構築
CAD で設計した URDF モデルを USD 形式に変換し、NVIDIA Isaac Lab (IsaacSim) を用いて物理シミュレーション環境を整備した。
強化学習アプローチの採用
MPC などの従来の制御法ではなく、柔軟な動作獲得を目的として強化学習(RL)を採用し、対称性や軌道最適化など多様な報酬設計を検討している。
Sim2Real の展望
シミュレーションでの学習結果を実機へ転送する Sim2Real への課題を含め、最終的に汎用性の高いヒューマノイドロボットの実現を目指す方針を示している。
環境と学習の設定分離
IsaacLabでは、ロボットモデルや物理パラメータを扱う環境設定(env)と、学習手法やハイパーパラメータを扱う学習設定(agent)を明確に分離して構成します。
トルク制御と観測・報酬の設計
今回はJointEffortActionCfgによるトルク制御を採用し、姿勢安定性や進行方向などを評価する詳細な観測項目と、転倒防止や滑らかな動作を促す多様な報酬関数を設定しました。
PPOアルゴリズムの採用
学習の不安定性を防ぎつつ効率的に方策を最適化するため、更新量のクリップを行う標準的な強化学習手法であるPPOを採用しています。
影響分析・編集コメントを表示
影響分析
この記事は、大企業主導になりつつあるフィジカル AI やヒューマノイド開発において、中小企業や研究機関レベルでも高品質なロボット開発が可能であることを示す実践的なケーススタディです。特に URDF から Isaac Lab への変換フローや強化学習の報酬設計に関する具体的な知見は、同分野の開発者にとって即戦力となる技術的インサイトを提供します。
編集コメント
シミュレーションから実機への転送(Sim2Real)という難易度の高い課題に取り組む過程を、具体的なハードウェア選定やコード構成と共に公開しており、開発者にとって非常に参考になる実践記事です。

ロボットの制作 ハードウェアの設計
シミュレーション USD の作成と IsaacSim での確認
Agent の設定 強化学習について
歩容改善の取り組みについて 対称性の学習
様々な報酬について 歩容・接地パターン系
足の軌道・ストライド・空中時間系
姿勢・位置・速度の追従/安定化系
ジョイント・動力学系(ペナルティ)
模倣学習(Imitation Learning)
実験 学習結果 強化学習の報酬ログ
こんにちは、ABEJA のラボで研究開発をしている藤本です。この記事は ABEJA アドベントカレンダー 2025 の 25 日目の記事です。
近年、フィジカル AI(Physical AI)の盛り上がりとともに、ロボティクス分野への注目も高まっています。ABEJA のブログでもロボット関連の記事が増えてきました。さまざまなロボットが開発されていますが、僕個人としては特にヒューマノイドに強い興味があります。
現在は技術的・環境的ハードルから、車輪ベースなど歩行以外の機構が主流ですが、最終的には汎用性の高いヒューマノイドに向かうだろうと考えています。展示会に足を運ぶと多くの企業がヒューマノイドを展示していますが、フルスクラッチで開発している例は意外と少ないと感じます。
「それなら自分で作ってみよう!」ということで、二足歩行ロボットの開発に挑戦してみました。機体の制作、シミュレーション、実機への転移(Sim2Real)などやることは盛りだくさんです。今回の記事では前編ということでシミュレーションまでの流れを紹介します。二足歩行の制御の方法としては MPC(モデル予測制御)を使う方法など様々ありますが、柔軟な動作を獲得しやすくできると良いので強化学習でやってみましょう。
まずはロボットの基本構成を考えます。今回は財布の事情もあり、シンプルに足の関節を 3 つに限定し、姿勢の認識も IMU(慣性計測装置)のみとします。アクチュエータとしては Dynamixel のモーターを採用しました。Dynamixel のモーターはポジション制御・速度制御・トルク制御など複数の制御方式を持っており、ROS との連携や Python の SDK もあるので便利そうです。以下のモーターを購入してみました。
XC-330-M181T × 2
XL-330-M288-T × 4
制御のボードとして OpenRB-150 を、姿勢推定 IMU(慣性計測装置)として WitMotion WT901B を使います。
次にロボットのモデルを作っていきましょう。CAD でロボットの機体を設計して URDF に変換します。作成した URDF は以下のようなモデルとなります。

それっぽいのが出来たので、各パーツを 3D プリンタで印刷して組み立ててみました。

組み立てたロボットは以下になります。実物があると、やっぱりテンションは上がりますね。

USD の作成と IsaacSim での確認
作成したロボットモデルの URDF から IsaacLab でシミュレーションをしていきましょう。まずは、URDF ファイルを IsaacLab で読み込めるように USD に変換します。
/isaaclab.sh -p scripts/tools/convert_urdf.py \
PATH_TO_URDF PATH_TO_USD \
--joint-target-type position \
--headless
変換したファイルを IsaacSim で確認してみましょう。IsaacSim のメニューから Create-> Physics->Ground Plane を選択、z 軸の位置を調整して大まかにロボットの高さを確認できるのでメモをしておき、後ほどロボットをスポーンさせる際の z 座標の設定で使います。今回は 0.189 くらいですね。

IsaacLab でプロジェクトを作っていきます。以下のコマンドで対話的にプロジェクトを作れます。プロジェクトを IsaacLab ディレクトリ内に直接作るか外部に作るか、環境について、学習フレームワークについて聞かれます。ここでは、external、single agent、rsl_rl を選択します。
./isaaclab.sh --new
プロジェクトを作ったら、ソースのあるディレクトリに移動してプロジェクトをインストールします。
$ cd [PROJECT_NAME]/source/[PROJECT_NAME]
pip install -e .
プロジェクトの構成は初期は以下のようになります。各ファイルについて簡単に説明しましょう。scripts ディレクトリは、学習や推論などの様々なスクリプトが含まれます。基本的には触る必要はありません。source ディレクトリ以下に、学習のための様々なスクリプトが含まれます。基本的に触るのは manager_based ディレクトリ以下の [PROJECT_NAME]_env_cfg.py と rsl_rl_ppo_cfg.py です。
. ├── README.md ├── scripts │ ├── list_envs.py │ ├── random_agent.py │ ├── rsl_rl │ │ ├── cli_args.py │ │ ├── play.py │ │ └── train.py │ └── zero_agent.py └── source └── [PROJECT_NAME] ├── config │ └── extension.toml ├── docs │ └── CHANGELOG.rst ├── pyproject.toml ├── setup.py └── [PROJECT_NAME] ├── __init__.py ├── tasks │ ├── __init__.py │ └── manager_based │ ├── __init__.py │ └── [PROJECT_NAME] │ ├── __init__.py │ ├── agents │ │ ├── __init__.py │ │ └── rsl_rl_ppo_cfg.py │ ├── mdp │ │ ├── __init__.py │ │ └── rewards.py │ └── [PROJECT_NAME]_env_cfg.py └── ui_extension_example.py
IsaacLab では、rsl_rl を用いて強化学習を行う際には環境設定(env)と学習設定(agent)について設定を行います。独自の観測・報酬関数・終了判定関数を使う時は、mdp ディレクトリ下にスクリプトをします。それ以外はパラメータの設定のみで動きます。環境側ではロボットモデル、地形、観測、報酬、シミュレーション設定などを扱い、学習側では学習手法・ネットワーク構造・ハイパーパラメータを設定します。
まずロボットの設定から始めていきます。ロボットの設定は [PATH_TO_ROBOT].py の ArticulationCfg です。
次は環境の設定に移りましょう。設定するのは InteractiveSceneCfg、ActionsCfg、ObservationsCfg、EventCfg、RewardsCfg、TerminationsCfg です。設定すべき項目が多すぎるので、ここでは IsaacLab のコード中にある classic/humanoid というアセットの設定をコピーして利用することとしました。
ActionsCfg で、アクチュエータにどのタイプの指令を送るかを設定します。トルク制御の場合は JointEffortActionCfg、ポジション制御の場合は JointPositionActionCfg を利用します。今回はトルク制御を想定して JointEffortActionCfg を利用します。
観測の情報は、ObservationsCfg に設定します。humanoid をベースにしつつノイズを与えて以下の観測を採用しました。
ロボット本体(ベースリンク)の地面からの高さ
ロボットベースの並進速度(x, y, z 方向)
ロボットベースの角速度(roll, pitch, yaw の回転速度)
ベースリンクの yaw(向き)と roll(左右の傾き)
base_angle_to_target
ロボットの前方方向とゴール方向の角度差(ターゲットへの向き誤差)
ロボットの「上向きベクトル」がワールドの上方向(Z 軸)とどれだけ一致しているか(姿勢の直立度)
base_heading_proj
ロボットの前方向ベクトルがゴール方向とどれだけ一致しているか(方向一致度)
各関節角度を正規化した値(関節位置)
各関節の速度(関節角速度、もしくは速度指令との相対値)
feet_body_forces
足リンクに伝わる外力(接触力や関節からの力などの 6D wrench)
前ステップで実行したアクション(関節コマンド)
EventCfg では、ロボットの各種イベントを設定します。以下を利用することにしました。
physics_material
ロボットや地面の摩擦係数・反発係数など、物理シミュレーションの材質パラメータをランダム化する設定
ロボット本体(ベース)の質量をランダムに変化させることで、重さの違いに対するロバスト性を上げる設定
エピソード開始時のロボットベースの位置・姿勢をランダム化する初期化設定
reset_robot_joints
ロボットの関節角度や関節速度をリセットするときにランダム化する初期姿勢設定
学習中にランダムな外力をロボットへ与え、外乱に対して安定に動けるようにするためのランダム押し設定
報酬は RewardsCfg に設定します。ここが一番大変です。実験では色々試しましたが最終的に採用した報酬を以下に記載します。この他に実装した報酬関数は別に記載します。
ゴール方向にどれだけ前進したか(タスクの進捗に応じた報酬)
エピソードが終了せず、ロボットが倒れずに生存している時間に対する報酬
ロボットが垂直に立っており、姿勢が安定している度合い
ロボットの前方方向がゴール方向と一致し、その方向へ移動できているか
垂直方向(Z)の速度の変化量を罰する項(上下にバタつかないようにする)
水平方向(X-Y)周りの角速度を罰する項(ピッチ・ロールの急変を抑える)
行動(アクション)値の L2 ノルム。過大な関節速度指令を抑えるためのペナルティ
flat_orientation_l2
ロボットが水平姿勢から大きく傾いていないか(安定した姿勢維持)
各関節にかかったトルクの大きさを罰する項(無駄な力を抑える)
関節角加速度の大きさを罰する項(急激な動作を抑えて滑らかにする)
reference_height
ロボットが目標とする基準高さ(センターの高さ)を維持できているか
feet_air_time_positive_biped
二足歩行時に、左右の足が適切な「空中時間」を持ち、歩行リズムが自然かどうか
接地中の足が横滑りしていないか(滑りをペナルティ)
termination_penalty
転倒・危険姿勢・ゴールから離れすぎなどでエピソードが途中終了したときのペナルティ
終了条件は TerminationsCfg で以下のようにしています。
1 エピソードの最大ステップ数(または最大時間)を超えた場合に終了する
ロボットの本体(胴体)の高さがしきい値より低くなった場合に終了する(転倒などの判定)
IsaacLab では、rl_games、rsl_rl、skrl などの強化学習ライブラリをバックエンドとして利用できます。今回は標準的な強化学習手法の一つである PPO を利用します。PPO とは、エージェントが行動方針(方策)を更新する際に、現在の方策と更新前の方策の確率比が大きく変わりすぎないよう更新量をクリップして調整しつつ、報酬が高くなる方向へ少しずつ安全に改善していく強化学習アルゴリズムです。その制御によって学習が不安定になりにくく、効率よく方策を最適化できることが特徴です。
強化学習に関する設定項目は以下です。
num_steps_per_env
1 エピソードあたり環境から収集するステップ数
アクションのクリッピング上限(制御指令の暴走防止)
アクション出力に付与する初期ノイズの標準偏差(探索用)
actor_obs_normalization
Actor に入力する観測を正規化するか
critic_obs_normalization
Critic に入力する観測を正規化するか
actor_hidden_dims
Actor ネットワークの隠れ層(ユニット数)
128 / 128 / 128
critic_hidden_dims
Critic ネットワークの隠れ層(ユニット数)
128 / 128 / 128
ネットワークで使用する活性化関数
num_learning_epochs
1 回の更新で学習するエポック数
num_mini_batches
ミニバッチ分割数(学習安定性向上)
GAE λ(Advantage の平滑性調整)
KL ダイバージェンスの目標値(過学習防止)
value_loss_coef
PPO のクリップ係数(Policy 更新の安定化)
IsaacLab では、実行に関する設定をタスクとして登録します。__init__.py
gym.register( id="Template-MyProject-Rl-v0", entry_point="isaaclab.envs:ManagerBasedRLEnv", disable_env_checker=True, kwargs={ "env_cfg_entry_point": f"{__name__}.[PROJECT_NAME]_env_cfg:MyRlEnvCfg", "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:MyPPORunnerCfg", }, )
ここまでのセッティングで学習をすると、以下のように大量のシミュレーションを並列に動かすことが出来ます。

左右対称の歩容を学習してもらえるのが理想ですが、報酬の設計・カリキュラムの設計が甘いと、思ったような動作をしてくれません。例えば、スキップしたり、飛び跳ねたり、左右非対称になってしまったり。ここでは、歩容改善の色んな取り組みについて紹介します。作業時間が十分取れなかったので、どれがどのくらい聞いたかは調査できませんでした・・・。
ロボットの安定な姿勢の一つとして、片足を前に出して、もう片足を後ろにし、すり足で進むというものがあります。報酬ですり足に対するペナルティを設定していたとしても、安定して進める報酬が勝ってしまい、左右非対称な歩行になってしまうことがしばしばあります。
これに対して、左右対称のデータ拡張やロスに関する手法があります。Learning Symmetric and Low-energy Locomotionという論文では、rsl_rlのフレームワークでは対称性を扱うことができるので、実装してみました。具体的には、観測と行動を左右対称に変換する関数をそれぞれ用意して設定します。ちなみにsymmetric lossを入れると学習中に崩壊することが多く、augmentationのみ利用するのが良さそうでした。
PPOでは学習をしていると途中でモデルが崩壊することがあります。一度崩壊するとそこに安定してしまい、抜け出せなくなってしまいます。これに対し、Mitigating Policy Collapse in Deep Reinforcement Learning では、学習率のベータを調整することで改善する手法を提案しました。具体的には、Adamにおけるbeta1とbeta2を同じ数値にすることで崩壊が減るようです。実装が簡単なので入れていますが、効果はよくわからなかったです。
人間が歩くときは、リズムよく左右の足を交互にステップを踏みます。ロボットにもそのような周期性の導入をするという研究があります。具体的には、Learning 3D Bipedal Walking with Planned Footsteps and Fourier Series Periodic Gait Planningにおいては、観測に周期情報(sin(t)とcos(t))を与え、報酬側で現在の周期に対応して足踏みが出来ているかどうかをチェックします。
Benchmarking Potential Based Reward Shaping for Humanoid Locomotion では、報酬を設計する際に、直接報酬ではなく、状態間の「遷移」に焦点を当て、ある状態から次の状態へ移動した際のポテンシャル関数の差分(変化量)を報酬とすることで、パラメータの変化に対してロバストになる。
論文の工夫を入れたら正しく動くというわけでもなく、色んな報酬を実装して試してみました。それぞれどのくらい効いたかもまとめたかったのですが、時間も無かったので、良い感じに動くまで試行錯誤していました。ちなみに結果としては、報酬を入れると、報酬に応じて動いてくれるものの、どうしても不自然な動きになってしまうことがわかりました。以下は今回の実験の中で実装した関数群です。沢山実装したなぁ。
左右交互に歩けるような報酬を色々考えてみました。
alternating_gait_reward
左右交互の接地パターンができているかを評価。両足接地/両足浮遊をペナルティ。
single_foot_contact_reward
片足のみ接地(single support)を理想とし、それ以外をゼロ報酬。
両足が空中にある=ジャンプ状態ならペナルティ。
split_stance_penalty
両足接地時に X 方向で過度に離れているとペナルティ。
lead_leg_switch_reward
左右のリードレッグ(前に出ている方)が適切に交互に切り替わると報酬。
feet_x_range_utilization_reward
両足が近すぎるとペナルティ、適度な左右差があると報酬。
feet_x_range_penalty
各足の X 位置が正常範囲(参照軌跡)から逸脱するとペナルティ。
左右足の X 位置差が小さすぎるとペナルティ。
gait_phase_reward
位相に応じて、スイング中の接地やスタンス中の足移動に対してペナルティを課します。
足の軌道・ストライド・空中時間系
歩容(ゲイツ)と似ていますが、歩容ほど制約が強くなく、足の軌跡に関する報酬もいくつか作成しました。
foot_slip_penalty
接地中の足が滑る(速度が閾値以上)場合にペナルティを課します。
foot_height_reward
スイング中の足が十分に持ち上がっていれば報酬を与えます。足の空中時間が閾値より長い場合も評価されます。
feet_air_time_positive_biped
片足支持時間と空中時間を正方向に報酬化します(2 足歩行専用)。接地足の滑り速度が大きい場合はペナルティを課します。
foot_stride_reward
離陸から着地までのストライド距離(XY 距離)に応じて報酬を与えます。
alternating_step_forward_reward / v2
スイング中の足が前進すれば報酬を与える、より良いバージョンです。
alternating_step_speed_reward
スイング中の足の速度がベースラインよりも前向きで速い場合に報酬を与えます。
feet_average_x_position_reward
両足の平均 X 位置が目標値に近い場合に報酬を与えます。
姿勢・位置・速度の追従/安定化系
姿勢の安定性に関する様々な報酬です。足先ではなく、体の状態に関する報酬となります。
target_speed_tracking_reward
目標速度に近づけば報酬、離れれば負の報酬となります。
reference_height_reward
ロボットの高さが参照平均に近いほど高報酬となります。
velocity_jerk_penalty
前フレームとの速度差が大きい(ガタつきがある)場合にペナルティを課します。
torso_forward_lean_reward
torso のピッチ角が目標の前傾角に近いほど報酬を与えます。
ジョイント・動力学系(ペナルティ)
joint_pos_limits_penalty_ratio
ジョイント角度が制限に近づくほどペナルティを課し、ギア比で重み付けを行います。
power_consumption
関節トルクと速度の積で消費パワーを計算し、総量に対してペナルティを課します。
模倣学習(Imitation Learning)
別の機体パラメータを使用することで、実機とは異なる状況でありつつも上手く学習させたモデルが完成したため、足の軌跡を模倣する試みも行いました。
link_position_imitation_reward
全リンクの位置を参照軌跡と比較し、誤差が小さいほど報酬を与えます。
link_relative_position_imitation_reward
base_link を基準とした相対リンク位置を模倣するための報酬です。
joint_angle_imitation_reward
関節角度を参照軌跡に合わせるための報酬です。
x_direction_imitation_reward
X 方向(進行方向)の相対位置のみを模倣するための報酬です。
先程の観測データには、ロボットに搭載していないセンサデータなどが含まれています。例えば目標値との相対距離などは、ロボットが自己位置推定を行わないと取得することができません。そういった本来計測できない特殊な情報を特権情報と呼びます。一方、そのような特権情報がなくても動作させたくなります。そこで、特権情報ありで推論したモデルの出力を、ロボットが計測可能な観測データに転移する蒸留(ディストillation)を行っていきましょう。
蒸留は、IsaacLab では環境側の設定として、特権情報をなくした生徒用の観測を設定します。Agent クラスとして、RslRlDistillationRunnerCfg クラスを利用します。ObservationsCfg の中に、StudentCfg というのを作り特権情報を廃した設定をしてあげます。IMU のみで計測できそうな以下の観測を残すことにしました。
ロボットベースの角速度(roll, pitch, yaw の回転速度)
ベースリンクの yaw(向き)と roll(左右の傾き)
各関節角度を正規化した値(関節位置)
各関節の速度(関節角速度、もしくは速度指令との相対値)
前ステップで実行したアクション(関節コマンド)
Agent 側では RslRlDistillationRunnerCfg を継承したクラスに、各種パラメータを設定します。obs_groups を以下のように設定することで、教師と生徒の観測を指定できます。
obs_groups = { "policy": ["student"], "teacher": ["policy"] }
gym.register( id="Template-MyProject-Rl-v0", entry_point="isaaclab.envs:ManagerBasedRLEnv", disable_env_checker=True, kwargs={ "env_cfg_entry_point": f"{__name__}.[PROJECT_NAME]_env_cfg:MyRlEnvCfg", "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:MyPPORunnerCfg", "rsl_rl_distillation_cfg_entry_point": f"{agents.__name__}.rsl_rl_distillation_cfg:MyDistillationRunnerCfg", }, )
学習は以下のように行います。学習ログを wandb に管理することも可能です。
python scripts/rsl_rl/train.py \
--task=Template-[PROJECT_NAME]-Rl-v0 \
--max_iterations=10000 \
--headless \
--logger wandb
学習したモデルの再生は以下のように行います。
python scripts/rsl_rl/play.py --task=Template-[PROJECT_NAME]-Rl-v0
蒸留についても同様に以下のように学習・再生を行います。
学習 python scripts/rsl_rl/train.py \ --task Template-[PROJECT_NAME]-Rl-v0 \ --agent rsl_rl_distillation_cfg_entry_point \ --load_run 2025-12-19_00-52-52 \ --checkpoint model_ \ --headless \ --logger wandb # 再生 python scripts/rsl_rl/play.py \ --task Template-[PROJECT_NAME]-Rl-v0 \ --checkpoint logs/rsl_rl/[PROJEC_NAME]/2025-12-20_00-27-36_distillation/model_29999.pt \ --agent rsl_rl_distillation_cfg_entry_point
では、実際に学習と推論(推論:学習済みのモデルを用いて予測や判断を行うこと)を実行していきましょう。実験では、強化学習および蒸留(蒸留:教師モデルの知識を学生モデルに転移させる手法)それぞれについての推論結果について以下に示します。
以下の図のように報酬が順調に伸びることを確認できています。

とはいえ、単に報酬が伸びるだけでは上手く行っているとは言えず、歩容(歩行の姿勢やリズム)に関する報酬などもチェックして、正しく歩けているかを検証する必要があります。例えば以下の例では、報酬は伸びているが交互の歩容の報酬は低いという状況になっています。交互の歩容の重みが低すぎて、前に行くことを優先してしまった結果として以下のような状況が発生します。とはいえ、単純に歩容のペナルティを大きくしすぎると、今度は良い歩き方を見つけてくれず学習が進まないという状況になったりもします。歩容など正解を見つけるのが難しい報酬値が上がるように、上手く機体を設計する・カリキュラム(段階的な学習計画)を設計する・中間報酬を入れるなどが必要になり、強化学習はなかなか大変ですね。

さて、色々調整をした結果として、ある程度良い感じに報酬が得られたモデルの再生結果は以下のようになりました。ノイズを多く
原文を表示

ロボットの制作 ハードウェアの設計
シミュレーション USDの作成とIsaacSimでの確認
Agentの設定 強化学習について
歩容改善の取り組みについて 対称性の学習
様々な報酬について 歩容・接地パターン系
足の軌道・ストライド・空中時間系
姿勢・位置・速度の追従/安定化系
ジョイント・動力学系(ペナルティ)
模倣学習(Imitation Learning)
実験 学習結果 強化学習の報酬ログ
こんにちは、ABEJAのラボで研究開発をしている藤本です。この記事はABEJAアドベントカレンダー2025の25日目の記事です。
近年、フィジカル AI の盛り上がりとともに、ロボティクス分野への注目も高まっています。ABEJA のブログでもロボット関連の記事が増えてきました。さまざまなロボットが開発されていますが、僕個人としては特にヒューマノイドに強い興味があります。
現在は技術的・環境的ハードルから、車輪ベースなど歩行以外の機構が主流ですが、最終的には汎用性の高いヒューマノイドに向かうだろうと考えています。展示会に足を運ぶと多くの企業がヒューマノイドを展示していますが、フルスクラッチで開発している例は意外と少ないと感じます。
「それなら自分で作ってみよう!」ということで、二足歩行ロボットの開発に挑戦してみました。機体の制作、シミュレーション、実機への転移(Sim2Real)などやることは盛りだくさんです。今回の記事では前編ということでシミュレーションまでの流れを紹介します。二足歩行の制御の方法としてはMPCを使う方法など様々ありますが、柔軟な動作を獲得しやすくできると良いので強化学習でやってみましょう。
まずはロボットの基本構成を考えます。今回は財布の事情もあり、シンプルに足の関節を3つに限定し、姿勢の認識もIMUのみとします。アクチュエータとしては Dynamixelのモーターを採用しました。Dynamixelのモーターはポジション制御・速度制御・トルク制御など複数の制御方式を持っており、ROSとの連携やPythonのSDKもあるので便利そうです。以下のモーターを購入してみました。
XC-330-M181T × 2
XL-330-M288-T × 4
制御のボードとしてOpenRB-150を、姿勢推定IMUとしてWitMotion WT901Bを使います。
次にロボットのモデルを作っていきましょう。CADでロボットの機体を設計してURDFに変換します。作成したURDFは以下のようなモデルとなります。

それっぽいのが出来たので、各パーツを3Dプリンタで印刷して組み立ててみました。

組み立てたロボットは以下になります。実物があると、やっぱりテンションは上がりますね。

USDの作成とIsaacSimでの確認
作成したロボットモデルのURDFからIsaacLabでシミュレーションをしていきましょう。まずは、URDFファイルをIsaacLabで読み込めるようにUSDに変換します。
/isaaclab.sh -p scripts/tools/convert_urdf.py \ PATH_TO_URDF PATH_TO_USD \ --joint-target-type position \ --headless
変換したファイルをIsaacSimで確認してみましょう。IsaacSimのメニューからCreate-> Physics->Ground Planeを選択、z軸の位置を調整して大まかにロボットの高さを確認できるのでメモをしておき、後ほどロボットをスポーンさせる際のz座標の設定で使います。今回は0.189くらいですね。

IsaacLabでプロジェクトを作っていきます。以下のコマンドで対話的にプロジェクトを作れます。プロジェクトをIsaacLabディレクトリ内に直接作るか外部に作るか、環境について、学習フレームワークについて聞かれます。ここでは、external、single agent、rsl_rlを選択します。
./isaaclab.sh --new
プロジェクトを作ったら、ソースのあるディレクトリに移動してプロジェクトをインストールします。
$ cd [PROJECT_NAME]/source/[PROJECT_NAME] pip install -e .
プロジェクトの構成は初期は以下のようになります。各ファイルについて簡単に説明しましょう。scriptsディレクトリは、学習や推論などの様々なスクリプトが含まれます。基本的には触る必要はありません。sourceディレクトリ以下に、学習のための様々なスクリプトが含まれます。基本的に触るのはmanager_basedディレクトリ以下の[PROJECT_NAME]_env_cfg.py
rsl_rl_ppo_cfg.py
. ├── README.md ├── scripts │ ├── list_envs.py │ ├── random_agent.py │ ├── rsl_rl │ │ ├── cli_args.py │ │ ├── play.py │ │ └── train.py │ └── zero_agent.py └── source └── [PROJECT_NAME] ├── config │ └── extension.toml ├── docs │ └── CHANGELOG.rst ├── pyproject.toml ├── setup.py └── [PROJECT_NAME] ├── __init__.py ├── tasks │ ├── __init__.py │ └── manager_based │ ├── __init__.py │ └── [PROJECT_NAME] │ ├── __init__.py │ ├── agents │ │ ├── __init__.py │ │ └── rsl_rl_ppo_cfg.py │ ├── mdp │ │ ├── __init__.py │ │ └── rewards.py │ └── [PROJECT_NAME]_env_cfg.py └── ui_extension_example.py
IsaacLabでは、rsl_rlを用いて強化学習をする際には環境設定(env)と 学習設定(agent)について設定を行います。独自の観測・報酬関数・終了判定関数を使う時は、mdpディレクトリ下にスクリプトをします。それ以外はパラメータの設定のみで動きます。環境側ではロボットモデル、地形、観測、報酬、シミュレーション設定などを扱い、学習側では学習手法・ネットワーク構造・ハイパーパラメータを設定します。
まずロボットの設定から始めていきます。ロボットの設定は[PATH_TO_ROBOT].py
ArticulationCfg
次は環境の設定に移りましょう。設定するのはInteractiveSceneCfg、ActionsCfg、ObservationsCfg、EventCfg、RewardsCfg、TerminationsCfgです。設定すべき項目が多すぎるので、ここではIsaacLabのコード中にあるclassic/humanoidというアセットの設定をコピーして利用することとました。
ActionsCfgで、アクチュエータにどのタイプの指令を送るかを設定します。トルク制御の場合はJointEffortActionCfg、ポジション制御の場合はJointPositionActionCfgを利用します。今回はトルク制御を想定してJointEffortActionCfgを利用します。
観測の情報は、ObservationsCfgに設定します。humanoidをベースにしつつノイズを与えて以下の観測を採用しました。
ロボット本体(ベースリンク)の地面からの高さ
ロボットベースの並進速度(x, y, z 方向)
ロボットベースの角速度(roll, pitch, yaw の回転速度)
ベースリンクの yaw(向き)と roll(左右の傾き)
base_angle_to_target
ロボットの前方方向とゴール方向の角度差(ターゲットへの向き誤差)
ロボットの「上向きベクトル」がワールドの上方向(Z軸)とどれだけ一致しているか(姿勢の直立度)
base_heading_proj
ロボットの前方向ベクトルがゴール方向とどれだけ一致しているか(方向一致度)
各関節角度を正規化した値(関節位置)
各関節の速度(関節角速度、もしくは速度指令との相対値)
feet_body_forces
足リンクに伝わる外力(接触力や関節からの力などの 6D wrench)
前ステップで実行したアクション(関節コマンド)
EventCfgでは、ロボットの各種イベントを設定します。以下を利用することにしました。
physics_material
ロボットや地面の摩擦係数・反発係数など、物理シミュレーションの材質パラメータをランダム化する設定
ロボット本体(ベース)の質量をランダムに変化させることで、重さの違いに対するロバスト性を上げる設定
エピソード開始時のロボットベースの位置・姿勢をランダム化する初期化設定
reset_robot_joints
ロボットの関節角度や関節速度をリセットするときにランダム化する初期姿勢設定
学習中にランダムな外力をロボットへ与え、外乱に対して安定に動けるようにするためのランダム押し設定
報酬はRewardsCfgに設定します。ここが一番大変です。実験では色々試しましたが最終的に採用した報酬を以下に記載します。この他に実装した報酬関数は別に記載します。
ゴール方向にどれだけ前進したか(タスクの進捗に応じた報酬)
エピソードが終了せず、ロボットが倒れずに生存している時間に対する報酬
ロボットが垂直に立っており、姿勢が安定している度合い
ロボットの前方方向がゴール方向と一致し、その方向へ移動できているか
垂直方向(Z)の速度の変化量を罰する項(上下にバタつかないようにする)
水平方向(X-Y)周りの角速度を罰する項(ピッチ・ロールの急変を抑える)
行動(アクション)値の L2 ノルム。過大な関節速度指令を抑えるためのペナルティ
flat_orientation_l2
ロボットが水平姿勢から大きく傾いていないか(安定した姿勢維持)
各関節にかかったトルクの大きさを罰する項(無駄な力を抑える)
関節角加速度の大きさを罰する項(急激な動作を抑えて滑らかにする)
reference_height
ロボットが目標とする基準高さ(センターの高さ)を維持できているか
feet_air_time_positive_biped
二足歩行時に、左右の足が適切な「空中時間」を持ち、歩行リズムが自然かどうか
接地中の足が横滑りしていないか(滑りをペナルティ)
termination_penalty
転倒・危険姿勢・ゴールから離れすぎなどでエピソードが途中終了したときのペナルティ
終了条件はTerminationsCfgで以下のようにしています。
1エピソードの最大ステップ数(または最大時間)を超えた場合に終了する
ロボットの本体(胴体)の高さがしきい値より低くなった場合に終了する(転倒などの判定)
IsaacLabでは、rl_games、rsl_rl、skrlなどの強化学習ライブラリをバックエンドとして利用できます。今回は標準的な強化学習手法の一つであるPPOを利用します。PPOとは、エージェントが行動方針(方策)を更新する際に、現在の方策と更新前の方策の確率比が大きく変わりすぎないよう更新量をクリップして調整しつつ、報酬が高くなる方向へ少しずつ安全に改善していく強化学習アルゴリズムです。その制御によって学習が不安定になりにくく、効率よく方策を最適化できることが特徴です。
強化学習に関する設定項目は以下です。
num_steps_per_env
1エピソードあたり環境から収集するステップ数
アクションのクリッピング上限(制御指令の暴走防止)
アクション出力に付与する初期ノイズの標準偏差(探索用)
actor_obs_normalization
Actor に入力する観測を正規化するか
critic_obs_normalization
Critic に入力する観測を正規化するか
actor_hidden_dims
Actor ネットワークの隠れ層(ユニット数)
128 / 128 / 128
critic_hidden_dims
Critic ネットワークの隠れ層(ユニット数)
128 / 128 / 128
ネットワークで使用する活性化関数
num_learning_epochs
1回の更新で学習するエポック数
num_mini_batches
ミニバッチ分割数(学習安定性向上)
GAE λ(Advantage の平滑性調整)
KLダイバージェンスの目標値(過学習防止)
value_loss_coef
PPOのクリップ係数(Policy更新の安定化)
IsaacLabでは、実行に関する設定をタスクとして登録します。__init__.py
gym.register( id="Template-MyProject-Rl-v0", entry_point="isaaclab.envs:ManagerBasedRLEnv", disable_env_checker=True, kwargs={ "env_cfg_entry_point": f"{__name__}.[PROJECT_NAME]_env_cfg:MyRlEnvCfg", "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:MyPPORunnerCfg", }, )
ここまでのセッティングで学習をすると、以下のように大量のシミュレーションを並列に動かすことが出来ます。

左右対称の歩容を学習してもらえるのが理想ですが、報酬の設計・カリキュラムの設計が甘いと、思ったような動作をしてくれません。例えば、スキップしたり、飛び跳ねたり、左右非対称になってしまったり。ここでは、歩容改善の色んな取り組みについて紹介します。作業時間が十分取れなかったので、どれがどのくらい聞いたかは調査できませんでした・・・。
ロボットの安定な姿勢の一つとして、片足を前に出して、もう片足を後ろにし、すり足で進むというものがあります。報酬ですり足に対するペナルティを設定していたとしても、安定して進める報酬が勝ってしまい、左右非対称な歩行になってしまうことがしばしばあります。
これに対して、左右対称のデータ拡張やロスに関する手法があります。Learning Symmetric and Low-energy Locomotionという論文では、rsl_rlのフレームワークでは対称性を扱うことができるので、実装してみました。具体的には、観測と行動を左右対称に変換する関数をそれぞれ用意して設定します。ちなみにsymmetric lossを入れると学習中に崩壊することが多く、augmentationのみ利用するのが良さそうでした。
PPOでは学習をしていると途中でモデルが崩壊することがあります。一度崩壊するとそこに安定してしまい、抜け出せなくなってしまいます。これに対し、Mitigating Policy Collapse in Deep Reinforcement Learning では、学習率のベータを調整することで改善する手法を提案しました。具体的には、Adamにおけるbeta1とbeta2を同じ数値にすることで崩壊が減るようです。実装が簡単なので入れていますが、効果はよくわからなかったです。
人間が歩くときは、リズムよく左右の足を交互にステップを踏みます。ロボットにもそのような周期性の導入をするという研究があります。具体的には、Learning 3D Bipedal Walking with Planned Footsteps and Fourier Series Periodic Gait Planningにおいては、観測に周期情報(sin(t)とcos(t))を与え、報酬側で現在の周期に対応して足踏みが出来ているかどうかをチェックします。
Benchmarking Potential Based Reward Shaping for Humanoid Locomotion では、報酬を設計する際に、直接報酬ではなく、状態間の「遷移」に焦点を当て、ある状態から次の状態へ移動した際のポテンシャル関数の差分(変化量)を報酬とすることで、パラメータの変化に対してロバストになる。
論文の工夫を入れたら正しく動くというわけでもなく、色んな報酬を実装して試してみました。それぞれどのくらい効いたかもまとめたかったのですが、時間も無かったので、良い感じに動くまで試行錯誤していました。ちなみに結果としては、報酬を入れると、報酬に応じて動いてくれるものの、どうしても不自然な動きになってしまうことがわかりました。以下は今回の実験の中で実装した関数群です。沢山実装したなぁ。
左右交互に歩けるような報酬を色々考えてみました。
alternating_gait_reward
左右交互の接地パターンができているかを評価。両足接地/両足浮遊をペナルティ。
single_foot_contact_reward
片足のみ接地(single support)を理想とし、それ以外をゼロ報酬。
両足が空中にある=ジャンプ状態ならペナルティ。
split_stance_penalty
両足接地時に X 方向で過度に離れているとペナルティ。
lead_leg_switch_reward
左右のリードレッグ(前に出ている方)が適切に交互に切り替わると報酬。
feet_x_range_utilization_reward
両足が近すぎるとペナルティ、適度な左右差があると報酬。
feet_x_range_penalty
各足の X 位置が正常範囲(参照軌跡)から逸脱するとペナルティ。
左右足の X 位置差が小さすぎるとペナルティ。
gait_phase_reward
位相に応じ、スイング中の接地/スタンス中の足移動をペナルティ。
足の軌道・ストライド・空中時間系
歩容と似ていますが、歩容ほど制約の強くない、足の軌跡に関する報酬も色々作ってみました。
foot_slip_penalty
接地中の足が滑る(速度が閾値以上)とペナルティ。
foot_height_reward
スイング中の足が十分に持ち上がっていれば報酬。
足の空中時間が閾値より長いと評価。
feet_air_time_positive_biped
片足支持時間・空中時間を正方向に報酬化(2足歩行専用)。
接地足の滑り速度が大きいとペナルティ。
foot_stride_reward
離陸〜着地までのストライド距離(XY距離)に応じて報酬。
alternating_step_forward_reward / v2
スイング中の足が前進すれば報酬、より良いバージョン。
alternating_step_speed_reward
スイング中の足速度がベースより前向きで速いと報酬。
feet_average_x_position_reward
両足の平均 X 位置が目標値に近いと報酬。
姿勢・位置・速度の追従/安定化系
姿勢の安定性に関する様々な報酬です。足先ではなく体の状態に関する報酬ですね。
target_speed_tracking_reward
目標速度へ近づけば報酬、離れれば負。
reference_height_reward
ロボット高さが参照平均に近いほど高報酬。
velocity_jerk_penalty
前フレームとの速度差が大きい(ガタつき)とペナルティ。
torso_forward_lean_reward
torso の pitch が目標前傾角に近いほど報酬。
ジョイント・動力学系(ペナルティ)
joint_pos_limits_penalty_ratio
ジョイント角度が制限に近づくほどペナルティ、ギア比で重み付け。
power_consumption
関節トルク×速度で消費パワーを計算、総量ペナルティ。
模倣学習(Imitation Learning)
別の機体パラメータを使うことで、実機とは異なる状況でありつつ上手く学習をさせたモデルが出来上がったので、足の軌跡を模倣するのもやってみました。
link_position_imitation_reward
全リンク位置を参照軌跡と比較し、誤差が小さいほど報酬。
link_relative_position_imitation_reward
base_link基準の相対リンク位置を模倣する報酬。
joint_angle_imitation_reward
関節角度を参照軌跡に合わせる報酬。
x_direction_imitation_reward
X方向(進行方向)の相対位置のみ模倣する報酬。
先程の観測データには、ロボットに搭載していないセンサデータなどが含まれます。例えば目標値との相対距離などは、ロボットが自己位置推定をしないと取ることができません。そういった本来計測できない特殊な情報を特権情報と呼びます。一方、そのような特権情報がなくても動作させたくなります。そこで、特権情報ありで推論したモデルの出力を、ロボットが計測可能な観測データに転移する蒸留をやっていきましょう。
蒸留は、IsaacLabでは環境側の設定として、特権情報をなくした生徒用の観測を設定します。Agentクラスとして、RslRlDistillationRunnerCfgクラスを利用します。ObservationsCfgの中に、StudentCfgというのを作り特権情報を廃した設定をしてあげます。IMUのみで計測できそうな以下の観測を残すことにしました。
ロボットベースの角速度(roll, pitch, yaw の回転速度)
ベースリンクの yaw(向き)と roll(左右の傾き)
各関節角度を正規化した値(関節位置)
各関節の速度(関節角速度、もしくは速度指令との相対値)
前ステップで実行したアクション(関節コマンド)
Agent側ではRslRlDistillationRunnerCfgを継承したクラスに、各種パラメータを設定します。obs_groupsを以下のように設定することで、教師と生徒の観測を指定できます。
obs_groups = { "policy": ["student"], "teacher": ["policy"] }
gym.register( id="Template-MyProject-Rl-v0", entry_point="isaaclab.envs:ManagerBasedRLEnv", disable_env_checker=True, kwargs={ "env_cfg_entry_point": f"{__name__}.[PROJECT_NAME]_env_cfg:MyRlEnvCfg", "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:MyPPORunnerCfg", "rsl_rl_distillation_cfg_entry_point": f"{agents.__name__}.rsl_rl_distillation_cfg:MyDistillationRunnerCfg", }, )
学習は以下のように行います。学習ログをwandbに管理することも可能です。
python scripts/rsl_rl/train.py \ --task=Template-[PROJECT_NAME]-Rl-v0 \ --max_iterations=10000 --headless --logger wandb
学習したモデルの再生は以下のように行います。
python scripts/rsl_rl/play.py --task=Template-[PROJECT_NAME]-Rl-v0
蒸留についても同様に以下のように学習・再生を行います。
学習 python scripts/rsl_rl/train.py \ --task Template-[PROJECT_NAME]-Rl-v0 \ --agent rsl_rl_distillation_cfg_entry_point \ --load_run 2025-12-19_00-52-52 \ --checkpoint model_ \ --headless \ --logger wandb # 再生 python scripts/rsl_rl/play.py \ --task Template-[PROJECT_NAME]-Rl-v0 \ --checkpoint logs/rsl_rl/[PROJEC_NAME]/2025-12-20_00-27-36_distillation/model_29999.pt \ --agent rsl_rl_distillation_cfg_entry_point
では、実際に学習と推論をやっていきましょう。実験では、強化学習および蒸留、それぞれについての推論結果について以下に示します。
以下の図のように報酬が順調に伸びることを確認できています。

とはいえ、単に報酬が伸びるだけでは上手く行っているとは言えず、歩容に関する報酬などもチェックして、正しく歩けているかをチェックする必要があります。例えば以下の例では、報酬は伸びているが交互の歩容の報酬は低いという状況になっています。交互の歩容の重みが低すぎて、前に行くことを優先してしまった結果として以下のような状況が発生します。とはいえ、単純に歩容のペナルティを大きくしすぎると、今度は良い歩き方を見つけてくれず学習が進まないという状況になったりもします。歩容など正解を見つけるのが難しい報酬値が上がるように、上手く機体を設計する・カリキュラムを設計する・中間報酬を入れるなどが必要になり、強化学習はなかなか大変ですね。

さて、色々調整をした結果として、ある程度良い感じに報酬が得られたモデルの再生結果は以下のようになりました。ノイズを多く
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み