做工业视觉这几年,我最怕听到的一句话就是:模型在测试集上效果挺好,一到现场就翻车。最近在做的钢水罐及未定义物体目标检测系统,正好把这几个坑全踩了一遍。钢水罐是炼钢环节的核心容器,承载一千多度的高温钢水,需要自动监控罐体的位置、轮廓和附近是否出现不明异物。车间里粉尘大、光照乱、罐体表面还会因为高温氧化不断变色,传统机器视觉的固定阈值和模板匹配根本招架不住。所以这个项目把方案换成了基于 Mask R-CNN 与 RegNetX 的实例分割思路——RegNetX 负责提取更高效、更稳定的特征,Mask R-CNN 负责对钢水罐做像素级的分割,并留出对训练阶段没见过的未定义物体的预警通道。这篇文章把整个过程从选型、标注、训练到部署的经验和教训都梳理一遍,适合做工业视觉落地、实例分割或开放场景目标检测的同行参考。
1. 项目背景:为什么钢水罐检测不能靠传统视觉
1.1 钢水罐监测场景的真实难点
钢水罐看似是一个巨大、轮廓清晰的容器,实际拍摄条件远比想象中恶劣。罐体在吊运、浇铸、返回的过程中,位置不停变化,角度也不断转换;现场还会有钢水飞溅、水雾升腾、红外辐射干扰。传统视觉方案常用固定 ROI、模板匹配或灰度阈值,只要罐体换一个角度,或者高光区域盖住罐口,特征就完全对不上,漏检和误检非常严重。
另一个麻烦是罐体本身的外观并不稳定。新的钢水罐表面有清晰的漆面和结构,但用一段时间后,氧化皮会成片脱落,罐沿会挂渣,高温会让局部区域变成亮红色甚至白热色。这种情况下,任何依赖颜色特征的视觉算法都会失效。深度学习目标检测虽然对颜色变化更鲁棒,但如果只是输出一个矩形框,依然很难满足后续的安全判断,因为安全逻辑需要知道“异物是否侵入罐口区域”或者“罐体轮廓是否完整”。
1.2 “未定义物体”到底指什么
工业现场的目标检测,通常只关注预先定义好的类别,比如钢水罐、鱼雷罐、渣罐、天车吊钩。但真实风险往往来自“没有提前定义”的物体,比如遗落在轨道上的扳手、断裂的渣块、掉落的耐材,甚至是误入作业区域的人。需求方不可能在项目初期把这些物体全部枚举清楚,即使枚举了,样本也很难收集。未定义物体,指的就是模型训练阶段没有见过、但在运行时需要被发现的异常目标。
实现上有两种思路:一种是把“未定义物体”直接当成一个额外的类,专门收集形态各异的异物样本去训练;另一种是借用模型的背景类概率和特征距离,对不属于任何已知类别的区域做开放集识别。两种方法不冲突,甚至可以叠加。我们在项目中先采用“额外类别”的方式,因为落地最快、解释成本最低;后续再引入特征距离计算,对模型判定为“未定义物体”的可信度做二次校验。
1.3 为什么选了 Mask R-CNN 而不是纯检测框
如果只做钢水罐定位,用 Faster R-CNN 或 YOLO 就够,但项目里不仅要“框住”,还要“圈出轮廓”。钢水罐带有耳轴、罐沿、罐底等不规则结构,普通检测框会框入大量背景,下游的报警逻辑很难判断框内区域到底有没有异物。一个像素级 mask 可以精确表达目标占用的空间范围,这样当异物出现在罐口上方、罐沿附近时,系统能通过 mask 区域重叠度做更准确的判断。
Mask R-CNN 和纯语义分割的区别在于实例级表达。钢水罐作业区域可能同时出现多台罐体和多个未定义物体,语义分割会把同类目标糊成一个连通域,很难区分“哪一个罐体出了问题”。Mask R-CNN 的输出天然自带实例编号,每个目标都有独立的 mask,后续和天车定位、罐体 ID 做关联时非常方便。这也是我最终放弃 DeepLab 这类语义分割模型的核心原因。
2. 模型选型拆解:RegNetX 为什么会出现在这种任务里
2.1 Mask R-CNN 骨架选择的前因后果
Mask R-CNN 本身是一个框架,真正决定特征上限的是骨干网络。最常用的骨干是 ResNet50 和 ResNet101,相关资料多、调参经验丰富,但在钢水罐检测这个任务里,我的体会是 ResNet 的“计算效率配比”不够理想。钢水罐在画面里经常处于中远距离,目标和相机之间隔着大量烟尘,需要网络在相同算力下提取更细腻的特征。RegNetX 正是在这种诉求下被引入的。
用 RegNetX 做骨干,需要配合 FPN 多尺度特征金字塔。钢水罐的大小在不同工位差异很大,有的视角下罐体几乎占满整个画面,有的视角下罐体只占画面的十分之一。FPN 可以把高层语义特征和低层细节特征融合起来,保证大小目标都能被检测到。RegNetX 生成的多尺度特征有一个特点:它的各个 stage 宽度和通道比例是经过设计空间搜索的,比人工设计的 ResNet 更均衡,所以喂给 FPN 之后,小目标的召回率通常表现更稳。
2.2 简单聊聊 RegNetX 的设计思路
RegNet 系列的核心不是某一组具体网络结构,而是提供了一套“设计空间”。设计空间里包含 depth、initial width、width slope、group width 等参数,通过统计模型在这些参数组合上的表现,找到性能与算力之间的帕累托前沿。RegNetX 就是这个设计空间里带 group conv 的一族网络结构,它的宽度不是像 ResNet 那样逐 stage 简单翻倍,而是按一定函数规律增长。
这种设计的实际价值在于:同样的 FLOPs 预算下,RegNetX 能获得比 ResNet 更低的误差。对工业侧来说,算力是硬约束。现场的工控机可能只有一张中端显卡,选一个计算量不过度膨胀、但特征表达足够的骨干,比一味追求大模型更实际。RegNetX 的不同档位正好覆盖从轻量到重量的需求,比如 RegNetX-1.6GF 适合老旧显卡,RegNetX-4GF 适合当前主流的推理卡,RegNetX-8GF 则适合离线分析。
2.3 与 ResNet / EfficientNet 的对比
| 骨干网络 | 特征提取能力 | 训练成本 | 推理成本 | 调参成熟度 |
|---|---|---|---|---|
| ResNet50 | 中规中矩,稳定 | 低 | 中 | 最高 |
| RegNetX-4GF | 同级算力下更优 | 中 | 中 | 略低 |
| EfficientNet-B4 | 强但结构复杂 | 高 | 高 | 低 |
单看 mAP 指标,EfficientNet 系列往往能刷出更好看的数字,但把它接进 Mask R-CNN 的 FPN 结构时,需要额外适配 stride 和通道数,而且在工业现场的工控机上推理延迟明显偏高。EfficientNet 的深度可分离卷积在部分硬件上优化得并不好,实际部署速度常常达不到理论预期。ResNet50 最省心,资料多、问题少,但精度上限偏低,尤其是在未定义物体这种组织形态差异很大的类别上,特征区分度不够。
RegNetX 处在中间档:不需要像 EfficientNet 那样做大量结构适配,又能在同算力下比 ResNet 多拿一点精度。我在项目里最终选了 RegNetX-4GF 作为骨干,主要是结合了现场显卡的显存和帧率要求。如果现场只有老一代显卡,我更建议退到 RegNetX-1.6GF,然后适当降低输入图像分辨率,而不是硬上大模型。
2.4 选型结论
选型这件事,不能只看测试集精度,还要看模型在“脏数据”下的表现。钢水罐场景里最不缺的就是光斑、粉尘、蒸汽遮挡,骨干网络如果太深太宽,很容易在训练集上记住那些高光噪声。RegNetX 的设计空间搜索其实隐式地加入了正则化效果,使网络不会在个别特征上过度自信。这是我实际训练后感受最明显的一点:同一批数据,RegNetX 的验证 loss 和训练 loss 之间的差距通常比 ResNet 更小,过拟合压力更小。
反过来也要提醒一句:RegNetX 在开源社区里的现成 Mask R-CNN 配置不如 ResNet 那么多,自己改配置时容易踩到 backbone 输出 stride 对不上 FPN 的坑。第一次接入时,建议先把骨干改成 RegNetX 但不改其他模块,跑一遍单张图确认 channel 对齐,再继续调参。
3. 数据集构建:钢水罐和“未定义物体”的标注策略
3.1 现场图像采集设备怎么放
模型效果的上限由数据决定,这个道理在工业场景里尤其明显。钢水罐图像的采集点位不能只放在一个固定视角,至少要覆盖三个典型机位:钢包回转台俯视位、浇铸跨侧方位、返回轨道末端位。相机高度和角度会影响罐体在图像中的尺度,只用一个视角训练出来的模型,换到另一个工位后检测精度会明显下降。
采集时段要跨白天、黑夜、凌晨,覆盖晴天和阴天。很多项目容易犯只采白班的错误,但钢厂里钢水罐是 24 小时连续运转的,夜间厂房灯光的色温和亮度变化比白天更极端。帧采样不要做连续视频帧的密集抽取,因为相邻帧高度相似,会造成训练集信息冗余。我的做法是每隔 3 到 5 秒抽一帧,同时观察钢包旋转台的动作,确保罐体前后左右各个角度都有覆盖。
3.2 标注规范:哪些东西该标成“未定义物体”
标注规范是整个项目里最容易扯皮、也最影响模型质量的部分。已知类别这一侧,钢水罐建议单独成一类,罐体附属的耳轴、罐沿属于同一个实例的一部分,不需要单独拆出来。未定义物体那一侧,要明确一个原则:必须是空间上可定位的实体异物,比如工具、残钢、渣块、防堵料,不能把火光、反光、水雾这些非持久现象标成未定义物体。
如果视野里一个物体被遮挡超过一半,无法判断真实类别,就统一标为“未定义物体”。这样标注看似简单,实际执行时会遇到大量边界情况。比如一个扳手半截插在渣土里,露出来的部分非常小,标还是不标?我的经验是:只要人眼能明确判断它不是一个固定设施,就应该标出来,哪怕面积很小。因为未定义物体的样本本来就稀缺,漏标一个,模型就少一次学习机会。
另外,同一张图里的多个互相接触的未定义物体,标注时要用语义边缘仔细切开,不能直接把一大片东西画成一个 mask,否则模型学到的是“一团杂物”而不是“独立异物”。这个细节直接影响 mask 边界的质量,也是后来排查误报时最容易翻车的地方。
3.3 数据增强与负样本处理
工业环境的数据增强不能照搬通用目标检测的套路。除了常规的随机亮度、对比度、饱和度调整,还要专门模拟两类现场干扰:一类是飞溅的火花,表现为图像里随机出现的高亮短线条和小亮斑;另一类是蒸汽和粉尘,表现为局部区域的模糊和对比度下降。我在增强管线里加入了一个随机叠加高光噪声的算子,用一些可在运行时生成的圆形亮斑覆盖到图像随机位置,发现能明显提高模型在高光区域的稳定性。
负样本同样不能忽略。所谓负样本,就是完全没有钢水罐和未定义物体的背景图,比如空荡荡的轨道、墙面、地面。加入负样本可以让模型学会抑制那些看起来像目标但实际不是目标的区域,减少部署现场的无意义误报。负样本不需要太多,占训练集的 10% 左右就够,并且要覆盖不同光照条件和不同机位。
注意:不要对标注好的 mask 做大幅旋转增强,尤其是超过 45 度的旋转。钢水罐在实际场景里虽然有不同角度,但不会倒立。过度旋转增强会让模型学到不符合物理规律的特征,反而损害真实场景表现。
3.4 不要忽略的类间相似度
钢水罐的检测还有一个天然难点:它与渣罐、中间包、废钢斗在外形上高度相似。如果标注时只关心“是不是罐子”,模型很容易把这些相似设备都识别成钢水罐。实际标注时,要把这些常见干扰物单独列出,哪怕不参与最终评估,也要作为背景负类或者单独类别放进训练集,让模型学会区分“钢水罐”和“长得很像钢水罐但不是的东西”。
另外,钢水罐在高温状态下的颜色变化极大,特别是满罐和空罐时温差造成的红外辐射差异,会让视觉特征发生偏移。我在项目里没有简单地只喂 RGB 图像,而是同时用传统图像处理估计整体色温,并在输入模型前做一次白平衡归一化。这个操作的目的是减少不同炉次之间的颜色漂移,让模型更多关注罐体的几何结构,而不是被颜色带偏。
4. 训练细节与调参记录
4.1 环境和依赖
整个训练环境基于 PyTorch 搭建,使用开源的实例分割框架,骨干网络替换为 RegNetX。具体版本不展开说了,因为这类框架迭代很快。但有一点建议:训练前把环境锁死,用 conda 创建独立环境,避免因为依赖库版本不一致导致复现结果漂移。
conda create -n industrial_ins python=3.8 -y conda activate industrial_ins pip install torch==1.10.0+cu113 torchvision==0.11.0+cu113 pip install opencv-python pip install pycocotools我用的是单卡训练,显存 11GB 左右。如果显存不够,优先降低 batch size,而不是急着换更小的骨干,因为 RegNetX 在显存占用上已经比同精度的 ResNet 更理想。
4.2 迁移学习与冻结训练策略
钢水罐的数据集规模通常不会太大,从头训练 Mask R-CNN 很难收敛。我的做法是加载在公开数据集上预训练好的 RegNetX 骨干权重,然后分阶段微调。第一阶段冻结骨干网络的前两个 stage,只训练 RPN、分类头和 mask head,让模型先学会钢水罐的基本位置和轮廓;第二阶段再解冻骨干网络,用较低的学习率微调所有层。
这个顺序很关键。如果一开始就全部放开训练,骨干网络受到小数据集的干扰,预训练学到的通用特征会被快速破坏,后面再想恢复正常很困难。解冻骨干的时机,我一般以分类 loss 的下降曲线为准,等到第一阶段 loss 明显收敛,再进入第二阶段,整体训练会稳定很多。
4.3 关键超参数
输入图像尺寸使用短边 800、长边 1333 的默认配置,但钢水罐远距离时比较小,所以我额外增加了数据增强里的随机缩放,让同一张图在训练时既有大尺度版本,也有小尺度版本,等于变相让模型适应不同距离的罐体。
超参数方面,批量大小设 4,学习率从 0.001 开始,采用余弦退火策略。如果显存紧张只能跑 batch size 2,学习率要同步降到 0.0005,这个对应关系容易被忽略。RPN 和 ROI Head 的 loss 权重保持默认,但我在 mask loss 上稍微加重,因为项目更看重轮廓精度,mask 质量直接决定后续区域重叠判断是否可靠。
迭代数不要太死板。我一般设置最大 20k 步,同时配合早停,验证集上的 mAP 连续 1000 步不涨就停。钢水罐这类目标相对单一,训练到后期很容易出现过拟合,早停比盲目多跑更有效。
4.4 训练遇到的问题和曲线解读
第一次训练时,验证集上的未定义物体召回率特别低,loss 却一路下降。后来检查发现,标注里未定义物体的样本数量只占全部目标的 8%,类别严重不均衡。解决办法是把缺少未定义物体样本的图片做重复采样,让模型每个 epoch 都能充分看到未知类别,而不是靠运气撞上。
另外,训练曲线中 mask loss 出现明显的周期性抖动,通常是因为同一个钢水罐在不同帧里的标注边界不一致。标注人员在罐口结渣位置经常产生分歧,有的人把渣块画进钢水罐 mask,有的人把渣块画成未定义物体。这类标注噪声会让 mask head 无所适从。后来我们重新制定了标注规则:罐体表面附着的、与罐体接触的渣块一律归入钢水罐,独立掉落在罐外区域的渣块才标成未定义物体。规则明确后,mask loss 的抖动明显减小。
5. 部署与系统集成(实操重点)
5.1 推理流程设计:检测、分割、报警的三段式
部署不是把模型跑起来就完事,而是要把模型输出变成现场能用的决策结果。我设计的推理流程分三段:第一段是模型推理,输出钢水罐和未定义物体的 bbox、mask、类别和置信度;第二段做后处理过滤,去掉置信度过低、面积过小的碎片区域;第三段做规则判断,根据目标区域和报警区域的重叠关系生成信号。
# 伪代码:推理后的目标过滤示例 valid_objects = [] for obj in predictions: if obj["score"] < 0.5: continue if obj["score"] < 0.7 and obj["mask_area"] < 500: continue valid_objects.append(obj)这套过滤逻辑的意义在于减少单帧偶发误报。钢水罐和异物的置信度阈值不能一刀切,因为钢水罐结构清晰,置信度通常很高;未定义物体形态复杂,置信度天然偏低。如果统一要求 0.7,可能把真正的异物全部过滤掉。我的做法是:钢水罐阈值 0.7,未定义物体阈值 0.4,并且再配合 mask 面积下限来排除噪声。
5.2 与现场控制系统对接时的取舍
现场控制系统需要的是简单的开关量或者结构化消息,而不是一张图片。所以模型服务端只输出 JSON,包括目标类别、置信度、归一化坐标和 mask 简化后的多边形点集。报警决策不在模型服务端做死,而是交给上层规则引擎,方便现场工程师调整逻辑。
模型推理的速度和现场要求的响应时间存在矛盾。检测钢水罐漏包这种事故,越快越好,但一味的低延迟会牺牲精度。我在工程里做了两级处理:关键区域用高分辨率、高帧率连续检测,次要区域用低帧率轮询。这样既能保证关键报警的实时性,又不至于让工控机长时间满负荷跑模型。
5.3 光照变化、粉尘干扰、模型抖动怎么处理
现场最大的坑不是模型,而是相机。钢水浇铸过程中,钢水辐射会让相机的自动曝光来回跳,图像亮度一会儿过曝一会儿过暗,模型输出开始剧烈抖动。解决办法是把相机曝光时间固定,光圈固定,关掉自动白平衡。然后在图像进入模型前,用统计量做一次归一化,把亮度和对比度拉到一个稳定范围。
粉尘和水雾会附着在镜头保护罩上,图像逐渐发白,模型精度断崖式下降。我在部署方案里加了一个图像清晰度检测任务:计算梯度能量,低于阈值就触发吹扫装置,并暂时把该相机的检测结果标记为低可靠,避免误报警。这个细节若干预不及时,整个系统会变成“每天报警,全是误报”。
注意:多帧投票可以抑制随机噪声,但不能盲目用于钢水罐场景。如果钢水罐正在快速回转,多帧平均会让轮廓变成半透明拖影,反而破坏 mask。我一般只对报警输出做连续 3 帧确认,不对 mask 图像本身做时间平均。
6. 常见问题与排查技巧实录
6.1 钢水罐漏检率高的原因
钢水罐漏检最常见的原因不是模型太弱,而是目标尺寸和 FPN 特征层不匹配。罐体远距离成像时像素面积小,锚框尺寸覆盖不到,模型根本没机会把它当成一个候选框。排查时,先看 RPN 阶段有没有输出包含钢水罐的 proposal,如果 RPN 就漏了,问题几乎可以锁定在锚框和输入尺寸上。
解决思路有两个:一是提高输入图像分辨率,相当于把目标整体放大;二是在 FPN 的 P2 层额外增加检测头,P2 层的 stride 更小,对小目标更友好。但如果现场算力有限,增加 P2 层会带来明显延迟上升,所以我更推荐优先调大输入尺寸,再观察漏检情况。
6.2 Mask 边界不干净,怎么修
钢水罐的 mask 边界经常出现锯齿和孔洞,尤其是罐沿附近有火光遮挡的时候。产生原因一方面是标注时罐口边缘不齐,另一方面是 mask head 对局部纹理过度敏感。直接在训练层面解决最彻底:用边缘更平滑的标注重新修正训练集,但投入很大。部署层面可以先试用形态学闭运算,把细小的孔洞补上,再用腐蚀去掉孤立噪点。
不过形态学操作参数不能拍脑袋定。kernel 大小取 3 到 5 就够了,太大容易把两个本应分开的目标粘连在一起。我踩过这个坑:为了补齐 mask 孔洞,把闭运算 kernel 调到 7,结果罐体和旁边的渣块直接连成一片,区域重叠判断全部失灵。
6.3 未定义物体误报成已知类
未定义物体最让人头疼的问题是误报成钢水罐。原因通常很简单:未定义物体样本太少,模型没有学到足够强的“它不是一个钢水罐”的证据。另一个原因是钢水罐本身形态变化大,模型对罐体的特征描述过于宽松,导致异形耐材、工具等物体被划进罐体类别。
我的处理方法是做难例挖掘:把验证集中所有误报样本挑出来,重新标注成对应的未定义物体子类,然后追加到训练集中继续训练。这个循环重复两到三轮后,误报会明显下降。如果时间紧张,也可以在决策层加入特征距离判断,对模型预测为已知类别但特征向量离类别中心很远的样本打上低可靠标签。
6.4 一个排查实例:从误报样本反推数据问题
有一次系统上线后频繁把浇铸区域的反射光斑识别成未定义物体。反射光在空间上不固定,亮度高,形状不规则,和未定义物体样本很像。我导出近一周的误报图片,按特征聚类后发现:训练数据里所有“高光小区域”几乎都被标成了未定义物体,因为标注人员把飞溅钢花和反光也框了进去。模型学到的是“这里有一片亮的东西,就是未定义物体”。
修正很直接:把训练标注中属于非实体光源的标签全部删除,只保留空间上可定位的异物,同时新增一批包含强反光但没有任何异物的负样本。重新训练后,这类误报减少了大约一半,剩下的再通过决策层的时间连续性判断过滤。这个案例说明,数据标注的错误会原封不动地变成模型的行为,不要一上来就怀疑网络结构。
7. 指标解读与实际收益
7.1 用哪些指标判断项目成功
工业项目不能只看 mAP。mAP 是一个综合指标,但安全系统更关心两类错误:漏报和误报。漏报指的是真正出现异物但没有被检测到,这是安全底线;误报指的是现场没有异常却频繁报警,这会让操作人员失去信任。我建议把评价指标拆成钢水罐检测召回率、未定义物体检测召回率、每班次误报次数和平均处理延迟四个维度。
在模型调优阶段,我会额外关注每个类别的召回率和置信度分布。未定义物体的召回率通常低于钢水罐,因为形态差异太大,不能只看总 mAP 掩盖这个短板。如果钢水罐 mAP 已经不错,但未定义物体召回率很低,这个项目依然不能上线,因为漏掉一个异物就可能是重大事故。
7.2 实际效果与局限
从我们的实地测试来看,换成 RegNetX 骨干后,钢水罐的 mask 边界稳定性比之前用 ResNet50 时更好,尤其是罐口高温区域附近的轮廓更完整。未定义物体的检测也能满足基本预警需求,但距离“完全开放集识别”还有差距:遇到全新的、和训练样本差异极大的物体时,模型仍然会漏。
这个局限不是 Mask R-CNN 或 RegNetX 单独能解决的,本质上是封闭集分类和开放世界识别之间的鸿沟。想要进一步提高,需要在决策层引入额外的鲁棒性判断,比如深度特征分布建模,而不能指望只靠分类层输出概率就能覆盖所有未知情况。
7.3 后续可以扩展的方向
这个项目后续最大的扩展空间是引入深度相机或激光雷达点云。钢水罐和异物在二维图像里容易受高光和遮挡影响,但在三维空间里,物体的几何轮廓要稳定得多。用 Mask R-CNN 先做二维实例分割,再把分割结果投影到点云上做三维校验,能够大幅降低误报。
另一个方向是把检测系统从“识别已知+未知”升级为“跟踪已知+未知”。当前只在单帧图像上做检测,没有利用多帧之间的关联。如果加入轻量级目标跟踪,就能根据物体运动轨迹判断是否是持久异物,过滤掉一闪而过的火光。这样既保留 Mask R-CNN 实例分割的精度,又多了时间维度的鲁棒性。
最后分享一个我在这个项目里最有感触的经验:不要急着换更复杂的模型,先把标注一致性、数据增强覆盖度和现场图像质量这三件事做扎实。钢水罐检测的绝大多数问题,最后都指向数据而不是模型。RegNetX 给了我更好的起点,但真正让系统稳定跑在车间的,是一遍又一遍修正标注规则、调整拍摄参数、观察误报样本的这些基础工作。如果你也在做类似的工业检测项目,建议从错误样本出发反推问题,而不是盲目堆算力。