An Open-Source Textbook for Embodied AI

LeRobot 手册
从一台机械臂到具身智能

一个 200 美元的机械臂、一份 Parquet 文件、三十条人类演示, 能训出一个会抓东西的模型。这本书讲清这条链路是怎么搭起来的, 以及你可以在哪一段插手。

版本
v0.6.1
代码规模
27 个子包 · 30 个策略
硬件
13 种机器人 · 13 种遥操设备
许可
Apache 2.0
PREFACE

为什么写这本书

「让每个人都能贡献并受益于共享的数据集与预训练模型。」
——LeRobot 的一句话目标

过去两年,具身智能的论文数量涨得比数据快。几乎每周都有新的 VLA 模型刷榜,几乎每家都宣称自己在「真实机器人上验证过」。但如果你真的拆开其中一篇的代码仓库,会发现复现它需要三样很贵的东西:一台特定型号的机械臂、一套特定布置的相机、以及作者没说清楚的数据处理脚本。

LeRobot 想解决的正是这三样。它做的事情听起来很朴素——把机器人硬件抽象成统一的接口,把数据统一成一种格式,把主流策略用纯 PyTorch 重写一遍,然后全部放到 Hugging Face 上。但就是这三件事,让「复现一个机器人策略」的成本从几周降到了几个小时。

这本书不打算写成 API 文档的翻译。文档告诉你每个参数是什么意思,但不会告诉你为什么要有这个参数、什么时候它会咬你一口。所以本书的组织方式是:先讲这个项目由哪些零件组成,再讲这些零件背后的科学原理,然后带你装一遍,最后告诉你从哪里可以改它。

四章的分工

  • 第一章拆仓库。把 src/lerobot 下的二十多个子包按职责分层,讲清每个模块解决什么问题、彼此怎么调用。读完你应该能在脑子里画出这个项目的架构图。
  • 第二章讲原理。从「为什么机器人学习比图像分类难」讲到动作表示、VLA 架构、以及 2026 年正在收敛的几个技术方向。这一章不依赖 LeRobot,但读完你会明白 LeRobot 为什么长成现在这样。
  • 第三章动手。硬件清单、接线、环境、五条命令跑通全流程、数据集结构、训练与部署、故障排查。这一章是可以照着做的。
  • 第四章参与。从「上传一个数据集」到「给核心加一个新策略」,按投入从轻到重排开,附代码落点与评审预期。
阅读前提

你需要会写 Python(能读懂 dataclass 和类型注解),用过一次命令行,知道什么是 GPU 显存。不需要学过机器人学,不需要有硬件——第三章最后一节给了完全无硬件的路径。

时效声明

本书基于 2026 年 11 月 1 日的 main 分支与 PyPI 上的 v0.6.1。这个仓库的迭代速度快得反常——当天还有新的 commit 推上来,开放 issue 数接近九百。文中所有文件路径与命令都以该时间点为准;如果你读到的是半年后的版本,请以仓库为准,但架构层面的判断大概率仍然成立。

CHAPTER 01

认识 LeRobot

What LeRobot Actually Is

大多数人对它的第一印象是「机器人版的 transformers」。这个类比有一半对,而错的那一半,恰好是最关键的部分。

1.1它是什么,不是什么

官方的定义只有一句话:LeRobot 用 PyTorch 为真实世界的机器人提供模型、数据集和工具。这句话里三个名词都是重点——模型、数据集、工具,缺一个都不成立。

更具体地说,它提供四样东西:

  • 一个硬件无关的控制接口。不管是 200 美元的 SO-101 舵机臂、几万块的 OpenArm,还是 Unitree G1 人形,在代码里都实现同一个 Robot 类:connect()、get_observation()、send_action()。上层逻辑不需要知道你插的是什么。
  • 一种数据格式。LeRobotDataset:视觉存 MP4(或图片),状态与动作存 Parquet,两者按时间戳逐帧对齐,附一份 meta/info.json 描述所有字段。托管在 Hugging Face Hub 上,可以流式读取、可以合并、可以切分。
  • 一批纯 PyTorch 的策略实现。从最经典的 ACT、Diffusion Policy,到 π0、GR00T N1.7、SmolVLA 这些 VLA,再到世界模型与奖励模型,全部用原生 PyTorch 写,不依赖任何闭源推理栈。
  • 一套 CLI。校准、遥操、录制、训练、评测、部署,各自对应一个 lerobot-* 命令。这些命令不只是脚本,它们是把配置系统、数据管线、硬件接口串起来的入口。

它不是什么

这部分比上面更重要,因为误解通常从这里开始。

  • 它不是 ROS。ROS 解决的是进程间通信、消息定义、分布式节点的编排;LeRobot 解决的是「怎么把一段人类演示变成一个能跑的神经网络」。两者可以共存(社区有 ROS 2 桥接插件),但设计目标不同。LeRobot 没有节点、没有 topic、没有 launch 文件,它是一个 Python 库加一组脚本。
  • 它不是仿真器。仓库里确实有 envs 模块,支持 LIBERO、MetaWorld 这类基准,但物理引擎不在它里面——它只是把这些环境包装成统一接口,方便做评测。
  • 它不是模型 zoo。虽然内置了三十个策略,但它的定位不是「给你一堆权重下载」,而是「给你一套能训、能评、能部署的流水线」。权重在 Hub 上,训练代码在你本地。
  • 它不是「开箱即用的机器人」。你需要自己买舵机、打印结构件、接线、校准。软件层把门槛降到了很低,但物理世界那一半的门槛还在。
一句话记住

LeRobot 的本质是一条流水线,而不是一堆模型。它的四个环节是「采 → 存 → 训 → 跑」,每个环节都可以单独拿出来用,也可以整条用。

1.2为什么会有这个项目

2024 年之前,机器人学习领域有一个很尴尬的现象:论文越来越多,能复现的越来越少。原因不复杂,但每一层都在叠加成本。

障碍具体表现LeRobot 的解法
数据碎片化每个实验室用自己的 HDF5 结构,字段名、坐标系、采样率都不同。A 组的数据在 B 组的代码里读不出来。统一的 LeRobotDataset 格式 + Hub 托管
硬件绑定策略代码里写死了关节数量、相机数量、控制频率。换一台机器人就要重写一遍 I/O。Robot 抽象类 + 配置驱动
实现不可读论文附带的代码经常是研究原型,硬编码路径、缺依赖、没有测试。纯 PyTorch 重写 + CI + 文档化的 API
硬件太贵主流的 ALOHA 双臂系统成本在数万美元,个人和小实验室进不来。主推 SO-100 / SO-101 这类几百美元的舵机臂
评测不统一每家在自己的任务集上报成功率,数字无法横向比较。统一接 LIBERO / MetaWorld,并提供 EnvHub 机制

把这五条放在一起,你会看到 LeRobot 不是在做某一个技术突破,而是在修基础设施。这也解释了为什么它的价值随着时间越来越高:基础设施的回报是复利式的,用的人越多,数据越多,格式越稳,后来者越没有理由另起一套。

2026 年,这套基础设施得到了学术界的正式确认——团队在 ICLR 2026 发表了论文《LeRobot: An Open-Source Library for End-to-End Robot Learning》。从「一个开源仓库」变成「一个有引用格式的学术成果」,这个转变本身说明了问题:在具身智能这个领域,基础设施已经成了需要被研究、被引用的对象。

1.3现状与版本

几个数字,帮你建立体量感(截至 2026 年 11 月 1 日):

指标数值说明
最新版本0.6.1PyPI 可直装,pip install lerobot
Python 要求>= 3.12注意:比很多教程里写的 3.10 要高
Star28,617机器人学习类开源项目里第一梯队
Fork5,991插件生态的基础
开放 issue877侧面说明迭代强度
许可Apache 2.0可商用,可闭源衍生

877 个开放 issue 不是坏消息,而是这个阶段的正常状态。它意味着有大量真实用户在用、在报问题,也意味着你遇到的问题大概率已经有人踩过——搜索 issue 是使用 LeRobot 的第一技能。

安装的两种方式

第一种,直接装 PyPI 包。适合只想跑策略、读数据集的人:

pip install lerobot
lerobot-info          # 打印版本、已装 extras、检测到的设备

基础安装很轻。功能按需加载,每种能力对应一个 extra:lerobot[dataset] 读数据,lerobot[training] 训模型,lerobot[core_scripts,feetech] 用 SO-101 录数据。这个设计的好处是:只读数据集的人不需要装 CUDA 相关的几百兆依赖。

第二种,从源码装。适合要改代码、要提 PR 的人(第四章会详细讲)。

第一个坑

网上大量教程写的是 pip install -e ".[aloha,pusht]" 这类旧写法。v0.6 起 extras 已经重组为按能力分组(dataset / training / hardware / viz / core_scripts / evaluation),旧的按硬件分组的部分写法已经失效。装之前先看 pyproject.toml 里的 [project.optional-dependencies],那是最权威的清单。

