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?

macOSでSO-101双腕アームを組んだ:泥臭い記録

0
Last updated at Posted at 2026-06-22

HuggingFace の LeRobot × SO-101 双腕アームを、macOS 上で組み立て&セットアップしてテレオペが動くところまでやりました。

手順そのものは ABEJA さんの記事(SO-101 組み立てレポート v2)が非常に丁寧なので、そちらと公式チュートリアルを併読してください。この記事は実際にやってみて踏んだ罠と、その診断・復旧の記録に絞ります。

補足:作業は Claude Code(Anthropic の CLI コーディングエージェント)に伴走してもらいながら進めました。ハマるたびにエラーログを貼って一緒に切り分けた記録なので、内容は実機で再現したものです。

リポジトリ:yutoAb/so101-lab

完成したもの

リーダー(手前、人間の手で握って動かす)→ フォロワー(奥)が追従する状態まで持っていきました。

テレオペデモ

リーダー(手前)を手で動かすとフォロワー(奥)が追従する

全体像

LeRobot × SO-101 のセットアップは以下の順序依存パイプラインで進みます。前段が生成する値(USB ポート、キャリブレーションファイル等)を後段が消費するので、順番を飛ばせません。

この記事は install 〜 teleoperate(黄色+緑のところ)までの実機ログです。

tl;dr:踏んだ罠 5 連発

# 症状 根本原因 直し方
1 USB ポート探し lerobot-find-portEOFError で落ちる 非対話シェルで input() が読めない ls /dev/tty.usbmodem* の差分法で代用
2 サーボ取り違え リーダー用(混在ギア比)のサーボをフォロワーフレームに組み込んでしまった 組立順の勘違い SO-101 はフレーム共通なので、組んだ方を「リーダー」に再定義 + グリッパー → ハンドル換装
3 キャリブ失敗 Missing motor IDs: 3, 4, 5, 6 デイジーチェーン断線(m2 と m3 の間のケーブル) 該当箇所のケーブルを差し直し
4 キャリブ表示で誤解 POSMINMAX に張り付いていて「校正失敗?」と焦った POS はホーム位置ではなく「可動域スキャン終了時の位置」 仕様。ホーム位置は別途保存されている
5 テレオペ初回失敗 [TxRxResult] Incorrect status packet! 一過性の通信エラー リトライで通る

環境

  • macOS 15.x(Apple Silicon)
  • Python 3.12(uv 管理)
  • lerobot==0.5.1uv add 'lerobot[feetech]'
  • SO-101 双腕キット(リーダー × 1 + フォロワー × 1)

ABEJA さんの記事だと「Windows 限定でファームウェア更新が必須」と書かれていますが、Mac では工場出荷ファームのままキャリブレーション・テレオペまで通りました(少なくとも今回購入したロットでは)。Mac ユーザーは、まず更新せずにキャリブまで走らせてみて、エラーが出てから対処すれば良さそうです。

罠 1: lerobot-find-port が対話 CLI で詰む

USB ポートの判定(リーダー基板はどっち?フォロワー基板はどっち?)は、公式チュートリアルだと lerobot-find-port という対話 CLI を使います。「片方の USB を抜いて Enter」のような流れで、消えたポートをそのアーム用と判定する設計。

これが非対話的なシェル(Claude Code の bash、CI、リモートシェル等)で動かすと EOFError で落ちます。input() を呼ぶので当然なんですが、結構詰まりました。

回避策:ls の差分法

両方つないだ状態で:

$ ls /dev/tty.usbmodem*
/dev/tty.usbmodem5B420772061
/dev/tty.usbmodem5B420772561

片方(リーダー側)の USB だけ抜く:

$ ls /dev/tty.usbmodem*
/dev/tty.usbmodem5B420772061   # 残った方 = フォロワー
                               # 消えた方 = リーダー

lerobot-find-port がやっているのは結局これなので、対話 CLI に頼らなくても十分です。

罠 2: サーボ取り違え事件

SO-101 はリーダー腕 6 軸でギア比が混在しています:

関節 リーダーのギア比
shoulder_pan 1:191
shoulder_lift 1:345
elbow_flex 1:191
wrist_flex 1:147
wrist_roll 1:147
gripper 1:147

一方でフォロワー腕は全 6 軸が 1:345で揃っています。

なぜリーダーだけ混在なのか

