GMO AI&ロボティクス商事株式会社

ヒューマノイドブログ

SMPでUnitree G1に自然な動きを学習させる — Score-Matching Motion Priorsの再現検証

エンジニアブログ

お問い合わせ
SMPでUnitree G1に自然な動きを学習させる — Score-Matching Motion Priorsの再現検証

参照モーションを1本もRLに与えないまま、Unitree G1が速度・向きコマンドに追従して走り・旋回し、転倒姿勢から平均1.1秒で立ち上がりました。SMPの公開G1再現実装を手元で学習・実行して確認した結果です。

SMP: Reusable Score-Matching Motion Priors for Physics-Based Character Control(Mu et al., 2025)は、モーション拡散モデルを凍結したまま下流タスクの強化学習に再利用する手法です。元論文はMimicKit上のヒューマノイドキャラクタ向けで、G1の設定は含まれません。これをUnitree G1向けにモーション特徴量・プライアー・タスク・報酬までエンドツーエンドでポートしたのが、オープンソースの再現実装SUZ-tsinghua/smp(大学のコースプロジェクト)です。

本記事は、この実装をGMOの検証環境で実際に学習・実行し、Steering(速度・向き追従)とGet-up(転倒から起立)の2タスクで動作と実測メトリクスを確認した記録です。手法とG1向け実装はそれぞれ元論文・公開リポジトリに帰属し、本記事の独自部分はGMO検証環境での再現と実測にあります。

この記事でわかること

  • SMPの仕組み — 拡散モデルのスコアをSDS報酬として使い、参照モーションデータなしで自然な動きを学習する方法
  • 凍結済みプライアーの正体 — 約0.7Mパラメータの小さなDiT風デノイザーと、50ステップDDPMの中身
  • G1向け再現の構成 — mjlab + PPO + 事前学習済みモーションプライアーのパイプライン(4096並列・50 Hz制御)
  • Steering / Get-upの学習結果 — RTX 5090 Laptop GPUでの実測メトリクスと学習曲線、デモ動画
  • 再現実装と元論文の差分 — 報酬を加算ではなくで合成する(公開リポジトリの)設計判断と、その効果

TL;DR

  • SMPは凍結したモーション拡散モデル(~0.7MのDiT, 50ステップDDPM)をSDS報酬として再利用し、参照データなしで自然な動きを学習できる
  • G1向けのmjlab実装(4タスク)はオープンソースの再現リポジトリSUZ-tsinghua/smpによるもので、本検証ではそれをGMO環境で実際に学習・実行して動作を確認した
  • RTX 5090 Laptop GPU・4096並列で、Steeringは10.7時間、Get-upは11.3時間の学習で収束
  • 収束時の成功率はSteeringが約94%のフル完走、Get-upが約99%の起立成功(いずれも手元の実測値)
  • 報酬を task × r_smp の積で合成する再現実装の設計は、重み調整なしでタスク達成とモーション品質を両立できていた

SMPとは

従来の敵対的模倣学習(AMP / GAIL)は、ポリシーごとに識別器を継続学習し、参照モーションデータへのアクセスを維持する必要があります。SMPはこの制約を外し、次の2点を満たすモジュール型のモーションプライアーを構築します。

  • Modular(モジュール性) — ポリシー学習中に元データセットへアクセス不要
  • Reusable(再利用性) — 一度学習したプライアーを凍結し、複数タスク・複数ポリシーに使い回せる

パイプラインは3段階です。

モーションCSV (LAFAN1 retargeting)
        │
        ▼
DDPM事前学習 (DiT ε予測, ~0.7M)
        │
        ▼
凍結済みプライアー (pretrained_*.pt) ──┐
                                       ▼
       タスク報酬 (速度追従・起立など) ─▶ PPO + SDS報酬 (下流タスク)
                                              │
                                              ▼
                             Unitree G1 MuJoCoシミュレーション
  1. モーションデータからDDPM(Denoising Diffusion Probabilistic Model)を事前学習
  2. 学習済みデノイザーを凍結し、Score Distillation Sampling(SDS)で類似度報酬 r_smp を算出
  3. タスク報酬と組み合わせてPPOでポリシーを学習

SDS報酬の核心は、シミュレーション上のモーションウィンドウ にノイズ ε を加え、凍結済みデノイザーの予測 ε̂ との残差で「参照分布への近さ」を測る点にあります。論文では次の形で定義されます。

