The lerobot-replay command replays actions from a recorded episode on a physical robot.
Command
Location: src/lerobot/scripts/lerobot_replay.py
Overview
The replay script:
- Loads episode data from a dataset
- Executes recorded actions on the robot
- Useful for verifying dataset quality
- Tests robot hardware functionality
- Demonstrates recorded behaviors
Key Options
Robot Configuration
Robot type: so100_follower, koch_follower, etc.
Serial port for robot connection (e.g., /dev/ttyUSB0).
Unique identifier for this robot instance.
Dataset Configuration
Dataset repository ID (e.g., myuser/my_dataset).
Episode index to replay (0-based).
Local path to dataset. Defaults to $HF_LEROBOT_HOME/{repo_id}.
Playback frames per second. Defaults to dataset fps.
Other Options
Use vocal synthesis to announce events.
Usage Examples
Basic Replay
lerobot-replay \
--robot.type=so100_follower \
--robot.port=/dev/ttyUSB0 \
--robot.id=follower \
--dataset.repo_id=myuser/pick_cube \
--dataset.episode=0
Replay from Local Dataset
lerobot-replay \
--robot.type=so100_follower \
--robot.port=/dev/ttyUSB0 \
--robot.id=follower \
--dataset.repo_id=myuser/my_dataset \
--dataset.root=./local_dataset \
--dataset.episode=5
Bimanual Robot Replay
lerobot-replay \
--robot.type=bi_so_follower \
--robot.left_arm_port=/dev/ttyUSB0 \
--robot.right_arm_port=/dev/ttyUSB1 \
--robot.id=bimanual_follower \
--dataset.repo_id=myuser/bimanual_task \
--dataset.episode=0
Replay at Different Speed
# Slower replay (half speed)
lerobot-replay \
--robot.type=so100_follower \
--robot.port=/dev/ttyUSB0 \
--robot.id=follower \
--dataset.repo_id=myuser/my_dataset \
--dataset.episode=0 \
--dataset.fps=15
# Faster replay (double speed)
lerobot-replay \
--robot.type=so100_follower \
--robot.port=/dev/ttyUSB0 \
--robot.id=follower \
--dataset.repo_id=myuser/my_dataset \
--dataset.episode=0 \
--dataset.fps=60
Replay without Sound
lerobot-replay \
--robot.type=so100_follower \
--robot.port=/dev/ttyUSB0 \
--robot.id=follower \
--dataset.repo_id=myuser/my_dataset \
--dataset.episode=0 \
--play_sounds=false
Workflow
- Load Dataset: Downloads or loads dataset from specified location
- Connect Robot: Establishes connection to robot hardware
- Filter Episode: Extracts actions for specified episode
- Replay Actions: Executes each action at specified FPS
- Disconnect: Safely disconnects from robot
Safety Considerations
- Clear Workspace: Ensure robot workspace is clear before replay
- Emergency Stop: Have emergency stop button accessible
- Speed: Start with slower FPS to verify behavior
- Observation: Monitor robot during replay
- Compatibility: Ensure robot configuration matches dataset
Programmatic Usage
from lerobot.scripts.lerobot_replay import replay
from lerobot.configs.replay import ReplayConfig
from lerobot.robots import RobotConfig
from lerobot.datasets.config import DatasetReplayConfig
config = ReplayConfig(
robot=RobotConfig(
type="so100_follower",
port="/dev/ttyUSB0",
id="follower",
),
dataset=DatasetReplayConfig(
repo_id="myuser/my_dataset",
episode=0,
),
play_sounds=True,
)
replay(config)
Custom Replay Logic
from lerobot.datasets import LeRobotDataset
from lerobot.robots import make_robot_from_config
from lerobot.processor import make_default_robot_action_processor
import time
# Load dataset
dataset = LeRobotDataset(
"myuser/my_dataset",
episodes=[0]
)
# Create robot
robot_config = RobotConfig(
type="so100_follower",
port="/dev/ttyUSB0",
id="follower",
)
robot = make_robot_from_config(robot_config)
robot.connect()
# Get episode actions
episode_frames = dataset.hf_dataset.filter(
lambda x: x["episode_index"] == 0
)
# Replay loop
action_processor = make_default_robot_action_processor()
try:
for idx in range(len(episode_frames)):
start_time = time.perf_counter()
# Get action
action_array = episode_frames[idx]["action"]
action = {
name: action_array[i]
for i, name in enumerate(dataset.features["action"]["names"])
}
# Process and send
robot_obs = robot.get_observation()
processed_action = action_processor((action, robot_obs))
robot.send_action(processed_action)
# Maintain FPS
elapsed = time.perf_counter() - start_time
time.sleep(max(0, 1/dataset.fps - elapsed))
finally:
robot.disconnect()
Troubleshooting
Robot Not Moving
- Verify robot is calibrated:
lerobot-calibrate --robot.type=... --robot.port=...
- Check robot connection and power
- Verify episode contains valid actions
- Check action names match robot configuration
Jerky Motion
- Ensure
--dataset.fps matches original recording FPS
- Check for communication delays on serial port
- Verify robot can achieve commanded positions
Episode Not Found
# List available episodes
python -c "
from lerobot.datasets import LeRobotDataset
ds = LeRobotDataset('myuser/my_dataset')
print(f'Total episodes: {ds.num_episodes}')
print(f'Episode indices: 0-{ds.num_episodes-1}')
"
See Also