LeRobot documentation

LeRobotDataset v3.0

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

LeRobotDataset v3.0

LeRobotDataset v3.0 是一种用于机器人学习数据的标准化格式。它提供对多模态时间序列数据、感觉运动信号和多摄像头视频的统一访问,并包含丰富的元数据,用于在 Hugging Face Hub 上进行索引、搜索和可视化。

在了解该格式数据集的布局之前,可以先在 LeRobot Dataset Visualizer 中打开一个数据集,查看其内容:包括摄像头录制画面、同步的状态与动作图表,以及所有 episode,全部在浏览器中呈现。请参阅 Explore a Dataset in the Browser。

本指南将引导您:

  • 了解 v3.0 的设计和目录结构
  • 记录数据集并将其推送到 Hub
  • 使用 LeRobotDataset 加载用于训练的数据集
  • 使用 StreamingLeRobotDataset 流式加载数据集而无需下载
  • 在训练期间应用图像变换以进行数据增强
  • 将现有的 v2.1 数据集迁移到 v3.0
  • 尝试使用其他 LeRobotDataset 格式和实现,例如 Lance

v3 的新功能

  • 基于文件的存储:每个 Parquet/MP4 文件包含许多个 episode(v2 版本中每个 episode 对应一个文件)。
  • 关系元数据:剧集边界和查找通过元数据而非文件名来解决。
  • Hub 原生流式处理:使用 StreamingLeRobotDataset 直接从 Hub 消费数据集。
  • 降低文件系统压力:文件数量更少、体积更大 ⇒ 初始化速度更快,且在大规模场景下问题更少。
  • 统一组织:目录结构清晰,数据和视频的路径模板保持一致。
  • 数据集存储后端:数据集也可以存储为 LanceDB 表,以便直接从对象存储进行随机访问和索引(参见 LanceDB 数据集后端)。

安装

LeRobotDataset v3.0 包含在 lerobot >= 0.4.0 中。请按照安装指南进行安装。

记录数据集

运行以下命令,使用 SO-101 记录数据集并推送到 Hub:

lerobot-record \
  --robot.type=so101_follower \
  --robot.port=/dev/tty.usbmodem585A0076841 \
  --robot.id=my_awesome_follower_arm \
  --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 1920, height: 1080, fps: 30}}" \
  --teleop.type=so101_leader \
  --teleop.port=/dev/tty.usbmodem58760431551 \
  --teleop.id=my_awesome_leader_arm \
  --display_data=true \
  --dataset.repo_id=${HF_USER}/record-test \
  --dataset.num_episodes=5 \
  --dataset.single_task="Grab the black cube" \
  --dataset.streaming_encoding=true \
  --dataset.encoder_threads=2
# Optional: --dataset.rgb_encoder.vcodec=auto

有关更多详细信息,请参阅录制指南。

格式设计

v3 的核心原则之一是将存储与用户 API 解耦:数据以高效的方式存储(少量大文件),而公共 API 则提供直观的 episode 级别访问。

v3 包含三大支柱:

  1. 表格数据:以 Apache Parquet 格式存储的低维、高频信号(状态、动作、时间戳)。通过 datasets 技术栈以内存映射或流式方式访问。
  2. 视觉数据:将相机帧拼接并编码为 MP4。同一 episode 的帧会被分组;视频按相机分片,以保持实际大小合理。
  3. 元数据:描述模式(特征名称、数据类型、形状)、帧率、归一化统计信息以及片段分割(指向共享 Parquet/MP4 文件的起始/结束偏移量)的 JSON/Parquet 记录。

为了扩展到数百万个回合,来自多个回合的表格行和视频帧会被拼接到更大的文件中。特定回合的视图是通过元数据而非文件边界来重建的。

LeRobotDataset v3 diagram
从基于剧集的数据集到基于文件的数据集

