最完整Droid数据集处理指南:从原始机器人数据到训练格式的无缝转换

最完整Droid数据集处理指南:从原始机器人数据到训练格式的无缝转换

【免费下载链接】openpi 【免费下载链接】openpi 项目地址: https://gitcode.com/GitHub_Trending/op/openpi

引言:机器人学习的数据痛点与解决方案

你是否还在为机器人学习项目中的数据格式转换而烦恼?面对DROID平台采集的原始数据,如何将其标准化为可直接用于训练的格式?本文将系统讲解openpi项目中Droid数据集的完整处理流程,从数据解析到格式转换,再到训练准备,帮助你高效完成机器人数据的预处理工作。

读完本文后,你将能够:

  • 理解Droid数据集的结构与特点
  • 掌握使用openpi工具转换Droid数据的方法
  • 了解数据处理中的关键技术点与优化策略
  • 成功将原始机器人数据转换为LeRobot格式用于模型训练

Droid数据集概述

Droid数据集是一个用于机器人学习的高质量数据集,包含机器人在执行各种任务时的传感器数据和动作指令。该数据集主要特点包括:

数据采集平台

  • 基于Panda机械臂(7自由度)
  • 配备多视角摄像头系统(手腕相机和外部相机)
  • 采样频率为15fps

数据内容

  • 图像数据:多个视角的RGB图像
  • 关节数据:关节位置、速度
  • gripper( gripper)状态
  • 动作指令:关节速度和 gripper位置
  • 语言指令:任务描述

数据格式挑战

原始Droid数据以HDF5格式存储,包含复杂的层次结构,直接用于训练前需要进行格式转换和预处理。openpi项目提供了专门的转换工具,可将Droid数据转换为LeRobot格式,便于后续模型训练。

转换工具与环境准备

工具介绍

openpi提供的convert_droid_data_to_lerobot.py脚本是转换Droid数据集的核心工具,位于examples/droid/目录下。该脚本实现了从原始Droid数据到LeRobot格式的完整转换流程。

环境要求

系统要求
  • Linux操作系统(推荐Ubuntu 22.04)
  • Python 3.8+
  • 足够的存储空间(根据数据集大小,至少需要原始数据2倍以上空间)
主要依赖库
  • lerobot:机器人学习数据集处理库
  • h5py:HDF5文件处理
  • OpenCV:图像处理
  • NumPy:数值计算
  • Pillow:图像操作
  • tqdm:进度条显示

安装步骤

  1. 克隆项目仓库:
git clone https://gitcode.com/GitHub_Trending/op/openpi
cd openpi
  1. 使用uv安装依赖:
uv sync

数据转换流程详解

转换工具工作原理

转换工具的核心工作流程如下:

mermaid

主要功能模块

工具主要包含以下功能模块:

  1. 数据读取模块:读取HDF5格式的轨迹数据和MP4格式的图像数据
  2. 数据解析模块:解析原始数据结构,提取关键信息
  3. 图像处理模块:图像大小调整、格式转换
  4. 数据整合模块:将多源数据整合为统一格式
  5. 格式转换模块:转换为LeRobot数据集格式
  6. 输出模块:本地保存或上传到Hugging Face Hub

详细步骤解析

1. 命令行参数解析

工具支持的主要参数:

参数名类型默认值描述
data_dirstr原始数据目录路径
push_to_hubboolFalse是否推送到Hugging Face Hub

使用示例:

uv run examples/droid/convert_droid_data_to_lerobot.py --data_dir /path/to/your/data

如需推送到Hugging Face Hub:

uv run examples/droid/convert_droid_data_to_lerobot.py --data_dir /path/to/your/data --push_to_hub
2. LeRobot数据集初始化

创建LeRobot数据集对象,定义要存储的特征:

dataset = LeRobotDataset.create(
    repo_id=REPO_NAME,
    robot_type="panda",
    fps=15,  # DROID数据通常以15fps录制
    features={
        # 外部相机1左视图
        "exterior_image_1_left": {
            "dtype": "image",
            "shape": (180, 320, 3),  # DROID RLDS数据集使用的分辨率
            "names": ["height", "width", "channel"],
        },
        # 外部相机2左视图
        "exterior_image_2_left": {
            "dtype": "image",
            "shape": (180, 320, 3),
            "names": ["height", "width", "channel"],
        },
        # 手腕相机左视图
        "wrist_image_left": {
            "dtype": "image",
            "shape": (180, 320, 3),
            "names": ["height", "width", "channel"],
        },
        # 关节位置
        "joint_position": {
            "dtype": "float32",
            "shape": (7,),
            "names": ["joint_position"],
        },
        # gripper位置
        "gripper_position": {
            "dtype": "float32",
            "shape": (1,),
            "names": ["gripper_position"],
        },
        # 动作(7维关节速度 + 1维gripper位置)
        "actions": {
            "dtype": "float32",
            "shape": (8,),
            "names": ["actions"],
        },
    },
    image_writer_threads=10,
    image_writer_processes=5,
)
3. 语言标注加载

加载Droid数据集的语言标注:

with (data_dir / "aggregated-annotations-030724.json").open() as f:
    language_annotations = json.load(f)
4. 数据解析与转换

遍历所有 episodes 并进行转换:

episode_paths = list(data_dir.glob("**/trajectory.h5"))
print(f"找到 {len(episode_paths)} 个 episodes 待转换")

