Back to SO-101 project

LeRobot · SO-101

Full Command Reference

Every command explained parameter by parameter — adapt each one for your hardware and task.

00

Pre-flight checklist

!

Do this every time before touching the robot. Skipping it causes confusing errors.

Step 1 — Serial port permissions

Grants read/write access to the arm motor buses. Required for both teleoperation and rollout.

sudo chmod 777 /dev/ttyACM*   # open full permissions on all ACM serial devices
ls -la /dev/ttyACM*            # confirm the ports exist and permissions applied

Step 2 — Camera permissions

Fixes the "TimeoutError: Timed out waiting for frame" error when cameras won't open.

sudo chmod 666 /dev/video*     # allow read/write on all video devices
ls -la /dev/video*             # confirm cameras are listed

Step 3 — Identify ports

Disconnect one arm, run the command, plug it back in — the printed port is that arm's address. Repeat for the other arm.

lerobot-find-port              # detects a newly plugged USB serial device

Then identify which /dev/videoX corresponds to which physical camera:

lerobot-find-cameras opencv    # lists all available OpenCV camera devices

Step 4 — Verify your port mapping

Before running any command, check these three things match your Step 3 output:

--robot.port= → FOLLOWER port (usually /dev/ttyACM0)

--teleop.port= → LEADER port (usually /dev/ttyACM1)

--robot.cameras → index_or_path values from lerobot-find-cameras

01

Teleoperation & Recording

Standard teleoperation

Use this to verify hardware is working and practice the task before committing to a recording session.

lerobot-teleoperate \
    --robot.type=so101_follower \           # fixed — do not change
    --robot.port=/dev/ttyACM1 \             # FOLLOWER port from lerobot-find-port
    --robot.cameras='{
        camera1: {type: opencv, index_or_path: /dev/video0, width: 640, height: 480, fps: 30},
        camera2: {type: opencv, index_or_path: /dev/video2, width: 640, height: 480, fps: 30}
    }' \                                     # video devices from lerobot-find-cameras opencv
    --robot.id=so101_follower_7V \          # any unique name for your follower — used for logging
    --teleop.type=so101_leader \            # fixed — do not change
    --teleop.port=/dev/ttyACM0 \            # LEADER port from lerobot-find-port
    --teleop.id=so_101_leader_7V \          # any unique name for your leader — used for logging
    --display_data=true

Parameter reference

FlagWhat to change it to
--robot.portYour FOLLOWER port — the arm that physically moves (e.g. /dev/ttyACM0)
--teleop.portYour LEADER port — the arm you hold and control (e.g. /dev/ttyACM1)
--robot.camerasReplace index_or_path values with your /dev/videoX devices from lerobot-find-cameras
--robot.id / --teleop.idAny unique label for your hardware — used in logs, can be anything
--display_datatrue to open a live camera preview window, false to skip

Record a dataset

Same as teleoperation but saves every episode to disk and pushes to Hugging Face Hub. Update the dataset flags for your specific task.

lerobot-record \
    --robot.type=so101_follower \
    --robot.port=/dev/ttyACM0 \             # FOLLOWER port from lerobot-find-port
    --robot.cameras='{
        camera1: {type: opencv, index_or_path: /dev/video3, width: 640, height: 480, fps: 30},
        camera2: {type: opencv, index_or_path: /dev/video6, width: 640, height: 480, fps: 30}
    }' \                                     # video devices from lerobot-find-cameras opencv
    --robot.id=so101_follower_7V \          # unique name for your follower
    --teleop.type=so101_leader \
    --teleop.port=/dev/ttyACM1 \            # LEADER port from lerobot-find-port
    --teleop.id=so_101_leader_7V \          # unique name for your leader
    --dataset.repo_id=YOUR_HF_USER/DATASET_NAME \ # Hugging Face Hub destination
    --dataset.single_task="Describe your task here" \ # plain English task description
    --dataset.fps=30 \                      # recording frame rate — match your camera fps
    --dataset.num_episodes=50 \             # total number of episodes to collect
    --dataset.episode_time_s=10 \           # how long each episode lasts (seconds)
    --dataset.reset_time_s=2 \              # time between episodes to reset the scene
    --dataset.root=data/my_dataset \        # local folder to store data before uploading
    --dataset.streaming_encoding=true \     # encode video on the fly to save disk space
    --dataset.vcodec=h264 \                 # video codec — h264 is widely compatible
    --dataset.encoder_threads=2 \           # CPU threads for encoding (2 is fine for Jetson)
    --display_data=true