目录结构(简化版)

  • meta/info.json:标准模式(特征、形状/数据类型)、FPS、代码库版本,以及用于定位数据/视频分片的路径模板。
  • meta/stats.json:用于归一化的全局特征统计信息(均值/标准差/最小值/最大值);通过 dataset.meta.stats 暴露。
  • meta/tasks.parquet:将自然语言任务描述映射为整数 ID,用于任务条件策略。
  • meta/episodes/:以 chunked Parquet 格式存储的每集记录(长度、任务、偏移量),以实现可扩展性。
  • data/:逐帧的 Parquet 分片;每个文件通常包含 多个 episode。
  • videos/:每个摄像头的 MP4 分片;每个文件通常包含多个 episode。

加载用于训练的数据集

LeRobotDataset 返回包含 PyTorch 张量的 Python 字典,并与 torch.utils.data.DataLoader 集成。以下是一个展示其用法的代码示例:

import torch
from lerobot.datasets import LeRobotDataset

repo_id = "yaak-ai/L2D-v3"

# 1) Load from the Hub (cached locally)
dataset = LeRobotDataset(repo_id)

# 2) Random access by index
sample = dataset[100]
print(sample)
# {
#   'observation.state': tensor([...]),
#   'action': tensor([...]),
#   'observation.images.front_left': tensor([C, H, W]),
#   'timestamp': tensor(1.234),
#   ...
# }

# 3) Temporal windows via delta_timestamps (seconds relative to t)
delta_timestamps = {
    "observation.images.front_left": [-0.2, -0.1, 0.0]  # 0.2s and 0.1s before current frame
}

dataset = LeRobotDataset(repo_id, delta_timestamps=delta_timestamps)

# Accessing an index now returns a stack for the specified key(s)
sample = dataset[100]
print(sample["observation.images.front_left"].shape)  # [T, C, H, W], where T=3

# 4) Wrap with a DataLoader for training
batch_size = 16
data_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size)

device = "cuda" if torch.cuda.is_available() else "cpu"
for batch in data_loader:
    observations = batch["observation.state"].to(device)
    actions = batch["action"].to(device)
    images = batch["observation.images.front_left"].to(device)
    # model.forward(batch)

在训练期间流式传输数据集

设置 --dataset.streaming=true 可在不预先下载数据集的 Parquet 和视频文件的情况下进行训练。Episode 会被即时获取到一个有界的内存池中,并且每个选定的帧在每个 epoch 中仍然只会被访问一次:

lerobot-train \
  --dataset.repo_id=yaak-ai/L2D-v3 \
  --dataset.streaming=true \
  --policy.type=act \
  --output_dir=outputs/train/act_streaming

相同的读取器也可在 Python 中使用,适用于 Hub 数据集以及存储在 HF Storage Bucket 中的数据集中的数据集:

from lerobot.datasets import StreamingLeRobotDataset

dataset = StreamingLeRobotDataset("yaak-ai/L2D-v3")
bucket_dataset = StreamingLeRobotDataset("my-org/my-bucket", repo_type="bucket")

在 CLI 中,使用 --dataset.repo_type=bucket 或 --dataset.root=hf://buckets/my-org/my-bucket 选择一个存储桶。

请参阅 Training Dataset Streaming,了解 worker 和 shard 如何映射到流式并发、MP4 旁路索引、内存设置、断点续传以及故障排除。

StreamingLeRobotDataset
直接从 Hub 流式传输,以支持即时训练。

图像转换

图像变换是在训练期间应用于相机帧的数据增强,旨在提升模型的鲁棒性和泛化能力。LeRobot 支持多种变换,包括亮度、对比度、饱和度、色相和锐度调整。

在创建/录制数据集期间使用 transforms

目前,数据增强仅在训练阶段应用,而非在录制阶段。当您创建或录制数据集时,原始图像会在未应用任何增强的情况下存储。这允许您稍后尝试不同的数据增强方法,而无需重新录制数据。

