
先交代一下背景Hugging Face 这几年不只是把大模型和数据集放到 hub 上它还投了不少力气在机器人这个方向。LeRobot 就是其中我个人觉得特别值得读源码的一个项目。我最初进入这个仓库时第一时间统计了一下整个代码库有 803 个 Python 文件吓了我一跳心里想这种体量怎么可能啃得动。后来从一条真实 demo 流程走通采集、训练、评估、部署之后我才意识到 803 个文件里真正需要精读的核心文件可能不到 20 个剩下的大部分是测试、示例、工具脚本和维护性代码。这篇内容我不会逐行贴代码而是把我在阅读 LeRobot 源码时形成的整体脉络、关键模块职责、比较容易踩坑的地方和我的源码调试方法分享出来。如果你正打算用 LeRobot 做一个机械臂操控项目比如 SO-101、Aloha 这类硬件或者想搞懂“机器人学习系统的工程代码到底应该怎么组织”这篇文章应该能帮你省下至少一周的摸索时间。我也会从一个源码阅读者的角度而不是官方文档的角度告诉你 803 个文件到底是怎么支撑起机器人学习全流程的。1. 为什么 803 个文件不可怕先看懂整体目录设计源码量大的项目最忌讳一上来就从__init__.py开始线性阅读。LeRobot 的代码在我看来真正值得先建立画面感的反而不是某个算法文件而是仓库的目录分层与入口文件设计。理解了这两个点后面无论读策略模型还是读机械臂驱动都能快速找到位置。1.1 一大半代码是辅助性文件核心集中在 common 之外如果你在本地用 find 统计*.py往往会呈现一个很大的数字因为这个仓库包含了大量自动化测试脚本、文档 site 的示例代码、third-party 兼容代码、旧版本兼容目录甚至还有研究人员临时提交的 benchmark 脚本。我本地 clone 一份较新的主线代码后粗略做了一个分析真正被运行时的主流程 import 到的模块基本都围绕在lerobot/common这个子包下。早期版本里部分子模块直接放在lerobot/datasets、lerobot/robot、lerobot/policies这类路径后来代码演进中逐渐收拢为lerobot/common。这种重构背后的动机很明显控制机器人、收集数据、训练策略、评估策略这几条核心链路都要依赖数据格式和设备接口如果不把公共部分抽出来共享极容易出现“仿真里能跑、真机上动作乱飞”这种割裂情况。读一个像 LeRobot 这样的开源机器人项目时我建议你在本地 IDE 里把目录折叠放平先只盯着几个主入口文件看。所谓主入口就是用户会在命令行里手动敲出来的脚本。它们往往不包含复杂算法逻辑更像是一份“流程编排说明书”告诉你数据流转的起点和终点。LeRobot 的入口设计很直接大多数人只和以下这几类打交道数据采集与机械臂操作入口策略训练入口模型评估入口数据集可视化/检查入口先想清楚你要做什么再倒推去读对应入口就不会被 803 这个数字唬住了。即使某个功能在代码里牵涉到几十个模块入口文件也能帮你建立“调用关系的最小闭环”。1.2 入口文件如何帮你建立全流程地图我记忆中较新的 LeRobot 主流程中训练脚本会经过一个比较复杂的hydra配置体系把数据集、策略类型、训练步数、模型保存路径等都通过 yaml 配置传入。我第一次看的时候很不适应因为我要找的策略参数藏在lerobot/configs下的某个 yaml 里而不是直接放在代码里。后来我意识到这正是大型机器人项目的现实需求算法同学希望调参不用改代码硬件同学希望换机械臂不用动训练逻辑于是配置与代码分离成了必然选择。因此阅读 LeRobot 时不要把入口脚本中的dataclass配置类当成核心业务逻辑它更多是负责把 yaml 文件、命令行参数、默认值三者合并。真正决定一次实验跑成什么样的是配置对象和 policy 类的__init__方法。你可以把入口脚本看作是“装配机”它把数据集、配置、策略、设备连接起来然后按固定顺序调用其中的接口。另外值得一提的是LeRobot 很多脚本的命名已经很接近“动词式”语义比如train.py、eval.py、control_robot.py这一点比很多把逻辑藏在函数深处的开源项目更友好。我建议你 clone 代码后先在命令行跑一次python lerobot/scripts/train.py --help哪怕不真正训练。因为参数帮助里会列出数据集 id、策略类型、设备等参数比文档里的列表更直接。这个命令本身不会执行任何危险操作却能帮你确认你手里的版本具体使用哪种入口风格。1.3 模块职责划分从机器人设备到算法策略的清晰边界LeRobot 的目录设计我最欣赏的一点是它把“物理设备”“数据格式”“策略模型”三者的边界切得很清楚。你在读源码时会反复看到机械臂、摄像头、数据集、策略、环境这几个词被拆到不同包里机械臂与末端执行器相关负责连接真实硬件、读取关节状态、发送控制指令相机相关负责读取图像帧、格式转换、帧率控制数据集相关负责把多轮动作和图像保存成标准格式并在训练时按批次取出策略相关负责根据观察到的状态和图像生成动作环境相关负责仿真环境、奖励计算、回合控制如果没有这一层抽象一个 ACT 策略文件里很可能既要读串口数据又要写 hdf5 文件代码维护会是一场灾难。LeRobot 的做法是把“谁来获取 observation”“谁来消费 observation”解耦开。策略只关心输入observation.state、observation.images输出动作张量。至于这个 observation 是来自真实机械臂还是仿真环境LeRobot 的接口层会尽量统一。所以读源码时你最好带着一个坐标系思维先确认“你现在在哪一层”。是设备层数据层策略层还是编排层如果策略训练结果不好问题大概率在数据层或策略层如果连接机械臂后动不了问题大概率在设备层。这样可以快速缩小排查范围。2. 机器人学习全流程的四条主线数据、环境、策略与评测源码分析与普通文档阅读最大的区别在于源码里各个功能并不是按“用户操作顺序”排列的。LeRobot 的真实调用链路更像一张网数据采集过程会用到机械臂驱动与数据集写入模块训练过程会用到数据集读取与策略模块评估过程又会同时用到策略、环境和机械臂。我在里面读了一段时间后抽象出了四条主线理解了这四条线后你再看任何具体文件都会有位置感。2.1 主线一演示数据是如何被采集和保存的真实机器人学习最常见的工作流是先由人遥控机械臂完成大量演示记录关节角度和摄像头画面再去训练策略。LeRobot 把这一条链路封装得很完整你应该重点看机械臂和数据集写入两个模块。机械臂驱动层面的典型设计是一个 Robot 抽象类会暴露几个关键方法例如连接、断开、读取观察、发送动作。这些方法可能会被上层统一调用。不同机械臂的差异比如 SO-101 和 Aloha 的关节数量、控制频率、串口协议不同都被尽量限制在驱动层内部界面层只需面对一个相对统一的上层接口。图片很多新接触 LeRobot 的人不理解为什么数据集采集不仅是存状态和动作还要存多路摄像头图像。原因是 Transformer 类策略很重要的一个输入就是视觉观察。如果没有图像策略就退化成纯状态到动作的映射在真实操作中很难适应物体位置变化。因此你在源码里会看到不少与图像编码、时间戳同步、图像尺寸变换相关的逻辑。它们最终都会被整理成一个 episode 中多个时间步的样本集合。数据集写入模块的设计是整条数据主线的重点。LeRobot 默认采用一种比较规整的分块存储格式每个 episode 单独或合并保存并在元数据中记录动作和观察的维度、图像编码方式、统计信息等。这样做的好处是训练阶段可以按 episode 索引快速读取不用把整段视频一次性载入内存。我第一次读数据集代码时觉得很多逻辑是在处理文件命名、缓存、数据校验似乎没有算法含量。后来才发现这类代码恰恰最能影响训练效果。如果你的数据保存时把关节速度单位记错策略学的动作再漂亮也没法在真机上执行。2.2 主线二仿真环境与真实环境的统一抽象LeRobot 在设计上支持一套 Gym 风格的环境接口方便你跑仿真实验或者用仿真评估策略。仿真环境内部会维护状态转换、奖励计算、回合结束判断。它的接口和真实机械臂的差异让人头疼于是 LeRobot 把“环境”这一层单独抽出来。这个抽象深究起来十分关键。比如你训练一个策略在仿真环境里可能知道当前关节角也能立刻用step(action)推进环境。而在真机上你需要“读取关节角度”和“下发动作指令”两个动作甚至要考虑电机响应时间。LeRobot 在仿真侧把控制指令包进环境 step 的 action 参数里在真实侧则通过机械臂的 send 方法发出命令。策略文件本身不需要关心它是在和仿真器说话还是在和串口说话。那为什么很多项目最终还是会分开训练和真机部署的脚本因为在真实机器人上你还需要处理安全停止、超时保护、机械臂限位等仿真中不存在的问题。这就是抽象不能过度的地方。在 LeRobot 里仿真环境主要服务于两个场景一是数据不足时的预训练验证二是模型训练完成后做自动评估比如统计连续数十次任务的成功率。真机部署仍然必须单独做一层安全逻辑。2.3 主线三策略模型是如何被训练出来的LeRobot 里的策略模型目录包含多种主流方案比如动作分块 TransformerACT、扩散策略、VQ-BeT 等。如果你是想快速上手建议先不要同时读这么多策略的数学细节而是先选一种与你目标硬件匹配的方案跑通训练比如 ACT 在 SO-101 上有很多现成 demo社区资料也最丰富。从源码角度一个策略模块通常至少包含两个类配置类和主模型类。配置类负责把用户可调参数集中起来比如 Transformer 层数、特征维度、dropout、学习率等。主模型类负责前向推理输入通常是归一化后的 observation输出是归一化后的 action 或动作分布。策略代码里最容易绕晕的是中间维度的变化你从零开始读时最好拿一个具体 batch 样本实际算一遍。我曾经为了理解 ACT 的 chunk 机制直接用 debugger 在模型前向里打印每个张量的 shape看完后才明白它本质上是对未来多个时间步的动作做一次性预测而不是像传统 RL 那样单步决策。训练脚本的源码反而比策略模型更容易看懂因为它主要做几件事加载数据集、创建 dataloader、初始化策略和优化器、设置梯度累积步数、每隔多少步保存 checkpoint。LeRobot 在训练时大量复用了 PyTorch 生态的组件包括 accelerator 库处理多卡和混合精度。读训练脚本时最需要关注的是它用什么方式把每个 batch 从数据集搬进模型以及模型保存时是否把归一化统计量一起保存。这直接关系到你后面加载模型时能不能正确反归一化。2.4 主线四模型评估、可视化与部署闭环很多开源项目只做到模型训练完就结束了LeRobot 非常重视评估和可视化闭环。评估脚本会加载之前保存的 checkpoint在仿真环境或真实机械臂上执行动作并记录成功率或平均回报。这个过程中还会把摄像头画面或机械臂运动过程录制成视频方便你查看策略表现。我看评估脚本源码时的一个收获是它并不总是从每个 episode 开头重新开始。因为真实机器人操作任务常带有随机初始状态评估时需要在每个回合重置环境或人为摆放物体否则结果会不可靠。LeRobot 通过一个 episode 长度上限和回合步数上限来控制评估时间避免单次评估无限循环卡死。如果你在部署时发现机械臂动作序列异常很大概率不是策略问题而是训练数据与部署时的观察空间不一致。例如训练时用了左摄像头图像评估脚本默认读取右摄像头图像那表现一定崩溃。这种不一致性在源码中尤其容易排查你就查 observation 的关键字。部署层面LeRobot 并不会在训练脚本里直接把动作实时发送给机械臂而是会走另一个结合真实 robot 驱动和策略推理的脚本。这里你需要关注策略是在 CPU 还是 GPU 上推理。因为机器人控制频率很高如果推理一次动作要几百毫秒机械臂运动看起来就会一顿一顿。我自己实践中通常会用较薄的动作分块一次性输出若干步降低推理频率同时保证运动平滑。3. 源码阅读里躲不开的核心机制观察空间、归一化与动作接口这一节我想单独抽出来写因为我在阅读 LeRobot 时发现不少困惑其实不是算法问题而是数据接口理解不到位。如果你已经能定位到策略模型文件和训练脚本但模型输出的动作行为异常极大可能是观察与动作的“单位”和“定义”没对齐。3.1 observation 与 action 到底长什么样LeRobot 的特征命名高度统一一般会看到observation.state、observation.images.cam_name、action等字段。observation.state通常是机械臂的关节角度向量、关节速度向量以及末端夹爪开合状态的拼接具体维度取决于机械臂自由度。例如 SO-101 这种六轴可加夹爪的机械臂原始状态维度也不高但是图像维度可能很大比如多路摄像头各 224x224。我一开始犯过一个错误以为动作的维度必须与 observation.state 的维度完全一致。实际上 LeRobot 中行动可能表示成末端执行器的位姿增量也可能是机械臂关节角度增量还可能包含夹爪开合量。尤其在做真机数据采集时不同遥控方式产生的 action 含义会不同所以源码里会有一个“控制模式”的概念例如使用绝对关节角度还是相对增量。在训练与部署两边必须保持一致。如果我在源码中看到一个actiontensor 维度比observation.state高我不会立刻认为是 bug而是会往前翻“这个动作空间是怎么定义的”。比如 Aloha 这种双手双夹爪系统状态空间可能同时包含两个手臂的关节量和夹爪量一旦你漏看了后缀字段后面所有维度推算都会错位。3.2 归一化与反归一化的实现位置许多策略模型在输入 observation 前会做归一化在输出 action 后又会做反归一化。LeRobot 把这个逻辑可能分散在数据集类、policy 内部或训练脚本中。你需要在源码里搜索normalize、unnormalize关键字搞清楚是谁在哪个阶段调用。归一化用的统计量通常来自训练数据集比如每个维度的均值、标准差、最小值、最大值。LeRobot 在数据集预处理或保存时会把统计信息固化下来加载模型后也要用同一套统计量还原动作。如果训练数据里某个关节角几乎没有变化它的方差很小归一化后数值可能不稳定源码里一般会用一个很小的 epsilon 防止除零。建议你调试策略输出时先不要直接丢给机械臂而是打印反归一化前后几个动作张量人工确认一下数值范围是否合理。机械臂关节角通常在正负 180 度以内如果模型输出的关节角反归一化后是几千度那不是训练没收敛就是你用的统计量与训练时不匹配。这种问题我在源码阅读过程中至少遇到过两次定位到归一化模块后发现根本原因是 checkpoint 与新的数据集 stats 对不上。3.3 策略模型与 Hugging Face 风格封装的结合LeRobot 很多策略会借鉴 Hugging Face Transformers 的模型加载方式比如保存模型权重成 safetensors 格式使用from_pretrained方法加载预训练权重。这种设计的一大好处是你可以在 hub 上分享、复用已经训练好的策略而不需要附带完整源码。源码层面则通过配置类和模型类的映射把字符串类型的策略名解析成具体的 Python 类。真正深入策略代码后你会发现它的网络结构并不一定特别复杂。ACT 方案主要是 CNN 视觉编码器加 Transformer 编码器/解码器扩散策略则用类似残差 U-Net 的结构对噪声向量迭代去噪。理解这些算法的重点不是重复贴公式而是搞清模型输入张量的 shape、每个时间步的 token 组织方式。比如 Transformer 类策略往往会把“batch 维”和“时间维”重新组织把当前时刻的状态和视觉特征拼接成一个 token 序列。LeRobot 的代码读起来绕很多时候就绕在这些 reshape 操作上。3.4 数据加载器与训练循环的性能细节LeRobot 训练任务里图片解码往往是性能瓶颈。如果数据文件里每帧图片都是 jpeg 压缩存储训练时会在 CPU 侧解压到 RGB 再做增强处理不好时 GPU 空闲率极高。源码中的 dataloader 通常会配置多个 worker 增加并行度还可能通过缓存机制减少重复读取。我建议你读训练脚本时重点关注 dataloader 的参数比如批量大小、每个 epoch 的样本抽取方式、是否使用pin_memory、是否打乱 episodes 但保持时间步顺序。这里的逻辑直接影响训练速度和策略是否过拟合到某条固定演示。如果你把同一个 episode 中的连续时间步同时放进一个 batch模型很容易“背下来”当前 episode泛化能力反而不行。LeRobot 的采样策略做了很多防泄露设计读的时候可以仔细体会这一点。4. 实操记录自定义机器人时我具体改了哪些位置这一节不是什么通用教程而是我个人的踩坑记录。我想通过一个“把 LeRobot 适配到另一台机械臂”的场景告诉你源码中真正需要改的地方其实很集中。很多人知道 LeRobot 支持 SO-101但要换到其他硬件时就不知道该从哪里下手其实这正是一个读懂源码架构的好练习。4.1 换机械臂时最需要关注的遥控与动作下发路径换机械臂的第一步不是改策略而是先跑通“人工遥控采集”。LeRobot 里通常有一套控制脚本允许你进入teleoperate模式。这时人通过主臂或游戏手柄控制从臂运动系统记录状态。如果你换的机械臂和 SO-101 的通信协议不一样你需要重新实现读取状态与发送目标位置这两个接口。源码中机械臂驱动的边界设计值得参考它的核心方法大概率包含获取当前关节角度、把目标关节角度写入设备。你要观察原有实现里是否包含“位置增量”或“速度控制”模式因为这会影响你记录 action 的方式。通信频率和超时处理也要在这个层面配置真机上如果串口断连上层往往只能看到 NaN 或异常。我曾为节省时间跳过这个步骤直接把旧机械臂的数据拿来训练结果动作在硬件上完全无法执行因为新旧机器人的关节顺序都不一样。后来我耐心阅读机械臂配置把所有关节编号和方向重新对齐才算跑通。所以源码阅读里最基础的关节映射关系比任何高级算法都重要。4.2 动作过滤与安全限位相关逻辑放在设备层还是策略层真实机械臂往往不能直接执行策略输出的原始动作尤其是策略生成的 delta 动作比较毛糙直接发送会给电机带来很大冲击。这个问题在源码阅读时经常被忽略因为很多人只看策略输出。我的处理原则是安全限位放设备层平滑过滤可以放在设备层或策略后处理层但绝不能放在数据采集层。否则你记录的真实演示就不是人的真实意图策略会学到一套被过滤过的动作分布。LeRobot 对这个问题提供了一些可配置的控制选项比如关节速度限制、最大动作变化量、夹爪开闭阈值等。这些配置不一定在深度学习模型里而可能在机械臂设备配置对象中。读源码时如果发现训练完的模型在真机动作抖动严重先检查这些限幅配置是否合理。4.3 修改训练配置时我的最小验证清单我经历过很长时间的“改配置-训练-失败-再改配置”循环。后来我给自己定了一个最小验证清单每次换机器人或换策略时都会先跑一遍先构造一个包含几条 episode 的最小数据集确认数据集能正常加载且 shape 正确。用一个很小的训练步数跑通训练例如一两个 batch 不 crash。在 eval 阶段用同一份配置加载 checkpoint看模型能输出动作而不报错。把模型动作打印出来做简单人工判断确认方向和单位与演示一致。LeRobot 源码中有不少组件可以帮助你完成这个流程比如数据集检查工具、可视化脚本。这些工具虽然不直接提升算法指标但对工程落地价值很大。我在测试时发现很多 bug 根本不需要看模型训练收敛情况只要跑最小验证就能暴露出来。4.4 一个印象深刻的踩坑图像时间戳与动作不同步真实机器人数据采集中图像的获取频率与关节角度读取频率往往不一致。比如关节角度可以 100Hz 读取但摄像头可能只有 30Hz。LeRobot 源码会处理这种时间对齐比如记录每个状态的时间戳在保存样本前重新采样。我实际调试时遇到的情况是动作序列看起来合理但机械臂执行时总是慢半拍。最后查看源码和数据集可视化发现录制时图像帧与关节状态对应关系偏了一个时间窗口相当于每一帧画面都滞后于实际手臂位置。这种错误在纯仿真训练中很难发现因为仿真环境的状态更新是同步的。真实机器人项目里时间同步机制往往比策略网络本身更影响效果。这也解释了为什么 LeRobot 在引入新硬件时不只是简单增加一个设备驱动类还需要配套测试数据的同步逻辑。如果你只是硬改驱动而忽略时间戳对齐最终训练效果不会理想。5. 从 803 个文件里提纯源码阅读路线的实用建议如果你已经读到这里说明你确实想从源码层面理解 LeRobot而不是只跑通一个 demo。最后这部分我分享一些很具体的源码阅读工具和排查方法希望能帮你少走弯路。5.1 用核心路径清单代替线性阅读与其按目录顺序读代码我建议你先把以下核心路径画出来然后沿着路径逐一看实现数据采集路径入口脚本 → 机械臂驱动类 → 相机驱动 → 数据集类 → 存储格式训练路径入口脚本 → 数据集读取 → policy 类 → 网络模块 → 优化器与 dataloader评估/部署路径入口脚本 → 模型加载 → 环境/机械臂 → 动作后处理 → 记录结果每条路径中的文件数量大约只有 510 个核心源码总量并不可怕。803 这个数字只是在提醒你这是一个跨硬件、跨策略的成熟项目而不是一个单算法 demo。为了让你快速定位我整理了一个我在阅读过程中常参考的模块职责速查表注意不同版本结构可能略有差异但职责基本一致源码区域大致位置主要职责重要程度机器人设备与驱动连接机械臂/相机、读取观察、发送动作高数据集读写与统计存储/加载 episodes、计算归一化参数高环境接口仿真状态更新、奖励、回合控制高仿真场景策略实现ACT/Diffusion/VQ-BeT 等网络结构和前向逻辑高训练脚本数据加载、优化、checkpoint 保存中评估脚本加载模型、执行动作、记录指标中配置系统统一配置与命令行参数中可视化工具展示 episode 回放、动作曲线低低重要程度不代表可以忽略。只是说你不必一开始就读。等到需要检查数据质量或调参时再回头使用这些工具即可。5.2 源码阅读常用的三个调试技巧读 LeRobot 这类复杂项目时我比较推荐的调试方法有三个。第一个是在关键函数里临时加 shape 打印尤其是策略前向和数据集__getitem__。机器人学习的数据流像一个多级管道任何一个环节 shape 定义不对模型不会报错但结果会诡异。打印 shape 是最快定位手段。第二个是利用极小的参数量和步数做 smoke test。不要一上来就跑完整训练拿 12 个 episode、1 个 batch、10 步训练循环跑通后再逐步放大。LeRobot 代码库结构复杂但大部分崩溃都能在 smoke test 阶段暴露。我每次改动机械臂驱动或数据集字段后都会执行一次这种冒烟测试。第三个是调用栈分析。训练报错时不要只看最后一行要学会从上往下看调用栈中的文件层级。如果错误在 Transformer 模型内部问题可能来自观察张量尺寸不符合预训练参数如果错误在数据加载阶段则优先检查字段是否存在或者类型是否匹配。调用栈里的文件路径本身就是在告诉你属于上文的哪条主线。5.3 我总结的源码问题排查顺序当你遇到一个具体 bug比如“训练 loss 不下降”或“机械臂执行动作异常”我建议按以下顺序排查而不是东一榔头西一棒子。先确认数据观察与动作定义是否正确再确认归一化统计量是否匹配再确认策略输入输出的是绝对动作还是增量动作接着确认部署端机械臂控制是否执行了反归一化最后才考虑网络结构或超参数问题。很多看似训练不收敛的问题最后都能归因到前几项。这个顺序在 LeRobot 项目里适用度非常高。我在调 SO-101 机械臂时踩过最典型的一个坑就是训练用的图像分辨率与部署时的实际分辨率不一致。从源码看采集数据时如果存成 224x224训练时也按 224x224 处理模型看起来一切正常但一旦某条部署链路里把原始图像 resize 成 128 或其他尺寸视觉特征分布会发生偏移。接口并没有强制检查分辨率于是表现为“仿真成功率很高真机一动就废”。遇到这类问题时检查图像预处理代码比改模型更有效。5.4 关于“803”这个数字的最终体会LeRobot 是我见过将深度学习模型、机械臂硬件控制、数据工程和在线评估结合得比较全面的开源项目之一。803 个 Python 文件支撑机器人学习全流程本质上是多种技术栈的协作而不是某个核心算法非常庞大。从目录结构中提炼出数据线、机器人线、策略线、评测线后你会发现 LeRobot 的真实复杂度是被良好的抽象吸收掉了。它真正的设计核心是把不具备学习能力的机械臂驱动、具备学习能力的神经网络以及适合训练的数据存储格式组合成一个完整闭环。我个人在实际操作中的建议是第一次读源码时不要试图理解全部文件也不要先从 ACT 等模型的多头注意力机制开始。先把数据从哪里来、动作到哪里去这条物理链路跑通再逐渐进入网络内部。机器人学习与纯 NLP 或 CV 任务有一个显著区别模型只负责从观察到动作的映射但观察和动作的质量受硬件、时间戳、限位、噪声影响极大。当你能把 LeRobot 的源码当作一个“数据集工厂 模型工厂 硬件接口”的组合体时803 这个数字也就不会再成为阅读障碍了。