Rerun 内置 LeRobot 数据集导入器:零代码可视化多模态机器人数据
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
导读
LeRobot 是 Hugging Face 推出的开源机器人数据格式,广泛用于具身智能与多模态机器人数据集(如 SmolVLA 所用的数据集)。Rerun 在re_importer中内置了 LeRobot 数据集导入器,用户只需把 Viewer 指向一个 LeRobot 数据集目录,即可像打开普通录制文件一样浏览其中的多路相机图像、深度图、视频流、标量状态与语言标注。本文以 examples/python/lerobot_loader/README.md 为主体,结合仓库内 re_lerobot 与 re_importer 的源码实现,带你从"下载一个数据集、一行命令打开"到"理解底层加载链路与格式支持边界",掌握在 Rerun 中使用 LeRobot 数据的完整方法。
快速上手:把 LeRobot 数据集目录当作一个录制文件
Rerun 对 LeRobot 数据集的加载体验与其他录制格式(.rrd、.mcap)对齐:你不需要写任何 Python 或 Rust 代码,Viewer 会识别目录结构并自动切换导入器。
获取一个示例数据集
README 中给出的示例来自 SmolVLA 博客中使用的数据集satvikahuja/mixer_on_off_new_1(托管在 Hugging Face Hub 上)。由于数据集文件由 Git LFS 管理,需要先确保 LFS 可用:
git lfs install # If not already installed git clone https://huggingface.co/datasets/satvikahuja/mixer_on_off_new_1说明:仓库源码 importer_lerobot.rs 的文档注释中还提到了另一个常用示例数据集
lerobot/pusht_image,同样可以直接用于体验本功能。
两种打开方式
方式一:在 Rerun Viewer 中点击打开文件,选择刚才克隆下来的目录即可加载。
方式二:直接从终端把目录路径作为参数传给rerun命令:
rerun mixer_on_off_new_1命令行参数的形式与加载其他录制文件完全一致,区别仅在于传入的是一个目录而不是文件。这正是 README 中强调的"像加载一个 rerun 文件一样"的体验。
SDK 能力对齐:目录即录制
README 的 "SDK support" 一节指出:由于该导入器是内置的,其他 SDK 功能(如播放、查询、导出、时间轴导航等)对 LeRobot 数据集同样生效,只是把"文件路径"换成了"目录路径"。从实现看,导入器会把每个 episode 包装为一个独立的 recording(store),因此你可以在 Viewer 的录制切换器中逐个回放 episode。
底层机制:导入器如何识别一个 LeRobot 数据集
目录级路由:DirectoryImporter 的委托逻辑
Rerun 的目录导入由 DirectoryImporter 负责。它先判断路径是否为目录,然后检查是否为 LeRobot 数据集:如果是,就返回Incompatible并交由专门的LeRobotDatasetImporter处理,从而避免把 LeRobot 目录当作普通文件目录逐个递归导入:
// LeRobot datasets are loaded by LeRobotDatasetImporter. #[cfg(feature = "lerobot")] if crate::is_lerobot_dataset(&dirpath) { return Err(crate::ImporterError::Incompatible(dirpath.clone())); }从 lib.rs 可以看出,LeRobotDatasetImporter与RrdImporter、DirectoryImporter、McapImporter、ParquetImporter等并列注册在内置导入器列表中(BUILTIN_IMPORTERS),且仅在启用lerobotfeature 且非 wasm 目标时编译。
版本识别:v1 / v2 / v3
lib.rs 中的LeRobotDatasetVersion::find_version负责判定数据集格式版本,规则非常直观(基于目录结构,而非文件内容):
| 版本 | 判定条件 | 说明 |
|---|---|---|
| v1 | 存在非空的meta_data/与data/子目录 | 旧格式,元数据目录名为meta_data,导入器已明确不支持 |
| v2 | 存在非空的meta/与data/子目录 | 元数据目录为meta |
| v3 | 在 v2 基础上额外要求meta/episodes/目录存在 | 每集元数据存放在meta/episodes/下 |
is_lerobot_dataset只要命中上述任一版本即返回 true。当find_version识别到 v1 时,importer_lerobot.rs 会直接打印"LeRobot 'v1.x' dataset format is unsupported."并跳过加载;v2/v3 才真正进入load_dataset流程。
支持的目录结构与元数据格式
v2 数据集布局
datasetv2.rs 中给出了标准的 v2 目录结构:
. ├── README.md ├── data │ └── chunk-000 │ ├── episode_000000.parquet │ ├── episode_000001.parquet │ ├── … ├── meta │ ├── episodes.jsonl │ ├── info.json │ ├── stats.json │ └── tasks.jsonl └── videos └── chunk-000 └── observation.image ├── episode_000000.mp4 ├── episode_000001.mp4 ├── …各目录/文件职责:
data/:以 Parquet 格式存储每集数据,按 chunk 分目录;meta/info.json:数据集全局元数据(机器人类型、总集数、总帧数、总任务数、总视频数、每 chunk 集数、fps、feature 定义、data_path/video_path路径模板等);meta/episodes.jsonl:逐行存储每集元数据(episode_index、关联任务列表、帧数);meta/tasks.jsonl:任务定义;meta/stats.json:feature 汇总统计;videos/:可选目录,按与data/相同的 chunk 组织存放视频观察数据。
路径模板中的{episode_chunk:03d}与{episode_index:06d}会被替换为实际数值,用于定位episode_000000.parquet或episode_000000.mp4这类文件。
v3 数据集布局
datasetv3.rs 描述了 v3 的结构差异——每集元数据改为 Parquet 文件、任务与子任务也以 Parquet 存储:
. ├── README.md ├── data/ │ └── chunk-000/ │ ├── episode_000000.parquet │ ├── episode_000001.parquet │ └── … ├── meta/ │ ├── episodes/ │ │ └── chunk-000/ │ │ ├── file-000.parquet │ │ ├── file-001.parquet │ │ └── … │ ├── tasks.parquet │ ├── stats.json │ └── info.json └── videos/ └── chunk-000/ └── observation.image/ ├── episode_000000.mp4 ├── episode_000001.mp4 └── …v3 的关键差异:
meta/episodes/中的每集元数据文件同时记录了该集主数据文件的位置(data/chunk_index、data/file_index)以及每个视频/图像 feature 各自独立的文件位置(列名形如videos/{feature_name}/chunk_index、videos/{feature_name}/file_index,以及from_timestamp/to_timestamp),这意味着多个 episode 可以高效共享同一个视频文件;- 任务定义从 JSONL 换成
tasks.parquet(任务名列兼容task与旧版__index_level_0__两种命名,见TASK_COLUMN_NAMES); - 可选的
subtasks.parquet用于子任务(subtask)标注,存在时才加载。
数据模型:Feature、DType 与时间线
Feature 与 DType
meta/info.json中的features映射定义了每个数据通道的类型与形状。在 lib.rs 中,Feature由三部分组成:
dtype:数据类型,支持Video、Image、Bool、Float32、Float64、Int16、Int64、String、Language;shape:数据维度,例如[3, 224, 224]表示 3 通道(RGB)、224×224 的图像;names:可选的维度名元数据,支持多种 JSON 表示(见下)。
channel_dim()方法用于推断通道数:优先查找名为channels的维度,找不到则取 shape 的最后一维。加载时按通道数分流:1 通道 → 深度图(DepthImage),3 通道 → 普通图像(EncodedImage),其他通道数会被warn_once告警并跳过。
names的四种合法 JSON 形态(见 lib.rs 的NamesVisitor及其单元测试):
"img_state_delta" // 单字符串 ["x", "y", "z"] // 扁平字符串数组 [["kLeftShoulderPitch", "kLeftShoulderRoll"]] // 嵌套字符串数组 {"motors": ["motor_0", "motor_1"]} // 单键对象(值可为数组或 null)如果names存在,导入器会为标量 feature 额外生成一个静态名称 chunk(SeriesLines名称),方便 Viewer 中的曲线图直接显示各关节/轴的名字。
时间线推导
common.rs 中的derive_timeline决定每个 episode 使用哪条时间线:
- 若数据中存在
frame_index列 → 使用序列时间线frame_index; - 否则若存在
timestamp列 → 使用持续时间时间线timestamp(秒,转换为纳秒); - 两者皆无则报错。
与此同时,LEROBOT_DATASET_IGNORED_COLUMNS定义了不会被可视化的元数据列:episode_index、index、frame_index、timestamp——它们只服务于分集与时间线,而不是实体数据。
各类 feature 的映射规则
结合build_plan(分别在 datasetv2.rs 与 datasetv3.rs 中),各 dtype 的落地方式如下:
| dtype | 处理方式 |
|---|---|
Video | v2:整段视频作为静态AssetVideo资产,并尝试读取帧时间戳生成VideoFrameReference;v3:按该集的from_timestamp~to_timestamp区间切出VideoStream(codec + samples),采样点均匀映射到帧时间线 |
Image | 按channel_dim分流:1 通道 →DepthImage,3 通道 →EncodedImage,其他通道数告警跳过 |
Float32/Float64 | 标量 feature,转为Scalars曲线数据(形状为列表/定长列表时,先抽取元素再转 f64) |
Int64且键为task_index | 渲染为task文本实体(TextDocument),把tasks.jsonl/tasks.parquet中的任务描述按帧写入 |
Int64且键为subtask_index(仅 v3) | 渲染为subtask文本实体 |
Language | v3 支持:language_persistent(持久化、带时间戳、广播到匹配帧)与language_events(事件、仅落在发射帧),按style/role/camera分段生成实体路径,tool_calls渲染到…/tool_calls子实体;v2 不支持并直接报错(有对应回归测试test_v2_language_dtype_is_rejected) |
Int16/Int64/Bool/String(其余场景) | 尚未实现,warn_once告警并跳过 |
性能与缓存设计:v3 的流式加载优化
对于动辄数 GB 的机器人数据,导入器在 v3 上做了明显的性能优化(见 datasetv3.rs):
- Episode 数据缓存(
episode_data_cache):打开数据集时按"文件→episode"分组,把同一 Parquet 文件一次性读入并拼接(concat_batches),再通过build_episode_row_index单遍扫描episode_index列构建行区间索引,按需切片出每个 episode 的RecordBatch。多个 episode 共用文件时只读一次磁盘。 - 视频引用计数缓存(
VideoBlobCache):视频 blob 懒加载(fs::read一次后缓存为Arc<[u8]>),并用remaining_refs统计每个视频文件仍被多少个 episode 引用;某集加载完成(或失败)后调用release_episode_videos递减引用,计数归零即从缓存中淘汰,从而在流式加载多集时控制内存峰值。 - 有序遍历:episode 元数据用有序 map 存储,
iter_episode_indices保证按索引升序返回——导入器会为每个 episode 广播一个 recording,有序遍历保证 Viewer 中 episode 顺序稳定(对应测试episode_indices_iterate_in_ascending_order)。
加载流程本身是异步的:importer_lerobot.rs 会在专用 IO 线程上执行load_and_stream_versioned,逐集生成SetStoreInfo与数据 chunk 并通过 crossbeam channel 发回,避免阻塞 rayond 线程池。
使用约束与注意事项
- v1 数据集不支持:仅 v2(含 v2.1)与 v3 可加载,v1 会被明确拒绝。
lerobotfeature 开关:LeRobot 导入器以 Cargo feature 形式编译(#[cfg(feature = "lerobot")]),且不适用于 wasm 目标;桌面版/服务端构建默认启用。- 图像通道数限制:仅 1 通道(深度)与 3 通道(RGB)图像会被渲染,其余通道数告警跳过。
- 部分 dtype 尚未落地:
Int16/Int64/Bool/String(非task_index/subtask_index场景)与 v2 的Language目前不会可视化,加载时会有warn_once提示。 - SDK 语义对齐:由于每个 episode 映射为一个独立 recording,SDK 中针对录制文件的查询、导出与回放能力均可直接用于 LeRobot 目录,无需额外适配。
延伸阅读
- 示例文档:examples/python/lerobot_loader/README.md
- 核心 crate 说明:crates/data_flow/re_lerobot/README.md
- 数据集解析与 chunk 生成:lib.rs、common.rs
- 各版本格式实现:datasetv2.rs、datasetv3.rs
- 导入器注册与目录路由:importer_lerobot.rs、importer_directory.rs、lib.rs
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考