1.4仓库地图:目录结构就是架构图

读一个陌生的开源项目,最有效的方式不是从 README 开始,而是先看 src/ 下的目录名。目录划分往往暴露了作者心里的抽象边界,而 README 只会告诉你它有多好。

LeRobot 的 src/lerobot/ 下有三十二个条目(二十七个包 + 几个单文件)。初看很碎,但按职责归一下,会发现它严格分成五层:

入口层ENTRY
scripts/configs/templates/jobs/
策略层POLICY
policies/processor/model/optim/rewards/rl/
运行时层RUNTIME
inference/rollout/remote_inference/transport/streaming/distributed/
数据层DATA
datasets/data_processing/transforms/annotations/
硬件层HARDWARE
robots/teleoperators/motors/cameras/
横切CROSS-CUTTING
common/utils/envs/lerobot_types.py__version__.py

↑ 五层栈。上层只依赖下层,反向依赖只通过抽象(Robot、Policy、Processor)发生。这意味着你可以只替换其中一层——比如保留数据与训练,换掉硬件层去接自己的机械臂。

这种分层不是学院式的洁癖,它有非常实际的好处:你可以在没有硬件的情况下训练和评测,也可以在没有 GPU 的情况下采数据。因为「硬件层」和「策略层」之间没有直接引用,中间隔了一个 processor 和一个 Robot 接口。

1.5逐模块详解

下面按从下往上的顺序讲。这个顺序不是随意的——它对应你将来读源码时最省力的路径:先看懂最底层的约定,再看上层怎么用它。

1.5.1硬件层:robots / teleoperators / motors / cameras

四个包,解决一件事:把物理世界翻译成 Python 字典。

robots/ 里最重要但不是最显眼的文件是 robot.py——它定义了抽象的 Robot 基类。所有具体机器人都是它的子类,都只承诺四件事:

class Robot:
    def connect(self)            # 打开串口/CAN,上电,读初始状态
    def get_observation(self) -> dict[str, Tensor]
    def send_action(self, action: dict[str, Tensor])
    def disconnect(self)

约定的细节在于字典的键名。不是随意的字符串,而是一套命名规则:shoulder_pan.pos、gripper.pos、wrist(相机名)等等。策略层不认识任何具体硬件,它只认识这些键。所以键名就是硬件与算法之间的契约——这也是为什么接一台新机器人时,最容易出错的地方不是驱动,而是键名对不上。

包已内置实现(部分)说明
robots/so_follower · bi_so_follower
koch_follower · lekiwi
openarm_follower · rebot_b601_follower
unitree_g1 · reachy2 · hope_jr
omx_follower · earthrover_mini_plus
从几百美元的舵机臂到人形与移动底盘。bi_* 前缀表示双臂。
teleoperators/so_leader · bi_so_leader · koch_leader
openarm_leader · rebot_102_leader
gamepad · keyboard · phone · homunculus
reachy2_teleoperator · unitree_g1
主从臂、手柄、键盘、手机、VR 手部追踪。遥操器本身也是一种「机器人」的对称物。
motors/Feetech · Dynamixel · CAN 设备伺服总线的读写、ID 分配、限位与使能。对应 extras:feetech、rustypot、can。
cameras/OpenCV(USB 相机)· RealSense统一成「打开 / 读帧 / 关闭」,支持第三方相机插件。

这里有一个容易被忽略的设计:遥操作臂和从动臂用同一套代码路径。so_leader 和 so_follower 都是 Robot 的子类,区别只是前者被读取、后者被写入。这带来的好处是校准逻辑、端口扫描、限位保护全部复用;代价是你会看到一些看起来多余的接口——比如给主臂也实现了 send_action。这是刻意的冗余,为了让主从对称。

1.5.2数据层:datasets——全项目最核心的包

如果这本书只能讲一个模块,我会选 datasets/。原因很简单:LeRobot 对行业最大的贡献不是那三十个策略,而是这个数据格式。策略会过时,格式不会——一旦几百个数据集都按同一种结构存在 Hub 上,它就变成了沉没成本最低的公共资产。

这个包有二十多个文件,按职责可以分成四组:

职责关键文件做什么
读写核心lerobot_dataset.py
dataset_writer.py
dataset_reader.py
LeRobotDataset 类本身。__getitem__ 返回一个字典,自动处理视频解码、时间戳对齐、字段重命名。
流式与大规模streaming_dataset.py
streaming_sidecar.py
lance_backend.py
multi_dataset.py
不下载全量也能训练;多数据集混合采样;列式后端(Lance)用于超大数据集。
编辑与合并aggregate.py
dataset_tools.py
compute_stats.py
删 episode、按比例切分、增删特征、合并多个数据集、统计归一化参数。
媒体与元信息image_writer.py
video_utils.py / pyav_utils.py
feature_utils.py
card_template.md
写 MP4、编解码、特征 schema 校验、自动生成数据集卡片。

几个值得单独说的设计:

  • 媒体外置。视频不塞进 Parquet,而是单独存 MP4,Parquet 里只记 episode_index 与 frame_index。这样做的直接好处是:训练时按帧随机采样,只需要解码对应的一小段视频,而不是把几百 GB 全读进内存。
  • 统计量预计算。compute_stats.py 把每个特征的均值、方差、分位数提前算好写进 meta。训练时的归一化不再需要扫一遍数据集,这对「边下边训」是必需的。
  • 多数据集混合(multi_dataset + recipe + sampler)。这是协同训练的基础设施:你可以按权重混合「自己的 50 条真机数据」和「十万条仿真数据」,而不需要先物理上合并成一个巨大文件。策略层看到的是一个普通 Dataset。
  • 版本迁移脚本。convert_dataset_v21_to_v30.py 单独放在 scripts 里——格式演进是常态,提供官方迁移路径比追求「一开始就设计对」更务实。
读源码的顺序

建议顺序:lerobot_dataset.py 的 __init__ 与 __getitem__ → dataset_metadata.py(看清 meta 结构)→ dataset_writer.py(理解写盘时序)→ 再看 aggregate.py。前三步读完,你对「一条数据到底存在哪」就没有疑问了。

1.5.3策略层之一:processor——被低估的中间件

如果 datasets 是骨架,processor 就是血管。它负责在「原始字典」和「模型张量」之间来回搬运,并在搬运过程中做所有必要的变换。

这个包有近二十个 processor,名字都很直白,但每一个对应一个真实存在的工程问题:

Processor解决的问题
normalize_processor用预计算的均值方差做归一化;推理时必须用训练时的同一份统计量,否则输出会静默偏移。
relative_action_processor
delta_action_processor
把绝对关节角转成相对末端增量。这是跨本体迁移的关键,第二章会展开。
rename_processor不同数据集字段名不同(observation.images.top vs cam_high),靠它对齐后再混训。
tokenizer_processor把语言指令切成 token,供 VLA 的文本塔使用。
device_processor把张量搬到 GPU,并统一 dtype(bf16 / fp32)。
image_augmentation_processor训练期增强:颜色抖动、随机裁剪。只在训练时启用。
policy_robot_bridge把策略输出接到 Robot.send_action,处理频率不匹配与缓冲。
hil_processorHuman-in-the-loop:人在环干预时切换控制源,用于 HIL-SERL 这类方法。

理解 processor 的关键是理解 pipeline.py:它把若干 processor 串成一条链,训练时正向走一遍,推理时反向还原。这是一个很干净的对称设计——模型只看到归一化后的相对动作,机器人只收到反归一化后的绝对指令,中间的转换全部由这条链负责。

为什么这个设计重要?因为机器人学习里最常见的 bug 不是模型不收敛,而是训练和推理的预处理不一致。少做一次反归一化,机械臂就会以一种看起来很「合理」但完全错误的方式运动,而且日志里不会有任何报错。把变换显式建模成 pipeline,并在 checkpoint 里保存这份配置,是这个 bug 的工程解法。

1.5.4策略层之二:policies——三十个实现,五类范式

policies/ 是外人最感兴趣、也最容易误读的包。它不是一个「模型集合」,而是一个带统一接口的注册表。核心是两个文件:factory.py(按名字造模型)和 pretrained.py(按名字加载权重)。每个策略是一个子目录,内部通常包含 configuration_*.py(超参 dataclass)与 modeling_*.py(网络与损失)。

按 README 的分类,三十个实现分布在五个范式里:

范式代表一句话
模仿学习 ILACT · Diffusion · VQ-BeT
Multi-Task DiT
从演示直接学策略。轻、快、稳,单任务首选。
强化学习 RLHIL-SERL · TDMPC用奖励信号在真机上继续优化,通常需要人在环干预。
视觉语言动作 VLAπ0 / π0-FAST / π0.5 · GR00T N1.7
SmolVLA · X-VLA · MolmoAct2
EO-1 · EVO1 · DM05 · FineART-VLA
LingBot-VLA 2.0 · WALL-OSS 系列
用预训练视觉语言模型做主干,接一个动作专家头。泛化强,显存贵。
世界模型VLA-JEPA · LingBot-VA · FastWAM
Long-WAM · LaWAM · FLUX 3 Action
不直接输出动作,而是学习「动作会让世界变成什么样」,可用于规划或合成数据。
奖励模型SARM · TOPReward · Robometer给轨迹打分,替代人工设计奖励函数。

