0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SO-101 で模倣学習、「実機で動かないポリシー」をデバッグして 60% まで動かしてみた

0
Last updated at Posted at 2026-07-10

tl;dr

  • 前回「学習なしで Claude に直接操縦させたら 1 時間で 1 回しか掴めなかった」の続き。今度は真っ当に lerobot-record → ACT 学習 → 実機評価 をやった
  • が、一発では動かなかった。実機評価でアームが完全静止(0/10)
  • 原因は学習データのフロントカメラに操作者本人が写り込んでいたこと。ACT が「人・手」ごと過学習し、人がいない評価シーンを「未知」と判断して固まっていた
  • 人を画角から外して録り直したら 50 エピソードで成功率 60%
  • ところが翌朝また数 cm 空振りするように。replay で「ロボットは無実」を証明し、開始姿勢のズレ・制御ループ 10Hz 落ち・キューブ位置が学習分布外、と順に潰した
  • 教訓:模倣学習が動かない時、犯人はモデルではなくデータと運用にいる

前回(Claude に SO-101 を「学習なし」で直接操縦させたら…)は「だから模倣学習をやる」で終わりました。その続きです。結論から言うと、模倣学習は動きました。ただしその過程は「録って学習したら動いた」ではなく、実機で動かない理由を一つずつ潰すデバッグ作業でした。

まず、最終的に動いた自律実行がこれです。学習済み ACT が自分でブロックを掴んでカップに入れています。

学習済み ACT による自律実行(掴んでカップに入れる)

ここに辿り着くまでの落とし穴を、同じ沼にハマる人のためにまとめてみました。

パイプライン

やることは LeRobot の標準的な流れです。

record.sh   … リーダーで操縦、フォロワーの軌跡 + カメラ映像を記録 → HF Hub
train.sh    … ACT を学習(GPU)→ 学習済みポリシーを HF Hub へ
eval.sh     … record から teleop を抜き --policy.path を渡して自律実行
  • ロボット:SO-101 双腕(今回はフォロワー側 6 軸で作業)
  • カメラ:正面 C920 + 手首マウントの UVC カメラの 2 台
  • タスク:紙粘土/ブロックを掴んで透明カップ(綿棒ケース)に入れる
  • ポリシー:ACT(Action Chunking Transformer, ResNet18 バックボーン)
  • 学習:RTX A6000、100k step で約 10.7 時間

学習 loss はきれいに下がります。ただし後で痛感しますが、この曲線がいくら下がっても実機で動くとは限りません

ACT の学習損失曲線(50 エピソード, A6000, 100k step)

第 1 の壁:実機評価でアームが完全に静止する(0/10)

50 本近くデータを録って ACT を学習。学習 loss は 0.022 まで下がった。意気揚々と eval.sh を回すと——アームがピクリとも動かない。10 エピソード全部、フォロワーは静止したまま。

まず切り分ける

学習 loss は下がっているのにポリシーが動かない。犯人は「ハード」「データ」「モデル」「推論」のどれか。順に潰します。

① ハードは動くか → Python API で直接 +10° の指令を送る。実測 +9.2°アームは動ける。 ハードは無実。

② 学習データはちゃんと動いているか → 全エピソードの action の振れ幅を集計。shoulder_lift 平均 114°、elbow 129°、gripper も 40/43 本で開閉。教師データは本物のピック動作。 データの「中身」は無実。

③ モデルは推論できるか(オフライン) → 学習画像をポリシーに食わせて予測 action を見る。ここで最初の落とし穴

lerobot 0.5.1 では正規化(mean/std)がモデル本体ではなく別ファイルpolicy_preprocessor_..._normalizer_processor.safetensors / postprocessor)に分離されている。ACTPolicy.from_pretrained() だけ呼んで select_action すると正規化が効かず出力が 0 付近に固まり、「モデルが壊れている」と誤診する。

make_pre_post_processors で pre/post を通したら、教師画像に対しては誤差 1〜3° で軌跡を再現した。モデルは正常。

犯人は「教師データの見た目」だった

ハードもデータもモデルも正常。なのに実機だけ動かない。差は「評価時に実際に写っている画」だけ。学習フレームと評価フレームを並べたら一発でした。

  • 学習時:フロントカメラに操作者本人(体・手)が毎フレーム写り込んでいた(テレオペ中、カメラの前に座っていた)
  • 評価時:人がいない、裸のテーブル

学習フレームの Before/After:左は操作者が写り込んだ v1、右は人を画角外にした v2(人物は匿名化)

50 本程度の ACT はこの「人・手」をシーンの一部として過学習していて、それが消えた評価シーンを「未知」と判断し、平均姿勢=静止に潰れていたのです。テレオペ中、自分がカメラに写っていた——ただそれだけのことでした。

録り直したら 60%