ここで ?={8,15,22} は拡散タイムステップの集合(Ensemble Score-Matching)です。単一タイムステップをランダムサンプリングすると報酬分散が大きくなるため、複数ノイズレベルを固定集合で評価して分散を抑えます。再現実装では報酬の鋭さ w_s=6.0 を採用しています。

凍結済みプライアーの中身:~0.7MパラメータのDiT

事前学習済みチェックポイント(pretrained_*.pt)に入っているデノイザーは、一般的な大規模拡散モデルではなく約698,674パラメータ(~0.7M)の小さなTransformerです。チェックポイントの設定値は次のとおりでした。

項目
アーキテクチャ
DiT風 ε予測(PixArt-α adaLN-single条件付け + SwiGLU FFN)
入力
59次元 × 10フレームウィンドウ
d_model / ヘッド数 / ブロック数
128 / 4 / 2
拡散ステップ数
50(Nichol & Dhariwal コサインβスケジュール)
パラメータ数
698,674
事前学習
batch 1024 / lr 3e-4 / EMAなし

注目点は拡散ステップが50しかないことです。標準的な線形βスケジュールは T≈1000 を前提としますが、コサインスケジュールは小さな T でも ᾱ_T≈0 に到達するため、50ステップでGSI(後述)の生成と報酬評価を高速に回せます。デノイザーが小さいことは、SDS報酬が4096環境分のforwardを毎ステップ走らせる本手法にとって実用上重要です(torch.compilemax-autotune でさらに高速化)。

検証環境