这个分类本身就是一份领域地图:IL 是基线,RL 是后训练,VLA 是泛化,世界模型是想象,奖励模型是评价。一个完整的现代流水线往往同时用到其中三四个——比如「ACT 冷启动 → VLA 泛化 → 奖励模型打分 → HIL-SERL 精调」。

另外几个单文件值得一提:chunk.py(动作块的时间维度处理,几乎所有现代策略都用到)、rtc.py(real-time chunking,实时分块执行)、pi_gemma.py(π 系列共用的主干封装)。这些是把「论文里的公式」变成「能跑在 30 Hz 控制循环里的代码」的地方。

1.5.5入口层:configs + scripts——配置即 API

LeRobot 有一个很强烈的设计选择:不用 YAML,用 Python dataclass 当配置。configs/ 下的 train.py、eval.py、dataset.py、policies.py 都是一层套一层的 dataclass,parser.py 负责把命令行参数解析进这些 dataclass。

lerobot-train --policy.type=act --dataset.repo_id=lerobot/aloha_mobile_cabinet --policy.push_to_hub=false

这行命令里,policy.type 决定实例化哪个 dataclass,dataset.repo_id 落到 DatasetConfig 上。好处是三层:类型检查在启动前就能发现拼写错误;IDE 能补全所有参数;配置对象可以直接序列化进 checkpoint,于是「用哪个参数训的」这件事被永久记录在模型里。

scripts/ 下有二十三个入口,是用户唯一需要记住的「表面」。完整的表在附录 A,这里只按功能分组给你一个印象:

  • 硬件准备:lerobot_find_port(找串口)、lerobot_setup_motors(分配 ID)、lerobot_calibrate(校准)、lerobot_find_joint_limits、lerobot_setup_can。
  • 采集与回放:lerobot_teleoperate、lerobot_record、lerobot_replay、lerobot_annotate。
  • 训练与评测:lerobot_train、lerobot_train_tokenizer、lerobot_eval、lerobot_rollout。
  • 数据工具:lerobot_dataset_viz、lerobot_edit_dataset、lerobot_imgtransform_viz、lerobot_build_mp4_sidecar、convert_dataset_v21_to_v30、augment_dataset_quantile_stats。
  • 服务化:lerobot_policy_server(把策略跑成一个服务端,机器人端做客户端)。

1.5.6运行时层:inference / rollout / transport

这一层是 v0.5 之后新长出来的,也是最容易被老教程忽略的部分。它回答一个具体问题:模型前向一次要 200 毫秒,但控制循环要求 30 Hz,怎么办?

inference/ 里的文件名几乎就是答案:chunk_settings.py(一次预测多少个动作)、prediction.py(预测)、execution.py(执行)、sync.py(同步模式)、rtc.py(实时分块)、latency.py(延迟估计)、policy_runner.py(跑策略的进程/线程)、dispatch.py、contracts.py(输入输出契约)。

核心思路是预测与执行解耦:策略一次吐出未来 K 步的动作块,执行端按控制频率逐步消费;与此同时策略已经在算下一块。当新块到达时,用一个插值或重叠平均把接缝抹平(rollout/ 里的 ring_buffer.py 与 strategies/ 就是干这个的)。

rollout/ 则是「把策略接到真机器人上」的编排层:robot_wrapper.py(包装硬件)、controller.py(控制循环)、planner.py(任务规划)、interactive.py(人工干预)、status.py(状态机)。transport/ 与 remote_inference/ 负责跨进程/跨机器通信——把大模型放在带显卡的服务器上,机器人端只跑一个轻客户端。

为什么这层值得单独学

绝大多数「模型在离线评测里 90%,上真机只有 40%」的案例,问题不在模型,而在这里:动作块接缝不平滑、延迟没补偿、控制频率抖动。读一遍 inference/ 比调三天超参有用。

1.5.7其余模块

  • envs/:把 LIBERO、MetaWorld 等基准包装成统一接口,供 lerobot-eval 调用。也支持通过 EnvHub 发布自定义环境。
  • rl/ 与 rewards/:强化学习算法与奖励模型的实现,配合 hil_processor 做人在环训练。
  • annotations/:数据标注(语言分段、技能标签),对应 lerobot_annotate。
  • data_processing/ 与 transforms/:更底层的图像/张量变换,被 processor 调用。
  • distributed/ 与 optim/:多卡训练、优化器与学习率调度。
  • jobs/:把任务提交到 HF Jobs(云端跑训练),hf.py 是 Hub 相关封装。
  • common/:control_utils.py(控制循环工具)、train_utils.py(训练工具)、trackio_utils.py(实验记录)。
  • templates/:新机器人/新策略的脚手架模板——第四章会用到。

1.6一次训练的数据流

把模块串起来看,整条链路只有四个阶段。每个阶段之间传递的不是「代码」,而是落在磁盘上的产物——这一点很重要,因为产物可以被检查、被替换、被单独复现。

01 RECORD 采集 主从臂遥操,三路同步写盘 lerobot-record 产出 → 数据集目录 02 TRAIN 训练 Dataset → processor → 梯度 lerobot-train 产出 → checkpoint 03 EVAL 评测 仿真里跑 N 轮,统计成功率 lerobot-eval 产出 → 指标 04 DEPLOY 部署 真机闭环,动作块流式执行 lerobot-rollout 产出 → 失败样本 失败样本回流 → 补采 → 重训 DATA FLOW / 四个阶段,靠磁盘产物衔接
图 1-1四个阶段之间没有隐式依赖:只要产物符合格式,你可以用别处采的数据训练、用别的框架训出的权重来部署。第四阶段产出的失败样本回到第一阶段,构成闭环——2026 年几乎所有论文都在这条回路上做文章。

值得说明的是第二阶段内部发生了什么。一个 batch 的组装是这样的:

  1. LeRobotDataset 按 frame_index 取出该帧的 Parquet 行(状态、动作、任务索引)与对应的视频帧(解码后是 HWC 张量)。
  2. 根据 meta/stats.json 里的统计量做归一化,把状态和动作压到零均值附近。
  3. 把绝对动作按配置转成相对增量(如果策略需要)。
  4. 语言指令经 tokenizer 变成 token id,补齐到固定长度。
  5. 所有张量搬到 GPU,统一 dtype,reshape 成 [B, T, ...]。
  6. 策略前向、算损失、反向、更新。

第 2 到第 5 步全部由 processor 完成,而不是写在策略内部。这是 LeRobot 与「论文仓库」最大的结构性差异:论文仓库把这些变换写死在 model.py 里,换个数据集就要改代码;LeRobot 把它抽出来变成可配置的链条,换个数据集只改配置。

1.7四条设计哲学

读到这儿,你已经见过全部零件。最后用四条原则把它们收拢——这四条不只解释 LeRobot,也解释了过去两年所有活得好的机器人开源项目。

01配置即 API,而不是装饰

用 dataclass 而不是 YAML,用 --policy.type 而不是 --config my.yaml。表面上是语法选择,实质上是把「可配置的东西」和「代码结构」对齐:每个可调项都必须对应一个明确归属的类。副作用是配置能随 checkpoint 一起保存,于是复现性变成了默认属性而不是额外工作。

02一切皆 dict[str, Tensor]

观测是字典,动作是字典。这个决定让「换机器人」从改代码变成改配置:只要新机器人的键名和老机器人一致,上层完全不用动。代价是失去了静态类型带来的安全感——键名拼错要到运行时才报错。这是 LeRobot 里最常见的一类 bug,也是为什么它提供了 rename_processor 和特征校验。

03纯 PyTorch,不绑框架

三十个策略全部用原生 PyTorch 写,不依赖某个训练框架的抽象。这让代码更啰嗦(你要自己写训练循环),但换来两件事:调试时 stack trace 是直的;任何人读 modeling_*.py 都能对上论文公式。对基础设施项目来说,可读性比简洁性重要。

04格式优先于模型

这是最重要的一条。团队把最多的工程投入放在了 datasets 而不是 policies:格式、流式、合并、统计、卡片、迁移脚本。因为模型会被超越,格式会沉淀。两年后 π0 可能没人用了,但那时候的数据集大概率还叫 LeRobotDataset。

判断一个基础设施项目能不能活下来,看它把工程量花在哪:花在「接入更多模型」上的,会随模型一起过时;花在「让别人更容易贡献数据」上的,会随数据一起增值。
CHAPTER 02

具身智能的原理与趋势

