SO-101 実機 Bring-up とテレオペ収録手順(Mac / LeRobot 0.6.1)¶
本手順は、WowRobo 組立済み SO-101(Leader / Follower)を Mac 上の LeRobot 0.6.1 で駆動し、テレオペ・カメラ付きデモ収録まで行うための実務手順である。機材一覧・作業エリアの契約寸法は hardware.md を正とする。一次情報は SO-101、Cameras、実機での模倣学習 である。
反映した実測:
- 2026-10-17/18 の bring-up(UGREEN ハブ経由の接続・両カメラ同時配信・テレオペ・
lerobot-record試行) - 2026-10-22 / 23 の本収録(当時のテーブル寸法、画角、キーボード操作、モータ温度、クリーンセット作成)。そのテーブルは廃止。廃止した配置 を参照
- 実機 bring-up と live eval の現行配置: 140 × 70 cm テーブル規格に準拠。現行テーブル配置 を参照
- 2026-10-27 の配置収録。既定 root は
so101_pick_sponge_layout20261026_20261027。so101_pick_sponge_main_20261020はアーカイブ
収録データセットの要約は real_dataset_so101_pick_sponge_main_20261023.md。sim→実機の単位変換は sim_to_real_act.md / units.md。
前提と環境¶
実機用の仮想環境は本リポジトリの .venv とは分離する(学習ジョブや sim 依存と干渉させない)。
# 例: 専用ディレクトリに venv を作成
python3.12 -m venv "$HOME/work/lerobot-hw/.venv"
source "$HOME/work/lerobot-hw/.venv/bin/activate"
pip install -U pip
pip install 'lerobot[feetech]==0.6.1'
# 収録・可視化・学習時に追加
pip install 'lerobot[dataset,viz]==0.6.1'
# 必要に応じて: pip install 'lerobot[hardware,training]==0.6.1'
| 項目 | 値 |
|---|---|
| Python | 3.12 |
| LeRobot | 0.6.1(pip install 'lerobot[feetech]') |
| ホスト | Apple Silicon Mac(例: MacBook Pro M4) |
| ロボット型 | --robot.type=so101_follower / --teleop.type=so101_leader |
| キャリブレーション id | follower / leader(以降の全コマンドで同一) |
ドキュメントでは ~ ではなく $HOME を使う(--dataset.root= 直後の ~ は展開されない)。
ハードウェア(2026-10-22 時点)¶
| 項目 | 内容 |
|---|---|
| ホスト | MacBook Pro M4。USB-C ハブ経由でアーム・カメラを接続 |
| Leader | ハンドル付き・グリッパなし。電源 5V。ポート例 /dev/tty.usbmodem5B790143611 |
| Follower | グリッパ付き。電源 12V。ポート例 /dev/tty.usbmodem5B8E1163071 |
| 固定 | 両アーム基部を C クランプでテーブル縁に固定 |
| 上カメラ | Logitech C920。VIJIM LS08。現行の固定はテーブル右端、手前から 16 cm。現行テーブル配置 |
| 手首カメラ | キット同梱 USB2.0_CAM1(Follower グリッパ側) |
アダプタの取り違えは厳禁。 挿す前にラベルの電圧と基板側を指差し確認する。DC を外しても基板の赤 LED が点灯することがある(USB 給電)。駆動トルクには DC アダプタが必要である。Follower の 12V は必ず ON(LED 点灯)。2026-10-18 実測: Follower 約 12.6 V / Leader 約 5.5 V。
ポートは抜き差し後に変わることがある。 作業前に ls /dev/tty.usbmodem* と lerobot-find-port で再確認する。
カメラ index(OpenCV / LeRobot)¶
| カメラ | OpenCV index | 解像度 / fps |
|---|---|---|
| wrist(USB2.0_CAM1、Follower グリッパ) | 0 | 640×480 / 30 |
| top(C920) | 1 | 640×480 / 30 |
この index は 2026-10-25 のケーブル交換後のもので、2026-10-26 も同じである(2026-10-18 / 22 の番号とも一致)。
index も抜き差し後にずれることがある。 lerobot-find-cameras opencv と outputs/captured_images/opencv_*.png で確認する(ffmpeg / avfoundation の番号は OpenCV と異なる)。
Rerun 表示時のエンティティ名は observation.top と observation.wrist である。
安全¶
- テレオペ開始時、Follower は Leader 姿勢へ一気に動く。開始前に両腕の姿勢をそろえる。
- 終了(Ctrl+C)でトルクが切れる(
disable_torque_on_disconnect)。重力で落ちることがある。終了前に手で Follower を支える。 - 非常時はまず Follower の 12V プラグを抜く。電源プラグにすぐ手が届く位置で作業する。
- カメラスタンドは Follower の可動範囲外。 C920 はテーブル右端、手前から 16 cm(2026-10-26)。2026-10-22 のポール(幅およそ 134 cm のテーブルで右端から約 23 cm)は廃止。テレオペ中にスタンドへ接触した実績あり。
現行テーブル配置(2026-10-26)¶
新しい bring-up、新しい収録、実機 live eval の正である。2026-10-26 ~00:36 SGT、上カメラを動かしてスポンジ・紙・Follower が写るようにしたあとの実測。2026-10-25 ~23:28 と 2026-10-22 を置き換える。数値は hardware.md と同じ。定数は port_pick_align.simulation.table_layout。
テーブル: 幅 140 cm × 奥行 70 cm。紙とスポンジはテープで印を付ける。古い図に合わせるためにハードウェアを動かさない。
| 対象 | 配置 | 備考 |
|---|---|---|
| Leader 基部 | 左から 27 cm / 右から 101 cm | 5V。基部の両端 |
| Follower 基部 | 左から約 68 cm / 右から 61 cm | 12V。左からの距離は概数 |
| 上カメラ(C920) | テーブル右端、手前から 16 cm | 紙・スポンジ・Follower が写る向き |
| 紙(ゴール) | 右から 69 cm、手前から 27 cm | 縦置き 幅 20 cm × 奥行 30 cm |
| スポンジ | 右から 54 cm、手前から 44 cm | 縦置き 幅 6 cm × 奥行 11 cm |
| 手首カメラ | Follower のグリッパ | OpenCV 0 |
| 上カメラ index | OpenCV 1 | 2026-10-25 のケーブル交換後も同じ |
上面図(手前縁が下):
奥(手前から 70 cm)
+------------------------------------------------------------------------------+
| スポンジ 6×11 縦置き |
| 右から 54 cm、手前から 44 cm |
| |
| Leader 基部 紙 20×30 縦置き Follower 基部 |
| 左から 27 cm 右から 69 cm 左から約 68 cm |
| 右から 101 cm 手前から 27 cm 右から 61 cm |
| 5V C920 |
| 右端 |
| 手前 |
| 16 cm |
+------------------------------------------------------------------------------+
手前縁(0) 140 cm
左 0
sim 座標系への写像¶
原点は Follower 基部の中点を手前縁に置いた点。+x は前方(テーブル奥へ)、+y は左。中点は、左から約 68 cm と右から 61 cm が示す区間の中央(左から 68 cm 〜 79 cm、中点 73.5 cm)。基部の手前オフセットは再計測していないので、原点は手前縁上である。紙・スポンジ・カメラの座標は実測点である(記録は点が角であるとは書いていない)。
| 対象 | sim (x, y) cm |
sim (x, y) m |
|---|---|---|
| Leader 中点 | (0, 40.5) | (0, 0.405) |
| Follower 原点 | (0, 0) | (0, 0) |
| 紙 | (27, 2.5) | (0.27, 0.025) |
| スポンジ | (44, −12.5) | (0.44, −0.125) |
| 上カメラ | (16, −66.5) | (0.16, −0.665) |
MuJoCo の so101_tabletop.xml は廃止した簡易シーンのままである(箱 (0.28, -0.08) m、ゴール (0.35, 0.12) m、真下カメラ (0.25, 0, 1.05) m)。これらの geom はこのテーブルではない。Follower まわりの可動範囲に障害物・液体を置かない。

