a.ローカル(mac)でSO-101を動作させる
a-1.Motors Busの検出とUSBポート番号の確認
コマンド
$ lerobot-find-port
結果
['/dev/ttyw4', '/dev/ttyw5', '/dev/ttyw6', '/dev/ttyw7', '/dev/ttyw8', '/dev/ttyw9', '/dev/ttywa', '/dev/ttywb', '/dev/ttywc', '/dev/ttywd', '/dev/ttywe', '/dev/ttywf', '/dev/tty.debug-console', '/dev/tty.Bluetooth-Incoming-Port', '/dev/ttys000', '/dev/ttys001', '/dev/ttys002', '/dev/ttys003', '/dev/tty.usbmodem5AB90676501', '/dev/tty.usbmodem5AB90669021']
> '/dev/tty.usbmodem5AB90676501' leader
> '/dev/tty.usbmodem5AB90669021' follow
a-2.リーダーのモーター初期化、設定
コマンド
$ lerobot-setup-motors --teleop.type=so101_leader --teleop.port=/dev/tty.usbmodem5AB90676501
結果。番号が表示される順番にモーターを接続してエンターを押す。
(組み立て順とは異なって、グリッパー側の6番から番号が割り振られることに注意)
% lerobot-setup-motors --teleop.type=so101_leader --teleop.port=/dev/tty.usbmodem5AB90676501
Connect the controller board to the 'gripper' motor only and press enter.
'gripper' motor id set to 6
Connect the controller board to the 'wrist_roll' motor only and press enter.
'wrist_roll' motor id set to 5
Connect the controller board to the 'wrist_flex' motor only and press enter.
'wrist_flex' motor id set to 4
Connect the controller board to the 'elbow_flex' motor only and press enter.
'elbow_flex' motor id set to 3
Connect the controller board to the 'shoulder_lift' motor only and press enter.
'shoulder_lift' motor id set to 2
Connect the controller board to the 'shoulder_pan' motor only and press enter.
'shoulder_pan' motor id set to 1
a-3.フォロワーのモーター初期化、設定
コマンド
$ lerobot-setup-motors --robot.type=so101_follower --robot.port=/dev/tty.usbmodem5AB90669021
結果。番号が表示される順番にモーターを接続してエンターを押す。
(組み立て順とは異なって、グリッパー側の6番から番号が割り振られることに注意)
% lerobot-setup-motors --robot.type=so101_follower --robot.port=/dev/tty.usbmodem5AB90669021
Connect the controller board to the 'gripper' motor only and press enter.
'gripper' motor id set to 6
Connect the controller board to the 'wrist_roll' motor only and press enter.
'wrist_roll' motor id set to 5
Connect the controller board to the 'wrist_flex' motor only and press enter.
'wrist_flex' motor id set to 4
Connect the controller board to the 'elbow_flex' motor only and press enter.
'elbow_flex' motor id set to 3
Connect the controller board to the 'shoulder_lift' motor only and press enter.
'shoulder_lift' motor id set to 2
Connect the controller board to the 'shoulder_pan' motor only and press enter.
'shoulder_pan' motor id set to 1
a-4.キャリブレーション(リーダー)
コマンド
$ lerobot-calibrate --teleop.type=so101_leader --teleop.port=/dev/tty.usbmodem5AB90676501 --teleop.id=so101_leader
結果
各モーターの可動範囲を一つずつ動かしていく。
% lerobot-calibrate --teleop.type=so101_leader --teleop.port=/dev/tty.usbmodem5AB90676501 --teleop.id=so101_leader
INFO 2025-12-19 09:31:47 calibrate.py:76 {'robot': None,
'teleop': {'calibration_dir': None,
'id': 'so101_leader',
'port': '/dev/tty.usbmodem5AB90676501',
'use_degrees': False}}
INFO 2025-12-19 09:31:48 01_leader.py:82 so101_leader SO101Leader connected.
INFO 2025-12-19 09:31:48 01_leader.py:99
Running calibration of so101_leader SO101Leader
Move so101_leader SO101Leader to the middle of its range of motion and press ENTER....
Move all joints sequentially through their entire ranges of motion.
Recording positions. Press ENTER to stop...
-------------------------------------------
-------------------------------------------
NAME | MIN | POS | MAX
shoulder_pan | 1177 | 2187 | 3093
shoulder_lift | 859 | 865 | 3243
elbow_flex | 847 | 3058 | 3063
wrist_flex | 908 | 1890 | 3072
wrist_roll | 67 | 1966 | 3677
gripper | 1906 | 1922 | 3146
Calibration saved to /Users/oggata/.cache/huggingface/lerobot/calibration/teleoperators/so101_leader/so101_leader.json
INFO 2025-12-19 09:32:33 1_leader.py:156 so101_leader SO101Leader disconnected.
a-5.キャリブレーション(フォロワー)
$ lerobot-calibrate --robot.type=so101_follower --robot.port=/dev/tty.usbmodem5AB90669021 --robot.id=so101_follower
% lerobot-calibrate --robot.type=so101_follower --robot.port=/dev/tty.usbmodem5AB90669021 --robot.id=so101_follower
INFO 2025-12-19 09:08:15 calibrate.py:76 {'robot': {'calibration_dir': None,
'cameras': {},
'disable_torque_on_disconnect': True,
'id': 'so101_follower',
'max_relative_target': None,
'port': '/dev/tty.usbmodem5AB90669021',
'use_degrees': False},
'teleop': None}
INFO 2025-12-19 09:08:15 follower.py:104 so101_follower SO101Follower connected.
INFO 2025-12-19 09:08:15 follower.py:121
Running calibration of so101_follower SO101Follower
Move so101_follower SO101Follower to the middle of its range of motion and press ENTER....
Move all joints sequentially through their entire ranges of motion.
Recording positions. Press ENTER to stop...
-------------------------------------------
-------------------------------------------
NAME | MIN | POS | MAX
shoulder_pan | 1127 | 2087 | 2975
shoulder_lift | 914 | 3107 | 3107
elbow_flex | 898 | 1249 | 2951
wrist_flex | 944 | 1423 | 3182
wrist_roll | 2046 | 2055 | 2057
gripper | 2010 | 2015 | 3385
Calibration saved to /Users/oggata/.cache/huggingface/lerobot/calibration/robots/so101_follower/so101_follower.json
a-6.カメラの検出
コマンド
$ lerobot-find-cameras
a-7.テレオペレーション
コマンド
//カメラ無し
$ lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5AB90669021 \
--robot.id=so101_follower \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem5AB90676501 \
--teleop.id=so101_leader
--display_data=true
//テレオペレーション-カメラx1
$ lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5AB90669021 \
--robot.id=so101_follower \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem5AB90676501 \
--teleop.id=so101_leader \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
--display_data=true
//テレオペレーション-カメラx2
$ lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5AB90669021 \
--robot.id=so101_follower \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem5AB90676501 \
--teleop.id=so101_leader \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30},second: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 20}}" \
--display_data=true
a-8.記録の準備
hugging faceでcredintialの作成
https://huggingface.co/settings/tokens
hugging faceでスペースの作成
https://huggingface.co/settings/tokens
コンソールでcredientialを使ってログインする
# ログイン
pip install huggingface_hub
export HUGGINGFACE_TOKEN=""
huggingface-cli login --token ${HUGGINGFACE_TOKEN} --add-to-git-credential
hf auth whoami
$ lerobot-find-port
$ lerobot-find-cameras
$ rm -rf ./record-gab-cookie-demo
a-9.記録スタート
# 1カメラ
$ lerobot-record \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5AB90669021 \
--robot.id=so101_follower \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem5AB90676501 \
--teleop.id=so101_leader \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
--display_data=true \
--dataset.repo_id=oggata/record-gab-cookie-demo \
--dataset.root=/Volumes/so101/lerobot_datasets/record-gab-cookie-demo/ \
--dataset.single_task="Pickup the rice crackers and put them in the basket." \
--dataset.num_episodes=30 \
--dataset.episode_time_s=30 \
--dataset.reset_time_s=5 \
--resume=true
## 2カメラ
$ lerobot-record \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5AB90669021 \
--robot.id=so101_follower \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem5AB90676501 \
--teleop.id=so101_leader \
--dataset.root=/Volumes/so101/lerobot_datasets/record-gab-cookie-demo/ \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30},second: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
--display_data=true \
--dataset.repo_id=oggata/record-gab-cookie-demo \
--dataset.root=./record-gab-cookie-demo \
--dataset.single_task="Pickup the rice crackers and put them in the basket." \
--dataset.num_episodes=7 \
--dataset.episode_time_s=20 \
--dataset.reset_time_s=10 \
--resume=true
## 外付けハードディスクに保存する
$ lerobot-record \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5AB90669021 \
--robot.id=so101_follower \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem5AB90676501 \
--teleop.id=so101_leader \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30},second: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
--display_data=true \
--dataset.repo_id=oggata/record-gab-cookie-demo \
----dataset.root=/Volumes/so101/lerobot_datasets/record-gab-cookie-demo/ \
--dataset.single_task="Pickup the rice crackers and put them in the basket." \
--dataset.num_episodes=7 \
--dataset.episode_time_s=20 \
--dataset.reset_time_s=10 \
--resume=true
b. GoogleColabでレコードデータをもとに学習を行う
b-1.準備
from google.colab import drive
drive.mount('/content/drive')
!pip install -q condacolab
import condacolab
condacolab.install()
import os
os.chdir('/content/') # ディレクトリを移動
print(os.getcwd())
!git clone https://github.com/huggingface/lerobot.git
!conda install ffmpeg=7.1.1 -c conda-forge
!cd lerobot && pip install -q -e .
import os
# LeRobot リポジトリをクローン
!git clone https://github.com/huggingface/lerobot.git /content/lerobot
# ディレクトリ移動して確認
os.chdir('/content/lerobot')
print(os.getcwd())
!ls -la src/lerobot/scripts/
# 必要な依存関係をインストール
!pip install -e .
from google.colab import userdata
import os
# シークレットから取得(strip()で前後の空白を削除)
hf_token = userdata.get('HF_TOKEN').strip()
wandb_key = userdata.get('WANDB_API_KEY').strip()
# 環境変数に設定
os.environ['HF_TOKEN'] = hf_token
os.environ['WANDB_API_KEY'] = wandb_key
# git credential helper を設定
!git config --global credential.helper store
# 新しいコマンドでログイン
!hf auth login --token {hf_token} --add-to-git-credential
# wandb ログイン
!wandb login {wandb_key}
b-2.パスの設定
hugging faceのデータディレクトリはこのパスによって変更すること。
https://huggingface.co/datasets/oggata/record-gab-cookie-demo
ルールとして、polycyにはact_という接頭語をつけている。
os.makedirs('/root/.cache/huggingface/lerobot/oggata', exist_ok=True)
os.chdir('/root/.cache/huggingface/lerobot/oggata')
print(os.getcwd())
!git clone https://huggingface.co/datasets/oggata/record-gab-cookie-demo
exp_name = "record-gab-cookie-demo"
dir_name = "oggata"
DATASET_REPO_ID = f"{dir_name}/{exp_name}"
POLICY_REPO_ID = f"{dir_name}/act_{exp_name}"
JOB_NAME = f"act_{exp_name}"
OUTPUT_DIR = f"/content/drive/MyDrive/Colab\ Notebooks/act_{exp_name}"
b-3.実行
途中から再開する場合は、--resume=trueをコメントアウトして使う。
中身よるが、batch_sizeは8、stepsは40000前後を指定。
import os
import shutil
# 既存ディレクトリを削除
output_dir = "/content/drive/MyDrive/Colab Notebooks/act_record-gab-cookie-demo4"
if os.path.exists(output_dir):
# shutil.rmtree(output_dir)
print(f"Deleted: {output_dir}")
!cd /content/lerobot/ && python src/lerobot/scripts/lerobot_train.py \
--dataset.repo_id=$DATASET_REPO_ID \
--policy.type=act \
--policy.repo_id=$POLICY_REPO_ID \
--output_dir=$OUTPUT_DIR \
--job_name=$JOB_NAME \
--policy.device=cuda \
--wandb.enable=true \
--batch_size=8 \
--save_freq=5000 \
--steps=40000 #\
# --resume=true
b-4. wandb.aiで確認する
b-4.再開
チェックポイントから再開する場合は下記のコードに習う。
import os
# 設定
OUTPUT_DIR = "/content/drive/MyDrive/Colab Notebooks/act_record-gab-cookie-demo"
CONFIG_PATH = f"{OUTPUT_DIR}/checkpoints/010000/pretrained_model/train_config.json"
# 再開
# L4でbatch_size=8 A100でbatch_size=32程度
print(f"Resuming training from: {OUTPUT_DIR}")
!cd /content/lerobot/ && python src/lerobot/scripts/lerobot_train.py \
--config_path="{CONFIG_PATH}" \
--policy.device=cuda \
--wandb.enable=true \
--batch_size=4 \
--save_freq=2000 \
--steps=40000 \
--resume=true
b-5.手動でデータをアップロードする
from lerobot.common.policies.act.modeling_act import ACTPolicy
from huggingface_hub import login
# ログイン
login(token="your_hf_token_here")
# モデルをロード
policy = ACTPolicy.from_pretrained(
"/content/drive/MyDrive/Colab Notebooks/act_record-gab-cookie-demo/checkpoints/040000"
)
# Hubにアップロード
policy.push_to_hub(
repo_id="oggata/act_policy_gab_cookie",
private=False
)
c.ローカル(mac)で学習データを元にSO-101を実行させる
c-1.アームの自動実行(学習データを利用して実行する)
$ lerobot-find-port
$ lerobot-find-cameras
$ rm -rf ./Volumes/so101/lerobot_datasets/eval_record-gab-cookie-demo/
$ lerobot-record \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5AB90669021 \
--robot.id=so101_follower \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30},second: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
--display_data=true \
--dataset.repo_id=oggata/eval_record-stack-blocks \
--dataset.root=/Volumes/so101/lerobot_datasets/eval_record-stack-blocks/ \
--dataset.single_task="StackWoodenBlocks." \
--dataset.push_to_hub=false \
--policy.path=/Volumes/so101/lerobot_datasets/record-stack-blocks/checkpoints/080000/pretrained_model
記録データの視聴
DecodingError(path, f"The fields {formatted_keys} are not valid for {stringify_type(cls)}")
draccus.utils.DecodingError: The fields use_peft are not valid for ACTConfig
# バックアップを作成
cp /Users/oggata/Documents/act_record-dungeons-1/checkpoints/040000/pretrained_model/config.json \
/Users/oggata/Documents/act_record-dungeons-1/checkpoints/040000/pretrained_model/config.json.bak
# use_peftを削除
python3 << 'EOF'
import json
config_path = '/Users/oggata/Documents/act_record-dungeons-1/checkpoints/040000/pretrained_model/config.json'
with open(config_path, 'r') as f:
config = json.load(f)
# use_peftを削除
if 'use_peft' in config:
del config['use_peft']
print("✓ use_peftを削除しました")
# pretrained_pathも更新(Colab用のパスなので)
if 'pretrained_path' in config and '/content/drive/' in config['pretrained_path']:
config['pretrained_path'] = None
print("✓ pretrained_pathをNullに設定しました")
with open(config_path, 'w') as f:
json.dump(config, f, indent=4)
print("✓ 設定ファイルを更新しました")
EOF

