メインコンテンツまでスキップ
ステージ 3 · 第 17 章 · 選択科目

17. SO-ARM 双腕訓練

1 本のアームから 2 本へ拡張します:4 本のアームを配線し、左右のリーダー・フォロワーペアをキャリブレーションし、bi_so_follower / bi_so_leader でテレオペレーションとデータ録画、ACT を訓練し、双腕ポリシーを展開します。

必要なものSO-ARM キット 2 式(アーム 4 本:リーダー 2 + フォロワー 2)。第 5〜16 章と同じソフトウェア環境。両手が必要なタスク — 例:左アームから右アームへの物体受け渡し。

17.1 なぜ双腕か​

動機

17.1 なぜ双腕か

実際の操作タスクの多くは本物の「両手」を要します:把持と受け渡し、箱折り、2 部品の組立。双腕模倣学習はまさに ACT が生まれた設定(ALOHA)であり、SO-ARM エコシステムはネイティブ対応 — LeRobot には bi_so_follower と bi_so_leader が組み込まれ、2 本のアームを 12 次元アクション(各アーム 6)の 1 台のロボットとして扱います。

第 8〜16 章で学んだことはすべて移転します。変わるのはハードウェアが倍増することと、すべてのコマンドに left_arm_config / right_arm_config が付くことだけです。

17.2 配線とポート​

配線

17.2 配線とポート

4 枚の制御ボードを接続し、各アームのポートを記録します:

役割ポート
左フォロワーアーム/dev/ttyACM0
右フォロワーアーム/dev/ttyACM1
左リーダーアーム/dev/ttyACM2
右リーダーアーム/dev/ttyACM3

キャリブレーション時のフォロワーは so101_follower、リーダーは so101_leader(LeRobot では so100_leader と so101_leader は同一実装)。

4 ポートすべてに権限を付与:

sudo chmod 666 /dev/ttyACM0 /dev/ttyACM1 /dev/ttyACM2 /dev/ttyACM3

17.3 キャリブレーション(重要ステップ)​

キャリブレーション

17.3 キャリブレーション(重要ステップ)

第 10 章の手順で各アームを個別にキャリブレーションします。id には必ず _left / _right 接尾辞を付けます — 双腕タイプはこの接尾辞付きのキャリブレーションファイルを探します。

左フォロワー:

lerobot-calibrate \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0 \
--robot.id=my_awesome_bimanual_follower_left

右フォロワー:

lerobot-calibrate \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM1 \
--robot.id=my_awesome_bimanual_follower_right

左リーダー:

lerobot-calibrate \
--teleop.type=so101_leader \
--teleop.port=/dev/ttyACM2 \
--teleop.id=my_awesome_bimanual_leader_left

右リーダー:

lerobot-calibrate \
--teleop.type=so101_leader \
--teleop.port=/dev/ttyACM3 \
--teleop.id=my_awesome_bimanual_leader_right

キャリブレーション後、ファイルは次に保存されます:

~/.cache/huggingface/lerobot/calibration/robots/so101_follower/my_awesome_bimanual_follower_left.json
~/.cache/huggingface/lerobot/calibration/robots/so101_follower/my_awesome_bimanual_follower_right.json
~/.cache/huggingface/lerobot/calibration/robots/so101_leader/my_awesome_bimanual_leader_left.json
~/.cache/huggingface/lerobot/calibration/robots/so101_leader/my_awesome_bimanual_leader_right.json
ヒント

すでに別の id でキャリブレーション済み? 物理手順をやり直さず、キャリブレーションファイルをコピーできます:

CAL_DIR=~/.cache/huggingface/lerobot/calibration/robots

cp $CAL_DIR/so101_follower/my_awesome_follower_arm1.json \
$CAL_DIR/so101_follower/my_awesome_bimanual_follower_left.json

cp $CAL_DIR/so101_follower/my_awesome_follower_arm2.json \
$CAL_DIR/so101_follower/my_awesome_bimanual_follower_right.json

cp $CAL_DIR/so101_leader/my_awesome_leader_arm3.json \
$CAL_DIR/so101_leader/my_awesome_bimanual_leader_left.json

cp $CAL_DIR/so101_leader/my_awesome_leader_arm4.json \
$CAL_DIR/so101_leader/my_awesome_bimanual_leader_right.json

17.4 双腕テレオペレーション​

テレオペ

17.4 双腕テレオペレーション

カメラなし:

lerobot-teleoperate \
--robot.type=bi_so_follower \
--robot.left_arm_config.port=/dev/ttyACM0 \
--robot.right_arm_config.port=/dev/ttyACM1 \
--robot.id=my_awesome_bimanual_follower \
--teleop.type=bi_so_leader \
--teleop.left_arm_config.port=/dev/ttyACM2 \
--teleop.right_arm_config.port=/dev/ttyACM3 \
--teleop.id=my_awesome_bimanual_leader \
--display_data=true

リストカメラ付き(lerobot-find-cameras opencv でインデックスを確認、カメラは追加・削除自由):

lerobot-teleoperate \
--robot.type=bi_so_follower \
--robot.left_arm_config.port=/dev/ttyACM0 \
--robot.right_arm_config.port=/dev/ttyACM1 \
--robot.id=my_awesome_bimanual_follower \
--robot.left_arm_config.cameras='{
left_wrist: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30}
}' \
--robot.right_arm_config.cameras='{
right_wrist: {"type": "opencv", "index_or_path": 4, "width": 640, "height": 480, "fps": 30}
}' \
--teleop.type=bi_so_leader \
--teleop.left_arm_config.port=/dev/ttyACM2 \
--teleop.right_arm_config.port=/dev/ttyACM3 \
--teleop.id=my_awesome_bimanual_leader \
--display_data=true
警告

2 本のフォロワーが同じ作業空間を共有します。周囲に注意し、フォロワー同士 — そして受け渡しタスク中のグリッパ同士 — の衝突を避けてください。

17.5 双腕データセットの録画​

録画

17.5 双腕データセットの録画

ローカルに保存(--dataset.push_to_hub=false を追加。repo_id は / を含む必要があるため、ローカルでは seeed/ をプレフィックスに使えます):

lerobot-record \
--robot.type=bi_so_follower \
--robot.left_arm_config.port=/dev/ttyACM0 \
--robot.right_arm_config.port=/dev/ttyACM1 \
--robot.id=my_awesome_bimanual_follower \
--robot.left_arm_config.cameras='{
left_wrist: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30}
}' \
--robot.right_arm_config.cameras='{
right_wrist: {"type": "opencv", "index_or_path": 4, "width": 640, "height": 480, "fps": 30}
}' \
--teleop.type=bi_so_leader \
--teleop.left_arm_config.port=/dev/ttyACM2 \
--teleop.right_arm_config.port=/dev/ttyACM3 \
--teleop.id=my_awesome_bimanual_leader \
--dataset.repo_id=seeed/bimanual_so101_task \
--dataset.push_to_hub=false \
--dataset.single_task="Pick the cube with left arm and hand it to right arm" \
--dataset.num_episodes=50 \
--dataset.fps=30 \
--dataset.episode_time_s=30 \
--dataset.reset_time_s=10 \
--dataset.video=true \
--dataset.vcodec=libsvtav1 \
--display_data=true

データは ~/.cache/huggingface/lerobot/seeed/bimanual_so101_task/ に保存され、構造は第 13 章と同じです。Hub にアップロードする場合は HF_USER を維持して push_to_hub=false を外し、--dataset.repo_id=${HF_USER}/bimanual_so101_task とします。

録画の再開(Resume)​

録画が異常終了した場合や複数セッションで分けて採集する場合に --resume を使います:

  • 必ず --resume=true を追加。付けないと LeRobotDataset.create() がディレクトリ既存エラーを出します;
  • --dataset.num_episodes は今回のセッションで録る本数です(目標 50 のうち 15 録画済み → 35 を設定);
  • エピソード録画中か自然終了後に終了するようにしてください。「環境リセット」フェーズでの終了は避ける(空エピソードの保存失敗につながる — 最新コードは自動スキップします):
lerobot-record \
--resume=true \
--robot.type=bi_so_follower \
--robot.left_arm_config.port=/dev/ttyACM0 \
--robot.right_arm_config.port=/dev/ttyACM1 \
--robot.id=my_awesome_bimanual_follower \
--robot.left_arm_config.cameras='{
left_wrist: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30}
}' \
--robot.right_arm_config.cameras='{
right_wrist: {"type": "opencv", "index_or_path": 4, "width": 640, "height": 480, "fps": 30}
}' \
--teleop.type=bi_so_leader \
--teleop.left_arm_config.port=/dev/ttyACM2 \
--teleop.right_arm_config.port=/dev/ttyACM3 \
--teleop.id=my_awesome_bimanual_leader \
--dataset.repo_id=seeed/bimanual_so101_task \
--dataset.push_to_hub=false \
--dataset.single_task="Pick the cube with left arm and hand it to right arm" \
--dataset.num_episodes=35 \
--dataset.fps=30 \
--dataset.episode_time_s=30 \
--dataset.reset_time_s=10 \
--dataset.video=true \
--dataset.vcodec=libsvtav1 \
--display_data=true