Why Robots Are Hard to Teach

监督学习假设数据是独立同分布的。机器人不满足这个假设——它的每一个动作都会改变下一个输入,所以错误会累积,而不是被平均掉。

2.1为什么机器人学习比图像分类难

如果你做过视觉模型,第一次接触机器人策略训练会有一种强烈的不适感:所有指标看起来都很好,真机上一塌糊涂。训练 loss 降到了 0.01,验证集上动作的 L2 误差只有几毫米,但机械臂在空中画出诡异的弧线,然后撞到桌子。

这不是调参问题,是问题结构本身不同。四个根本差异:

1分布会移动,而且是被你自己推动的

图像分类里,测试集的分布和训练集不同,但至少是固定的。机器人不一样:策略在状态 s 采取动作 a,环境转移到 s',而 s' 的分布取决于策略本身。你在训练时看到的状态分布,和部署时遇到的状态分布,是同一个策略的两副面孔。一旦策略在某个状态下出错,它就会进入训练数据里从未出现过的区域,然后错得更离谱。这个现象叫协变量偏移(covariate shift),在机器人里它有一个更狠的名字:复合误差。

2错误会累积,不会被平均

分类错一帧,下一帧还有机会;控制错一步,后面全歪。一条 10 秒、30 Hz 的轨迹有 300 步,每步 99% 的正确率,整条轨迹完全成功的概率是 0.99³⁰⁰ ≈ 5%。这就是为什么论文里 90% 的单步精度毫无意义,而「任务成功率」才是唯一指标。

3同一个状态有多个正确答案

从左边绕还是右边绕,先抓把手还是先扶杯身——都行。传统回归损失(MSE)面对这种多模态分布,会输出两个答案的平均,而平均值往往既不是左也不是右,是撞在中间。这是 Diffusion Policy 出现的直接原因,也是为什么现代策略几乎不再直接用 MSE 回归动作。

4数据贵得离谱

ImageNet 有 1400 万张图,采集成本约等于零(网上本来就有)。机器人没有「互联网规模的动作数据」:每一条示范都要一个人、一台机器、一段时间。行业公认的量级是:要达到 GPT-3.5 那种通用性,需要一亿小时的真实交互;而目前全球有效数据只有几十万小时。

2.2形式化:其实就是一个闭环

把上面这些说清楚,只需要一张图。机器人控制是一个部分可观测马尔可夫决策过程(POMDP),但在模仿学习的语境下,它可以简化成最朴素的形式:

POLICY 策略 π(a | o) 神经网络:图像 + 语言 + 本体状态 → 未来 K 步的动作 ENVIRONMENT 机器人 + 世界 动力学、接触、摩擦、光照 → 新的观测与状态 a_t 动作 o_t+1 观测 人类专家(遥操作 / 手持设备) 产生 (o, a) 配对 → 这就是「数据」 监督信号 FIG 2-1 / 模仿学习就是在这个环上补箭头 实线 = 部署时发生的事;虚线 = 训练时补上的监督
图 2-1策略与环境构成闭环。模仿学习做的事,就是用人类演示把这个环「拆开」变成监督学习:给定 o,回归 a。但拆开之后,闭环里的复合误差问题并没有消失,只是被藏起来了。

用公式写就是最小化负对数似然:

L(θ) = − E(o,a)~D [ log πθ(a | o) ]

看起来和图像分类的交叉熵一模一样。区别在于:分类的 a 是一个离散标签,机器人这里的 a 是一个连续的、多维的、有时序结构的向量——一台六轴臂加一个夹爪就是 7 维,双臂就是 14 维,人形可能是 40 维以上。「怎么表示这个 a」是整个领域最核心的工程问题之一,下一节专门讲。

2.3模仿学习三代:从回归到生成

过去八年,机器人模仿学习只发生了两次范式转移。理解这两次,你就理解了 LeRobot 里为什么会有这么多策略实现。

第一代行为克隆(BC):直接回归

最简单的方法:一个 CNN 提图像特征,拼上本体状态,MLP 输出动作。损失用 MSE。

它的问题是上一节第 2、3 条的直接后果:MSE 面对多模态动作会输出平均解,且复合误差无人处理。业界的补救办法是DAgger:让策略自己去跑,跑偏了由人类接管纠正,把纠正后的 (o, a) 加回数据集重训。理论上正确,实践上很贵——每个人类纠正都要实时介入。

第二代动作块 + 生成模型:ACT 与 Diffusion Policy

2023 年两个工作几乎同时给出了两个方向的解法,它们构成了今天所有策略的底座。

ACT(Action Chunking with Transformers) 的思路是「少做决定」:不预测下一步动作,而是一次预测未来 100 步(一个 chunk)。这样复合误差的累积次数从 300 次降到 3 次。为了处理多模态,它引入了一个 CVAE 结构——训练时用一个编码器从完整动作序列推断隐变量 z,推理时把 z 置为零(取分布的均值)。执行时用 temporal ensembling 把相邻 chunk 的重叠部分加权平均,抹平接缝。

Diffusion Policy 的思路是「把动作生成当成图像生成」:把动作序列看作一条一维的「轨迹图像」,用 DDPM 从噪声逐步去噪出动作。多模态问题被天然解决——不同的噪声种子会收敛到不同的有效解,而不是平均。代价是推理需要多步去噪,慢。

为什么 ACT 至今仍是默认选择

在单任务、中等精度、几百条数据的场景下,ACT 训练快(单卡几小时)、推理快(一次前向)、调参少。它不是最强的,但它是性价比最高的基线。任何新方法如果打不过一个认真调过的 ACT,就还没有到值得部署的程度。

第三代流匹配(Flow Matching):一步到位的生成

扩散模型要迭代几十步,太慢。流匹配学的是一个速度场:从噪声到动作的直线路径上,每一步该往哪走。训练目标更简单(回归速度场而不是噪声),推理可以用很少的步数(4 到 10 步)就得到高质量样本。π0 系列用的就是这套。

工程上还有一个更激进的变体:π0-FAST 把动作离散化成 token,用自回归的方式逐 token 生成。这让动作生成可以直接复用语言模型的解码器与 KV cache,但牺牲了连续性。LeRobot 里 pi0_fast 就是这一类。

代际代表处理多模态推理开销适用
BCMLP / ResNet 策略不能,会平均极低单一解、高精度、数据量大
ACTACT · VQ-BeTCVAE 隐变量低(一次前向)单任务、几百条演示
扩散Diffusion Policy天然支持高(多步去噪)多解任务、精度要求高
流匹配π0 · π0.5 · SmolVLA天然支持中(4—10 步)通用操作、跨本体

2.4动作表示之战:一个被低估的关键设计

同一份演示数据,用不同的动作表示喂给模型,学出来的东西完全不同。这个问题在论文里常常藏在附录,但在工程上是第一决策。

表示形式优点代价LeRobot 中的对应
绝对关节角7 维(6 关节 + 夹爪)简单、无歧义、可直接下发绑定本体;换个臂就废;对校准误差敏感原始数据集默认
绝对末端位姿位置 3 + 旋转 6D与关节结构无关依赖逆运动学求解,可能无解需自行配置
相对末端增量Δ位置 + Δ旋转跨本体能力最强;数值小、易归一化误差会累积;需要本体状态作为基准relative_action_processor
增量动作块[K, 7] 的相对序列兼顾时序平滑与跨本体需要处理 chunk 接缝delta_action_processor + inference

为什么「相对增量」会成为主流?因为它是目前唯一能同时满足三个约束的表示:可跨本体(不依赖关节数量)、可混训(不同机器人的数据可以放在同一个 batch 里)、可增量部署(每次只走一小步,出错也小)。

但它有代价,而且这个代价经常被忽略:相对动作的误差会累积。每步偏 0.5 毫米,走 100 步就偏 5 厘米,而且没有任何机制把它拉回来。解法通常有两层:一是执行端有绝对位置的下位控制(舵机自身闭环),二是策略的观测里必须包含当前末端状态,让它「知道自己偏了」。

一个实用的判断:如果你只打算用一台机器人,绝对关节角最简单;如果你打算用别人的数据、或者把模型迁移到第二台机器人,从第一天就该用相对末端增量。

2.5VLA:把语言模型接进控制回路

2024 年之后,机器人策略的主线变成了「拿一个预训练的视觉语言模型(VLM)当主干,再接一个动作输出头」。这个范式叫 VLA(Vision-Language-Action)。

动机很直接:VLM 已经在互联网规模的图像-文本对上学会了「杯子是什么」「把手在哪」「这句话指哪个物体」。这些是语义先验,而机器人数据的瓶颈恰恰是样本量。用 VLM 初始化,等于把几十亿张图的知识免费搬进了策略里。

