很多朋友在接触Lerobot的时候,第一反应是“这套东西是不是只能搭配官方指定的那几款机器人方案”。我这次专门尝试了用飞特(Feetech)舵机从零拼一台机械臂,配合Hugging Face开源的Lerobot框架来驱动。整个流程走下来,最大的感受是:Lerobot并不是某个硬件的专属框架,只要舵机支持串行总线控制、能通过串口收发指令,放到Lerobot里调度是完全可行的。
这篇文章不是概念科普,而是把我实际搭建过程中踩过的坑、调过的参数、换过的接线方式全部整理出来。从硬件选型到软件配置,再到机械臂标定和数据采集,按照我实际操作的时间线来写。想要用手头舵机组一台能跑模仿学习任务的机械臂、又不想买整套一体化方案的朋友,这篇应该能帮你省下至少两天的摸索时间。
1. 项目整体思路:Lerobot加飞特舵机到底怎么搭
1.1 Lerobot是什么,它能干什么
Lerobot是Hugging Face团队维护的一个开源机器人学习框架,核心目标是让机器人通过模仿学习(Imitation Learning)和强化学习(Reinforcement Learning)来完成各种操作任务。它跟传统那种“写死运动轨迹”的机械臂控制方式不一样,更强调让模型从演示数据中学习策略,然后在新环境下复现动作。
Lerobot的整个工作流可以拆成四步:
- 采集演示数据(人手动拖着机械臂演示动作,或者通过遥控器操作,系统记录每个时刻的关节角度和图像)。
- 训练策略模型(使用采集到的数据训练一个神经网络,比如ACT算法)。
- 部署推理(将训练好的模型加载到机器人上,输入当前观测,输出动作指令)。
- 评估迭代(观察模型执行效果,补充更多演示数据,继续优化)。
这个框架的优势在于,它把数据采集、模型训练、策略部署的链路都打通了,你不用自己拼接一堆零零散散的脚本。对于个人开发者来说,等于拿到了一套完整的机器人学习基础设施。
1.2 为什么选飞特舵机而不是伺服电机或步进电机
很多机械臂项目用的是步进电机或闭环伺服电机,但Lerobot官方推荐的SO-100、SO-101方案里用的其实就是串行总线舵机。这类舵机的特点是控制简单——一根串行总线上挂多个舵机,每个舵机分配独立ID,通过串口总线发送角度指令就能驱动,不需要像步进电机那样额外配置驱动器、编码器等外围电路。
飞特舵机在国内采购方便、价格合理,像我用的STS3215,15kg·cm扭矩,做小型教学机械臂完全够用。更重要的是,飞特的串行总线协议是公开的,有Python调用示例,方便跟Lerobot这类框架对接。
1.3 整套系统的逻辑架构
一套完整的Lerobot加飞特舵机机械臂,从底层到上层分成这几层:
- 机械本体:舵机、结构件、末端执行器(夹爪或吸盘)。
- 串行总线层:所有舵机通过一根信号线串联,接在USB转串口适配器上。
- 系统驱动层:Ubuntu系统中识别串口设备,通过pyserial或自定义串口程序与舵机通信。
- 机器人控制层:Lerobot框架中的robot模块,负责位置指令下发和状态读取。
- 学习算法层:Lerobot实现的数据采集、模型训练和策略部署代码。
这条链路最核心的点在于:Lerobot通过robot实例与舵机通信,而robot实例底层只需要你实现send_action和read_observations两个核心方法。所以只要你的飞特舵机能够支持角度读取和角度控制,接进来就是水到渠成的事。
2. 硬件准备:飞特舵机机械臂的材料清单与选型分析
2.1 舵机选型:STS3215是起步首选
飞特舵机旗下有很多系列,我这次用的是STS3215,属于串行总线舵机,扭矩15kg·cm,工作电压5V到7.4V。这个扭矩做小型5自由度或6自由度机械臂足够,而且它支持位置反馈,方便Lerobot读取当前关节角度作为观测值。
如果你是做一个简单的入门方案,也可以用SCS0009这类小舵机,但扭矩太小,带不动稍长的机械臂结构件,很容易出现抖动、堵转。建议至少选10kg·cm以上的舵机,否则后续加装夹爪或者传感器时会非常痛苦。
2.2 控制板与USB转串口方案
飞特舵机的串行总线物理层是半双工模式,需要用专门适配器连接电脑USB口。常见的方案有三种:
- 飞特官方USB串行舵机调试板:插上即用,稳定,官方提供了调试软件,适合第一次接触的人。
- 兼容FTDI芯片的USB转串口模块加电平转换电路:需要自己接线,成本低,但焊接工作量多一些。
- Arduino Mega加转接板:适合后续要做嵌入式扩展的,但Lerobot环境下直接用串口通信反而多了一层中间环节,不建议初期使用。
我实际操作下来,推荐方案一或方案二。如果用方案二,注意供电一定要独立,USB转串口模块的3.3V或5V电流太小,不足以驱动舵机,舵机电源必须单独接稳压模块。
2.3 供电方案:最容易出错的地方
串行总线舵机在运动瞬间电流很大,STS3215锁机电流能到2A以上。要是一个6轴机械臂同时发力,瞬间电流可能冲到10A以上。
我建议如下供电方案:
- 使用7.4V 2S锂电池或5A以上的稳压电源模块直接给舵机总线供电。
- 控制逻辑(USB转串口板)和舵机电源之间做好共地连接,避免信号电平不一致。
- 不要用电脑USB口的5V去驱动任何舵机。
共地这一步很多人遗漏,结果是舵机偶尔能控制、偶尔失灵,信号乱跳。逻辑上是舵机电源的地和USB转接板的地必须接在一起,这样才能保证信号线有统一参考电位。
2.4 机械结构与结构件选择
机械臂本体有两种打法:
第一种是买成品的舵机机械臂支架,常见的有六轴铝合金支架、3D打印支架,孔位和舵机尺寸匹配,省去自己设计的麻烦。这类支架要注意确认它适配的舵机安装孔距是标准25T舵盘,STS3215通用。
第二种是完全自己3D打印结构件。Lerobot官方仓库里有一些开源机械臂模型可以参考,比如SO-100系列的STL文件,但那是针对特定舵机的。如果要用飞特舵机,建议在Fusion 360里自己改一下安装孔位。
我自己是先用3D打印做了两版试结构,确认尺寸无误后才换到铝合金支架。如果是刚开始做,先用便宜的打印件验证方案,再决定要不要投入金属件,成本能省不少。
3. 软件环境搭建:Ubuntu与Lerobot框架安装
3.1 系统准备与基础依赖
Lerobot是在Linux环境下开发的,Windows下跑起来会有很多依赖问题。这里直接用Ubuntu 20.04或22.04是最省心的。我自己用的是Ubuntu 22.04,实测没问题。
系统装好后,先把基础工具链准备好:
sudo apt update sudo apt install -y git python3-pip python3-venv然后用虚拟环境隔离依赖,避免污染系统Python环境。
cd ~ git clone https://github.com/huggingface/lerobot.git cd lerobot python3 -m venv .venv source .venv/bin/activate pip install -e .这里有个细节:Lerobot的依赖中包括PyTorch,默认安装会拉取CPU版本。如果你的电脑带NVIDIA显卡,想加速训练,需要提前安装CUDA对应版本的PyTorch,再执行上述安装指令。
3.2 安装飞特舵机串口通信依赖
飞特舵机的通信依赖于pyserial,这个库在Lerobot环境中可能已经作为依赖装好了,为了保险可以单独确认一下:
pip install pyserial然后插上USB转串口适配器,用ls /dev/ttyUSB*查看设备是否被识别。这里要注意,新系统下普通用户默认没有访问串口设备的权限,需要把当前用户加入dialout组:
sudo usermod -aG dialout $USER执行完注销重新登录一次,权限才能生效。这一步不做好,后面运行Lerobot时大概率会报Permission denied。
3.3 验证串口通信是否正常
在正式启动Lerobot之前,先用Python写一个最简单的串口扫描程序,确认能跟舵机建立通信。这一步可以帮你提前排除硬件接线问题:
import serial import time ser = serial.Serial('/dev/ttyUSB0', 1000000, timeout=0.1) # 飞特舵机PING指令:FF FF ID LEN CMD CHECKSUM # 以ID 1为例,指令包为 FF FF 01 02 01 03 cmd = bytes([0xFF, 0xFF, 0x01, 0x02, 0x01, 0x03]) ser.write(cmd) time.sleep(0.1) resp = ser.read(10) print(resp.hex())如果返回非空内容,说明舵机正在响应。如果返回空字节,优先检查接线和舵机ID,不要急着怀疑代码。飞特舵机在出厂的默认ID一般是1,如果你接了多个舵机且都保持ID 1,总线上会出现冲突响应不稳定的问题,这也是后面要处理好的点。
4. 舵机侧配置:ID分配与运动控制基础
4.1 舵机ID分配:多个舵机如何区分
机械臂上多个舵机需要有不同的ID,否则没法独立控制。飞特舵机提供两种改ID的方法:
第一种是用飞特官方的串口调试软件,Windows下打开软件,连接USB转串口板,选中舵机后修改ID。这个方式直观,适合只有一个舵机的情况。
第二种是直接用Python脚本发送写指令,适合批量修改。飞特的指令格式中,操作寄存器地址为5的字节可以修改ID:
import serial def write_register(ser, servo_id, addr, data): # FF FF ID LEN CMD ADDR DATA CHECKSUM cmd = bytes([0xFF, 0xFF, servo_id, 0x04, 0x03, addr, data]) checksum = (~sum(cmd[2:]) & 0xFF) cmd += bytes([checksum]) ser.write(cmd) ser = serial.Serial('/dev/ttyUSB0', 1000000, timeout=0.1) # 将ID为1的舵机改成ID 3 write_register(ser, 1, 5, 3)改完ID后,最好重新上电,再用PING包确认每个ID的响应是否正常。
4.2 角度控制模式与位置指令
飞特串行总线舵机的位置控制范围一般在0度到360度之间,具体由角度值和舵机内部的位置限位寄存器决定。STS3215的角度范围默认是0到4095,对应0到360度。Lerobot内部使用的通常是弧度制或表示为归一化的角度值,因此在实际控制前要做换算。
以发送到舵机位置1为例,角度值转位置值的公式为:
position_raw = angle_deg / 360 * 4095例如想让舵机转到90度,则目标位置原始值为90 / 360 * 4095 = 1023.75,取整后发送1024。
用Python发送位置控制指令的写法如下:
def send_position(ser, servo_id, position_raw, speed=500): # FF FF ID LEN CMD P1H P1L SPH SPL CK len_ = 7 cmd = bytes([0xFF, 0xFF, servo_id, len_, 0x03]) cmd += bytes([position_raw & 0xFF, (position_raw >> 8) & 0xFF]) cmd += bytes([speed & 0xFF, (speed >> 8) & 0xFF]) checksum = (~sum(cmd[2:]) & 0xFF) cmd += bytes([checksum]) ser.write(cmd)如果你只是想让机械臂先动起来,不需要纠结协议里每个字节的含义,但这个基础必须打牢,否则后面Lerobot里遇到莫名其妙的错位会很头大。
4.3 读取当前位置与状态反馈
Lerobot在数据采集时,需要读取每个舵机当前的角度作为观测值。飞特舵机也支持读取位置:
def read_position(ser, servo_id): # FF FF ID 04 02 38 04 CK cmd = bytes([0xFF, 0xFF, servo_id, 0x04, 0x02, 0x38, 0x04]) checksum = (~sum(cmd[2:]) & 0xFF) cmd += bytes([checksum]) ser.write(cmd) time.sleep(0.02) resp = ser.read(20) # 返回数据中第7、8字节是位置低字节和高字节 if len(resp) >= 8: pos = resp[6] | (resp[7] << 8) return pos return None注意半双工串口的机制是发送指令后必须切换方向才能收到数据,这个切换在硬件层由USB转接板自动完成,但在代码层面要保证发送和接收之间有足够的时间间隔,不能连续快速写指令。
5. 配置Lerobot驱动飞特舵机机械臂
5.1 创建属于你的Robot类
Lerobot内置了一些机器人类型,比如SO-100、SO-101、Aloha等。要让飞特舵机接入Lerobot,最直接的方式是继承Robot基类,实现自己的控制逻辑。
在Lerobot代码中新建文件my_feetech_robot.py,大概框架如下:
from lerobot.robot.robot import Robot class MyFeetechRobot(Robot): def __init__(self, config): super().__init__(config) self.ser = serial.Serial(config.serial_port, 1000000, timeout=0.1) self.servo_ids = config.servo_ids def read_observations(self): positions = [] for servo_id in self.servo_ids: pos = read_position(self.ser, servo_id) positions.append(pos / 4095.0) return {"joint_positions": np.array(positions)} def send_action(self, action): for i, servo_id in enumerate(self.servo_ids): pos_raw = int(action[i] * 4095) send_position(self.ser, servo_id, pos_raw)action是一个归一化到0到1之间的向量,表示每个关节的目标位置比例。这样Lerobot的算法输出就可以直接映射为机械臂控制指令。
5.2 编写机械臂配置文件
在Lerobot仓库中,机器人配置一般用Python字典或yaml定义。我单独建了一个配置:
my_feetech_config = { "serial_port": "/dev/ttyUSB0", "servo_ids": [1, 2, 3, 4, 5, 6], "joint_names": ["waist", "shoulder", "elbow", "wrist_pitch", "wrist_roll", "gripper"], "fps": 30, }这里joint_names对应你机械臂的实际关节顺序,后面训练模型时用到的关节索引就依赖这个顺序,一定要跟舵机安装顺序保持一致。
5.3 零点标定与机械限位
零点标定是最影响使用体验的一步。舵机的零点位置不一定跟机械臂的“好看姿态”重合。我的做法是:
- 将机械臂调整到一个自然姿态,比如所有关节都接近垂直或水平。
- 逐个舵机断电,手动掰到目标零点角度,记住此时舵机的实际转动角度。
- 通过修改舵机中位偏移量(飞特舵机有角度偏移寄存器,地址为20和21附近),把实际零点校正到机械臂的基准姿态。
如果你不想改舵机寄存器,也可以在Lerobot的Robot类里维护一组zero_offset,读取位置后减去偏移量即可。两种方式原理一样,看你怎么方便。
5.4 数据采集与回放实操
Lerobot框架中最常用的是通过遥控模式采集演示数据。我的方案是做一个简单的遥控手柄:在电脑端写一个Python键盘监听脚本,按上下左右按键控制各个关节转动,同时通过Lerobot的record接口保存轨迹。
其实Lerobot本身提供了数据记录逻辑,格式是HDF5文件,包含每个时间步的关节位置和图像帧。你只需要保证机械臂的观测和动作能正确同步到这些字段中。
采集一段演示数据的命令类似于:
python lerobot/scripts/control_robot.py \ --robot-path my_feetech_robot.py \ --robot-config my_feetech_config \ --mode record \ --episodes 10这里的control_robot.py是Lerobot提供的交互入口,会根据你的配置调用read_observations和send_action。
回放时,同样的脚本切换成--mode replay,指定某个episode的编号,机械臂就会按记录的轨迹重新执行一遍。回放是检验数据质量的直接方式,如果轨迹有明显的抖动或偏移,说明采集数据时就有问题,不要急着训练。
5.5 训练策略模型
数据采集完成后,就可以用Lerobot提供的训练脚本训练模仿学习模型了。以ACT算法为例:
python lerobot/scripts/train.py \ --algorithm act \ --dataset-dir ~/lerobot/datasets/my_task \ --steps 5000 \ --batch-size 64 \ --num-epochs 100训练过程中,模型会学习从观测到动作的映射。对于机械臂任务来说,观测通常是关节角度加摄像头图像。如果你的配置里没有加摄像头,模型仍能用关节角度做输入,只是对复杂操作的支持会差很多,建议至少加一个俯视摄像头。
6. 常见问题与排查技巧实录
6.1 舵机完全没有反应
排查顺序:
- 检查舵机电源电压,STS3215需要5V以上才能工作,推荐7.4V。
- 检查串口接线,舵机总线信号线要接到USB转接板的RXD和TXD交叉线序。
- 确认舵机ID是否冲突,多个同ID舵机挂在总线上会导致命令混乱。
- 用PING指令单发,确认每个舵机都能返回。
6.2 舵机抖动、发热严重
抖动通常有几种原因:机械结构松动导致负载不均、位置环增益过高、供电电压不稳定。建议先用一点点的油门测试,比如一次只发送小幅角度增量,看是否稳定响应。供电优先使用稳压电源,不要用电池直接驱动舵机,瞬时压降会影响控制板通信。
6.3 Lerobot运行时报串口占用错误
有三种常见情况:一是上一次异常退出后,串口没有彻底释放,重启电脑解决;二是服务程序后台占用串口,可以用lsof /dev/ttyUSB0查看占用进程并kill掉;三是USB转接板的驱动未识别,可以在dmesg里看内核日志确认设备是否正常加载。
6.4 数据采集时图像帧率很低
如果加了摄像头,导致Lerobot图像采集很卡,优先检查USB带宽和分辨率。将摄像头分辨率降到640x480、帧率降到30fps,基本能满足摸索阶段的需求。另外,图像采集和舵机控制尽量分线程,不然同步等待会拉低整体控制频率。
7. 后续扩展方向与我的实际体会
7.1 加视觉定位做自动抓取
机械臂跑通基础的数据采集和回放之后,可以引入视觉模型。Lerobot社区里有不少基于SAM或目标检测结合机械臂抓取的案例,简单做法是先用OpenCV做颜色识别或ArUco码定位,得到目标位置的像素坐标,然后通过简单的逆运动学或视觉伺服引导机械臂靠近抓取点。
7.2 多机械臂协同实验
飞特舵机总线支持一条线挂多个舵机,而Lerobot也支持多机械臂配置。在两个机械臂分别独立串口的情况下,可以创建两个Robot实例,分别控制左右臂,实现双臂协同。这个过程会复杂一些,因为数据采集时要同时记录多臂状态和时间步对齐,但对于研究双臂操作任务来说非常值得试。
7.3 这套方案的定位与局限
最后说说我对“Lerobot+飞特舵机”这套组合的真实评价。优点是投入成本低、供应链稳定、舵机协议公开透明、跟开源框架对接灵活,适合个人开发者验证模仿学习算法,也适合教学场景。局限性是舵机的减速齿轮虚位和重复定位精度一般,面对精密装配这类任务会比较吃力,但在搬运、分拣、简单插拔等场景完全够用。
我个人在实际操作中的体会是,机械臂类项目最容易翻车的不是算法,而是机械装配和供电问题。如果你能把每个关节装得够紧、把供电做得够稳、把舵机ID分配清晰,Lerobot跑起来会非常顺畅。接下来我打算在这台机械臂上尝试更细粒度的数据采集策略,比如混合遥操作,让模型学到的动作能泛化到新的物体位置上。这个方向很有潜力,后续有进展了再来分享。