钢材表面缺陷检测实战:从YOLOv5到YOLOv10的选型与部署

📅 发布时间:2026/10/19 19:32:31
钢材表面缺陷检测实战:从YOLOv5到YOLOv10的选型与部署
钢材表面质量把控这件事在很长一段时间里都靠质检员肉眼盯。带钢在产线上以每分钟几百米的速度跑缺陷转瞬即逝人盯久了必然疲劳漏检几乎无法避免。这也是基于深度学习的表面缺陷检测这几年在金属加工行业里反复被讨论的原因。而真正谈到落地绕不开的就是YOLO这一系列检测算法——从YOLOv5到YOLOv7、YOLOv8再到新出的YOLOv10同一个任务在不同的版本上跑性能和坑都大不一样。这篇文章是我在钢材表面缺陷检测项目上从零到一的完整记录数据集怎么准备、YOLOv5怎么训练、同一份数据下YOLOv7/8/10到底谁更强、部署到真实质检环节会遇到哪些实验室里想不到的问题。适合准备拿工业视觉课题练手的人也适合已经在做质检系统但想换一套更可靠模型的工程师。1. 表面缺陷检测为什么会选中目标检测这条路1.1 人工质检和传统视觉的局限先聊产线现状。钢材表面质检的传统方案一靠人眼二靠常规图像处理。人眼的短板是客观生理限制质检员盯着一块匀速运动的带钢表面注意力最多维持二十来分钟疲劳之后细小缺陷的漏检率会肉眼可见地上升。更麻烦的是一条产线的质检位往往要求连续运转好几个小时漏检带来的客诉和退货成本远高于多招一个质检员的成本。传统图像处理阈值分割、边缘提取、模板匹配在单一背景的样品图上很有说服力但钢材表面本身就不是干净背景——有氧化色差、有油污残留、有纹理起伏这些干扰在图像上的表现和真实缺陷高度相似。规则只要定得稍微严一点误检率就飙升定得松一点漏检又回来了。我见过有人用形态学运算试图把划伤和纹理噪声分开现场一换钢种整套参数立刻失效。1.2 缺陷的本质是找框加分类的问题把问题抽象一下无论裂纹、夹杂还是划伤在图像里都是一个局部区域有位置、有大小、有类别属性。这正好落在目标检测的模型输出空间里——矩形框标出位置分类头给出缺陷类型。所以表面缺陷检测天然适合目标检测架构而不是图像分类或语义分割。分类模型给不出位置信息没法指导现场定位缺陷分割模型需要像素级标注成本高到工业数据规模根本撑不起来。综合精度、速度、标注成本三者目标检测是那个最平衡的选择。这也是我在这个项目里始终坚持检测路线而不是分割路线的原因。1.3 六类典型缺陷各自的脾气钢材表面缺陷数据集里最常出现的六个类别识别难度差异非常大我这里逐个说一下它们的脾气裂纹细密的龟裂纹理颜色接近背景边界模糊模型最容易漏检。夹杂点状或短条状的异物压入对比度中等形状不固定。斑块大面积灰暗区域边缘柔和极易和光照不均混淆。麻点密集的小凹坑典型的小目标问题。氧化铁皮压入深色不规则块状与背景差异明显但形状多变。划伤细长线条长宽比很极端标注框很容易把无关背景包进去。这六类的共性是类内差异大、类间差异小。同一个划伤有的又深又长有的浅得像一层水印不同类型的缺陷之间比如斑块和氧化铁皮压入肉眼看起来都是深色块。模型要在这种数据上把类别分开不是单纯堆参数就能解决的。1.4 为什么最终落在了YOLO家族工业实时性要求让两阶段检测器先提候选框再分类基本出局YOLO这类单阶段算法用一次前向同时完成定位和分类在速度和精度之间取得了很好的平衡。而且YOLO系列版本迭代快、社区资料多、部署方案成熟从训练到导出再到嵌入式推理每个环节都有现成方案。对一个落地项目来说这比理论上更优但资料稀少的算法重要得多。2. 数据集准备先给模型定好天花板2.1 公开数据和现场数据怎么配合我做对比实验用的是公开的冷轧带钢表面缺陷数据集六类缺陷每类约三百张样本按8:2切成训练集和验证集。公开数据的好处是标注规范、类别齐全适合做算法验证和版本横向对比坏处是背景太干净和真实产线的光照、氧化色、油污环境存在差距。如果目标是部署上线我的建议是公开数据打底现场数据微调。先用公开数据把模型的基本能力训出来再采集几百张现场图片做增量训练。现场图片哪怕只有两三百张对真实场景的适配效果也远超纯公开数据。没有现场数据就直接上产线早晚会在某个钢种切换的时刻遭遇大规模误检。2.2 标注格式和目录组织YOLO系列统一使用txt格式的标注文件每个图像对应一个同名txt每一行代表一个目标框内容是类别id 归一化中心x 归一化中心y 归一化宽 归一化高。我整理了一个固定目录结构方便后续换模型时直接复用defect-dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── defect.yaml如果你手里的标注是VOC的xml或者COCO的json需要先转成YOLO格式。下面这个Python脚本是我一直在用的转换辅助逻辑很简单读取xml里的obj把xmin、ymin、xmax、ymax换算成归一化的cx、cy、w、h宽高除以图片宽高防止越界import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, img_w, img_h, class_names): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) cx ((xmin xmax) / 2) / img_w cy ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) name os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, name .txt), w) as f: f.write(\n.join(lines))有个细节很多人会忽略坐标系和归一化之前必须确认图片尺寸和标注软件里的尺寸一致。我遇到过标注软件自动压缩了预览图导出坐标和原图尺寸对不上训练时loss一直不降最后发现是标注坐标全偏了。2.3 数据增强里的一个关键取舍YOLO系列自带一套挺强大的数据增强包括mosaic四图拼接、mixup、HSV扰动、随机翻转、随机平移缩放。对缺陷检测来说mosaic对小目标尤其有效它把小缺陷放大到整图比例模型能学到更多上下文。但这里有个容易踩的坑mosaic在训练后期最好降权或关闭。因为mosaic生成的是拼接图真实产线图片不会长成这种四宫格样子训练到后期模型的分布会被拼接图带偏在正常验证图上反而发飘。我习惯把mosaic从1.0逐步降到0.3到0.5之间或者跑到一半epoch之后关掉。具体怎么设要看loss曲线遇到训练loss一直降、验证mAP却不动甚至往下掉先怀疑数据增强和真实分布的偏差。类别不平衡也得单独处理。公开数据里斑块、裂纹样本多麻点、氧化铁皮压入样本少如果直接用原始分布训练少样本类别的AP会很难看。最简单的解法是给少数类别多复制几轮过采样训练时人为拉平各类别样本比例。我实测过光这一步就能让麻点和氧化铁皮压入的AP提高5个点以上。3. YOLOv5实战训练配置、参数和过程分析3.1 环境准备和工程摆放YOLOv5的依赖非常轻核心就是PyTorch和它的一套配套库。我用的环境大概是这样Python 3.8、PyTorch 1.13、CUDA 11.7、单张显卡显存12G。跑s模型、640分辨率、batch 16完全没压力。数据配置文件defect.yaml是训练的第一步。文件内容不长但每个字段都有讲究path: ./defect-dataset train: images/train val: images/val nc: 6 names: 0: crazing 1: inclusion 2: patches 3: pitted_surface 4: rolled-in_scale 5: scratchespath字段是数据集根目录train和val是相对路径。nc必须和names里的类别数一致。有个低级但常见的错误names的索引顺序和标注txt里的类别id对不上结果模型把裂纹当成了划伤损失函数还以为自己在收敛。3.2 预训练权重、分辨率和训练参数训练命令我贴一下后面逐个参数解释python train.py \ --data defect.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --patience 30 \ --hyp data/hyps/hyp.scratch-low.yamlweights用yolov5s.pt而不是从头训练。COCO预训练权重里已经包含大量通用物体特征迁移到钢材缺陷这种小样本数据上收敛速度和最终精度都明显更好。工业数据集通常只有几千张从头训纯属浪费算力。img 640是训练分辨率。缺陷里很多是小目标有条件可以试1280对小缺陷检测有明显帮助但显存和推理耗时都会涨。我的建议是640起步先把整体流程跑通再决定要不要升分辨率。batch 16在显存允许时尽量大。大batch能让梯度的方向更稳定训练曲线更平滑。epochs 150对不到两千张图的数据集来说足够了。我开了早停patience 30如果连续30个epoch验证集mAP没有提升就自动停止能省掉无效训练时间。hyp选择low版本因为钢材缺陷数据量小增强太猛容易把模型训坏。3.3 训练过程监控的几个观察点训练开始后不要盯着终端里的loss数字胡思乱想要看三样东西loss曲线、验证集mAP曲线、PR曲线。一个典型过程是这样的前30个epoch训练loss快速下降验证集mAP从0直接爬到0.6附近这个阶段模型在学大局特征到epoch 80左右loss降速变缓mAP在小范围内震荡此时模型在打磨边界细节。如果这个阶段持续很久不涨大概率不是算力不够而是数据问题——我遇到过标注边界太松导致类别内特征发散的情况。另外记得看每类的AP而不是只看mAP这个总指标。我在YOLOv5s上的初步结果六类平均mAP0.5在0.75左右但单看裂纹只有0.55。如果不拆开看类别根本不会知道是裂纹拖了后腿。3.4 遇到mAP怎么调都上不去怎么办我最想强调的工作习惯是先查badcase再谈调参。把验证集里所有置信度在0.3到0.7之间的检测框导出成图片一张张过目至少要过一百张。绝大多数时候你会发现模型犯错是有规律的裂纹被标成背景麻点被一个更大的框错误覆盖斑块和氧化铁皮压入被互相混认。这些规律指向的是数据问题而不是模型问题——裂纹样本的标注边界是不是太含糊麻点的框是不是经常把两个挨着的缺陷画成一个我试过花了两周调超参最后AP只涨了一个点而重新洗了一遍标注之后裂纹AP直接涨了七八个点。这件事给我的教训是先把评估集搞干净再动模型。4. YOLOv7、YOLOv8、YOLOv10的横向实测与选型判断4.1 三个版本各自的核心变化YOLOv7主打的是结构设计和训练策略的改良E-ELAN结构让特征在不同层之间的流动更充分重参数化卷积把训练期的复杂结构在推理期折叠成单路卷积辅助训练头的设计让浅层也能学到更强的特征。整体思路是在保持推理速度的前提下把精度往上抬一截。YOLOv8则做了架构上的大调整检测头从anchor-based改成anchor-free不再需要预先定义锚框每个位置直接回归目标框的四条边主干里的C3模块换成C2f模块特征重用更充分检测头拆成分类和回归两个独立分支并用DFL损失做边界回归。这些改动让它在小目标和密集场景下更稳。YOLOv10的核心诉求是端到端无痛推理它通过一对多和一对一联合标签分配策略让模型推理阶段完全不需要NMS后处理同时用轻量化分类头、空间通道解耦下采样、秩引导块设计把网络瘦身。少掉NMS那一步延迟和省算力的效果非常实在。做个类比v7是在原有的发动机结构上做精细化调校v8是重新设计了点火和燃烧方式v10更激进直接把变速箱摘了让动力系统直连车轮。4.2 同一份数据上的实测数据为了尽量公平我在完全相同的训练集、验证集、超参和训练策略下微调了这几个模型输入分辨率统一640训练150个epoch。表格里的数字是我某一次实验的记录不同轮次会有波动所以不要把它当绝对标准看趋势就好模型参数量(M)mAP0.5mAP0.5:0.95单张推理耗时(ms,同GPU)YOLOv5s7.20.750.4810.5YOLOv736.90.790.5213.2YOLOv8s11.20.780.518.8YOLOv10s16.50.760.506.1几个稳定结论YOLOv7在这个任务上精度最高代价是模型大、推理稍慢YOLOv8s在精度和速度之间比较均衡YOLOv10s推理最快省掉NMS的额外耗时精度没有明显崩盘但也不是最强。值得注意的是v10对裂纹这类纹理型缺陷的AP普遍比v8低一到两个点可能和轻量化主干对纹理特征的提取能力下降有关。4.3 实际项目里怎么选版本我的选型建议按场景来分如果做的是教学演示、快速验证或者团队对某个框架最熟YOLOv5s仍然是非常好的起点资料多、坑少、周边工具齐全。如果质检工位对误检率要求高、现场的干扰物多YOLOv7的精度优势值得你拿算力去换。算力确实贵一点但漏一个缺陷导致的赔偿可能比一台显卡贵十倍。如果要做多路并发检测比如一条产线同时挂着四路相机YOLOv10的低延迟、免NMS特性可以显著降低算力成本。如果后续还想扩展分割、姿态等任务选YOLOv8系列更顺手整个家族的模型结构和工具链是统一的。不要唯指标论。我之前在某个工位测试时发现某些版本在库里AP数据很漂亮但导出到特定推理框架后个别算子不受支持被迫改图重训最后反而不如AP低零点几个点的旧版本好部署。兼容性、生态成熟度、团队熟悉度在工业项目里的权重比那1%的mAP差距高得多。5. 部署与落地模型跑起来只是开始5.1 ONNX与TensorRT导出训练完只是第一步产线上跑的是经过格式转换的推理版本。通用流程是PyTorch权重先导出成ONNX再转成TensorRT的engine格式做GPU加速。这么转一圈推理耗时往往能再降三分之一以上特别是在TensorRT开FP16之后。导出时有几个参数要提前想清楚固定输入尺寸还是动态尺寸batch为1还是大于1。工业相机通常一次给一张图固定尺寸、batch为1是最稳妥的方案。千万不要图省事直接开动态尺寸个别算子会因为shape不确定而被迫退回慢速路径加速效果大打折扣。另外导出后务必用同一张图对比PyTorch原模型和导出版本的输出确认框坐标和类别没有漂移。数值精度上FP16可能让置信度偏百分之零点几但如果框的位置变了说明某个算子被错误替换这时候要回头更新推理框架版本。5.2 现场误检的来源和兜底手段实验室里再漂亮的指标到产线上都要重新面对现实污染。我总结过真实场景误检的几大来源光照随车间时段变化、不同钢种表面氧化程度不同、相机镜头有灰尘和反光、钢板上有水渍油渍残留、质检员在板上留了记号笔痕。应对手段通常分四个层次采集现场负样本把常见干扰物单独处理或作为纯背景图片混入训练集参与负样本学习在推理端适当拉高置信度阈值宁可不报也不要每张图乱报对同一物理位置的连续多帧输出做时间维度的投票只有连续N帧都检出同一个位置的缺陷才对外报警能滤掉大部分偶发误检在上位机做规则约束比如缺陷面积小到不构成质量影响、距带钢边缘太近就不报警这些规则很适合过滤掉相机反光造成的边界误判。5.3 相机、光源与整个系统的延迟预算这其实是容易被算法工程师忽略的部分。钢材产线的带钢运动速度、检测幅宽、缺陷允许的最小尺寸共同决定了系统的帧率和延迟预算。举个例子如果带钢速度是3米每秒检测幅宽1米相机分辨率决定最小可检缺陷尺寸那么相机触发频率、曝光时间、模型推理时间、结果回传PLC的时间加起来必须控制在一个运动周期以内否则缺陷即使被检测到到达执行机构时也已经跑过了报警位置。光源的选择同样关键明场照明适合找划伤和麻点这类有方向性的缺陷暗场或低角度光源适合突显压入的氧化铁皮。很多项目最后模型精度高但现场效果差问题不在模型而在图像采集那一端。5.4 让模型持续进化的回流闭环模型部署上线不是终点。我的做法是建立一套脏数据回流机制每两周收集一次现场的误检和漏检图片人工复核后补充进训练集和验证集做一轮增量训练再替换线上模型。钢材质检的特点就是钢种和工艺会变缺陷分布也跟着变模型必须持续适配否则上线三个月之后精度就会悄悄滑坡。顺便说一个部署工程师常踩的坑评估新模型时直接拿新图片和旧模型的输出做对比而不去复盘新模型为什么变了。有时候新模型改了置信度分布把所有框的分数整体压低导致报警策略的阈值失效。这种情况要先对齐置信度分布再去比对检测效果否则会被正常的分布偏移当成故障来排查。6. 复盘这次折腾下来最想保留的几个习惯第一永远先把评估集整理干净。不管是公开数据还是现场数据标注质量决定了模型能力的天花板模型结构和超参只是决定在天花板底下能飞多高。第二一切结论都要落到每类AP和badcase上。总mAP会骗人单类AP不会。裂纹、麻点这类难例的AP比综合指标更能反映模型能不能上产线。第三选型时要把部署链路当一个整体来评估。同是YOLO家族v5、v7、v8、v10的导出兼容性和周边生态完全不同选型不是选模型是选一整套从数据到部署的方案。第四建立持续迭代的机制比任何一次训练都重要。现场数据会变模型必须跟着变设计好回流和重训的节奏系统才真正能用。我最后想说的是钢材表面缺陷检测这个任务模型本身只占整个项目的一小半工作量。前期数据、中期选型、后期部署每一环都是在为模型的可落地性打工。如果你正在做类似的项目别急着换更先进的模型版本先把数据和评估流程做扎实回报会比想象中大得多。