主题
L2 数据生成层:三种轨迹来源调研
目的:在动手设计本仓库 L2 子结构之前,搞清楚 scripted / RL / teleop 三条采集路径在 IsaacLab 里到底怎么实现、差在哪、最终能不能合到同一个 LeRobot v2 dataset。
配套阅读:LeRobot 版本研究、GR00T 1.7 Dataset 约束、LeRobot v2 Schema、obs/action 对齐。
TL;DR
| 来源 | 量级 | 用途 | 本仓库选型 | Action 写盘 |
|---|---|---|---|---|
| Scripted | 10⁵+ /天(GPU 并行) | 底座技能 / 长尾覆盖 | Warp SM + mimic RecorderManager | 8d IK-abs |
| RL rollout | 10³–10⁴ | recovery / 长尾 / exploration | rsl_rl PPO + joint_pos task + rollout 时录制层覆写[1] | 8d IK-abs(覆写) |
| Teleop | 10²–10³ | 高质量 ground truth | Se3Keyboard IK-rel + 脚本侧复合 abs | 8d IK-abs |
三来源 schema 必须对齐到同一个 8d IK-abs,否则下游 LeRobot v2 转换需按 source 分支,破坏 "一个 dataset 三来源" 的契约。
1. 数据金字塔
混合训练时通常对 teleop 上采样(GR00T N1.5、π0 等)。本仓库下游消费方已锁 GR00T N1.7(CLAUDE.md),N1.5/N1.6 兼容性问题不在范围内。本文出现的 "GR00T N1.5" 等版本号仅用于行业惯例描述(数据金字塔、上采样实践),不是消费方约束。
NOTE
未确认(与本仓库无关):scripted/RL/teleop 官方推荐混合比例;GR00T 训练脚本 collator 是否在采样阶段使用 episodes.jsonl 自定义 source 字段做加权。本仓库只产数据,下游 finetune 不在范围。
2. Scripted policy
采集路径
代表实现:IsaacLab/scripts/environments/state_machine/[2]——lift_cube_sm.py、lift_teddy_bear.py、open_cabinet_sm.py。SM 状态枚举如 REST → APPROACH_ABOVE_OBJECT → APPROACH_OBJECT → GRASP_OBJECT → LIFT_OBJECT,整个 SM 用 Warp kernel 在 GPU 上并行 num_envs 个实例。本仓库 l2/scripted/state_machine.py + l2/scripted/collect.py 改编自 lift_cube_sm.py。
Motion planner(cuRobo / RmpFlow / SkillGen)走另一条路,集成在 mimic 流水线[3]:generate_dataset.py --use_skillgen 走 SkillGen,否则走 mimic 默认线性插值。
WARNING
state_machine/*.py 本身不接 RecorderManager,纯演示用。批量产数据的标准路径是把 SM 当 "种子轨迹生成器" 喂给 ManagerBasedRLMimicEnv,由 mimic 的 datagen[4] replay+扰动后用 RecorderManager 自动写 HDF5。
obs / action 特征
- Action:IK abs(绝对位姿,7-d pose + 1-d gripper),SM 直接在 world frame 算目标 EE pose。
- Observation:proprio + 物体姿态(特权信息)为主,RGB 不强制(训练时再开 camera sensor)。
- 频率:与 env.step 同频,典型 30–60 Hz。
generation_keep_failed 经验法则
MimicEnvCfg.datagen_config.generation_keep_failed 默认 False[5]。各 mimic env_cfg 显式覆写,规律:
- 默认
True(保留失败轨迹):agibot_*、galbot_*、franka_stack_ik_abs_mimic_env_cfg、franka_stack_ik_rel_skillgen_env_cfg、franka_stack_ik_rel_visuomotor_cosmos_mimic_env_cfg、franka_stack_ik_rel_blueprint_mimic_env_cfg、franka_bin_stack_ik_rel_mimic_env_cfg - 默认
False:franka_stack_ik_rel、franka_stack_ik_rel_visuomotor、pickplace_gr1t2、nutpour_gr1t2、exhaustpipe_gr1t2、locomanipulation_g1
经验:成功率高 / 任务难才打开 keep_failed,把失败轨迹当 recovery data 收。本仓库默认跟成功率高的任务对齐 keep_failed=True。
3. RL policy rollout
选型决策
| 方面 | 选择 | 理由 |
|---|---|---|
| RL 框架 | rsl_rl | IsaacLab 现成基础设施最完整;rsl-rl-lib==5.0.1 通过 isaaclab_rl extras 装;OnPolicyRunner / RslRlVecEnvWrapper / RslRl PPO cfg 全直接用 |
| 训练 task | Isaac-Lift-Cube-Franka-v0(joint_pos) | IsaacLab config/franka/__init__.py 只对 joint_pos task 注册了 rsl_rl_cfg_entry_point,IK-Abs task 未注册 PPO cfg |
| Action 对齐 | rollout 时录制层重写[1:1] | PPO 学 IK-abs(含 4d unit quat)的 box action space 不收敛;写自定义 ActionTerm 的工程成本和教学含金量都不划算 |
为什么不在 rollout 之前先 train 一个 IK-Abs PPO:IsaacLab 没提供 cfg,从 0 调一周不一定收敛;教学价值低;joint_pos PPO + 录制层重写能让 RL rollout 跟 scripted/teleop 落到同一个 8d schema。
录制基础设施
HDF5DatasetFileHandler:IsaacLab/source/isaaclab/isaaclab/utils/datasets/hdf5_dataset_file_handler.py:45[6]。- 官方 HDF5 schema:顶层 attrs
format_version=1(v1=XYZW quat,v0=legacy WXYZ);顶层 groupdata/(带total/env_argsattrs);每条 episode 是data/demo_{i}group,attrsnum_samples/seed/success;group 内可递归存任意 nested dict。本仓库data/demos/*.hdf5schema 与官方对齐——l2/scripted/env_cfg.py/l2/teleop/env_cfg.py都直接挂ActionStateRecorderManagerCfg,写出官方data/demo_{i}/{actions, obs/<key>}布局[7]。早期试验期用过的episodes/{i}/...自造布局已弃用。 - 典型用法[8]:
python
env_cfg.recorders: ActionStateRecorderManagerCfg = ActionStateRecorderManagerCfg()
env_cfg.recorders.dataset_export_dir_path = output_dir
env_cfg.recorders.dataset_filename = output_file_name
env_cfg.recorders.dataset_export_mode = DatasetExportMode.EXPORT_SUCCEEDED_ONLYobs / action 特征
- 训练 obs:joint_pos task 的
LiftEnvCfg.ObservationsCfg.PolicyCfg,5 个 obs term,concatenate_terms=True(PPO 要 1-D 向量输入)。 - rollout obs:跟训练同一组 obs term,
concatenate_terms=True不变(PPOMLPModel硬要求 1-D obs);自定义PolicySplitObsRecorder按observation_manager.group_obs_term_dim["policy"]把 1-D obs 切回 dict 写obs/<key>,与 scripted HDF5 schema 一致。 - action:训练时 8d joint_pos(7 个
panda_joint.*+ 1 个panda_finger.*binary gripper);rollout 时录制层覆写为 8d IK-abs 等价[1:2]。 - 频率:
sim.dt=0.01×decimation=2= 50 Hz,跟 scripted 一致。RGB 必须打开 camera sensor,throughput 显著下降。
Episode 元数据
推荐:policy_checkpoint_hash、algo(PPO/SAC/...)、seed、step_return、success bool、action_distribution_stats。RL 失败率天然较高,这正好是 recovery data 的来源——加 success label 后保留是 2026 趋势(AgiBot World 2026 把 "错误恢复轨迹" 列为一等公民)。
4. 人类遥操作(teleop)
设备抽象层
IsaacLab/source/isaaclab/isaaclab/devices/[9]:keyboard/、spacemouse/、gamepad/ 出 6-DoF SE3 delta + gripper;openxr/ 走 VR / 手追踪 + retargeter;haply/ 力反馈。teleop_device_factory.create_teleop_device 按 env_cfg.teleop_devices dispatch。
选型决策(keyboard 最小教学版)
教学定位 + 没有 SpaceMouse / VR 硬件,所以 l2/teleop/ 只支持 keyboard:
| 方面 | 选择 | 理由 |
|---|---|---|
| 设备 | Se3Keyboard 出 7d delta + gripper | 不需硬件,零成本上手;接口跟 SpaceMouse 同形(都是 7d SE3 delta),后续接 SpaceMouse 只是替个 device class |
| Task | Isaac-Lift-Cube-Franka-IK-Abs-v0(与 scripted 同 task) | 写盘 action 直接 8d IK-abs,HDF5 schema 与 l2/scripted / l2/rl 完全一致,L3 convert.py 不需按 source 分支 |
| Action 对齐 | 在 record loop 里手动 rel→abs 复合 | Se3Keyboard.advance() 出 IK-rel 7d delta;脚本读 ee_frame 当前 pose 复合:abs_pos = ee_pos_curr + dpos、abs_quat = quat_mul(delta_quat, ee_quat_curr)、gripper 透传 |
为什么不直接用 IsaacLab IK-Rel task 让 controller 自己复合:那样写盘 actions 列就是 IK-rel 7d,跟 scripted/RL 的 8d 不一致;L3 转换层就要按 source 分支。把复合做在脚本侧让三来源 schema 对齐才是核心契约。
为什么不沿用 IsaacLab 自带 record_demos.py:它强 import isaaclab_mimic[10] 带额外包依赖;XR / IsaacTeleop / SpaceMouse multi-device 流水线对教学最小版过度设计;自己 ~150 行精简版(l2/teleop/record.py)更可控、注释好嵌入。
success 与 reset 模式
- success 走 "连续 N 步 + 手动 export"[11]:把 success DoneTerm 从
terminations抽出None,主 loop 自己跑success_term.func(env, ...)计数。这避免人手抖动让 cube 瞬时进 success 圈触发 auto-reset 把 episode 切短。 time_out=None:默认episode_length_s=5.0太短,给人留足够操作时间,只在 success /object_dropping/ 用户按 R 时 reset。
obs / action 特征
- 几乎一定带多视角 RGB——这是与 scripted/RL 最大的差别,VLA 训练必需图像。本最小版未加,按
l2/scripted/env_cfg.py:20-23的--with_camera扩展点同步加即可。 - Action 空间随设备:keyboard / SpaceMouse → IK rel(SE3 delta + gripper)→ 复合成 8d IK-abs;VR 手追踪 → IK abs 或 retarget 到关节(不在本仓库范围);Leader-follower → 直接关节角(不在本仓库范围)。
- 频率:env step 30 Hz(
record_demos.py --step_hz 30同款),低于 RL 50 Hz,更贴近设备采样率与人类响应延迟。
Episode 元数据
operator_id、device、cloudxr_session_id、success flag、recovery_label、language_instruction。RecorderManager 默认按 success 过滤;generation_keep_failed=True 才留失败 / 恢复轨迹。
5. 三种来源合一个 dataset?
可以,前提是:
- 统一
fps/featuresschema。 - action 空间对齐到同一表示(本仓库选 8d IK-abs)。否则 scripted IK-abs / RL joint_pos / teleop IK-rel 直接冲突,只能放不同 dataset 或用不同
modality.json切片。 task_index跨来源统一编号。- 在
episodes.jsonl加自定义source ∈ {scripted, rl, teleop}字段做分层采样——lerobot loader 用[json.loads(line) for line in f]全量保留 dict,只读episode_index/length,所以安全添加任意自定义字段[12],只要不 shadow 三个标准 key(episode_index/tasks/length)。GR00T 公开 dataset 的episodes.jsonl自带非 spec 字段trajectory_id,这是先例。
IMPORTANT
具体的 LeRobot v2 文件布局、info.json / modality.json / parquet 列约定不在本文展开——以前的版本写过一份"通用 v2 spec"表,但跟 GR00T 1.7 实际期望存在差异(float32 vs float64、next.done/next.reward 写不写、stats schema),容易误导。请直接看 LeRobot v2 Schema 和 GR00T 1.7 Dataset 约束。
IsaacLab 自带 GR00T 转换器(参照实现)
IsaacLab/scripts/imitation_learning/locomanipulation_sdg/gr00t/convert_dataset.py[13]——把 IsaacLab HDF5 转成 GR00T flavor 的 LeRobot v2,约 370 行。这是本仓库 L3 转换器最直接的参照。要点:
- 自带
create_modality_json(state, action):从 dict-of-arrays 自动算 start/end 切片,加固定 video + annotation 字段。 - 自带
create_info_json(...):硬编码codebase_version="v2.0"、splits={"train":"0:100"}、total_chunks=0(注意:跟 v2 标准的total_chunks=ceil(N/chunks_size)不一致;GR00T loader 不检查这个,但本仓库要做更严格的实现)。 cv2.VideoWriter(*"mp4v")写视频,fps=20.0硬编码。mp4v 不是 h264,跟 NVIDIA 公开 dataset 的video.codec="h264"不一致;GR00T 当前不强校验 codec,但更规范的做法是用 ffmpeg + h264。pose_to_transform/compute_relative_pose做坐标系转换(base_pose 相对系),quat 走 scalar-first (w,x,y,z) 约定。
6. 实现难度与教学顺序
实现难度从易到难:Scripted < RL rollout < Teleop。
- Scripted:state_machine 即跑即用,套 mimic RecorderManager 就能批量产数据。
- RL rollout:训练耗 GPU 时但单脚本简单;难点在 reward shaping 与挂
ActionStateRecorderManagerCfg。 - Teleop:要硬件 / CloudXR 配置 / retargeter 调参,但教学含金量最高(直接对应真实采集流程)。
推荐教学顺序:
scripted lift_cube
↓ 用 mimic 扩增成更大数据集
↓ 加 RL play 录小数据集(demonstrate recovery 概念)
↓ 加 SpaceMouse / keyboard teleop 录 10–30 条
↓ 三者合到同一个 GR00T-flavor LeRobot v2
(仓库范围之外)GR00T N1.7 LoRA finetunerollout 时在
l2/rl/env_cfg.py的 rollout-only 配置挂自定义IKAbsEquivalentActionRecorder(RecorderTerm子类):每个 pre_step 用ee_framesensor 当前 EE pose + finger joint 二值化 gripper 组成 8d 等价 IK-abs action,覆写默认PreStepActionsRecorder写出的 8d joint action 列(7 panda_joint + 1 binary gripper);同时覆写obs/actions(last_action term)也用 8d。详见 obs/action 对齐 末尾"l2/rl 实现细节"。teleop 实现细节见同文档末尾"l2/teleop 实现细节"。 ↩︎ ↩︎ ↩︎IsaacLab/scripts/environments/state_machine/—lift_cube_sm.py/lift_teddy_bear.py/open_cabinet_sm.py。 ↩︎IsaacLab/scripts/imitation_learning/isaaclab_mimic/generate_dataset.py(--use_skillgen走 SkillGen);motion planner 实现在IsaacLab/source/isaaclab_mimic/isaaclab_mimic/motion_planners/。 ↩︎IsaacLab/source/isaaclab_mimic/isaaclab_mimic/datagen/generation.py。 ↩︎基类
MimicEnvCfg.datagen_config.generation_keep_failed默认False:IsaacLab/source/isaaclab/isaaclab/envs/mimic_env_cfg.py:35。各 env_cfg 在IsaacLab/source/isaaclab_mimic/isaaclab_mimic/envs/*显式覆写,例如agibot_place_upright_mug_mimic_env_cfg.py:27。 ↩︎IsaacLab/source/isaaclab/isaaclab/utils/datasets/hdf5_dataset_file_handler.py:45。Recorder 实现在IsaacLab/source/isaaclab/isaaclab/envs/mdp/recorders/recorders.py:PreStepActionsRecorder/PreStepFlatPolicyObservationsRecorder/PostStepStatesRecorder/PostStepProcessedActionsRecorder,由RecorderManager在 step 钩子调用。 ↩︎完整 schema 见 HDF5 Schema。 ↩︎
用法抄自
IsaacLab/scripts/tools/record_demos.py:260。 ↩︎IsaacLab/source/isaaclab/isaaclab/devices/:keyboard/se3_keyboard.py、spacemouse/se3_spacemouse.py、gamepad/se3_gamepad.py、openxr/、haply/、teleop_device_factory.py。teleop entry 脚本:IsaacLab/scripts/environments/teleoperation/teleop_se3_agent.py。 ↩︎IsaacLab/scripts/tools/record_demos.py:123强 importisaaclab_mimic。 ↩︎抄自
IsaacLab/scripts/tools/record_demos.py:240-242与391-399。 ↩︎GR00T 公开 dataset 的
episodes.jsonl实例:{"episode_index": 0, "tasks": [...], "length": 316, "trajectory_id": "..."}——trajectory_id是非 spec 字段。Sample 来自 NVIDIA PhysicalAI-Robotics-GR00T-X-Embodiment-Sim。lerobot loader 实现见 HuggingFace LeRobot datasets 模块。 ↩︎IsaacLab/scripts/imitation_learning/locomanipulation_sdg/gr00t/convert_dataset.py。NVIDIA 数据准备文档:Isaac-GR00T data_preparation.md。 ↩︎