最近刚把一个视觉识别分拣项目从测试环境搬到了真实产线,就是标题里这个“AI驱动的二手机器人智能识别分拣系统”。解释一下,这里说的“二手机器人”不是指机器人本体是旧的,而是指被回收上来的二手设备和机器人零部件——旧电机、旧电路板、旧控制器、减速机这类东西。产线上一大堆杂七杂八的物料混在一起,之前靠老师傅肉眼分类,一天下来眼睛基本废掉,而且不同师傅的标准还不一样。我们做的这套系统就是让工业相机拍一张照片,AI自动判断这是什么物料、应该进哪个料筐,然后机械臂自己抓过去放好。
这个项目看起来只是“深度学习目标检测+机械臂抓取”,但真做完你会发现,算法只是冰山一角,后面还压着手眼标定、通信协议、节拍优化、现场抗干扰一大堆事。这篇我就按从方案设计到产线落地的时间线,把每一步的选择逻辑和踩过的坑讲透,给打算做类似“AI视觉+机器人分拣”的朋友一个完整参考。不管你是刚入门还是已经在调试现场,都不亏。
1. 项目需求拆解与整体方案设计
1.1 产线最真实的需求是什么
很多人一听“AI分拣”,第一反应是拿个相机拍完就开始跑模型。但真实产线给你的需求往往不是“识别准确率要多少”,而是几个特别朴素的数字:
- 每分钟处理多少件(节拍)
- 一共分多少种类别
- 允许抓漏和抓错的比例
- 现有产线能不能停线改造
我们这个项目,物料类别一共8种:小型电机、中大型减速机、旧控制板、散装线束、铸铁壳体、铝合金外壳、橡胶密封件、塑料杂件。节拍要求是每分钟不低于8件,也就是说从拍照到抓走放好,单件周期不能超过7.5秒。准确率要求识别正确率不低于95%,分错的那一档里又不允许出现两种外观特别接近的类别混料。
这些数字直接决定了后面所有选型。如果节拍30秒一件,那很多方案随便做;要是7.5秒一件,相机曝光、模型推理、机器人运动路径、PLC报完信号再动,每一环都得掐着算。所以第一步不是写代码,是跟工艺、产线负责人一起把节拍逐段拆开,确认瓶颈在识别还是抓取。
另外,真实产线的物料不会是整整齐齐排好放在工件盘里的,而是从上一级输送线直接倒进进料斗,堆叠、遮挡、反光、脏污全都来。这意味着不能用那种“拍一张干净图直接识别”的思路,得从源头就把视觉方案定成能扛干扰的。
1.2 视觉方案选型:2D还是3D,打光怎么选
识别这部分最开始的争论是上3D相机还是2D相机。3D相机能拿到高度信息,理论上对堆叠物料的区分更好,但现场节拍7.5秒一件,3D点云获取和配准本身就占掉很多时间,而且成本高出好几倍。后来我们把产线上的实物拉了一批做测试,发现对于这8类物料,2D RGB图像已经足够区分关键特征——电机有明显的外形轮廓和出线口,减速机有法兰面,控制板是绿色基板加金色触点,壳体有铸造纹理,这些用普通工业面阵相机拍彩色图就能学到。
于是定了2D方案:一台彩色工业相机,分辨率500万像素,配一个12mm定焦镜头,部署在进料端上方约800mm高度,视野約600mm×500mm,保证一次能覆盖一个标准料筐上方的最大物料堆。相机前方加了偏振片,光源用两个条形LED灯,一个打在正前方,一个从侧面补光。偏振片这个细节非常关键——旧电机壳体的金属表面会有大量镜面反光,不压偏振的话,白色光斑会把表面纹理盖住,模型识别率直接掉5个百分点。
除了识别,2D方案还少一个麻烦:不需要做点云配准,手眼标定时只要解决像素坐标到机器人坐标的对应关系就行,后面我会专门讲。如果你的物料类别里有大量形状相同但高度差异极大的东西,比如薄垫片和厚法兰混在一起,那还是得老老实实上3D。我们这个项目是靠类别本身的轮廓和颜色特征撑住的。
1.3 机器人型号与工作站布局
机器人选的是小型六轴工业机械臂,负载5kg,臂展参考工作半径800mm左右。为什么不用Delta并联机器人?因为物料是堆叠的散件,Delta适合规则排列的轻小件,而且末端要带视觉传感器的时候,并联机器人的惯性特性不太适合。六轴灵活性高,能覆盖进料斗、识别位、8个分拣料筐这几个位置。
工作站布局我们调了两次才定稿。第一版是相机固定在进料斗上方,机械臂在旁边抓。但实际跑起来发现一个问题:物料倒进料斗以后是堆叠状态,相机拍完一轮,机械臂抓走最上层几件,下一轮再拍时,因为抓走动过,物料位置全变了,得重新识别,但机械臂还没离开视野,遮挡了相机。后来改成“先拍照、再让臂伸进去抓”的时序——相机抓图先缓存,机械臂移动到安全等待点,确认图像采集完成后再进入料斗抓取。这个等待点必须避开视野覆盖区域,不然机械臂本体就被当成物料了。
分拣料筐围在机械臂四周做了半圆形布置,每个筐对应一个品类,机械臂从中心向四周放料。这样布局的好处是每个料筐到中心点的距离差异不大,路径规划简单,不会出现某个筐特别远导致节拍超标。
选型上还有一个容易被忽略的点:机械臂要支持实时通信(我们用的是一套基于TCP的私有协议,也有EtherCAT选项),不能选那种只能离线示教、不能联动外部信号的型号。分拣系统最怕机械臂输出一个完成信号要等200ms,整套循环节拍直接就垮了。
2. AI识别模型:数据、训练与部署
2.1 数据采集和标注里最容易被低估的工作量
模型本身我用的是YOLO系列,因为检测速度要快,而且单帧图里可能有多个物料目标同时出现。很多人以为回去下载个公开数据集就能训,实际完全不是这么回事。二手物料外观差异非常大,同样是小型电机,有的带接线盒,有的不带;有的表面漆层掉了一半,有的全是油泥。公开数据集里根本没有这种“真实世界带伤带脏”的物料照片。
所以数据只能用产线现场照片。我们花了两周时间,架好正式光源,用相机连续采集,一共拿到大概4200张有效图像。采集的姿势很重要:不能只拍排列整齐的图,一定要模拟真实生产状态——把几十个物料混在一起,故意抖动输送线,让它们堆叠、部分遮挡,再拍照。我们大概有六成是这种“乱堆”图,四成是相对单件的图。
标的时候要注意类别边界。最烦的是“旧控制板”和“铸铁壳体”偶尔会同时出现,因为有些控制板装在铸铁壳体内没拆干净,视觉上会看到绿色基板从镂空处露出来。如果按一个目标框去标,同时包含两种特征,模型就学糊涂了。后来我们把规则定义为:只要视野内能看到超过30%的控制板特征区域,就单独标一个控制板目标;否则它只是壳体的一个属性,不额外标注。这个规则很笨,但直接把验证集mAP提了4个点。
类别不平衡也是个坑:散装线束和塑料杂件非常好采,照片多;中型减速机数量少,而且外观又复杂,易与其他类别混淆。我们处理的办法是给减速机那一类做copy-paste增强,把减速机从原始照片里切出来,以不同角度贴到背景图上,人工保证不出现不真实遮挡,生成大概800张扩展样本。注意不能只做水平翻转和亮度抖动,那种增强对于“外观本身复杂”的类别帮助有限。
2.2 模型选型与训练实验记录
基础实验我们对比了YOLOv8的小版本和nano版本。nano速度确实快,在测试机上推理一张图只要不到30ms,但mAP差了不少,特别是小目标(线束、橡胶密封件)几乎掉到82%以下,现场误抓会变多。最后用小版本,推理时间约60ms,识别准确率能到96.5%,完全满足产线要求。推理设备只是一台中端工控机加一张消费级显卡——不需要上工业级显卡,但显存建议不低于8G,因为现场图像分辨率大,批量推理会占显存。
训练参数我们用了几天试出来的组合:输入分辨率640×640,batch size 16,初始学习率0.01,用余弦退火衰减,训练150轮。数据增强上除了常规的马赛克、随机透视,还专门加了“亮度随机压暗30%”和“高斯噪声”。原因很直接,现场的光照会随时间变化,中午阳光从窗户进来会提升整体亮度,夜班灯光又会偏暗;油污的表面在视觉上就有噪声纹理。这一组增强加完,配套工厂“白天开窗/晚上关窗”两种光照模式下,准确率稳定性明显好很多。
训练完成后最重要的不是看训练集loss,而是做一次“现场回测”:把训练时完全没见过的照片一批批塞给模型,统计每个类别的置信度分布。我们发现“铝合金外壳”经常拿不准,置信度在0.5到0.7之间飘,而其他类别多数在0.85以上。这说明模型对该类别的特征学到的不够。解决办法不是盲目加图,而是专门回去拍了铝合金外壳的各种姿态,特别是把外壳的压铸浇口和定位销拍清楚。不要再整批重训,而是用增补样本在预训练权重上继续微调20轮,效果好很多,还省时间。
2.3 模型部署与推理加速的实操配置
模型最终导出成ONNX格式,用支持显卡加速的推理引擎跑。部署时有个关键点:要固定输入图像的预处理参数。训练时YOLO内部会做letterbox,把图像等比缩放到640×640并补灰边,部署时如果代码里没做同样的letterbox,识别率会莫名其妙掉。我们就在这上面吃过亏:刚开始用OpenCV直接resize成640×640,图像被拉伸,细长线束检测直接崩了,后来才发现必须先补边再缩放。
实际的单帧推理流水线是这样:
# 伪代码,展示部署推理的数据流,实际生产用C++封装 import cv2 import numpy as np import onnxruntime as ort session = ort.InferenceSession("sorting_model.onnx", providers=["CUDAExecutionProvider", "CPUExecutionProvider"]) # 关键:先做letterbox,让图像等比缩放+补边,而不是直接resize def letterbox(img, new_shape=640, color=(114, 114, 114)): h, w = img.shape[:2] r = min(new_shape / h, new_shape / w) new_unpad = (int(round(w * r)), int(round(h * r))) img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) dw = (new_shape - new_unpad[0]) / 2 dh = (new_shape - new_unpad[1]) / 2 top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) return img # 抓取的图像先做letterbox再转张量 img = cv2.cvtColor(cam_frame, cv2.COLOR_BGR2RGB) img = letterbox(img).astype(np.float32) / 255.0 img = img.transpose(2, 0, 1)[None] output = session.run(None, {session.get_inputs()[0].name: img})[0] # 后处理:NMS + 坐标映射回原图(letterbox逆变换)这段代码里最容易被忽略的是最后一步“坐标映射回原图”——模型输出的检测框坐标是在640×640的letterbox图里的,要除以缩放比例、减去补边偏移,才能映射回原始500万像素图像坐标。我们第一次联调时漏了逆变换,机械臂抓过去总是偏到目标右上角,就是因为框的坐标不对。
推理加速方面,还有一个“图像采集与推理重叠”的时序设计:相机采集当前物料堆图像后,让机械臂去抓上次识别出来的目标,同时CPU/GPU处理当前图像的推理任务。这样推理的60ms被干活时间藏掉了,整套循环的节拍大约能提升10%-15%。工控机的IO线程单独开一个,负责跟PLC通信,不要跟推理线程争CPU。
3. 机械臂抓取:手眼标定与分拣控制
3.1 手眼标定:为什么矩阵会漂,怎么标才稳
机械臂要抓相机检测到的物料,必须把像素坐标换算成机械臂坐标系下的坐标,这一步靠手眼标定。我们用的是眼在手上(相机装在机械臂末端)还是眼在手外(相机固定在外侧)?这里有一个取舍:眼在手上的好处是相机跟着机械臂走,可以近距离看清物料,对堆叠物料的抓取更友好;但标定过程稍复杂。我们考虑到现场布局和节拍,选了眼在手外固定安装。这样相机和机械臂的坐标系关系是固定的,标定完成后只要机械臂底座不挪位,矩阵不用重算。
手眼标定需要一个标定板,用棋盘格就行。流程很简单但细节极多。机械臂带着末端上的针尖,去点标定板上几个已知角点的物理位置,建立标定板和机械臂坐标系的对应关系;同时相机提取同一棋盘格在图像里的角点,建立标定板和相机坐标系的对应关系。两个关系一连,就能解出相机坐标系到机械臂坐标系的变换矩阵。
但我们实践下来,这个“机械臂点角点”的过程是最容易出错的。推荐的做法不是用针尖一个个去碰,那样依赖机械臂示教的准确性,而是用打印的大尺寸棋盘格放在一个固定位置,让机械臂带着一个定制探针依次接触五个已知角点,每接触一个就记录当前TCP坐标。注意棋盘格一定要贴平,下面玻璃板会反光,建议贴在不反光的亚克力板上。另外,标定取样点不要全部集中在一个小区域,要让角点分布在全视野的各个角落,不然解出来的矩阵外推误差会很大。
标定完的验证方法也重要:把同样一块物料放画面四个角落,机械臂根据视觉坐标去抓,看抓取位置偏差。我们实测四个角留3mm以内的误差算正常,超过5mm就需要重新标定。误差偏大的原因百分之八十是棋盘格没放平或者机械臂TCP参数没校准。
3.2 从检测框到抓取点:中心计算、高度补偿与夹具设计
拿到了像素坐标,不等于能抓。相机只给了2D图像坐标,没有Z方向高度信息。因为我们用的2D方案,抓取高度怎么定?我的做法是分两层逻辑:如果不是堆叠状态,顶部大多数物料都在同一个高度平面(料筐边框高度加上物料平均厚度),机械臂可以直接下降到这个预设高度;如果是堆叠状态,相机会检测到互相遮挡的目标,此时只取综合置信度最高、且与其他框重叠面积最小(即最上层)的目标作为本次抓取对象,避免机械臂去抓被压在下层的物料。这个逻辑简单,但真实产线够用。
抓取点不是检测框中心,这个坑我一开始没意识到。像线束这种长条形物体,检测框是长方形,如果机械臂去抓框中心,很可能抓到中间悬空的部分,一抬臂就掉。所以我们在部署时对每个类别都加了一个“抓取偏移配置”:比如线束,抓到框中心偏一端约1/4长度处,那里往往是线束的捆扎头;电机类,抓壳体中部靠重心的位置;减速机,抓法兰边缘。这些偏移值是我们拿着实物在标定板上反复试出来的,在配置表里维护,跟模型权重分开管理,改起来不用动代码。
末端执行器最初选的是二指平行夹爪,后来发现不行。旧电机壳体很多是圆筒形,二指夹上去容易打滑,而且电力线多余部分会卡进夹爪缝隙。最终换成了三指自适应夹爪加吸盘组合:吸盘用于抓控制板和带光滑面的壳体,三指夹爪用于抓电机、减速机等不规则件。吸盘用真空发生器驱动,抓取控制板这类薄片件时又稳又快。这块的经验是:不要试图用一个执行器搞定所有类别,分类别切换执行动作的成本远比想象低。
3.3 PLC联调与产线时序控制
分拣系统不是单独跑的,它嵌在输送线里,需要跟PLC对话。我们定了完整的握手协议:进料斗物料到位传感器触发后,PLC发“拍照”命令;视觉程序识别完成后,把目标类别、像素坐标、抓取序号通过TCP发回PLC;PLC确认机械臂收到并执行抓取,抓完放料到位再发下一拍。协议里必须要有超时重发机制:如果视觉程序在2秒内没回包,PLC自动进入暂停并报警,防止机械臂空抓或撞料斗。
时序上我们调了很久的核心问题是“视觉工作时间”和“机械臂工作区间”重叠。理想状态下是视觉在机械臂抓上一个目标时就把下一个目标的位置算好,但机械臂在运动过程中会遮挡相机视野。我们的解法是固定时序:机械臂每次抓完放好以后,回到一个叫“拍照位”的固定等待点,这个点在相机视野之外但离料斗很近;PLC在机械臂到达拍照位后触发拍照,相机此时拍到的是没有被遮挡的物料堆。拍照完成后,机械臂马上进入料斗抓取并放料,放完回拍照位,下一张图也在放料期间拍摄。这样每循环一次,视觉时间与机械臂行程时间是重叠的。
写控制逻辑时还要注意并发安全:PLC与视觉程序之间的TCP连接要考虑断线重连,有一次因为网线被现场叉车碰了一下,通信中断,整套线停了十分钟,后来加了心跳包,每两秒互相发一个状态帧,断了自动重连并重新初始化当前状态。
4. 产线实测:稳定运行的问题排查与优化实录
4.1 现场光照变化:白天与夜班的识别差异
产线跑了几天后,第一个冒出来的问题就是白天和夜班识别效果不稳定。白天太阳从车间窗户直射进来,地面反射光会灌到料斗里,物料表面出现大块反光斑;夜班只靠LED灯,色温明显变暖,部分金属件的颜色特征改变。排查办法不是马上调模型,而是先看相机参数是否固定:把这些干扰用“固定曝光+固定白平衡”的方式压住,相机设置为自动曝光是现场识别的大忌——每次环境光变化,图像亮度就会跳,模型在亮度分布上的泛化能力再强也扛不住一天跳十几次。
我们直接在相机端把曝光时间、增益、白平衡全部改手动,固定一组适合当前光源的参数。白天窗户那一个方向,挂一块不透光的遮光帘,把直射阳光挡住。之后又对模型做了一轮适应夜班色温的微调:专门拍了一批夜班环境下的图,做亮度校准后增强进训练集。这里有个经验:不要让模型去适应无规律的自动曝光,而是先把图像采集端“钉死”,再让模型去学剩余变化,问题范围会小很多。
4.2 误抓和漏抓的典型案例:怎么从现象反推原因
整理现场问题记录时,发现误抓和漏抓各出现两类典型情况。一是把“铝合金外壳”抓成了“铸铁壳体”,原因不是模型错,而是实际抓取时机械臂的吸盘先吸到空压机气管,导致真空度不足,物体在中途掉落,掉落后又恰好落到旁边一个铸铁壳体筐里。这种问题如果只盯着模型调参,调到天荒地老也解决不了——根因在末端执行器。
二是“散装线束”经常漏检。线束卷成一团,颜色跟黑色料筐背景非常接近,置信度低到0.4以下。我们加了两种手段:把料筐内表面换成反光率低的哑光黑,消除部分反光;给相机拍摄背景单独加一层蓝色底衬,线束大多偏黑或偏棕,蓝色底衬能显著拉大类间色差。改完以后,线束类别的检测置信度从0.4直接升到0.78。这是典型的“用物理手段降低视觉难度”的思路,比纯堆数据高效得多。
4.3 节拍不达标时的优化顺序
刚开始联调时,单件周期一直在9.2秒左右,离目标7.5秒差得远。我们按耗时分布逐个优化:拍照占0.3秒,模型推理0.06秒,TCP通信约0.1秒,机械臂运动占最大头约5秒,放料和回拍照位又占3秒多。第一个优化是把机械臂的关节速度从80%提到95%,同时调整路径点,走圆弧过渡而不是每个点都精确到位,这一下省了1秒。第二个优化是让放料路径不必每次都回拍照位,而是放完料直接斜插回拍照位的下一个必经点,把两次定位合并成一次连续运动,又省了0.5秒。
入口料斗的物料堆积形状也会影响节拍:物料堆得越尖,机械臂每次抓取需要额外调整夹爪角度的概率越大。我们给进料斗加了一个振动匀料器,每隔几拍让料斗振动一下,把物料从尖堆变成更薄的铺层,机械臂的抓取成功率上去了,动作时间反而更稳定。最终做到平均7.1秒一件,达标。
4.4 常见故障速查表
现场运维的人不需要懂算法,但需要一张能对着排查的表。我整理了我们项目用得最多的故障排查表,贴在这里供参考。
| 故障现象 | 可能原因 | 快速排查步骤 | 解决方案 |
|---|---|---|---|
| 机械臂抓空 | 目标物在拍照后被移位 | 观察拍照后与抓取前是否有输送带震动 | 调整拍照和抓取间隔,增加机械臂进入前等待 |
| 抓取位置明显偏移 | 手眼标定矩阵漂移 | 抓取验证块检查四个角误差 | 重新标定,确认棋盘格与机械臂坐标原点 |
| 识别置信度忽高忽低 | 自动曝光/白平衡开启 | 查相机固件参数是否为手动 | 固定曝光、增益、白平衡 |
| 同材质不同类别混淆 | 特征冲突 | 截取样本做特征可视化 | 调整标注规则,增加关键特征特写样本 |
| 通信超时导致停线 | TCP断线 | 查网线状态、心跳包日志 | 加断线重连和状态缓存 |
| 控制板被吸起后滑落 | 吸盘与板面不贴合 | 检查吸盘密封圈与板面平整度 | 换小直径吸盘,增加放气延时 |
遇到解决不了的问题,一定先记录现场的照片、机械臂报警码、PLC状态日志三样东西再动手。没有这三样,后面分析全靠猜,效率极低。
最后说一个小经验:如果要从零复现一个类似系统,千万别一上来就买全套设备。我们第一版标定时还在用实验室的简陋料筐和一台旧机械臂,先把“拍照-识别-下发坐标-机械臂抓取”这条最小链路跑通,证明准确率和节拍有潜力,再谈换正式机械臂和改造产线。把最小闭环跑通,比什么都重要。这套系统正常运行之后,我也更加确信:深度学习模型只是整个分拣系统里的一个模组,真正决定项目成败的,是光路、机械、时序和现场工程这些“看不见”的功夫。