视频编码参数

启用视频存储后,LeRobot 会将每个相机流存储为一个 MP4 文件,而不是为每个时间步保存一个图像文件。视频编码跨时间进行压缩,与一大堆 PNG 相比通常能减少 dataset 大小和 I/O,同时保持 MP4 —— 一种每个播放器和加载器都能理解的格式。

将帧编码为 MP4 是一个完整的 FFmpeg 流水线:编码器选择、像素格式、GOP/关键帧、质量与速度的权衡,以及可选的额外编码器标志。其中大多数参数都可以通过 rgb_encoder 进行用户调整,这是一个嵌套的 RGBEncoderConfiglerobot.configs.video.RGBEncoderConfig),通过 PyAV 传递。

你可以通过 CLI 使用 --dataset.rgb_encoder.<field> 设置这些参数(例如配合 lerobot-recordlerobot-rollout)。同一个配置块会应用于该次运行中的每个相机视频流。

必须开启视频存储,rgb_encoder 才会生效 —— 在 Python API 中使用 use_videos=True,或在 CLI 上使用 --dataset.video=true( 录制默认值)。关闭视频后,输入保持为图像,rgb_encoder 会 被忽略。

关于帧何时写入与编码(流式 vs. episode 后)、队列以及其他顶层 --dataset.* 开关的详细信息,请参阅流式视频编码。关于编码参数对比和实验,请参阅 video-benchmark Space


示例

lerobot-record \
    --robot.type=so100_follower \
    --robot.port=/dev/tty.usbmodem58760431541 \
    --robot.cameras="{laptop: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --robot.id=black \
    --teleop.type=so100_leader \
    --teleop.port=/dev/tty.usbmodem58760431551 \
    --teleop.id=blue \
    --dataset.repo_id=<my_username>/<my_dataset_name> \
    --dataset.num_episodes=2 \
    --dataset.single_task="Grab the cube" \
    --dataset.streaming_encoding=true \
    --dataset.encoder_threads=2 \
    --dataset.rgb_encoder.vcodec=h264 \
    --dataset.rgb_encoder.preset=fast \
    --dataset.rgb_encoder.extra_options={"tune": "film", "profile:v": "high", "bf": 2} \
    --display_data=true

调节参数

默认值经过调优,以在典型机器人 dataset 上平衡压缩率视觉质量解码/定位速度。更改它们可能会影响录制(CPU 负载、丢帧)和训练(解码吞吐量、图像质量)。

只有在有明确理由时才覆盖这些参数,并在依赖新设置之前衡量其对流水线的影响。

以下所有标志在 CLI 上都以 --dataset.rgb_encoder. 为前缀。

参数类型默认值说明
vcodecstr"libsvtav1"视频编解码器名称。"auto" 会从固定的偏好列表中选取第一个可用的硬件编码器,否则回退到 libsvtav1
pix_fmtstr"yuv420p"输出像素格式。必须为你 FFmpeg 构建中所选编解码器所支持。
gint2GOP 大小 —— 每 g 帧一个关键帧。作为 FFmpeg 选项 g 发出。
crfintfloat30抽象质量值,按编解码器映射(见映射)。在映射单调的情况下,越低 → 质量越高 / 输出越大。
presetintstr12 *编码器速度预设;含义取决于编解码器。
* 未设置且 vcodec=libsvtav1 时,LeRobot 默认为 12
fast_decodeint0libsvtav10–2,通过 svtav1-params 传递。
h264 / hevc(软件):如果 >0,设置 tune=fastdecode
其他编解码器:通常不使用。
video_backendstr"pyav"目前视频编码仅实现了 "pyav"
extra_optionsdict{}在上面的结构化字段之后合并的额外 FFmpeg 或编解码器特定选项。不能覆盖那些字段已设置的键。

深度流

深度图(Intel RealSense、Reachy 2)作为独立的视频流与 RGB 流一起存储。原始深度(uint16 毫米或 float32 米)无法通过 8 位编解码器保存,因此 LeRobot 会将每个深度图量化为 12 位编码([0, 4095])—— 默认采用对数方式,以匹配深度传感器的 1/depth 误差特性 —— 然后将其打包为高位深像素格式(gray12le),并使用 12 位编解码器进行编码。

原始深度 uint16 毫米
float32 米
录制时间 裁剪 到 [depth_min,
depth_max]
量化 12 位编码 0–4095
对数(默认)或线性
打包 为 gray12le
平面
编码 HEVC
Main 12
MP4 已存储
加载时间 反量化 为 mm / m

通过一个并行的 depth_encoder 块(DepthEncoderConfig)配置深度流水线。它共享每个 RGBEncoderConfig 字段(vcodecpix_fmtcrf……),并新增四个量化器参数,通过 --dataset.depth_encoder.<field> 设置:

lerobot-record \
    ... \
    --dataset.depth_encoder.vcodec=hevc \
    --dataset.depth_encoder.depth_min=0.05 \
    --dataset.depth_encoder.depth_max=5.0 \
    --dataset.depth_encoder.use_log=true
参数类型默认值说明
vcodecstr"hevc"HEVC Main 12(支持 12 位的编解码器,兼容 MP4)。
extra_optionsdict{"x265-params": "lossless=1"}深度默认无损(精确往返);crf 会被忽略。传入 extra_options={} 并设置 crf 可获得更小的有损流。
pix_fmtstr"gray12le"用于承载量化编码的单通道 12 位像素格式。
depth_minfloat0.01以米为单位的深度映射到量子 0。低于该值的值在解码时会被裁剪。
depth_maxfloat10.0以米为单位的深度映射到量子 4095。高于该值的值在解码时会被裁剪。
shiftfloat3.5用于对数量化以在零附近保持数值稳定性的对数前偏移(米)。必须满足 depth_min + shift > 0
use_logboolTrue如果为 true,则在对数空间中进行量化(推荐用于典型深度传感器)。设为 false 则进行均匀/线性量化。

无论输入深度的单位是什么,depth_mindepth_maxshift 始终以解释。输入会自动检测:整数数组(例如直接来自 RealSense 的 uint16 毫米)按毫米处理,浮点数组按米处理。 选择 depth_min / depth_max 以覆盖传感器的实际工作范围 —— 该范围之外的量子会饱和,这可能会压碎边界处的细节。

深度特征在 meta/info.json 中用 "is_depth_map": true 标记,其量化器设置(video.depth_minvideo.depth_maxvideo.shiftvideo.use_log)会被持久化 —— 这正是让深度在加载时能够反量化回物理单位的原因。

加载时的输出单位

depth_encoder录制时的关注点。深度图在加载时(例如训练期间)反量化后所采用的单位,由读取时标志 --dataset.depth_output_unit 单独设置:

lerobot-train \
    --dataset.repo_id=<my_username>/<my_dataset_name> \
    --dataset.depth_output_unit=m \
    --policy.type=act
参数类型默认值说明
depth_output_unitstr"mm"深度图在加载时反量化后所采用的物理单位:"mm"(毫米)或 "m"(米)。

这纯粹是解码时的呈现选择 —— 它不会改变存储的视频或其元数据,因此同一 dataset 可以作为 mmm 读取而无需重新编码。它对没有深度相机的 dataset 没有影响。


在 dataset 元数据中的持久化

视频流的第一个 episode 编码后,编码器配置会被持久化到 dataset 元数据meta/info.json)中每个视频特征下,与从文件本身探测得到的值并列。对于视频特征 observation.images.<camera>info.json 中的布局为:

{
  "features": {
    "observation.images.laptop": {
      "dtype": "video",
      "shape": [480, 640, 3],
      "info": {
        "video.height": 480,
        "video.width": 640,
        "video.codec": "h264",
        "video.pix_fmt": "yuv420p",
        "video.fps": 30,
        "video.channels": 3,
        "is_depth_map": false,
        "video.g": 2,
        "video.crf": 30,
        "video.preset": "fast",
        "video.fast_decode": 0,
        "video.video_backend": "pyav",
        "video.extra_options": { "tune": "film", "profile:v": "high", "bf": 2 }
      }
    }
  }
}

有两个来源为 info 块提供内容:

来源来源位置字段
流派生使用 PyAV 从编码后的 MP4 读回。video.height, video.width, video.codec, video.pix_fmt, video.fps, video.channels, is_depth_map, audio.*
编码器派生取自 RGBEncoderConfig / DepthEncoderConfigvideo.g, video.crf, video.preset, video.fast_decode, video.video_backend, video.extra_options

该块仅从第一个episode 填充一次。它假设 dataset 中的每个 episode 都使用相同的 rgb_encoder 编码。不支持在录制过程中 更改编码器设置 —— info.json 只会反映第一个 episode 所使用的参数。


合并 dataset

使用 merge_datasets 聚合 dataset 时,视频文件会按原样拼接(不重新编码),info.json 中的编码器字段按每个键合并:

合并规则字段行为
必须匹配video.codec, video.pix_fmt, video.height, video.width, video.fps流派生字段必须在各来源之间匹配,否则 FFmpeg 的 concat 解复用器会失败。
宽松合并video.g, video.crf, video.preset, video.fast_decode, video.extra_options编码器调优字段。如果所有来源一致,则保留该值;如果不一致,则设为 null(对于 video.extra_options 则为 {}),并记录一条警告。
在 GitHub 上更新