Parameter reference

FlagWhat to change it to
--robot.portFOLLOWER port (the arm that moves)
--teleop.portLEADER port (the arm you hold)
--robot.camerasReplace index_or_path with your /dev/videoX devices from lerobot-find-cameras
--robot.id / --teleop.idAny unique label — used for logging only
--dataset.repo_idYOUR_HF_USERNAME/dataset-name — where it gets pushed on the Hub
--dataset.single_taskPlain English description of the task (e.g. "Pick up the red cube")
--dataset.num_episodes50 minimum for simple tasks, 100+ for harder ones — more diversity = better model
--dataset.episode_time_sSet to just longer than your task takes — don't leave too much dead time
--dataset.reset_time_sHow long you have to reset the scene between episodes — increase if your reset is slow
--dataset.fpsMatch your camera fps — 30 is standard
--dataset.rootLocal save path — change if you want a different folder name
--dataset.vcodech264 is default and widely supported; h265 for better compression if supported
--dataset.encoder_threads2 works on Jetson; increase on a desktop for faster encoding
Note

Press → to save an episode and advance. Press ← to discard and re-record. Vary the object's starting position slightly between episodes — diversity prevents OOD freezing during inference.

02

Training

Basic ACT training

lerobot-train \
    --dataset.repo_id=YOUR_HF_USER/DATASET_NAME \ # your recorded dataset on the Hub
    --policy.type=act \                     # model architecture — act | diffusion | smolvla
    --training.batch_size=32 \              # samples per gradient step — lower if you run out of memory
    --training.num_epochs=100 \             # full passes through the dataset — increase if loss hasn't converged
    --wandb.enable=true

Parameter reference

FlagWhat to change it to
--dataset.repo_idThe HF Hub dataset you recorded — YOUR_HF_USERNAME/dataset-name
--policy.typeact for ACT, diffusion for Diffusion Policy, smolvla for SmolVLA
--training.batch_size32 is standard — drop to 8 or 16 if you get out-of-memory errors
--training.num_epochsStart at 100, increase if validation loss is still falling at the end
--wandb.enabletrue to log to W&B dashboard, false to train without logging

Fine-tuning with LoRA & weight decay

Use LoRA for heavier models like SmolVLA to save GPU memory and reduce overfitting on small datasets.

lerobot-train \
    --dataset.repo_id=YOUR_HF_USER/DATASET_NAME \
    --peft.method_type=LORA \               # parameter-efficient fine-tuning method
    --peft.r=16 \                           # LoRA rank — higher = more capacity, more memory (try 8, 16, 32)
    --optimizer.weight_decay=1e-2 \         # L2 regularization strength to reduce overfitting
    --optimizer.grad_clip_norm=1.0

Parameter reference

FlagWhat to change it to
--peft.method_typeLORA is the supported option — enables low-rank adapter layers
--peft.rLoRA rank: 8 = light, 16 = default, 32 = high capacity (more memory)
--optimizer.weight_decay1e-2 is a safe default — increase toward 1e-1 if the model overfits badly
--optimizer.grad_clip_norm1.0 is standard — lower to 0.5 if you see exploding gradients

Data augmentation

Adds random color jitter and crops during training. Use this once the model has learned the basic task to improve robustness to lighting and position variation.

lerobot-train \
    --dataset.repo_id=YOUR_HF_USER/DATASET_NAME \
    --policy.image_transforms.enable=true \ # turns on the augmentation pipeline
    --policy.image_transforms.max_num_transforms=3

Parameter reference

FlagWhat to change it to
--policy.image_transforms.enabletrue to enable augmentation, false to train on raw images
--policy.image_transforms.max_num_transforms1–4 — more transforms = stronger augmentation = more robust but slower training

