Instructions to use JingwuLuo/pick_and_place_cube_quick_ur3 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use JingwuLuo/pick_and_place_cube_quick_ur3 with LeRobot:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Ο0.5 policies for UR3 cube pick-and-place
Ο0.5 policies fine-tuned on teleoperated demonstrations of a dual-arm UR3 (only the right arm moves, 30 Hz,
three cameras) putting an orange, blue or pink cube on a white box, a plate or another cube. The checkpoints come
in three generations, one folder per generation; each generation was trained on a larger cleaned set of
recordings. Only the 107-episode set is published as a dataset
(JingwuLuo/pick_and_place_cube_quick_ur3,
tag v2.1); the 174- and 223-episode sets are not uploaded.
| folder | training episodes | held out | steps | weights | what differs between its policies |
|---|---|---|---|---|---|
107TrialsTrainData/ |
97 | 10 | 5000, 9999 | raw | action-chunk label (3 policies) |
174TrialsTrainData/ |
158 | 16 | 5000, 9999 | raw | action-chunk label (3 policies) |
223TrialsTrainData/ |
223 | 5 | 10000, 19999 | EMA 0.999 | joint representation and state input (3 policies, same label) |
223TrialsTrainData/ is the newest generation.
The policies
223TrialsTrainData: same label, three input/output designs
All three use the curvature-resampled label (span 48, below), the same data and the same recipe; they differ in
what the model predicts and what it sees. The serve flag column is required at serving time: the flag changes the
model configuration that the checkpoint is loaded into.
| folder | joints predicted as | state input to the model | run.py serve flags |
--name |
|---|---|---|---|---|
pi05_ur3_cube_quick_v4h5_curvature_s48_pp/ |
deltas from the observed state (grippers absolute) | none | none | pi05_ur3_cube_quick_v4h5_curvature_s48_pp |
pi05_ur3_cube_quick_v4h5_curvature_s48_abs_pp/ |
absolute joint positions (grippers absolute) | none | --absolute-joints |
pi05_ur3_cube_quick_v4h5_curvature_s48_abs_pp |
pi05_ur3_cube_quick_v4h5_curvature_s48_state_pp/ |
deltas from the observed state (grippers absolute) | the 14-dim state as discrete tokens in the prompt | --state-input |
pi05_ur3_cube_quick_v4h5_curvature_s48_state_pp |
--repo-id for all three: pick-and-place-cube-quick-v4h5-curvature-s48-d0-train (the folder name under
assets/ in each checkpoint).
- Delta policy (
..._curvature_s48_pp): the network outputs, for each of the 10 chunk points, the joint change relative to the state in the observation; the server adds the state back. Quantile statistics are per chunk point; the q01βq99 ranges of the right-arm joint deltas are 0.10β0.36 rad. - Absolute policy (
..._abs_pp): no delta transform; the 10 points are absolute joint angles, normalised with per-point quantile statistics (q01βq99 ranges of the right-arm joints 0.36β2.6 rad). The network has no view of the state, so the joint positions come from the images alone. The server still needsobservation/state: the spline that re-times the points starts there. - State-input policy (
..._state_pp): the state is normalised with its quantile statistics (right-arm joint q01βq99 ranges 0.37β2.6 rad; the gripper entries have range 1) and written into the prompt as 256-bin discrete tokens, the Ο0.5 convention. Left-arm state dimensions, which never change, have their normalisation range floored to 0.05 so that a near-constant dimension does not span the whole token range. Outputs are deltas as in the delta policy. The client must send an accurateobservation/statewith every request; a stale or offset state changes the tokens the model sees as well as the integration of the deltas.
In all three, left-arm action dimensions have a normalisation range floored to 0.05 rad (the left arm stays parked), and the gripper dimensions are absolute (0 open, 1 closed).
107 and 174 folders: three labels
The three policies of a generation differ only in how the action chunk is labelled; all predict deltas, take no state input and need no extra flag.
| policy folder suffix | label | points per chunk | reach of one chunk |
|---|---|---|---|
curvature_s48_pp |
curvature-resampled, span 48 frames | 10 | 48 recorded frames (1.6 s) |
two_phase_s48_pp |
two-phase: evenly spaced in time over 48 frames, one point per frame around grasp, lift and release | 10 | 29 frames median |
time_s10_pp |
per-frame | 10 | 10 frames (0.33 s) |
Folder names: pi05_ur3_cube_quick_all_<suffix> (107) and pi05_ur3_cube_quick_v3_<suffix> (174). --repo-id is
the assets/ folder name inside each checkpoint:
| generation | curvature | two-phase | per-frame |
|---|---|---|---|
| 107 | pick-and-place-cube-quick-all-curvature-s48-d0-train |
pick-and-place-cube-quick-all-two-phase-s48-train |
pick-and-place-cube-quick-all-time-s10-train |
| 174 | pick-and-place-cube-quick-v3-curvature-s48-d0-train |
pick-and-place-cube-quick-v3-two-phase-s48-train |
pick-and-place-cube-quick-v3-time-s10-train |
Contents of a checkpoint
<generation>TrialsTrainData/<policy folder>/<step>/
params/ model weights (orbax)
assets/<repo_id>/norm_stats.json quantile statistics: state (14), actions per chunk point (10, 14)
assets/<repo_id>/arc_resample.json label config and robot block (fps 30, joint velocity limits, gripper holds)
(its `keyframes` field names the keyframes file; the path was shortened to the file name)
_CHECKPOINT_METADATA
The optimizer state is not included. In 223TrialsTrainData the params are the exponential moving average of the
weights.
Serving host
- NVIDIA GPU with CUDA 12. JAX preallocates 75% of GPU memory by default; set
XLA_PYTHON_CLIENT_MEM_FRACTION=0.5to share the GPU (tested at that setting on a 46 GB L40S). - openpi with the patches in
luo-jingw/VLA_AGENTintegrations/openpi/patches/applied, and VLA_AGENT installed in the same environment (pip install -e <VLA_AGENT>). The 107 and 174 checkpoints need commit75f7f11or later; the--absolute-jointsand--state-inputflags of the 223 policies need commit627357eor later. - openpi fetches the PaliGemma tokenizer (
gs://big_vision/paligemma_tokenizer.model) on first use; without internet access, copy~/.cache/openpi/big_vision/from a machine that has it (or setOPENPI_DATA_HOME).
Download one policy:
python -c "from huggingface_hub import snapshot_download; snapshot_download('JingwuLuo/pick_and_place_cube_quick_ur3', \
allow_patterns=['223TrialsTrainData/pi05_ur3_cube_quick_v4h5_curvature_s48_pp/19999/**'], local_dir='ckpt')"
Serve it (from the openpi root). Delta policy:
export PYTHONPATH=scripts:<VLA_AGENT>/src:<VLA_AGENT>/examples/ur3_dual_arm
python <VLA_AGENT>/examples/ur3_dual_arm/run.py serve \
--name pi05_ur3_cube_quick_v4h5_curvature_s48_pp \
--repo-id pick-and-place-cube-quick-v4h5-curvature-s48-d0-train \
--data-root /unused \
--checkpoint ckpt/223TrialsTrainData/pi05_ur3_cube_quick_v4h5_curvature_s48_pp/19999 \
--port 8000 --cap 4 --velocity-fraction 0.5
Absolute policy: the same command with --name pi05_ur3_cube_quick_v4h5_curvature_s48_abs_pp, the matching
--checkpoint folder, and --absolute-joints. State-input policy: --name pi05_ur3_cube_quick_v4h5_curvature_s48_state_pp,
the matching --checkpoint folder, and --state-input. Serving a policy without the flag it was trained with
processes its input and output wrongly: an absolute policy served without --absolute-joints has its absolute
angles added to the state as if they were deltas.
--nameand--repo-idmust be the folder name and theassets/<repo_id>name of the checkpoint (tables above);--data-rootis not read when serving.- Velocity timing: every chunk is timed so that no joint exceeds
velocity_fractionΓ the UR3 limits (3.14 rad/s base, shoulder, elbow; 6.28 rad/s wrists) and it never runs faster thancapΓ the recording's pace; the arm holds still 0.50 s after a gripper close and 0.52 s after an open (measured actuation time). The teleoperation recordings never exceeded 2.5 rad/s: start with--velocity-fraction 0.5and raise it after the first trials.--uniform --cap 1(points at fixed frame spacing, the recording's pace) is meaningful for the per-frame policy only; see the next section.
Resampled (curvature) and two-phase policies: execute the server's targets, never the raw points
The per-frame policy's 10 points are the next 10 control frames. The curvature (every 223 policy) and two-phase policies are not:
- curvature, span 48: the 10 points lie along the next 1.6 s of path, placed by path curvature (denser per unit of joint path in turns, for example the reversal at a grasp or release, and sparser on straight moves; there is no extra density in time or at gripper events); their times are not predicted;
- two-phase, span 48: one point every 4.8 frames during gross motion and one per frame around grasp, lift and release, so a chunk covers anywhere from 10 to 48 frames.
So for these:
- Use the
run.py serveserver above (vla_openpi.launch.serve). It fits a cubic spline from the observed joints through the 10 points and re-times it with velocity timing, returningactionsat 30 Hz. A plain openpiserve_policy.pywould return the raw points, and sending each point as one control frame jumps up to 48 frames of motion per step. - Keep velocity timing (the default); do not pass
--uniform. Uniform timing assumes evenly spaced points, which holds for neither label. - The number of targets per response varies with the path (29 in our test of the curvature policy, several
dozen for a long move); read
len(actions)and execute at most that many. - Around a gripper switch, the response repeats the same target for the hold time (0.50 s close, 0.52 s open): keep sending those repeated targets; they are what makes the arm wait for the gripper.
- Send the measured joints as
observation/state; the spline starts there, so a stale state makes the first targets jump. With asynchronous execution (the arm keeps moving while the next request is in flight), drop the first d targets of the new response, d = latency Γ 30 frames; when the arm waits for each response, start at the first target. arc_pointsandarc_point_framesare for inspection: the points and the frames the server placed them at.
Request and response
Client: openpi_client.websocket_client_policy.WebsocketClientPolicy(host, port).infer(obs).
| key | value |
|---|---|
observation/image |
top camera, uint8 HΓWΓ3 RGB (recorded at 240Γ320) |
observation/left_wrist_image |
left wrist camera, uint8 HΓWΓ3 (240Γ424) |
observation/right_wrist_image |
right wrist camera, uint8 HΓWΓ3 (240Γ424) |
observation/state |
float32 (14): left joints 0β5 (rad), left gripper 6, right joints 7β12 (rad), right gripper 13; a gripper entry is the last gripper command sent (0 open, 1 closed), not the measured opening |
prompt |
English instruction, e.g. Put the pink cube on the white box. |
Images of any size are resized with padding to 224Γ224 by the server; use the same camera placement as the recording. How the state is used depends on the policy: the server of the delta and state-input policies adds it to the predicted deltas to get absolute targets; the absolute policy uses it only as the start of the spline; the state-input policy also feeds it to the model as discrete tokens. In every case the spline starts from it.
Response:
| key | value |
|---|---|
actions |
float32 (N, 14): one absolute target per 30 Hz control frame; actions[j] is the target for frame t + j + 1 after the observation |
arc_points |
float32 (10, 14): the model's points (absolute) |
arc_point_frames |
the frames the points are placed at |
Executing: send actions as joint position targets at 30 Hz (e.g. UR servoJ with t = 1/30 s), execute the
first 4β8 targets, then query again with a fresh observation. Right gripper: close when actions[:, 13] > 0.5.
Keep the left arm where it is (dims 0β6 only reproduce its parked pose). Measured on one L40S, client on the same host: 0.17 s for the first request, 0.08 s after it.
Prompts
Trained with five phrasings per episode drawn at random (Put the orange cube on the white box.,
Pick up the orange cube from the table and place it on top of the white box., Move the orange block onto the white box., Could you stack the orange cube on the white box?, orange cube onto white box). Name the cube's
colour (orange, blue, pink) and the target (white box, plate, or another cube).
Single-move training episodes per task (107 generation):
| cube β target | episodes | cube β target | episodes | |
|---|---|---|---|---|
| orange β white box | 10 | orange β blue cube | 8 | |
| blue β white box | 8 | orange β pink cube | 7 | |
| pink β white box | 11 | blue β pink cube | 4 | |
| orange β plate | 8 | pink β orange cube | 4 | |
| blue β plate | 9 | blue β orange cube | 3 | |
| pink β plate | 8 | pink β blue cube | 3 |
Tasks onto the white box or the plate have the most data; stacking onto another cube, especially pink β blue and blue β orange, has little. The 15 multi-move episodes (stack or unstack two or three cubes) are too few to expect multi-step instructions to work.
Training
| item | 107 and 174 | 223 |
|---|---|---|
| base | pi05_base, action horizon 10 |
same |
| trained | action expert (Gemma 300M) and the action and time projections, 430 M parameters; SigLIP and Gemma 2B frozen | same |
| actions | joints as deltas from the state, grippers absolute; quantile normalisation per chunk point, left-arm joints floored to a 0.05 rad range | delta policy and state-input policy as before; the absolute policy predicts joints as absolute positions |
| state input | none | none, except the state-input policy (256-bin discrete tokens) |
| optimisation | batch 32, 10,000 steps, AdamW (Ξ²2 0.95, weight decay 1e-4, clip 1.0), cosine LR 2.5e-5 β 2.5e-6 after 500 warm-up steps | same, 20,000 steps (about 10 passes over 62,410 training rows) |
| weight averaging | none | EMA, decay 0.999; saved params are the averaged weights |
| prompt | one of the episode's five phrasings, drawn at random per sample | same |
| hardware | one L40S per policy, about 3 s per step | one GPU per policy, 3.1 s per step |
The 223 policies were trained from the same 223 episodes, with 5 held-out episodes (raw recording indices 54, 102, 122, 180, 185). Logged batch loss at the end of training (normalised space; the three designs predict different quantities and their losses are not comparable): delta 0.009, absolute 0.006, state input 0.008.