YOLOv5焊缝质检全流程:从数据集标注到PyQt界面部署 简介本资源面向工业质检与计算机视觉方向的开发者、学生及研究人员提供一套基于YOLOv5的焊缝质量好坏检测完整方案可用于钢材缺陷检测场景的模型训练、推理与界面演示。压缩包共约2000个文件整体约222.96MB其中jpg图片1134张为原始样本xml与txt各1134份分别对应LabelImg标注的两种标签格式便于直接接入不同训练流程。资源内含已训练好的检测模型并附PR曲线、loss曲线等训练过程记录方便评估模型收敛情况与检测精度。PyQt界面支持图片、视频及摄像头三种输入方式可通过选项切换适合快速验证与现场演示。目前已有987人学习下载适合希望掌握缺陷检测全流程、复用数据集与权重进行二次开发或课程实践的读者参考。1. 焊缝质检从人工目检到 YOLOv5一条产线级落地路径焊工师傅拿着强光手电趴在工件上找气孔、咬边、未熔合一天下来眼睛发花漏检率还压不住——这是很多中小制造厂的真实状态。焊缝质量好坏检测要解决的就是把这种依赖老师傅经验的目检换成一套能跑在工控机上的自动判别系统。YOLOv5 焊缝质量好坏检测 权重 数据集 pyqt 界面这套组合的价值在于它不是一个只跑在实验室的 demo而是从数据标注、模型训练、权重导出到桌面端交互的完整闭环车间质检员点一下按钮就能出结果。这篇文章面向三类人想用 YOLOv5 训练自己数据集但卡在焊缝场景的算法工程师、需要给产线做一套可交付质检工具的集成开发者、以及手上有标注数据却不知道怎么串成产品的技术负责人。我会按「数据怎么准备 → 模型怎么训 → 权重怎么用 → PyQt 界面怎么接」的顺序拆开讲中间穿插焊缝检测特有的坑比如缺陷尺度差异大、正负样本极度不均衡、工业相机成像的灰度分布和 COCO 完全不是一回事。读完你应该能判断这个方向值不值得投入以及第一步该动哪里。2. 焊缝数据集怎么建从工业相机采图到 YOLO 格式标注2.1 焊缝缺陷的类别定义与标注边界焊缝质量好坏检测本质是一个目标检测任务但类别定义直接决定模型能不能落地。常见做法是把缺陷分成气孔、夹渣、未熔合、咬边、裂纹这几类再加一个「合格焊缝」类做对照。这里有个血泪经验不要一上来就分七八类标注一致性根本保证不了模型学到的全是标注噪声。我一般建议先做二分类——合格 / 不合格跑通全流程后再细分缺陷类型。标注边界是另一个翻车高发区。气孔是圆形暗斑边界清晰咬边是焊缝边缘的凹槽边界模糊。如果两个标注员对咬边的起止位置理解不一致同一张图能标出完全不同的框。解决办法是写一份标注规范文档规定每类缺陷的最小外接矩形怎么取、模糊区域怎么处理、小于多少像素的缺陷直接忽略。这份文档比模型结构重要得多。工业相机采图还要注意曝光。焊缝表面反光强烈过曝区域会丢失纹理欠曝又看不清气孔。建议用环形光源加漫射板固定曝光时间和增益保证同一批次图像灰度分布一致。如果现场光照条件变化大训练集里必须覆盖不同光照样本否则模型上线后遇到阴天或换灯管就集体翻车。2.2 从原始图像到 YOLO 标注文件的转换脚本YOLOv5 要求标注格式是每行class_id x_center y_center width height坐标全部归一化到 0~1。如果你用 LabelImg 或 Labelme 标的是 VOC 的 XML需要转一道。下面这个脚本处理 VOC 转 YOLO同时做图像尺寸校验和越界框修正。import os import xml.etree.ElementTree as ET from PIL import Image # 类别映射顺序必须和训练时的 data.yaml 一致 CLASS_MAP {porosity: 0, slag: 1, lack_of_fusion: 2, undercut: 3, crack: 4} def voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 用实际图像尺寸而不是 XML 里的 size避免标注工具写错 img_name root.find(filename).text img_path os.path.join(img_dir, img_name) with Image.open(img_path) as im: w, h im.size lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in CLASS_MAP: continue bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 越界框裁剪工业图像边缘常有标注溢出 x1, x2 max(0, x1), min(w, x2) y1, y2 max(0, y1), min(h, y2) if x2 - x1 2 or y2 - y1 2: continue # 过滤掉小于 2 像素的无效框 xc (x1 x2) / 2.0 / w yc (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{CLASS_MAP[cls_name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) if lines: txt_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines)) voc_to_yolo(./annotations, ./images, ./labels)逻辑说明脚本先读 XML 里的文件名再用 PIL 打开原图取真实宽高这一步很关键——很多标注工具在图像被旋转或缩放后会写错 size 字段用真实尺寸能避免坐标错位。越界裁剪和最小框过滤是焊缝场景的刚需因为焊缝边缘的咬边标注经常超出图像边界不处理会导致训练时 loss 异常。参数说明CLASS_MAP的键必须和标注文件里的name完全一致大小写敏感。x2 - x1 2这个阈值可以按你的图像分辨率调整高分辨率图像可以放宽到 5 像素。输出目录要和图像目录同级YOLOv5 默认按images/xxx.jpg找labels/xxx.txt。2.3 数据集划分与焊缝场景的样本均衡划分训练集 / 验证集 / 测试集常见做法是 7:2:1。但焊缝检测有个特殊问题合格样本远多于不合格样本如果随机划分验证集里可能一个缺陷都没有mAP 看着高其实没学到东西。我一般用分层抽样保证每个集合里各类缺陷的比例接近整体分布。样本不均衡的另一个坑是背景图太多。产线上大部分焊缝是合格的如果直接把所有合格图都塞进去模型会倾向于预测「无缺陷」。解决办法是控制合格图比例比如合格 : 不合格 1:1 到 2:1 之间多余的合格图做负样本挖掘时再用。YOLOv5 训练时可以用--balance参数做简单均衡但效果有限更稳的是在数据层面控制。提示焊缝数据集的图像数量不是越多越好。500 张标注精准的图效果往往好过 5000 张标注粗糙的图。先保证标注质量再考虑扩量。3. YOLOv5 训练焊缝检测模型超参数怎么调才不翻车3.1 从预训练权重到焊缝数据集的迁移配置YOLOv5 训练自己的数据集第一步是改data.yaml。这个文件告诉模型去哪里找图、有几类、类名是什么。焊缝场景的data.yaml大概长这样path: ../welding_dataset train: images/train val: images/val test: images/test nc: 5 names: [porosity, slag, lack_of_fusion, undercut, crack]nc是类别数必须和标注文件里的 class_id 最大值加一一致。names的顺序就是 class_id 的顺序写反了模型会把气孔认成夹渣。路径可以用相对路径相对于data.yaml所在位置。预训练权重用yolov5s.pt还是yolov5m.pt取决于你的算力和精度要求。焊缝缺陷尺度差异大小气孔可能只有十几个像素咬边可能横跨半张图。yolov5s速度快但小目标召回率一般yolov5m在焊缝场景通常更稳。如果工控机是 Jetson 或 RK3568 这类边缘设备建议先用yolov5s跑通再考虑量化。训练命令的核心参数python train.py \ --img 640 \ --batch 16 \ --epochs 200 \ --data data/welding.yaml \ --weights yolov5s.pt \ --cfg models/yolov5s.yaml \ --hyp data/hyps/hyp.scratch-low.yaml \ --cache--img 640是输入分辨率。焊缝小缺陷多的话可以提到 1280但显存占用翻倍batch 要相应降到 8 或 4。--cache把图像缓存到内存加速训练但数据集超过 10GB 就别开了内存扛不住。--hyp用低增强配置因为焊缝图像的几何变换旋转、剪切会破坏缺陷的形态特征比如气孔的圆形被拉成椭圆就不对了。3.2 焊缝检测的锚框聚类与超参数调整YOLOv5 默认锚框是基于 COCO 聚类的和焊缝缺陷的尺度分布不匹配。焊缝缺陷往往更小、更扁直接用默认锚框会导致小目标召回率低。正确做法是用kmeans在自己的数据集上重新聚类锚框。python utils/autanchor.py --data data/welding.yaml --img-size 640 --thr 4.0 --n 9这个脚本会输出 9 个锚框的宽高按从小到大排列。把结果替换到模型配置文件里的anchors字段。--thr是 IOUs 阈值焊缝场景建议用 4.0 而不是默认的 4.0 以上因为缺陷框重叠少阈值可以放宽。学习率是另一个玄学参数。YOLOv5 默认用余弦退火初始学习率 0.01。焊缝数据集小的话几千张学习率降到 0.001 更稳否则容易在前期就过拟合。如果训练 loss 震荡厉害先检查标注有没有问题再调学习率。我见过太多人 loss 不降就怪模型最后发现是标注文件里 class_id 写错了。数据增强方面焊缝场景要慎用 Mosaic。Mosaic 把四张图拼成一张对小目标检测有帮助但焊缝缺陷的上下文很重要——气孔周围的焊缝纹理是判断依据拼图会破坏这种上下文。建议 Mosaic 概率降到 0.5 以下或者训练后期关掉。HSV 增强可以保留工业图像的灰度变化本来就需要模型适应。3.3 训练过程监控与早停策略训练启动后看runs/train/exp下的results.csv和train_batch*.jpg。train_batch图能直观看到数据增强后的样本长什么样如果增强后的图连你都认不出缺陷模型更学不会。results.csv里重点看metrics/mAP_0.5和val/box_loss。mAP 涨、val loss 降说明在学mAP 平、val loss 涨过拟合了该早停。YOLOv5 自带--patience参数默认 100 轮没提升就停。焊缝数据集小建议设成 30~50省时间。早停后取best.pt而不是last.ptbest.pt是验证集 mAP 最高的权重泛化更好。如果训练到一半发现 mAP 卡在 0.5 上不去先别急着调模型。用验证集跑一遍推理把预测框画出来看。常见问题是模型把咬边和未熔合搞混这两类在灰度图上确实像。解决办法是在标注规范里把这两类的边界定义得更清楚或者干脆合并成一类「边缘缺陷」。注意训练日志里的P和R要一起看。精度高召回低说明模型保守漏检多召回高精度低说明误检多。焊缝质检通常更怕漏检所以优先保召回精度可以通过后处理阈值调。4. 权重文件怎么用推理、导出与边缘部署4.1 用训练好的权重跑单张与批量推理训练完拿到best.pt第一步是验证它在测试集上的表现。YOLOv5 的detect.py可以直接跑python detect.py \ --weights runs/train/exp/weights/best.pt \ --source ./test_images \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --save-conf--conf-thres是置信度阈值低于这个值的框被丢弃。焊缝检测建议从 0.25 开始调漏检多就降到 0.15误检多就升到 0.4。--iou-thres是 NMS 的 IoU 阈值焊缝缺陷重叠少可以设到 0.5 以上避免把相邻的两个气孔合并成一个。--save-txt把检测结果存成 YOLO 格式的 txt方便后续做统计。批量推理时如果图像数量大加--nosave只存 txt 不存图速度快很多。推理结果里的conf字段是置信度可以按这个字段做二次过滤。比如只保留 conf 0.5 的框或者对同一张图里置信度最高的框做特殊处理。4.2 权重导出 ONNX 与 TensorRT 的注意事项工控机部署通常不用 PyTorch 直接跑而是导出 ONNX 或 TensorRT。YOLOv5 的export.py支持多种格式python export.py \ --weights best.pt \ --include onnx \ --img 640 \ --batch 1 \ --opset 12 \ --simplify--opset 12是 ONNX 算子集版本太低不支持某些算子太高有些推理引擎不认。--simplify用 onnx-simplifier 简化计算图能去掉冗余节点加速推理。导出后一定要用onnxruntime跑一遍验证输出和 PyTorch 一致数值误差在 1e-3 以内算正常。导出 TensorRT 时--include engine需要本机装 TensorRT。焊缝检测如果部署在 Jetson 上建议用 FP16 而不是 INT8INT8 量化对小目标的精度损失比较明显。如果必须用 INT8校准集里一定要包含各种缺陷样本否则量化后的模型可能把气孔直接忽略掉。RK3568 这类 NPU 部署是另一个路线需要把 ONNX 转成 RKNN。转换时注意输入尺寸和归一化参数要和训练时一致YOLOv5 的归一化是x/255如果 RKNN 配置里写成x/127.5 - 1结果会全错。这个坑我踩过排查了一整天才发现是归一化对不上。4.3 推理结果的后处理与置信度校准模型输出的原始框需要后处理才能用。YOLOv5 的detect.py已经做了 NMS但如果自己写推理代码要注意几个点。第一输出是[batch, num_anchors, 5nc]前 5 个是x, y, w, h, obj_conf后面是类别概率。第二obj_conf和类别概率要相乘才是最终置信度。第三NMS 之前要按置信度排序否则结果不稳定。置信度校准是焊缝质检的特殊需求。模型输出的 0.8 置信度不代表 80% 的概率是缺陷。工业场景需要的是「宁可错杀不可放过」所以可以做一个简单的校准对每个类别统计验证集上的精度-召回曲线选一个让召回率不低于 95% 的置信度阈值。这个阈值通常比默认的 0.25 低可能在 0.1 左右。后处理还有一个容易忽略的点框的坐标要映射回原图。如果推理时做了 letterbox 缩放后处理时要反向变换。YOLOv5 的scale_coords函数就是干这个的自己写的话注意 padding 的偏移量。5. PyQt 界面集成从模型加载到质检结果可视化5.1 PyQt5 主界面布局与模型加载线程PyQt 界面要把模型推理、图像显示、结果统计串起来。核心问题是推理是耗时操作不能放在主线程否则界面卡死。正确做法是用QThread把推理放在子线程通过信号槽把结果传回主线程更新 UI。from PyQt5.QtCore import QThread, pyqtSignal import torch class InferenceThread(QThread): result_ready pyqtSignal(object, object) # 原图, 检测结果 def __init__(self, model_path, img_path): super().__init__() self.model torch.hub.load(./yolov5, custom, pathmodel_path, sourcelocal) self.model.conf 0.25 self.img_path img_path def run(self): # 推理在子线程执行不阻塞 UI results self.model(self.img_path) self.result_ready.emit(results.render()[0], results.pandas().xyxy[0])逻辑说明torch.hub.load加载本地 YOLOv5 仓库和权重sourcelocal避免联网下载。results.render()返回画了框的图像results.pandas().xyxy[0]返回检测结果的 DataFrame包含坐标、置信度、类别。信号result_ready携带两个对象主线程收到后更新 QLabel 和 QTableWidget。参数说明self.model.conf是置信度阈值可以在界面上做成滑块让用户调。self.model.iou是 NMS 阈值默认 0.45。如果界面要支持批量检测把img_path换成列表在run里循环处理每处理完一张发一次信号。5.2 检测结果表格与缺陷统计面板界面右侧通常放一个表格显示每个缺陷的类别、置信度、坐标。用QTableWidget实现列头设成「类别 / 置信度 / X1 / Y1 / X2 / Y2」。每次推理完清空表格再填充注意用setRowCount而不是逐行插入后者在缺陷多的时候会卡。统计面板显示合格 / 不合格数量、各类缺陷占比。可以用QLabel加QProgressBar做简单可视化。合格判定逻辑如果检测结果里没有任何缺陷框或者所有框的置信度都低于阈值判为合格否则不合格。这个逻辑要可配置因为不同客户对「合格」的定义不一样。图像显示区域用QLabel的setPixmap注意图像要按比例缩放到控件大小否则会变形。用QPixmap.scaled时保持Qt.KeepAspectRatio。如果原图很大先缩放到合适尺寸再显示避免内存暴涨。5.3 界面与推理后端的解耦设计界面代码和推理代码要分开否则换个模型或改个后处理逻辑就要动 UI 文件。我一般把推理封装成一个类界面只调用这个类的接口。比如class WeldDetector: def __init__(self, weights, conf0.25, iou0.45): self.model torch.hub.load(./yolov5, custom, pathweights, sourcelocal) self.model.conf conf self.model.iou iou def detect(self, img_path): results self.model(img_path) df results.pandas().xyxy[0] # 返回统一格式列表每个元素是 (类别, 置信度, x1, y1, x2, y2) return [(row[name], row[confidence], row[xmin], row[ymin], row[xmax], row[ymax]) for _, row in df.iterrows()] def is_qualified(self, detections, conf_thr0.5): # 合格判定没有置信度超过阈值的缺陷 return all(d[1] conf_thr for d in detections)这样界面只依赖WeldDetector的detect和is_qualified两个方法换模型、改阈值、加后处理都不影响 UI。is_qualified的阈值可以做成界面参数不同产线要求不同。提示PyQt 界面打包成 exe 时YOLOv5 的模型文件和依赖库要一起打包。用 PyInstaller 的话--add-data把 yolov5 目录和权重文件加进去否则运行时报找不到模块。6. 焊缝检测落地的几个硬核技巧与验证方法模型训完、界面跑通不代表能上线。焊缝质检的验收标准是漏检率不是 mAP。我一般用一批产线上真实采集的、没参与训练的图做盲测统计漏检和误检。漏检的定义是有缺陷但模型没框出来或者框的 IoU 低于 0.3。误检是没缺陷但模型框了或者类别判错。盲测至少 200 张覆盖不同光照、不同工件。如果漏检集中在某类缺陷先看训练集里这类样本有多少。少于 50 个实例的话模型很难学好需要补标。补标时优先标那些模型漏掉的图这叫难例挖掘。YOLOv5 可以用--save-txt把推理结果存下来和标注对比自动找出漏检的图。另一个技巧是测试时增强TTA。推理时把图水平翻转、缩放几个尺度分别推理再合并结果能提升 1~3 个点的 mAP。代价是推理时间翻倍工控机性能够的话值得开。YOLOv5 的detect.py有--augment参数但自己写推理代码时要注意合并逻辑同一缺陷被多次检测到要按 IoU 去重。置信度阈值不要拍脑袋定。用验证集画 P-R 曲线找召回率 95% 对应的置信度。这个阈值通常比默认值低可能在 0.1~0.2。界面上可以放一个滑块让质检员微调但默认值要用这个校准过的。最后说一个我踩过的坑模型在实验室的图上表现很好到产线上就翻车。原因是产线相机的白平衡和实验室不一样图像整体偏黄。解决办法是在产线相机上采一批图混进训练集做微调。微调时学习率降到 0.0001只训 20~30 轮避免破坏已经学好的特征。这个习惯我保持到现在任何模型上线前一定用现场数据做一次微调验证不然就是给自己埋雷。希望帮到你。本文还有配套的精品资源点击获取