LeRobot documentation

LeRobotDataset v3.0

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.7.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