资讯动态

深度学习焊接缺陷检测:从射线底片到YOLO落地全流程

发布时间:2026/10/20 3:30:42 来源:尧图企业网站定制
焊接质检这个活儿很多人觉得不就是拍张片子找裂纹嘛真入行才知道坑有多深。尤其像射线底片这类焊评底片老法师看一眼能给你说出门道来但人眼会疲劳、标准会波动、老师傅还会退休。我拿Python折腾这套深度学习焊接缺陷检测系统前后换过三条技术路线踩了无数标注的坑、部署的坑最后总算是把手动看片的流程跑成了半自动流水线。这篇就把整个思路捋一遍从数据准备到模型选型再到最容易被忽略的落地细节一次讲透。这套系统能做的事简单说就是拿深度学习模型替代一部分人工评片的工作自动识别气孔、夹渣、未熔合、咬边、裂纹这几类最头疼的焊接缺陷类型并在原图上框出位置、标出置信度。适合正在做工业视觉质检项目、被检测精度和现场部署来回折磨的工程师也适合想入门缺陷检测但不知道从何下手的同学参考。1. 焊接质检里的痛点比想象中更难啃先聊一个反直觉的事焊缝缺陷检测的技术难点其实不在“检测算法”本身而在“数据长什么样”。焊接缺陷图像和自然图像的差别非常大。拿射线底片比如X射线或γ射线数字成像来说缺陷往往只占整张图像的百分之零点几的像素面积裂纹是细长条气孔是小圆点夹渣是一小坨明暗不均的区域。人眼看底片的时候是靠多年经验在非常复杂的背景纹理里找“不自然的局部异常”。这种找异常的逻辑和传统视觉算法里那种“设定阈值提取灰度轮廓”的套路完全不是一回事光照一变化、板材厚度一变、焊道余高一变阈值就崩了。这也是为什么早年间很多工厂上过传统的图像处理方案最终都退回人工了——不是算法工程师不行是问题本身的分布太散。深度学习为什么能干这个活核心在于卷积神经网络学到的不是固定阈值而是“缺陷的概率分布特征”。它能在大量样本中自动提炼出气孔和飞溅的灰度差异、裂纹和划痕的形状差异。但前提是你得给它喂足够多、标注足够一致的样本。我后来在实际项目中最大的感受是焊接缺陷检测项目的成功与否七成取决于数据准备阶段三成才轮到模型和调参。另外缺陷检测的行业标准也复杂。像气孔、夹渣、未熔合、未焊透、裂纹、咬边每种缺陷都有评级规范不同标准体系评定要求也各不相同。单模型直接出“合格/不合格”很容易闹笑话所以最开始只做缺陷分类和定位是更合理的技术切分把判定规则留着跟工艺规范对齐。系统定位成辅助评片工具决策权仍在人手里这个边界务必守好。2. 数据从底片到数据集的完整加工流程2.1 样本采集先搞清楚你可用的数据在哪焊接缺陷检测项目最理想的数据来源是工厂历史存档的射线底片和数字检测报告。但现实是很多厂的底片是胶片存底的要先用数字化设备扫一遍分辨率、对比度参差不齐。更麻烦的是底片的档案记录经常只有结论没有图像或者图像里带了各种遮蔽框、字符标记真正有效区域要人工裁出来。如果条件像我做的这个项目一样一开始只有几百张带标记的缺陷截图那就得靠“小样本起步 持续扩充”的策略。先把已有截图按缺陷类别归档再跟质检老师傅确认每类在图上的典型形态。这一步千万别省因为不同厂用的焊接工艺不同缺陷形态差异很大——埋弧焊的气孔和手工焊的气孔长得就不一样用过气电立焊的更是另一番景象。我就吃过这个亏第一版模型拿标准气孔图训完到现场测的时候被一堆“带尾巴的密集气孔”打懵了。2.2 标注工具与格式转换标注是另一个大坑。检测标注我用的是LabelImg和CVAT换着来本地少量标注用LabelImg批量多人标注用CVAT更方便。标注规范一定要先写好哪类虚线算裂纹、哪类算咬边灰度低于背景的是气孔、亮于背景的是夹渣这些规则不提前统一两个标注员能给同一张图标出完全不同的框。标注产出的是Pascal VOC格式的XML文件或者COCO格式的JSON但训练用的大多是YOLO格式的txt。我自己写了个转换脚本把XML转成YOLO需要的归一化坐标这一步的坑在于坐标系别搞反X、Y中心坐标必须除以图像宽度和高度而不是直接除以实际像素尺寸就完事。转完还要做一轮可视化校验把标注框画回原图上人工扫一遍不协调的框直接过滤掉。2.3 数据增强的边界别把缺陷给增强没了工业检测里数据增强不能乱来。像随机裁剪、平移、旋转这类几何增强是安全的因为实际拍摄时零件摆放本来就有角度偏差。但色彩抖动、对比度剧烈变换这类操作要慎用焊接缺陷图像的核心特征就是灰度对比关系颜色增强很容易把缺陷“增强”成背景。我实测下来最适合焊接底片场景的增强组合是水平翻转、垂直翻转、随机旋转90度、轻微缩放和移位。这样既能把样本量撑起来又不会破坏缺陷的真实特征。3. 模型选型为什么我从分割模型转向单阶段检测3.1 先别急着上大模型第一版我走的是语义分割路线用类似UNet的编码器-解码器结构对每个像素做缺陷分类。原理上讲这是最贴近“找异常区域”这个任务本质的做法理论上对细长裂纹应该很友好。但实际跑下来问题很多一是标注成本翻了倍逐像素标注一张底片太慢老师傅根本不愿意配合二是分割模型的输出结果是掩膜后续要自己再写逻辑把掩膜转成目标框、连成连通域后处理链路长了误差就大了三是小目标缺陷在分割任务里很容易被背景类淹没损失函数不调整的话整个掩膜输出几乎是全黑的。后来我果断切到了目标检测路线。单阶段检测网络把“分类 定位”合在一个网络里直接输出边界框和类别对气孔、夹渣这类离散缺陷友好很多。虽然细长裂纹的定位精度不如分割理想但考虑到现场评片本来就需要把缺陷区域框出来让人复核检测框的粒度已经够用了。3.2 对比过的那几类模型选型逻辑是什么我在项目里实际对比过的路线有三条两阶段检测、单阶段检测、以及分割模型做辅助。简单说说差异模型路线代表小目标缺陷表现训练与部署成本适合场景两阶段检测Faster R-CNN比较好候选框机制对小目标友好训练慢部署体积大样本量少但对精度要求极高比如科研验证单阶段检测YOLO系列、RT-DETR中上新版本靠多尺度融合改善明显训练快部署友好工业落地首选实时性要求高、现场反馈迭代快的场景分割模型UNet、Mask R-CNN对裂纹形态刻画精细标注成本高后处理复杂缺陷形态极端复杂、需要精确轮廓的专项分析最终我选的是YOLO系列的某个版本作为主力模型原因很直接工业现场多半是离线跑批对单张图推理时间有要求但不是极致苛刻YOLO能在保证精度的情况下把训练迭代周期压到很短——现场收集一批新缺陷图当天就能迭代一版模型这个优势在真实产线上太重要了。3.3 针对焊接缺陷的模型配置心得模型当然不是装个预训练权重就完事有几个配置细节是在实际测试里反复调出来的第一输入尺寸不要贪大。焊接底片动辄几千乘几千像素直接resize到640会让小缺陷直接消失。折中的办法是把大图先切成带重叠区域的patch比如1024x1024的块每块重叠200像素再把推理结果拼回去。重叠区域是为了避免目标正好在切割缝上被截断。第二anchors参数的聚类很重要。用数据集重新算锚框尺寸别用默认的COCO锚框。因为气孔和裂纹的形状比例与自然图像里的物体完全不同默认锚框会导致召回率奇低。我就经历过训练完loss很漂亮但验证集上一片小气孔都找不到的诡异情况后来发现就是锚框尺度跟目标尺寸完全错位。4. 训练脚本与推理脚本的落地细节4.1 标签格式转换脚本务必先跑通再标数据前面说过标注格式的坑这里给一份我用的XML转YOLO格式核心代码标注成VOC格式的话直接跑这个转换就行import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_path, txt_path, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines)) # 用法 class_names [porosity, slag, lack_of_fusion, undercut, crack] xml_to_yolo(data/001.xml, data/001.txt, class_names)这段代码看着简单但每次跑完一定要做可视化验证不要轻信转换结果。我曾经因为xml里坐标读错字段导致训练集里一半框都在图像左上角训练了一个星期才发现。可视化脚本就是读txt、画框、存图、人工扫一遍十分钟的事省下的时间是按天算的。4.2 训练前的目录结构和参数选择YOLO系列训练对目录结构有固定要求images和labels分开放train、val子目录各一份。实操时我习惯再加一份test集因为单独做指标计算更干净。目录结构大概率长这样datasets/ weld_defect/ images/ train/ val/ test/ labels/ train/ val/ test/模型参数方面有个容易被忽略的焊件底片图像普遍是灰度图原样读进来就是单通道。很多检测框架默认按三通道处理会直接复制通道等于把同一张图喂了三遍没毛病但也没好处。真要想省显存可以在预处理阶段对图像做灰度直方图均衡化提升对比度让缺陷区域更明显。这一步比调什么模型结构都见效。4.3 数据增强放在训练流程里的位置我用的增强组合集中在弱几何变换用库实现也简单。要注意的是增强参数要谨慎像旋转角度太大、翻转后语义不变这类不适合所有工业场景——比如焊缝方向有固定朝向时垂直翻转会改变“上坡焊/下坡焊”的语义模型学到的特征就被搞混了。稳妥起见先只在预处理里做亮度微调和轻微噪声把硬增强留给后续版本迭代时再上。4.4 训练到部署的完整流程训练完成后导出的权重文件可以直接用推理脚本跑。核心就是加载模型、读图、预处理、推理、后处理加画框标注。如果要在流水线上跑批脚本里还得加一个“缺陷结果汇总模块”把所有检测框信息输出成结构化报表——记录缺陷类型、位置、置信度、对应的工件编号这样后端MES或质检系统才能对接。我当时部署推理的环境是一台带显卡的工控机图像从现场设备拍完后传到这台机器Python脚本跑批出结果整个过程控制在单张图两秒以内现场完全能接受。5. 指标、调参与结果复盘5.1 别只看mmAP工业场景最该盯的是召回率模型训练完第一件事不是刷新loss曲线看热闹而是盯住“每类缺陷在验证集上的检出率”。焊接缺陷检测最大的损失是什么是漏检。气孔漏检十个还能解释成小目标遮挡裂纹漏检一条可能就是重大安全事故的隐患。所以我把评价指标重点放在各类别的召回率上尤其是裂纹这一类宁可把置信度阈值调低一点、多给出几个疑似框让复核人员自己筛也不能让缺陷悄悄溜走。具体做法是对验证集单独做一次预测分析把预测框和标注框做IoU匹配按类别统计TP、FP、FN画混淆矩阵看哪些类经常混。我最常见的混淆是夹渣和未熔合——在灰度图上这俩长得确实像都是不规则的暗区。解决办法是回到数据集细化标注规则找老师傅明确“层间未熔合看位置靠近母材侧的细长暗带是未熔合形状不规则的金属氧化物残留是夹渣”然后重新清洗了这部分标签模型效果立刻上了一个台阶。5.2 老数据也要定期回炉模型上线后发现一个重要问题前几天效果挺好过一阵子焊缝形态变了换了焊丝品牌或者焊工手法微调检出率就会掉。深度学习模型是个“经验主义者”它只相信自己见过的模式。应对办法是把现场新增的有问题样本每周回流到训练集做一轮增量训练。这个持续迭代机制看着不起眼但比任何算法优化都实在——模型要是不更新再先进的网络结构也会慢慢“过期”。6. 车间部署时那些文档里不会写的坑6.1 硬件选型别一刀切上高端卡很多人一听到深度学习部署就下意识想到高端显卡。真正下到车间你会发现焊接场景有粉尘、有震动、电压还可能不稳高配显卡机器在恶劣环境下的故障率并不低。我把推理服务做成C/S架构训练在有GPU的工作站完成部署端用普通的无风扇嵌入式工控机加一块低功耗推理卡跑单张图推理300毫秒左右稳定性比大显卡工控好很多整体功耗也低。如果现场连GPU都没有退一步用CPU做推理也不是不行代价是单张图可能要一两秒看产线节拍够不够用。6.2 图像采集端的笨问题才致命部署时最头疼的往往不是模型是图像质量不一致。不同检测设备拍的底片分辨率不同、灰度范围不同、甚至焊缝在图里的位置都不同。模型训练时用的图像宽高比和现场不一致预测结果就会系统性偏移。我在预处理阶段写了个标准化模块管你是哪个设备来的图先统一大小、统一灰度范围、统一归一化方式再送进模型。上线之后先拿历史数据回测确认模块处理过的图像指标不降再切新图进来。6.3 不是所有缺陷都该框出来最后说一个非常现实的认知现场需要的不是让模型检测出所有异常而是让模型检测出值得处理的缺陷。焊缝余高在底片上形成的正常纹理会被模型误判成“疑似夹渣”飞溅颗粒会误判成“疑似气孔”。这类误检光调IoU阈值没用要从两个方向下手一是数据层面在多批正常纹理样本里加入负样本和难负样本让模型学“这些不是缺陷”二是逻辑层面结合灰度统计和连通域面积做后处理把面积过小的疑似目标直接过滤。我个人的体会是一个准确的“不检出列表”往往比模型精度提升一个点有更大工程价值。现场老师傅看一次误检报火警之后就不太信这套系统了信任这东西一旦破了后面再解释“阈值调高了就会少误报”都是苍白无力的。顺便说一句如果项目和焊评底片打交道图片里常常包含各种设备编号、拍片日期之类的字符水印。模型在训练时如果图像里总带着这些字它可能会把水印区域当成背景特征来学推理时遇到没有水印的新图反而会下降。我后来在预处理阶段统一做了字符区域裁剪或者掩膜遮挡整体精度又回升了一点。这类细枝末节基本上只有亲手跑过一轮完整项目的人才会意识到。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