pi05-so101-erythromycin-tea / SO101_PI05_HANDOFF.md
CodeChild's picture
Add model card, deployment handoff, licenses, and release code patch
e30919b verified
|
Raw
History Blame Contribute Delete
18.6 kB
# SO-101 pi0.5 训练与真机部署交接
更新时间:2026-08-04
状态:8000-step 微调和离线评估已完成;**尚未进行真机闭环验证**
本文是当前 checkpoint 的权威交接说明。文中将训练时已经确定的事实和首次上真机的建议明确分开。任何真机客户端都必须先读完“动作语义”和“首次上机流程”,尤其不能把服务端返回值再次当作 delta 累加。
## 1. 一页摘要
| 项目 | 当前结果 |
| --- | --- |
| 任务 | `Pick up the red erythromycin ointment box and place it on top of the green Rizhao tea tin.` |
| 模型 | OpenPI pi0.5 base,全参数微调 |
| 最终 checkpoint | `checkpoints/pi05_so101_erythromycin/pi05_so101_stage1_wandb/7999` |
| 训练量 | 8000 次 optimizer update,batch size 8 |
| 推理参数 | EMA 参数,`ema_decay=0.99` |
| 训练数据 | `clean_train`:67 episodes / 15070 frames |
| 验证数据 | `clean_val`:18 episodes / 3972 frames |
| 未使用数据 | `recovery`:5 episodes / 1515 frames |
| 相机 | 固定相机 `fixed` + 腕部相机 `wrist`,RGB 640x480 @ 30 FPS |
| 状态/动作 | 6 维 SO-101 校准后位置空间 |
| 动作表示 | 前 5 维在模型内部为相对当前 state 的 delta;第 6 维夹爪为 absolute |
| 服务端输出 | `(50, 6)`,已经还原为 SO-101 **绝对目标** |
| 数据/控制频率 | 30 Hz,约 33.33 ms/step |
| 预测 horizon | 50 steps;50 个控制 tick 约 1.667 s |
| 首次真机建议 | 30 Hz 执行,每次只执行前 5 steps 后重规划;这是建议,不是已验证参数 |
| W&B | <https://wandb.ai/99087192-zhejiang-university/openpi/runs/xgk0h74f> |
最重要的三点:
1. policy server 返回的动作已经是 absolute,机器人端不要 `cumsum`,也不要再次加当前 state。
2. 训练数据是 30 Hz。不能把 50 个目标按 10 Hz 执行,否则同一段轨迹会被拉长约 3 倍。
3. 50 是预测长度,不代表首次测试应开环执行完整 50 steps。先执行 5 steps 后重新观测和规划。
## 2. Artifact 和代码来源
仓库:
```text
/home2/czj/AutoResearch/real_machine/so_arm101/openpi_so101_pi05
```
最终 checkpoint:
```text
/home2/czj/AutoResearch/real_machine/so_arm101/openpi_so101_pi05/checkpoints/
pi05_so101_erythromycin/pi05_so101_stage1_wandb/7999
```
目录内容和磁盘占用:
| 目录 | 用途 | 大小 |
| --- | --- | ---: |
| `params/` | 推理必需;保存的是 EMA 参数 | 约 12 GiB |
| `assets/` | 推理必需;包含仅由 `clean_train` 计算的 norm stats | 约 16 KiB |
| `train_state/` | 仅继续训练需要;包含 optimizer state 等 | 约 31 GiB |
| 全部 | 可推理并可续训 | 约 42 GiB |
`7999` 是从 0 开始计数的最终保存 step,对应已经完成 8000 次更新。checkpoint 保存逻辑在存在 EMA 时会将 EMA 参数放进 `params/`,所以 policy server 加载的就是 EMA 推理权重。
当前 OpenPI 上游基准 commit:
```text
15a9616a00943ada6c20a0f158e3adb39df2ccac
```
**可移植性提醒:** SO-101 policy、数据 split 读取和训练脚本目前包含本地尚未提交的适配代码。仅上传权重并指向干净的上游 commit,不能保证能识别 `pi05_so101_erythromycin` 配置。上传 Hugging Face 前,应把这些修改形成一个可检出的 Git commit/tag,或随模型仓库提供完整 patch,至少覆盖:
```text
src/openpi/policies/so101_policy.py
src/openpi/training/config.py
src/openpi/training/data_loader.py
scripts/compute_so101_norm_stats.py
scripts/eval_so101_checkpoint.py
scripts/run_so101_training.sh
scripts/so101_preflight.py
```
## 3. 数据、划分和任务
数据包:
```text
/home2/czj/AutoResearch/real_machine/so_arm101/
so101_erythromycin_on_tea_grid90_v2_portable
```
总数据为 90 episodes / 20557 frames / 685.233 s,SO-101 follower,单一语言任务,两路原始 AV1 视频。训练必须以此文件为准:
```text
splits/split_manifest.json
```
实际划分:
| split | episodes | frames | 是否用于本次训练 |
| --- | ---: | ---: | --- |
| `clean_train` | 67 | 15070 | 是;也只用它计算 norm stats |
| `clean_val` | 18 | 3972 | 只用于离线验证 |
| `recovery` | 5 | 1515 | 否;保留给后续 recovery 实验 |
**绝不能直接使用 `dataset/meta/info.json` 中的 `train: 0:90`。** 那是原始录制范围,不是实验划分;使用它会把 validation 和 recovery 都混入训练。
`clean_val` 是六个完整 held-out layout settings:`setting_01``setting_09``setting_12``setting_17``setting_21``setting_29`。当前没有独立 test split,`clean_val` 仍可能参与 checkpoint 选择,因此不能把它称为最终无偏测试集。
任务 prompt 必须保持完全一致:
```text
Pick up the red erythromycin ointment box and place it on top of the green Rizhao tea tin.
```
## 4. 输入 schema 和相机处理
policy server 的单次输入:
```python
observation = {
"observation/state": state_float32_6,
"observation/fixed_image": fixed_rgb_uint8_hwc,
"observation/wrist_image": wrist_rgb_uint8_hwc,
"prompt": "Pick up the red erythromycin ointment box and place it on top of the green Rizhao tea tin.",
}
```
约束:
- `state_float32_6.shape == (6,)`
- 图像使用 RGB,而不是 OpenCV 默认的 BGR。
- 最稳妥的图像格式是 `uint8`、HWC、范围 `[0, 255]`;分辨率按采集配置为 640x480。
- `fixed` 必须对应训练时的固定相机视角,`wrist` 必须对应腕部视角,不得互换、镜像或旋转。
- OpenPI 内部使用等比例 `resize_with_pad` 变成 224x224,不应在客户端做会改变宽高比的强制拉伸。
- pi0.5 需要三个图像槽;SO-101 适配会把第三个 `right_wrist_0_rgb` 塞零并设置 `mask=false`。客户端不需要发送第三路图像。
### 训练中实际使用的图像增强
本次没有添加 SO-101 专用的自定义增强,但 OpenPI 的标准 JAX 训练预处理在 `train=True` 时确实启用:
- 固定相机:95% 随机裁剪后 resize、随机旋转 `[-5 deg, +5 deg]`、颜色扰动;
- 腕部相机:颜色扰动,不做上述随机裁剪和旋转;
- 颜色扰动参数:brightness `0.3`、contrast `0.4`、saturation `0.5`
- 离线评估和 policy inference 使用 `train=False`,不做随机增强。
因此复现实验时不能将本次训练描述成“完全无图像增强”。
## 5. 状态、动作顺序和校准
state 和 action 的六维顺序完全相同:
| index | LeRobot 名称 | 含义 |
| ---: | --- | --- |
| 0 | `shoulder_pan.pos` | shoulder pan |
| 1 | `shoulder_lift.pos` | shoulder lift |
| 2 | `elbow_flex.pos` | elbow flex |
| 3 | `wrist_flex.pos` | wrist flex |
| 4 | `wrist_roll.pos` | wrist roll |
| 5 | `gripper.pos` | gripper |
这些值不是电机原始 encoder tick,而是 LeRobot SO-101 校准后的 position space:前五维按关节角度使用,夹爪使用线性校准空间,通常映射到约 `[0, 100]`。部署机器必须使用与采集机器一致的关节顺序、方向、零点、角度定义和夹爪标定。
不要只因为数值“看起来在范围内”就假设两台机器人标定一致。首次连接时应逐关节读取 state,与已知安全姿态和采集数据样本对照;发现符号、offset 或夹爪开合方向不一致时禁止下发模型动作。
## 6. Delta 的精确定义
训练配置中的 mask 是:
```python
delta_action_mask = (True, True, True, True, True, False)
```
设发起推理时当前状态为 `s`,数据中的第 `t` 个绝对目标为 `a[t]`。训练输入模型前执行:
```text
d[t, 0:5] = a[t, 0:5] - s[0:5]
d[t, 5] = a[t, 5]
```
这里 50 个未来目标全部减去同一个当前状态 `s`。它不是:
```text
a[t] - a[t-1]
```
也不是每步速度。因此绝对不能沿时间轴对模型结果做 cumulative sum。
推理时 OpenPI 的输出 transform 会执行相反操作:
```text
a_hat[t, 0:5] = d_hat[t, 0:5] + s[0:5]
a_hat[t, 5] = d_hat[t, 5]
```
随后移除模型内部 padding,只返回前 6 维。所以外部收到:
```python
result["actions"].shape == (50, 6)
```
`result["actions"]` 已经是机器人校准空间中的 absolute position targets。机器人客户端只需验证、限幅并按顺序下发;不要再次加 state,不要 `cumsum`
## 7. 30 Hz 和 50-step action chunk
训练 action chunk 按数据集的 30 Hz 采样:
```text
control period = 1 / 30 s = 33.33 ms
action horizon = 50 steps
50 control ticks = 1.667 s
last sampled target offset = 49 / 30 s = 1.633 s
```
### 已确定的事实
- 模型每次预测 50 个顺序目标。
- 每个相邻目标的训练时间间隔是 33.33 ms。
- policy 不会自动决定机器人端执行其中多少个动作。
- 50-step prediction horizon 不等于必须 50-step open-loop execution。
### 首次真机建议,尚未验证
- 机器人目标下发循环保持 30 Hz。
- 初始设置执行前缀 `K=5`,即每次预测后执行约 167 ms,再用新图像和新 state 重规划。
- shadow 和低速测试稳定后,可根据实测推理延迟尝试 `K=5..10`;不要一开始开环执行完整 50 steps。
- 若采用异步推理,记录 observation timestamp、response timestamp、p50/p95 round-trip latency 和实际控制 jitter。执行前缀至少应覆盖正常的 p95 推理时间;可用 `ceil(p95_latency_seconds * 30)` 估算最低 K,再留少量调度余量。
- 如果覆盖 p95 延迟所需的 K 已经大于 10,首次上机不应简单增大开环窗口来掩盖问题;应先降低推理/网络延迟,或采用可安全 hold 的同步流程。
- 丢弃明显过期、乱序或基于旧 observation 的 response。切换到新 chunk 时记录其 observation 序号,避免旧结果覆盖新结果。
- 不要按 10 Hz 直接执行这 50 个动作;那会把约 1.67 s 的训练轨迹拉成约 5 s。
`K=5` 是保守起点,不是已经通过真机成功率验证的超参数。最终 K 应由推理延迟、安全性和真实 rollout 数据共同决定。
## 8. Policy server 启动
在 145 上先检查 GPU,再选择空闲设备:
```bash
nvidia-smi
```
从仓库根目录启动:
```bash
CUDA_VISIBLE_DEVICES=<GPU_ID> .venv/bin/python scripts/serve_policy.py \
policy:checkpoint \
--policy.config pi05_so101_erythromycin \
--policy.dir checkpoints/pi05_so101_erythromycin/pi05_so101_stage1_wandb/7999
```
部署前至少做一次 motors-off 请求,并断言:
```python
actions = np.asarray(result["actions"])
assert actions.shape == (50, 6)
assert np.isfinite(actions).all()
```
若从 Hugging Face 下载的是 inference-only snapshot,snapshot 根目录应直接包含 `params/``assets/`,此时 `--policy.dir` 指向 snapshot 根目录即可。
## 9. 真机安全门和首次 rollout 流程
以下保护应在机器人客户端实现,不能依赖模型自己学会:
1. **形状和数值检查:** 必须是 `(50, 6)` 且全部 finite;出现 NaN、Inf、缺帧或超时立即 hold/stop。
2. **硬件绝对限位:** 按该台 SO-101 的校准和物理限制 clamp 五个关节;夹爪限制在有效线性标定区间。训练集 q01/q99 不是硬件安全限位。
3. **单步变化限制:** 对每个相邻 absolute target 应用 `max_relative_target` 或等价检查,拒绝突跳;同时限制速度和必要的加速度。
4. **工作空间约束:** 禁止桌面穿透、自碰撞、相机线缆拉扯和进入人员区域。
5. **时序保护:** 30 Hz monotonic scheduler;监测 missed deadline、queue underrun、过期 chunk 和相机/state 时间差。
6. **失联行为:** policy server、相机或网络超时后进入定义好的 safe hold/stop,而不是继续无限执行旧 chunk。
7. **现场保护:** 低速/低力矩起步、急停可触达、单人专职观察、首次 rollout 不无人值守。
推荐按以下顺序放行:
### 阶段 A:离线接口检查
- 用一条已录制 observation 请求模型;保存输入图像、state 和 `(50, 6)` 输出。
- 检查 RGB/BGR、相机顺序、图像方向和 prompt。
- 画出六维 action chunk,并比较 `actions[0, :5] - state[:5]`;确认没有明显跳变。
### 阶段 B:shadow inference,电机不执行
- 真机按 30 Hz 采集相机和 state,但仅打印/记录模型动作。
- 统计各维 min/max、最大单步变化、相对当前 state 的最大偏差和推理 p95 latency。
- 人工确认夹爪开合方向、所有关节符号和目标姿态合理。
### 阶段 C:低速闭环
- 从安全 home pose 开始,桌面清空危险障碍物。
- `K=5`,30 Hz,开启全部限位和急停,人工全程监护。
- 先做短时运动并主动停止,再做完整任务。
### 阶段 D:正式评估
- 固定初始姿态、物体布局、光照和相机位置,并记录每次实验配置。
- 每个 layout 做多次独立 rollout;建议至少 10 次,报告成功数和总次数,而不只展示最好视频。
- 同时记录抓取成功、最终放置稳定、掉落、碰撞、人工干预、超时和完成时间。
## 10. 离线评估结果
评估脚本使用 batch size 4、固定随机 seed、无随机图像增强。结果如下:
| 模型 | split | 实际样本数 | flow-matching loss |
| --- | --- | ---: | ---: |
| 原始 pi0.5 base | `clean_val` | 3972 | 0.04753249 |
| 最终微调模型 | `clean_train` | 15068 | 0.00407172 |
| 最终微调模型 | `clean_val` | 3972 | 0.01467515 |
说明:
- `clean_train` 原有 15070 frames;评估按完整 batch 统计,因此使用 15068 个样本。
- 相对原始 pi0.5 base,微调模型的 validation loss 降低约 69.126%。
- 微调模型的 val/train loss 比约为 3.604,存在明显泛化差距。
- validation 是完整 held-out settings,不与 train 重叠;但没有独立 test split。
- flow-matching loss 衡量训练目标,不等于抓取成功率、放置成功率或安全性。
- 在完成受控真机 rollout 前,不能声称该模型已经可以可靠完成任务。
复现最终模型离线验证:
```bash
CUDA_VISIBLE_DEVICES=<GPU_ID> .venv/bin/python scripts/eval_so101_checkpoint.py \
--checkpoint-dir checkpoints/pi05_so101_erythromycin/pi05_so101_stage1_wandb/7999 \
--split-name clean_val
```
## 11. 训练配置记录
W&B run 中记录的实际运行参数优先于源码中的默认值:
| 参数 | 值 |
| --- | --- |
| base weights | `gs://openpi-assets/checkpoints/pi05_base/params` |
| fine-tuning | full parameters,`freeze_filter=Nothing()` |
| updates | 8000 |
| batch size | 8 |
| seed | 42 |
| precision | bfloat16 |
| optimizer | Adam,`b1=0.9``b2=0.95`,gradient clip 1.0 |
| LR | warmup 1000,peak `2.5e-5`,配置的 decay horizon 30000 |
| EMA | 0.99 |
| checkpoint interval | 1000;manager 只保留最新 regular checkpoint |
| W&B run ID | `xgk0h74f` |
训练只运行到 8000 updates,因此 30000-step LR decay schedule 没有完整走完。源码默认训练步数后来仍可显示 30000,不要据此误称本 checkpoint 已训练 30000 steps。
## 12. Hugging Face 发布建议
### 推荐默认:inference-only,约 12 GiB
模型 repo 根目录至少包含:
```text
params/
assets/
_CHECKPOINT_METADATA # 建议保留原始 checkpoint 元数据
README.md # Hugging Face model card
SO101_PI05_HANDOFF.md
LICENSE_GEMMA.txt
NOTICE
LICENSE_OPENPI.txt # 或等价保留 OpenPI Apache-2.0 文本
```
还应提供一个可复现的 OpenPI code commit/tag 或 patch。该 checkpoint 是 Orbax/OpenPI 格式,不是可直接用 `transformers.AutoModel.from_pretrained()` 加载的标准 Transformers 权重;model card 必须明确要求通过 OpenPI policy loader 使用。
### 可选:resumable,约 42 GiB
只有在确实需要继续训练时才额外上传:
```text
train_state/
```
上传 `train_state` 会增加约 31 GiB,并且仍需完全匹配的代码、配置和 optimizer 定义。默认不建议为了推理上传它。
### 许可证和数据权限
- OpenPI 代码为 Apache-2.0。
- 权重由包含 Gemma 的 pi0.5 派生,发布时不能把整个模型简单标成纯 Apache-2.0。
- HF model card 建议使用 `license: other`,正文同时说明 OpenPI Apache-2.0 和 Gemma Terms。
- 分发时保留完整 `LICENSE_GEMMA.txt`,并在 `NOTICE` 中包含:
```text
Gemma is provided under and subject to the Gemma Terms of Use found at ai.google.dev/gemma/terms
```
- 数据包当前明确写着“未分配数据许可证”。除非数据拥有方确认可再分发,否则不要顺手把原始双相机视频或整个数据集上传到公开 HF repo。
- 发布前由发布者确认 Gemma 条款和数据授权;本文不构成法律意见。
### 上传前需要确认的信息
1. HF namespace:使用个人账号 `CodeChild`,还是某个 organization。
2. model repo 名,例如 `pi05-so101-erythromycin-tea`
3. repo 是 `public` 还是 `private`
4. 上传 inference-only(约 12 GiB,推荐)还是 resumable(约 42 GiB)。
5. model card 上的作者、机构、联系方式和希望展示的模型名称。
6. 是否有允许公开链接的数据集 repo;若没有,model card 只描述数据,不上传原始数据。
7. 是否确认按 Gemma Terms 分发派生权重并保留要求的 notice。
8. SO-101 适配代码是发布为 Git commit/tag,还是随模型提供 patch。
本机已检测到可用的 Hugging Face 登录,当前身份为 `CodeChild`。不要在聊天中粘贴 access token;如果要换账号,应在本机运行 `hf auth login`,并使用具有目标 namespace write 权限的 token。
## 13. 发布和上机前最终 checklist
- [ ] HF repo 中同时有 `params/``assets/`,norm stats 没有遗漏。
- [ ] code commit/tag 或 patch 可以构造 `pi05_so101_erythromycin` policy。
- [ ] model card 明确 Orbax/OpenPI 加载方法、delta 语义、30 Hz 和 50-step horizon。
- [ ] model card 不把 flow-matching loss 写成真机成功率。
- [ ] Gemma license 和 `NOTICE` 完整,数据发布权限已确认。
- [ ] 真机校准、关节顺序、方向和 gripper range 与采集端一致。
- [ ] fixed/wrist 图像为 RGB、方向正确、时间同步。
- [ ] 客户端确认输出是 absolute,没有二次加 state 或 `cumsum`
- [ ] 30 Hz 调度、`K=5` 初始前缀、超时 hold、限位和急停均已实现。
- [ ] 完成 motors-off shadow inference 后才进入低速真机测试。
- [ ] 真机结果按多次 rollout 的成功/失败和安全事件完整记录。