実機 live-eval チェックリスト¶
scripts/run_act_real.sh --live、scripts/run_smolvla_real.sh --live、または lerobot-rollout の前に実施する。デプロイのフラグは real_robot_deploy.md。採点、trim、学習後の live 評価プロトコルは real_eval_protocol.ja.md。
| # | 確認 |
|---|---|
| 1 | テーブルは 140 × 70 cm。2026-10-22(約 134 cm)と 2026-10-25 ~23:28(140×69、C920 は右から 28 cm)は使わない |
| 2 | Leader 基部は左から 27 cm / 右から 101 cm(5V)。Follower 基部は左から約 68 cm / 右から 61 cm(12V) |
| 3 | 紙は右から 69 cm、手前から 27 cm(縦置き 20×30 cm) |
| 4 | スポンジは右から 54 cm、手前から 44 cm(縦置き 6×11 cm) |
| 5 | C920 は右端、手前から 16 cm。紙・スポンジ・Follower が写る向き |
| 6 | 手首カメラは Follower のグリッパ。OpenCV wrist = 0、top = 1。抜き差し後は scripts/preflight_real_so101.sh と lerobot-find-cameras opencv で再確認 |
| 7 | Rerun: observation.top に紙・スポンジ・Follower、observation.wrist に接近時のグリッパ先端とスポンジ |
| 8 | 先に dry-run。トルクオフの前に Follower を手で支える。12V プラグに手が届くこと |
上カメラ画角¶
top には次がすべて写っていること:
- 白い紙
- 黄色いスポンジ
- Follower 基部およびグリッパの作業経路
2026-10-26 の設置は、その画角のために C920 を右端、手前から 16 cm に置いている。収録または live eval の前に Rerun(observation.top / observation.wrist)で確認する。2026-10-22 のポールや、2026-10-25 の位置(右から 28 cm)へ戻さない。
廃止した配置¶
新しいセッションをここから組まない。
2026-10-25 ~23:28¶
テーブル 140×69 cm。C920 は右から 28 cm。スポンジは右から 70 cm / 手前から 45 cm。紙は右から 82 cm / 手前から 26 cm。Follower は右から 62 cm。
2026-10-22(本収録テレオペのみ)¶
下の距離は左端から。テーブル幅はおよそ 134 cm。スポンジは Follower 基部とのクリアランスのため前方へ寄せてあった。clean28 データセットはこの配置で収録した。それらのエピソードを 2026-10-26 のテーブルとして書き換えない。
| 対象 | 左端から | 手前縁から(該当時) |
|---|---|---|
| Leader アーム左端 | 28 cm | — |
| 白い紙(縦置き) | 56 cm | 13 cm |
| Follower アーム左端 | 78 cm | — |
| 黄色いスポンジ | 81 cm | 29 cm |
| 上カメラポール | 111 cm(右端から 23 cm) | — |
左端 0 cm 右端 ~134 cm
+------------------------------------------------------------------+
| |
| Leader 紙(白) Follower スポンジ(黄) C920 |
| 28cm 56cm /13cm前 78cm 81cm /29cm前 111cm|
| [====] [紙] [====] [スポ] |pole|
| |
| (手前縁) |
+------------------------------------------------------------------+
廃止した図(簡易 MuJoCo シーン。2026-10-22 のテープ位置そのものではない): so101_layout_guide_superseded_pre_20261026.png。
USB とポート識別¶
source "$HOME/work/lerobot-hw/.venv/bin/activate"
# 両アームに USB と電源を接続した状態で実行
lerobot-find-port
# プロンプトが出たら、識別したいアームの USB を 1 本だけ抜き、Enter
# 表示されたポートを記録し、USB を挿し直す。もう一方も同様(1 台ずつ)
| アーム | ポート(2026-10-18 / 22 の記録) | 電源 | id |
|---|---|---|---|
| Leader | /dev/tty.usbmodem5B790143611 |
5V | leader |
| Follower | /dev/tty.usbmodem5B8E1163071 |
12V | follower |
初回 bring-up では Leader / Follower のポートを取り違え、テレオペ時にグリッパ付きアームが Leader 側として動いた。 その場合はポートを再識別し、キャリブレーションをやり直す。
モーター ID の確認¶
WowRobo 組立済みキットでは、STS3215 の ID 1–6 とボーレート 1,000,000 が既設定であることが多い。書き込み前にポート走査で確認し、正しければ lerobot-setup-motors はスキップする。
python -c "from lerobot.motors.feetech import FeetechMotorsBus; print(FeetechMotorsBus.scan_port('$PORT_FOLLOWER'))"
python -c "from lerobot.motors.feetech import FeetechMotorsBus; print(FeetechMotorsBus.scan_port('$PORT_LEADER'))"
- 正常例:
{1000000: [1, 2, 3, 4, 5, 6]}→ キャリブレーションへ進む。 - ID 欠落・別ボーレートのみ検出などの場合は電源・配線を確認し、必要なら公式どおり
lerobot-setup-motorsを実行する(SO-101)。
キャリブレーション¶
ホスト側にキャリブレーション JSON が無いと、後続コマンド接続時に自動で始まる。明示的に実行する。
lerobot-calibrate \
--robot.type=so101_follower \
--robot.port=$PORT_FOLLOWER \
--robot.id=follower
lerobot-calibrate \
--teleop.type=so101_leader \
--teleop.port=$PORT_LEADER \
--teleop.id=leader
実務上の注意:
- 今キャリブレーション中のポートのアームだけを動かす。 もう一方を動かすと
same min and max系の失敗が起きうる。 - 開始姿勢は各関節が可動範囲の中央付近(おおむね L 字)。
wrist_roll以外の各関節を両方向いっぱいに動かす。wrist の巻き付きを避ける。- グリッパは全開〜全閉。Leader のトリガー行程はもともと狭い。
- 保存先(既定
HF_LEROBOT_HOME=~/.cache/huggingface/lerobot): ~/.cache/huggingface/lerobot/calibration/robots/so_follower/follower.json~/.cache/huggingface/lerobot/calibration/teleoperators/so_leader/leader.json- 再実行時は既存ファイル利用か
cで再キャリブレーションかのプロンプトが出る。
テレオペ¶
開始前に Leader を Follower と同じ休止姿勢に合わせる。
lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=$PORT_FOLLOWER \
--robot.id=follower \
--teleop.type=so101_leader \
--teleop.port=$PORT_LEADER \
--teleop.id=leader
任意で --robot.max_relative_target=10(度単位の 1 ステップ上限)や --teleop_time_s=60 を付けてよい。終了前に Follower を手で支える。
確認: 各関節が同じ方向に追従する、グリッパが開閉する、異音・振動がない。
カメラ表示つき¶
macOS では初回にカメラ権限が必要(システム設定 → プライバシーとセキュリティ → カメラ)。iPhone Continuity Camera が index を奪うことがある。Disconnect し、必要なら Continuity Camera をオフにする。
lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=$PORT_FOLLOWER \
--robot.id=follower \
--robot.cameras="{ top: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30} }" \
--teleop.type=so101_leader \
--teleop.port=$PORT_LEADER \
--teleop.id=leader \
--display_data=true
収録前チェック(必須)¶
毎回 lerobot-record の直前に実施する。 ポート、カメラ index、テープ位置は上の節で定義する。この表がセッション開始の確認項目である。
デバイスの有無は scripts/preflight_real_so101.sh で確認する。テレオペまたは収録の前に実行する。読み取り専用であり、モーターバスは開かず、アームは動かさず、データセットも書かない。Leader port、Follower port、wrist cam、top cam のそれぞれが PASS または FAIL を出し、最後に RESULT PASS または RESULT FAIL を出す。ハードな失敗は非ゼロで終了する。RESULT FAIL のあとにはテレオペも lerobot-record も始めない。
この事前チェックはシリアルノードの存在と、各カメラインデックスが開けることだけを見る。画角は判定しない。下の 6 は引き続き Rerun を見る。
既定値は scripts/record_real_so101.sh と同じである。
| 確認 | 既定 |
|---|---|
| Leader port(5V) | /dev/tty.usbmodem5B790143611 |
| Follower port(12V) | /dev/tty.usbmodem5B8E1163071 |
| wrist cam | OpenCV index 0 |
| top cam | OpenCV index 1 |
macOS は同じアダプタに /dev/cu.usbmodem* も作る。このリポジトリの LeRobot コマンドは tty ノードを使う。抜き差しでノードが動いたときは PORT_LEADER、PORT_FOLLOWER、WRIST_INDEX、TOP_INDEX で上書きする。venv は $LEROBOT_HW_ROOT/.venv(LEROBOT_HW も可。既定は $HOME/work/lerobot-hw)で、scripts/run_act_real.sh と同じである。
カメラ確認は、その venv の OpenCV で 1 フレーム取る。index は lerobot-record が使う値である。OpenCV が無いときは system_profiler と ffmpeg の avfoundation でカメラを列挙し、ffmpeg があれば 1 フレーム取る。avfoundation の番号は OpenCV の index ではない。Continuity Camera、iPhone カメラ、iPad カメラが列挙されたら失敗である。外して Continuity Camera をオフにする。カメラを初めて開くときは macOS のカメラ権限が要る(システム設定 → プライバシーとセキュリティ → カメラ)。
PORT_* と *_INDEX は事前チェックだけに効く。scripts/record_real_so101.sh のポートとカメラ index はハードコードのままである。抜き差しのあとは ls /dev/tty.usbmodem* の tty ノードに合わせてそのスクリプトを直す。事前チェックは他の tty.usbmodem* / cu.usbmodem* を表示するだけで、どれかを自動では選ばない。自動選択は Leader と Follower の取り違えになる。
| # | 確認項目 |
|---|---|
| 1 | テーブル配置は 現行の 140 × 70 cm 配置 である。スポンジと紙はその節のテープ印に合わせ、旧配置のテープ印とは混同しない。この配置データは旧アーカイブ so101_pick_sponge_main_20261020 と混ぜない。 |
| 2 | Leader の電源は 5V、Follower の電源は 12V で、基板の LED が点灯している。アダプタはそれぞれのアームに付いたままである。 |
| 3 | ls /dev/tty.usbmodem* と scripts/preflight_real_so101.sh でポートを読み直した(Leader port と Follower port が PASS)。スクリプトの既定は Leader /dev/tty.usbmodem5B790143611、Follower /dev/tty.usbmodem5B8E1163071。抜き差しで変わったら scripts/record_real_so101.sh を直す。 |
| 4 | OpenCV index は wrist 0、top 1 である。iPhone の Continuity Camera は切断し、オフにしてある。scripts/preflight_real_so101.sh の wrist cam と top cam が PASS である。画像は lerobot-find-cameras opencv で確認する。 |
| 5 | Leader と Follower は同じ休止姿勢から始める。テレオペ開始時に Follower が急に寄らない。 |
| 6 | 同じ robot / teleop / camera フラグで短時間テレオペし、Rerun(observation.top / observation.wrist)を見る。top: 紙・スポンジ・Follower 基部/グリッパ経路が画角に入る。wrist: 接近時にグリッパ先端とスポンジが見える。経路はスタンドとクランプに当たらない。カメラ警告はない。スポンジと紙は 2026-10-26 配置のテープ印の開始位置にある。 |
| 7 | 収録前に lerobot-teleoperate を止める。収録スクリプトも pkill する。残っているとカメラとシリアルを取り合う。 |
| 8 | ROOT は新しいデータセットディレクトリか、現在の配置 root(so101_pick_sponge_layout20261026_20261027、または ..._batch2 のような名前付きバッチ)への意図した resume である。ROOT が so101_pick_sponge_main_20261020 に解決されるとスクリプトは終了する。 |
| 9 | lerobot-record の前に root を mkdir しない。scripts/record_real_so101.sh はディレクトリが存在するだけで --resume=true にする。空ディレクトリも同じである。初回のツリーは lerobot-record が作る。新しいバッチは、まだ存在しない ROOT を使う。 |
| 10 | シェルは対話 Terminal である。Right でエピソード終了、Left で撮り直し、Esc で停止。エピソード開始と同時に動かし始める。置き終わったら Right を押し、静止した末尾をデータセットに残さない。 |
| 11 | およそ 10 エピソードごとに休む。Follower の電源を切り、モータを冷やす。 |
デモ収録ルール(2026-10-22/23 で確定)¶
- 1 エピソード = 1 回の pick-and-place。 以前、1 エピソードに 2 回置いた試行は破棄した。
- 開始と終了を同じ休止姿勢にする。
- エピソード開始と同時に動かし始める。 初期姿勢を数秒保つ区間は後で先頭だけ切る。その待機だけではエピソードを捨てない。
- スポンジを紙に置いたら Right 矢印でエピソードを早めに終了する(
episode_time_s=25の末尾 およそ 10–17 秒は腕が静止していた)。QC スクリプトはその末尾を採点しない。Right で終えると、その静止区間はデータセットに入らない。 - ゆっくり・一貫した動きで行う。
- およそ 10 エピソードごとに休憩し、Follower の電源を切ってモータを冷やす。およそ 20 エピソード連続後、Follower の
shoulder_liftが指令に遅れ、mean|action-state|がおよそ 10–12° まで上がった(通常はおよそ 1.5°)。
収録スクリプト¶
リポジトリの scripts/record_real_so101.sh を使う(実行権限付き)。
# N エピソードを追加。データセットが既にあれば自動で resume
scripts/preflight_real_so101.sh
scripts/record_real_so101.sh [N]
収録の前に scripts/preflight_real_so101.sh が RESULT PASS を出す。
| 項目 | 値 |
|---|---|
| 既定 N | 50 |
| データセット root | $HOME/work/lerobot-hw/datasets/so101_pick_sponge_layout20261026_20261027 |
| repo_id | yohei1126/so101_pick_sponge_layout20261026 |
| タスク文 | Pick up the yellow sponge and place it on the white paper |
| episode / reset | 25 s / 10 s、fps 30 |
| ログ | $HOME/work/lerobot-hw/outputs/record_main_*.log(cwd は lerobot-hw) |
規格化テーブル配置(140×70 cm)で収録するデモの既定 root は上のパスである。旧配置のアーカイブデータセット(so101_pick_sponge_main_20261020)と混在させないため、スクリプト側で旧アーカイブへの上書き収録を防止するガードが入っている。別セットを始めるときだけ ROOT で別パスを渡す。
スクリプトは開始前に lerobot-teleoperate を pkill する。ポートとカメラ index はスクリプト内にハードコードされている。抜き差し後は編集するか、環境に合わせて上書きする。
スクリプトの前に root を mkdir しない。空ディレクトリを含め、ディレクトリが既にあると --resume=true になり、そのパスへ追記する。初回のツリーは lerobot-record が作る。
対話ターミナルで実行する(必須)¶
キーボード操作:
| キー | 意味 |
|---|---|
| Right | 現在エピソードを終了して次へ |
| Left | 撮り直し |
| Esc | 収録停止 |
バックグラウンド/非対話起動ではキーボードが効かない。 macOS の対話 Terminal で実行する。Terminal にアクセシビリティおよび入力監視の権限が必要な場合がある。
バックグラウンドが必要な場合は nohup ではなく新しいセッションで切り離す(start_new_session / setsid)。nohup ジョブは親終了後に落ちた実績がある。
音声キュー¶
--play_sounds は macOS のデフォルト出力デバイスへ出る。作業用 Mac では Bluetooth の Jabra ヘッドセットがデフォルトだった。収録前に出力デバイスを確認する。
手動の lerobot-record 例¶
スクリプトと同等のフラグ:
source "$HOME/work/lerobot-hw/.venv/bin/activate"
cd "$HOME/work/lerobot-hw"
lerobot-record \
--robot.type=so101_follower \
--robot.port=$PORT_FOLLOWER \
--robot.id=follower \
--robot.cameras="{ top: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30} }" \
--teleop.type=so101_leader \
--teleop.port=$PORT_LEADER \
--teleop.id=leader \
--dataset.repo_id=yohei1126/so101_pick_sponge_layout20261026 \
--dataset.single_task='Pick up the yellow sponge and place it on the white paper' \
--dataset.num_episodes=10 \
--dataset.episode_time_s=25 \
--dataset.reset_time_s=10 \
--dataset.fps=30 \
--dataset.no_stamp=true \
--dataset.push_to_hub=false \
--dataset.root=$HOME/work/lerobot-hw/datasets/so101_pick_sponge_layout20261026_20261027 \
--display_data=true \
--play_sounds=true \
--resume=true
- 既存 root への追記は
--resume=true(スクリプトは root が存在すれば自動で true)。上の例の root には切り出した 10 本が既にあるので、追記は resume である。 - dataset v3.0 ではカメラごとに動画が連結され、
videos/observation.images.<cam>/chunk-000/file-000.mp4に格納される。 - データセット本体は git 外(Mac 上)に置く。
シャットダウン¶
- テレオペ/収録を止める(Ctrl+C または Esc)。Follower を手で支える。
- 12V / 5V アダプタを抜く(先に DC、その後 USB)。
- 運搬時は基部を持つ。ケーブルを持って吊らない。
収録後の QC¶
この節はテレオペ収録データの QC だけを扱う。閾値は scripts/qc_teleop_dataset.py のままである。学習後の実機 ACT 評価(オープンループ採点と live ロールアウト)は 実機評価プロトコル を参照。
scripts/qc_teleop_dataset.py は、ローカルの LeRobot v3 root を 1 つ以上、エピソードごとに採点する。LeRobotDataset を作る前に meta/info.json があることを確認するので、無いパスは作られない。読み込みは video_backend="pyav" と download_videos=False で、列は action、observation.state、episode_index を使う。動画はデコードしない。アームへの指令は出さず、エピソードの結合・除外・切り詰めもしない。
実機用 venv($HOME/work/lerobot-hw/.venv)を有効にする。HF_HUB_OFFLINE=1 にすると読み込みはディスク上で完結する。
source "$HOME/work/lerobot-hw/.venv/bin/activate"
HF_HUB_OFFLINE=1 python scripts/qc_teleop_dataset.py \
--root "$HOME/work/lerobot-hw/datasets/so101_pick_sponge_layout20261026_20261027" \
--root "$HOME/work/lerobot-hw/datasets/so101_pick_sponge_layout20261026_20261027_batch2"
--repo-id は --root が 1 つのときだけ使える。既定は info.json の repo_id で、無ければ local/<dirname> である。--fps はデータセットの fps を上書きする。--out は stem、.csv / .md のパス、またはディレクトリである。--format は csv、md、both(既定 both)である。
測る量¶
腕関節は、名前に gripper を含まない action である。名前が dim* で 2 個以上あるときは dim1 を shoulder_lift とする。標準偏差は母標準偏差(np.std、ddof=0)である。
| 量 | 定義 |
|---|---|
| 平均動作 | 腕関節の action について、フレーム間絶対差分の平均。単位は度/フレーム |
| 先頭アイドル | 腕関節の \|action - action[0]\| の最大が --motion-eps-deg(既定 1.0)に初めて達するフレーム番号。一度も離れなければ idle_fr はエピソード長 T で、to_mot は空。idle_s = idle_fr / fps |
| 遅れ | 関節ごとの mean \|action - state\|。グリッパを含む。lag_sl は常に shoulder_lift。max_lag は平均が最大の関節 |
| action 標準偏差 | 腕関節ごとの action の標準偏差。LOW_MOTION はその最大を使う |
フラグ¶
比較の向きはスクリプトの実装と一致する。
| フラグ | 深刻度 | 条件 | ACT 学習の前 |
|---|---|---|---|
LOW_MOTION |
high | 腕関節の action 標準偏差の最大が --low-motion-std-deg(既定 5)未満 |
そのエピソードを外す。収録全体がほぼ静止している |
HIGH_LAG |
high | 全関節(グリッパを含む)の mean \|action-state\| の最大が --high-lag-deg(既定 6)以上 |
外すか、再確認する。この機体で熱を持つ関節は通常 shoulder_lift である(健全時およそ 1.5–2°、長い連続収録でおよそ 10–12°)。フラグ自体は閾値に達した任意の関節に付く |
LONG_HEAD_IDLE |
warn | idle_s が --head-idle-s(既定 2)より大きい。ちょうど 2.0 秒では付かない |
そのエピソードの idle_fr / to_mot を使い、先頭アイドルだけを軽く切る。待機だけでは捨てない。30 フレーム固定で切ると、3–4 秒の待機の大半が残る |
深刻度は、LOW_MOTION または HIGH_LAG があるとき high、フラグが LONG_HEAD_IDLE だけのとき warn、それ以外は ok である。--strict が終了コード 1 にするのは high だけである。warn だけの実行は 0 で終わる。info.json が無い、読み込みに失敗した、エピソードが 0 件、のいずれも 1 で終わる。
静止した末尾は収録時の規則である。置き終わったら Right を押す。スクリプトはその末尾を採点しない。
標準出力はエピソード 1 行と、最後の episodes / high / warn / ok である。$HOME/work/lerobot-hw/outputs/ があるときは qc_teleop_<UTC>.csv と .md をそこへ書く。無いときは先頭の --root の隣へ書く。
ACT 学習の前¶
スクリプトは報告だけをする。学習前の作業は手で行う。
- 同じ配置のバッチを 1 つのクリーンな root にまとめる。
HIGH_LAGのエピソードを外すか再確認する。2026-10-27 では set 1 の episode 3(shoulder_liftの遅れ 8.92°)である。- 先頭アイドルを軽く切る。
LONG_HEAD_IDLEのエピソードは残す。 - この配置を
so101_pick_sponge_main_20261020と混ぜない。
2026-10-27 の配置 QC(10 + 10 エピソード、7,369 + 7,373 フレーム)は Mac 上の $HOME/work/lerobot-hw/outputs/qc_layout20261026_20261027.md にある。
次のステップ¶
sim チェックポイントを実機へ直接載せない。sim ポリシーは関節がラジアン、実機 Follower は本体関節が度・グリッパが 0–100 であり、入出力契約が一致しない。
- デモ収録 → QC(前節、
scripts/qc_teleop_dataset.py)→ クリーンセット作成(real_dataset_so101_pick_sponge_main_20261023.md) - 実機データで ACT 学習:
python -m port_pick_align.training.train_act(training.md)。MPS 上で実行する。sim データはtrain_act --preset sim - Phase 2-2(sim→実機): 単位変換ラッパーで dry-run → 低速 live。sim_to_real_act.md
- 実機学習ポリシー(ACT / SmolVLA): 単位変換不要。dry-run → live は real_robot_deploy.md(
scripts/run_act_real.sh/scripts/run_smolvla_real.sh)。代替としてlerobot-rolloutも可。初回は--max-relative-target/--max-delta-degを小さめに、低速・短時間で行う
Phase 2-2 クイックリンク(詳細は sim_to_real_act.md)¶
# Dry-run(モータ非駆動); 必ず先にこれ
python -m port_pick_align.deploy.run_sim_policy_on_real \
--checkpoint "$HOME/work/lerobot-hw/outputs/sim_act/best_model.pt" \
--port "$PORT_FOLLOWER" \
--dry-run \
--device mps \
--episode-time-s 20
# Live(12V スイッチに手を置く。初回は max-delta を小さめに)
python -m port_pick_align.deploy.run_sim_policy_on_real \
--checkpoint "$HOME/work/lerobot-hw/outputs/sim_act/best_model.pt" \
--port "$PORT_FOLLOWER" \
--device mps \
--episode-time-s 20 \
--max-delta-deg 3 \
--robot-max-relative-target 3
実機学習ポリシー(推奨: リポジトリのランナー)¶
詳細・安全設定・推奨 live フラグは real_robot_deploy.md。
# ACT(実機 train_act チェックポイント、単位変換なし); 既定は dry-run
./scripts/run_act_real.sh --device mps --episode-time-s 15
# 初回 live 例
./scripts/run_act_real.sh --live --device mps \
--max-delta-deg 2 --max-delta-gripper 3 --n-action-steps 10 --episode-time-s 15
# SmolVLA fine-tune
./scripts/run_smolvla_real.sh --live --device mps --n-action-steps 25 --episode-time-s 15
代替: lerobot-rollout(単位変換不要)¶
lerobot-rollout \
--strategy.type=base \
--policy.path=outputs/train/act_so101_real/checkpoints/last/pretrained_model \
--robot.type=so101_follower \
--robot.port=$PORT_FOLLOWER \
--robot.id=follower \
--robot.cameras="{ top: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
--robot.max_relative_target=5 \
--task="Pick up the yellow sponge and place it on the white paper" \
--duration=30
トラブルシューティング¶
| 症状 | 対処 |
|---|---|
| ポートが出ない / 変わる | データ通信対応 USB-C か確認。ハブを外して直結。ls /dev/tty.usbmodem*。抜き差し後は再識別 |
事前チェックが RESULT FAIL |
FAIL の行を読む。そのポートかカメラを直し、Continuity Camera をオフにして scripts/preflight_real_so101.sh をやり直す。テレオペと収録は RESULT PASS を待ってから始める |
| ポート取り違え(グリッパ腕が Leader 動作) | lerobot-find-port を 1 台ずつやり直し、再キャリブレーション |
Permission denied / Resource busy |
lsof でポート占有プロセスを終了。収録前に lerobot-teleoperate を止める |
Missing motor IDs |
scan_port、12V/5V、3 ピン配線、ポート取り違えを確認 |
| テレオペは動くが Follower が動かない | 12V アダプタ ON(LED 点灯)を確認 |
| top カメラが開かない/タイムアウト | 直前のプロセス解放直後に起きうる。再試行で直ることが多い |
| キーボード(Right / Left / Esc)が効かない | 対話 Terminal で実行。macOS のアクセシビリティ/入力監視を許可 |
| 音声キューが聞こえない | macOS のデフォルト出力デバイスを確認(Bluetooth ヘッドセット等) |
nohup の収録が死ぬ |
新しいセッションで切り離す(start_new_session / setsid)。対話 Terminal が本線 |
| torchcodec が FFmpeg dylib 不足で失敗 | LeRobot は pyav にフォールバックする。長いエラーは無害 |
AVFFrameReceiver is implemented in both ... |
無害。収録成功時も出ることがある |
shoulder_lift の追従遅れ(mean |action-state| ≈ 10–12°) |
連続収録を止め、Follower 電源を切って冷却。およそ 10 epi ごとに休憩 |
| カメラ index がずれる / 内蔵だけ見える | Continuity Camera をオフ。lerobot-find-cameras opencv と保存画像で確認 |
--display_data で rerun エラー |
pip install 'lerobot[viz]==0.6.1' |
lerobot-record の import エラー |
pip install 'lerobot[dataset]==0.6.1' |
--dataset.root=~/... が失敗 |
~ は展開されない。$HOME または絶対パスを使う |
| Follower がカメラスタンドに当たる | C920 は右端、手前から 16 cm のままにする。廃止したポール位置へ戻さない。紙とスポンジはテープで印 |
参照¶
- hardware.md: 購入機材・作業エリア寸法
- real_dataset_so101_pick_sponge_main_20261023.md: clean28 データセット要約
- sim_to_real_act.md: Phase 2-2: sim ACT → 実機 Follower(変換ラッパー)
- real_robot_deploy.md: 実機学習 ACT / SmolVLA の dry-run → live
- real_eval_protocol.ja.md: 学習後の実機 ACT 評価(オープンループ採点と live 評価手順)。収録 QC の閾値は本ページ
- units.md: ラジアン / 度 / グリッパ 0–100
- training.md: ACT 学習
- LeRobot SO-101
- LeRobot Cameras
- Imitation Learning on Real-World Robots