File size: 5,496 Bytes
3acefc3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
library_name: pytorch
tags: [robotics, rm65, openpi, pi05, imagewam]
---
# RealMan RM65 right-arm policies: pi0.5 (openpi) and ImageWAM (private)

This repo holds two policies finetuned on the same RealMan RM65 right-arm dataset. The dataset has
4 pick-and-hold tasks, 187 episodes and 58,399 frames. Each policy comes with its two latest checkpoints.

| model | training run | checkpoints | size each |
|---|---|---|---|
| pi0.5 (openpi) | `pi05_rm65_4tasks_trim`, 30k steps | `25000`, `29999` (final) | 6.7G (params only, no optimizer state) |
| ImageWAM FLUX.2-klein-4B | `rm65_4tasks_trim_flux2_klein_4b/2026-09-25_07-56-03`, 10 epochs | `step_006000.pt`, `step_006020.pt` (final) | 17.8G |

## Robot interface (both models)

- **State and action:** each is `float32[7]` = `right_q1..right_q6` (joint angles, rad) + `right_gripper`
  (0 = closed, 100 = open). Actions are **absolute** joint targets, not deltas.
- **Cameras:** `top` (head) and `wrist` (right wrist), both 640x480 RGB, recorded at 30 fps.
- **Prompts:** use the exact strings the models were trained on. Each one names the object to pick:
  - `Pick up the rightmost yellow Lay's canister with the right arm and retract while keeping it grasped.`
  - `Pick up the rightmost Nongfu Spring water bottle with the right arm, retract, and hold it without releasing it.`
  - `Pick up the rightmost hand soap bottle, retract the arm, and hold the bottle without releasing it.`
  - `Pick up the rightmost Coca-Cola container, retract the arm, and hold it without releasing it.`

## Training data

The training data merges the 4 per-task deliveries (`rm65_4tasks_187_20260921_delivery`: yellow / nongfu /
handsoap / cocacola) and then trims them:

- **Head:** the static frames at the start of every episode are dropped.
- **Tail:** the static frames at the end are cut to 10.
- **Middle:** short pauses inside an episode are kept.
- **Result:** 66,121 frames down to 58,399.

To rebuild the dataset, run these two scripts in order:
1. `openpi/code/scripts/merge_datasets.py`
2. `openpi/code/scripts/trim_static_frames.py`

## openpi pi0.5 — `openpi/`

- **Checkpoints:** `checkpoints/pi05_rm65_4tasks_trim/<step>/` contains `params/` and
  `assets/rm65/4tasks_v1_trim/norm_stats.json`. The norm stats must stay next to the params they came with.
- **Code** (`code/`):
  - **`rm65_config_excerpt.py`** holds `LeRobotRM65DataConfig` plus the `pi05_rm65_4tasks_trim` TrainConfig.
    Add both to `src/openpi/training/config.py`.
  - **`b601_policy.py`** provides the input/output transforms. RM65 has the same 7-dim, two-camera layout
    as B601, so they are reused unchanged.
- **Model and training setup:**
  - pi0.5 base, `action_horizon=30` (1 s of actions at 30 fps).
  - SigLIP trained fully, the Gemma-2B LLM with LoRA, the action expert fully.
  - Batch 32, 30k steps.
- **Serving:**

      uv run scripts/serve_policy.py policy:checkpoint \
          --policy.config=pi05_rm65_4tasks_trim \
          --policy.dir=<this repo>/openpi/checkpoints/pi05_rm65_4tasks_trim/29999

- **Inference input:** `observation/top`, `observation/wrist` (HxWx3 uint8), `observation/state` (7) and `prompt`.
- **Inference output:** `actions` of shape [30, 7].

## ImageWAM — `imagewam/`

The layout mirrors the ImageWAM repo root, so you can copy this directory over `<IW>/ImageWAM/`:

- **Weights and stats:**
  - `runs/rm65_4tasks_trim_flux2_klein_4b/2026-09-25_07-56-03/checkpoints/weights/step_*.pt` are the
    full model weights, with LoRA merged.
  - **`dataset_stats.json`** in the same run directory holds the normalization stats. It must always be
    paired with the checkpoints from **this run**.
  - `config.yaml` in the same run directory is the resolved training config. Its absolute paths point
    to the original machine.
- **Configs and launch script:**
  - `configs/data/rm65_4tasks_trim.yaml`
  - `configs/task/rm65_4tasks_trim_flux2_klein_4b.yaml`
  - `scripts/rm65/run_train_rm65_4tasks_trim.sh`
- **Text cache:** `dataset/rm65_4tasks_trim/flux2_qwen3_cache_4b/` holds the Qwen3 embeddings of the 4 prompts,
  so you don't need to run Qwen3 again for them.

**How RM65 maps onto the model:** the base checkpoint is the RoboTwin 14-dim joint-space model, with layout
`[arm 6, gripper] x 2`. RM65 occupies the **left-arm slot**:

- **Action and state:** the 7 dims sit at `[0:7]` of the model's 14 dims. `[7:14]` is zero padding and is
  masked out of the loss. Take `action[..., :7]` from the model output.
- **Cameras:** the robotwin 288x256 layout has three slots, filled like this:
  - `cam_high` <- `top`
  - `cam_left` <- `wrist`
  - `cam_right` <- a black (all-zero) frame
- **Action chunk:** 16 steps at 30 fps.

**Training setup:**
- LoRA with rank 16 on the video expert; the action expert is trained fully.
- Learning rate 5e-5.
- Effective batch 96, 10 epochs.
- The loss is upweighted around gripper open/close transitions.

**Serving:** the ImageWAM repo only ships a G1 serving script (`scripts/g1/serve_imagewam_g1.py`,
which is 16-dim and G1-specific). An RM65 server has not been written yet. It has to reproduce the camera
and dimension mapping above.

## Not included

- **FLUX.2 base weights** (needed to build ImageWAM):
  - `flux-2-klein-base-4b.safetensors` from black-forest-labs/FLUX.2-klein-base-4B
  - `ae.safetensors` from black-forest-labs/FLUX.2-dev (gated)
- **pi0.5 base weights:** only needed for retraining, from `gs://openpi-assets/checkpoints/pi05_base`.
- **The dataset itself.**