Lightweight arm control for robot learning.
Minimal, ROS-free Python stack for high-frequency control of direct-drive robot arms. Teleoperation, data collection, and learned policy deployment — everything between bare metal and your research code.
Built for I2RT YAM bimanual arms.
| No ROS | Direct CAN bus at 100 Hz. One process, one config, one launch command. |
| Plug-and-play teleop | Viser web UI, GELLO leader arms, Pico VR — swap with a YAML change. |
| Policy-ready | Ship your VLA as a server, point limb at it, run inference. |
| Data collection | Hands-free episode recording with foot pedal / VR button triggers. |
Docs: CLI Reference · Teleoperation · Data Collection · Policy Server Spec
git clone --recurse-submodules https://github.com/TToTMooN/limb
cd limb
curl -LsSf https://astral.sh/uv/install.sh | sh # install uv if needed
uv venv --python 3.11
uv syncSubmodule: Includes i2rt (motor driver) under
dependencies/. Clone with--recurse-submodules.
VR teleop: XRoboToolkit SDK requires a separate install after
uv sync:bash scripts/install_xrobotoolkit_sdk.sh
echo 'SUBSYSTEM=="net", KERNEL=="can*", ACTION=="add", RUN+="/sbin/ip link set %k up type can bitrate 1000000"' \
| sudo tee /etc/udev/rules.d/99-can.rules
sudo udevadm control --reload-rules && sudo udevadm triggerVerify: ip link show | grep can — expect can_follow_l and can_follow_r.
See docs/teleop.md for GELLO and VR hardware setup.
All common operations are exposed through a single limb CLI. Full reference in docs/cli.md.
uv run limb --help # list subcommands
uv run limb <cmd> --help # flags for one subcommanduv run limb teleop --config-path configs/yam_viser_bimanual.yaml # Viser web UI
uv run limb teleop --config-path configs/yam_gello_bimanual.yaml # GELLO leader arms
uv run limb teleop --config-path configs/yam_vr_bimanual.yaml # Pico VRCollection configs are overlays — combine with any teleop config:
uv run limb record --config-path configs/yam_gello_network_bimanual.yaml configs/collection_pedal.yaml
uv run limb record --config-path configs/yam_vr_bimanual.yaml configs/collection_vr.yamluv run limb teleop --config-path configs/yam_pi0_bimanual.yaml # OpenPI (pi0)
uv run limb teleop --config-path configs/yam_policy_bimanual.yaml # Generic WebSocket# Hardware discovery (cameras, CAN, pedals, etc.)
uv run limb devices
# Replay a recorded episode on hardware (robot config read from episode metadata)
uv run limb replay --episode-dir recordings/red_cube_task/episode_20260304_153045_0001
# Visualize a recorded episode in Rerun
uv run limb visualize --episode-dir recordings/red_cube_task/episode_20260304_153045_0001
# Convert a session to LeRobot v2.1 format (no lerobot dependency needed)
uv run limb convert-lerobot --input-dir recordings/red_cube_task --output-dir datasets/red_cube
# Only include successful episodes
uv run limb convert-lerobot --input-dir recordings/red_cube_task --output-dir datasets/red_cube --success-only
# Upload a dataset (S3 / GCS / HuggingFace Hub)
uv run limb upload --source datasets/red_cube --target hf://myuser/red_cubeuv run scripts/diagnostics/test_realsense_cameras.py
uv run scripts/diagnostics/test_gello_input.py
uv run scripts/diagnostics/test_vr_input.pylaunch.py → control loop @ 100 Hz
├─ Agent.act(obs) → action # teleop device or VLA policy server
├─ session.step(obs, action) # episode recording + trigger management
├─ RobotEnv.step(action) # CAN bus → DM motors
└─ ViserMonitor.update(obs) # camera feeds + URDF viz
limb/
envs/ # launch.py, robot_env.py, config loader
agents/ # teleop (Viser, GELLO, VR) + policy (OpenPI, WebSocket)
robots/ # YAM CAN driver, IK solvers (Pink, pyroki)
devices/ # GELLO reader, JoyCon gripper, VR client
recording/ # episode recorder, triggers, session manager
visualization/ # Viser URDF + camera monitor
sensors/ # RealSense, OpenCV, ZED camera drivers
configs/ # YAML launch configs
robot_configs/ # per-arm hardware specs
from limb.agents.agent import Agent
class MyAgent(Agent):
def act(self, obs):
return {"left": {"pos": target_joints}, "right": {"pos": target_joints}}agent:
_target_: my_package.MyAgentuv run ruff check limb/ && uv run ruff format limb/Python 3.11 · uv · tyro for CLI · loguru for logging · ruff (line length 119)
Inspired by GELLO and robots_realtime.