ODE Policy — R1 Lite "microwave" (two arms)
Checkpoints of the ODE policy trained on real-robot demonstrations of the Galaxea R1 Lite using both arms: the right arm puts a bowl into the microwave, the left arm works the door. The policy learns a velocity field ȧ = f(c, s, a) over the 12 joint targets of both arms and produces each 8-step action chunk by integrating that field (RK4) from the current target, instead of denoising a chunk from noise.
Files
| Path | Content |
|---|---|
checkpoints/{50,100,150,200,250,300}.ckpt |
Full training checkpoints (698 MB each): model, ema_model, optimizer state dicts + the pickled Hydra config. Serve the EMA weights (the loader does this by default). |
config/config.yaml, config/overrides.yaml |
Resolved training config and the command-line overrides of this run |
logs/logs.json.txt |
Per-step / per-epoch training log (loss, lr, epoch time, val_loss every 5 epochs) |
SHA256SUMS |
Checksums of every uploaded file |
Checkpoints are torch.save(..., pickle_module=dill) payloads written by the ODE_Policy training runner.
Loading them needs that codebase at a version with two-arm support (branch r1lite_exp, commit e9cc9c0
or later: ODEImagePolicy with n_grippers / state_pose_index) and
torch.load(..., pickle_module=dill, weights_only=False) — only load checkpoints you trust.
Data
at237299966/r1lite_microwave:
LeRobot v2.1, 34 episodes / 14,432 frames at 15 Hz, single task "microwave".
| Input / output | Definition |
|---|---|
| Cameras | head_rgb (720×1280), right_wrist_rgb and left_wrist_rgb (360×640), padded to square then resized to 256×256 (same geometry as the robot client) |
agent_pos |
14-D, the robot client's order: [L1..L6 (rad), L_grip (mm), R1..R6 (rad), R_grip (mm)] |
ODE state a |
the 12 absolute joint targets [L1..L6, R1..R6] (rad). Row convention: a[t] = commanded target at t−1 (episode start: measured joints) |
| Gripper head | binary per gripper (left, right), +1 close / −1 open; the label flips where the operator's gripper ramp starts |
| Window | 8 observation frames, 8-step action chunk |
Training
| Model | ResNet18 (GroupNorm, random crop) per camera (3 cameras), 8 frames → condition c (512); vector field = 4-layer FiLM-MLP, width 512; proprioception enters the field only (cond_use_s=false); separate gripper head (2 logits per step) |
| Integration | RK4, 8 control steps per chunk, vel_scale=0.05; observation fixed over the chunk (obs_update=fixed) |
| Start perturbation | 50% of samples, per-joint std 0.005 rad except R2 0.0414 and R3 0.0276 rad, decay 0.7 per step |
| Optimisation | AdamW, lr 1.5e-4, cosine schedule over 300 epochs, warm-up 500 steps, EMA |
| Batch | 2 × A100-40GB (DDP), 40 per GPU = 80 global; 175 steps/epoch, 52,500 steps in total |
| Wall clock | 9 h 0 min (108 s/epoch) |
| epoch | 0 | 10 | 50 | 100 | 150 | 200 | 250 | 300 |
|---|---|---|---|---|---|---|---|---|
| train loss | 1.38 | 0.28 | 0.080 | 0.046 | 0.029 | 0.018 | 0.013 | 0.011 |
| val loss | 0.85 | 0.98 | 1.14 | 1.31 | 1.55 | 1.74 | 1.93 | 2.00 (ep 295) |
The validation loss is a weak signal: one held-out episode, raw (non-EMA) weights, with the training start perturbation still active. Its minimum is at epoch 5 (0.80) and it rises steadily afterwards (34 demos), so compare the early and late checkpoints on the robot before picking one.
Deployment
The robot side is the unmodified openpi-r1lite closed-loop client (main_r1lite_closedloop.py, 15 Hz,
executes row 0 of every (8, 14) reply; it must send all three cameras). The server reads the two-arm
layout from the checkpoint, re-integrates a chunk every 8 ticks from the last target it sent and walks
the client through it; both arms are commanded (no left-arm echo):
python -m roboverse_learn.il.deploy.serve_policy \
--checkpoint checkpoints/100.ckpt --host 0.0.0.0 --port 8000 --device cuda:0 \
--gripper_close 0 --gripper_open 100 --gripper_ramp_ticks 28 24 # gripper ramps: left right
python -m roboverse_learn.il.deploy.test_client --host 127.0.0.1 --port 8000 -n 40 --bimanual --gripper_range 0 100 # pre-flight
Set the torso to the demonstrations' pose [-0.6845, 1.4986, 0.8031] rad before a run (it is not part of
the policy's state). Measured on an A100: ~21 ms on re-planning ticks, < 0.1 ms on the others; loopback
round trip p95 35 ms (budget 66.7 ms). Code: ODE_Policy repository, neural_ode/real/ (data +
training) and roboverse_learn/il/deploy/ (server).