为现有数据集添加转换(API)

在加载用于训练的 dataset 时,使用 image_transforms 参数:

from lerobot.datasets import LeRobotDataset
from lerobot.transforms import ImageTransforms, ImageTransformsConfig, ImageTransformConfig

# Option 1: Use default transform configuration (disabled by default)
transforms_config = ImageTransformsConfig(
    enable=True,  # Enable transforms
    max_num_transforms=3,  # Apply up to 3 transforms per frame
    random_order=False,  # Apply in standard order
)
transforms = ImageTransforms(transforms_config)

dataset = LeRobotDataset(
    repo_id="your-username/your-dataset",
    image_transforms=transforms
)

# Option 2: Create custom transform configuration
custom_transforms_config = ImageTransformsConfig(
    enable=True,
    max_num_transforms=2,
    random_order=True,
    tfs={
        "brightness": ImageTransformConfig(
            weight=1.0,
            type="ColorJitter",
            kwargs={"brightness": (0.7, 1.3)}  # Adjust brightness range
        ),
        "contrast": ImageTransformConfig(
            weight=2.0,  # Higher weight = more likely to be selected
            type="ColorJitter",
            kwargs={"contrast": (0.8, 1.2)}
        ),
        "sharpness": ImageTransformConfig(
            weight=0.5,  # Lower weight = less likely to be selected
            type="SharpnessJitter",
            kwargs={"sharpness": (0.3, 2.0)}
        ),
    }
)

dataset = LeRobotDataset(
    repo_id="your-username/your-dataset",
    image_transforms=ImageTransforms(custom_transforms_config)
)

# Option 3: Use pure torchvision transforms
from torchvision.transforms import v2

torchvision_transforms = v2.Compose([
    v2.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
    v2.GaussianBlur(kernel_size=3, sigma=(0.1, 2.0)),
])

dataset = LeRobotDataset(
    repo_id="your-username/your-dataset",
    image_transforms=torchvision_transforms
)

可用的转换类型

type 可以是 torchvision.transforms.v2 中的任意类,例如:

  • ColorJitter:调整亮度、对比度、饱和度和色相
  • SharpnessJitter:随机调整图像锐度
  • RandomAffine、RandomRotation:几何抖动
  • GaussianNoise、MotionBlur、JPEGCompression、GaussianPatchBrightness、RandomShadow、CoarseDropout、GammaCorrection、PlanckianJitter:真实机器人上可见的相机和光照效果
  • Identity:无变换(适用于测试)

任何其他 torchvision.transforms.v2 类都可以作为 type 指定(例如 RandomResizedCrop 或 GaussianBlur);它会逐个样本应用。您也可以直接将 torchvision 变换传递给 image_transforms 参数。

现在所有变换均已批处理化,相同的批处理变换在两个后端上运行。逐样本类(SharpnessJitter、GaussianNoise、MotionBlur、GaussianPatchBrightness、RandomShadow、CoarseDropout、GammaCorrection、PlanckianJitter、RandomSubsetApply)以及 make_transform_from_config 仍可作为其 Batched* 替代项的已弃用前端进行导入,使用时会发出警告;它们将在未来的版本中移除。ImageTransforms 是一个普通的 torch.nn.Module,接受 (C, H, W) 或 (T, C, H, W) 张量。通过 type 指定变换名称的配置不受影响。

现有配方(recipes)的行为变更。训练时以 uint8 读取帧,而 LeRobot 中除 ColorJitter、RandomAffine 和 RandomRotation 之外的其他变换,过去会原样返回非浮点输入。因此,启用 GaussianNoise、MotionBlur、GaussianPatchBrightness、RandomShadow、CoarseDropout、GammaCorrection 或 PlanckianJitter 的配方实际上是在未应用这些变换的情况下进行训练的。现在这些变换在两个后端上都会生效,因此此类配方的训练效果与之前不同:它现在会应用所请求的数据增强。对于原本就已生效的变换,除舍入行为外保持不变,因为批量处理路径以浮点数进行计算,并在最后统一舍入一次,而不是在每个变换之后都进行舍入。