Auto-evaluation during training

Runs the policy on the real robot periodically during training to track success rate without stopping the run.

lerobot-train \
    --dataset.repo_id=YOUR_HF_USER/DATASET_NAME \
    --eval.n_episodes=5

Parameter reference

FlagWhat to change it to
--eval.n_episodesHow many test episodes to run at each eval checkpoint — 5 is enough to get a signal without wasting too much time
03

Rollout modes

All rollout modes share the same hardware flags (robot.port, robot.cameras, robot.id). The key differences are the strategy type, dataset flags, and policy flags.

Mode 1 — Base (evaluation only)

The policy drives the robot. Nothing is recorded. Use this just to watch the policy run.

!

Cannot use any --dataset.* flags in base mode.

lerobot-rollout \
    --strategy.type=base \                  # evaluation only — no recording
    --robot.type=so101_follower \
    --robot.port=/dev/ttyACM1 \             # FOLLOWER port from lerobot-find-port
    --robot.id=so101_follower_7V \          # unique name for your follower
    --robot.cameras='{
        camera1: {type: opencv, index_or_path: /dev/video0, width: 640, height: 480, fps: 30},
        camera2: {type: opencv, index_or_path: /dev/video2, width: 640, height: 480, fps: 30}
    }' \                                     # video devices from lerobot-find-cameras opencv
    --policy.path=YOUR_HF_USER/MODEL_NAME \ # HF Hub model path or local checkpoint folder
    --policy.revision=latest \              # which version to load — latest or a commit hash
    --policy.temporal_ensemble_coeff=0.01 \ # blends predictions over time to smooth motion
    --policy.n_action_steps=1 \             # actions executed per inference step
    --display_data=true

Parameter reference

FlagWhat to change it to
--robot.portFOLLOWER port from lerobot-find-port
--robot.camerasReplace index_or_path with your /dev/videoX devices
--policy.pathHF Hub path (user/model-name) or local path to a pretrained_model folder
--policy.revisionlatest for the newest version, or paste a specific commit hash to pin a version
--policy.temporal_ensemble_coeff0 = no blending (raw output), 0.01–0.1 = smooth motion, higher = more lag
--policy.n_action_steps1 = re-infer every step (most reactive), higher = execute a full chunk before re-inferring

Mode 2 — Sentry (record evaluation)

Policy drives and always records every episode to a dataset. Use this to build an evaluation set or log policy behaviour over time.

lerobot-rollout \
    --strategy.type=sentry \                # policy drives + always records
    --dataset.repo_id=YOUR_HF_USER/EVAL_DATASET \ # where to push evaluation recordings
    --dataset.num_episodes=10 \             # how many eval episodes to run
    --dataset.episode_time_s=600 \          # max seconds per episode before auto-stopping
    --robot.type=so101_follower \
    --robot.port=/dev/ttyACM0 \             # FOLLOWER port from lerobot-find-port
    --robot.id=so101_follower_7V \
    --robot.cameras='{
        camera1: {type: opencv, index_or_path: /dev/video3, width: 640, height: 480, fps: 30},
        camera2: {type: opencv, index_or_path: /dev/video6, width: 640, height: 480, fps: 30}
    }' \
    --policy.path=YOUR_HF_USER/MODEL_NAME \ # policy to evaluate
    --display_data=true

Parameter reference

FlagWhat to change it to
--dataset.repo_idWhere the recorded eval episodes get pushed — use a separate dataset from your training data
--dataset.num_episodesHow many evaluation runs to record
--dataset.episode_time_sMax episode length in seconds — set long enough that the task can complete
--policy.pathHF Hub path or local checkpoint to the policy you want to evaluate

Mode 3 — Highlight (triggered recording)

Policy drives but nothing is saved automatically. Press S whenever you see something worth keeping — it saves the last N seconds from a rolling buffer.

