简介:YOLOv8焊缝质量好坏检测完整方案,面向工业质检与智能制造领域的算法工程师、科研人员及目标检测初学者,覆盖数据准备、模型训练、效果评估与部署调用全流程。资源包内置训练好的模型权重,附带了训练阶段的PR曲线、loss曲线等评估图表,可以直接用于焊缝好坏的实时检测;同时配有经labelimg标注的钢材焊缝数据集,包含jpg原始图片,以及xml与txt两种格式的标签,分别存储在两个文件夹中,便于按VOC或YOLO格式直接开展二次训练与迁移学习。文件合计2000个,类型以txt标注/说明、md笔记、jpg图像、py脚本和yaml配置为主,另有C++/Python推理源码、HTML测试页面、PDF参考文档、模型权重pt与缓存文件等,整体压缩包约155.52MB,目录结构清晰,能帮助读者快速定位训练脚本、配置文件、评估结果与部署代码。已有224人学习下载,适合希望快速落地焊缝质量检测方案,或完整复现YOLOv8训练、评估与推理流程的开发者。
1. 焊缝探伤结果还在靠肉眼?YOLOv8 已能把“好不好”压成两个类
在钢结构、压力容器和管道焊接场景里,焊缝外观质量检查是出厂前绕不开的一环。传统做法是老师傅拿着样板比对,或者用磁粉、超声设备逐段扫,效率低且标准因人而异。这套 YOLOv8 焊缝质量好坏检测方案,把焊缝图直接喂给目标检测模型,一次前向推理就能标出焊缝位置,并给出“好/坏”两个类别。模型已训练好,解压即可用,附带的数据集是我个人比较认可的——用 labelimg 标注的钢材缺陷照片,jpg 格式,同时保留 xml 和 txt 两套标签,省去反复转换标注格式的时间。
适合谁用:手里攒了一批焊缝图想快速验证检测可行性的人,做质检系统原型开发的工程师,以及拿 YOLOv8 做毕业设计、需要完整训练闭环的同学。这篇博文不讲空泛的 AI 概念,直接从数据格式、模型结构、loss 曲线怎么读到 Python/C++ 两侧怎么跑通,按一线工程师拆项目的思路写。
2. 数据集与标签体系:xml 和 txt 并存,先看懂再喂给模型
2.1 目录结构与标注文件的对应关系
解压后你首先看到的是数据部分,图片存放在 JPEGImages 或类似命名的目录里,xml 和 txt 分别放在两个独立文件夹。xml 是 labelimg 默认导出的 Pascal VOC 格式,txt 是 YOLO 训练需要的归一化坐标格式。两者的对应关系不是自动生成的,需要脚本统一处理。
import xml.etree.ElementTree as ET import os def voc2yolo(xml_path, out_dir, class_list): tree = ET.parse(xml_path) w = int(tree.findtext("./size/width")) h = int(tree.findtext("./size/height")) lines = [] for obj in tree.findall("object"): name = obj.findtext("name") if name not in class_list: continue cls_id = class_list.index(name) xmin = float(obj.findtext("bndbox/xmin")) ymin = float(obj.findtext("bndbox/ymin")) xmax = float(obj.findtext("bndbox/xmax")) ymax = float(obj.findtext("bndbox/ymax")) cx = (xmin + xmax) / 2 / w cy = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") out_name = os.path.splitext(os.path.basename(xml_path))[0] + ".txt" with open(os.path.join(out_dir, out_name), "w") as f: f.write("\n".join(lines)) class_list = ["good", "bad"] # 按你训练时的类别顺序 voc2yolo("data/xmls/0001.xml", "data/labels", class_list)这段脚本把 VOC 的绝对坐标转成 YOLO 要求的中心点加宽高的归一化格式。注意 class_list 的顺序要和训练时的 data.yaml 完全一致,否则类别会对错位。比如训练时放的是["good", "bad"],这里就必须保持相同排序。
2.2 标签质量对模型上限的影响
焊缝图像本身背景复杂,有飞溅、氧化皮、打磨痕迹,这些都不是目标,标注时很容易把噪声圈进去。我拿到这套数据时的经验是,先随机抽 50 张图检查标签框是否贴着缺陷边缘。框松一点,模型收敛快但误检多;框太紧,训练初期 loss 降得慢。特别要看斜向裂纹,xml 里 bndbox 只能给轴向对齐矩形,如果缺陷有明显的长条形倾斜,建议在标注阶段就按最小外接矩形处理,不要为了框住整条裂纹把背景大面积包含进去。
训练集和验证集在 labelimg 导出时往往是混在一个文件夹里,需要先划分再训练。常见做法是按 8:1:1 划分 train/val/test,且划分前先对文件名做随机打乱,避免同一个工件连续帧同时进训练集和验证集,导致验证指标虚高。
3. 模型训练与结果判读:c2f 结构、PR 曲线和 loss 曲线怎么配合看
3.1 为什么选 YOLOv8 而不是 YOLOv5 或 R-CNN 系列
焊缝检测属于典型的小目标与长条形目标混合场景,既有几像素宽的细微裂纹,也有大块的咬边。YOLOv8 的 backbone 引入了 c2f 模块,在保持轻量的同时增强了梯度回传路径,对长条形裂纹这种低对比度目标更友好。另外 YOLOv8 是 anchor-free 结构,输出层直接预测中心点偏移和宽高,省去了聚类 anchor 的步骤,对形状分布不均匀的焊缝缺陷更省心。如果你用的是 GTX 1660 Ti 这类 6G 显存的卡,YOLOv8n 或 YOLOv8s 是合理的起点。
数据增强方面,YOLOv8 默认开启 mosaic 和 mixup。焊缝缺陷很多是细线状,mosaic 把四张图拼一起训练,模型被迫学习跨上下文的特征,反而对检测完整裂纹有帮助。但训练后期如果发现 val loss 震荡,优先关闭 mixup。
3.2 训练命令与关键参数
假设数据目录已经整理成 YOLO 格式:
yolo train \ model=yolov8s.pt \ data=welding.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ optimizer=auto \ device=0welding.yaml 内容参考:
path: ./welding-data train: images/train val: images/val names: 0: good 1: bad训练命令里的参数逐个说:batch=16 是 6G 显存下 yolov8s 的安全值;imgsz=640 是速度和精度的平衡点,焊缝缺陷如果普遍小于 20×20 像素,可以试着调到 960,但推理时间会显著增加;patience=20 表示 20 个 epoch 内 val loss 不下降就早停,避免过拟合到训练集的噪声标注上。
3.3 PR 曲线和 loss 曲线到底看什么
训练完成后,runs/detect/train/下会生成PR_curve.png和各类 loss 图。很多人只看 PR 曲线的右上部区域,那是错误姿势。PR 曲线是 confidence 从高到低连续变化时 precision 与 recall 的权衡线,真正要关注的是曲线下方靠右的拐点位置以及 AP 数值。焊缝检测中 bad 类 AP 通常低于 good 类,因为坏焊缝样本本身就是少数类。
yolo predict model=runs/detect/train/weights/best.pt source=./test-images跑完看labels.jpg和混淆矩阵。若混淆矩阵里 good 被误判成 bad 的比例偏高,问题大概率不在模型,而在标注阶段对“好焊缝”的边界定义不一致。loss 曲线方面,重点看box_loss是否在最后 20 个 epoch 还在明显下降,如果是,说明 epochs 设短了;如果 val/box_loss 上升而 train/box_loss 继续下降,就是典型过拟合,回退到早停点即可。
3.4 训练自己的数据集时如何按需改进
如果你的焊缝场景和这份数据差异不大,直接用 best.pt 做迁移学习:
yolo train model=runs/detect/train/weights/best.pt data=your_data.yaml epochs=50这比从头训练收敛快得多。改进方向上,热词里常提到 yolov8 改进、损失函数改进,这类属于锦上添花。实测中 c2f 结构对细长缺陷已经够用,更值得优先调整的是scale_range增强参数或fliplr。焊缝缺陷往往有水平对称性,但垂直于焊缝的裂纹翻转后物理含义不变,而字符类缺陷翻转会语义错误。卤素灯下拍摄的焊缝图还有白色高光,这类高光处的缺陷极易漏检,可以考虑在训练集中按 5% 概率加入高斯噪声模拟高光干扰。
4. 部署与推理验证:从 Python 快速验证到 C++ 侧代码的承接
4.1 Python 推理:几分钟验证模型效果
拿到训练好的模型,第一步是在自己的图上快速验证,不急着上 C++。准备一段最基础的推理脚本:
from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") results = model.predict( source="test_images/", conf=0.35, iou=0.5, save=True, save_txt=True, project="runs/detect", name="inference" ) for r in results: boxes = r.boxes names = r.names if boxes is not None: for i in range(len(boxes)): cls_id = int(boxes.cls[i].item()) conf = boxes.conf[i].item() xyxy = boxes.xyxy[i].tolist() print(f"class={names[cls_id]} conf={conf:.3f} box={xyxy}")conf=0.35是焊缝场景的推荐起步值。裂纹和咬边这类缺陷边缘模糊,confidence 普遍低于日常物体检测,设在 0.5 容易漏检。输出端save_txt=True会把检测框坐标保存到 txt 文件,方便后续直接对接 C++ 后处理逻辑。
4.2 C++ 侧代码再看:inference.cpp 与 main.cpp 的衔接
工程包里出现了 inference.h、inference.cpp 和 main.cpp,这是典型的 C++ 部署结构。一般做法是 inference 类封装模型加载和推理接口,main 负责读取图像、调用推理、画框和结果输出。我建议按三个层次读:先看 inference.h 里暴露了哪些接口,一般会有构造函数加载权重与初始化 session,一个 predict 成员函数接收 Mat 图像返回检测结果;再看 main.cpp,重点看图像预处理是否对齐到训练时的输入尺寸与归一化方式。
C++ 部署的一个常见坑是图像缩放。训练时 ultralytics 的 letterbox 会先等比例缩放再填充灰色边,C++ 端如果用 OpenCV 简单 resize,图像比例失真,框会偏移。一般做法是手动实现 letterbox 逻辑,并且记录缩放系数和填充偏移,后处理画框时再映射回原图坐标。
cv::Mat letterbox(const cv::Mat& src, int target_size, float& scale, int& pad_w, int& pad_h) { float r = std::min(target_size * 1.0f / src.cols, target_size * 1.0f / src.rows); int new_w = std::round(src.cols * r); int new_h = std::round(src.rows * r); scale = r; pad_w = (target_size - new_w) / 2; pad_h = (target_size - new_h) / 2; cv::Mat resized; cv::resize(src, resized, cv::Size(new_w, new_h)); cv::Mat canvas(target_size, target_size, CV_8UC3, cv::Scalar(114, 114, 114)); resized.copyTo(canvas(cv::Rect(pad_w, pad_h, new_w, new_h))); return canvas; }这段letterbox逻辑说明了部署侧的注意点:scale、pad_w、pad_h需要传回后续的框坐标映射函数,否则检测框位置整体偏移。C++推理时如果用 GPU 跑 torchscript 模型,需要注意 torch::jit 的版本和 engine 对齐,CPU 版则可以直接把模型导出成 onnx,用 ONNX Runtime 加载,代码会简洁不少。
4.3 从 Python 到 C++ 的结果一致性验证
很多人推进到这一步,发现同一张图 Python 和 C++ 检测结果不一致。这通常不是模型差异,而是预处理细节不同。
| 检查项 | Python 端 | C++ 端 | 一致性问题 |
|---|---|---|---|
| 输入尺寸 | 640 | 640 | 必须相同 |
| 色彩空间 | BGR | BGR | imread 默认 BGR,别转成 RGB |
| 归一化 | 0-1 像素值 | 0-1 像素值 | 除 255.0 注意浮点运算 |
| 填充颜色 | 灰 114 | 灰 114 | 不一致会导致边缘框掉点 |
我习惯把同一张图在两端各跑一次,比对每个框的坐标差异是否在 2 像素以内,再放大量图片做批量对比。两端都跑通了,再去接工业相机批量检测。YOLOv8 的类别只有 good 和 bad 两个,后处理不需要 NMS 类别过滤,但如果未来扩展成多种缺陷类型,就需要按类别单独设置 confidence 阈值,因为裂纹和咬边的最优阈值差异可能相当大。
5. 上产线前的三个动作:参数校准、难例挖掘与数据增强边界测试
5.1 用验证集自动搜索最优 conf 阈值
把训练时的 PR 曲线信息落到实际使用中,可以写一段遍历 confidence 的脚本,找到验证集上 F1 最高的阈值,而不是肉眼指定 0.35。
import numpy as np from ultralytics import YOLO from pathlib import Path model = YOLO("runs/detect/train/weights/best.pt") imgs = list(Path("val_images").glob("*.jpg")) thresholds = np.arange(0.2, 0.6, 0.05) best = (0, 0) for thr in thresholds: f1_total = 0 for img_path in imgs: r = model.predict(str(img_path), conf=thr, iou=0.5, verbose=False)[0] tp = (r.boxes.cls == 1).sum().item() fp = (r.boxes.cls == 0).sum().item() # good被当bad也算误报 fn = max(1, len(imgs) - tp) p = tp / max(tp+fp, 1) r_ = tp / max(tp+fn, 1) f1_total += 2 * p * r_ / max(p+r_, 1e-6) avg_f1 = f1_total / len(imgs) if avg_f1 > best[1]: best = (thr, avg_f1) print(f"threshold={thr:.2f}, avg_f1={avg_f1:.3f}")这段把 good 类误检成 bad 也计入 FP,更贴合产线逻辑——把合格焊缝判废比漏检更影响效益。实际运行时 p/r 的计算方式可以随业务侧重调整。
5.2 难例挖掘:把误检样本回炉训练
产线跑一周,收集误检样本重新标注并加入训练集,是提升模型最有效的方式。但注意不要直接把这些图混入原训练集就完事,而是保持原始的类分布比例。如果坏焊缝在产线上只占 3%,训练集里堆积到 30%,模型会过度偏向 bad 类,在线推理时把大量合格焊缝框成缺陷。另一个可行手段是只在后处理层面对 bad 类做评分加权,不动训练数据。
5.3 数据增强边界测试
模型训完想确认增强策略是否过猛,可以对比增强前后的精度差。YOLOv8 默认开启 mosaic、hsv 扰动、平移缩放。焊缝图中 hsv 扰动对金属表面反光影响不明显,而 mosaic 对细长裂纹的影响较大。如果 val loss 曲线显示过拟合早,优先调低增强参数而不是加正则项。
测试时把增强关掉单独跑一遍:
yolo train model=runs/detect/train/weights/best.pt data=welding.yaml epochs=30 hsv_h=0 hsv_s=0 hsv_v=0 mosaic=0.0如果关闭增强后 val loss 反而更低,说明原始训练时增强过强,应降低 mosaic 频率。如果精度下降明显,说明增强是有效的,不要动。这个实验值得做,很多人在部署早期把所有参数都归零,反而牺牲了模型的鲁棒性。
本文还有配套的精品资源,点击获取