三种「接头」方式

  • 离散 token 头:把每个动作维度离散成 256 个 bin,当成词表里的 token,用自回归解码。代表是 RT-2、π0-FAST。优点是复用语言模型全套设施;缺点是量化误差,精细任务上会抖。
  • 扩散头:VLM 出特征,后面接一个扩散模型生成连续动作。代表是 Diffusion Policy 的 VLM 版本、GR00T 早期版本。优点是连续、平滑;缺点是慢。
  • 流匹配头:VLM 出特征,接一个小的「动作专家」网络,用流匹配生成动作块。代表是 π0、π0.5、SmolVLA。目前是主流。

为什么要「双塔解耦」

π0 之后几乎所有人都采用了一个结构:视觉语言塔(大、预训练、冻结或低秩微调)与动作专家塔(小、从零训练、全参更新)分离,中间用共享注意力交换信息。

原因有两个。第一是防止灾难性遗忘:如果让整个 VLM 全参微调去学「怎么拧瓶盖」,它会把「什么是瓶盖」这部分语义知识覆盖掉。第二是参数效率:动作专家通常只有几亿参数,而主干有几十亿,冻结主干能让训练显存降一个档。

LeRobot 里的体现是:smolvla(450M,能在消费级显卡上跑)、pi05(3.6B)、groot(3B,Cosmos-Reason 主干 + DiT 头)走的是同一条路,只是主干与头的规模不同。

被低估的敌人:延迟

一个 3B 的 VLA,在单张消费级显卡上前向一次要 100 到 300 毫秒。而机械臂的控制频率是 30 到 100 Hz,也就是每 10 到 33 毫秒要一个新指令。模型比控制器慢一个数量级。

解法不是「让模型变快」,而是改架构:一次预测未来 K 步(动作块),执行端按控制频率逐步消费,模型在后台算下一块。这就是第一章提到的 inference/ 层的全部意义,也是 rtc.py(real-time chunking)存在的原因。新块到达时,旧块还没走完,两者的重叠部分要做加权融合——这个融合策略的细节,直接决定了真机上动作是否平滑。

一个常见误解

「参数量越大效果越好」在 VLA 上不成立。SmolVLA 用 450M 参数在多个基准上追平 3B 级模型,原因是它的训练数据经过了精心的筛选与配比。在具身智能里,数据质量对性能的边际贡献远大于参数规模——这也是为什么 LeRobot 把工程量押在数据集上。

2.6数据的三个难题

前面所有方法论的讨论,最后都撞在同一堵墙上:数据。而「数据不够」这个说法太笼统,它实际是三个不同的问题。

难题一规模:差两到三个数量级

语言模型靠互联网文本,视觉模型靠互联网图片,两者都是「本来就有」的副产品。机器人动作数据没有这样的来源——每一条都要人花时间。目前全球有效的高质量具身数据在几十万小时量级,而行业估算需要一亿小时。这个缺口不可能靠「多建几个数采中心」补上,因为那是一条线性增长的路。

难题二本体:数据是绑在硬件上的

一台 Franka 采的数据,换一台 SO-101 就不能直接用:关节数不同、连杆长度不同、相机位置不同。解法有两条——一是用相对动作表示降低绑定程度(见 2.4),二是用重定向(retargeting)把动作映射到新本体。两条都不完美,而且都需要额外工作。

难题三长尾:真实世界的多样性在别人手里

实验室能复刻的场景是「样板间」:一张桌子、几个固定物体、稳定光照。而真实世界分布在酒店客房、冷链仓库、理发店、汽修厂里。这些场景你租不到,也搭不出来。这就解释了过去一年为什么会出现「手持采集设备」和「众包数据平台」——把采集权从机构手里放出去,是唯一能触达长尾的方式。

路线代表单位成本真实度能解决哪个难题
真机遥操作ALOHA · DROID · SO-101高最高(真实动力学)精度、接触丰富任务
手持无本体UMI · Grabette低中(缺力反馈)规模、长尾
人类第一视角EgoDex(Vision Pro)极低(被动)低(人手≠机器人手)规模、语义先验
仿真合成RoboTwin · Genie Sim极低(算力)低(物理近似)规模、可控评测

2.72026 年的六个趋势

把近一年的论文与开源发布放在一起看,有六条线正在收敛。它们也是你判断「下一个值得学的技术」的依据。

趋势一架构已经收敛:VLM 主干 + 独立动作专家

从 π0 到 GR00T 到 SmolVLA 到 X-VLA,结构惊人地一致:一个大主干负责理解,一个小子网负责出动作,中间用注意力交换信息。差异只剩在动作头类型(流匹配 / 扩散 / 离散 token)和数据配比上。这意味着「设计一个新架构」的边际回报正在下降,而「准备一份别人没有的数据」的回报在上升。

趋势二推理与执行彻底解耦

动作块 + 异步推理 + 实时分块(RTC)从「优化技巧」变成了「默认架构」。LeRobot 专门为此建了 inference/ 与 remote_inference/ 两个包,说明这已经是基础设施问题,不是模型问题。

趋势三世界模型从「生成视频」转向「预测后果」

早期的世界模型目标是把视频生成得像;现在的目标是用它做动作条件预测——给定动作,未来状态会怎么变。这带来两个用途:一是作为策略的辅助损失(学到物理直觉),二是合成训练数据。LeRobot 里 World Models 这一整类(VLA-JEPA、LingBot-VA、FastWAM、Long-WAM 等)就是这条线。

趋势四奖励模型成为独立组件

强化学习在真机上最大的障碍是「没有奖励函数」。人工设计奖励脆弱,学一个判别器又容易奖励黑客。把奖励模型单独做成一类可复用的组件(SARM、TOPReward、Robometer),让「评价」和「优化」解耦,是近一年很务实的进展。

趋势五失败数据进入一等公民

过去的数据集只收成功示范。2026 年几个大版本都改了:有的把「抓空、滑落、碰撞」单独做成一期主题,有的用「人工干预 + 回滚」把失败轨迹转成成对的偏好样本做后训练。只训成功态的模型,学不会世界怎么拒绝它。

趋势六评测正在标准化,但还没统一

LIBERO、MetaWorld、RoboTwin 成了事实上的公共基准,LeRobot 用 envs/ 把它们包成统一接口,还用 EnvHub 允许社区发布新环境。但「仿真分数能不能预测真机成功率」仍然没有公论——目前最诚实的做法是同时报仿真与真机,并说明真机的测法。

这一章的一句话总结

具身智能的算法层已经收敛到「够用」的程度:一套 VLM + 动作专家的架构,配上动作块与异步推理,就能覆盖大部分操作任务。真正没有收敛的是数据——规模、本体、长尾三个问题都还开着。所以在 2026 年,把时间投在「怎么采到别人没有的数据」上,比投在「再设计一个动作头」上回报更高。

CHAPTER 03

动手搭建

From Screws to a Trained Policy

这一章可以照着做。但请先读完 3.1,因为它决定了你要买什么——买错硬件的成本,远高于配错环境的成本。

3.1三条路线,先选一条

很多人一上来就问「买哪台机器人」。这个问题问早了。先确定你要做的是哪件事:

你的目标该走的路投入多久看到结果
只想看看这东西是什么3.9 无硬件路线:读公开数据集 + 跑仿真评测0 元一个周末
要训一个能抓东西的策略SO-101 主从臂 + 两个相机1,600—3,000 元一周
要做研究、跑基准、发论文先用仿真(LIBERO / RoboTwin),再考虑真机一块 GPU两周
要把自己的设备接进来写一个 Robot 子类(第四章 4.3)0 元 + 你的硬件三到五天

本章主线走第二条:SO-101。选它的理由不是性能好,是它把「标准」这件事做到了极致——结构件开源、舵机型号固定、社区里有几千人用同一套东西采数据,出问题能搜到答案。

3.2硬件清单

SO-101 是一套主从结构:你用一只手带动「主臂」,另一只手(严格说是另一台同构的臂)作为「从臂」跟随。主臂不需要电机驱动,只读角度;从臂才出力。所以两臂的舵机数量相同,但主臂的舵机工作在「读取」模式。

物料数量说明
结构件(3D 打印)1 套PLA 或 PETG。打印精度直接影响回差,建议 0.2 mm 层高、40% 填充。
Feetech STS3215 舵机12 个主臂 6 + 从臂 6。带磁编码器,可读绝对位置——这是它能断电续位的前提。
舵机控制板(USB 转总线)2 块每臂一块。板上有电平转换与电源输入。
电源1—2 个5V / 5A 以上。电压不足会表现为「舵机抖动」或「中途掉线」,很容易误判成软件问题。
USB-C 数据线2 条必须是数据线,不能是充电线。这是新手第一大坑。
USB 摄像头2 个一个装腕部(看夹爪),一个装全局(看桌面)。建议 640×480 / 30 fps 起步。
桌面夹具 / 配重1 套不打夹具的臂会在快速动作时整体位移,数据全废。
螺丝与轴承包1 套通常随结构件一起买。

