File size: 5,593 Bytes
a2565ec | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | ---
license: gemma
library_name: openpi
pipeline_tag: robotics
tags:
- robotics
- vla
- pi0
- pi05
- openpi
- franka
- manipulation
- imitation-learning
---
# Οβ.β
β place the cube in the bowl (real Franka)
A [Οβ.β
](https://github.com/Physical-Intelligence/openpi) vision-language-action policy
fine-tuned on **100 real Franka Emika teleoperation demonstrations** of *"place the cube
in the bowl"*.
Serving code, robot client and the full I/O contract:
**[github.com/sleepmastergx/deploy, branch `pi0.5`](https://github.com/sleepmastergx/deploy/tree/pi0.5/pi05_place_cube)**
## Checkpoints
Four training steps are published so you can evaluate across them. Only `params/` and
`assets/` are here β `train_state/` (optimizer state, 32 GB per step) is not needed for
inference and is not published.
| folder | step | β epochs | size |
|---|---|---|---|
| `5000/` | 5,000 | 10 | 12.4 GB |
| `10000/` | 10,000 | 20 | 12.4 GB |
| `15000/` | 15,000 | 31 | 12.4 GB |
| `19999/` | 20,000 (final) | 41 | 12.4 GB |
openpi numbers steps 0-indexed, so `19999` is the completed 20,000-step model.
**Start with `19999`, but do not assume it is best.** 100 demonstrations over ~41 epochs
is a deep-overfit regime; training loss reached 0.0016, which says the model fit the data,
not that it will place cubes. Only real-robot rollouts settle which checkpoint to use β if
`19999` looks memorised or brittle, work backwards.
`assets/george/place_cube_new_first100/norm_stats.json` inside each folder holds that
checkpoint's normalization statistics. **They travel with the checkpoint and must not be
mixed between runs** β the policy cannot be served without them, and the wrong ones
produce mis-scaled actions rather than an error.
## Training
| | |
|---|---|
| Base | `pi05_base` (openpi's robot-pretrained Οβ.β
) |
| Data | 100 episodes / 15,624 frames @ 10 Hz, single Franka + 2 RealSense cameras |
| Steps | 20,000, batch 32, AdamW, cosine 5e-5 β 5e-6 after 1k warmup, EMA 0.999 |
| Model | `action_dim=32`, `action_horizon=16`, `discrete_state_input=True` |
| Hardware | 2 Γ H200, ~4 h 25 m |
| Final loss | 0.0016 (from 0.163) |
## Usage
```bash
hf download SleepMastger/pi05_place_cube --local-dir ./pi05_place_cube
git clone -b pi0.5 https://github.com/sleepmastergx/deploy.git
cd deploy/pi05_place_cube
PYTHONPATH=$PWD/src python serve_pi05.py --ckpt ../../pi05_place_cube/19999
```
Requires openpi installed (JAX + CUDA, Python β₯ 3.11) and a GPU with > 8 GB VRAM.
## Interface
The policy server expects exactly these keys β no batch dimension, no client-side
resizing, no client-side normalization:
```python
{
"observation/image": uint8 (256, 256, 3), # third-person / agentview
"observation/wrist_image": uint8 (256, 256, 3), # wrist
"observation/state": float (8,), # RAW, unnormalized
"prompt": "place the cube in the bowl",
}
```
with
```python
state[0:3] = eef_pos # metres
state[3:6] = quat2axisangle(eef_quat) # eef_quat is (x, y, z, w), scalar-last
state[6] = +gripper_width / 2 # metres (~0.0796 open, ~0.029 closed)
state[7] = -gripper_width / 2
```
Returns `(16, 7)` actions, already un-normalized:
- dims `0:3` β `dx, dy, dz` delta end-effector position in **metres per 100 ms step**
- dims `3:6` β rotation, **always β 0**; this policy cannot rotate (training saw only
zero rotations)
- dim `6` β gripper, **1.0 = OPEN, 0.0 = CLOSE**, threshold at 0.5
Control rate is **10 Hz**. Predict 16, execute ~8, re-plan.
> The gripper polarity is the easiest thing to get backwards, so to be explicit: **1 is
> open.** It was verified against the training data β `action[6]` steps 1 β 0 at exactly
> the frame `state[6]` falls 0.0398 β 0.0146 (width 0.0796 β 0.029, closing on the cube).
State is not a side channel: with `discrete_state_input=True` it is normalized, binned
into 256 buckets and written into the *text prompt*, so a wrong state changes what the
model reads. Full spec in
[CONTRACT.md](https://github.com/sleepmastergx/deploy/blob/pi0.5/pi05_place_cube/CONTRACT.md).
## Verification
Replaying held-out recorded episodes through the served policy and scoring against logged
actions:
| | episode_0 | episode_1 |
|---|---|---|
| xyz RMSE | 0.00019 m | 0.00020 m |
| ground-truth per-step RMS | 0.00106 m (error/signal 0.18) | 0.00121 m (0.16) |
| max \|rpy\| | 3.3e-09 | 3.6e-09 |
| gripper match | 100 % (96/96) | 100 % (96/96) |
| inference | ~51 ms | ~51 ms |
Reproduce with `tools/replay_test.py` in the deploy repo. This measures whether a
deployment reproduces the training contract β **not** whether the policy succeeds at the
task, which only real rollouts can tell you.
## Limitations
- **No rotation control.** Rotation was constant zero in every training episode.
- **One task, one scene, one camera setup.** 100 demonstrations from a single rig. Expect
nothing outside that distribution.
- **Deep-overfit regime.** See the checkpoint note above.
- **Camera framing matters.** The policy is sensitive to it, and a mismatch degrades
behaviour silently β it keeps acting confidently and does the wrong thing. Reproduce the
collection framing, and run the replay test before trusting a new setup.
## License
Released under the [Gemma Terms of Use](https://ai.google.dev/gemma/terms). This model is
fine-tuned from openpi's `pi05_base`, whose vision-language backbone is PaliGemma/Gemma,
so Gemma's terms carry through to this derivative. The openpi *code* is Apache-2.0
separately.
|