YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
G1_23 Dex1 取瓶 / 放瓶模型发布包(2026-09-26)
Unitree G1_23(5 自由度手臂)+ Dex1 夹爪真机数据上训练的三个模型,每个带最新两个 checkpoint、推理所需的全部配置,以及从 GitHub 仓库开始推理所需的代码补丁。
| 模型 | 任务 | checkpoint | 训练数据 |
|---|---|---|---|
| openpi π0.5 | place:把瓶子放到红色 X 处 | 16000、19999(最终) |
place-at-red-x-g1-23:99 episode / 22,091 帧 |
| ImageWAM (FLUX.2-klein-4B) | pick:拿起最左边的瓶子 | step_009000、step_010000(最终) |
pick-leftmost-g1-23:181 episode / 50,699 帧 |
| ImageWAM (FLUX.2-klein-4B) | place:把瓶子放到红色 X 处 | step_005000、step_006000(最终) |
place-at-red-x-g1-23:99 episode / 22,091 帧 |
openpi 的 pick 模型仍在训练中,不在本包内。
1. 目录结构
g1_23_release_20260926/
├── README.md
├── SHA256SUMS 所有文件的校验和(sha256sum -c SHA256SUMS)
├── patches/
│ ├── openpi-fintune_g1_23.patch 打在 leihao100/openpi-fintune 上
│ └── imagewam_g1_23.patch 打在 yuyangalin/ImageWAM 上
├── openpi/pi05_g1_23_place_red_x/
│ ├── 16000/ {params/, assets/, _CHECKPOINT_METADATA}
│ └── 19999/ {params/, assets/, _CHECKPOINT_METADATA}
└── imagewam/
├── configs/ 训练/推理用的 hydra 配置(补丁里也有,这里单独放一份方便查看)
│ ├── data/g1_23_dex1_joint.yaml
│ └── task/g1_23_{pick_leftmost,place_red_x}_flux2_klein_4b.yaml
├── g1_23_pick_leftmost_flux2_klein_4b/
│ ├── step_009000.pt step_010000.pt 完整权重(LoRA 已合并),各 17 GB
│ ├── dataset_stats.json 该 run 的归一化统计,推理必须用这份
│ ├── config.yaml 该 run 的完整训练配置(存档)
│ └── g1_23_pick_leftmost_prompt_embeds.npz 3 条指令的 Qwen3 文本嵌入
└── g1_23_place_red_x_flux2_klein_4b/
└── (同上:step_005000.pt、step_006000.pt、dataset_stats.json、config.yaml、*_prompt_embeds.npz)
openpi checkpoint 只包含推理需要的 params 和 assets(归一化统计在 assets/place-at-red-x-g1-23/norm_stats.json)。用于续训的 train_state(优化器状态,每个 13 GB)没有复制,原件在 nnmc65:/shared/user64/workspace/yuhao/pi/openpi-fintune/checkpoints/pi05_g1_23_place_red_x/v1/。
2. 模型接口(三个模型相同)
状态 / 动作:12 维关节空间。手臂单位是弧度,夹爪是 Dex1 原始读数(约 5.35 为张开,约 2.8–4.3 为夹住):
| 维度 | 含义 |
|---|---|
| 0–4 | 左臂 ShoulderPitch, ShoulderRoll, ShoulderYaw, Elbow, WristRoll |
| 5–9 | 右臂(顺序同上) |
| 10 | 左夹爪 |
| 11 | 右夹爪 |
两个任务都只用左臂,右臂和右夹爪基本不动。输出的动作是绝对关节位置,不是增量。
相机:3 路 RGB,训练分辨率 480×640,30 fps。
| 位置 | 数据集里的 key |
|---|---|
| 头部 | observation.images.cam_left_high |
| 左腕 | observation.images.cam_left_wrist |
| 右腕 | observation.images.cam_right_wrist |
指令:必须与训练时逐字一致。
- pick:
Pick up the leftmost {cola / hand soap / water} bottle in the designated target area. - place:
Pick up the {cola / hand soap / water} bottle and place it at the red X-marked target position.
每次推理的输出:
- openpi:
(32, 12)的动作块,对应 32 步 × 1/30 s。 - ImageWAM:
(16, 12)的动作块,对应 16 步。
3. openpi 推理
3.1 准备代码
补丁基于 feat/b601-pick-red-cube 分支的 084dc0f,端到端验证也在这个版本上做的。补丁也能直接打在 main 上,但那种组合没有做过推理验证。
git clone https://github.com/leihao100/openpi-fintune.git
cd openpi-fintune
git checkout 084dc0f # 即 origin/feat/b601-pick-red-cube
git apply <本包>/patches/openpi-fintune_g1_23.patch
GIT_LFS_SKIP_SMUDGE=1 uv sync
补丁改动了以下内容:
src/openpi/policies/unitree_policy.py:UnitreeG1Outputs新增action_dim参数,默认 16,G1_23 用 12。src/openpi/training/config.py:LeRobotUnitreeG1DataConfig新增action_dim;- 新增
pi05_g1_23_pick_leftmost和pi05_g1_23_place_red_x两个 TrainConfig。
src/openpi/transforms.py(及其测试):Normalize会把归一化后的值裁剪到 ±10。训练时开着这个裁剪,所以推理也必须带上,否则右夹爪这类几乎不动的维度,读数稍有偏差就会被放大成极大的输入。scripts/convert_g1_23_v30_to_v21.py:把 G1_23 采集下来的 LeRobot v3.0 数据转换成 v2.1。重新训练时才用得到,推理不需要。scripts/infer_g1_23_offline.py:离线推理检查脚本(见 3.3)。
3.2 起推理服务
uv run scripts/serve_policy.py policy:checkpoint \
--policy.config=pi05_g1_23_place_red_x \
--policy.dir=<本包>/openpi/pi05_g1_23_place_red_x/19999
单张 GPU 即可(验证时用的是一张 L40S 46 GB)。归一化统计会从 checkpoint 的 assets/ 自动读取。
客户端(openpi_client 的 WebSocket 协议)每次发送以下字段:
{
"observation/image": head_rgb, # uint8 HxWx3,头部相机
"observation/left_wrist_image": left_wrist_rgb, # uint8 HxWx3
"observation/right_wrist_image": right_wrist_rgb, # uint8 HxWx3
"observation/state": state_12, # float32 (12,),顺序见第 2 节
"prompt": "Pick up the cola bottle and place it at the red X-marked target position.",
}
返回 {"actions": (32, 12)},即原始单位的绝对关节目标。
3.3 离线检查(可选,不需要接机器人)
uv run scripts/infer_g1_23_offline.py pi05_g1_23_place_red_x \
<本包>/openpi/pi05_g1_23_place_red_x/19999 <place-at-red-x-g1-23 数据集目录>
脚本会从数据集取 5 帧真实观测做推理,并打印预测动作与数据集动作的误差。
4. ImageWAM 推理
4.1 准备代码
补丁基于官方 main,在 5d4a341 上验证通过;它同样能打在 70bc471 上。
git clone https://github.com/yuyangalin/ImageWAM.git
cd ImageWAM
git apply <本包>/patches/imagewam_g1_23.patch
# FLUX.2 官方源码(被 .gitignore 忽略,需要单独 clone,钉在这个提交)
git clone https://github.com/black-forest-labs/flux2 third_party/flux2
git -C third_party/flux2 checkout 50fe5162777813d869182b139e83b10743caef15
# 机器人侧客户端:服务端要从这里导入 msgpack 协议编解码
git clone https://github.com/leihao100/g1-client.git ../g1-client
补丁共 49 个文件,是官方仓库之外的全部 G1 工作:
- G1 的数据处理、夹爪关键帧 loss 加权、推理加速模块(
src/imagewam/...); scripts/g1/下的服务端、控制面板、基准脚本;- 本次新增的内容:
ConcatScatter:把 12 维映射到 RoboTwin 基座的 14 维槽位。左臂 → 0–4,右臂 → 7–11,左夹爪 → 6,右夹爪 → 13,槽位 5 和 12 为填充。- G1_23 的 data / task 配置。
scripts/g1/infer_g1_23.py:离线推理脚本。scripts/g1/run_train_g1_23.sh:训练脚本。- 对
serve_imagewam_g1.py的修复:原来写死了 16 维,而且推理时没有走 merger。修复后按shape_meta取维度,并与训练保持一致(先归一化再 merger,输出反向)。已对照验证:原有 16 维 G1_29 任务的预测结果在修改前后完全一致。
4.2 环境
照 ImageWAM 仓库的安装方式来。以下是 nnmc64/65 上实际能用的组合:
uv venv --python 3.11 .venv
uv pip install --python .venv/bin/python torch==2.7.1 torchvision==0.22.1 torchcodec==0.5 \
--index-url https://download.pytorch.org/whl/cu128
uv pip install --python .venv/bin/python -e ".[shared]"
uv pip install --python .venv/bin/python transformers==4.56.1 gradio==5.50.0 websockets==15.0.1 msgpack==1.2.1
uv pip install --python .venv/bin/python nvidia-npp-cu12==12.4.1.87 peft==0.19.1
注意两点:
transformers必须是 4.56.1,并且要在.[shared]之后装。- torchcodec 0.5 需要 FFmpeg 7(
libavutil.so.59)。系统里没有的话,可以用conda create -n ff7 -c conda-forge ffmpeg=7,再把它加进LD_LIBRARY_PATH。
基座权重:推理时要用来构建模型结构。
| 文件 | 来源 |
|---|---|
flux-2-klein-base-4b.safetensors |
HF black-forest-labs/FLUX.2-klein-base-4B |
ae.safetensors |
HF black-forest-labs/FLUX.2-dev,需在 HF 上接受许可 |
Qwen3 不需要:文本嵌入已经预先导出在 *_prompt_embeds.npz 里。
在仓库根目录建 .env.local,scripts/common.sh 会自动加载它。离线推理要手动 source:
REPO_ROOT=<ImageWAM 路径>
FLUX2_SRC=<ImageWAM 路径>/third_party/flux2
FLUX2_MODEL_PATH=<路径>/flux-2-klein-base-4b.safetensors
FLUX2_AE_MODEL_PATH=<路径>/ae.safetensors
LD_LIBRARY_PATH=<ff7 环境>/lib:<ImageWAM>/.venv/lib/python3.11/site-packages/nvidia/npp/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
4.3 离线推理(不需要接机器人)
cd ImageWAM && set -a && source .env.local && set +a
export PYTHONPATH=$PWD/src:$FLUX2_SRC/src:$FLUX2_SRC
cd scripts/g1
D=<本包>/imagewam/g1_23_pick_leftmost_flux2_klein_4b
CUDA_VISIBLE_DEVICES=0 ../../.venv/bin/python infer_g1_23.py \
--task-config g1_23_pick_leftmost_flux2_klein_4b \
--ckpt $D/step_010000.pt \
--dataset-stats $D/dataset_stats.json \
--prompt-embeds $D/g1_23_pick_leftmost_prompt_embeds.npz \
--dataset-root <pick-leftmost-g1-23 数据集目录> \
--episode 0 --num-calls 5
place 模型的用法相同:把 pick_leftmost 换成 place_red_x,数据集换成 place-at-red-x-g1-23。
4.4 起推理服务
cd ImageWAM && set -a && source .env.local && set +a
export PYTHONPATH=$PWD/src:$FLUX2_SRC/src:$FLUX2_SRC
D=<本包>/imagewam/g1_23_place_red_x_flux2_klein_4b
CUDA_VISIBLE_DEVICES=0 .venv/bin/python scripts/g1/serve_imagewam_g1.py \
--task-config g1_23_place_red_x_flux2_klein_4b \
--ckpt $D/step_006000.pt \
--dataset-stats $D/dataset_stats.json \
--prompt-embeds $D/g1_23_place_red_x_prompt_embeds.npz \
--g1-client-path ../g1-client \
--port 8000
协议与 g1-client 的 PolicyClient 相同(msgpack over WebSocket)。三路图像按"头部、左腕、右腕"的顺序发送,state 为 12 维,返回 (16, 12) 的动作块。
ckpt、dataset_stats.json、prompt_embeds.npz 必须来自同一个任务目录,不能混用。
5. 已做的验证(2026-09-26,nnmc65)
| 模型 | 代码 | 结果(与数据集真实动作的平均绝对误差) |
|---|---|---|
openpi place 19999 |
GitHub 084dc0f + 本补丁(干净 worktree) |
5 帧:输出 (32, 12);手臂 0.0055 rad(约 0.3°);左夹爪 0.008 |
ImageWAM pick step_010000 |
官方 5d4a341 + 本补丁(干净 worktree) |
ep0 5 帧:输出 (16, 12);左臂 0.0126 rad;左夹爪 0.013;单次 0.24 s |
ImageWAM place step_006000 |
同上 | ep40 3 帧:输出 (16, 12);左臂 0.0086 rad;左夹爪 0.0095 |
| 原有 16 维 G1_29 模型(回归测试) | 修改前后的 serve_imagewam_g1.py |
预测输出逐值一致 |
三次推理用的 checkpoint、dataset_stats、prompt_embeds 都直接取自本目录。这些都是离线验证,还没有上真机闭环测试。
6. 注意事项
ImageWAM place 后期有过拟合迹象:
step val_loss action_l1 2000 0.224 0.033 6000 0.343 0.056 验证集只有 1 个 episode(16 个样本),数字噪声较大。本包按要求放的是最新两个 checkpoint;如果真机效果不好,建议试
step_002000/step_003000,原件在nnmc65:/shared/user64/workspace/yuhao/imagewam/ImageWAM/runs/g1_23_place_red_x_flux2_klein_4b/2026-09-26_09-25-59/checkpoints/weights/。ImageWAM pick 没有过拟合:action_l1 从 0.024 一直降到 0.013,最终的
step_010000就是最好的。机器人侧需要适配 G1_23:现有的 g1-client 是按 16 维 G1_29(7 自由度手臂)写的,需要按第 2 节的 12 维顺序组装 state,并把返回的 12 维动作下发给 5 自由度手臂。服务端这边不需要改。
数据格式:训练数据来自
pick_leftmost_g1_23_dex1_lerobot_only_20260922和place_at_red_x_g1_23_dex1_lerobot_only_20260923,经convert_g1_23_v30_to_v21.py转成了 v2.1(视频为 H.264,头部相机的 key 由cam_high改为cam_left_high)。pick 数据剔除了 handsoap_094922Z 的 ep28(右臂下垂)。nnmc65 上用 JAX 多卡训练 openpi 不稳定:10 次 4 卡训练中有 8 次出现 NaN 或 loss 异常(GPU 0–3 和 4–7 上都出现过),原因未查明;单卡训练与推理都正常。这不影响推理。本包里的 openpi place 属于正常的那 2 次之一(在 GPU 4–7 上训练),loss 曲线全程平稳下降(0.117 → 0.0011)。