対策は単純。フロントカメラの画角に操作者を入れない(カメラの真横に座る)。ついでに 2 つ改善:

  • リセット時間を でスキップせず、10 秒使ってキューブ再配置+アームを毎回同じホーム姿勢へ戻す(開始姿勢の標準偏差が 46° → 0.2° に激減)
  • タスク実演は「Recording episode N」表示後だけ(リセット中は記録されない)

50 エピソード録り直して再学習。学習半分(50k step)の checkpoint で評価したら——成功率 6/10。前回 0/10 だったのが嘘のよう。データを直しただけで、モデルは同じ ACT です。

第 2 の壁:翌朝、また数 cm 空振りする

100k まで学習を回し、翌朝に完成版を評価。ところが掴む手前で数 cm ずれて空振り。しかも 50k に戻しても失敗。昨夜 60% だったモデルが今朝は連続で外す。確率的にありえない(0.4^6 ≒ 0.4%)ので、昨夜と今朝で何かが変わったはず。

順に潰します。

開始姿勢のズレ → 手でホームに戻していたが、6 軸を手で合わせるのは無理で、特に手首の向き(flex +10°, roll -15°)がズレていた。手首カメラは手首に付いているので、この向きのズレで「前方が見える」→「真下しか見えない」に激変し、位置合わせが数 cm 狂う。
開始姿勢へ正確に移動するスクリプト(go_home.py を書いて解決。トルクを保持したまま切断し、そのまま eval に入れるようにした。

制御ループが 10Hz に落ちていた → ログに Record loop is running slower (10.8 Hz)。犯人は --display_data=true の rerun ライブ表示で CPU を 176% 食っていた。ACT は 100 ステップのチャンクを開ループ実行するので、10Hz だと 1 エピソード中ほぼ 1 回しか再計画せず、初期の奥行き誤差が最後まで補正されない。
display_data=false(評価は実機を目で見る)で解決。

それでも数 cm 外す。ロボットが壊れた? ここで効いたのが lerobot-replay

lerobot-replay学習データの関節指令をそのまま再生する(ポリシーもカメラも介在しない)。同じ位置にキューブを置いて再生し、掴めれば「ロボットは正常」、外せば「機械/キャリブレーションがズレた」と切り分けられる。

replay したらきれいに掴んでカップに入れたロボットは無実。 つまり機械もキャリブも正常で、問題はポリシー/知覚側。

結論:キューブが学習分布の外だった

replay で「確実に掴める」と分かった位置に、今度はポリシー(eval)でキューブを置いて実行 → 掴めた

犯人が確定しました。今朝バラバラに置いていたキューブ位置が、50 本の学習データがカバーしていない領域だったのです。ACT は「学習した一番近い軌跡」を再生するので、手薄な位置では系統的に数 cm ずれる。加えてループ 10Hz 落ちが補正を弱めていた。モデルもロボットも正常で、運用(置く位置・表示設定・開始姿勢)の問題でした。

教訓:模倣学習が動かない時のチェックリスト

今回ハマった順に、そのまま切り分け手順になります。

症状 疑うもの 切り分け方
実機で完全静止 正規化が効いていない誤診 / データの見た目 pre/post processor を通してオフライン推論。学習フレームに余計なもの(人・手)が写っていないか目視
数 cm 系統的に空振り 開始姿勢・カメラ位置のズレ 開始関節角をスクリプトで統一。手首カメラの見え方を学習時と比較
動きがカクつく/補正が効かない 制御ループ速度 ログの Hz を確認。display_data=false、重いプロセスを止める
ロボットが原因か知りたい 機械/キャリブ lerobot-replay で教師指令を再生。掴めればロボットは無実
特定位置だけ外す 学習データの位置カバレッジ不足 学習分布内の位置で成功率を測る。足りなければデータ拡張

一番の学びはこれです。模倣学習が動かない時、犯人はたいていモデルではなくデータと運用にいる。 loss は下がるし、オフラインでは軌跡を再現する。それでも実機では、カメラに写った自分の手ひとつ、手首の 10° のズレひとつで止まる。人間が無意識に吸収している「同じ環境・同じ構え」を、ロボットには全部お膳立てしてやる必要がある。

副産物:使えるようになった道具

  • go_home.py:フォロワーを学習開始姿勢へ正確に移動。評価の再現性が跳ね上がる
  • lerobot-replay の使い所:ハードとポリシーを切り分ける最強の一手
  • HF_HUB_DISABLE_XET=1:回線によっては新転送バックエンドがハングする。旧方式で 100kB/s → 6.5MB/s に改善

次にやること

50 本は「実証」には十分でしたが、掴める範囲を広げるにはデータ量です。

  1. キューブ位置・向きをもっと広く振って 100〜200 エピソード収集
  2. 4.6V 電源だとブラウンアウトが頻発するので 7.4V 系電源を調達
  3. 100k 版の成功率を学習分布内で正当に測定 → データ拡張の効果を定量比較

「テレオペのデモを録るだけ」の裏に、これだけの落とし穴が畳み込まれている——というのを、また遠回りして理解しました。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?