An Action Chunking Transformer (ACT) policy trained by imitation learning to perform
the manipulation task "put the right banana in the pot" on a Universal Robots UR7e
arm with two RGB cameras.
Policy: ACT (Transformer encoder–decoder with a CVAE), ResNet18 vision backbone
(ImageNet-pretrained), chunk_size = 100, n_action_steps = 100.
Images were captured at 720p and resized on-the-fly to 360×640 during training
(aspect-preserving half-resolution; no dataset re-encode). You must resize every live
camera frame to 360×640 at inference — a different aspect ratio or interpolation degrades
the policy.
Data & hardware setup
Component
Detail
Robot
Universal Robots UR7e — 6-DOF collaborative arm, joints in radians. Inference uses the UR7e follower only.
Teleoperation (data collection)
GELLO low-cost 3D-printed leader arm. Leader signals are recorded but are not policy inputs at inference.
Camera 1
Intel RealSense D435 — RGB only
Camera 2
Intel RealSense D435if — RGB only
Camera streams
1280×720 (720p) @ 30 fps, color only (no depth / IR recorded). ACT is trained on the two RGB views resized to 360×640.
Task
"put the right banana in the pot" — table with distractor objects (2 bananas, apple, carrots/peppers, watermelon slice) and a silver pot; success = the correct banana placed in the pot.
Offline evaluation (open-loop: predicted actions vs. ground truth)
Evaluated on held-out episodes (train vs. held-out gap is negligible → generalizes, no
overfitting). The step 50,000 checkpoint (this model) is the best by held-out L1.
The wrist joint (cmd6) carries the largest error — it is the axis with the most natural
variation. The gripper channel is close to binary.
How to use
Load the policy (LeRobot)
python
1from lerobot.policies.act import ACTPolicy
2from lerobot.policies import make_pre_post_processors
34policy = ACTPolicy.from_pretrained("Bigenlight/act_banana_in_pot")5policy.eval()67# Normalization is NOT baked into forward() in lerobot 0.6.1 — it lives in the8# processor pipeline saved alongside the checkpoint. select_action returns a9# NORMALIZED action; the post-processor converts it back to radians.10preprocessor, postprocessor = make_pre_post_processors(11 policy_cfg=policy.config,12 pretrained_path="Bigenlight/act_banana_in_pot",13)
Per control tick:
python
1policy.reset()# once at the start of each rollout2obs = preprocessor(obs)# normalize + batch + move to device3action = policy.select_action(obs)# normalized4action = postprocessor(action)# radians, numpy (7,)
select_action returns one action per call from an internal queue; on an empty queue it
predicts a full 100-step chunk and replans after 100 executed actions (temporal ensembling
is off by default in this config).
Deploy on a real UR7e
LeRobot ships no UR robot class — you build the observation dict yourself and stream
joint targets with ur_rtde at 30 Hz
(33.3 ms control period). Loop: read getActualQ() + gripper → grab both cameras, BGR→RGB,
resize to 360×640 → build observation.state / observation.images.cam1 / cam2 →
preprocess → select_action → postprocess → servoJ(q_target[:6]) + drive gripper from
grip_cmd.
⚠️ Safety — actions are ABSOLUTE joint positions (the single biggest safety driver):
Start near the dataset initial poseq1..q6 ≈ [2.84, -1.41, 1.78, -2.01, -1.66, -3.42]
rad before enabling the policy, or the first absolute command is a large jump.
Clamp per-tick joint change (e.g. ≤ 0.05–0.1 rad / 33 ms early on) and clamp to UR
software joint limits.
Reduced speed for first trials; keep a hand on the E-stop dead-man switch.
Camera mapping matters: the policy learned a fixed cam1/cam2 → physical-viewpoint
mapping. Swap them and it fails silently. Verify wiring every session.
Grippergrip_cmd is ~binary — threshold (e.g. >0.5 → close) and map to your driver.
A full deployment guide (with a reference ur_rtde skeleton and all the citations to the
lerobot inference/normalization code paths) is in the project's DEPLOY_UR.md.
HIL-SERL prep bundle (reward classifier, SAC config, runbook) for taking this task
online with reinforcement learning:
Bigenlight/banana_in_pot_hilserl
Limitations
Small dataset (51 demos). Metrics reflect an easy, temporally-correlated task; expect
reduced robustness to novel object positions, lighting, or camera placement.
Offline metrics only. All numbers above are open-loop (predicted vs. ground-truth
actions). Real closed-loop task success on the arm has not been measured here — a physical
UR7e deployment is required for the final verdict.
Absolute-joint action space demands the safety guards above; the policy was only ever
conditioned on states near the data-collection start pose.
The ResNet18 backbone is ImageNet-pretrained (not robotics-pretrained); the CVAE and
transformer are trained from scratch on this task.