成本区间:整套约 1,600 元(自行采购舵机 + 打印)到 3,000 元(含打印服务的成品套件)。第三方套件从 199 到 500 美元不等,差异主要在舵机版本与结构件质量。价格波动大,下单前先看仓库的硬件文档。

省钱的地方与不能省的地方

结构件可以自己打印,能省几百块。但舵机不要买便宜替代型号:总线协议、编码器分辨率、堵转扭矩任何一项不同,都会导致校准漂移或夹爪夹不住东西,而这些问题在软件层完全看不出来。电源也不要省,供电不足造成的间歇性掉线极难排查。

3.3组装与接线:三个必须做对的细节

机械装配按官方图纸走即可。这里只讲三件图纸上不会强调、但会决定你后面两周是否顺利的事。

01总线是菊花链,不是星型

六个舵机串在同一条总线上:控制板 → 舵机1 → 舵机2 → … → 舵机6。每个舵机有两个接口,进和出。接反了不会烧,但不通。上电前先用手拨一下每个关节,确认机械上能自由转动(主臂必须能,从臂在断电时也能)。

02舵机 ID 必须一条一条设

出厂 ID 都是 1,六个串在一起会冲突。正确做法是每次只接一个舵机,用 lerobot-setup-motors 把它改成目标 ID,断电,再接下一条。这一步枯燥但不可跳过——ID 错了的表现是「某个关节乱转」,而不是报错。

lerobot-setup-motors --robot.type=so101_follower --robot.port=/dev/ttyACM0

03主臂的舵机要设成「只读」

主臂不输出力矩,否则你会感觉像在推一堵墙,而且反向电流可能损伤控制板。配置里通过 --teleop.type 选择 so101_leader 就自动处理了——这也是为什么主从必须用不同的 type,即使它们是同一套硬件。

3.4软件环境

先确认 Python 版本。v0.6.1 要求 Python ≥ 3.12,这一点和大量旧教程(写着 3.10)不同。用 3.10 会直接装不上。

python --version        # 需要 3.12 或更高
pip install "lerobot[core_scripts,feetech]"
lerobot-info            # 打印版本、extras、检测到的设备

extras 是按能力分的,不是按硬件分的。选错会导致「命令存在但导入失败」:

extra给你的东西什么时候需要
dataset读数据集、视频解码、Hub 下载几乎所有场景都要
training训练循环、加速、实验记录(trackio)要训模型
hardware串口、键盘监听、配置比对接真机
vizRerun / Foxglove 可视化调试数据流
core_scriptsrecord / replay / calibrate / teleoperate 全套做采集
evaluation评测框架(还需环境与策略各自的 extra)跑基准
feetech / rustypot / can对应总线的驱动按你的舵机型号

如果要跑 VLA,还要额外装对应策略的 extra(比如 lerobot[pi] 之于 π0 系列),并注意 transformers 的版本被钉在一个较窄的区间里——这是经常导致「昨天还能跑,今天 import 失败」的原因。

3.5五步跑通全流程

这五步是 LeRobot 的「主路径」。每一步都产出可检查的东西,不要跳步。

STEP 1找端口

lerobot-find-port
# 拔掉设备再插上,观察新出现的是哪个,通常是 /dev/ttyACM0、/dev/ttyACM1
# macOS 是 /dev/tty.usbmodemXXXX
sudo usermod -aG dialout $USER   # Linux:把当前用户加入串口组,重新登录生效

权限问题占新手报错的一半。表现是 Permission denied: '/dev/ttyACM0',看起来像硬件坏了,其实只是没进组。

STEP 2校准

lerobot-calibrate --robot.type=so101_follower --robot.port=/dev/ttyACM0 --robot.id=my_arm
lerobot-calibrate --teleop.type=so101_leader  --teleop.port=/dev/ttyACM1 --teleop.id=my_leader

校准做的事:让每个关节走到两个极限位置,记录编码器读数与机械限位的映射,写进 ~/.cache/lerobot/calibration/。--robot.id 就是这条校准记录的键,之后所有命令都必须带同一个 id,否则会读到别的机器的校准文件,表现为「动作幅度不对」。

STEP 3遥操

lerobot-teleoperate \
  --robot.type=so101_follower --robot.port=/dev/ttyACM0 --robot.id=my_arm \
  --teleop.type=so101_leader --teleop.port=/dev/ttyACM1 --teleop.id=my_leader \
  --display_data=true

先别录数据,先练。目标是能连续十次拿起同一个物体而不碰倒旁边的东西。手不稳的时候采的数据,是在给模型喂噪声——而且你事后无法从 loss 上看出来。

STEP 4录制

lerobot-record \
  --robot.type=so101_follower --robot.port=/dev/ttyACM0 --robot.id=my_arm \
  --robot.cameras='{top:{type:opencv,index_or_path:1,width:640,height:480,fps:30},
                    wrist:{type:opencv,index_or_path:0,width:640,height:480,fps:30}}' \
  --teleop.type=so101_leader --teleop.port=/dev/ttyACM1 --teleop.id=my_leader \
  --dataset.repo_id=${HF_USER}/so101_pick_cube \
  --dataset.num_episodes=30 \
  --dataset.single_task="pick the red cube and put it in the bowl" \
  --dataset.streaming_encoding=true \
  --display_data=true

录制过程中键盘就是编辑器:→ 保存这一条并进入下一条,← 作废重录,ESC 结束并编码上传。这条交互设计比大多数标注工具都好用——它把「筛选」放进了采集过程,而不是留到事后。

关于数量:一个简单的抓放任务,30 到 50 条通常够训出一个能用的 ACT。但关键不是条数,是变化:每次换物体位置、换一点光照、换一点相机角度。三十条一模一样的演示,等于一条数据重复三十遍。

STEP 5训练

lerobot-train \
  --dataset.repo_id=${HF_USER}/so101_pick_cube \
  --policy.type=act \
  --output_dir=outputs/train/act_pick_cube \
  --job_name=act_pick_cube \
  --policy.device=cuda \
  --steps=20000

单卡 RTX 4090 上,ACT 训 2 万步大约几十分钟到两小时(取决于分辨率与 batch size)。换策略只改 --policy.type——这是 LeRobot 最舒服的一点:数据管线完全不变。lerobot-train 会把配置一起存进 checkpoint,所以「这个模型是用什么参数训的」不需要你另外记。

3.6数据集长什么样

录完之后,~/.cache/huggingface/lerobot/${HF_USER}/so101_pick_cube/ 下会是这样的结构:

so101_pick_cube/ ├── meta/ │ ├── info.json # 版本、fps、总帧数、所有特征的 dtype 与 shape │ ├── stats.json # 每个特征的均值/方差/分位数,训练时归一化直接用 │ ├── tasks.jsonl # 一行一个任务描述 │ └── episodes.jsonl # 一行一条 episode:起止帧、任务、长度 ├── data/ │ └── chunk-000/ │ ├── file-000.parquet # 状态、动作、时间戳、索引(不含图像) │ └── file-001.parquet └── videos/ └── chunk-000/ ├── observation.images.top/file-000.mp4 └── observation.images.wrist/file-000.mp4

四个设计点值得注意:

  • 视频与表格分离。Parquet 里只有数值,图像在 MP4 里,两边靠 episode_index + frame_index 对齐。这样随机采样一帧只需解码几百毫秒的视频片段。
  • chunk 分片。数据量大时按 chunk 组织,避免单文件上千兆导致读取变慢。
  • stats.json 预计算。归一化参数在录制时就统计好。这是「边下边训」能成立的前提,也让训练与推理的行为一致。
  • features 是 schema。info.json 里的 features 定义了所有字段的名字、类型、维度。合并两个数据集前必须先对齐 features,否则会得到一个静默错误的混合体。

想看一眼数据长什么样,不必写代码:

lerobot-dataset-viz --repo-id=${HF_USER}/so101_pick_cube --episode-index=0

3.7评测与部署

仿真评测

先把模型放到标准基准上跑一轮,得到一个可与他人比较的数字:

lerobot-eval \
  --policy.path=${HF_USER}/act_pick_cube \
  --env.type=libero --env.task=libero_object \
  --eval.n_episodes=10

注意:LIBERO 上的分数和你自己的任务没有直接关系,它的价值是确认模型没有训坏,以及给你一个横向参照。

真机部署

两种方式,取决于模型大小:

# 方式一:模型就在本机,直接跑
lerobot-rollout --policy.path=${HF_USER}/act_pick_cube \
               --robot.type=so101_follower --robot.port=/dev/ttyACM0

# 方式二:模型在带显卡的服务器上,机器人端做客户端
lerobot-policy-server --policy.path=${HF_USER}/pi05_finetuned --port=8000

第二种就是第一章说的「推理与执行解耦」。当模型前向要 200 毫秒、控制周期只有 33 毫秒时,你必须把推理放到另一个进程甚至另一台机器上,让执行端靠动作块缓冲顶住。

部署时最容易犯的错

