基于Lerobot SO-101与YOLOv5的捡袜子机器人:从视觉识别到运动规划全流程实践 1. 项目缘起与核心需求解析1.1 一个“懒人”的浪漫与工程挑战事情的起因说出来有点不好意思。我妻子有个习惯或者说是很多家庭都存在的“甜蜜的烦恼”脱下来的袜子总是不太愿意直接放进洗衣篮。客厅沙发旁、卧室床边、书房椅子下时不时就会出现一两只“落单”的袜子。提醒过几次但生活嘛总有些随性的时刻。作为一个有点“技术宅”倾向的丈夫我琢磨着与其反复念叨不如用点“魔法”——不对是用工程手段来解决这个问题。这个想法就是“建造一个能自动捡起妻子袜子的机器人手臂”的雏形。它听起来像是个玩笑或者一个极客的炫技项目但对我而言它背后有几个非常实际且分层的需求。第一层当然是解决那个具体的、微小的家庭痛点用一种有趣且不令人反感的方式。第二层则是我个人对机器人技术特别是低成本、开源机器人平台的浓厚兴趣一直想找个有明确目标的实战项目来练手。第三层更深一些是验证在非结构化家庭环境中一个相对简单的机器人系统能否可靠地完成一项看似简单、实则充满变数的任务。这涉及到视觉识别、路径规划、抓取策略等一系列机器人学的经典问题但场景从实验室的规整桌面换成了我家铺着地毯、光线多变、杂物偶尔出现的客厅。1.2 为什么是Lerobot SO-101确定了要做个捡袜子的机械臂后选型是第一个关键决策。市面上从玩具级的舵机机械臂到工业级的六轴机器人选择很多。我最终锁定Lerobot SO-101是基于以下几个核心考量首先开源性与社区支持。Lerobot是一个在创客和机器人教育领域颇有名气的开源机器人项目。SO-101是其一款桌面级五自由度5-DOF机械臂型号。开源意味着我可以获得全部的结构设计文件通常是STP或STEP格式、电路图、固件源码和控制软件。这对于后期可能需要的任何定制化修改比如加装末端执行器、调整臂长至关重要。一个活跃的社区论坛或GitHub仓库能让我在遇到问题时有地方寻找答案或灵感。其次性价比与可玩性。与动辄数千上万的商用教育机器人臂相比SO-101的套件价格亲民得多。它通常以散件形式出售需要自己动手组装这本身就是学习和理解机器人机械结构、传动原理的绝佳过程。它的舵机是标准的数字舵机如MG996R通用性强损坏后更换成本极低。再者足够的性能冗余。捡袜子这个任务对负载和精度要求并不算高。袜子很轻定位精度有1-2厘米也足够。SO-101的负载通常在500克左右工作半径约30-40厘米对于在沙发、床边小范围作业绰绰有余。这种“性能过剩”其实是好事它为算法的容错性和未来的功能扩展比如捡更重的电视遥控器、小玩具留出了空间。最后软件生态兼容性。Lerobot通常支持多种控制方式包括Arduino/ESP32直接控制、通过ROS机器人操作系统进行上层控制。我计划使用视觉识别因此需要一个能方便集成摄像头和运行视觉算法如OpenCV的平台。基于树莓派ROS的方案与SO-101的兼容性很好形成了从感知到决策再到执行的完整链路。注意选择开源平台时一定要提前调研其社区活跃度最近一次代码提交、论坛帖子数量、文档完整度。一个“死”掉的项目后续调试会异常痛苦。2. 系统整体设计与技术栈选型2.1 从袜子到坐标任务分解让机械臂捡起一只随意丢放的袜子这个任务可以分解为以下几个核心子模块感知Perception机器人需要“看见”袜子。这包括目标检测在摄像头画面中从背景地板、地毯、家具中识别出“袜子”这个物体。位姿估计确定袜子在三维空间中的位置X, Y, Z坐标和大致朝向。对于捡取任务朝向信息有时不如位置信息关键但知道袜子是卷起来的还是摊开的有助于规划抓取方式。规划Planning路径规划计算机械臂末端夹爪从当前位置可能是“待机”位置无碰撞地运动到袜子正上方抓取点的最优轨迹。抓取规划决定夹爪以何种姿态角度去抓取袜子。对于柔性、形状不固定的袜子这可能不是一个精确的点而是一个区域。控制Control运动执行将规划好的关节角度序列通过控制器如舵机控制板发送给SO-101的各个舵机驱动机械臂按轨迹运动。抓取执行控制末端执行器我选用的是二指夹爪执行抓取动作。交互与流程Interaction Workflow整个系统的工作流程如何触发一次捡取任务是定时巡检还是由人发出指令比如按个按钮或语音命令捡起后袜子运送到哪里固定的投放点如洗衣篮上方2.2 硬件架构搭建基于以上分解我搭建了如下硬件系统机械臂本体Lerobot SO-101 五自由度机械臂套件。自己组装的过程花了大概两个晚上重点是确保各关节紧固、线缆整齐并校准了每个舵机的初始零位。末端执行器我放弃使用复杂的吸盘或多指灵巧手选择了一个简单的、由微型舵机驱动的二指平行夹爪。夹指内侧贴了硅胶垫以增加摩擦力。对于袜子这种柔软、可变形物体平行夹爪通过“捏”的方式比吸盘更可靠吸盘对多孔织物效果差。主控与感知单元树莓派4B4GB内存。它承担了“大脑”的角色运行Ubuntu系统和ROS Noetic专为树莓派优化的机器人操作系统发行版。连接USB摄像头我用了罗技C920因为它驱动兼容性好且能提供不错的图像质量负责图像采集。运行视觉识别程序基于OpenCV和深度学习模型。通过ROS节点发布控制指令。底层控制器一块Arduino Mega 2560。树莓派通过USB串口向Arduino发送目标关节角度指令Arduino负责生成具体的舵机控制信号PWM并驱动SO-101的五个舵机以及夹爪舵机。这样做的好处是将实时性要求高的底层舵机控制与上层计算解耦树莓派崩溃了机械臂也不会乱动。供电为Arduino和舵机单独提供一路大电流5V/10A开关电源避免树莓派因电流冲击不稳定。树莓派单独供电。硬件连接示意图逻辑层面[USB摄像头] -- [树莓派4B (运行ROS/OpenCV)] | | (USB/串口) V [Arduino Mega] | | (PWM信号线) ------------------------------------- | | | | | [关节1] [关节2] [关节3] [关节4] [夹爪舵机] 舵机 舵机 舵机 舵机2.3 软件技术栈抉择软件是项目的灵魂我的选型围绕ROS展开操作系统与中间件ROS Noetic。它是机器人软件的“骨架”提供了节点间通信、消息传递、坐标变换TF、可视化Rviz等基础设施。使用ROS我可以将视觉识别、运动规划、底层控制分别写成独立的节点它们通过话题Topic和服务Service来交换数据模块清晰易于调试。视觉识别方案A传统图像处理最初尝试用OpenCV的颜色阈值分割袜子通常是深色与浅色地板对比明显结合轮廓检测。优点是速度快、计算资源消耗小。但在光线变化、袜子颜色多样、与地板颜色相近时鲁棒性极差。方案B深度学习最终采用YOLOv5s小型模型进行目标检测。我在家里拍摄了约300张包含袜子在不同位置、姿态、光照条件下的图片使用LabelImg进行标注然后在自己的电脑上有GPU进行训练。将训练好的模型转换为ONNX或TorchScript格式部署在树莓派上使用libtorch进行推理。虽然速度比方案A慢约0.5-1秒一帧但识别准确率从70%提升到了95%以上彻底解决了误检和漏检问题。运动规划与控制运动学为SO-101建立了正向运动学模型已知关节角求末端位置和逆向运动学IK模型。由于是5自由度臂对于给定的末端位置X, Y, Z和朝向通常固定为垂直向下抓取其逆解可能有多组或无法精确到达。我使用了数值迭代法如雅可比矩阵伪逆法来求解并在ROS中编写了对应的IK求解节点。路径规划由于家庭环境相对静态我采用了点到点的直线插补和关节空间插补。在MoveIt!ROS中强大的运动规划框架和自定义简单规划器之间我选择了后者。因为场景简单障碍物固定我知道沙发和床的位置直接规划从A点到B点的关节空间轨迹并加入速度和加速度限制更轻量、更可控。控制接口在ROS中创建了一个/arm_controller节点它订阅/sock_position话题视觉节点发布的袜子3D坐标调用IK求解服务生成关节角度序列然后通过rosserial包封装成串口消息发送给Arduino。Arduino固件编写了一个简单的固件监听串口指令。指令包含每个舵机的目标角度0-180度映射到0-1024的PWM值和运动时间用于平滑移动。Arduino采用线性插值的方式让舵机匀速运动到目标位置避免抖动和冲击。实操心得在资源受限的树莓派上跑YOLO模型一定要选“s”小或“n”纳米版本。同时可以利用OpenCV的DNN模块进行推理它针对CPU做了不少优化。另外将摄像头分辨率从1080p降到720p或480p能显著提升处理帧率对捡袜子这种非高速任务完全足够。3. 核心模块实现与实操细节3.1 视觉识别模块的“驯服”之路让机器人稳定地“看见”袜子是整个项目最耗时也最关键的环节。1. 数据采集与标注的“脏活累活” 我拿着手机和摄像头在家里各个可能掉落袜子的角落不同材质的地板、地毯、沙发阴影处、床角以不同角度、不同光照白天自然光、晚上暖光/白光灯拍摄了数百张照片。关键是要覆盖足够的“负样本”——即没有袜子但容易被误认为是袜子的物体比如深色的遥控器、数据线团、阴影区域。标注工具用的是LabelImg给每张图中的袜子画上边界框Bounding Box保存为YOLO格式的txt文件。2. 模型训练与优化 在有一张NVIDIA GPU的电脑上我使用Ultralytics提供的YOLOv5代码进行训练。几个关键参数--img 640输入图像尺寸。更小的尺寸如320速度更快但精度可能下降640是一个较好的平衡点。--batch-size 16根据GPU显存调整。--epochs 100训练轮数。观察训练损失和验证集精度mAP曲线在精度不再显著提升时提前停止。--data sock.yaml数据配置文件里面指定了训练集、验证集路径和类别数我这里就一类sock。训练完成后使用export.py脚本将PyTorch模型转换为torchscript格式便于在树莓派上部署。3. 树莓派部署与推理加速 在树莓派上安装libtorchPyTorch的C接口。编写一个C ROS节点这个节点订阅摄像头驱动发布的图像话题/camera/image_raw。对图像进行预处理缩放到640x640归一化转换为Tensor。加载torchscript模型并进行推理。对输出进行后处理非极大值抑制NMS过滤掉置信度低的检测框。将检测框的中心像素坐标通过相机标定得到的参数结合已知的夹爪离地高度因为机械臂是固定的摄像头安装在机械臂底座或附近通过手眼标定得到与机械臂基座的变换关系转换到机械臂基坐标系下的3D坐标X, Y。Z坐标高度我直接设定为一个固定值即夹爪下降到刚好能触碰到地面的高度略低于地板平面。将计算出的袜子X, Y, Z坐标发布到/sock_position话题。相机标定实操我打印了一张标准的棋盘格标定板用摄像头从不同角度拍摄了约20张照片使用OpenCV的calibrateCamera函数标定出了相机的内参焦距、主点和畸变系数。这一步至关重要它消除了镜头畸变并将像素坐标映射到真实的物理尺度。3.2 运动规划与控制让手臂优雅地动起来1. 建立机器人模型URDF 在ROS中机器人的物理结构用URDF文件描述。我为SO-101创建了一个URDF文件定义了它的连杆link和关节joint包括尺寸、质量、惯性矩粗略估计、关节类型全是旋转关节和旋转轴。这个模型不仅用于运动学计算还可以在Rviz可视化工具中显示用于仿真和调试。2. 逆运动学IK求解 对于捡袜子任务我固定末端执行器的姿态为垂直向下Roll0, Pitch90度, Yaw0这样夹爪可以像人手一样垂直去捏取。那么对于视觉模块给出的目标点(x, y, z)我需要求解5个关节角(θ1, θ2, θ3, θ4, θ5)。 由于5自由度臂无法满足末端任意位置和姿态我固定了姿态所以问题简化为求解位置。我使用了数值迭代法。基本思路是给定当前关节角用正运动学计算当前末端位置P_current。计算当前位置与目标位置P_target的误差向量。计算机器人在当前构型下的雅可比矩阵J它描述了关节速度与末端速度的线性关系。利用Δθ J⁺ * ΔP其中J⁺是雅可比矩阵的伪逆计算所需的关节角变化量Δθ。更新关节角θ_new θ_current α * Δθα是一个步长因子用于控制收敛速度。重复迭代直到误差|ΔP|小于某个阈值如1毫米或达到最大迭代次数。我在ROS中创建了一个IK求解服务。运动控制节点收到目标位置后调用该服务获得一组可行的关节角度解。如果无解目标点超出工作空间则规划失败机器人不动作。3. 轨迹生成与执行 得到目标关节角后不能直接让舵机跳过去需要生成平滑的轨迹。我采用了五次多项式插值来规划每个关节从起点到终点的角度-时间曲线。它能保证起点和终点的速度、加速度均为零运动平滑无冲击。 规划出的轨迹是一系列时间点对应的关节角度序列。控制节点以固定频率如50Hz遍历这个序列将每个时刻的角度值通过rosserial发送给Arduino。4. Arduino端的平滑执行 Arduino收到一组目标角度{θ1_t, θ2_t, ..., θ5_t, gripper_angle}和总运动时间T。它会在每个控制周期如20ms计算当前时间点对应的期望角度线性插值并转换为PWM值发送给舵机。同时我加入了一个低通滤波器来进一步平滑PWM指令有效减少了舵机在运动中的“嗡嗡”声和抖动。// Arduino端伪代码示例 void executeTrajectory(float targetAngles[NUM_JOINTS], float durationSec) { unsigned long startTime millis(); float startAngles[NUM_JOINTS]; // 记录起始角度 for(int i0; iNUM_JOINTS; i) startAngles[i] currentAngles[i]; while(millis() - startTime durationSec * 1000) { float t (millis() - startTime) / (durationSec * 1000.0); // 归一化时间 [0, 1] t constrain(t, 0, 1); for(int i0; iNUM_JOINTS; i) { // 线性插值 float desiredAngle startAngles[i] (targetAngles[i] - startAngles[i]) * t; // 调用平滑移动函数内部包含低通滤波 smoothMoveServo(i, desiredAngle); } delay(20); // 控制周期 } // 确保最终到达目标位置 for(int i0; iNUM_JOINTS; i) smoothMoveServo(i, targetAngles[i]); }3.3 抓取策略与末端执行器设计捡袜子不同于抓取刚性物体需要特别的策略1. 夹爪设计 我使用的二指夹爪开合范围约0-5厘米。指尖内侧粘贴了约2毫米厚的硅胶垫。硅胶提供了良好的摩擦力和一定的柔韧性既能夹紧袜子又不会因为太硬而将其推开或夹坏。2. 抓取流程Approach接近机械臂运动到袜子正上方约10厘米处夹爪完全张开。Descend下降机械臂末端垂直下降直到夹爪的指尖轻微触碰到地面通过电流反馈或设定一个固定的“地面”Z坐标。对于地毯这种柔软表面我会让夹爪稍微“嵌入”一点比如多下降3毫米确保能接触到袜子。Grasp抓取夹爪闭合。闭合的力度需要仔细调整太轻会夹不住太重可能把袜子“捏”成一团甚至拉破。我通过实验找到了一个能可靠抓取大多数棉袜的舵机角度对应特定的夹紧力。Lift提升夹爪保持闭合状态机械臂垂直提升一段距离如15厘米将袜子提离地面。Transport运送机械臂运动到洗衣篮上方。Release释放夹爪张开袜子掉落进篮子。3. 应对不确定性袜子姿态如果袜子是卷成球状的抓取成功率最高。如果是摊开的夹爪可能会只夹住一部分在提升过程中可能滑落。我的策略是一旦视觉检测到袜子一个矩形框我就以该框的中心作为抓取点。对于摊开的袜子这个点可能落在袜子的中部抓取依然有效。多次尝试如果一次抓取后视觉检测发现袜子还在原处或移动很小则判定为抓取失败可以重新规划一次抓取或者调整抓取点比如在检测框内随机采样几个点进行尝试。4. 系统集成、调试与实战优化4.1 ROS节点网络搭建整个系统的软件部分由多个ROS节点协同工作它们通过话题和服务进行通信[usb_cam Node] 发布 /camera/image_raw (图像数据) | V [sock_detector Node] 订阅 /camera/image_raw 运行YOLO模型 发布 /sock_position (geometry_msgs/PointStamped) | V [arm_controller Node] 订阅 /sock_position 调用 /solve_ik Service (逆解服务) 生成轨迹 通过 /cmd_joint_angles (自定义消息) 发布给rosserial | V [rosserial_python Node] 订阅 /cmd_joint_angles 通过串口转发给Arduino | V [Arduino Mega] 解析指令控制舵机运动使用rqt_graph工具可以清晰地看到这个节点网络图。这种松耦合的设计让调试变得非常方便。例如我可以单独测试视觉节点用rqt_image_view查看检测结果也可以手动发布一个虚拟的/sock_position来测试机械臂的运动而无需真的放一只袜子。4.2 调试过程中踩过的“坑”与解决方案舵机抖动与噪声问题机械臂运动时某些关节舵机会剧烈抖动或发出很大噪音甚至发热严重。排查首先是电源问题。用万用表测量舵机动作时的电压发现当多个舵机同时运动时电压会被拉低到4.5V以下导致舵机工作不稳定。其次是机械结构问题检查所有螺丝是否紧固连杆是否有卡滞。解决为舵机提供独立的大功率电源5V/10A开关电源并与树莓派/Arduino的逻辑电源共地。在Arduino控制代码中加入动作平滑算法如前面提到的线性插值和低通滤波避免给舵机发送突变的PWM信号。确保机械臂安装底座绝对稳固任何微小的晃动都会被放大。视觉坐标转换不准问题机器人总是抓取在袜子旁边几厘米的位置。排查检查相机标定参数是否正确。检查手眼标定相机与机械臂基座的变换关系是否准确。我采用的方法是“四点标定法”让机械臂末端依次移动到四个已知的、在相机视野内且彼此距离较远的精确位置比如一张标定纸上的四个角点记录下机械臂基座坐标系下的坐标和相机图像中的像素坐标然后用cv2.findHomography()函数计算单应性矩阵。但这种方法假设了地面是平面且抓取高度固定。解决精细执行手眼标定流程。后来我改用了一种更鲁棒的方法在机械臂末端安装一个明显的标记如LED控制机械臂在空间内移动多个位置用相机捕捉标记点同时记录机械臂末端的真实坐标通过求解一个AXXB的方程来获得更精确的变换矩阵。标定后抓取精度显著提高误差在1厘米以内。逆运动学无解或奇异点问题对于某些目标点IK求解器失败或者机械臂运动到接近完全伸直的状态时变得不稳定。排查目标点是否超出了机械臂的工作空间机械臂是否接近了奇异构型如第二和第三关节接近一条直线时失去一个方向的自由度解决在调用IK求解器前先进行工作空间检查。对于SO-101其工作空间大致是一个球形壳层。简单判断目标点距离基座中心的水平距离和垂直高度是否在合理范围内。对于奇异点在轨迹规划时尽量避免让关节角经过这些敏感区域。我还在IK求解器中加入了阻尼最小二乘法在接近奇异点时求解会更稳定但会牺牲一些精度对于捡袜子任务足够。误检与漏检问题在光线昏暗或袜子颜色与背景相似时YOLO也会出错。解决数据增强。在训练数据中增加亮度、对比度随机变化模拟不同光照。增加随机遮挡。此外在推理端加入时间一致性滤波连续多帧比如5帧都在同一位置附近检测到袜子才判定为有效目标这滤除了大部分瞬时误检。4.3 完整工作流程与用户体验优化最终我将整个系统封装为一个启动文件.launch文件一键启动所有节点。并添加了一个简单的状态机逻辑待机状态机械臂停在“休息位”一个紧凑、不占空间的姿态。触发检测可以通过多种方式触发定时触发每30分钟自动运行一次检测和捡取流程。按钮触发在墙上贴一个无线按钮妻子看到袜子后按一下机器人开始工作。语音触发接入了简单的离线语音识别模块如Edge Impulse训练的“捡袜子”关键词检测。执行流程机械臂运动到“观测位”一个能俯瞰大部分目标区域的位置。视觉系统扫描如果检测到袜子获取坐标。规划抓取轨迹并执行。将袜子运送到固定在墙上的小型“袜子投放口”连接着通往洗衣篮的滑道。返回待机状态。异常处理如果抓取失败视觉反馈袜子未移动会尝试最多2次。如果连续失败则放弃本次任务并让机械臂回到观测位通过一个RGB LED灯闪烁黄色报警提示可能需要人工干预。实操心得家庭环境中的机器人安全性和可靠性比性能更重要。我在机械臂基座安装了急停开关在软件中设置了关节软限位和速度/加速度限制防止其以过快的速度撞击家具或人。所有运动都在一个较为缓慢、稳定的速度下进行虽然效率不高但让人安心。可靠性方面除了上述的误检过滤、多次尝试机制我还加入了系统自检上电时所有关节缓慢运动一遍极限位置确保没有卡住定期如每天一次执行一次空跑循环检查各个功能是否正常。5. 项目总结、成本与未来展望5.1 成本核算与耗时硬件成本Lerobot SO-101套件约800元。树莓派4B 内存卡 外壳约600元。Arduino Mega 2560约80元。USB摄像头罗技C920约300元。二指夹爪套件约150元。舵机、螺丝、线材、电源等杂项约200元。总计约2130元。大部分部件都可以复用或用于其他项目。时间成本机械臂组装与基础调试2个周末。学习ROS基础、搭建开发环境1周业余时间。视觉识别模块开发与训练2周大部分时间在采集数据和训练模型。运动规划与控制集成1周。系统联调、避坑、优化2周。总计约1.5个月的业余时间投入。5.2 效果评估与妻子反馈经过一个月的“服役”这个机器人手臂大约成功捡起了80%的“目标袜子”。失败案例主要集中于袜子被完全塞在沙发垫缝隙深处视觉看不见、多只袜子堆叠在一起识别为一个目标抓取时可能只抓起最上面一只、以及极少数误抓其他深色小物体的情况。妻子的反馈从最初的“好奇”和“觉得好玩”变成了“偶尔还真能帮上忙”。最重要的是它以一种积极、有趣的方式提醒了家庭物品收纳的小习惯而没有引发任何唠叨或争执。从工程角度看这个项目成功地将机器人技术的多个环节感知、规划、控制集成到一个有明确应用场景的系统中并解决了从实验室到真实家庭环境落地的一系列实际问题。5.3 可改进方向与扩展思路这个项目还有很多可以优化和扩展的空间感知升级使用深度摄像头如Intel RealSense D435替代普通RGB摄像头直接获得三维点云可以更精确地获取袜子的3D位置和形状甚至判断其堆积状态。训练更精细的模型不仅能检测袜子还能区分“正面”、“反面”或者“成对匹配”但这需要更大量的标注数据。抓取策略优化引入触觉传感器或力矩传感器。在夹爪内侧安装柔性压力传感器可以感知抓取力度实现自适应抓取避免抓空或抓得太紧。研究针对柔性物体的专用抓取规划算法例如基于物理仿真的抓取点预测。移动性扩展将机械臂安装在一个移动底盘如差速轮式机器人上使其能够自主巡逻整个房间扩大工作范围。多任务能力通过更换不同的末端执行器如吸盘、钩子并升级视觉识别模型可以让它完成更多家务比如捡起散落的玩具、将桌面上的空杯子送到厨房等。这个项目对我来说远不止于捡起几只袜子。它是一个完美的载体让我系统地实践了从机械设计、电子电路、嵌入式开发到计算机视觉、机器人运动规划与控制的全栈机器人开发流程。每一个遇到的问题和解决的方案都是宝贵的经验。它证明了即使是用相对廉价的硬件和开源软件也能构建出能在复杂真实环境中完成特定任务的机器人系统。这其中的乐趣和成就感或许才是这个项目带给我的最大收获。