簡単に言うと「人間が手で動かす(=バックドライバビリティが必要)」からです。

  • 高いギア比(1:345)= 高トルク・バックドライブが重い
  • 低いギア比(1:147)= 低トルク・バックドライブが軽い

リーダーは人間が手で動かして教示します。全部 1:345 にすると重くてギクシャクしか動かず、教示データの品質が落ちます。逆に全部 1:147 にすると、手を離した瞬間に自重で崩れ落ちる。なので自重を支える必要がある shoulder_lift だけ 1:345、shoulder_pan と elbow_flex は中庸の 1:191、手首・グリッパーは軽い 1:147、というバランス設計になっています。

何が起きたか

Phase 3(モーター ID 書き込み)でリーダー用サーボに ID 1〜6 を書いた後、そのままフォロワー用のフレームに組み付けてしまった。半分組み立てた段階で「あれ、ギア比が混在してる側はリーダーだったよな…?」と気付きました。

SO-101 のサーボはアーム内部にケーブルが通されているので、組み立て後にやり直すには分解が必要。終わった、と思いました。(サーボは一度取り付けたら、硬すぎてもう取れません😭)

救済策:組んだ方を「リーダー」と再定義する

ここで救いだったのが、SO-101 のリーダーとフォロワーはフレームの 3D プリント部品が共通で、違うのは末端だけ:

  • フォロワー: 末端にグリッパー(物を掴む爪)
  • リーダー: 末端にハンドル(人間が握る取手)

つまり、「フォロワーとして組んだ腕」を「リーダー」と再定義して、グリッパーをハンドルに換装すれば成立する。あとは残りのフレーム + 残ったフォロワー用サーボ(全 1:345)でフォロワーを組み直す

幸い、キットにはもう 1 セット分の 3D プリント部品とハンドルパーツが残っていたので、これで完了。

教訓

  • 組立にサーボを 1 個ずつトレイに並べて「これはリーダーの shoulder_pan(1:191)、これは…」とラベリングする
  • ギア比が混在するパーツは紙テープに関節名を書いて貼る
  • 公式手順の「組立前に ID を書き込め」は、組立後に分解不可能だからこそ厳守

罠 3: キャリブで Missing motor IDs(デイジーチェーン断線)

組み直したリーダーでキャリブレーションを実行:

$ bash scripts/calibrate.sh
==> Calibrating leader arm...
RuntimeError: FeetechMotorsBus motor check failed on port '/dev/tty.usbmodem5B420772561':

Missing motor IDs:
  - 3 (expected model: 777)
  - 4 (expected model: 777)
  - 5 (expected model: 777)
  - 6 (expected model: 777)

Full found motor list (id: model_number):
{1: 777, 2: 777}

ID 1, 2(shoulder_pan, shoulder_lift)は見えるのに、3〜6(elbow_flex 以降)が全滅。

これは典型的なデイジーチェーン切断です。Feetech STS3215 は 3 線バス通信で、サーボが数珠つなぎになっています:

m2 と m3 の間のサーボバスケーブルを物理確認したら、コネクタが半挿しで抜けかけていました。差し直して再実行 → 全 ID 認識 OK。

教訓

  • エラーメッセージの「どの ID が見えて、どの ID が消えているか」で断線箇所が一意に特定できる
  • 組立中、関節をぐりぐり動かすとコネクタが緩む可能性がある(アーム内部で挟まれることもある)
  • 全 ID 消えていれば「電源 or 基板からの 1 本目」、途中から消えていれば「その境目のケーブル」

罠 4: キャリブの POS 値で誤解しかけた話

フォロワーのキャリブ結果が以下のように表示されました:

NAME            |    MIN |    POS |    MAX
shoulder_pan    |    679 |   1953 |   3285
shoulder_lift   |    885 |    904 |   3270    ← POS が MIN にほぼ張り付き
elbow_flex      |    893 |   3120 |   3134    ← POS が MAX にほぼ張り付き
wrist_flex      |    804 |   2838 |   3162
gripper         |   1988 |   1997 |   3481    ← POS が MIN にほぼ張り付き

最初「POS がホーム位置なら、いくつかの関節が可動域の端に張り付いている = ホーム位置がおかしい」と思って焦りました。

実際には、POS は最初に Enter を押した時のホーム位置ではなく、「可動域スキャンを Enter で止めた瞬間の現在位置」。可動域スキャンの最後にどこかの関節を端っこまで動かして Enter を押すと、その関節の POS は MIN や MAX に張り付きます。ホーム位置は別途保存されているので、これで全く問題ありません。

