简介:基于YOLOv5目标检测与MoveIt机械臂运动规划,融合语音提示与节点反馈,构建了一套可完成垃圾识别、定位与自动分类抓取的毕业设计项目,适合人工智能、自动化、电子信息、物联网等专业学生用于毕设、课设、项目初期演示,也适合ROS初学者借助完整代码进行实战入门。压缩包共356个文件,大小约188.62MB,涵盖Python/C++核心控制代码、YOLOv5训练好的模型权重、MoveIt配置与URDF机器人模型、语音提示音频以及yaml/launch启动配置文件,各类型文件分工明确,目录结构清晰,并附有设计文档,便于快速部署和按模块理解。内容中包含语音控制、节点反馈及主执行模块,可整体展示“视觉识别—机械臂规划—抓取分类”的完整链路。已有97人浏览学习,可在此基础上扩展垃圾类别、优化抓取策略,也可作为课程设计或毕业设计的直接基础。
1. 把垃圾分类机器人的实现路径拆开:YOLOv5加MoveIt源码项目能不能落地?
视觉识别和机械臂控制,单独拎出来都有大量成熟代码,真正让课设、毕设翻车的往往是两套系统怎么拼起来。这个标题里的“垃圾分类机器人”并不是一台能处理所有垃圾的万能设备,它解决的是一个非常具体的闭环:用YOLOv5从相机画面中识别出垃圾类别和位置,再把结果交给MoveIt控制机械臂完成分类投放。对正在做机器人方向课设、准备比赛原型、或者想搞懂“视觉如何驱动运动”的从业者来说,这套源码和设计文档的价值在于少走两个月的弯路;新手也能按文档把环境配通、把模型跑起来。开头先说实话:YOLOv5精度不是瓶颈,MoveIt规划也不是,真正的坎是坐标变换、状态衔接和异常处理。
2. 系统架构与源码目录:YOLOv5和MoveIt是怎么串成一条垃圾分拣流水线的
2.1 数据流与坐标系:检测框如何变成机械臂的目标位姿
整条流水线可以拆成六个环节,每次抓取都是一次完整的数据闭环:
| 环节 | 输入 | 输出 | 关键参数 |
|---|---|---|---|
| 相机采集 | 实时图像 | 一帧 BGR 图像 | 分辨率 640x640 |
| YOLOv5 推理 | 图像 | 类别、置信度、目标框 | conf=0.45, iou=0.45 |
| 坐标映射 | 目标框中心像素 | 相机坐标系三维点 | 相机内参、深度值 |
| 手眼变换 | 相机坐标系点 | 机械臂基座坐标系点 | T_base_camera |
| MoveIt 规划 | 目标位姿 | 机械臂运动轨迹 | planning_time=5s |
| 夹爪投放 | 轨迹执行 | 将垃圾放入对应分类盒 | 夹爪开合行程 |
最难理解的是第二步到第四步。YOLOv5输出的(xmin, ymin, xmax, ymax)是像素坐标,机械臂 move_group 接受的pose.position是三维笛卡尔坐标。相机如果是固定在工作区上方的,常见做法是先假设垃圾放在固定高度平面上,用像素坐标反投影得到相机坐标系下的点,再乘上一个预先标定好的变换矩阵T_base_camera,转成机械臂基座坐标。如果垃圾堆叠或者高低不平,只靠单目相机就会抓空,此时要么换深度相机,要么限制每次只处理单层摆放的垃圾。
我在这个环节的习惯是,先把所有坐标变换写成一个独立模块,不混进检测脚本里。这样当你怀疑位置不对时,可以直接打印P_camera和P_base对比,很快定位是相机内参问题还是手眼标定问题,不用一头扎进整个项目里翻逻辑。
2.2 为什么选YOLOv5和MoveIt:选型理由其实很现实
YOLOv5不是最新版本,但“YOLOv5环境配置”和“YOLOv5训练自己的数据集”这两件事的资料密度,比YOLOv8、YOLOv11都高。对一个做机器人项目的人来说,社区资料就是后悔药:遇到训练不收敛、权重导出失败、部署时算子不支持,几乎都能搜到现成的解决记录。YOLOv5的源码结构也直白,train.py、detect.py、hyp.scratch-low.yaml摆在明处,不需要再套一层封装就能训练自己的数据集。
MoveIt的选择则完全从工程效率出发。MoveIt本身不控制具体硬件,它基于URDF模型做运动规划、碰撞检测和逆运动学求解,上层用MoveGroupCommander就能发布目标位姿。这比自己写逆解省太多事。对六轴机械臂,YOLOv5加MoveIt几乎是一套标准答案;对四轴或非标机械臂,MoveIt依然能用,但规划失败频率会明显增加,第5章会专门讲。
这两个库都吃Python,所以源码包内部通信通常直接用ROS话题或者socket。如果你不用ROS,也可以用普通的Python进程:YOLOv5检测线程把结果放进队列,机械臂控制线程从队列取目标位姿执行。这种方式更轻,但失去了ROS里tf、MoveIt原生的坐标同步能力,我自己做演示项目时还是推荐保留ROS层。
2.3 拿到源码包后先看什么:目录结构与依赖安装
一份正常的垃圾分类机器人源码包,目录通常长这样:
garbage_robot/ ├── detect/ # YOLOv5 相关代码、权重、数据配置 │ ├── train.py │ ├── detect.py │ └── weights/ ├── moveit_control/ # ROS 包,MoveIt 配置与机械臂控制脚本 │ ├── config/ │ ├── launch/ │ └── scripts/ ├── docs/ # 设计文档、接口说明、部署文档 └── requirements.txt我拿到压缩包的第一件事不是跑 demo,而是先按文档装依赖,因为很多“项目跑不起来”其实是环境问题。
conda create -n yolov5 python=3.8 -y conda activate yolov5 cd garbage_robot/detect pip install -r requirements.txt这段命令创建了一个独立的 Python 3.8 环境,避免把系统 Python 搞乱。requirements.txt里一般是 torch、opencv-python、pyyaml 这些YOLOv5基础依赖。如果你有 NVIDIA 显卡,建议再单独装 GPU 版的 torch,否则训练会慢到怀疑人生。机械臂侧如果是 ROS Noetic,还要装 MoveIt 相关包:
sudo apt install ros-noetic-moveit ros-noetic-moveit-commander ros-noetic-tf2-ros cd ~/catkin_ws catkin_make source devel/setup.bash这里的catkin_make会编译整个工作区里的 MoveIt 配置包。依赖装好之后,我一般坚持“先视觉后机械臂”的顺序:先单独跑detect.py确认模型和相机正常,再单独启动机械臂的 MoveIt demo,最后才把两端拼起来。直接一把梭启动全部节点,出现问题很难分清是视觉的锅还是规划的锅。
3. YOLOv5垃圾识别:YOLOv5训练自己的数据集、超参数设计与部署接口
3.1 垃圾数据集标注与目录格式
要训练自己的垃圾分类模型,首先要确定类别。常见垃圾分类类别包括塑料瓶、易拉罐、纸盒、电池、果皮等等,数量不用追求太多,6到10类足够演示使用。标注工具可以用 labelImg,注意保存格式要选 YOLO,也就是每个图像对应一个同名.txt文件,里面每行是一个目标,格式为class cx cy w h,其中坐标都是归一化到 0 到 1 的浮点数。
数据集目录结构一般像下面这样:
datasets/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/在detect/目录下,还要准备一个garbage.yaml:
path: ../datasets/ train: images/train val: images/val nc: 6 names: 0: plastic_bottle 1: can 2: carton 3: battery 4: fruit_peel 5: paper这个path是相对于训练命令执行位置的路径,建议用绝对路径避免找不到数据;nc必须和names数量一致。垃圾检测和通用目标检测有一个差异:很多垃圾目标小、表面反光、形态不规则,所以标注时不要把背景杂物标进去,宁可漏标也不要乱标,否则模型会学到一堆错误特征。
如果手头没有自建数据集,可以使用公开垃圾分类数据集先跑通流程,后续再补充自己场景下的照片。我通常会在训练前统计一下每个类别的样本数量,出现严重不均衡时,再用复制粘贴、翻转等手段做类别均衡,不然训练出来的模型会对样本多的类别有严重偏向。
3.2 YOLOv5训练命令与超参数:从garbage.yaml到best.pt
训练命令是YOLOv5项目里最核心的一行,下面这个是我在实际项目中常用的配置:
python train.py --img 640 --batch 16 --epochs 100 --data garbage.yaml --weights yolov5s.pt --project runs/train --name garbage --cache这段命令表示训练输入图像尺寸为 640x640,批次大小 16,共训练 100 轮,初始权重使用 COCO 预训练好的yolov5s.pt。--cache会把图像提前加载进内存,能明显加快训练速度,但如果内存不足 16G,建议去掉这个参数。
超参数表现在集中在data/hyps/hyp.scratch-low.yaml里,重点调三个地方:
lr0:初始学习率,默认 0.01。如果训练过程的 loss 震荡剧烈,我会降到 0.005。hsv_h、hsv_s:颜色增强幅度。金属罐和塑料瓶反光强,太大的饱和度增强会让模型把反光当成特征,建议适当调低。fliplr:水平翻转概率,默认 0.5。如果垃圾分类和摆放位置有关,比如瓶盖方向会影响夹取,翻转后可能导致模型学错特征。
训练结束后,结果会保存在runs/train/garbage/目录下,其中weights/best.pt是验证集上表现最好的权重,后续部署只认这个文件,不要图省事用last.pt。另一个任务是看results.png里的val/box_loss和mAP@0.5曲线,判断是否收敛。如果 mAP 卡在 0.6 以下反复横跳,优先检查数据集而不是改模型,增加光照不同的真实场景图通常比堆训练轮数更有效。
3.3 部署封装:把best.pt变成机械臂能用的检测结果
训练完模型后,机械臂控制脚本不能直接调train.py,它需要一个轻量接口。常见做法是把推理封装成函数:
import cv2 import torch import json model = torch.hub.load('./yolov5', 'custom', path='weights/best.pt', source='local', force_reload=True) model.conf = 0.45 model.iou = 0.45 def detect_garbage(frame): results = model(frame) boxes = results.pandas().xyxy[0] targets = [] for _, row in boxes.iterrows(): item = { "category": row["name"], "confidence": round(float(row["confidence"]), 4), "x_center": round(float((row["xmin"] + row["xmax"]) / 2), 2), "y_center": round(float((row["ymin"] + row["ymax"]) / 2), 2) } targets.append(item) return targets这段代码加载本地best.pt,用source='local'避免每次从网络下载。model.conf控制置信度阈值,设成 0.45 是为了过滤低置信度的误检;model.iou是 NMS 的 IoU 阈值,避免同一个瓶子被多个框重复套住。detect_garbage返回的是包含类别和中心像素坐标的列表,直接可以序列化成 JSON 发给机械臂控制节点。
要注意的是,这里往外发的是像素坐标x_center, y_center,不是三维坐标。真正转成机械臂基座坐标的动作应该放在机械臂侧,因为那里才有T_base_camera和当前机械臂状态。这样分层有个好处:视觉定位和运动控制可以分别调试,哪个环节出问题,就单独在哪个环节打日志。
4. MoveIt机械臂分类:从URDF配置到抓取投放状态机
4.1 用moveit_setup_assistant生成配置包:规划组与末端执行器
MoveIt第一次使用需要生成配置包,命令很简单:
roslaunch moveit_setup_assistant setup_assistant.launch启动后选择机械臂的 URDF 文件,然后创建规划组。例如六轴机械臂,我会把手臂的六个关节放进一个名为arm的规划组,再把夹爪设成独立的gripper组,并勾选 gripper 为末端执行器。这一步听起来是点点鼠标,实际会直接影响后续能否规划成功。如果 URDF 里的碰撞体没定义好,MoveIt规划时会认为机械臂和桌面一直是碰撞的,导致无法规划或规划轨迹异常。
配置完成后会生成一个moveit_config包,里面有几个关键文件:
config/kinematics.yaml:逆运动学求解器配置,默认 KDL,如果出现奇异点规划失败,我会改成 TRAC-IK。config/ompl_planning.yaml:OMPL 规划器参数,默认配置够用,不需要动。config/joint_limits.yaml:关节限位,如果机械臂实际关节限位和 URDF 不一致,必须改这里。
验收配置包是否正常的方式:
roslaunch your_moveit_config demo.launch在 Rviz 里拖动机械臂末端,如果能平滑移动且不穿模,说明URDF和配置基本没问题。这一步过了,后面的代码控制才能真正跑起来。很多项目源码自带这个配置,但如果你换了一台机械臂,还是需要重新生成一次配置包再合并进去,这是我吃过亏的地方:直接沿用别人配置,结果关节限位不同,MoveIt经常规划到关节极限外又自动放弃。
4.2 move_group_commander控制机械臂到达目标位姿
YOLOv5把目标点给过来之后,机械臂侧的核心代码是用MoveGroupCommander让机械臂运动到指定位置。我一般这样写:
import rospy from moveit_commander import MoveGroupCommander, PlanningSceneInterface rospy.init_node("garbage_robot_control", anonymous=True) arm = MoveGroupCommander("arm") arm.set_planning_time(10.0) arm.set_goal_position_tolerance(0.002) arm.set_goal_orientation_tolerance(0.005) # 假设 obj_x, obj_y, obj_z 已经转换到机械臂基座坐标系 pose_target = arm.get_current_pose().pose pose_target.position.x = obj_x pose_target.position.y = obj_y pose_target.position.z = obj_z plan = arm.plan(pose_target) if plan: arm.execute(plan, wait=True) else: rospy.logwarn("MoveIt planning failed")这里set_planning_time(10.0)给规划器10秒搜索时间;位置容差设为2毫米,姿态容差5毫弧度,对垃圾分类抓取来说足够,太严会让规划反复失败。arm.plan()返回规划结果,成功才执行,失败时要记录日志,不要硬执行。
有一个细节我想多说一句:get_current_pose()取的是当前末端位姿,通常是一个七维数组(x, y, z, qx, qy, qz, qw)。直接改position会保持当前姿态不变,也就是机械臂会保持夹爪朝下的姿态平移到目标点。这是抓取垃圾最常用的姿态,但如果你要抓起一个侧放的瓶子,姿态需要翻转,那就不能只改位置,还要给pose_target.orientation赋值。手动指定四元数时,建议先用 Rviz 里的 “Set Goal” 拖动到一个合适的抓取角度,再读取这里的四元数填到代码里,比自己手算可靠得多。
4.3 分类投放状态机:检测、夹取、投放怎么衔接
执行层面最容易翻车的,是 YOLOv5 检测线程和机械臂执行线程互相打架。我不会让机械臂每检测到一帧就动一次,而是用一个简单状态机来控制:
STATE_DETECT, STATE_GRASP, STATE_PLACE = 0, 1, 2 current_state = STATE_DETECT latest_target = None while not rospy.is_shutdown(): if current_state == STATE_DETECT: latest_target = detect_garbage(frame) if latest_target: current_state = STATE_GRASP elif current_state == STATE_GRASP: move_to_pose(latest_target) gripper.close() rospy.sleep(0.5) current_state = STATE_PLACE elif current_state == STATE_PLACE: place_pose = get_place_pose(latest_target["category"]) move_to_pose(place_pose) gripper.open() latest_target = None current_state = STATE_DETECT这个状态机的逻辑是:先检测,检测到目标才进入夹取;夹取动作里先移动到目标点,再闭合夹爪,等0.5秒让夹爪稳定;然后根据类别决定投放到哪个盒子里,最后回到检测状态。把一次完整动作拆成状态而不是连续调用函数,是因为中间任何一步失败都可以安全回到初始状态,而不会出现“机械臂已经在投放,视觉又触发一次新的抓取”这种竞态条件。
这里有一个可直接照搬的经验:所有 MoveIt 失败都回到STATE_DETECT,不重试。原因是规划失败往往意味着当前姿态奇异或目标点不可达,原地重试大概率还是失败,不如让机械臂回到 home 点重新找目标。省下来的时间足够重新检测一次。
5. 垃圾分拣避坑指南与常见问题排查
5.1 YOLOv5对小件垃圾漏检:现象、原因与解决
现象:塑料瓶、易拉罐这些大目标检测没问题,一旦出现瓶盖、小纸团、电池,模型经常直接忽略,或者置信度低于 0.45 被过滤。
原因:小目标在 640x640 分辨率下占的像素太少,训练时 anchor 和特征金字塔对小目标的匹配不充分;另外如果数据集里大目标占绝大多数,模型学到的大尺度特征会盖过小尺度特征。
解决:第一步把训练和推理的--img从 640 提高到 960,小目标召回率通常会立刻提升。第二步重新聚类 anchor,YOLOv5 训练时会自己聚类,但如果你关闭了--noautoanchor,要确认训练日志里“anchors”是否更新;第三步对小目标类别的图像做过采样或拼接增强,比如把一个大图切成四块训练,相当于强制模型看到更多小目标。还是不行的话,最后再考虑调低model.conf到 0.35,但这个操作会增加误检,建议放在最后。
5.2 MoveIt频繁报Planning failed:现象、原因与解决
现象:控制脚本里arm.plan(pose_target)经常返回失败,机械臂停在原地不动,Rviz 里显示目标点周围一片路径规划失败的报错。
原因:最多的情况是目标位姿不在机械臂工作空间内,比如垃圾放在桌面边缘,机械臂够不到;第二常见的是机械臂末端姿态导致关节奇异,平移能到,但姿态无法保持;第三是碰撞体没排除,比如机械臂模型与工作台面的碰撞体一直在干涉。
解决:先不要改规划器。在 Rviz 里手动把目标点拖到抓取位置,看能不能规划成功,如果手动也不行,说明是工作空间或碰撞问题。我会把抓取动作拆成两段:先运动到目标点上方 10 厘米处,再垂直下降到抓取点,这样既减少碰撞,也降低姿态奇异的概率。如果目标点在可达范围边缘,调整机械臂底座位置或者把垃圾放置区域向内缩 5 到 8 厘米,比折腾规划器参数更省时间。
5.3 机械臂抓取位置总偏差几厘米:现象、原因与解决
现象:YOLOv5识别得很准,检测框稳稳套住目标,但是机械臂下去之后夹爪总是偏一个固定方向,有时候抓空,有时候蹭到垃圾边缘。
原因:这不是识别问题,是坐标变换问题。最常见的是手眼标定矩阵不准,很多人只标定一次就不再验证,等机械臂长期运行或相机位置被碰一下,误差就会暴露出来。其次是把检测框中心直接当成抓取点,忽略了垃圾的实际中心和夹爪夹持中心不重合的问题。
解决:先做一个静态验证:放一个固定物体,运行 YOLOv5 得到像素中心,再在 Rviz 里把机械臂末端手动移动到物体上,记录当前位姿,和视觉推出来的位姿对比,差值就是标定误差。如果差值是固定的,可以在代码里加一个固定偏移量修正;如果差值不固定,那就重新做手眼标定,采集 20 组以上不同位置的样本再求解。另外,夹爪闭合中心可能不在相机识别的物体中心上,需要在夹爪上贴一个标记点,做一次夹持补偿。
5.4 树莓派5部署YOLOv5模型实时性不够:现象、原因与解决
现象:在树莓派5上部署自己训练的YOLOv5模型后,CPU推理速度只有三五帧,机械臂动作一顿一顿,检测结果跟不上抓取节奏。
原因:树莓派5的CPU算力跑yolov5s本身就吃力,如果代码里还用了torch.hub.load加载模型并全程占用 CPU 做预处理,速度会更差。垃圾分拣又不需要实时视频流,检测频率 5Hz 已经足够,机械臂抓取一次至少需要两三秒。
解决:首选把模型换成yolov5n,识别精度掉不了多少,但推理速度能翻一倍。其次把权重导出为 ONNX,用onnxruntime推理,比直接跑 PyTorch 模型更快。再次,图像预处理下手:先把输入分辨率从 640 降到 416,垃圾分拣场景通常拍摄距离固定,416 对小一点的垃圾仍然够用。如果用了树莓派 AI Kit 这类 NPU 加速卡,可以进一步把模型量化到 INT8,但要注意量化后小目标可能更容易漏检,必须重新验证数据集。
另外我建议把检测频率限到 3 到 5 帧每秒,并在机械臂执行时暂停检测,避免无意义地消耗 CPU。机械臂本身按规划轨迹运动,不需要每秒都看视觉结果。
6. 让闭环更稳的三步验证:先静态后动态再回退
6.1 静态回放:检测结果不直接控制机械臂
第一个验证方法是离线的。把一段包含垃圾的录製视频截成图片序列,跑一遍检测脚本,把所有目标框和中心点打印出来,和人工标注对比。这一步只验证视觉,不接通机械臂。如果静态图片里检测位置偏了,立刻能发现是模型问题还是坐标系问题,不用把机械臂拉进来一起背锅。很多项目看着“一运行就乱动”,其实是视觉输出已经错了,机械臂只是忠实执行错误指令。
6.2 手动示教一个黄金抓取位,再验证自动位姿
第二个验证方法是手动示教。把机械臂切到手动模式,用示教器把夹爪移到垃圾上方,调整到一个舒服的抓取姿态,读取当前末端位姿。然后把这个位姿写进代码,让它自动运行一次,如果自动位姿和手动位姿一致,说明坐标变换链路没问题。手动位姿是检验自动位姿最可信的标尺,因为最终机械臂抓得准不准,看的就是这段真实空间关系。
6.3 给运动规划加超时和回退
最后一个习惯是超时与回退。MoveIt 规划默认会搜索很久,我不希望它卡住整个状态机,所以arm.set_planning_time(5.0)后还要包一层超时判断:如果plan返回失败,直接让机械臂回到 home 点,再重新进入检测状态。代码里加一个全局last_error_log,每次规划失败都记录当时的位姿和目标点,连续失败三次就停下来报警,不要再自动重试。自动重试是黑匣子行为,少看日志的代价是机械臂反复运动,可能导致相机位置松动,最终让标定失效,变成一套从头再来的翻车现场。
我自己调试这类机器人时最深刻的体会,是不要贪心一次跑通完整流程。每一个模块先在原地转一圈,确认输入输出符合预期,再往下接。YOLOv5识别不对就回去改数据集,MoveIt规划不对就回去查 URDF 和位姿,坐标偏了就重新标定,每一步都留好日志,最后整套系统反而稳定得很。希望这份拆解和踩坑记录能帮你把垃圾分类机器人这条闭环走通,让你拿到源码和设计文档后不再只是看个热闹,而是真正把它变成能动、能抓、能分类的实物。
希望帮到你。
本文还有配套的精品资源,点击获取