エピソードの再生と削除​

実機双腕で指定エピソードを再生(episode は 0 始まり — 24 は 25 本目):

lerobot-replay \
--robot.type=bi_so_follower \
--robot.left_arm_config.port=/dev/ttyACM0 \
--robot.right_arm_config.port=/dev/ttyACM1 \
--robot.id=my_awesome_bimanual_follower \
--dataset.repo_id=seeed/bimanual_so101_task \
--dataset.episode=24

悪いエピソードの削除(第 13 章ツールボックス):

python -m lerobot.scripts.lerobot_edit_dataset \
--repo_id=seeed/bimanual_so101_task \
--operation.type=delete_episodes \
--operation.episode_indices="[24]"

元データは .../bimanual_so101_task_old/ にバックアップされます。確認後、手動で削除してください。

17.6 ACT 訓練​

訓練

17.6 ACT 訓練

ローカルデータセットから:

lerobot-train \
--dataset.repo_id=seeed/bimanual_so101_task \
--policy.type=act \
--policy.device=cuda \
--steps=60000 \
--output_dir=outputs/train/act_bimanual_so101 \
--wandb.enable=false \
--policy.push_to_hub=false

Hugging Face Hub から:

export HF_USER=your_hf_username

lerobot-train \
--dataset.repo_id=${HF_USER}/bimanual_so101_task \
--policy.type=act \
--policy.device=cuda \
--steps=100000 \
--output_dir=outputs/train/act_bimanual_so101 \
--wandb.enable=false \
--policy.push_to_hub=false

上記は ACT のデフォルトパラメータ(chunk_size=100、dim_model=512 など)を使用します。ポリシーはデータセットから 12 次元の状態 / アクション(各アーム 6)を自動的に読みます — 追加設定は不要です。

17.7 実機展開​

展開

17.7 実機展開

第 16 章のレシピで評価します。今度は双腕です — 評価データをローカルに保存:

lerobot-record \
--robot.type=bi_so_follower \
--robot.left_arm_config.port=/dev/ttyACM0 \
--robot.right_arm_config.port=/dev/ttyACM1 \
--robot.id=my_awesome_bimanual_follower \
--robot.left_arm_config.cameras='{
left_wrist: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30}
}' \
--robot.right_arm_config.cameras='{
right_wrist: {"type": "opencv", "index_or_path": 4, "width": 640, "height": 480, "fps": 30}
}' \
--dataset.root=seeed_eval/eval_bimanual_so101_task8 \
--dataset.push_to_hub=false \
--dataset.num_episodes=10 \
--dataset.single_task="Pick the cube with left arm and hand it to right arm" \
--dataset.fps=30 \
--dataset.video=true \
--policy.path=outputs/train/act_bimanual_so101/checkpoints/last/pretrained_model \
--display_data=true

または --dataset.repo_id=${HF_USER}/eval_bimanual_so101_task にして(dataset.root を外す)評価を Hub にアップロードします。

17.8 FAQ​

FAQ

17.8 FAQ

問題原因解決
テレオペ時に再キャリブレーションを要求されるbi_so_follower が _left / _right 接尾辞付きファイルを見つけられない接尾辞付き id で再キャリブレーション、または既存ファイルをコピー(17.3 節)
リーダーが手で動かせないリーダーのトルクが無効化されていない再キャリブレーションまたはモーターを確認
再録時に「ディレクトリが既に存在」--resume=true がないlerobot-record コマンドに --resume=true を追加
左右のアームが入れ替わっているポート設定ミスleft_arm_config.port と right_arm_config.port を入れ替える
訓練時にデータセットが見つからないローカルデータセットに root 未指定訓練時に --dataset.root=./datasets/xxx を追加
データセットが自動アップロードされるpush_to_hub=false 未設定録画時に --dataset.push_to_hub=false を追加
You must add one or several frames before calling add_episode で終了リセットフェーズで終了し、現 Episode にフレームがない録画済みデータは無傷。--resume=true で継続。最新コードは空エピソードを自動スキップ
Loading Comments...