“M8内六角螺栓,直径8毫米,在1280×720的画面里也就占30多个像素。就是这30多个像素,让我前面那套识别方案在产线上连续翻车,漏检率一度爬到2%以上,后面直接卡了一条装配线。项目名里的那个‘2’,就是从这次翻车里长出来的——实时识别螺栓2,本质上是一套针对紧固件小目标、高反光场景重新设计的视觉检测方案。”
这套方案要解决的不只是“能不能认出螺栓”,而是三件事叠加:在产线节拍内完成识别、在金属强反光下保持稳定、在多规格混线时还能分清楚型号。如果你是做机器视觉的工程师、想在边缘设备上跑实时目标检测的开发者,或者正被“小目标+反光+速度”这三座大山折磨,这篇内容应该对你有用。下面写的内容来自我自己的项目复盘,所有数据都来自模拟环境验证,具体参数和配置只做思路参考,别直接当成你们产线的验收标准。
1. 为什么会有“实时识别螺栓2”:v1版本的三个现场短板
1.1 节拍不达标:单帧推理时间拖了整条产线
v1用的是通用目标检测模型,直接在工控机上跑960×960输入,单帧推理稳定在250毫秒左右。当时产线要求的节拍是单件120毫秒,也就是说从相机抓图到PLC收到结果,不能超过120毫秒。v1在最顺利的情况下也要180毫秒以上,这还没算图像采集和通信的时间。节拍不达标带来的直接后果是:产线要么降速运行,要么堆料停机,生产效率被砍掉将近一半。
为什么v1会这么慢?拆开看有三个原因。一是任务本身叠加了“识别多个规格螺栓+区分方向”的需求,模型参数不敢压太狠;二是后处理NMS在候选框多的时候会吃掉不少时间;三是采集软件用的是按帧轮询模式,图像还没传完就开始做解码预处理,CPU和GPU抢资源。这里面每一项单看都不是大问题,但叠在一起就把延迟顶过了红线。v2在设计时列的第一条硬指标就是:整链路单帧必须压到120毫秒以内,最好留出20%余量。
1.2 小螺栓漏检:目标太小是原罪
M6的螺栓头直径只有6毫米,在镜头工作距离下投到传感器上,也就是十几个像素。通用检测模型的neck和head设计对中等尺寸目标最友好,对小目标天然不敏感。v1在验证集上的mAP看着有0.8,但一统计小目标类别的召回率,只有六成出头。产线上真正出问题的大多就是M6、M8这些小规格。
这里要解释一个很多人忽略的点:目标检测模型里的“小目标”问题,不光是分辨率低,更关键的是浅层特征图里的语义信息不足。模型的深层特征图负责识别“是什么”,浅层特征图负责定位“在哪里”,小目标几乎全靠浅层特征来撑。v1没有给小目标单独加监督,网络训练时会把大部分注意力放在大目标上,小螺栓在特征图里对应区域太小,梯度贡献被大目标淹没。v2的第一个改动方向就是重新设计检测头,把小目标监督单独提了一路。
1.3 反光和运动模糊:模型在图库上“分数虚高”
离线验证的时候,v1用的测试图库是专门去现场挑的“干净图”:光源稳定、螺栓静止、背景没有杂物。结果一到现场就露馅,金属螺栓头部的高反光让画面里有大块过曝区域,螺槽的纹理特征被白光吞掉;转盘上的螺栓还在缓慢移动,拖出一点点运动模糊后边缘直接虚化。模型在这两类图上频繁出现漏检和误检,抽检漏检率2.1%,远超当初定的千分之一目标。
这件事给我的教训是:离线图库的“平均质量”完全不能代表现场的真实分布。v2从数据侧和算法侧同时做了干预,数据侧是重新采集了一批带过曝和运动模糊的真实样本,算法侧是加入了专门的抗干扰增强。后面会展开讲。
2. 数据采集与标注:螺栓识别的难点全在反光和细节
2.1 光源和相机位姿:先决定上限再谈模型
金属反光是做视觉检测最头疼的问题之一,而它恰恰可以通过硬件手段先缓解一大半。v2在打光上做了三件事:把直射光换成低角度环形光,让螺栓头边缘形成轮廓反差而不是镜面反射;在镜头前加偏振片,过滤掉大部分镜面反射光;相机曝光时间从常用的5毫秒压到1毫秒以下,避免亮部过曝。
这三件事的效果立竿见影。镜头前的偏振片一加,画面里原来白花花一片的螺栓头部立刻露出了螺槽和边缘纹理。你可能觉得这是“传统视觉”的思路,和目标检测没关系,但恰恰相反——深度学习模型对输入图像质量的下限要求其实很高,光源调好了,训练出来的模型稳定性会明显上一个台阶。这里有个很实用的建议:采集训练数据的时候,刻意把光源开成高中低三档,每种档位都拍一批,让模型见过不同亮度条件下的螺栓。这样上线后即使光源控制器漂移,模型也不会立刻崩。
相机位姿方面,我踩过一个坑:只用一个垂直俯拍视角采集数据,模型确实在这个视角下检测很好,但产线调试时稍微调了相机支架的倾斜角,检测率直接掉了一截。v2补了三种视角的数据:垂直俯拍、45度斜拍、侧面低角度拍。如果你的工位相机角度已经固定,至少也建议拍两组不同角度的数据做测试,验证一下模型的位姿鲁棒性。
2.2 标注要避开的三个坑
标注是数据质量的地基,但螺栓这个场景有它的特殊性。第一个坑是水平框在螺栓斜置时框进太多背景。螺栓头是圆的,斜着停的时候水平标注框的四角会落到背景板上,模型训练时会把这些背景特征学进“螺栓”的语义里。v2的折衷方案是:对正常位姿的样本用紧密水平框,对斜置样本和部分遮挡样本,先用裁剪放大的预处理再标注,而不是直接在原图上硬拉一个大框。
第二个坑是模糊样本别硬标。一张已经运动模糊到看不清螺栓槽型的图,你凭经验标注了,模型就会学到一个“错误的模板”,在清晰图上反而容易误判。v2的原则是:模糊样本要么放到专门的分桶里做弱监督,要么直接丢掉,不要混进主训练集。
第三个坑是空背景样本被忽略。很多项目只标“有螺栓的图”,完全不拍空背景。这会导致模型在没有任何螺栓的背景板上也强行给出检测框,误报率偏高。建议每个场景至少拍5%到10%的空背景图,标签为空,让模型学会“这里什么都没有”。
2.3 数据增强真正见效的三个操作
螺栓检测里,通用增强操作很多,HSV扰动、随机裁剪、翻转、旋转都有用,但实测下来最容易解决现场痛点的是三个。第一是模拟过曝增强,随机把图像亮度拉高并压缩高光区域细节,训练模型在反光下的抗性;第二是运动模糊增强,用随机方向和高度的模糊核处理图像,对应转盘运动导致的拖影;第三是对小目标做细节保留,标准随机裁剪会频繁把螺栓主体裁掉一半,让模型学到错误的局部模式。v2的增强策略是:对标注目标较小的情况,先用随机缩放在整图内重贴,再裁剪,保证每个批次里都有足够多的小目标正样本。
这三个增强看起来简单,但它们直接影响的是模型在“反光、模糊、小尺寸”这三个崩溃点上的表现。当时模型加了这三项之后,现场漏检率从2.1%往下掉到了0.5%附近,大家都松了一口气。
3. 模型选型与训练调参:速度精度平衡的一次系统性实践
3.1 选型逻辑:从通用检测器到轻量级检测的取舍
v1直接套用了当时通用检测器里精度最高的那一档,事实证明在产线节拍面前走不通。v2选型时列了个对比表,把备选方案分了三类:大型通用检测器、中等规模轻量检测器、传统视觉+分类器组合。大型检测器精度确实高,但推理时间普遍在200毫秒以上,直接出局。传统视觉方案在干净场景下速度快,可一旦过曝或背景变化,鲁棒性完全扛不住,也放弃了。
最后选定的是中等规模的轻量级单阶段检测框架,配一个专门加强的小目标检测头。选它的理由有三条:一是单阶段结构天然比两阶段快;二是这类框架的预训练权重在公开数据集上经过考验,迁移到螺栓场景上手容易;三是社区开源工具链成熟,导出到推理加速框架的坑少。这最后一条在工业部署里非常重要,一个模型在训练集上跑得再准,如果推理框架不支持它的算子,上线阶段会把人折磨死。
3.2 小目标漏检怎么治:特征层和损失函数的具体改动
v2在检测头里加了一条浅层特征输出,专门负责小尺寸目标的检测。模块层面其实不复杂,但训练时有两个细节必须注意。一是浅层特征图的通道数不能贪多,否则推理阶段计算量飙升,v2反复试下来,在速度可接受的前提下选了前两层的特征拼接。二是训练时对小目标分支单独设置权重,让它在整体损失里的占比不随batch变化被稀释。我习惯的做法是统计每个batch里小目标的个数,动态放大它们对应的损失系数。
损失函数这边,v1用的标准边框回归损失在高宽比收敛时总是不太稳定。v2换成了带几何匹配的损失函数(SIoU这类),核心改进是把预测框和真实框的宽高比惩罚项加进去,解决螺栓这种窄长目标在回归时“横竖不分明”的问题。改完之后,检测框的位置抖动明显减少,尤其是内六角螺栓的六边形轮廓定位更准了。
这里补充一个训练策略:先用公开预训练权重在低分辨率下微调50轮,让模型先学会提取通用特征;再切到目标分辨率继续训练80轮;最后把浅层小目标分支unfreeze,用更低学习率精调20轮。三个阶段的思路是“先学得会,再学得准,最后学得细”。这套流程比一步到位直接跑几百轮稳定得多。
3.3 训练评估:mAP只是起点,现场漏检率才是终点
不少团队在模型迭代时只看mAP曲线,这是一个典型的误区。mAP是多个类别、多个IoU阈值下的综合平均,一个螺栓类别识别得很差,只要另一个大类识别得好,mAP也可能拉高。v2的评估体系加了三个更细的指标:小目标类别单独统计的召回率(Recall@Small)、重叠度高阈值下的检测框定位精度(mAP@0.5:0.95)、以及模拟现场光照和运动模糊条件下的“场景抽检漏检率”。
在v2的迭代过程中,后面这个“场景抽检漏检率”帮了大忙。模型在标准测试集上mAP已经接近0.9了,但换到过曝增强的验证子集里,漏检率还是偏高。因为这个指标单独被卡住,团队才沉下心来补了光源偏振和增强策略,而不是提前上线。建议你的项目也把“最接近现场条件”的验证子集做成固定测试集,每次迭代都盯住它,这比盯着mAP曲线有用得多。
3.4 训练阶段踩过的两个坑
第一个坑是类别不均衡。产线上M10螺栓的数量远大于M6小螺栓,常规采样训练时模型几乎被M10的样本“带跑”,对M6的检测头分支没有准确的梯度更新。解决办法是类别加权采样,每个batch里让各类别数量大致均衡,而不是按真实分布。第二个坑是标注噪声。人工标注的框和螺栓真实边缘之间经常有一两像素的错位,训练时模型被迫在这些噪声上反复拟合,损失曲线容易出现“阶段性平台”。v2后来加了一道标签清洗流程,统计每个标注框和周围样本的一致性,偏差过大的框重新人工核对。清理一圈之后,同样的训练配置下mAP又涨了近两个点。
4. 实时性优化:从单帧快跑到全链路低延迟
4.1 推理侧的四板斧:量化、剪枝、固定shape、NMS简化
实时性优化的第一站是推理本身。v2的模型在PyTorch里裸跑大概是110毫秒,经过四步改造压到了58毫秒。第一步是把模型导出为通用推理加速框架的格式,并开启半精度推理,因为螺栓检测任务对数值精度不敏感,半精度损失几乎可以忽略,但速度能提升约三成。第二步是剪枝——模型的网络里有些卷积核通道对输出贡献非常小,按权重幅值和激活统计剪掉了一部分冗余通道,模型体积小了约20%。
第三步是固定输入shape。很多推理框架在动态尺寸下会频繁重新分配内存和调度kernel,固定成640×640或1280×1280之后,显存分配稳定了,推理延迟也低了。这里有个细节推理建议:如果螺栓宽度固定,优先用1280×1280的分辨率配合固定shape,小目标召回率的提升很明显;如果节拍更紧,再用640×640求速度。第四步是简化NMS,把按类别分别执行的NMS合并成一个类别无关的全局NMS,候选框数量多的时候省出的时间非常可观。
4.2 相机、采集、处理全链路时延拆解
很多人只优化模型推理时间,却忽略整条链路的延迟。v2上线前做了一次完整的时延拆解,数据大致是这样的:
| 环节 | 耗时 | 说明 |
|---|---|---|
| 相机曝光 | 2ms | 压到最短,配合偏振光源 |
| 图像传输 | 8ms | 千兆网口传输,避免丢帧 |
| 解码与预处理 | 5ms | GPU上做resize和归一化,不走CPU |
| 模型推理 | 58ms | 半精度+剪枝+固定shape后 |
| NMS后处理 | 3ms | 全局NMS,按得分截断 |
| 结果通信输出 | 2ms | 以标准协议发给下位机 |
| 合计 | 78ms | 节拍120ms,留出约35%余量 |
这个表做完,全团队对痛点一目了然:模型推理占大头,其他环节加一起才20多毫秒。即便后续需要增加更复杂的识别逻辑,余量也足够撑住。强烈建议你在做类似项目时先做一次全链路时延拆解,别只盯着模型。
4.3 触发式识别和状态机过滤:避免每帧做全套决策
产线画面里大部分时间是“转盘转到下一工位,没有螺栓”或者“螺栓在移动中”,如果每帧都跑一次完整推理,算力浪费很大。v2在采集端加了光电传感器触发模式:只有螺栓运行到指定拍照位置时才触发相机抓帧,其他时间相机休眠。单线工况下,这直接把平均功耗和发热降了下来。
如果还是遇到连续帧场景,建议搭配状态机而不是每帧独立判断。v2里用了一个“两帧确认、连续三帧不确认才告警”的规则:第一次检出时先作为候选,下一帧再次检出且位置变化在合理范围内,才正式输出结果;连续丢了两帧也不急着报漏检,第三帧若重新检出则自动恢复。这套机制显著降低了偶发的误报和抖动导致的漏判,代价是逻辑复杂度变高,但换来的是现场稳定性。
5. 部署到产线:干扰处理、结果闭环和现场避坑
5.1 选工控机和模型导出时的注意点
选硬件时不必盲目追求高端芯片,关键看两件事:推理加速框架的支持度,以及I/O接口的齐全度。v2用的是一台无风扇的工控机,配上边缘级显卡,在1280×1280输入下半精度推理跑到了前述的58毫秒。如果你用纯CPU,也未必跑不动,但建议输入降到640×640,并测量实际节拍再做决定。
模型导出有个很实际的经验:导出前把输入尺寸和batch size固定,推理框架里很多算子会针对固定shape生成专用kernel,性能差异可能在10%到20%之间。另外,导出来的模型不要只在本地跑一次验证就完事,一定要在目标设备上跑一段长时间压力测试,因为工业场景下长时间运行引发的显存泄漏和微小的资源碎片问题,在短测试里根本看不出来。
相机选型方面,优先选全局快门的工业相机,避免卷帘快门在运动场景下产生果冻效应。镜头要固定焦距、手动光圈,装配后即锁定,不要用自动光圈镜头,它在产线亮度变化时会自行调整曝光,直接干扰识别稳定性。
5.2 现场三大干扰:反光、震动、背景杂物
偏振镜加上低角度光源之后,反光问题基本可控。万一还有残余高光,另一个通用技巧是“两次曝光融合”:同一位置拍短曝光和长曝光两张图,做HDR合并后输入模型。这个方法在强反光场景下很有效,代价是吞吐量减半,适合节拍不那么紧的工位。
震动问题在小螺栓识别里会被放大——画面轻微抖动,螺栓边缘就模糊了。v2的做法是给相机支架加了橡胶减振垫,同时把相机帧率提到最高,用连续多帧中质量最好的一帧做判断。背景杂物则靠划定ROI区域来处理,让模型只关注工作窗口,转盘上的其他金属零件不会进入检测范围。
5.3 结果怎么反馈给PLC和执行机构
模型输出的是“螺栓所在位置和类别”,但要真正驱动产线,还要把结果变成控制信号。v2通过标准工业通信协议把检测结果队列发给下位机:包含工位号、检测时间、类别置信度、检测框的像素坐标和“OK/NG”标记。下位机收到NG信号后,按预设逻辑执行分流或者停机。通信这一层最容易踩的坑是延迟抖动,v2用循环缓冲和超时重传机制来规避,确保PLC侧拿到的不是过期数据。
报警逻辑这里我想多说一句:不要单帧NG就立刻停机。螺栓在运动状态下一帧识别失败是正常现象,v2设了个“连续N帧未检出且置信度低于阈值才触发停机”的规则,N根据产线节拍和停机成本来调。这个规则有效避免了大量误停,但在安全性要求极高的位置,需要评估有没有出现过漏报未拦截的风险,再决定是否调整。
6. 实测指标对比与下一步可以做的扩展
6.1 从v1到v2的指标变化
拿v2在模拟产线条件下的实测表现来对比,整个系统的改进一目了然:
| 指标 | v1 | v2 |
|---|---|---|
| 单帧推理耗时 | 260ms | 58ms |
| 整链路耗时 | 380ms | 78ms |
| mAP@0.5 | 0.78 | 0.93 |
| 小目标类别召回率 | 0.61 | 0.88 |
| 现场抽检漏检率 | 2.1% | 0.12% |
| 误报率 | 1.8% | 0.37% |
单看mAP涨幅似乎不算大,但真正关键的是现场抽检漏检率从2.1%降到了0.12%这一档。这再次印证了:脱离现场条件谈mAP没有意义,数据采集、光源改善和增强策略的工程优化,对产出质量的影响往往比模型结构本身更大。
6.2 这个方案还可以怎么扩展
v2完成之后,后续还有几个明确可做的事。第一是从2D识别延伸到六维位姿估计,加一个回归头输出螺栓头部的平面旋转角和倾斜方向,这样可以给装配机器人提供抓取角度,意义比单纯识别大得多。第二是和拧紧工具联动,视觉确认螺栓是否到位后直接把结果传给拧紧枪控制器,形成一个“识别-拧紧-复检”闭环。第三是多工位相机联动,把几个工位的识别结果汇总到统一中台,做整线级别的质量统计。这几个方向都建立在同一个前提上:当前的实时识别链路已经稳定,才有余量去叠加更复杂的感知逻辑。
最后说点个人体会。做这类识别项目,我最深的感受是:模型只占一半,另一半是数据、光源和工程管线。如果你正在复现类似的方案,给你一个最实际的建议:先把现场的光源调好,再回头调模型。光源不行,后面再好的模型都在补数据缺陷。另一个小技巧是把验证集里故意塞入一批过曝和运动模糊的图,每次迭代都盯着这批图看,它能提前暴露你上线后才会遇到的坑,比什么指标都管用。