LeRobot 側で表示文言を変えてくれると嬉しいですが、現状はそういう仕様だと知っておくと精神衛生上良いです。

罠 5: テレオペ初回の Incorrect status packet!

キャリブが終わって、いよいよテレオペ。

$ bash scripts/teleoperate.sh
INFO so_leader.py:78 my_leader SOLeader connected.
INFO follower.py:105 my_follower SOFollower connected.

ConnectionError: Failed to sync read 'Present_Position' on ids=[1, 2, 3, 4, 5, 6]
after 1 tries. [TxRxResult] Incorrect status packet!

接続のハンドシェイクは通る(=サーボは見えている)のに、関節位置の読み取りでパケットが化けたエラー。「またデイジーチェーンか…」と思って物理確認しようとしたんですが、ダメ元でもう一度実行したら何事もなく動きました

$ bash scripts/teleoperate.sh
[警告は出るが、その後は正常動作]

たぶん起動直後の初期化タイミングで、まれにバスがノイズを拾ったとかその辺。再現性が無いタイプのエラーは、原因究明より「もう一回やる」の方が早いことがあります(ただし常時再現するなら配線を疑う)。

副産物:uv run ベースの bash スクリプト構成

毎回 lerobot-* の長いコマンドをコピペするのが嫌だったので、薄いラッパー bash スクリプト集にしました。

scripts/
  _env.sh              # USB ポート、HF トークン、データセット名等を集約(.gitignore)
  _env.sh.example      # テンプレ
  install.sh           # uv sync + HF auth チェック
  find-port.sh         # lerobot-find-port のラッパー(対話 CLI)
  setup-motors-follower.sh
  setup-motors-leader.sh
  calibrate.sh         # 両アームのキャリブを連続実行
  teleoperate.sh
  record.sh            # データ収集(リーダー操縦 + カメラ + Hub push)
  train.sh             # ポリシー学習
  eval.sh              # 学習済みポリシーで自律推論

ポイント:

  • 環境変数は scripts/_env.sh に集約.gitignore 対象。HF トークン・USB パス含む)
  • 全スクリプトが uv run lerobot-* を呼ぶ(pip/venv 不要)
  • タスク切り替えは _env.shTASK_NAME を書き換えるだけ
  • record.sheval.sh は同じ lerobot-record を呼ぶ:違いは --teleop.*(人間操縦)か --policy.path(学習済みポリシー)か

例えば calibrate.sh は:

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/_env.sh"
cd "$SCRIPT_DIR/.."

echo "==> Calibrating follower arm..."
uv run lerobot-calibrate \
    --robot.type=so101_follower \
    --robot.port="$FOLLOWER_PORT" \
    --robot.id="$FOLLOWER_ID"

echo "==> Calibrating leader arm..."
uv run lerobot-calibrate \
    --teleop.type=so101_leader \
    --teleop.port="$LEADER_PORT" \
    --teleop.id="$LEADER_ID"

echo "==> Done. Next: bash scripts/teleoperate.sh"

lerobot-calibrate 単体の CLI を毎回叩くより、bash scripts/calibrate.sh の方が頭を使わなくて済みます。リポジトリ全体は yutoAb/so101-lab にあります。

次にやること

ここまでで Phase 1〜6(インストール → ポート判定 → モーター設定 → 組立 → キャリブ → テレオペ動作確認)完了。次は:

  • Phase 7: カメラ(Logitech C920n を発注済み)で record.sh を実行 → HF Hub にデータセット push
  • Phase 8: Colab か手元 GPU で train.sh → ポリシー学習
  • Phase 9: eval.sh で学習済みポリシーを自律実行

カメラが届いて Phase 7 以降を回せたら、続編を書く予定です。

まとめ

  • 公式手順の通り進めれば理屈上は詰まらないが、実機では泥臭いトラブルが連発する
  • 一番怖いのは組立後にやり直しが効かない作業(サーボ ID 書き込み、ケーブリング)。組立前にラベリングを徹底
  • エラーメッセージは正直Missing motor IDs: 3, 4, 5, 6 は「2 と 3 の間で切れている」と明示的に教えてくれている
  • 再現性のないエラーはリトライしてから原因究明(ただし常時再現は配線を疑う)
  • macOS では現状ファームウェア更新なしでも完走可能

参考

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?