相机配置必须和训练时完全一致——分辨率、帧率、哪个 index 对应哪个视角。模型不认识「顶视图」这个概念,它只认识张量里的第几个通道。顺序一换,动作就全乱,而且不会报错。

3.8故障排查

下面这张表覆盖了社区里出现频率最高的十二类问题。建议先按「现象」查,不要按「怀疑」查。

现象最可能的原因处理
Permission denied /dev/ttyACM*用户不在串口组sudo usermod -aG dialout $USER,重新登录
找不到端口用的是充电线换一条确认能传数据的 USB-C 线
某个关节乱转舵机 ID 冲突单独接该舵机重设 ID
动作幅度不对 / 方向反了校准文件不匹配(id 不一致)检查 --robot.id,重新校准
舵机间歇性掉线供电不足换 5V/5A 以上电源,缩短总线
录的视频全黑相机 index 选错 / 被占用lerobot-find-cameras 确认 index
训练 loss 不降stats 缺失或动作维度不匹配检查 info.json 的 features 与 stats
真机上动作乱飞相机顺序与训练时不一致逐项对齐 cameras 配置
显存不足模型太大或 batch 太大减 batch、开梯度检查点、LoRA 微调主干
视频解码报错ffmpeg / torchcodec 平台 wheel 缺失按 pyproject 注释选对应版本,或回退到 PyAV 解码
推理频率跟不上同步推理改用 rollout + policy server,配动作块
合并数据集后字段错位features 不一致先用 rename_processor 对齐再 merge

3.9零硬件路线

如果你现在一分钱都不想花,仍然可以完整走一遍除了「真机部署」之外的所有环节。

pip install "lerobot[dataset,training,evaluation]"

# 1. 拿一份公开数据集的调试子集(100 条,约 2 GB)
huggingface-cli download lerobot/droid_100 --repo-type dataset --local-dir ./droid_100

# 2. 看一眼它长什么样
lerobot-dataset-viz --repo-id=lerobot/droid_100 --episode-index=0

# 3. 训一个基线
lerobot-train --dataset.repo_id=lerobot/droid_100 --policy.type=act --steps=5000

# 4. 在仿真基准上评测一个公开权重
lerobot-eval --policy.path=lerobot/pi0_libero_finetuned \
             --env.type=libero --env.task=libero_object --eval.n_episodes=10

这套流程跑完,你会得到三样东西:一个能复现的 checkpoint、对数据格式的肌肉记忆、以及对「仿真分数有多不可靠」的直观感受。第三样最值钱——它会让你在将来看到任何「某模型在某基准上 95%」的宣称时,先问一句「真机怎么测的」。

CHAPTER 04

如何参与 LeRobot

From User to Contributor

开源项目的贡献不是从「提 PR」开始的,是从「你遇到的问题别人也会遇到」这个判断开始的。

4.1五种角色,五种进入方式

「参与」这个词很容易让人想到写代码。实际上这个项目的贡献面比代码宽得多,而且最稀缺的那部分恰好不是代码。

角色你做的事门槛稀缺程度
用户用、报 issue、在 Discord 回答别人的问题会用不稀缺,但有用
数据贡献者采自己的场景,按格式上传到 Hub有设备最稀缺
硬件适配者把自己那台机器人接进来(核心或插件)会写 Python高
策略作者复现一篇论文,加成 policies/懂论文 + 工程中
维护者审 PR、定接口、修 CI长期投入极稀缺

注意到一个反直觉的地方:数据贡献比代码贡献更稀缺。因为代码谁都能写,但「别人没有的场景」只有你有。这也是第三章反复强调「先想清楚你要采什么」的原因——它同时是你做研究和使用开源的最优策略。

4.2最小贡献:上传一个数据集

这是投入产出比最高的一件事:不需要改一行代码,但会永久留在公共资产里。

第一步把数据采到「能复现」的程度

  • 相机配置写清楚:几个、装在哪、分辨率、帧率。
  • 任务描述唯一可判:「把红方块放进左边的碗」,而不是「整理一下」。
  • 每条 episode 之间有变化:物体位置、光照、背景至少各变三档。
  • 保留失败样本。可以单独放一个子集,但不要全删——它的价值正在被重新认识。

第二步命名与结构

仓库里 datasets/card_template.md 是官方卡片模板,datasets 包还会自动生成基础字段。命名建议遵循社区的默认习惯:{机器人}_{任务}_{场景},全小写、连字符分隔。不要在名字里塞版本号和日期,版本靠 git tag 与 Hub 的 revision 管理。

第三步写卡片,这是真正的贡献

一张好的数据集卡片应该让陌生人不问你任何问题就能用起来。必须包含:

  1. 硬件:机器人型号、舵机型号、相机型号与安装位置。
  2. 任务定义与成功判据(怎么算成功,这一条 90% 的数据集都没写)。
  3. 规模:episode 数、总帧数、平均时长。
  4. 采集方式:遥操作 / 手持 / 仿真;谁采的、采了多久。
  5. 已知问题:哪些场景没覆盖、哪些 episode 质量差。
  6. 许可:能否商用、能否再分发、要不要署名。
一个判断标准

如果你的数据集卡片里没有「已知问题」这一节,那它大概不是一份诚实的数据集。所有真实采集都有缺陷,写出来比藏起来对使用者更有价值。

4.3接入一台新机器人

LeRobot 内置了十余种机器人,但真实世界里的机械臂远不止这些。接入一台新机器人是整个项目里最受欢迎的一类贡献——每多一种硬件,生态的覆盖面就宽一圈。

目录结构

src/lerobot/robots/my_robot/ ├── __init__.py # 导出类,并注册到工厂 ├── config.py # MyRobotConfig(dataclass):端口、id、相机、限位… ├── my_robot.py # MyRobot(Robot):connect / get_observation / send_action └── (可选) utils.py # 协议解析、单位换算

必须实现的四件事

抽象基类在 robots/robot.py。子类至少要给出 connect、get_observation、send_action、disconnect,另外还有一个常被漏掉的 observation_features / action_features——它告诉数据层「这台机器人的观测有哪些字段、什么形状」。漏了它,录制时会直接失败。

from lerobot.robots.robot import Robot

class MyRobot(Robot):
    config_class = MyRobotConfig
    name = "my_robot"

    def connect(self): ...          # 打开总线,使能舵机,读初始位姿
    def get_observation(self):     # → {"shoulder_pan.pos": tensor, "top": image, ...}
        ...
    def send_action(self, action): # 写入目标位置,处理限位与超时
        ...

三个容易踩的坑

  • 键名要和现有约定一致。<关节名>.pos、相机直接用名字。键名不一致的数据集无法和别人混训,这条比代码风格重要得多。
  • 单位统一。舵机常用「刻度值」,策略期望「弧度或米」。换算放在机器人层,不要放到 processor 里——processor 是共享的。
  • send_action 要幂等且非阻塞。控制循环在 30 Hz 上跑,任何阻塞调用都会拖垮频率,进而让动作块接缝变大。

接完之后,别忘了同步做三件事:在 robots/__init__.py 注册、在 docs/ 补一页硬件说明、在 tests/ 加一个不依赖硬件的接口测试(用 mock 总线)。没有测试的硬件 PR 基本不会被合,因为维护者无法验证。

4.4加一个新策略

如果你复现了一篇论文,把它加进 policies/ 是最有价值的落点。结构比机器人复杂一些:

src/lerobot/policies/my_policy/ ├── __init__.py ├── configuration_my_policy.py # dataclass:层数、维度、chunk 大小、学习率… └── modeling_my_policy.py # nn.Module:forward(batch) → loss / 动作

接口契约

策略类需要提供两样东西:训练时能吃到 batch 并返回 loss(或可直接反传的 dict),推理时能接受单帧观测并返回动作块。policies/factory.py 负责按 --policy.type 实例化,pretrained.py 负责按 Hub 名加载。

最容易出问题的是动作块的时间维度。LeRobot 统一用 [B, K, A](batch、chunk 长度、动作维度)的约定,所有涉及 chunk 的工具(chunk.py、rtc.py、rollout 的环形缓冲)都依赖这个形状。如果你的模型内部用的是别的排布,必须在 forward 出口处转回来。

还需要做的

  1. 在 policies/__init__.py 与 factory 里注册类型名。
  2. 声明它需要的 processor 链(归一化?相对动作?tokenizer?)。这一步决定它能否直接吃标准数据集。
  3. 写 docs/source/policy_my_policy_README.md:论文链接、显存需求、训练时长、已知失效场景。
  4. 加测试:至少一个前向 shape 测试,一个在极小数据集上的过拟合测试(loss 应该能降到接近零——降不下去说明实现有 bug)。
维护者最在意的一件事

不是你的模型刷了多高的分,而是它能不能在别人的机器上跑起来。判断标准很朴素:别人 pip install 之后,照着你的 README 复制三条命令,能不能得到一个 checkpoint。做不到的话,分数再高也不会被合。

