最近团队在一条3C结构件产线上被误检折磨得不轻。报表上99%的整体准确率看起来很漂亮,实际上每天过检几百只,工人宁可相信人眼也不信视觉系统。工业视觉的误检问题,从来不只是模型指标的事,更是一整套工程问题。
这个项目前后折腾了两个月,我把误检率从接近15%压到了5%左右。真正起作用的不是换了个更大的模型,而是四个工程技巧:成像确定性控制、样本治理、后处理规则、在线反馈闭环。这篇踩坑实录第三篇,把这套能落地的打法完整拆开讲,适合正在做工业视觉检测、被误检率搞到头大的朋友参考。
先交代背景。产线做的是连接器外观检测,视野里有金属面、塑料卡扣、焊点。原系统检出缺陷后做良品判定,但工人反馈每天都有一堆“假缺陷”:金属反光被当成划痕、水渍被当成脏污、正常公差边缘被当成缺料。过杀率一高,产线为了保产能只能把视觉系统屏蔽一半,最后等于人工检测。这种场面,做视觉的兄弟一定懂。
1. 先盘点误检来源,别急着调参
一开始团队的做法很直接:误检多就调阈值。阈值从0.5拉到0.7,过杀少了,漏检开始冒头。后来又试过换特征、换模型,收效都有限。折腾了一周才意识到,如果不能先把“误检是从哪来的”这个问题拆清楚,所有调参都是在碰运气。
1.1 实际产线上的三类高频误检
第一类是金属表面的镜面反射。料件在工位上来料角度稍微偏一点,打光后的反光斑就会移位置,模型把反光斑当成划痕。这类误检占了最初误检量的大约四成,而且很难靠刷训练数据解决,因为每次的角度都在变。
第二类是来料批次差异。同一型号连接器的表面纹路,一批和下一批会差不少。旧批次训练出来的模型遇到新批次的正常纹理,直接报成“表面异物”。这类问题最气人,因为样本图看着很规范,模型就是学偏了。
第三类是负样本本身就不纯。早期采集的训练集里,有些标注人员把正常件的轻微按压痕标成了缺陷,模型学了一堆“伪缺陷模板”,上线后反而把真正的轻微划痕漏掉,把正常的按压痕标红。
1.2 误检的四层来源与对应策略
我把误检来源归成四层,逐层去查,排查效率高了很多:
| 来源层 | 典型表现 | 解决思路 |
|---|---|---|
| 成像层 | 环境光漂移、镜面反光、白平衡抖动 | 打光固化、频闪抑制、参数锁定 |
| 数据层 | 标注错标、负样本覆盖不足、正负样本失衡 | 样本清洗、难例挖掘、增强可控 |
| 模型层 | 固定阈值不适配、小目标误判 | 动态阈值、后处理约束 |
| 业务层 | 来料批次变化、产品改型、节拍压力 | 在线反馈闭环、灰度发布 |
业务层很容易被忽视。你花两周调好的模型,新批次料件一到可能就打回原形。这不是模型不够强,而是工业现场本身处在持续变化里。所以后面所有技巧,本质上都是围绕“如何让系统在变化中仍然稳定”来设计的。
2. 技巧一:成像确定性控制,把“环境”变成“实验室”
工业视觉有一个默认前提:图像质量稳定。一旦图像本身不稳定,后面再强的算法都是无根之木。所以第一刀我砍在成像上。
2.1 环境光是最隐蔽的毒药
车间白天和晚上环境光差异很大,顶灯、窗外阳光、旁边工位的弧光焊渣,都会进入相机视野。最初影像是供线边屏幕看的,没做遮光,同一只连接器我们测过灰度直方图,上午和下午峰值能差30%。模型在上午学到的特征,下午就开始抖。
解决思路上不是完全黑箱化,而是“确定性化”。我做的第一件事是在工位四周加遮光帘,把太阳光和直射灯光挡住;第二件事是给光源控制器接上频闪触发,让光源只在相机曝光的瞬间点亮高亮度脉冲。频闪的物理原理很简单:环境光在曝光窗口内是持续均匀的,而脉冲光源可以在几十到几百微秒内提供极高亮度的光。曝光时间设到100到500微秒这个量级时,环境光贡献的能量就微乎其微了。
这个改动上线后,同一料件在不同时间段的成像直方图方差降了一个数量级。后续所有阈值、规则才真正有了“稳定输入”。
2.2 打光方案:角度、波长、频闪
关于打光,我没用太花哨的方案,核心是匹配检测目标的材质和缺陷类型。
金属反光严重的表面,用低角度环形光加偏振片。低角度光以掠射角打到平面,镜面反射不会直接进入相机,一旦表面有细微划痕,光线会在划痕边缘发生散射,缺陷位置会变亮。这个对浅划痕的检出效果极好,但有一个副作用:工件边缘也可能形成亮边,容易被误报。
高反光的平面区域,我还试过同轴光。同轴光通过分光镜让光线垂直入射,再垂直反射回相机,对反光平面上的凹坑、划痕响应很干脆,但背景往往偏暗,对塑料卡扣上的弱缺陷不友好。
实际选型我是按照“缺陷类型→材质反射特性→光源角度”这个顺序倒推的。如果检测目标是“划痕”,优先低角度环光;如果是“平面脏污”,优先穹顶光做无影照明;如果是“金属零件的焊接轮廓”,可以考虑同轴光。
2.3 相机参数锁定与漂移监控
频闪和遮光解决的是外部干扰,相机参数锁定解决的是内部漂移。我把曝光时间、增益、gamma、白平衡全部从自动改成手动,一次标定后存成配置文件,设备每次启动都加载同一份参数。
更关键的是加了一个“成像健康检查”。每天开机先用标准块拍一张图,计算灰度均值和方差,对比黄金基准。如果偏差超过5%,就触发报警提示现场处理光源或更换衰减严重的灯珠。这一步看起来土,但非常管用。有一次报警发现灰度整体往下掉,查了半天是光源老化,亮度衰减了将近两成,误差率已经开始抬头。没有这个监控,问题只会等到产线投诉才暴露。
提示:这类漂移监控只需要在系统启动或换班时跑一次,代码量不大,但能把很多隐性故障提前暴露,强烈建议安排上。
3. 技巧二:样本治理,先把训练集洗干净
打光稳定后,误检率有下降,但离5%还有距离。接下来问题集中在数据和模型端。
3.1 标注清洗的三步操作
旧标注集里确实混着不少垃圾。我组织了一次彻底的标注复核,流程分三步。
第一步做自动筛查:用当时的模型跑一遍所有训练图,把模型置信度与人工标签不一致的样本全部挑出来,通常这类样本要么标签错,要么图像本身有争议。
第二步做交叉复核:让两个标注员独立复核这批样本,对有分歧的样本提交给工艺工程师判断,以工艺标准为最终依据。这一步特别能治“伪缺陷模板”,比如某个区域允许有轻微压痕,工艺上不算缺陷,但之前标注人员不知道,把它标成缺陷。
第三步做标签规范化:统一缺陷类别定义,细化判断标准,把“轻微”“明显”这种模糊词换成尺寸或形态量化描述。
清洗完之后,训练集的整体正确率高了不少,第一轮训练效果就有明显改善。这一环节没有捷径,前期偷的懒后面都会变成误检。
3.2 难例挖掘:让模型自己告诉你它怕什么
只清洗旧样本还不够,因为真正让模型误检的往往都是“没见过”的样本。这里我用了目标检测里很经典的难例挖掘思路。
具体操作:把线上采集的一周正常产品图像跑一遍现有模型,把置信度分数在阈值附近、假阳性最高的图像按分数排序,挑出前500张,人工确认后加入训练集。这个过程重复三轮。
第一轮加进去的主要是反光斑和金属纹理,第二轮开始出现一些很边缘的光影,第三轮几乎都是只有工艺工程师才能一眼判断的“假缺陷”。每一轮回灌后,模型对正常纹理的敏感度会明显下降。
难例挖掘的本质是让模型看到“哪些东西是干扰”,而不是只看到“缺陷长什么样”。很多误检表面上像模型问题,背后其实是对正常样本表达不足。
3.3 数据增强的度:别把正常变异变成缺陷
刚开始我把疼痛撒在增强上,亮度抖动、加噪、旋转、裁剪全上,结果误检不降反升。原因很简单:过强的增强会把“正常纹理”扭曲成“异常特征”,模型反而学会了从噪声里找缺陷。
后来我把增强幅度严格限定在生产真实波动范围内。亮度抖动只在正负10%到15%,这个量级模拟的就是光源老化和批次色差;高斯噪声sigma取2左右;旋转不超过正负5度,因为实际工位角度偏差不会更大。超出这个范围的增强,不是在提升泛化,而是在制造幻觉。
样本比例上,我一开始正负样本比是1比10,背景负样本极多,正样本不足。后来把比例调整到1比3左右,并确保每个缺陷类别都有数量相近的代表。这种均衡让模型不再偏向“什么都是负样本”,对漏检率的控制很有帮助。
注意:如果模型误检集中在某一类缺陷,先看这类缺陷的样本是不是太少。大量正样本叠加增强,效果往往比盲目加模型参数更好。
4. 技巧三:阈值与后处理规则,把AI输出变成工程约束
模型训完只是开始,真正决定产线体验的是输出一侧的工程约束。这里我用了三个手段。
4.1 阈值不是拍脑袋,用成本曲线定
很多团队用默认的0.5当阈值,这是最偷懒的做法。不同缺陷类别对过杀和漏检的成本完全不一样:漏掉一个焊点不良可能造成整批退货,过杀一个只是返工浪费。我把两类成本折算成一个成本函数,然后绘制P/R曲线,在曲线上找总成本最低的点作为各类别阈值。
具体到这条产线,划痕类缺陷的漏检成本高,阈值设在0.4左右,宁可多过杀一点;表面脏污类缺陷误检容忍度低,阈值拉到0.7。最终得到一个“分类型动态阈值表”。有了这个表,就不再是全局用一个固定分数卡所有东西了。
4.2 检测结果的后处理五件套
模型输出检测框和mask之后,我在后端加了几条硬规则,看似简单,削掉的误检非常多。
第一是连通域面积过滤。很多误检是单像素或几像素的噪声点,模型却给了高分。连通域面积小于5像素的直接丢弃,这个动作就把纯噪声误检干掉大半。
第二是ROI约束。检测结果必须落在产品有效区域内,超出边界的框直接丢弃。有些误检根本不在工件上,而是背景里的夹具反光、传送带缝隙,这类问题模型会犯傻,规则不会。
第三是形状约束。划痕应该是细长结构,长宽比有一个合理范围;脏污趋向于片状。如果形状特征和缺陷类型不符,直接否决。
第四是区域豁免。某些区域工艺上允许少量毛刺或纹理,属于正常外观,在后处理里豁免。这个必须和工艺工程师一起确认,否则容易又漏检。
第五是类别互斥规则。同一位置不可能同时是“划痕”和“异物”,如果两个类别同时出框且交叠面积大,取置信度高那个,另一个丢掉。
下面是一个简化的后处理伪代码,供参考:
def postprocess(detections, roi, cfg): results = [] for det in detections: if det.conf < cfg.threshold[det.label]: continue if not inside_roi(det.bbox, roi): continue if area(det.mask) < cfg.min_area[det.label]: continue if not shape_ok(det, cfg.shape[det.label]): continue results.append(det) return suppress_overlap(results, iou_thresh=cfg.iou_thresh)这些规则看起来是在“补模型的锅”,但本质上它们是把工程经验和工艺知识固化到系统里。模型负责学习特征,规则负责兜住底线,两者互不替代。
4.3 从单帧判定到多帧一致性
这条产线的节拍允许拍多张图,我给视觉系统加了多帧投票机制:同一工件在运动过程中采集两到三帧,缺陷至少在两帧中被同时检出才判定为真缺陷。
多帧投票对反射类误检特别有效。反光斑的位置会随工件移动变化,单帧可能高分,连续帧的检出位置却不稳定;而真实划痕每次出现的位置相对固定。
代价是计算量翻倍或三倍。所以我把投票逻辑保留在缺陷候选区域,而不是整个画面重跑,这样既稳住了误检,又保住了节拍。
5. 技巧四:在线反馈闭环,让系统越用越稳
前三个技巧做完,误检率已经降到6%左右。最后这1个点,是靠在线反馈闭环磨下来的。
5.1 工人反馈按钮与数据回流
在现场工人工位上加了两个按钮:“假报警”和“漏掉了”。工人点到系统输出的缺陷框时,可以一键反馈“这不是缺陷”;当产线巡检发现真正漏检时,也可以标记“这里漏掉了”。
点击按钮的同时,系统自动保存当前图像、检测框、置信度和产品编号,全部回流到样本服务器。这一步的价值在于:你不用派算法工程师天天蹲产线,现场工人就是最便宜、最高效的数据标注员。
回流数据每天大约两百到三百条,量不大,但质量极高,全是模型在真实环境中犯的错。我每周用这批增量数据做一次小规模微调,然后把模型跑一遍固定回归集,确认没有旧样本退化。
5.2 灰度发布:别让新模型上线就背锅
模型迭代最忌讳直接全量替换。新模型可能修好了A类误检,却把B类老误检重新引入,直接上线不只是误检率波动,还会让现场失去对系统的信任。
我采用了三级发布流程。第一级是影子运行:新模型和旧模型并行跑,但只用旧模型判级,新模型的输出只记录不执行,跑三到五天对比误检率。第二级是小流量切量:让新模型接管大约5%到10%的检测量,观察误检率和漏检率,找产线班组长访谈确认体验。第三级才是全量切换,同时保留旧模型版本,随时可以一键回滚。
这套流程看起来拖慢节奏,实际算总账反而是最快的。没有灰度之前,一次失败的升级容易造成半天到一天的产线混乱;有了灰度之后,风险被控制在很小的范围内。
5.3 迭代节奏与回归测试
在线反馈闭环跑顺后,团队形成了一周一个版本的迭代节奏。周一把回流数据清洗完,周二训练,周三跑回归集和灰度,周四全量更新,周五复盘指标。稳定迭代两个月后,误检率慢慢磨到了5%附近,而且越来越稳。
回归集是这个节奏里最不能省的东西。我固定了一个一千张左右的回归数据集,里面包含所有历史版本的误检和漏检案例。每版模型上线前必须先通过这个回归集,一旦某个历史案例从“检对”变“检错”,立刻排查原因。很多版本迭代都是靠回归集拦住了“修一个坑挖一个坑”的问题。
6. 数据说话:四个技巧的独立贡献与复用边界
有人可能会问:四个技巧到底各起了多大作用?我这边的经验数据供参考,具体数值会因产线结构不同而有差异,但趋势有参考价值。
6.1 分阶段效果对比
| 阶段 | 误检率 | 过杀率 | 漏检率 | 主要变化 |
|---|---|---|---|---|
| 原始系统 | 约15% | 12% | 0.3% | 反光、纹理误检集中爆发 |
| 打光固化+相机参数锁定 | 约11% | 8% | 0.4% | 成像稳定,误检大头被削掉 |
| 样本清洗+难例挖掘 | 约8% | 5% | 0.3% | 模型更懂正常纹理 |
| 阈值+后处理规则 | 约5% | 2.5% | 0.5% | 输出侧硬约束兜底 |
| 在线反馈闭环(三个月) | 约4.5% | 2.1% | 0.4% | 持续迭代,指标缓慢下降 |
这里要提醒一句:漏检率从0.3%到0.4%看起来变高了,实际是因为回灌难例后模型对“类缺陷干扰”更保守了,同时我们通过多帧投票和区域豁免把影响压到最小。最终产线工人在效率和漏检之间找到一个可接受的平衡点。
6.2 这套打法能复用到哪些场景
这个组合拳比较适合表面外观检测、缺陷分类、定位测量这类相对可控的工业视觉场景,尤其是检测对象固定在工位上、成像环境可以改造的场景。
如果是开放场景、移动机器人视觉、复杂背景下的语义分割,成像确定性控制和ROI约束就不太适用,重点要放到数据采样和模型训练策略上。但样本治理、难例挖掘、灰度发布这几条,几乎在所有视觉落地项目里都能用。
还有一个容易被忽略的点:这四个技巧本身也是分优先级的。如果你的误检率还在两位数,优先做成像控制;如果成像已经很稳定,再碰数据;只有当数据和成像都稳了,阈值和后处理规则的收益才会被放大。顺序反了,往往事倍功半。
最后再分享一点个人体会。我在这套打法里最大的收获,是理解了误检率不是“调”出来的,而是“控”出来的。控制成像变量的漂移,控制训练数据的纯度,控制输出侧的约束,控制迭代始终在回归集上走。每一步看起来都算不上惊艳,但叠在一起,效果出乎意料。工业视觉本质上是工程系统,模型的精度只是系统里的一环。先把系统控稳了,模型才能发挥出它应有的价值。如果这篇记录能让你少踩几个坑,那这两个月就没白折腾。