配置选项

  • enable:启用/禁用转换(默认值:False)
  • max_num_transforms:每帧应用的最大变换数量(默认值:3)
  • random_order:以随机顺序而非标准顺序应用转换(默认值:False)
  • weight:每个变换的采样概率(值越高,被选中的可能性越大;如果权重之和不为 1,它们将被归一化)
  • kwargs:特定于转换的参数(例如亮度范围)
  • backend:训练期间执行转换的位置,"dataloader"(默认)或 "gpu";详见下文
  • gpu_compile:在 "gpu" 后端上,对批量转换使用 torch.compile(默认值:True)
  • gpu_chunk_size:在 "gpu" 后端上,每次增强这么多样本以限制瞬时内存(默认值:32)

在 GPU 上运行 transforms

默认情况下,transforms 在 DataLoader 的 workers 中按样本运行。当使用多个摄像头时,这通常是 workers 中最耗时的操作,导致 GPU 等待数据。设置 backend=gpu 可以让 workers 仅执行解码,并在策略设备上对每个 batch 应用相同的 transforms:

lerobot-train \
  --dataset.repo_id=your-username/your-dataset \
  --dataset.image_transforms.enable=true \
  --dataset.image_transforms.backend=gpu \
  --policy.type=act \
  --policy.device=cuda

随机化保持不变:每个样本独立抽取其自身的变换和参数,同一相机历史中的各帧共享这些变换和参数。数据增强仅应用于训练批次,且在策略预处理器之前执行;评估批次、rollout 以及保存的检查点均不受影响。

需要了解的事项:

  • 需要加速器;--policy.device=cpu 会被拒绝。同样,使用自定义的 image_transforms 可调用对象手动构建的数据集也会被拒绝,因为这些操作在 worker 中运行。
  • 支持数据加载器后端支持的所有转换类型。没有批量实现的类型(JPEGCompression,以及除 ColorJitter、RandomAffine、RandomRotation 和 Identity 之外的 torchvision 类)会在批次内逐个样本应用,因此无法获得加速;JPEGCompression 还会在 CPU 上进行往返处理。对于包含多个样本的批次,会拒绝改变帧大小的转换。
  • 可通过 --seed 复现,每个进程使用不同的种子。数据增强不会消耗策略的随机流,且 gpu_compile 和 gpu_chunk_size 不会改变所应用的内容。
  • 两种后端都会执行相同的批处理变换,以浮点数形式运行,并在最后统一进行舍入,因此在相同参数下,一种后端增强后的样本与另一种后端增强后的相同样本结果一致。
  • gpu_compile 针对每种图像形状仅产生一次 15-30 秒的开销。峰值内存约为批处理图像的两份 float 副本加上一个 chunk 的几份副本;如果内存紧张,请调低 gpu_chunk_size。random_order=true 会将变换时间乘以 max_num_transforms。

从 v2.1 迁移到 v3.0

转换器会将每个剧集的文件聚合为更大的分片,并写入剧集偏移量/元数据。请按照以下说明转换您的数据集。

# Convert an existing v2.1 dataset hosted on the Hub:
python -m lerobot.scripts.convert_dataset_v21_to_v30 --repo-id=<HF_USER/DATASET_ID>

