Skip to content

域随机化(Domain Randomization)

域随机化是提升机器人策略泛化能力的关键技术,通过在训练数据中引入多样化的环境参数,使模型能够适应真实世界的不确定性。

快速开始

bash
# 基础使用(默认启用视频,每 5 个 episode 录一个)
uv run sim2lerobot scripted collect \
    --enable_dr --num_demos 50 --num_envs 8

# 批量生成 500 条轨迹(渐进式强度)
uv run python scripts/batch_generate_with_dr.py \
    --num_batches 5 --demos_per_batch 100 --num_envs 16 \
    --curriculum --dr_start 0.5 --dr_end 1.2

# 禁用视频以加快速度
uv run sim2lerobot scripted collect \
    --enable_dr --num_demos 100 --no_video

实现的随机化项

本项目在 scripted 数据采集中实现了以下域随机化:

1. 物体初始位置随机化

  • 范围:x ∈ [-0.2, 0.2] m,y ∈ [-0.3, 0.3] m(相对桌面中心)
  • 作用:增加物体摆放位置的多样性,避免模型过拟合特定位置
  • 实现mdp.reset_root_state_uniform

2. 物体质量随机化

  • 范围:原质量的 50% ~ 150%
  • 作用:模拟不同重量的物体,提升抓取和提升动作的鲁棒性
  • 实现mdp.randomize_rigid_body_mass

3. 物体摩擦系数随机化

  • 范围:静态/动态摩擦系数 ∈ [0.4, 1.2]
  • 作用:模拟不同材质表面(光滑/粗糙),提升抓取稳定性
  • 实现mdp.randomize_rigid_body_material

4. 机器人初始关节位置随机化

  • 范围:每个关节 ±5° (±0.087 rad) 小扰动
  • 作用:避免模型依赖精确的初始姿态,提升启动鲁棒性
  • 实现mdp.reset_joints_by_offset

5. 目标位置随机化(扩大范围)

  • 范围
    • x ∈ [0.35, 0.65] m(原本 [0.4, 0.6])
    • y ∈ [-0.3, 0.3] m(原本 [-0.25, 0.25])
    • z ∈ [0.2, 0.6] m(原本 [0.25, 0.5])
  • 作用:增加目标位置的多样性
  • 实现:修改 commands.object_pose.ranges

