去年我在一条金属结构件外观检测产线上待了将近三个月。刚开始,工艺负责人给我的需求听起来很简单:把表面划痕、麻点、漏工序的工件自动分出来,每天最好能检十万件。当时我还没意识到,这不是“找个模型套上去”就能解决的问题,而是一个从成像、数据、训练到产线联动的完整系统工程。最终我们把飞桨(PaddlePaddle)作为整套方案的技术底座,把模型真正跑进了实时工位,并持续迭代到现在。这篇文章就把整个工业自动化案例从选型、数据、训练到部署上线的过程完整记录下来,尤其是那些不踩一次就根本发现不了的细节。
1. 从一个真实产线痛点说起:缺陷检测为什么难自动化
1.1 人工质检的隐形成本
很多工厂的人工质检看起来“成本不高”,无非是几个质检员坐在灯光下看零件。但真正算过账的人都知道,这岗位的隐形成本非常高。不同的人对缺陷的判断标准不一样,同一个人在不同班次、不同疲劳程度下的标准也不一样。同一个划痕,白班可能判 NG,夜班可能直接放过去。
更麻烦的是,质检员往往是最难留住的岗位。工作单调、责任重大,稍有漏检出了客诉,压力全落在个人头上,人员流动率一直居高不下。每走一个人,就要重新培训,而“判断标准”这种东西主要靠经验传递,老员工带新人时教的往往是“凭感觉”。这种模式在订单稳定、缺陷类型单一的时候勉强能转,一旦产品批次变多、表面状态变化,人工质检就成了整个产线的瓶颈。
我们那条线的漏检率其实不高,但问题在于不稳定。客户只要抽检到一批里面有漏检,整批退回,损失远超几个质检员的人工成本。这也是产线负责人最终下决心要做自动化的直接原因:不是要完全替代人,而是要把标准固定下来,让每一次检测都保持同一个尺度。
1.2 传统机器视觉的边界在哪里
刚开始我们也没有直接上深度学习,而是先评估了传统机器视觉方案。市面上成熟的光学检测设备,多数依靠阈值分割、边缘提取、模板匹配这类规则算法。这类方案在“背景稳定、缺陷类型固定、对比度高”的场景下表现不错。比如透明瓶子的灌装液位检测、元器件引脚缺失检测,规则算法又快又稳。
但结构件表面的划痕、麻点、轻微压伤,属于典型的高变异性缺陷。同一道划痕,在不同角度光的照射下可能呈现为一条细线,也可能变成一片反光。麻点的大小、深浅、分布更是千变万化。规则算法要对每一种形态写一套逻辑,光是调阈值就能让人崩溃。而且这家产线的工件表面本身就有纹理、有加工痕迹,模型需要学会区分“正常纹理”和“异常缺陷”,这恰恰是传统视觉最难突破的地方。
我们尝试过用多角度打光配合传统算法来提高对比度,效果确实有改善,但要覆盖的缺陷形态太多,规则参数组合起来完全不可维护。这时候才真正确定方向:必须用深度学习模型来做特征提取,把“什么是缺陷,什么不是”交给模型从数据里学。
2. 飞桨方案选型:检测、分类与分割如何落到产线上
2.1 拿到需求后我为什么先选检测而不是分类
很多第一次接触工业视觉的人会想当然地认为,质检就是“这张图是好的还是坏的”,所以首选图像分类。但我们最终没有按分类来做,而是选择了目标检测,这个决定看起来绕了弯,实际对后期帮助非常大。
分类模型只能输出整张图的结论,比如“OK”或“NG”。但如果这个工件被判为 NG,工艺人员最想知道的是:缺陷到底在哪、属于哪一类、面积有多大。这些信息用来反推是哪道工序出了问题,也用来做复判和追溯。检测模型输出的是缺陷的位置框、类别和置信度,NG 图片可以自动截取缺陷区域,保存到质量系统里,让工程师一眼看清问题位置。对产线来说,这不是“多此一举”,而是能不能真正落地的问题。
我记得项目中期对方提出一个需求:统计划痕在零件上的分布位置,用来判断是不是某一个夹具造成的问题。分类模型根本做不到这点,检测模型直接按坐标区域做统计就完成了。这个需求如果一开始选了分类,后来大概率要推翻重做。
当然,也有一类场景适合用分割而不是检测。如果缺陷需要精确测量不良面积,比如“麻点直径超过 0.3 毫米才算不良”,或者是边缘不规则且难以用矩形框描述的情况,那就要上实例分割模型。飞桨这套体系里检测、分割、分类都有对应的成熟组件,我们这次的主线用的是目标检测,个别测量类需求单独用分割模型补上,两条路并行。
2.2 飞桨生态里选具体模型的判断依据
飞桨的模型库覆盖面很广,从轻量级目标检测到高精度大模型都有,真正需要考虑的不是“哪个模型准”,而是“哪个模型在产线约束条件下最合适”。我们的判断依据按优先级排列,大致是这样的:
第一是节拍。工位允许的检测时间等于工位节拍减去机械取放、定位、通信的时间。我们那条线的节拍是 3 秒一件,机械动作大概占掉 1.8 秒,留给视觉的时间只有 1.2 秒。在这个前提下,再好的模型如果单帧推理超过 800 毫秒,就很难稳定使用。第二是缺陷尺度差异。划痕可能在整图中只占几十个像素,而麻点甚至更小,轻量模型如果输入分辨率不够,小目标很容易漏。第三是部署环境。当时现场工控机用的是 CPU,后来才加了 GPU 卡,所以模型必须能在 CPU 上跑出可接受的速度,在 GPU 上又能继续提速。
综合这些条件,我们最后选的是一套轻量级检测模型做主体,输入分辨率从 640 提到 1280,保住了小缺陷的检出能力;同时保留了一个高精度模型作为离线复判。表格里这几类模型的选择思路比较直观:
| 模型倾向 | 特点 | 适合场景 |
|---|---|---|
| 轻量级检测模型 | 推理快、内存占用低 | 高速产线、边缘硬件受限 |
| 高精度检测模型 | 精度高、参数量大、速度较慢 | 离线抽检、复判、复杂缺陷 |
| 实例分割模型 | 输出像素级区域 | 需要测量缺陷面积和形状的工位 |
选型这件事,最忌一开始就追求精度指标。工业项目里“跑得起来”比“跑得准”更优先,因为精度可以靠后续数据迭代调上去,但如果速度和节拍不匹配,方案在架构上就失败了。
3. 数据工程:只有 200 张缺陷图怎么把模型练起来
3.1 现场数据采集与标注规范
刚启动项目时,我们面对的困难很现实:缺陷图片只有 200 张左右,而且都是从生产线上陆陆续续攒下来的。用这点数据训练模型,几乎是不可能完成的任务。第一件事不是急着标注,而是先把采集环境固定下来。
工业视觉里有一句话叫“成像先行”。训练数据如果是在手机拍摄、环境光不稳定的条件下收集的,模型学到的东西根本没法迁移到产线。我们搭了一套固定的成像单元:工业相机、定焦镜头、低角度环形光源和背光源,保证拍摄角度、距离、光照条件都和真实工位一致。这样采集回来的图片,虽然数量不多,但每张图的信息分布是接近线上实际状态的。
标注规范更花功夫。我和另外两个工程师一起把缺陷类别重新梳理了一遍,最终确认了六类:划痕、麻点、压伤、异物、油污、漏工序。每类缺陷都写了标注说明,附上典型图例。最关键的一点是“不确定的图先不标”:一张图上看不准是划痕还是压伤,就单独放一个待定文件夹,等现场工艺工程师确认后再标,而不是凭感觉硬标进去。
我们当时用标注工具导出了 COCO 格式数据,后续直接喂给模型训练流程,减少了格式转换带来的坑。建议所有项目都从第一天就定好标注格式和文件命名规则,不然后面补标注、改格式会浪费大量时间。
3.2 数据增强、合成与类不平衡处理
只有 200 张缺陷图,直接训练轻量检测模型很容易过拟合。我们做了几层数据扩充。
第一层是常规增强:亮度扰动、对比度扰动、轻微高斯噪声、随机翻转。工业场景里要特别小心旋转增强——工件方向固定后,旋转 90 度或 180 度不一定符合物理语义,可能导致模型学到错误的方向特征。我们只保留了翻转,没有做随机任意角度旋转。
第二层是图像合成。把已有的缺陷区域从图上抠出来,当作“素材”,再贴到大量正常的工件表面图上。贴的时候做了缩放、旋转、亮度调整和边缘融合,让合成缺陷尽量自然。这一招效果非常明显,把有效训练样本从 200 张扩大到接近 2000 张,基本解决了“没数据可用”的问题。
第三层是负样本的补充。很多做视觉项目的人只盯着缺陷图,却忽略了正常样本同样重要。没有足够的正常样本,模型的整体置信度很难校准,误杀率会一路飙升。我们的正常样本与缺陷样本比例最终控制在 1:1 到 1:2 之间,既保证模型见过足够多“正常态”,又不会让缺陷特征被淹没。
类不平衡的问题我们通过两种方式解决:一是对数量较少的缺陷类别做多倍过采样;二是在训练配置里对不同类别设置不同的 loss 权重,让模型对“图纸少见但实际危害大”的缺陷更加敏感。
3.3 我踩过的标注坑:小缺陷漏标最致命
做训练之前我对标注这件事比较乐观,觉得已经有标注团队了,照着规范做就行。但第一次验证结果出来,模型在划痕类别上的召回率只有 80% 左右,怎么调参数都上不去。后来分析了所有漏检图,发现一个共同特征:被漏检的划痕区域,几乎都在训练标注里漏标了。
问题在于,模型会把“没有标注”的区域当作背景来学习。如果一张图里有一条真实划痕但没被框出来,模型看到这个特征时就会倾向于不输出检测框。这比标注位置不够准确更致命,因为它是系统性偏差。
我们后来加了标注一致性检查:不定期抽取几十张图,让两个人分别标,然后计算标注框之间的 IoU。如果一致性低于 95%,说明规范还不够清晰,需要返工。同时还发现一个细节:油污和水渍在图上看起来很像,如果不单独设类,模型会非常困惑。最终我们把这些“干扰物”单独定义为一个类别,让模型先学区分,再学判定,效果好很多。
数据这块我的最大感受是:在工业视觉项目里,数据整理占掉的时间和模型调参一样多,甚至更多。想通这一点后,后面所有进度安排都顺畅了。
4. 训练与调优:从能跑到稳定的四个关键动作
4.1 超参数怎么定:batch size、学习率与预训练权重
模型一开始跑到 50 个 epoch 时,loss 下降得倒是很快,但验证集上的召回率一直不理想。后来复盘发现,数据量不够大的时候,从头训练网络几乎不可能学到稳定的表面特征,必须借用预训练权重。
我们用飞桨相关的预训练模型作为起点,先固定骨干网络参数训练几个 epoch,让检测头先适应工业数据的分布;然后再放开全部参数做整体微调。这套做法的逻辑很简单:预训练权重已经有了丰富的通用视觉特征,直接微调可以避免小数据把骨干网络里的好特征“洗掉”。
超参数方面,我们使用的配置大体是这样:输入分辨率 1280×1280,batch size 8,初始学习率 0.0001,余弦衰减,训练 100 个 epoch。这里有个经验公式可以参考:如果 batch size 翻倍,学习率大致也要翻倍,才能保持梯度的平稳程度。但这个规律不是绝对的,还是要看 loss 曲线的实际表现来微调。
最容易被忽略的是推理时的预处理要和训练时完全一致。比如训练时做了归一化,推理时如果忘了做同样操作,模型效果会莫名其妙地掉几个点。遇到这种问题,不要去调模型,先去检查预处理代码是不是和训练配置对齐了。
4.2 损失函数与后处理阈值调整
训练的 loss 曲线看多了以后,会发现一个常见陷阱:整体 loss 一直在降,但特定缺陷类型的召回率却在掉。这是因为不同类别的难易程度差异很大,占比较高的类别主导了梯度,难检的小目标反而被忽略了。
我们针对数量少且难检的缺陷类别单独调高了权重,让模型在训练时更关注这些困难样本。后处理环节同样关键。检测模型输出的原始预测框里,有大量置信度较低、重复度较高的候选框,如果直接拿来判定 NG,误杀率会非常高。需要调整 NMS 阈值、置信度阈值,甚至加一些规则过滤。
举个例子,某些非常细的划痕在图上只有几十像素长,模型有时会把它预测成两个相邻的小框,这时后处理阶段可以加一条规则:同一区域的两个小框如果距离很近、方向一致,就合并成一个框,避免重复报警。这些规则不需要写得复杂,但能显著提升产线端的可用性。
4.3 一组实验对比与精度变化
调优过程中我记录了一组典型对比数据,更能说明问题:
| 配置 | 召回率 | 精确率 | 每万件误杀数 |
|---|---|---|---|
| 基础模型 + 640 输入 + 默认阈值 | 85.2% | 71.6% | 214 |
| 高分辨率输入 1280 + 增强数据 | 91.8% | 78.9% | 96 |
| 高分辨率 + 增强 + 后处理规则 + 阈值微调 | 93.1% | 94.5% | 11 |
第一版配置虽然召回率有 85%,但每万件误杀 214 件,产线根本没法用——工人会频繁跑过去处理假报警,几分钟就烦了。提升输入分辨率以后,模型对小目标的感知能力增强,召回率升了 6 个点,误杀也降到 96。真正起决定性作用的是后处理规则和置信度阈值微调,精确率直接拉到 94.5%,误杀降到 11 件,这才达到产线能接受的稳定状态。
这组数据也说明一个道理:工业项目里的“精度”和学术比赛里的“精度”不是一回事。学术榜单更看重召回和平均精度,工业现场更看重误杀率是不是影响节拍,是不是增加工人负担。
5. 部署与产线集成:模型不是跑通就完事
5.1 推理性能优化:从模型导出到推理引擎
模型训练阶段用的是动态图,灵活性高,适合迭代调试,但直接部署到产线会产生额外开销。我们做的第一件事是把训练好的模型导出为静态推理模型,再接入推理引擎。
飞桨的推理库提供了统一的接口,开启多线程和内存优化后,CPU 上的单帧推理时间明显下降。后来给工控机加了 GPU,我又开启了半精度推理,在精度几乎不掉的情况下速度又提升了几乎一倍。但这里要注意:开启半精度后,必须用一批线上真实图片做回归验证,不能只看速度指标。因为个别层在半精度下可能出现小幅度精度损失,对细划痕这种低对比度目标,可能会有影响。
部署时另一件重要的事是预热。模型加载后的前几次推理往往偏慢,因为缓存还没建立,所以我们在服务启动时会先跑几十张图“预热”,确保正式图片进来时已经有稳定性能。
5.2 相机触发、结果判定与设备联动
模型跑通只是第一步,真正让产线“动起来”的是整套联动逻辑。我们现场的信号链路是这样的:工件到位触发光电传感器,传感器信号通过 PLC 传给工控机,工控机收到信号后控制相机拍照,图片进入检测服务,服务把结果返回给控制程序。
控制程序拿到结果后,如果是 OK,则放行;如果是 NG,则启动声光报警,并在屏幕上显示缺陷框截图。这个逻辑说起来简单,但工程细节非常多。比如图片服务必须设置超时时间,默认 500 毫秒内必须返回结果,超过就把这张图标记为“检测异常”,生产线降级为人工复判,而不是停在原地死等,否则整个节拍都会被打乱。
通信方式也很关键。我们最开始用了 HTTP 请求,每次推理一次就发起一次 HTTP 调用,虽然也能跑,但延迟和资源开销都不理想。后来改成进程内直接调用推理接口,生产端到端延迟稳定在 300 毫秒以内,这才满足节拍要求。
5.3 边缘端与服务器端的部署取舍
项目早期讨论过要不要把多台工位的图片全部传到中心服务器做统一推理。从硬件成本看,服务器集中部署更省显卡;但从工程可靠性看,单点故障风险太高,一旦服务器挂了,整条产线的视觉检测全部停摆。
我们最后选择了边缘端部署:每个工位一台工控机,模型直接放在本地跑,服务器只做数据汇总、模型下发和远程监控。这样做的好处是单台工位故障不影响其他工位,坏一台也只是这一条工位暂停,另外几条线继续生产。代价是硬件成本略高,但和停产损失相比微不足道。
如果以后要加更多工位,我倾向于保持边缘端部署思路,而不是去搞统一推理集群。工业场景追求的是确定性,集中式架构带来的资源利用率提升,很难抵消故障半径扩大的风险。
5.4 7×24 小时稳定运行:日志、重启与备份
模型部署后最容易被低估的是长期运行的稳定性问题。视觉服务本质上是一个常驻进程,时间久了容易出现显存占用持续增长、句柄泄漏、图像缓存没有及时释放等问题。我们上线之初就遇到过连续跑两天后显存溢出,服务直接崩溃的情况。
解决办法是分三层做保障。第一层是日常监控,写脚本每小时记录一次显存占用、推理延迟、失败图片数量,设定阈值后自动告警。第二层是自动恢复,工控机上设置守护进程,检测到服务无响应就自动重启,并且把当前图片状态记录到数据库,重启后不会丢失结果。第三层是备份机制,模型文件、配置文件、日志全部按日期归档,每次更新模型前导出对应版本,一旦新模型线上表现异常,能快速回滚。
还有一个小细节容易被忽略:相机镜头脏污。工业现场灰尘大,镜头沾灰后拍出来的图像整体模糊,模型会突然开始大量漏检。我们的对策是加一个图像质量监控模块,计算每帧图的梯度值和亮度均值,一旦指标偏离基线就报警提醒清洁镜头,不要等到漏检了才发现。
6. 上线三个月后的复盘:这些细节决定项目成败
6.1 误杀率比漏检率更容易让产线崩溃
上线第一天,系统的检出能力其实不错,但没过多久,操作工人就开始频繁找我吐槽:怎么这么多假报警?有一次一个正常工件表面有一条轻微的加工纹理,模型每隔十几件就报一次 NG,工人过去看了半天,发现没问题,只能手动放行。
这正是我在实验阶段反复强调误杀率的原因。漏检是概率性的,偶尔发生,人不会太敏感;误杀是高频的,频繁发生时,工人对系统的信任会快速崩塌。如果工人形成条件反射“报警就是误报”,那么当真实缺陷发生时,他们很可能也会直接放行,整个系统就形同虚设。
我的建议非常明确:上线初期不要直接切自动拦截。先在观察模式下运行一周,系统只记录检测结果、不干预产线,同时安排人工复核所有 NG 报警,把误杀和漏报的样本全部收集起来。等模型和阈值都稳定了,误杀率降到可接受范围,再切到自动拦截模式。这个过渡看起来慢,实际是上线成功概率最高的路径。
6.2 数据回流与持续迭代机制
工业视觉项目上线不是终点,而是起点。产线会换批次、换材料、换天气,光照、环境、工件表面状态都在变化,模型必须跟着数据持续迭代。我们建立了一套数据回流机制:每台工位每天检测的全部图片,按日期和条码归档;每次人工复判的结果回写到数据库,作为新样本的标注来源。
每周做一次数据分析和标注补充,每月重新训练一版模型。更新模型前,必须用固定的历史验证集跑回归测试,要求召回率不能低于上一版,误杀率至少不能变差,才允许上线。这个流程坚持了几个月,模型在新批次产品上的表现一直保持稳定,没有出现“这版模型怎么变蠢了”的问题。
值得强调的一点是:持续迭代时不要只往训练集里加缺陷样本,还要同步加入大量真实的正常样本。我们曾有一段时间只补充了新的缺陷类型,结果模型整体置信度偏高,误杀率小幅上升。后来把正常样本也按比例回流,问题才得到改善。
6.3 给后来者的几条实在建议
很多做算法的人刚接触工业项目时,总觉得“给一堆图,标好框,模型一训,上线完事”。真实项目远比这复杂,这里把我几次踩坑换来的经验直接列出来:
- 先固定成像和打光,再谈算法。同一个场景,光照变一点,模型精度可能就差几个点,环境不稳定,一切调优都是徒劳。
- 第一版方案优先选择简单模型撑全流程,不要在模型选型上过度追求新鲜和复杂。全链路跑通之后,再根据瓶颈去替换或升级模型。
- 算节拍时别只看推理耗时,要把相机触发、图像传输、结果返回、机械动作的时间全部算进去,留足 20% 余量。
- 产线环境远比实验室复杂,手套油污、灰尘、灯光老化、工件摆放偏移,这些都会成为真实影响模型效果的因素。建议至少在产线蹲几天,把每个工位看一遍再动手。
这几个月做下来,我最大的感受是:飞桨帮我们把模型部分做得很顺,但工业自动化项目的成败,更多取决于数据规范、工程稳健和现场沟通。技术是下限,而真正决定一个项目能跑多远的,是把产线的真实规则理解得透不透。