4.5更轻的路:插件机制

如果你不想改核心仓库,或者你的硬件太小众不适合进主库,LeRobot 提供了一套插件发现机制:任何已安装的、以特定前缀开头的 Python 包,都会被自动导入。

lerobot_robot_<name>          # 自定义机器人
lerobot_teleoperator_<name>   # 自定义遥操设备
lerobot_camera_<name>         # 自定义相机/传感器

装好之后,插件注册的 type 就能直接在 CLI 里用,不需要改 LeRobot 一行代码:

pip install lerobot_robot_myarm

lerobot-record --robot.type=myarm --teleop.type=so101_leader \
               --dataset.repo_id=${HF_USER}/my-dataset

这条路的价值在于把「贡献」和「审核」解耦了:项目维护者不需要为每一种小众硬件负责,硬件作者也不需要等 PR 合并。社区已经用这种方式接上了 xArm、UR5e、Franka、Piper、WidowX、ARX5、GELLO、SpaceMouse、Meta Quest、ROS 2 桥接,以及各类触觉与深度相机。

对个人来说,这也是最现实的路径:先做成插件跑通,用的人多了,再考虑提请并入核心。反过来做,你会在 review 里耗掉三个月。

4.6开发流程与评审预期

环境准备

git clone https://github.com/<your-handle>/lerobot.git
cd lerobot
git remote add upstream https://github.com/huggingface/lerobot.git
git lfs install && git lfs pull     # 测试需要的素材在 LFS 里
pre-commit install

提 PR 前的四项检查

  1. pre-commit run --all-files 通过(格式、lint、import 排序)。
  2. pytest -sv ./tests 通过。测试按 extra 分 tier:需要 dataset / hardware / viz 的测试必须打上对应 mark,否则会在 base tier 里被跳过或失败。本地可以只跑一层:pytest -sv tests -m dataset。
  3. 涉及 CUDA 的测试打 @pytest.mark.gpu,它只在 GPU CI 里跑。
  4. 如果你改了公开 API,docstring 要符合规范——API 文档是从 docstring 生成的,格式错误会在 CI 里被卡。

分支与提交

不要在 main 上开发,用描述性分支名;提交前 rebase 到 upstream/main(不是 merge,保持历史线性)。PR 描述按模板填:改了什么、为什么、怎么验证的。「怎么验证的」这一栏是评审的第一眼,写「已本地测试」等于没写。

现实预期

这个仓库有近九百个开放 issue,维护团队不大。这意味着:小改动(文档、bugfix、测试)通常几天内会有回应;涉及接口的大改动可能几周没人看。一个务实的策略是先在 issue 里把设计讨论清楚,再写代码——直接提一个大 PR 而被搁置,是这个项目里最常见的挫败感来源。

4.7不写代码的贡献

最后说几件被严重低估的事。

  • 把错误报清楚。一个附了版本号、lerobot-info 输出、完整 traceback、最小复现步骤的 issue,价值等同于一个修复。反之,「record 命令报错了」这样的 issue 只会被关掉。
  • 补文档里的空缺。官方文档覆盖了主路径,但「在 Windows 上装 torchcodec」「用 WSL 接 USB 设备」「不同批次的舵机固件差异」这类边角,全靠社区补。
  • 翻译与教程。README 里专门列了一篇中文教程(同济子豪兄的 SO-ARM101 全流程文档),说明维护团队认可这类贡献的价值。一个有截图、有踩坑记录的中文教程,对中文使用者的帮助超过十个 PR。
  • 写「失败报告」。「我按论文复现了但我这里不 work,我的配置是 X,现象是 Y」——这种帖子在 Discord 里极受欢迎,因为它省下了后面十个人的时间。
开源项目的健康度不看 star 数,看「新手能不能在一天内跑通」。而决定这一点的,往往不是核心代码,是那些把坑标出来的人。
APPENDIX A

CLI 速查

Twenty-Three Entry Points

命令名由 src/lerobot/scripts/ 下的脚本文件名转写而来,具体参数以 <命令> --help 为准。

分组命令用途
硬件lerobot-info打印版本、extras、检测到的设备
lerobot-find-port扫描串口,找出机械臂对应的那个
lerobot-find-cameras列出可用相机与 index
lerobot-setup-motors逐个设置舵机 ID 与波特率
lerobot-calibrate记录关节极限,写校准文件
lerobot-find-joint-limits / setup-can探测关节范围 / 配置 CAN 总线
采集lerobot-teleoperate主从遥操,用于练习与调试
lerobot-record录制数据集,自动编码并上传 Hub
lerobot-replay回放已录 episode,验证数据可用性
lerobot-annotate给数据加语言分段与技能标签
训练lerobot-train训练策略,配置随 checkpoint 保存
lerobot-train-tokenizer为离散动作策略训练 tokenizer
评测与部署lerobot-eval在 LIBERO / MetaWorld 等基准上评测
lerobot-rollout在真机上跑策略,处理动作块流式执行
lerobot-policy-server把策略跑成服务端,机器人端做客户端
数据工具lerobot-dataset-viz可视化数据集(视频 + 曲线)
lerobot-edit-dataset删 episode、切分、增删特征、合并
lerobot-imgtransform-viz预览图像增强效果
lerobot-build-mp4-sidecar为旧数据集补建视频附属文件
convert-dataset-v21-to-v30从 v2.1 格式迁移到 v3.0
augment-dataset-quantile-stats补算分位数统计
APPENDIX B

术语表

Glossary

术语英文一句话解释
策略Policy从观测到动作的映射函数,通常是神经网络。π(a|o)。
动作块Action Chunk一次预测未来 K 步动作,而不是只预测下一步。降低复合误差、缓解推理延迟。
动作专家Action ExpertVLA 里专门负责生成连续动作的小网络,与视觉语言主干解耦。
本体Embodiment机器人的物理形态(关节数、连杆、传感器)。「跨本体」指策略能迁移到不同形态。
遥操作Teleoperation人通过主臂/手柄/VR 实时控制机器人,同时记录数据。
协变量偏移Covariate Shift策略自己把状态分布推离训练分布的现象,是复合误差的根源。
行为克隆Behavior Cloning最朴素的模仿学习:监督学习 (o, a) 对。
DAggerDataset Aggregation让策略自己跑、人纠正、把纠正数据加回训练集,迭代。
流匹配Flow Matching学生成动作的速度场,几步就能出高质量样本,比扩散快。
VLAVision-Language-Action用预训练视觉语言模型做主干、接动作头的策略范式。
重定向Retargeting把一种本体的动作映射到另一种本体(如人手→二指夹爪)。
Episode—一次完整的任务尝试,从复位到结束。数据集的基本单位。
features—数据集的字段 schema:名字、dtype、shape。合并数据集前必须对齐。
stats—预计算的归一化统计量,存在 meta/stats.json。
RTCReal-Time Chunking在新动作块到达时与旧块平滑衔接的技术,解决推理慢于控制的问题。
APPENDIX C

三十天路径

A Month, Four Weeks

如果你打算系统学一遍,这份日程是按「每周 6—8 小时」排的。它的原则是:每一周都要产出一个可以被别人看到的东西,而不是攒到最后。

第一周 · 读懂

  • 读 README 与文档的 Installation / LeRobotDataset / Bring your own policies 三页。
  • 按 3.9 跑通零硬件路线,训一个 ACT,哪怕它什么都不会。
  • 打开 lerobot_dataset.py,把 __getitem__ 逐行读懂。产出:一篇笔记,画出数据从磁盘到张量的路径。

第二周 · 训练与评测

  • 换三种策略(ACT / Diffusion / SmolVLA)在同一份数据上训,记录显存、时长、成功率。
  • 在 LIBERO 上跑一次 lerobot-eval,理解「基准分数」的含义与局限。
  • 读 processor/ 下的 normalize 与 relative_action,搞清训练/推理的一致性怎么保证。产出:一张对比表,三种策略的代价与效果。

第三周 · 上手硬件

  • 组装、校准、遥操熟练(目标是连续十次成功)。
  • 采 30—50 条,训出第一个真机能用的策略。
  • 记录失败模式,针对性补采 20 条,重训。产出:一个能跑的策略 + 一份失败分析。

第四周 · 发布与参与

  • 把数据集与策略卡一起发到 Hub(4.2 的六项检查全过一遍)。
  • 给仓库提一个真正的小 PR:文档修正、测试补充、或一个插件。
  • 在 Discord 回答一个别人的问题。产出:一次公开提交 + 一次公开回答。
最后一句

这本书讲的所有东西,都可以浓缩成一句话:具身智能的瓶颈不在算法,在「有没有人愿意把手怎么干活这件事录下来」。LeRobot 把录、存、训、跑这四件事的门槛降到了一个人可以独立完成的水平。剩下的部分,取决于你手上有什么别人没有的场景。