項目
GPU
NVIDIA GeForce RTX 5090 Laptop GPU
OS
Ubuntu (Linux 6.8) / CUDA 13.0
Python
3.13.11
並列環境数
4096
シミュレータ
MuJoCo(mjlab
物理 / 制御周波数
200 Hz(timestep 0.005)/ 50 Hz(decimation 4)
学習アルゴリズム
PPO(rsl_rl 5.2.0)
リポジトリ

セットアップ

依存関係は uv で管理されています。事前学習済みプライアーが同梱されているため、拡散モデルの事前学習をスキップしてRLから始められます。

git clone https://github.com/SUZ-tsinghua/smp.git
cd smp
uv sync

同梱されている事前学習チェックポイントは次のとおりです。

チェックポイント
学習データ
使用タスク
pretrained_loco.pt
walk / jog / run
Smp-Forward-G1
pretrained_lafan_run.pt
LAFAN run サブセット
Smp-Steering-G1, Smp-Location-G1
pretrained_getup_f2s2.pt
起立(fall→stand)
Smp-Getup-G1

モーションデータはLAFAN1 Retargeting Datasetg1 スプリット(30 fps, 36列CSV)を使用します。正規化統計 datasets/norm_stats.npz もリポジトリに含まれており、LAFAN全体で計算した広い正規化範囲を使うことが重要です。q01/q99の範囲はチェックポイントに焼き込まれ、凍結済みデノイザーが見る特徴量マッピングそのものになります。狭いデータで再計算すると、RL中にポリシーが分布外に出た際に特徴量が±1へ飽和し、デノイザーのスコア推定(=SMP報酬)が必要な場面で劣化します。

モーション表現とGSI

G1向けの1フレーム特徴量は59次元です。

[root_pos(3), root_rot(6), joint_pos(29), ee_pos(15), root_lin_vel(3), root_ang_vel(3)]

10フレームのスライディングウィンドウを pelvis アンカー・yaw-only ローカル座標で構成し、オンラインで MotionFeatureBuffer が再構築します。最終フレームのyaw方向を基準にするため、特徴量はワールド上の位置・向きに依存しません。

再現実装は論文の Generative State Initialization(GSI) も備えています。実装は3つの工夫で成り立っています。

  • 起動時にプールを生成 — 凍結済みプライアーから50ステップDDPMで4096個のモーションウィンドウを一括サンプリングし、毎リセットの拡散コストを償却する
  • リセット時にプライム — プールから1つのウィンドウを引き、最終フレームでシミュレーション状態を初期化し、10フレームウィンドウ全体でバッファを埋める。これにより r_smp がステップ0から意味を持つ
  • 2400ステップごとにリフレッシュ — プールの1024個のウィンドウをFIFOで新規サンプルに差し替え、初期分布が固定化しないようにする

バッファは「環境原点相対」で保持するため、4096環境がワールド上のどこに配置されても報酬は不変です。Reference State Initialization(RSI)と同様の探索効果を、元データセットなしで得られます。

報酬設計:元論文との意図的な差分

元のMimicKit実装はタスク報酬とSMP報酬を加算し、重みバランス(task_reward_weight : smp_reward_weight)を調整します。対して再現実装(SUZ-tsinghua/smp)は、意図的にで合成します。

def task_smp_product(env, task_terms, fixed_timesteps=(8, 15, 22), ws=6.0):
    # task = Σ wᵢ · taskᵢ(env)
    task = sum(w * func(env, **kw) for func, w, kw in task_terms)
    # r = (Σ wᵢ · taskᵢ) × r_smp
    return task * smp_guidance_reward(env, fixed_timesteps=fixed_timesteps, ws=ws)

積は両方の因子が大きいときだけ大きくなり、どちらかが0に近づくと0へ崩れます。これにより次の2点が同時に得られます。

  • 重み調整が不要 — 加算形式で必要だった task : smp のバランス比(タスクや学習段階ごとに最適点が動く)が消える
  • 片側だけの報酬ハックを防止 — 「自然だがタスクを無視する動き」も「タスクは達成するが不自然な動き」も、どちらも報酬≈0に落ちる

SMP報酬の内部では、各タイムステップのMSEを DiffNormalizer(タイムステップ別のカウントベース移動平均)で正規化しています。EMAと違いカウントが増えると平均が固定されるため、ポリシーの変化に引きずられない安定した基準になります。一方、後述の smp_too_low 終了判定だけは生のMSEを使い、多様体からの絶対距離として閾値を固定しています。

タスク1:Steering(速度・向き追従)

3〜8秒ごとにランダムな移動方向・速度(0.5〜2.0 m/s)・向きコマンドを再サンプリングし、G1に追従させます。

  • タスク報酬0.5 × 速度追従 + 0.5 × 向き整合(速度が目標方向に逆投影されると0)
  • プライアーpretrained_lafan_run.pt(走行モーション)
  • エピソード長 — 20秒(1000ステップ)

学習結果

30,000イテレーション(10.7時間、W&B run: smp_steering_g1)で学習しました。

メトリクス
最終値
Mean reward
5.88
task_smp_product 報酬
0.29
速度誤差 error_vel_xy
1.18 m/s
向き誤差 error_face
0.19
Mean episode length
978.8 / 1000 steps(97.9%)
スループット
~74,565 steps/s
Steeringの学習推移。左:報酬の上昇と速度誤差の低下。右:失敗終了(自己接触・転倒)の収束。速度誤差は探索初期に2 m/sを超える水準まで上がった後1.18へ収束し、自己接触による終了は1000イテレーション以内にほぼ消える。

終了理由の内訳(収束時)は次のとおりです。

終了理由
相対比率
time_out(20秒フル完走)
約94%
self_collision(自己接触)
約6%
base_too_low(転倒, 高さ0.3m未満)
約0%

ほぼ全エピソードが20秒を走り切り、転倒による終了は学習を通じて消滅しました。残る失敗は脚同士の自己接触で、急旋回時にわずかに発生します。

Steeringタスク:ランダムな速度・向きコマンドに追従しながら走行モーションを維持

再生コマンド

uv run scripts/play.py Smp-Steering-G1 \
  --wandb-run-path <org>/smp/<run_id> \
  --num-envs 4

タスク2:Get-up(転倒から起立)

凍結済みプライアー(pretrained_getup_f2s2.pt)からGSIで転倒姿勢を初期化し、5秒以内に起立させます。

  • タスク報酬0.7 × 頭部上向き速度 + 0.3 × 頭部高さ追従
    • 上向き速度は頭部サイトの世界速度(胴体ピッチの ω×r を含む)を使い、頭が0.9m未満のときだけ評価
  • 成功条件 — 頭部高さ ≥ 1.2 m、速度 ≤ 0.5 m/s を25ステップ(0.5秒)維持
  • エピソード長 — 5秒(250ステップ)

smp_too_low:暴れ起立を防ぐ終了判定

Get-upでは「とにかく頭を上げる」暴れ動作が報酬ハックになりがちです。これを防ぐため、生のSMPスコア exp(-ws · raw_err) が閾値0.02を下回ったら(5ステップの猶予後)エピソードを終了します。多様体を離れるとスコアが0へ向かうため、不自然なショートカットは即座に打ち切られます。

学習結果

30,000イテレーション(11.3時間、W&B run: smp_getup_g1)で学習しました。

メトリクス
最終値
Mean reward
0.18
task_smp_product 報酬
0.036
Mean episode length
54.5 steps(≈ 1.09秒で起立確定)
スループット
~69,878 steps/s
Get-upの学習推移。左:報酬と平均エピソード長。右:起立成功の上昇とoff-manifold終了の崩壊。 smp_too_low は序盤に1イテレーション当たり最大~182回発火するが、~5000イテレーションで約1回まで崩壊し、入れ替わるように起立成功が定着する

終了理由の内訳(収束時)は次のとおりです。

終了理由
相対比率
stood_up(起立成功)
約99%
smp_too_low(多様体逸脱)
約0.6%
time_out(5秒で未決着)
0%

収束時にはほぼ全エピソードが起立に成功し、5秒のタイムアウトに達するエピソードは皆無でした。平均エピソード長54.5ステップ(50 Hz制御で約1.09秒)は、転倒姿勢から起き上がって0.5秒静止を確認するまでの時間です。学習の主役は smp_too_low で、序盤は多様体逸脱で大量に打ち切られながら、自然な起立軌道へと収束していきます。

Get-upタスク:転倒姿勢から自然なモーションで起立

再生コマンド

uv run scripts/play.py Smp-Getup-G1 \
  --wandb-run-path <org>/smp/<run_id> \
  --num-envs 4

学習コマンド

# Steering
uv run scripts/train.py Smp-Steering-G1 --env.scene.num-envs=4096

# Get-up
uv run scripts/train.py Smp-Getup-G1 --env.scene.num-envs=4096

チェックポイントは logs/rsl_rl/ 以下に保存され、W&Bプロジェクト smp にも同期されます。

その他のタスク

本リポジトリには以下のタスクも登録されていますが、本記事ではSteeringとGet-upに絞って検証しました。

タスクID
説明
Smp-Forward-G1
+x方向への速度コマンド追従(0.5〜5.0 m/s)
Smp-Location-G1
ワールド座標のxy目標地点への歩行

Sim2Realに向けたドメインランダム化

実機展開を見据え、共有環境には起動時・実行時のランダム化が組み込まれています。

項目
範囲
足の摩擦係数
0.3〜1.2(左右の足ジオメトリで共有)
エンコーダバイアス
±0.015 rad
胴体COMオフセット
xy ±0.025 m / z ±0.03 m
ランダムプッシュ
1〜3秒間隔、並進 ±0.5 m/s・ヨー最大 ±0.78 rad/s

観測はasymmetric actor-criticで、actorはノイズ付き観測(base速度・角速度・重力投影・関節角・関節速度・前回行動)のみを受け取り、criticはノイズなし・履歴10フレームを参照します。

所感と今後

SMPの最大の利点は、モーションプライアーを一度学習すれば参照データなしで複数タスクに転用できる点です。AMPのようにポリシーごとに識別器を再学習する必要がなく、~0.7Mと小さいプライアーのおかげで4096環境分のSDS報酬を毎ステップ回しても実用的な速度(~70k steps/s)が出ます。

報酬の積合成(task × r_smp)は、タスク達成とモーション品質の両立をシンプルに促せました。特にGet-upでは、smp_too_low による多様体逸脱の打ち切りが学習の主要な信号として働き、序盤の暴れ起立を抑えながら自然な軌道へ収束させています。閾値(0.02)と猶予ステップ(5)の設定は学習安定性に効くため、難タスクではここがチューニングの勘所になります。

本検証はシミュレーション上の再現です。実機G1へのデプロイ(Sim2Real)については、上記のドメインランダム化が既に組み込まれているものの、追加検証が必要です。

参考リンク

採用についてお知らせ

GMO AI&ロボティクス商事(GMO AIR)では、フィジカルAI・ヒューマノイドロボティクス分野のリサーチエンジニアリサーチサイエンティストを募集しています。

Unitree G1などの最先端ヒューマノイド実機を用いた全身制御・動作学習の研究から、お客様先への導入という社会実装まで幅広く携われるポジションです。

ご興味のある方は募集職種一覧よりご確認のうえ、ぜひご応募ください。

ブログの著者欄

常盤晟

常盤 晟

GMOインターネットグループ株式会社 グループ研究開発本部 AI研究開発室


2025年に新卒としてGMOインターネットグループ株式会社に入社。現在はGMO AIRに参画し、AIxロボティクスの研究開発に従事。理学博士(物理学)

当サイトでは利便性の向上を目的にクッキーを使用しています。クッキーポリシーの詳細は こちら をご覧ください。

拒否