for episode_path in tqdm(episode_paths, desc="转换 episodes"):
    # 加载原始数据
    recording_folderpath = episode_path.parent / "recordings" / "MP4"
    trajectory = load_trajectory(str(episode_path), recording_folderpath=str(recording_folderpath))
    
    # 解析 episode_id 以获取语言指令
    metadata_filepath = next(iter(episode_path.parent.glob("metadata_*.json")))
    episode_id = metadata_filepath.name.split(".")[0].split("_")[-1]
    language_instruction = language_annotations.get(episode_id, {"language_instruction1": "Do something"})[
        "language_instruction1"
    ]
    
    # 写入LeRobot数据集
    for step in trajectory:
        camera_type_dict = step["observation"]["camera_type"]
        wrist_ids = [k for k, v in camera_type_dict.items() if v == 0]
        exterior_ids = [k for k, v in camera_type_dict.items() if v != 0]
        dataset.add_frame(
            {
                # 注意:需要将BGR格式转换为RGB格式
                "exterior_image_1_left": resize_image(
                    step["observation"]["image"][exterior_ids[0]][..., ::-1], (320, 180)
                ),
                "exterior_image_2_left": resize_image(
                    step["observation"]["image"][exterior_ids[1]][..., ::-1], (320, 180)
                ),
                "wrist_image_left": resize_image(step["observation"]["image"][wrist_ids[0]][..., ::-1], (320, 180)),
                "joint_position": np.asarray(
                    step["observation"]["robot_state"]["joint_positions"], dtype=np.float32
                ),
                "gripper_position": np.asarray(
                    step["observation"]["robot_state"]["gripper_position"][None], dtype=np.float32
                ),
                # 重要:使用关节速度动作,因为pi05-droid预训练使用关节速度动作
                "actions": np.concatenate(
                    [step["action"]["joint_velocity"], step["action"]["gripper_position"][None]], dtype=np.float32
                ),
                "task": language_instruction,
            }
        )
    dataset.save_episode()
5. 数据上传(可选)

如果指定了--push_to_hub参数,转换完成后会将数据集推送到Hugging Face Hub:

if push_to_hub:
    dataset.push_to_hub(
        tags=["libero", "panda", "rlds"],
        private=False,
        push_videos=True,
        license="apache-2.0",
    )

关键技术点解析

1. 图像数据处理

Droid数据集包含多个视角的图像数据,需要进行统一处理:

def resize_image(image, size):
    image = Image.fromarray(image)
    return np.array(image.resize(size, resample=Image.BICUBIC))

处理流程:

  1. 读取原始图像(BGR格式)
  2. 转换为RGB格式([..., ::-1]实现通道翻转)
  3. 调整大小为(320, 180)
  4. 保存为LeRobot格式

2. 动作数据处理

Droid数据集的动作数据包含关节速度和 gripper位置,需要进行适当拼接:

# 关节速度(7维)+ gripper位置(1维)= 8维动作向量
"actions": np.concatenate(
    [step["action"]["joint_velocity"], step["action"]["gripper_position"][None]], dtype=np.float32
),

注意:pi05-droid模型预训练使用关节速度动作,因此在转换时应使用关节速度而非位置作为动作信号。

3. 多线程图像写入

为提高处理效率,LeRobot数据集支持多线程图像写入:

image_writer_threads=10,  # 线程数
image_writer_processes=5,  # 进程数

这在处理大量图像数据时能显著提升转换速度。

常见问题与解决方案

1. 数据路径问题

问题:找不到轨迹文件或图像文件。

解决方案:确保data_dir参数正确指向包含Droid数据的根目录,且目录结构符合预期:

RAW_DROID_PATH/
  - <...>/
    - recordings/
       - MP4/
         - <camera_id>.mp4  # 单视角视频
    - trajectory.hdf5
  - <...>/

2. 内存占用过高

问题:处理大型数据集时内存不足。

解决方案

  • 增加--max_files参数限制同时处理的文件数
  • 分批次处理数据集
  • 确保系统有足够的交换空间

3. 图像格式不兼容

问题:图像读取或转换错误。

解决方案

  • 检查OpenCV和Pillow库版本是否兼容
  • 验证输入图像文件是否损坏
  • 尝试使用不同的重采样方法(如Image.LANCZOS)

优化建议

1. 数据过滤

对于特别长的轨迹,可以考虑均匀采样以减少数据量:

# 示例:从长轨迹中均匀采样1000个样本
indices_to_save = np.linspace(0, horizon-1, 1000, dtype=int)

2. 并行处理

对于包含大量episodes的数据集,可以考虑并行处理多个episodes:

from concurrent.futures import ProcessPoolExecutor

with ProcessPoolExecutor() as executor:
    executor.map(process_episode, episode_paths)

3. 数据验证

转换完成后,建议对生成的数据集进行基本验证:

# 检查数据集大小是否符合预期
assert len(dataset) > 0, "转换后的数据集为空"

# 检查特征形状是否正确
assert dataset[0]["joint_position"].shape == (7,), "关节位置特征形状不正确"

总结与展望

本文详细介绍了使用openpi工具将Droid机器人数据集转换为LeRobot格式的完整流程,包括环境准备、数据解析、格式转换和优化建议。通过这套流程,你可以高效地将原始机器人数据转换为适合模型训练的格式。

未来,openpi项目计划进一步优化数据转换工具,增加更多自动化预处理功能,如异常检测、数据增强等,以提高机器人学习数据的质量和可用性。

希望本文能帮助你顺利完成Droid数据集的处理工作,为后续的机器人学习研究打下坚实基础。如有任何问题或建议,欢迎参与openpi项目的开发与讨论。

附录:数据集结构对比

数据类型原始Droid格式LeRobot格式
图像数据HDF5中的二进制数据单独的图像文件 + JSON元数据
关节数据嵌套HDF5结构扁平化数组
动作数据分离的关节和 gripper数据合并的8维向量
语言指令单独JSON文件与对应轨迹关联
存储方式单个HDF5文件目录化结构,支持流式访问

【免费下载链接】openpi 【免费下载链接】openpi 项目地址: https://gitcode.com/GitHub_Trending/op/openpi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值