功能说明

  • 聚合 parquet 文件:episode_000000.parquet、episode_000001.parquet、… → file-000.parquet、…
  • 聚合 mp4 文件:episode_000000.mp4、episode_000001.mp4、… → file-000.mp4、…
  • 更新 meta/episodes/*(分块 Parquet),包含每个 episode 的长度、任务以及字节/帧偏移量。

常见问题

在推送之前始终调用 finalize()

在创建或记录数据集时,您必须调用dataset.finalize()以正确关闭 parquet 写入器。有关更多详细信息,请参阅PR #1903。

from lerobot.datasets import LeRobotDataset

# Create dataset and record episodes
dataset = LeRobotDataset.create(...)

for episode in range(num_episodes):
    # Record frames
    for frame in episode_data:
        dataset.add_frame(frame)
    dataset.save_episode()

# Call finalize() when done recording and before push_to_hub()
dataset.finalize()  # Closes parquet writers, writes metadata footers
dataset.push_to_hub()

为什么需要这样做?

Dataset v3.0 使用增量 parquet 写入和缓冲元数据以提高效率。finalize() 方法:

  • 将任何已缓冲的 episode 元数据刷新到磁盘
  • 关闭 parquet 写入器以写入 footer 元数据,否则 parquet 文件将会损坏
  • 确保数据集有效以便加载

如果不调用 finalize(),你的 parquet 文件将不完整,数据集也无法正常加载。

其他格式和实现

LanceDB 数据集后端

v3 数据集也可以存储为 LanceDB 表。LanceDB 是一个基于 Lance 格式构建的多模态数据湖仓,专为 AI 访问模式设计:支持对任意行及其视频字节进行快速随机访问,并可在同一张表上建立标量、向量和全文索引,从而让 EDA、挖掘、数据整理和训练直接基于对象存储运行。该布局保持 meta/ 不变,并新增一个用于表格数据的 frames 表和一个以 blob 形式存储 MP4 的 videos 表。meta/info.json 中的 "storage_format": "lance" 用于选择读取器;LeRobotDataset API、数据项以及训练流水线均保持不变。

  • 直接基于 Hub 或 HF Storage Buckets 进行训练,GPU 持续获得数据供给。 随机帧就地读取,每个批次仅读取其所需的字节,因此每个批次都是全局洗牌,且无需预先下载任何内容。
  • 转换成本大约相当于一次文件复制。视频不会被重新编码,因此各项内容与源文件完全一致。DROID 的转换耗时为 34 分钟。
  • 零拷贝特征工程。 向训练器读取的表中添加分数、嵌入或修正后的标签等列,而无需重写视频或复制数据集,然后使用索引对这些列进行过滤和搜索。读取器会忽略其不识别的列。

有关其工作原理、基准测试和示例,请参阅 LanceDB LeRobot 指南。

相机特征必须以视频形式存储。以图像形式存储相机帧的数据集不受读取器支持,在转换前需要重新编码为视频。

读取 Lance 数据集需要 lancedb 附加组件(pip install "lerobot[lancedb]")。该格式为只读:请在默认格式下记录和编辑数据集,然后使用 lerobot-lancedb 包进行转换:

pip install "lerobot-lancedb"
lerobot-lance-convert --repo-id lerobot/pusht --out ./pusht-lance  # or --root <local dataset dir>

加载使用相同的类:

from lerobot.datasets import LeRobotDataset

# Hub dataset repo
dataset = LeRobotDataset("lance-format/pusht-lance")

# HF Storage Bucket, read in place
dataset = LeRobotDataset("my-org/pusht-lance", repo_type="bucket")

# the same bucket as a root URI (other object store URIs work the same way)
dataset = LeRobotDataset("lerobot/pusht", root="hf://buckets/my-org/pusht-lance")

HF Storage Bucket 选项在 lerobot-train 中可用:

lerobot-train \
  --dataset.repo_id=my-org/pusht-lance \
  --dataset.repo_type=bucket \
  --policy.type=act

可视化功能同时支持两种查看器。lerobot-dataset-viz --repo-id lance-format/pusht-lance --episode-index 0 会像往常一样在 Rerun 中打开一个 episode,而 --display-mode foxglove 则通过 WebSocket 将其提供给 Foxglove。无论哪种方式,都只获取屏幕上的帧,无需进行 MCAP 导出或下载。

Update on GitHub