Research code for driving a SO-ARM101 robot arm with Vision-Language-Action (VLA) models and multimodal LLMs. A researcher types a task prompt ("pick up the pen"), a policy backend turns camera frames + joint state into joint targets, and every action passes a safety validator before it reaches the servos.
The repo has two tracks:
control/) — the active core. A plugin harness runs 16+ interchangeable policy backends against the physical arm. GPU models are hosted on Google Colab and reached over ngrok tunnels.LIBERO/) — the LIBERO/MuJoCo benchmark extended with a SOARM robot model, used for sim inference, demonstration collection and OpenVLA-OFT fine-tuning (Phases 1–7). control/run_vla_episode.py --backend <name> (local Mac, arm on USB)
│
├─ Backend registry control/vla_bridge/backends/
│ ├─ gRPC lane → Colab LeRobot PolicyServer (ngrok TCP, port 5173)
│ ├─ HTTP lane → Colab resident Flask adapters (ngrok HTTP, ports 3001–3006)
│ └─ Cloud lane → Gemini / Claude / GPT / HF APIs (direct HTTPS)
│
├─ safety_validator.py joint-limit clamp, NaN reject, step/velocity caps, stale-obs hold
├─ io_logger.py per-step JSON Lines (frames, state, raw/validated action, latency)
└─ stereo + depth AR0144 stereo camera → FastFS depth service on Colab (port 3000)
│
▼
SO-ARM101 follower (LeRobot SO101Follower, Feetech STS3215 bus)
All backends implement one ActionSource protocol (get_action(joint_state, instruction)). Backends are discovered automatically: drop a module in backends/ with @register(...) and a matching backend_configs/<name>.json.
| Lane | Backends | Hosted by |
|---|---|---|
| gRPC (LeRobot 0.6.1 PolicyServer) | smolvla, pi0, pi05, act, diffusion, groot-n17 | control/vla_bridge/notebooks/grpc_*.ipynb |
| HTTP adapters | openvla-oft (3001), wallx (3002), openvla (3003), octo (3004), rdt1b (3005), groot-n15 (3006) | control/vla_bridge/notebooks/http_*.ipynb |
| Direct cloud (VLA) | gemini-robotics, gemini-robotics-2 | none |
| Direct cloud (MLLM, raw-JSON joint chunks) | mllm-claude, mllm-gpt, mllm-hf-qwen, mllm-hf-deepseek, mllm-hf-kimi | none |
| Local | scripted | none |
control/ Real-hardware harness, teleop, recording, tests
run_vla_episode.py Episode entry point (--backend)
vla_bridge/
backends/ One module per backend + base_*.py lane helpers + action conversions
backend_configs/ <backend>.json (checkpoint, endpoint, action space, camera map)
notebooks/ GENERATED per-backend Colab notebooks + README (do not hand-edit)
*_server.py Colab model servers and patched gRPC launchers
colab_server_utils.py Colab process/port/diagnostics helper
backend_smoke.py Robot-free live check of a backend
test_*.py pytest suite (~1.7k tests)
scripts/ Colab notebook generator, URDF conversion/verification
diagnostics/ Servo bring-up, stereo calibration, UAT material
So-101/ SO-101 URDF + meshes
LIBERO/ Vendored LIBERO benchmark with the SOARM robot + Colab notebooks
explorations/ Dataset and scene exploration scripts
.planning/ GSD planning state (roadmap, phases, codebase map)
cd control
python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python -m pytest -q # full suite, no hardware needed
Provider API keys (Gemini, Anthropic, OpenAI, HF) are read from environment variables; see control/.envrc. control/COMMANDS.md lists the day-to-day commands (calibration, teleop, recording).
control/vla_bridge/notebooks/ (index and per-model notes in its README.md) on a fresh GPU runtime./content (sha256-verified), launches the server, checks that its own process owns the port, and opens an ngrok tunnel. HF and ngrok tokens are read from Google Drive or prompted for./health) if anything looks off, and the teardown cell when done.Gated checkpoints need an HF token that has accepted the licence (google/paligemma-3b-pt-224 for pi0/pi05, nvidia/Cosmos-Reason2-2B for GR00T N1.7).
From control/, smoke-test the backend without the robot, then run an episode:
.venv/bin/python -m vla_bridge.backend_smoke --backend smolvla \
--server-address <ngrok-host:port> --instruction "pick up the pen"
.venv/bin/python run_vla_episode.py --backend smolvla --out outputs/run \
--server-address <ngrok-host:port> --instruction "pick up the pen"
Ctrl+C is the e-stop; every exit path returns the arm to its start pose before disconnecting.
Notebooks are generated. Edit the server source (control/vla_bridge/*_server.py) or the spec (scripts/colab_notebook_specs.py), then regenerate from the repo root:
python scripts/build_colab_notebooks.py # rewrite notebooks
python scripts/build_colab_notebooks.py --check # drift check (also run by control/test_colab_notebooks.py)
Roadmap and history: .planning/ROADMAP.md. Codebase reference docs: .planning/codebase/ (stack, architecture, structure, conventions, testing, integrations, concerns).
transformers==5.5.4.diffusion_policy_server.py, groot_n17_server.py) monkey-patch lerobot 0.6.1 internals and refuse any other lerobot version.backend_configs/*.json record verified revisions.See .planning/codebase/CONCERNS.md for the full list.
Research code for driving a SO-ARM101 robot arm with Vision-Language-Action (VLA) models and multimodal LLMs. A researcher types a task prompt ("pick up the pen"), a policy backend turns camera frames + joint state into joint targets, and every action passes a safety validator before it reaches the servos.
The repo has two tracks:
control/) — the active core. A plugin harness runs 16+ interchangeable policy backends against the physical arm. GPU models are hosted on Google Colab and reached over ngrok tunnels.LIBERO/) — the LIBERO/MuJoCo benchmark extended with a SOARM robot model, used for sim inference, demonstration collection and OpenVLA-OFT fine-tuning (Phases 1–7). control/run_vla_episode.py --backend <name> (local Mac, arm on USB)
│
├─ Backend registry control/vla_bridge/backends/
│ ├─ gRPC lane → Colab LeRobot PolicyServer (ngrok TCP, port 5173)
│ ├─ HTTP lane → Colab resident Flask adapters (ngrok HTTP, ports 3001–3006)
│ └─ Cloud lane → Gemini / Claude / GPT / HF APIs (direct HTTPS)
│
├─ safety_validator.py joint-limit clamp, NaN reject, step/velocity caps, stale-obs hold
├─ io_logger.py per-step JSON Lines (frames, state, raw/validated action, latency)
└─ stereo + depth AR0144 stereo camera → FastFS depth service on Colab (port 3000)
│
▼
SO-ARM101 follower (LeRobot SO101Follower, Feetech STS3215 bus)
All backends implement one ActionSource protocol (get_action(joint_state, instruction)). Backends are discovered automatically: drop a module in backends/ with @register(...) and a matching backend_configs/<name>.json.
| Lane | Backends | Hosted by |
|---|---|---|
| gRPC (LeRobot 0.6.1 PolicyServer) | smolvla, pi0, pi05, act, diffusion, groot-n17 | control/vla_bridge/notebooks/grpc_*.ipynb |
| HTTP adapters | openvla-oft (3001), wallx (3002), openvla (3003), octo (3004), rdt1b (3005), groot-n15 (3006) | control/vla_bridge/notebooks/http_*.ipynb |
| Direct cloud (VLA) | gemini-robotics, gemini-robotics-2 | none |
| Direct cloud (MLLM, raw-JSON joint chunks) | mllm-claude, mllm-gpt, mllm-hf-qwen, mllm-hf-deepseek, mllm-hf-kimi | none |
| Local | scripted | none |
control/ Real-hardware harness, teleop, recording, tests
run_vla_episode.py Episode entry point (--backend)
vla_bridge/
backends/ One module per backend + base_*.py lane helpers + action conversions
backend_configs/ <backend>.json (checkpoint, endpoint, action space, camera map)
notebooks/ GENERATED per-backend Colab notebooks + README (do not hand-edit)
*_server.py Colab model servers and patched gRPC launchers
colab_server_utils.py Colab process/port/diagnostics helper
backend_smoke.py Robot-free live check of a backend
test_*.py pytest suite (~1.7k tests)
scripts/ Colab notebook generator, URDF conversion/verification
diagnostics/ Servo bring-up, stereo calibration, UAT material
So-101/ SO-101 URDF + meshes
LIBERO/ Vendored LIBERO benchmark with the SOARM robot + Colab notebooks
explorations/ Dataset and scene exploration scripts
.planning/ GSD planning state (roadmap, phases, codebase map)
cd control
python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python -m pytest -q # full suite, no hardware needed
Provider API keys (Gemini, Anthropic, OpenAI, HF) are read from environment variables; see control/.envrc. control/COMMANDS.md lists the day-to-day commands (calibration, teleop, recording).
control/vla_bridge/notebooks/ (index and per-model notes in its README.md) on a fresh GPU runtime./content (sha256-verified), launches the server, checks that its own process owns the port, and opens an ngrok tunnel. HF and ngrok tokens are read from Google Drive or prompted for./health) if anything looks off, and the teardown cell when done.Gated checkpoints need an HF token that has accepted the licence (google/paligemma-3b-pt-224 for pi0/pi05, nvidia/Cosmos-Reason2-2B for GR00T N1.7).
From control/, smoke-test the backend without the robot, then run an episode:
.venv/bin/python -m vla_bridge.backend_smoke --backend smolvla \
--server-address <ngrok-host:port> --instruction "pick up the pen"
.venv/bin/python run_vla_episode.py --backend smolvla --out outputs/run \
--server-address <ngrok-host:port> --instruction "pick up the pen"
Ctrl+C is the e-stop; every exit path returns the arm to its start pose before disconnecting.
Notebooks are generated. Edit the server source (control/vla_bridge/*_server.py) or the spec (scripts/colab_notebook_specs.py), then regenerate from the repo root:
python scripts/build_colab_notebooks.py # rewrite notebooks
python scripts/build_colab_notebooks.py --check # drift check (also run by control/test_colab_notebooks.py)
Roadmap and history: .planning/ROADMAP.md. Codebase reference docs: .planning/codebase/ (stack, architecture, structure, conventions, testing, integrations, concerns).
transformers==5.5.4.diffusion_policy_server.py, groot_n17_server.py) monkey-patch lerobot 0.6.1 internals and refuse any other lerobot version.backend_configs/*.json record verified revisions.See .planning/codebase/CONCERNS.md for the full list.