lerobot-rollout \
    --strategy.type=highlight \             # policy drives, press S to save the last N seconds
    --strategy.ring_buffer_seconds=10.0 \   # how many seconds of history to keep in the buffer
    --dataset.repo_id=YOUR_HF_USER/HIGHLIGHTS \ # where saved clips get pushed
    --robot.type=so101_follower \
    --robot.port=/dev/ttyACM0 \             # FOLLOWER port from lerobot-find-port
    --robot.id=so101_follower_7V \
    --robot.cameras='{
        camera1: {type: opencv, index_or_path: /dev/video3, width: 640, height: 480, fps: 30},
        camera2: {type: opencv, index_or_path: /dev/video6, width: 640, height: 480, fps: 30}
    }' \
    --policy.path=YOUR_HF_USER/MODEL_NAME \
    --display_data=true

Parameter reference

FlagWhat to change it to
--strategy.ring_buffer_secondsSize of the rolling memory window in seconds — press S to save this many seconds of past data
--dataset.repo_idWhere the saved highlight clips get pushed on the Hub
--policy.pathThe policy that will drive the robot autonomously

Mode 4 — DAgger (human-in-the-loop)

Policy drives but you can take over at any time with the leader arm. The recorded data mixes autonomous and human-corrected segments — great for targeted improvement on failure cases.

lerobot-rollout \
    --strategy.type=dagger \                # policy drives, human can intervene with leader arm
    --dataset.repo_id=YOUR_HF_USER/DAGGER_DATA \ # where corrected episodes get saved
    --robot.type=so101_follower \
    --robot.port=/dev/ttyACM0 \             # FOLLOWER port from lerobot-find-port
    --robot.id=so101_follower_7V \
    --teleop.type=so101_leader \
    --teleop.port=/dev/ttyACM1 \            # LEADER port — needed so you can intervene
    --robot.cameras='{
        camera1: {type: opencv, index_or_path: /dev/video3, width: 640, height: 480, fps: 30},
        camera2: {type: opencv, index_or_path: /dev/video6, width: 640, height: 480, fps: 30}
    }' \
    --policy.path=YOUR_HF_USER/MODEL_NAME \
    --display_data=true

Parameter reference

FlagWhat to change it to
--dataset.repo_idWhere DAgger correction episodes are saved — add this data to your training set and retrain
--teleop.portLEADER port — required in DAgger so you can physically take over from the policy
--policy.pathThe policy to run autonomously — typically your latest trained checkpoint
04

Policy architectures

SmolVLA

A Vision-Language-Action model — understands natural language task descriptions alongside camera input. Requires extra dependencies.

# First install the smolvla extras
pip install 'lerobot[smolvla]'

lerobot-rollout \
    --strategy.type=base \
    --policy.path=YOUR_HF_USER/SMOLVLA_MODEL \ # your trained SmolVLA checkpoint
    --dataset.episode_time_s=600 \              # max episode length — set generously for VLA models
    --policy.device=cuda \                      # cuda for GPU, cpu for CPU-only (much slower)
    --display_data=true

Parameter reference

FlagWhat to change it to
--policy.pathPath to your SmolVLA checkpoint on the Hub or locally
--dataset.episode_time_sVLA models are slower to infer — give them more time per episode
--policy.devicecuda for any NVIDIA GPU, cpu if no GPU available (expect very slow inference)

Diffusion policy

Generates actions by iteratively denoising from random noise. More expressive than ACT for multi-modal tasks but slower to infer.

lerobot-rollout \
    --strategy.type=base \
    --policy.path=YOUR_HF_USER/DIFFUSION_MODEL \ # your trained diffusion checkpoint
    --policy.num_inference_steps=5 \             # denoising steps — fewer = faster, less accurate
    --policy.noise_scheduler_type=DDIM \         # DDIM is faster than DDPM at same quality
    --policy.device=cuda \
    --display_data=true

Parameter reference

FlagWhat to change it to
--policy.pathPath to your Diffusion Policy checkpoint
--policy.num_inference_steps5–10 is a good range — lower = faster inference, higher = more accurate actions
--policy.noise_scheduler_typeDDIM for faster sampling, DDPM for highest quality (slower)
--policy.devicecuda for GPU, cpu for CPU-only

Overriding chunk size / action execution

Controls how many consecutive actions the model executes before re-inferring. Higher values are faster but less reactive to scene changes.

