Daft 加载 DROID 机器人数据集实战:从 raw() 场景分类到 HDF5 轨迹与相机帧解码

发布时间:2026/9/17 2:14:32
Daft 加载 DROID 机器人数据集实战:从 raw() 场景分类到 HDF5 轨迹与相机帧解码 Daft 加载 DROID 机器人数据集实战从 raw() 场景分类到 HDF5 轨迹与相机帧解码【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft本文以 Daft 的daft.datasets.droid模块为主体讲解如何在 Daft 中将 DROIDDistributed Robot Interaction Dataset这一大规模机器人操作数据集当作惰性 DataFrame 来探索从raw()的 episode 目录发现与元数据展开到scenes()场景分类表连接、trajectory()对 HDF5 轨迹的按需读取再到camera_frames()对三路相机 MP4 的抽帧解码。读完后你可以直接在 GCS 公共桶或本地目录上运行这些 API并理解其底层由from_glob_path、Hdf5File、video_frames等函数拼成的调用链。DROID 数据集与 Daft 的访问模型DROID 是目前最常用的野外in-the-wild机器人操作数据集之一包含 76,000 条演示轨迹、约 350 小时交互数据采集于 564 个场景、86 个任务使用 Franka Panda 机器人平台完成数据中包含同步的 RGB 相机流、相机标定参数以及自然语言任务描述。Daft 的访问模型是把原始 DROID 发布版本加载为惰性lazy的、episode 级别的 DataFrame——元数据直接展开为顶层列trajectory.h5轨迹文件挂载为daft.Hdf5File列三路相机 MP4 挂载为daft.VideoFile列。在真正触发collect()/show()之前不会下载任何视频或轨迹数据这让你可以先做过滤、投影、采样再物化大文件。Quickstart三行代码预览原始数据最简单的入手方式是直接从公开数据源加载并预览import daft # Load a sample of the raw DROID data daft.datasets.droid.raw().show(3)输出是一个包含 25 列的宽表每个 episode 一行例如首行的类型信息为列名类型uuid/lab/user/building/robot_serialStringdateDatescene_id/trajectory_lengthInt64successBooltrajectoryFile[Hdf5]wrist_cam_video/ext1_cam_video/ext2_cam_videoFile[Video]wrist_cam_extrinsics等List[Float64]实际样例数据会显示诸如GuptaLab553d1bd52023-07-09-…的 episode uuid、gs://gresearch/robotics/droid…的 episode 目录以及Hdf5(path: gs://gresearch/rob…)、Video(path: gs://gresearch/ro…)这样的惰性文件引用。每一行对应一个 DROID episode该 episode JSON 文件中的元数据被展开unnest为顶层列同时附上轨迹 HDF5 文件和三路 MP4 相机录像的惰性文件引用。Episode 目录布局每个 DROID episode 存储在独立目录中episode/ |---- metadata_episode_id.json # Episode metadata (building, task, camera serials, etc.) |---- trajectory.h5 # Low-dimensional action and proprioception trajectories |---- recordings/ |---- MP4/ |---- camera_serial.mp4 |---- camera_serial-stereo.mp4 # Optional stereo views |---- SVO/ |---- camera_serial.svo # Raw ZED SVO recordingsraw()目前挂载的惰性引用为trajectoryepisode 的trajectory.h5文件wrist_cam_video腕部相机 MP4ext1_cam_video外部相机 1 MP4通常是左视角ext2_cam_video外部相机 2 MP4通常是右视角。立体 MP4 与原始 SVO 录像目前尚未以列形式暴露源码中raw()也留有include_stereo的注释占位与 SVO 支持的 TODO。数据 Schema25 个顶层列raw()返回每 episode 一行的 DataFrame各metadata_*.json中的元数据字段被展开为顶层列再附加下列文件列。列名类型说明episode_dirStringepisode 目录路径uuidString唯一 episode 标识labString采集实验室userString数据采集者姓名user_idString数据采集者标识dateDate采集日期timestampString采集时间戳buildingString建筑或环境名称scene_idInt64建筑内的场景标识successBoolean演示是否成功current_taskString自然语言任务描述trajectory_lengthInt64轨迹的时间步数量robot_serialString机器人硬件序列号wrist_cam_serialString腕部相机序列号ext1_cam_serialString外部相机 1 序列号ext2_cam_serialString外部相机 2 序列号wrist_cam_extrinsicsList[Float64]腕部相机外参ext1_cam_extrinsicsList[Float64]外部相机 1 外参ext2_cam_extrinsicsList[Float64]外部相机 2 外参trajectoryFile指向trajectory.h5的惰性引用wrist_cam_videoVideoFile指向腕部相机 MP4 的惰性引用ext1_cam_videoVideoFile指向外部相机 1 MP4 的惰性引用ext2_cam_videoVideoFile指向外部相机 2 MP4 的惰性引用原始元数据 JSON 中还有hdf5_path、wrist_mp4_path、ext1_mp4_path等路径字段raw()选择直接暴露构造好的文件列而非原始路径字符串。这一点在 daft/datasets/droid.py 的_METADATA_DTYPE中可以完整看到——它定义了 28 个 struct 字段含 SVO/MP4 路径最终select()只挑出 25 个顶层列输出。数据源与前置条件原始 DROID 数据集托管在 Google Cloud Storage 的gs://gresearch/robotics/droid_raw约 8.7 TB。daft.datasets.droid.raw()默认就从该公共桶读取无需任何凭证即可开始。如果你更倾向使用本地副本可以用gsutil下载 episode 后把本地路径传给raw()# Download the full raw dataset (~8.7 TB) gsutil -m cp -r gs://gresearch/robotics/droid_raw /path/to/droid_raw # Or download a smaller subset for development gsutil -m cp -r gs://gresearch/robotics/droid_raw/episode_path /path/to/droid_raw/注数据集格式细节与按需下载建议可参考 DROID 官方数据集文档DROID 项目主页及数据集说明页。从公开 GCS 桶加载默认行为Daft 会对数据集根目录下的metadata_*.json做 glob 发现读取每个 episode 的元数据并据此构造轨迹与视频文件路径import daft df daft.datasets.droid.raw()从源码看daft/datasets/droid.py当io_config为空且path是官方公共桶时会显式构造IOConfig(gcsGCSConfig(anonymousTrue))实现匿名访问随后调用daft.from_glob_path(f{path}/**/metadata_*.json)发现文件该函数定义在 daft/io/file_path.py。raw()的文档字符串还提示了一个实用技巧只想读 test 或 train 子集时把 path 指定为更受限的 glob 路径例如gs://gresearch/robotics/droid_raw/test/**/metadata_*.jsongs://gresearch/robotics/droid_raw/train/**/metadata_*.json默认的 glob 是gs://gresearch/robotics/droid_raw/**/metadata_*.json。另外公共数据集中个别 episode 缺失相机录像这些行会被置为None缺失文件的处理逻辑见下文源码剖析。从本地或自定义路径加载把path指向任何镜像了原始 DROID 布局见上节 Episode 目录布局的目录即可import daft df daft.datasets.droid.raw(path/path/to/droid_raw)GCS 之外的远端对象存储只要通过path传入并配合适当的io_configdaft.io.IOConfig也支持。只加载 episode 子集由于raw()返回惰性 DataFrame你可以在物化任何视频或轨迹数据之前先过滤、投影、采样import daft ( daft.datasets.droid.raw() .where(daft.col(success)) .where(daft.col(building) Ross) .select(uuid, current_task, trajectory_length, wrist_cam_video) .limit(10) )raw() 的源码级调用链理解 daft/datasets/droid.py 中raw()的实现能解释上面所有行为匿名 GCS 配置仅当未传io_config且path为_PUBLIC_GCS_BUCKET时注入GCSConfig(anonymousTrue)episode 发现daft.from_glob_path({path}/**/metadata_*.json)拿到每个元数据文件路径col(path).download(...).cast(string).try_deserialize(json, _METADATA_DTYPE)把 JSON 解析为固定 28 字段 struct同时用regexp_replace(col(path), r/metadata_[^/]\.json$, )反推出episode_dir元数据展开unnest(col(metadata))把 struct 拍平成顶层列文件列构造trajectory由hdf5_file({episode_dir}/trajectory.h5)构造三路视频由video_file({episode_dir}/recordings/MP4/{serial}.mp4)构造其中 serial 取自元数据中的wrist_cam_serial/ext1_cam_serial/ext2_cam_serial缺失文件归一对四个文件列分别套一层when(file_exists(col(...)), col(...)).otherwise(lit(None))——这就是缺失录像的 episode 文件列为None的实现列序整理最后select()按元数据 → trajectory → 腕部相机 → ext1 相机 → ext2 相机的分组顺序输出 25 列这个顺序由 tests/datasets/test_droid.py 中的test_droid_raw_local_schema_order_and_file_columns用例精确锁定本地构造一个含trajectory.h5与三个 MP4 的伪 episode断言 schema 列序、DataType.file(MediaType.hdf5())/DataType.file(MediaType.video())类型以及file_path()推导出的完整路径。同一测试文件中还有test_droid_raw_sets_missing_camera_recordings_to_null验证删除 ext1 MP4 后该列变为None。此外trajectory()等函数在 tests/datasets/test_droid.py 中有大量本地 fixture 级测试用h5py现场构造trajectory.h5覆盖字段读取、未知字段报错、缺失 trajectory 过滤、schema 列序等无需真实网络即可回归验证。场景分类表scenes() 与 SCENE_CLASSIFICATIONSDROID 数据集附带由 GPT-4V 生成的场景分类标注。scenes()读取托管在 Hugging Face 上的 Parquet 镜像hf://datasets/Eventual-Inc/droid-scene-classifications/scene_classifications.parquet该表派生自 DROID 作者发布的补充场景分类数据CC-BY 4.0有效标签共 12 个列举在daft.datasets.droid.SCENE_CLASSIFICATIONS中Bathroom、Bedroom、Hallway / closet / doorway、Home dining room、Home kitchen、Home office、Industrial dining room、Industrial kitchen、Industrial office、Laundry、Living room、Unknown。把场景标签接到 episode 数据上可以按需过滤或 joinimport daft # Load a sample of the raw DROID data df daft.datasets.droid.raw().limit(100) # Read and filter the scene classification table scene_classifications daft.datasets.droid.scenes().where( daft.col(scene_classification) Home kitchen ) # Join scene labels onto the episode data df df.join(scene_classifications, onscene_id, howinner) df.select( uuid, scene_id, scene_classification, current_task, success, ).show(3)输出形如│ uuid ┆ scene_id ┆ scene_classification ┆ current_task ┆ success │ │ WEIRD5a2110372023-11-20-20h… ┆ 2364934467 ┆ Home kitchen ┆ Do anything you like that tak… ┆ false │从源码看daft/datasets/droid.pyscenes()的实现就一行daft.read_parquet(_HF_SCENE_CLASSIFICATIONS_PATH, io_configio_config)但文档明确说明该镜像的同步是尽力而为best-effort可能不总是最新。join 的正确性按scene_idinner join、支持过滤由 tests/datasets/test_droid.py 的test_scenes_can_be_filtered_and_joined_by_user在本地 Parquet fixture 上验证test_scene_classifications_matches_official_labels则断言标签集合恰好 12 个且包含 Home kitchen、Hallway / closet / doorway。惰性读取轨迹trajectory()DROID 数据集辅助函数遵循的模式是先惰性发现 episode 文件再把选中的、已知路径的 HDF5 dataset 读成类型化 tensor 列。import daft # Load a sample of the raw DROID data df daft.datasets.droid.raw().limit(3) df daft.datasets.droid.trajectory( df, fields[action/joint_position, action/gripper_position], ) df.show(3)输出中action/joint_position变为Tensor shape(187, 7)这样的Tensor[Float64]列action/gripper_position为Tensor shape(187)。关键参数与行为结合 daft/datasets/droid.pyfields要读取的 HDF5 dataset 路径列表。默认值_DEFAULT_TRAJECTORY_FIELDS是一组精选的 action/observation 字段8 个 action 字段 4 个 robot_state 观测字段如action/joint_position、observation/robot_state/joint_positions等见 L115-L128已知字段全集模块级字典_TRAJECTORY_DTYPESL62-L112声明了 40 余个合法路径及其目标 dtype如observation/timestamp/control/step_start是Tensor[Int64]observation/timestamp/skip_action是Tensor[Bool]。传入未知路径会直接抛ValueError: Unknown trajectory field(s)空列表或缺少trajectory列同样报错——这些校验路径都有对应的测试test_trajectory_requires_trajectory_column、test_trajectory_rejects_empty_fields、test_trajectory_rejects_unknown_fields输出列固定无论输入 DataFrame 带多少列trajectory()都重排为7 个元数据列 请求的字段列 6 个相机列的精选列序test_trajectory_uses_curated_raw_column_order验证了无关列会被丢弃跳过缺失内部先where(col(trajectory).not_null())trajectory为 null 的行在读取前就被剔除test_trajectory_filters_missing_trajectory验证依赖要求函数开头检查h5py是否可用不可用时提示pip install daft[hdf5]。实现上trajectory()内部动态定义了一个daft.funcUDF以DataType.struct({field: dtype})为返回类型、use_processFalse、unnestTrue函数体是with file.to_tempfile() as tmp, h5py.File(tmp.name, r) as h5: return {field: h5[field][()] ...}。也就是说轨迹文件经Hdf5File.to_tempfile()落到临时文件后由 h5py 打开读取读出的 struct 再被 unnest 成独立 tensor 列。自定义 HDF5 布局手写 UDF 读轨迹如果你的 HDF5 布局不同可以用daft.functions.hdf5_file()构造惰性Hdf5File引用再配合类型化 UDF 读取 dataset若需要递归遍历直接调用Hdf5File.visit()让这份开销在直连 Python 代码或 UDF 中显式发生。Hdf5File本身daft/file/hdf5.py还内建了metadata()递归收集各对象 h5path/kind/shape/dtype/chunks/compression、keys()、attrs()、visit()与read()读单个或批量 dataset 为 NumPy 数组等 h5py 风格辅助 API。import h5py import daft from daft import col, DataType, Hdf5File # Build the UDF that will read the trajectory data and return a struct of the requested fields daft.func( return_dtypeDataType.struct({ action/gripper_position: DataType.tensor(DataType.float64()), action/target_gripper_position: DataType.tensor(DataType.float64()), observation/robot_state/gripper_position: DataType.tensor(DataType.float64()), }), use_processFalse, unnestTrue, ) def read_droid_trajectory(file: Hdf5File): with file.to_tempfile() as tmp, h5py.File(tmp.name, r) as h5: return { action/gripper_position: h5[action/gripper_position][()], action/target_gripper_position: h5[action/target_gripper_position][()], observation/robot_state/gripper_position: h5[observation/robot_state/gripper_position][()], } if __name__ __main__: df ( daft.datasets.droid.raw() .where(col(success)) .where(col(trajectory).not_null()) .select(col(current_task), read_droid_trajectory(col(trajectory))) ) df.show(3)输出为三个Tensor[Float64]列如Tensor shape(946)与 episode 的trajectory_length一一对应。轨迹与相机帧一起读camera_frames()用trajectory()把选中的 HDF5 dataset 读成 tensor 列再用camera_frames()解码 MP4 相机帧拿到图像数据。camera_frames()默认解码全部三路相机传单个相机名如cameraswrist或相机名列表可收窄输出支持的相机名为wrist、ext1、ext2。import daft episodes ( daft.datasets.droid.raw() .where(daft.col(success)) .limit(3) ) traj daft.datasets.droid.trajectory( episodes, fields[action/joint_position, action/gripper_position], ) frames daft.datasets.droid.camera_frames( traj, cameras[wrist, ext1, ext2], width224, height224, sample_interval_seconds0.5, ) frames.show(3)输出在每行每 episode上新增camera_cam_frames列类型为List[Struct[frame_index: Int64, frame_time: Float64, frame_time_base: String, frame_pts: Int64, frame_dts: Int64, frame_duration: Int64, is_key_frame: Bool, data: Image[MIXED]]]。camera_frames() 的完整参数与行为参数默认值说明episodes—含 DROID 相机VideoFile列的 episode 级 DataFrameraw或trajectory的输出cameras(wrist, ext1, ext2)单个相机名或列表非法名抛Unknown camera(s)自动去重cameras[wrist, wrist]只加一列start_time0起始时间秒end_timeNone结束时间秒None表示全部帧width/heightNone目标缩放尺寸必须成对提供is_key_frameNoneTrue仅解关键帧False仅解非关键帧None全解sample_interval_secondsNone约按该时间间隔秒采样帧源码要点该函数本质上是daft.functions.video_frames定义在 daft/functions/video.py的逐相机封装——对每个选中的相机执行video_frames(col(f{camera}_cam_video), ...)并以with_columns追加{camera}_cam_frames列同样先检查av模块可用性要求pip install daft[video]并校验输入 DataFrame 中存在对应的{camera}_cam_video列缺失则抛Missing columns。空视频引用会得到空帧列表而不是报错test_camera_frames_returns_empty_frames_for_missing_camera验证。真实的端到端解码由 tests/io/av/test_droid_camera_frames.py 用仓库自带样例视频验证start_time0, end_time0.05, width64, height48时恰好解出 1 帧frame_index 0、is_key_frame is True、data.shape (48, 64, 3)。延伸阅读视频模态指南docs/modalities/videos.md讲解video_frames解码与daft.VideoFile用法是camera_frames()的底层能力来源。文件模态指南docs/modalities/files.md讲解用daft.File读取轨迹 HDF5 文件。张量模态指南docs/modalities/tensors.md讲解Hdf5Files/Hdf5File张量读取细节。DROID 数据集 API 参考docs/api/datasets.mdraw/scenes/trajectory/camera_frames的完整参数文档由 docstring 自动渲染。模块实现 daft/datasets/droid.py 与测试 tests/datasets/test_droid.py、tests/io/av/test_droid_camera_frames.py可直接对照本文的调用链说明。【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考