Human-to-Robot 数据合成管线
P9 Human-to-Robot 数据合成管线
概述
将人类第一视角(ego-centric)手部操作采集数据,自动转换为 Franka Panda 双臂机器人演示数据, 支持 episode 粒度分布式并行处理。
- 输入:EgoVerse 格式的原始 zarr 数据集(第一视角 RGB 视频 + 手部位姿/关键点 + 头部位姿)
- 输出:LeRobot v3.0 数据集(state/action parquet + H.264 编码视频 + 元信息)+ 3×3 分屏质检视频
- 业务价值:真实机器人数据采集成本高,人手演示采集便宜可规模化。本 pipeline 把人手演示自动转成机器人演示,绕过真机采集,降低具身智能训练数据成本
数据处理分屏 Demo 示例
处理流程
1原始 zarr(人手操作录像 + 位姿)
2 → S1 数据加载清洗 → S2 动作对齐
3 → S3 人手分割(GPU) → S4 背景补全(GPU) → S5 场景深度估计(GPU)
4 → S6 Panda 重定向渲染(GPU,深度感知遮挡合成)
5 → S7 LeRobot 数据集写出 → S8 分屏 Demo(GPU) → S9 数据集校验
算子映射
| 步骤 | 算子 | 说明 |
|---|---|---|
| S1 | EgoVerseLoader | zarr v3 读取 + 哨兵帧过滤 + FPS 重采样 + head-relative 坐标变换 |
| S2 | ActionAlignment | state/action 向量构造 + Savitzky-Golay 平滑 |
| S3 | SAM3HandMask | SAM3 视频预测器时序分割人手/前臂/人体,输出二值 mask |
| S4 | ProPainterInpaint | ProPainter 视频 inpainting,擦除人体补全背景 |
| S5 | DepthAnything3 | DA3 单目深度估计,输出米制场景深度图 |
| S6 | PandaRetarget | 末端位姿 → 6-DOF IK → MuJoCo EGL 渲染 → 深度感知遮挡合成 |
| S7 | LeRobotWriter | LeRobot v3.0 数据集写出 + H.264 编码 + 全局 stats 聚合 |
| S8 | DemoGrid | 3×3 分屏对比视频生成(含 MuJoCo 重渲染),供人工质检 |
| S9 | DatasetValidate | LeRobot 数据集结构校验 + 加载器 smoke test |
数据流向
1<raw_data_root>/(EgoVerse zarr,每个子目录一个 episode)
2 → S1 → workspace/01_load/{ep}.npz
3 → S2 → workspace/02_align/{ep}.npz
4 → S3 → workspace/03_mask/{ep}/masks.npz + person_masks.npz + hand_arm_masks.npz + mask_overlay.mp4
5 → S4 → workspace/04_inpaint/{ep}/bg.mp4 + inpaint_meta.json
6 → S5 → workspace/05_depth/{ep}/scene_depth.npz + depth_vis.mp4
7 → S6 → workspace/06_retarget/{ep}_ik.npz + {ep}_robot_on_bg.mp4 + summary.json
8 → S7 → workspace/07_lerobot/(最终训练数据集)
9 → S8 → workspace/08_demo/{ep}_demo.mp4(质检视频)
10 → S9 → stdout 校验报告
前置条件
| 资源 | 说明 |
|---|---|
| GPU | S3/S4/S5/S6/S8 依赖 GPU,需有 Pro6k(96GB 显存)或同等 GPU |
| CPU | S1/S2/S7/S9 纯 CPU,建议使用独立 CPU 队列避免浪费 GPU 资源 |
| PFS 存储 | 跨 step 共享中间产物,是 pipeline 正常运行的必要条件;按 77MB/episode 预估容量 |
| 镜像 | ccr-registry.baidubce.com/aihc/human2robot:daft-gpu-0.5.2-v4 |
| 模型权重 | 镜像自带 /opt/h2r_assets/:SAM3(sam3/sam3.pt)、Depth-Anything-3-Base(DA3-BASE/)、ProPainter(propainter/)、mujoco_menagerie(mujoco_menagerie/) |
| 样例数据 | 镜像自带 /data/egoverse/narrow_tabletop,可用于验证流程 |
全局参数说明
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
image |
string | 否 | ccr-registry.baidubce.com/aihc/human2robot:daft-gpu-0.5.2-v4 |
|
priority |
string | 否 | high |
任务优先级 |
cpu_queue_id |
string | 是 | — | CPU 步骤队列 ID,在百舸「资源池→队列」查询,格式 aihcq-xxxxxxxx |
gpu_queue_id |
string | 是 | — | GPU 步骤队列 ID,需确认队列中有 Pro6k 或同等 GPU |
gpu_card_resource |
string | 否 | baidu.com/rtx_rpbzzz6_96g_cgpu |
K8s GPU 资源名,固定为 Pro6k 资源名,一般不需要修改 |
pfs_id |
string | 是 | — | PFS 实例 ID,格式 pfs-xxxxxxxx,需与资源池同地域 |
pfs_source_path |
string | 是 | — | PFS 内源路径,即你在 PFS 上的根目录,如 /your-username |
pfs_mount_path |
string | 是 | — | PFS 挂载到容器内的目标路径,格式 /mnt/pfs/xxxxxx,在百舸 PFS 挂载配置页面查看 |
pfs_base_dir |
string | 是 | — | PFS 挂载点下的工作根目录,建议 {pfs_mount_path}/{project},所有产物写入此目录 |
code_root |
string | 否 | /workspace/LoongForge/loongforge/embodied/tools/ego2robot |
镜像内代码根目录,一般不需要修改 |
assets_root |
string | 否 | /opt/h2r_assets |
镜像内模型权重根目录,一般不需要修改 |
raw_data_root |
string | 是 | /data/egoverse/narrow_tabletop |
输入数据根目录,包含所有 EgoVerse zarr episode 子目录(见「步骤详解 S1」中的格式说明) |
run_id |
string | 是 | — | 本次跑批唯一标识,建议带日期如 20260916-run1,同时决定工作区目录名;相同 run_id 会复用断点标记跳过已完成 episode |
target_fps |
string | 否 | 30.0 |
S1 重采样目标帧率,需与 LeRobot 数据集 FPS 一致 |
action_skip |
string | 否 | 5 |
S2 action 前瞻步数,action[t] = state[t+skip](绝对目标位姿),5 帧在 30fps 下约 166ms |
smooth_window |
string | 否 | 11 |
S2 Savitzky-Golay 平滑窗口,只作用于平移列与夹爪列 |
body_prompt |
string | 否 | person |
S3 SAM3 文本 prompt |
mask_mode |
string | 否 | both |
S3 分割通道:both(person 文本 + 手臂几何框)/ person / arms |
mask_dilation |
string | 否 | 4 |
S4 送 ProPainter 前的 mask 膨胀像素,偏小会在 bg.mp4 留人体边缘残影 |
inpaint_chunk_frames |
string | 否 | 120 |
S4 ProPainter 分块帧数,显存不足往下调 |
depth_mode |
string | 否 | depth-aware |
S5/S6 合成模式;改为 alpha 会整段跳过 S5,step2 只跑 mask+inpaint |
depth_epsilon |
string | 否 | 0.02 |
S6 遮挡判定容差(米) |
depth_process_res |
string | 否 | 504 |
S5 DA3 推理分辨率 |
retarget_height |
string | 否 | 360 |
S6 渲染高度,必须与 bg.mp4 一致 |
retarget_fy |
string | 否 | 490.1961 |
S6 相机纵向焦距,用于换算 MuJoCo fovy;缺省值对应样例数据内参 |
robot_preset |
string | 否 | panda_dual_g2 |
S7 机器人布局预设,决定 state/action 维度为 18 |
num_gpus_mask_inpaint_depth |
string | 否 | 0.5 |
S3+S4+S5 每 Actor GPU 占比,Pro6k 上建议 0.5(每卡 2 并发) |
num_gpus_retarget |
string | 否 | 0.5 |
S6 每 Actor GPU 占比 |
num_gpus_demo |
string | 否 | 0.5 |
S8 每 Actor GPU 占比 |
max_fail_ratio |
string | 否 | 0.05 |
单 step 允许的 episode 失败比例,超过则该 step 判定失败 |
head_cpu |
int | 否 | 6 |
Head pod CPU 核数 |
head_memory |
int | 否 | 24 |
Head pod 内存(GB) |
shared_memory |
int | 否 | 64 |
共享内存(GB),ProPainter 在 /dev/shm 落中间帧,step2 必须给足 |
submitter_backoff_limit |
int | 否 | 0 |
RayJob Submitter 启动失败重试次数 |
步骤详解
S1 数据加载清洗(EgoVerseLoader)
读取 EgoVerse zarr episode,清洗数据并标准化。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
target_fps |
string | 30.0 |
重采样目标帧率,统一时间刻度 |
-
输入:
<raw_data_root>/{ep}/,每个 episode 子目录须包含:zarr.json— episode 元信息(帧数、采集帧率等),也是 episode 目录识别依据images.front_1/— 第一视角 RGB 帧(逐帧 JPEG,zarr 格式)obs_head_pose/— 头部位姿 (N, 7)left.obs_ee_pose/、right.obs_ee_pose/— 双手末端位姿 (N, 7)left.obs_keypoints/、right.obs_keypoints/— 手部关键点 (N, K, 3)annotations/— 任务描述文本
- 输出:
workspace/01_load/{ep}.npz(清洗后的位姿、关键点、annotations,12 个字段)
S2 动作对齐(ActionAlignment)
将人手位姿序列转换为机器人训练可用的 state/action 向量。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
action_skip |
string | 5 |
action 前瞻步数,action[t] = state[t+skip] |
smooth_window |
string | 11 |
Savitzky-Golay 平滑窗口 |
- 输入:
workspace/01_load/{ep}.npz - 输出:
workspace/02_align/{ep}.npz(含state(N,16)、action(N,16)、annotations,8 个字段)
16 维 = 左右手 ee_pose 各 7 维(位置 3 + 四元数 4)+ 夹爪宽度各 1 维。
S3 人手分割(SAM3HandMask)
SAM3 视频预测器时序分割人手/前臂/人体,产出二值 mask。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
body_prompt |
string | person |
文本 prompt |
mask_mode |
string | both |
分割通道:both / person / arms |
num_gpus_mask_inpaint_depth |
string | 0.5 |
每 Actor GPU 占比(S3+S4+S5 共用) |
- 输入:原始 zarr 视频帧 + 手部 3D 关键点投影为 2D 几何框提示
- 输出:
workspace/03_mask/{ep}/masks.npz、person_masks.npz、hand_arm_masks.npz、mask_overlay.mp4
S4 背景补全(ProPainterInpaint)
ProPainter 视频 inpainting,按 S3 mask 擦除人体并补全背景。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
inpaint_chunk_frames |
string | 120 |
分块帧数,显存不足往下调 |
mask_dilation |
string | 4 |
送 ProPainter 前的 mask 膨胀像素 |
num_gpus_mask_inpaint_depth |
string | 0.5 |
每 Actor GPU 占比(S3+S4+S5 共用) |
- 输入:原始 zarr 视频帧 +
workspace/03_mask/{ep}/masks.npz - 输出:
workspace/04_inpaint/{ep}/bg.mp4(去人体背景视频)、inpaint_meta.json
S5 场景深度估计(DepthAnything3)
Depth Anything V3 单目深度估计,输出米制场景深度图,供 S6 判断机器人与场景的前后遮挡关系。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
depth_mode |
string | depth-aware |
改为 alpha 时整段跳过本步骤 |
depth_process_res |
string | 504 |
推理分辨率 |
num_gpus_mask_inpaint_depth |
string | 0.5 |
每 Actor GPU 占比(S3+S4+S5 共用) |
- 输入:
workspace/04_inpaint/{ep}/bg.mp4(在去人背景上估深度,避免人手干扰) - 输出:
workspace/05_depth/{ep}/scene_depth.npz(float16,N×360×640,单位米)、depth_vis.mp4
S6 Panda 双臂重定向(PandaRetarget)
将人手末端位姿重定向到 Panda 关节角(IK),MuJoCo EGL 渲染并按深度合成到背景。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
depth_mode |
string | depth-aware |
depth-aware 用深度判定遮挡,alpha 退化为纯 alpha 混合 |
depth_epsilon |
string | 0.02 |
遮挡判定容差(米) |
retarget_height |
string | 360 |
渲染高度,必须与 bg.mp4 一致 |
retarget_fy |
string | 490.1961 |
相机纵向焦距,用于换算 MuJoCo fovy |
num_gpus_retarget |
string | 0.5 |
每 Actor GPU 占比 |
-
输入:原始 zarr(世界系位姿 + 相机参数)+
workspace/02_align/{ep}.npz(仅取帧数 N)workspace/04_inpaint/{ep}/bg.mp4+workspace/05_depth/{ep}/scene_depth.npz
- 输出:
workspace/06_retarget/{ep}_ik.npz(关节角序列 + IK 误差统计)、{ep}_robot_on_bg.mp4(合成视频,640×360)、summary.json
S7 LeRobot 数据集写出(LeRobotWriter)
将所有 episode 的产物打包为 LeRobot v3.0 数据集。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
robot_preset |
string | panda_dual_g2 |
机器人布局,(7,2,7,2) → 18 维 state/action |
-
输入:
workspace/06_retarget/(全部 episode 的_ik.npz与_robot_on_bg.mp4)workspace/02_align/(annotations任务文本)
- 输出:
1workspace/07_lerobot/
2 meta/info.json ← codebase_version "v3.0" / robot_type "panda_dual" / fps 30
3 meta/stats.json ← 各字段统计(min/max/mean/std/quantile)
4 meta/tasks.parquet ← 任务描述文本
5 meta/episodes/ ← 每个 episode 的起止帧索引
6 data/chunk-000/file-000.parquet ← state/action 帧数据(18 维)
7 videos/observation.images.ego/.../file-000.mp4 ← H.264 编码视频
S8 分屏 Demo(DemoGrid)
生成 3×3 分屏对比视频,供人工质检处理效果。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
num_gpus_demo |
string | 0.5 |
每 Actor GPU 占比 |
-
输入:原始 zarr +
workspace/03_mask/+workspace/04_inpaint/workspace/05_depth/+workspace/06_retarget/
- 输出:
workspace/08_demo/{ep}_demo.mp4(3×3 九宫格)
九格内容:原始画面 / 手部关键点 / 夹爪间距曲线 / SAM3 mask / inpaint 背景 / 场景深度可视化 / 纯机器人渲染 / 机器人合成 / IK 误差曲线。
S9 数据集校验(DatasetValidate)
对 LeRobot 数据集做结构与一致性校验。
- 输入:
workspace/07_lerobot/ - 输出:stdout 校验报告,全部通过输出
All validation checks passed!
任务拓扑
1+-------------------+ +-----------------------------+
2| step1-preprocess | | step2-mask-inpaint-depth |
3| S1+S2 (CPU) | \ / S3+S4+S5 (GPU) |
4+-------------------+ \/ +---------------------------+
5 ↓
6 +------------------+
7 | step3-retarget |
8 | S6 (GPU) |
9 +------------------+
10 / \
11 ↓ ↓
12+----------------------+ +------------+
13| step4-lerobot-merge | | step5-demo |
14| S7+S9 (CPU) | | S8 (GPU) |
15+----------------------+ +------------+
5 个工作流 step,step1/step2 并行启动(菱形结构),step3 等两者完成后启动,step4/step5 并行执行。
| 工作流 Step | 包含算子 | 资源 | 依赖 | Ray 形态 |
|---|---|---|---|---|
| step1-preprocess | S1 + S2 | CPU | 无(与 step2 并行) | 每 episode 一个 task |
| step2-mask-inpaint-depth | S3 + S4 + S5 | GPU | 无(与 step1 并行) | Actor 池 |
| step3-retarget | S6 | GPU | step1 + step2 | Actor 池 |
| step4-lerobot-merge | S7 + S9 | CPU | step3(与 step5 并行) | 单 task(barrier) |
| step5-demo | S8 | GPU | step3(与 step4 并行) | Actor 池 |
断点续跑机制
每个 episode 每个步骤完成后写入 .done 标记文件,重新提交工作流时自动跳过已完成的 episode。
1{pfs_base_dir}/workspace-{run_id}/.done/
2 {ep}_s1 {ep}_s2 {ep}_s3 {ep}_s4 {ep}_s5 {ep}_s6 {ep}_s8
标记内容是一行 JSON(时间戳 + 帧数或耗时),仅记录「跑完了」,不校验产物完整性。
- 同一个
run_id重跑 → 只补跑失败/未完成的 episode,已完成的秒过 - 换
run_id重跑 → 新工作区,全量重算 - S7/S9(step4)无 episode 级标记 → 只要被调度就重新扫描
06_retarget ∩ 02_align并重写07_lerobot/
手动触发重跑:删除对应标记后重新提交工作流。
1# 重跑某个 episode 的某步(S3 重算需连带删 s4/s5,否则下游不会更新)
2rm workspace-{run_id}/.done/{ep}_s3 workspace-{run_id}/.done/{ep}_s4 workspace-{run_id}/.done/{ep}_s5
3
4# 重跑某一步的全部 episode
5rm workspace-{run_id}/.done/*_s6
6
7# 重跑全部
8rm -rf workspace-{run_id}/.done/
评价此篇文章
