tl;dr
- 「模倣学習めんどくさいので、あなた(Claude Code)がフォロワー動かしてみて」をやってみた実験
- Claude が LeRobot の Python API でフォロワーアームを直接制御、カメラ画像を見ながら関節角を指令する「超低速テレオペ」を実施
- ブロックを掴むのに成功したのは十数回試行して 1 回。しかも運搬中に落とした
- ハマった罠:単眼カメラの奥行き喪失 / 電源電圧 4.6V 問題 / P 制御の定常偏差 / ブラウンアウトでサーボ脱落
- 結論:この全部を人間の小脳は無料で解いている。だから模倣学習をやる
前回(組立編)の続きです。SO-101 の組立とテレオペ確認が終わり、カメラも到着。次は本来 lerobot-record でデータ収集なのですが、「学習なしで、LLM に直接アームを操縦させたらどこまでできるのか」を試しました。結果、模倣学習パイプラインの存在意義を理解できた、という記録です。
実験のセットアップ
やり方は原始的です。Claude Code に以下のループを回させます。
- カメラでフレームを撮る → Claude が画像を「見る」
- 関節角のデルタを決めて Python API で送る
- また撮って確認 → 修正 → …
制御スクリプトの本体はこれだけ(抜粋):
from lerobot.robots.so_follower import SOFollower, SO101FollowerConfig
cfg = SO101FollowerConfig(
port=os.environ["FOLLOWER_PORT"],
id=os.environ["FOLLOWER_ID"],
)
robot = SOFollower(cfg)
robot.connect(calibrate=False)
# 現在姿勢を読んで、目標へ線形補間でゆっくり移動
pose = {k: v for k, v in robot.get_observation().items() if k.endswith(".pos")}
target = {**pose, "shoulder_pan.pos": pose["shoulder_pan.pos"] - 5}
for i in range(1, n + 1):
a = i / n
robot.send_action({k: pose[k] + (target[k] - pose[k]) * a for k in pose})
time.sleep(1 / 30)
1 サイクル(撮影 → 判断 → 移動)に数秒〜十数秒かかります。人間のテレオペが 30Hz で回すループを、0.1Hz 以下で回すイメージです。この時点で勝負はだいたい決まっているのですが、実際にやると予想と違う場所で詰まりました。
Trap 1: 単眼カメラは奥行きが分からない
最初は横から作業エリアを映す固定カメラ(Logicool C920n)1 台でスタート。
この画像、グリッパがオレンジのブロックの真上にいるように見えますよね。実際は奥行き方向に 5cm ズレていました。画面上で重なって見えても、カメラの視線方向の前後ズレは 1 枚の画像からは原理的に判別できない。頭では知っていましたが、「閉じたのに掴めてない」を 4 連発してようやく腹落ちしました。
苦肉の策として編み出したのが接触プロービング:
指先をテーブル面まで下ろして、pan 軸で左右に掃く。ブロックが動けば同じ奥行きにいる、動かなければズレている
物理接触をセンサ代わりにする、ソナーみたいな手法です。泥臭い。
Trap 2: elbow が動かない → 電圧を読んだら 4.6V だった
途中、elbow_flex だけがコマンドを無視する現象が発生。ケーブル断線を疑ってサーボのレジスタを直接読んだら:
motor temp volt load
shoulder_pan 27C 4.6V 0
shoulder_lift 24C 4.5V 0
elbow_flex 25C 4.6V 200 ← 20% 負荷で釣り合って停止
全サーボが 4.6V 駆動。STS3215 の定格は 7.4V なので、トルクが半分近くまで落ちていました。付属の AC アダプタを確認したら 5V 4A。elbow は姿勢的に一番重力負荷が大きい関節なので、真っ先に「動けなく」なったわけです。
さらに終盤、グリッパのストール電流が重なった瞬間に電圧がしきい値を割り、Input voltage error! → サーボがバスから脱落という事件も発生しました(電源再投入で復帰)。掴んだ瞬間に指の力が抜けるロボット、切ない。
Trap 3: 「ゆっくり動かすほど動かない」— P 制御の定常偏差
安全のため「1 ステップの目標変化を 3 度以内にクランプ」して動かしていたのですが、これが裏目に出ました。
Feetech サーボの位置制御は P 制御です。出力トルク ∝ P ゲイン × 位置誤差。つまり:
- 目標を現在位置のすぐ近くに置く(誤差小)→ トルクが出ない → 重力・摩擦に負けて動かない
- 人間のテレオペは操作が雑(誤差大)→ トルクが出る → 動く
「丁寧に動かそうとするほど動けない」という逆説。実測すると、指令 5 度に対して実際は 2 度しか動かない場面もありました。レジスタを見たら P ゲインが 16(標準の半分)だったので 64 に上げたところ追従は劇的に改善——それでも最後の数 mm を外し続けました。
手首カメラ投入で「あと数 mm」まで来た
奥行き問題の根本対策として、Innomaker の小型 UVC カメラをフォロワーの手首にマウント。
手首視点だと「ブロックが指の間に入っているか」が直接見えるので、横視点の奥行き詐欺がほぼ消えます。実際、手首カメラ導入後の 1 回目のアプローチで掴めました。紫ブロックがジョーの間に挟まって持ち上がった瞬間は、正直ガッツポーズしました。
……が、カップへの運搬中に落としました。開口部でブロックの端 1 キューブをつまむ浅い把持だったので、旋回の慣性に耐えられなかった。
その後の掴み直しは「あと数 mm」の無限ループに:
P ゲインの定常偏差で指令の半分しか動かない → オーバーシュート指令で補償 → 今度は行き過ぎてブロックを小突いて回転させる → 振り出しへ。最終的にこの日は把持成功 1 回 / 試行十数回で諦めました。
学び:この全部を、人間の小脳は無料でやっている
今日ぶつかった壁を並べると、そのまま「模倣学習が解いている問題リスト」になります。
| 直接制御でぶつかった壁 | 模倣学習ではどうなるか |
|---|---|
| 単眼で奥行きが分からない | 人間がテレオペで奥行きを解決。policy はその視覚→行動の対応を学ぶ |
| 制御ループが 0.1Hz | 30Hz のリアルタイム制御 |
| P 制御の定常偏差で数 mm 外す | 人間が無意識に偏差込みで補正した軌跡がそのまま教師データになる |
| 「あと 5mm」の連続的な視覚フィードバックができない | 視覚サーボ自体が policy に焼き付く |
人間がリーダーアームを握って操作するとき、奥行き推定も偏差補正も慣性の予測も全部無意識にやっています。lerobot-record はその無意識を丸ごとデータ化する仕組みで、ACT はそれを 30Hz で再生できる形に蒸留する。「テレオペのデモを 50 本録るだけ」の裏側にはこれだけの問題が畳み込まれている——というのを、遠回りして理解しました。
副産物もありました:
- 手首カメラが常設になった。front + wrist の 2 カメラ構成は ACT の成功率を上げる定番。データ収集の準備としてはむしろ前進
- 電源が 5V でトルク不足という事実の発見。データ収集時の安定性にも関わるので、7.4V 系電源の調達を検討中
- P ゲイン 16 問題の発見。テレオペの追従性チューニングの余地あり
次にやること
- 剛体ターゲット(レゴ or 木製キューブ)で
lerobot-record、50 エピソード収集 - ACT を学習(ローカルに CUDA がないので Colab か研究室 GPU)
-
lerobot-record --policy.path=...で自律実行——今日 Claude が 1 時間かけて 1 回しか成功しなかったタスクを、30Hz の policy がどれだけあっさり解くのかを見届ける
参考
- LeRobot
- SO-101 (TheRobotStudio)
- 前回:macOS で SO-101 双腕アームを組んだ:サーボ取り違え事件と泥臭いリカバリの全記録
- 作業リポジトリ: yutoAb/so101-lab



