基于LeRobot与ACT策略的SO-101机器人模仿学习实战指南
1. 项目概述与核心价值最近在机器人模仿学习圈子里LeRobot这个开源框架的热度持续攀升尤其是它针对SO-101这类桌面机械臂的友好支持让很多研究者和开发者找到了低成本、高效率的入门路径。今天我想和大家深入聊聊的就是如何利用LeRobot从零开始为SO-101机器人训练一个ACT策略。这不仅仅是跑通一个Demo更是理解如何将前沿的模仿学习算法落地到真实物理硬件上的完整过程。ACT即Action Chunking with Transformers是一种非常巧妙的策略架构它通过预测未来一小段时间内的动作序列动作块来让机器人的运动更加平滑和连贯这对于SO-101完成插拔、抓取等精细操作至关重要。如果你手头有一台SO-101机器人或者正在仿真环境中进行算法验证希望通过模仿学习来教会它一些新技能那么这篇文章就是为你准备的。我们将绕过那些过于理论化的部分直接切入实操从环境搭建、数据准备、模型训练到策略部署一步步拆解。你会发现即使没有庞大的计算集群利用LeRobot清晰的模块化设计和SO-101良好的仿真支持你也能在个人工作站上完成一个可用策略的训练。整个过程不仅会涉及代码和配置更会分享我在调试中遇到的那些“坑”和解决技巧这些是官方文档里不会写的实战经验。2. 核心思路与方案选型解析2.1 为什么选择ACT策略与LeRobot框架在开始动手之前我们得先搞清楚两个核心选择为什么是ACT策略又为什么是LeRobot框架这决定了我们整个项目的技术基调和实现路径。首先看ACT策略。传统的机器人策略通常是“单步”预测即根据当前观测比如图像、关节角度输出下一个瞬间的动作指令。这种方式在高速、动态环境中容易产生抖动和不连贯的运动。ACT策略的核心创新在于“动作分块预测”。简单来说模型不再只预测下一个动作而是像Transformer处理句子一样一次性预测未来一段时间例如0.5秒内的一系列动作构成一个“动作块”。这个块会被缓存在一个队列里机器人按顺序执行。这样做有几个显著优势第一运动轨迹自然平滑因为动作序列是在一个一致的上下文下规划出来的第二降低了实时推理的频率减轻了计算压力第三对于带有延迟的系统更加鲁棒。对于SO-101这类执行精细操作的机器人动作的平滑性和预见性至关重要ACT的策略范式非常匹配。再看LeRobot框架。它是一个由Meta AI开源、社区驱动的机器人学习库其设计哲学就是“让机器人学习像训练CV/NLP模型一样简单”。它针对像SO-101这样的桌面机械臂提供了开箱即用的支持包括标准化的数据加载与处理它定义了一套清晰的数据格式无论是你自行采集的真实数据还是仿真数据都能方便地接入。丰富的模型实现ACT、扩散策略Diffusion Policy等主流模仿学习算法都已集成无需自己从头实现复杂的网络结构。便捷的训练与评估流水线提供了训练脚本、日志记录、模型检查点保存和可视化工具大大降低了工程复杂度。仿真与实机部署桥梁LeRobot与PyBullet、MuJoCo等仿真环境以及ROS等机器人中间件有较好的集成设计方便从仿真验证过渡到真机测试。选择LeRobot意味着我们可以将精力集中在任务定义、数据质量和调参上而不是重复造轮子。特别是对于SO-101社区已经提供了相关的模型配置和示例起步速度会快很多。2.2 项目整体流程设计基于以上选型我们整个训练流程可以划分为四个清晰的阶段形成一个从虚拟到现实、从数据到智能的闭环环境搭建与仿真启动这是所有工作的基础。我们需要搭建一个能够模拟SO-101机器人及其工作环境的仿真世界。通常使用PyBullet因为它免费、轻量且与LeRobot集成良好。这一步要确保机器人的URDF模型正确加载关节控制接口畅通并且能渲染出用于策略观测的图像。示范数据采集与处理模仿学习数据为王。我们需要为SO-101录制演示数据。可以在仿真环境中用键盘、鼠标或者脚本“手把手”控制机器人完成目标任务比如将积木从一个位置移动到另一个位置。LeRobot会负责将这些操作记录成标准格式的数据集。ACT模型训练与调优利用上一步得到的数据集配置并启动ACT模型的训练。这个阶段的核心是理解模型的关键超参数如动作块长度、预测时域、网络结构等并通过训练损失和仿真测试来评估策略的学习效果。策略评估与部署应用训练好的模型需要在实际场景中验证。先在仿真环境中进行大量测试评估其成功率、鲁棒性。如果效果满意则可以进一步考虑部署到真实的SO-101硬件上运行这其中会涉及相机标定、坐标变换、实时推理优化等一系列工程问题。这个流程看似线性实则充满迭代。往往需要在数据采集和模型训练之间来回几次根据模型的表现来补充或修正演示数据。3. 环境搭建与核心依赖配置3.1 基础软件环境准备工欲善其事必先利其器。一个稳定、兼容的Python环境是后续所有工作的基石。我强烈建议使用Conda或Mamba来管理环境以避免不同项目间的依赖冲突。# 创建一个新的Python环境建议使用Python 3.9或3.10兼容性最好 conda create -n lerobot_so101 python3.9 -y conda activate lerobot_so101 # 安装PyTorch。请务必根据你的CUDA版本如果有GPU去PyTorch官网获取正确的安装命令。 # 例如对于CUDA 11.8 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装LeRobot核心库 pip install lerobot # 安装仿真环境支持。PyBullet是首选。 pip install pybullet # 安装额外的工具库用于数据可视化和处理 pip install matplotlib opencv-python ipywidgets注意PyTorch的版本与CUDA驱动版本的匹配至关重要。如果版本不匹配可能会导致无法使用GPU甚至运行时崩溃。使用nvidia-smi查看CUDA版本并严格对照PyTorch官网的安装指令。3.2 SO-101机器人仿真环境搭建LeRobot本身并不包含具体的机器人模型但它提供了加载标准URDF文件的接口。SO-101有时也被称为SO-Ar101或SO-arm101的模型文件通常需要从相关资源库获取。获取机器人模型你可以从SO-101机器人的官方网站、GitHub仓库例如so-robotics/so-arm101或LeRobot的示例资产中寻找其URDF文件通常是一个.urdf或.xacro文件。确保下载的模型包含准确的几何、惯性参数和关节限位。搭建简易仿真场景在PyBullet中我们不仅需要加载机器人还需要创建任务场景。例如一个简单的拾放任务可能需要一张桌子、一个目标方块和一个初始位置的方块。import pybullet as p import pybullet_data import time # 连接物理引擎 physicsClient p.connect(p.GUI) # 使用p.DIRECT则不显示图形界面用于后台训练 p.setAdditionalSearchPath(pybullet_data.getDataPath()) p.setGravity(0, 0, -9.8) # 加载地面 planeId p.loadURDF(plane.urdf) # 加载SO-101机器人假设URDF文件路径为 ./assets/so_arm101/urdf/so_arm101.urdf robotStartPos [0, 0, 0.5] robotStartOrientation p.getQuaternionFromEuler([0, 0, 0]) robotId p.loadURDF(./assets/so_arm101/urdf/so_arm101.urdf, robotStartPos, robotStartOrientation, useFixedBaseTrue) # 加载任务物体 - 一个红色方块作为操作目标 cubeStartPos [0.3, 0, 0.55] cubeId p.loadURDF(cube.urdf, cubeStartPos, globalScaling0.05) p.changeVisualShape(cubeId, -1, rgbaColor[1, 0, 0, 1]) # 改为红色 # 进入仿真循环 for i in range(10000): p.stepSimulation() time.sleep(1./240.)这段代码创建了一个包含地面、SO-101机器人和一个红色方块的仿真世界。p.GUI模式会打开可视化窗口方便我们观察。在实际训练的数据采集阶段我们可能更倾向于使用p.DIRECT模式以提升速度。3.3 LeRobot数据存储结构初始化LeRobot要求数据按照特定的目录结构进行组织。我们需要提前创建好这个结构用于存放后续采集的演示数据。# 在你的项目根目录下创建如下结构 mkdir -p ~/lerobot_data/so101_pick_and_place cd ~/lerobot_data/so101_pick_and_place mkdir -p train val # 在每个文件夹内LeRobot期望找到 observation action, episode_data 等子文件夹或文件。LeRobot使用一种基于HDF5或视频帧的数据格式。一种更简单的方式是直接使用LeRobot提供的DatasetAPI来自动化处理数据存储我们会在数据采集部分详细说明。4. 示范数据采集与预处理实战4.1 设计演示任务与采集脚本数据质量直接决定模型上限。对于SO-101的拾放任务我们需要设计清晰的任务流程并编写脚本记录机器人的状态和我们的控制指令。任务定义机器人末端执行器从初始位置移动至红色方块上方下移并闭合夹爪抓起方块将方块抬起并移动至目标位置例如桌面另一个标记点最后放下方块并打开夹爪。采集脚本关键组件观测Observation通常包括机器人本体的状态关节角度、末端位姿和外部感知如固定视角的RGB图像、深度图像。对于初学者可以先用关节角度和末端位姿作为观测。动作Action发送给机器人的控制指令。对于SO-101通常是末端执行器的相对位置增量delta x, y, z和夹爪的开合指令。也可以直接使用关节位置或速度控制。数据记录使用LeRobot的Episode和Dataset类来封装和保存数据。import numpy as np from lerobot.common.datasets.lerobot_dataset import LeRobotDataset from lerobot.common.datasets.utils import save_episode # 初始化一个空的数据集结构 # 这里我们定义观测空间和动作空间 info { fps: 10, # 控制频率与仿真步频可能不同 observation: { state: {shape: (7,), dtype: float32} # 例如6个关节角1个夹爪开合度 }, action: {shape: (4,), dtype: float32} # 例如dx, dy, dz, gripper_action } dataset LeRobotDataset(info, root~/lerobot_data/so101_pick_and_place/train) episode [] # 在仿真循环中每一帧或每N个仿真步执行以下操作 while task_not_finished: # 1. 获取当前观测 joint_states p.getJointStates(robotId, range(6)) # 假设前6个是旋转关节 joint_positions [state[0] for state in joint_states] gripper_open get_gripper_state() # 自定义函数获取夹爪状态 obs_state np.array(joint_positions [gripper_open], dtypenp.float32) # 2. 通过键盘、鼠标或算法生成当前动作 (action) # 例如根据目标计算末端执行器位置增量 target_pos calculate_target() current_pos get_ee_pos() delta_pos target_pos - current_pos gripper_cmd 1.0 if should_grasp else -1.0 action np.concatenate([delta_pos, [gripper_cmd]]).astype(np.float32) # 3. 将动作应用到仿真中 apply_action(robotId, action) # 4. 记录数据点 frame { observation: {state: obs_state}, action: action, # 可以记录其他信息如reward, done等但模仿学习通常不需要 } episode.append(frame) p.stepSimulation() # 一个episode一次任务尝试结束后保存数据 save_episode(dataset, episode, episode_index0)这个脚本勾勒出了数据采集的核心循环。在实际操作中calculate_target和apply_action函数需要根据你选择的控制接口位置控制、速度控制、力控来具体实现。一个关键技巧是采集数据时控制频率fps不宜过高。10-20Hz通常足够这既能保证动作序列的平滑又能减少数据冗余加快后续训练速度。4.2 数据质量检查与增强采集完一批数据后例如20-50次成功的任务演示不要急于开始训练先做数据质量检查。可视化回放编写一个脚本读取保存的数据并驱动仿真环境重新播放一遍机器人的动作。观察运动轨迹是否自然任务是否被正确完成。这是发现采集脚本bug如坐标轴错误、单位不一致的最直接方法。数据统计检查动作值的分布范围是否合理有无异常值NaN或极大/极小值。检查观测值是否在传感器合理量程内。数据增强可选但有效对于图像观测可以在训练时在线进行简单的增强如随机裁剪、颜色抖动以提升模型的泛化能力。对于状态观测可以添加轻微的高斯噪声。LeRobot的数据加载器通常支持这些增强操作。实操心得在早期我常常因为坐标系混乱世界坐标系、机器人基座标系、末端坐标系混用而导致采集的数据无效。我的建议是在仿真环境中明确画出各个坐标系的原点和轴向PyBullet可以用p.addUserDebugLine并在数据采集脚本的注释中清晰定义每个数据的坐标系。统一使用机器人基座标系或世界坐标系作为基准会减少很多麻烦。5. ACT模型训练详解与参数调优5.1 LeRobot中ACT模型配置解析LeRobot将模型配置、训练参数等都放在了统一的配置文件中通常是YAML格式。我们需要根据SO-101的任务特性来调整这些配置。一个简化版的核心配置项解读# config.yaml model: name: act # 指定使用ACT模型 # 网络结构参数 hidden_dim: 512 # Transformer隐藏层维度 num_encoder_layers: 6 # Encoder层数 num_decoder_layers: 6 # Decoder层数 nheads: 8 # 注意力头数 # ACT特有参数 chunk_size: 50 # 动作块长度预测未来多少步动作 action_horizon: 10 # 动作时域每次执行多少步后重新预测 observation_adapter: # 观测适配器将不同模态的观测编码为向量 state: type: mlp # 对状态观测使用多层感知机编码 input_dim: 7 # 输入维度对应我们的7维状态 output_dim: 128 # 编码后维度 action_adapter: type: mlp input_dim: 4 # 动作维度 output_dim: 128 dataset: name: so101_pick_and_place # 自定义数据集名 root: ~/lerobot_data/so101_pick_and_place split: train train: batch_size: 16 # 根据GPU内存调整 num_epochs: 200 learning_rate: 1e-4 lr_scheduler: cosine # 余弦退火学习率调度 save_every_n_epochs: 20 # 每20个epoch保存一次检查点 eval_every_n_epochs: 5 # 每5个epoch在验证集上评估一次关键参数深度解析chunk_size(动作块长度)这是ACT的灵魂参数。它定义了模型一次预测的未来动作序列长度。chunk_size * (1/fps)就等于模型“展望”的未来时间。例如chunk_size50,fps10则模型预测未来5秒的动作。设置太长预测难度剧增训练不稳定且推理时延迟大。设置太短动作块带来的平滑和预见性优势减弱。对于SO-101的拾放任务全程可能3-10秒设置为30-100是合理的起点需要根据任务时长调整。action_horizon(动作时域)定义了每次从预测的动作块中取出多少步来执行。执行完这action_horizon步后再根据新的观测预测下一个动作块。它必须小于等于chunk_size。较小的action_horizon如5-10意味着更频繁的重规划对动态环境适应性强但计算开销大。较大的action_horizon则更流畅但响应变慢。通常设置为chunk_size的1/5到1/2。hidden_dim,num_layers,nheads这些是Transformer架构的标准参数决定了模型的容量和表达能力。对于SO-101的7维状态4维动作任务相对简单不需要过大的模型。hidden_dim256或512num_layers4或6通常足够。模型越大拟合能力越强但也越容易过拟合训练更慢。5.2 启动训练与监控配置好YAML文件后可以使用LeRobot提供的训练脚本启动训练。# 假设配置文件为 config.yaml python -m lerobot.scripts.train --config-path ./config.yaml训练开始后监控以下指标至关重要训练损失Train Loss通常是预测动作与真实动作之间的均方误差MSE或平滑L1损失。观察其是否平稳下降并在后期趋于收敛。如果损失剧烈震荡或迟迟不降可能是学习率太高、批次大小不合适或数据有问题。验证损失Validation Loss在未参与训练的数据上计算的损失。用于检测过拟合。理想情况是训练损失和验证损失同步下降且最终差距不大。如果验证损失很早就停止下降甚至上升而训练损失持续下降就是过拟合的典型信号。仿真测试成功率这是最直接的性能指标。定期如每10个epoch在仿真环境中运行当前训练好的策略若干次如20次计算任务成功的比例。这个指标比损失函数更能反映策略的实际可用性。LeRobot通常集成了TensorBoard或WB等可视化工具。务必利用它们来绘制损失曲线和关键指标。避坑技巧训练初期损失可能非常高。一个常见原因是动作值的量纲不统一。例如末端位置增量单位米的值范围在[-0.01, 0.01]左右而夹爪指令可能是[-1, 1]。这会导致损失函数被大数值的维度主导。务必对动作数据进行标准化例如减去均值除以标准差让每个维度的数据分布接近标准正态分布。这能极大提升训练的稳定性和速度。可以在数据加载阶段通过LeRobot的Normalizer组件自动完成。5.3 模型调优策略如果第一次训练结果不理想可以按以下顺序进行排查和调优数据问题优先模型学不好十有八九是数据问题。回顾你的演示数据演示是否足够多样是否覆盖了任务可能遇到的各种初始状态是否有错误的演示失败案例尝试增加演示数据量50-100条优质演示通常是一个好的起点并确保数据质量。调整模型容量如果数据确信没问题但模型在训练集上损失都降不下来欠拟合可以尝试增大模型增加hidden_dim或num_layers。反之如果训练损失很低但验证损失高、成功率低过拟合则尝试减小模型或增加Dropout等正则化手段。优化超参数学习率是最敏感的超参数之一。尝试使用学习率预热warmup和余弦退火cosine decay策略。批次大小batch_size也会影响训练GPU内存允许的情况下适当增大批次大小通常能使训练更稳定。修改ACT核心参数重新审视chunk_size和action_horizon。对于一个快速抓取任务也许未来1秒chunk_size20 20Hz的预测就足够了。尝试不同的组合观察对任务成功率的影响。6. 策略评估、部署与问题排查6.1 仿真环境中的策略评估训练完成后我们得到一个模型检查点.ckpt或.pth文件。下一步是在仿真环境中系统性地评估它。编写一个评估脚本其核心逻辑如下# 加载训练好的模型 policy load_policy_from_checkpoint(path/to/checkpoint.ckpt, config) policy.eval() # 设置为评估模式 num_eval_episodes 50 success_count 0 for ep in range(num_eval_episodes): obs env.reset() # 重置仿真环境随机化物体初始位置如果做了随机化 done False episode_reward 0 while not done: # 模型预测动作。ACT模型需要维护一个动作队列。 with torch.no_grad(): action policy(obs) # 执行动作 obs, reward, done, info env.step(action) episode_reward reward if info.get(is_success, False): success_count 1 success_rate success_count / num_eval_episodes print(f评估成功率: {success_rate:.2%})评估时关键是要在与训练数据分布不同但合理的条件下测试。例如位置泛化随机化方块和目标点的位置而不是固定在训练时的位置。姿态扰动给机器人的初始关节角度添加微小噪声。视觉变化如果用了图像可以改变灯光、背景纹理。一个只在训练初始位置上成功的策略是没用的。良好的成功率例如85%是部署到真机的前提。6.2 从仿真到真实SO-101的部署考量将仿真中训练的策略部署到真实的SO-101机器人是“临门一脚”也是挑战最大的一步。这里存在著名的“仿真到现实”Sim2Real鸿沟。感知对齐如果你的策略使用了图像观测那么仿真中的虚拟相机和真实相机的内参、畸变、颜色、光照完全不同。解决方案包括域随机化在仿真训练时就随机化纹理、光照、相机参数等让模型学会忽略这些无关特征。域适应使用少量真实数据对模型进行微调。使用更鲁棒的观测对于拾放任务可以考虑使用机械臂的关节编码器结合外部运动捕捉系统如Vicon或RGB-D相机如Intel Realsense提供的精准3D位置信息作为观测这比纯RGB图像更容易跨域。动力学差异仿真中的物理参数质量、摩擦、电机响应与真实世界不符。这会导致相同的动作指令产生不同的运动效果。系统辨识尝试测量真实机器人的动力学参数并更新到仿真模型中。动作空间设计使用阻抗控制或力/力矩控制作为底层让策略输出期望的力而非直接的位置这对接触任务如抓取更鲁棒。在线自适应在真实机器人上运行时用一个快速的在线模型来实时修正动作。实时性保证真实系统对延迟敏感。确保你的推理循环从获取观测、模型预测到发送指令能在控制周期例如100Hz内完成。可能需要对模型进行优化如使用TensorRT、ONNX Runtime加速或模型剪枝、量化。一个务实的部署流程建议第一步在仿真中使用状态观测关节角、末端位姿训练一个高性能策略。第二步在真实机器人上通过手眼标定等方法获取目标物体在机器人基坐标系下的精确3D位置作为额外的观测输入。第三步将仿真训练的策略部署到真机但将其输出的动作末端位姿增量作为参考信号输入到一个高增益的PID位置控制器中。这样策略负责高层规划PID负责底层稳定跟踪可以一定程度上抵抗动力学误差。第四步收集真实机器人执行时的数据观测和成功/失败标签用这些数据对策略进行少量迭代的微调Fine-tuning。6.3 常见问题与排查技巧实录在实际操作中你一定会遇到各种各样的问题。下面是我总结的一些典型问题及其排查思路问题现象可能原因排查与解决思路训练损失为NaN或突然爆炸1. 学习率过高。2. 数据中存在异常值Inf/NaN。3. 网络梯度爆炸。1. 立即降低学习率如从1e-4降到1e-5。2. 仔细检查数据预处理步骤确保输入数据是归一化且有限的。3. 使用梯度裁剪torch.nn.utils.clip_grad_norm_。验证损失远高于训练损失策略表现差1. 严重过拟合。2. 训练集和验证集分布不一致。3. 验证集数据量太少或质量差。1. 增加训练数据量或使用数据增强、Dropout、权重衰减等正则化方法。2. 检查数据划分逻辑确保随机、均匀划分。3. 增加验证集大小并手动检查验证集数据是否正常。策略在仿真中抖动严重1.action_horizon设置过小重规划太频繁。2. 模型预测的动作噪声大。3. 底层控制器增益太高。1. 适当增大action_horizon让机器人更连贯地执行一个动作块。2. 在模型输出层后加入低通滤波器平滑动作序列。3. 仿真中降低PyBullet中位置控制器的增益。策略总是错过目标或抓取失败1. 演示数据不足或质量不高未覆盖足够的情况。2. 观测空间信息不足例如缺少物体相对位置。3. 动作空间设计不合理如移动速度太快。1. 针对性补充失败场景的演示数据数据增强。2. 在观测中显式加入目标物体相对于末端的位置信息。3. 对动作输出进行限幅或让策略预测速度而非位置增量。仿真运行正常真机完全失败1. Sim2Real鸿沟感知、动力学。2. 坐标系未对齐。3. 通信延迟或丢包。1. 参见上一节部署考量采用域随机化或状态观测。2. 仔细校准机器人基坐标系、世界坐标系和相机坐标系之间的变换关系。3. 检查通信链路确保控制指令以稳定频率送达。最后再分享一个小技巧在训练ACT模型时除了监控损失一定要定期可视化预测的动作块。将模型预测的未来几十步动作在图中画出来并与专家演示的真实动作块进行对比。这能给你非常直观的反馈模型是学会了连贯的动作规划还是只是在杂乱无章地预测这个习惯能帮你提前发现很多模型学习行为上的问题而不是等到评估阶段才看到失败的结果。