⚠️ 摩擦系数随机化(暂时禁用)

  • 原计划:静态/动态摩擦系数 ∈ [0.4, 1.2]
  • 状态:因 IsaacLab PhysX backend 的类型转换问题暂时禁用
  • 位置:已在代码中注释并留下扩展点(env_cfg_with_dr.py:128-142

视频录制(默认启用)

视频录制现在默认启用,方便直观检查轨迹质量和域随机化效果:

  • 默认间隔:每 5 个 episode 录一个视频
  • 输出位置data/{task}/scripted/{run_id}/videos/*.mp4
  • 性能开销:~10-20% 时间,每个视频约 600 KB
bash
# 录制所有 episode(详细检查)
uv run sim2lerobot scripted collect \
    --enable_dr --num_demos 20 --video_interval 1

# 禁用视频(快速生成)
uv run sim2lerobot scripted collect \
    --enable_dr --num_demos 100 --no_video

详见:视频录制与域随机化

使用方法

基础用法

启用域随机化,使用标准强度(dr_scale=1.0):

bash
uv run sim2lerobot scripted collect \
    --task lift_cube \
    --num_demos 100 \
    --num_envs 16 \
    --enable_dr

调整随机化强度

--dr_scale 参数控制随机化强度(0.0 ~ 2.0+):

bash
# 弱随机化(50% 强度)
uv run sim2lerobot scripted collect \
    --enable_dr --dr_scale 0.5 \
    --num_demos 100

# 标准随机化(100% 强度)
uv run sim2lerobot scripted collect \
    --enable_dr --dr_scale 1.0 \
    --num_demos 100

# 强随机化(150% 强度)
uv run sim2lerobot scripted collect \
    --enable_dr --dr_scale 1.5 \
    --num_demos 100

批量生成(固定强度)

使用 Bash 脚本批量生成:

bash
# 生成 10 个批次,每批 100 条轨迹,使用 16 个并行环境
bash scripts/batch_generate_with_dr.sh 10 100 16

# 自定义 DR 强度
bash scripts/batch_generate_with_dr.sh 10 100 16 1.5

批量生成(渐进式 Curriculum)

使用 Python 脚本支持渐进式强度增长:

bash
# DR 强度从 0.5 线性增长到 2.0
uv run python scripts/batch_generate_with_dr.py \
    --num_batches 10 \
    --demos_per_batch 100 \
    --num_envs 16 \
    --curriculum \
    --dr_start 0.5 \
    --dr_end 2.0

# 固定强度批量生成
uv run python scripts/batch_generate_with_dr.py \
    --num_batches 5 \
    --demos_per_batch 50 \
    --dr_scale 1.2

批量生成脚本对比

特性Bash 脚本Python 脚本
固定 DR 强度
渐进式 curriculum
元数据记录简单详细 JSON
依赖bash, grep, awkPython 3
推荐场景快速测试生产批量

输出结构

批量生成的输出结构(每批是一个 run 子目录,默认输出根目录是 data/{task}/scripted):

data/lift_cube/scripted/
├── batch_001_20260604_143022/
│   ├── episodes.hdf5
│   ├── run.log                        # collect 自身 stdout(log() tee)
│   ├── batch.log                      # 批量脚本捕获的完整 subprocess 输出(含 stderr)
│   └── meta.json
├── batch_002_20260604_143145/
│   ├── episodes.hdf5
│   ├── run.log
│   ├── batch.log
│   └── meta.json
├── ...
├── batch_010_20260604_145512/
│   └── ...
└── metadata_20260604_143022.json      # 批量元数据(仅 Python 脚本)

元数据文件示例

json
{
  "task": "lift_cube",
  "num_batches": 10,
  "demos_per_batch": 100,
  "num_envs": 16,
  "curriculum": true,
  "dr_start": 0.5,
  "dr_end": 2.0,
  "timestamp": "20260604_143022",
  "total_success": 982,
  "total_failed": 18,
  "batches": [
    {
      "batch_id": 1,
      "batch_name": "batch_001_20260604_143022",
      "dr_scale": 0.5,
      "num_demos": 100,
      "success": 100,
      "failed": 3,
      "output_file": "data/lift_cube/scripted/batch_001_20260604_143022/episodes.hdf5",
      "log_file": "data/lift_cube/scripted/batch_001_20260604_143022/batch.log"
    },
    ...
  ]
}

合并与转换

批量生成后,将所有 HDF5 文件合并并转换为 LeRobot 格式:

bash
# 合并所有批次(convert 对目录输入做递归 glob,自动收集所有 run 子目录下的 hdf5)
uv run python src/sim2lerobot/l3/convert.py \
    --input data/lift_cube/scripted \
    --output data/lerobot/lift_cube_scripted_dr \
    --fps 50

# 检查合并后的数据
uv run python tools/h5_inspect.py data/lerobot/lift_cube_scripted_dr --stats

性能考虑

采集速度

  • 无 DR:~5-10 demos/min(4 envs)
  • 有 DR:~5-10 demos/min(开销可忽略,DR 在 reset 时执行)
  • 加视频:~3-7 demos/min(-30% ~ -40%)

并行环境数

num_envsGPU 内存速度推荐场景
4~2 GB基准开发测试
8~3 GB1.8x日常采集
16~5 GB3.2x批量生产
32~9 GB5.5x大规模(需要 RTX 3090+ 24GB)

批量生成建议

小规模测试(验证 DR 效果):

bash
uv run python scripts/batch_generate_with_dr.py \
    --num_batches 3 \
    --demos_per_batch 20 \
    --num_envs 8 \
    --dr_scale 1.0

中等规模(日常训练数据):

bash
uv run python scripts/batch_generate_with_dr.py \
    --num_batches 10 \
    --demos_per_batch 100 \
    --num_envs 16 \
    --curriculum \
    --dr_start 0.5 \
    --dr_end 1.5

大规模(生产级数据集):

bash
uv run python scripts/batch_generate_with_dr.py \
    --num_batches 50 \
    --demos_per_batch 200 \
    --num_envs 32 \
    --curriculum \
    --dr_start 0.3 \
    --dr_end 2.0

扩展点

当前未实现但可扩展的随机化项:

1. 光照随机化

需要自定义 event term 修改 scene.light 的 intensity:

python
cfg.events.randomize_light_intensity = EventTerm(
    func=custom_randomize_light,
    mode="reset",
    params={"intensity_range": (2000.0, 5000.0)},
)

2. 物体尺寸随机化

需要在 mode="startup" 时调用 mdp.randomize_rigid_body_scale(不能在 reset 时调用)。

3. 相机参数随机化

  • 相机位置/朝向
  • 焦距、视野角
  • 需要先实现相机观测(当前 scripted 只用 proprio)

4. 视觉随机化

  • 纹理、颜色
  • 背景
  • 通常在 L3 数据增强阶段做(如 Cosmos),不在仿真层

验证 DR 效果

1. 可视化轨迹多样性

bash
# 生成带视频的数据
uv run sim2lerobot scripted collect \
    --enable_dr --num_demos 10 --video --video_interval 1

# 查看视频,应该看到:
# - 物体位置各不相同
# - 抓取策略略有差异(适应不同摩擦/质量)

2. 统计分析

python
import h5py
import numpy as np

with h5py.File("data/lift_cube/scripted/batch_001_20260604_143022/episodes.hdf5", "r") as f:
    # 收集所有 episode 的初始物体位置
    positions = []
    for ep_key in f["data"].keys():
        obj_pos = f[f"data/{ep_key}/obs/object_position"][0]  # 初始位置
        positions.append(obj_pos)
    positions = np.array(positions)

    print("物体初始位置分布:")
    print(f"  x: mean={positions[:, 0].mean():.3f}, std={positions[:, 0].std():.3f}")
    print(f"  y: mean={positions[:, 1].mean():.3f}, std={positions[:, 1].std():.3f}")
    print(f"  z: mean={positions[:, 2].mean():.3f}, std={positions[:, 2].std():.3f}")

应该看到 std > 0.05(有显著随机化),而无 DR 时 std < 0.02。

相关文档