lerobot-rollout \
    --strategy.type=base \
    --policy.path=YOUR_HF_USER/MODEL_NAME \
    --policy.n_action_steps=20 \  # execute 20 actions before running inference again
    --display_data=true

Parameter reference

FlagWhat to change it to
--policy.n_action_steps1 = re-infer every step (slowest, most reactive), 20–50 = execute a chunk (faster, less adaptive)
05

Async inference

Splits the policy (GPU compute) and the robot controller (real-time I/O) into two separate processes communicating over a local socket. This removes inference latency from the control loop for higher throughput. Run each command in its own terminal.

Terminal 1 — Policy server

python -m lerobot.async_inference.policy_server \
    --host=127.0.0.1 \ # address the server binds to — use 127.0.0.1 for local, 0.0.0.0 for network
    --port=8080

Parameter reference

FlagWhat to change it to
--host127.0.0.1 to stay local (safe default), 0.0.0.0 to accept connections from other machines on the network
--portAny free port — must match --server_address in the robot client

Terminal 2 — Robot client

python -m lerobot.async_inference.robot_client \
    --server_address=127.0.0.1:8080 \       # must match host:port of the policy server
    --robot.type=so101_follower \
    --robot.port=/dev/ttyACM0 \             # FOLLOWER port from lerobot-find-port
    --robot.cameras='{
        camera1: {type: opencv, index_or_path: /dev/video3, width: 640, height: 480, fps: 30},
        camera2: {type: opencv, index_or_path: /dev/video6, width: 640, height: 480, fps: 30}
    }' \                                     # video devices from lerobot-find-cameras opencv
    --policy_type=act \                     # architecture of the model being served
    --pretrained_name_or_path=YOUR_HF_USER/MODEL_NAME \ # model to load on the server side
    --actions_per_chunk=50

Parameter reference

FlagWhat to change it to
--server_addressIP and port of the policy server — use 127.0.0.1:8080 if both are on the same machine
--robot.portFOLLOWER arm port from lerobot-find-port
--robot.camerasReplace index_or_path with your /dev/videoX devices
--policy_typeArchitecture of your model — act | diffusion | smolvla
--pretrained_name_or_pathHF Hub path or local folder of the model to serve
--actions_per_chunkActions the client executes per server response — higher = lower network overhead, less reactive
06

Utilities & troubleshooting

Replay a recorded episode

Plays back a specific episode from a dataset on the real robot — useful for verifying data quality before training.

lerobot-replay \
    --dataset.repo_id=YOUR_HF_USER/DATASET_NAME \ # the dataset to replay from
    --dataset.episode_index=0

Parameter reference

FlagWhat to change it to
--dataset.repo_idThe HF Hub dataset you want to replay — can be a training or eval dataset
--dataset.episode_index0-indexed episode number — check your dataset on the Hub to see how many episodes you have

Visualize a dataset

Opens a browser-based viewer to browse all episodes in a dataset — inspect camera feeds and joint trajectories before training.

lerobot-dataset-viz \
    --dataset.repo_id=YOUR_HF_USER/DATASET_NAME

Parameter reference

FlagWhat to change it to
--dataset.repo_idAny LeRobot-format dataset on the Hub — yours or someone else's

Calibrate the robot

Run this after assembly or if the arm starts behaving unexpectedly. Saves calibration data to disk so all subsequent commands use it automatically.

lerobot-calibrate \
    --robot.type=so101_follower \   # fixed — do not change
    --robot.port=/dev/ttyACM0

Parameter reference

FlagWhat to change it to
--robot.portThe port of the arm you want to calibrate — run separately for follower and leader if needed

Fix permissions (quick reset)

If cameras or arms stop responding mid-session, run this before anything else.

sudo chmod 666 /dev/video*     # reset camera permissions
sudo chmod 666 /dev/ttyACM*    # reset serial port permissions
Note

If still unresponsive after this, unplug and replug all USB connections and re-run lerobot-find-port — port assignments can shift after a reconnect.

Want to understand how ACT works?

Read our deep dive on the architecture behind the model.

ACT deep dive →