☰
YOLOv7绝缘子缺陷检测实战:从数据集配置到推理部署全流程
2026/9/26 16:13:25 网站建设 项目流程

简介:本资源面向电力巡检与计算机视觉方向的研究人员、工程师及学生,提供一套可直接运行的YOLOv7绝缘子缺陷检测完整方案,用于识别绝缘子裂纹、腐蚀、磨损等缺陷,降低人工巡检成本与风险。压缩包共166个文件,约231.59MB,包含31个Python脚本、36个yaml配置、35张jpg样本图、14个ipynb实验笔记、11张png可视化图、5个xml标注文件,以及pt权重、sh脚本、md说明与Dockerfile等,覆盖数据、训练、推理与部署全流程。目前已有1369人学习下载。资源内含针对绝缘子场景训练好的模型权重与配套数据集,可直接复现训练曲线与TensorBoard指标,并借助ONNXRuntime、TensorRT等notebook完成动态批量推理与YOLOv7与YOLOv5的对比实验,便于读者快速验证、二次调参与迁移到自有电力数据,是入门与进阶目标检测落地的实用参考。

1. 绝缘子缺陷检测为什么值得用 YOLOv7 重做一遍

输电线路巡检里,绝缘子破损、掉串、自爆这类缺陷一旦漏检,后果不用我多说。过去几年大家普遍用 YOLOv5 或者更早的 Faster R-CNN 做这件事,能跑,但精度和速度总有一头不满意。YOLOv7 在 2022 年出来后,我拿它在一批航拍绝缘子数据上重训了一版,同样的数据量,mAP@0.5 比 YOLOv5s 高了将近 4 个点,推理速度在单张 3060 上还能维持在 40 FPS 以上。这不是玄学,是 YOLOv7 的 ELAN 结构和辅助训练头确实在中小目标上更吃得开,而绝缘子缺陷——尤其是自爆点——在整张图里往往只占几十个像素。

这套「YOLOv7 绝缘子缺陷检测模型+代码+数据集」的方案,适合两类人:一类是电力巡检方向的研究生或工程师,手头有几百到几千张标注图,想快速搭一个能用的基线;另一类是已经跑过 YOLOv5 但卡在精度瓶颈上,想换架构试试的从业者。数据集方面,公开的绝缘子缺陷数据不算多,常见做法是自己从航拍视频抽帧再标,或者用 CPFL 那类公开电力数据集做迁移。代码层面,YOLOv7 官方仓库的结构比 YOLOv5 稍微绕一点,配置文件、权重、数据 yaml 三者的对应关系容易搞混,这也是后面要重点拆的地方。

2. 从数据到配置:YOLOv7 绝缘子检测的落地链路

2.1 绝缘子缺陷数据集的组织与标注格式转换

绝缘子缺陷检测的数据集,我一般按三类来标:正常绝缘子、自爆/破损、掉串。标注工具用 LabelImg 或者 X-AnyLabeling 都行,导出 VOC 格式的 XML。但 YOLOv7 吃的是 YOLO 格式的 txt,每行是class_id x_center y_center width height,全部归一化到 0~1。这个转换脚本我写过不下五遍,每次都有新坑,下面这版是稳定能用的。

import os import xml.etree.ElementTree as ET # 类别映射,顺序必须和训练时 data.yaml 里的 names 一致 CLASS_MAP = {"normal": 0, "broken": 1, "flashover": 2} def voc_to_yolo(xml_dir, out_dir, img_w, img_h): os.makedirs(out_dir, exist_ok=True) 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() # 有些数据集 size 字段缺失,这里用传入的 img_w/img_h 兜底 size = root.find("size") w = int(size.find("width").text) if size is not None else img_w h = int(size.find("height").text) if size is not None else img_h lines = [] for obj in root.iter("object"): name = obj.find("name").text.strip() if name not in CLASS_MAP: continue cls_id = CLASS_MAP[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) # 归一化并转中心点格式 x_c = (xmin + xmax) / 2.0 / w y_c = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h # 边界裁剪,防止标注越界导致训练报错 x_c, y_c = min(max(x_c, 0), 1), min(max(y_c, 0), 1) bw, bh = min(max(bw, 0), 1), min(max(bh, 0), 1) lines.append(f"{cls_id} {x_c:.6f} {y_c:.6f} {bw:.6f} {bh:.6f}") out_path = os.path.join(out_dir, xml_file.replace(".xml", ".txt")) with open(out_path, "w") as f: f.write("\n".join(lines)) voc_to_yolo("./annotations", "./labels", 1920, 1080)

这段代码的关键在CLASS_MAP和边界裁剪。类别映射一旦和data.yaml里的names顺序对不上,训练时 loss 会正常降,但推理结果全是错类,这种翻车我见过太多次。边界裁剪是防标注框超出图像范围,YOLOv7 的 dataloader 对越界坐标不会报错,但会算出负面积,导致某些 batch 的 loss 直接变 NaN。

转换完之后,目录结构建议这样组织:

dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── insulator.yaml

insulator.yaml的内容:

train: ./dataset/images/train val: ./dataset/images/val nc: 3 names: ["normal", "broken", "flashover"]

注意:nc必须等于names的长度,且names的顺序要和转换脚本里的CLASS_MAP完全一致。改类别时两处一起改,别只改一处。

2.2 YOLOv7 模型配置文件的选择与修改

YOLOv7 官方仓库里配置文件不少,yolov7.yaml、yolov7x.yaml、yolov7-tiny.yaml等等。绝缘子缺陷检测我一般从yolov7.yaml起步,如果显存吃紧或者要上边缘设备,再换yolov7-tiny.yaml。配置文件里唯一必须改的是nc,在 head 部分的三个检测头前面各有一处。

# yolov7-insulator.yaml 片段 head: - [-1, 1, Conv, [512, 1, 1]] - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 51], 1, Concat, [1]] - [-1, 3, Conv, [256, 3, 1]] # 128 - [-1, 1, Conv, [256, 1, 1]] - [-1, 3, Conv, [256, 3, 1]] - [-1, 1, Conv, [256, 1, 1]] - [-1, 1, Conv, [255, 1, 1]] # 这里 255 = 3 * (5 + nc),nc=3 时是 24,不是 255

上面这个片段是官方原版的,255对应的是 COCO 的 80 类。改成绝缘子 3 类时,要把三个检测头前的255改成3 * (5 + 3) = 24。这个5是x, y, w, h, obj,3是类别数。改错这个数,模型能加载但输出维度对不上,训练时直接报 shape mismatch。

# 训练命令,单卡 3060 12G 的配置 python train.py \ --weights yolov7.pt \ --cfg cfg/training/yolov7-insulator.yaml \ --data data/insulator.yaml \ --hyp data/hyp.scratch.p5.yaml \ --epochs 150 \ --batch-size 8 \ --img-size 640 640 \ --device 0 \ --workers 4 \ --name insulator_v7

参数说明:--weights用官方 COCO 预训练权重,迁移学习收敛快很多;--batch-size 8是 12G 显存下 640 分辨率的稳妥值,想再大就开--img-size 512;--hyp用hyp.scratch.p5.yaml,如果数据量少于 1000 张,建议把里面的mosaic概率从 1.0 降到 0.5,否则小数据集上 mosaic 增强会引入太多噪声。

2.3 训练过程监控与关键指标解读

训练启动后,runs/train/insulator_v7/下会生成results.txt和results.png。重点看三个指标:box_loss、obj_loss、mAP@0.5。绝缘子缺陷检测里,obj_loss比box_loss更值得盯,因为缺陷目标小,obj 分支的梯度容易震荡。

# 实时看训练日志里的关键行 tail -f runs/train/insulator_v7/results.txt | awk '{print $1, $2, $3, $8, $9}'

如果obj_loss在前 20 个 epoch 一直不降,常见原因是 anchor 和你的数据不匹配。YOLOv7 默认 anchor 是基于 COCO 的,绝缘子缺陷的宽高比偏极端(细长条),这时候跑一遍 k-means 重新聚类 anchor 会有明显改善。

# anchor 聚类,用你的训练集标注跑 import numpy as np def kmeans_anchors(labels, k=9, img_size=640): # labels: N x 2 的数组,每行是归一化的 w, h boxes = labels * img_size # 初始化:随机选 k 个点 np.random.seed(42) centers = boxes[np.random.choice(len(boxes), k, replace=False)] for _ in range(100): # 计算每个框到各中心的 IoU 距离 distances = 1 - np.array([iou(boxes, c) for c in centers]).T labels_idx = distances.argmin(axis=1) new_centers = np.array([boxes[labels_idx == i].mean(axis=0) for i in range(k)]) if np.allclose(centers, new_centers): break centers = new_centers return centers def iou(boxes, center): # 简化版:用面积比近似 w = np.minimum(boxes[:, 0], center[0]) h = np.minimum(boxes[:, 1], center[1]) inter = w * h union = boxes[:, 0] * boxes[:, 1] + center[0] * center[1] - inter return inter / union

聚类出来的 9 个 anchor 按面积从小到大排,替换配置文件里anchors那三行。这一步做完,obj_loss通常能在 30 个 epoch 内降到 0.05 以下。

3. 推理部署与效果验证:从权重到实际巡检画面

3.1 用 detect.py 跑通单张与批量推理

训练完的权重在runs/train/insulator_v7/weights/best.pt。推理直接用官方detect.py,但有几个参数必须调。

python detect.py \ --weights runs/train/insulator_v7/weights/best.pt \ --source ./test_images \ --img-size 640 \ --conf-thres 0.35 \ --iou-thres 0.45 \ --device 0 \ --save-txt \ --project runs/detect \ --name insulator_test

--conf-thres 0.35是我在绝缘子数据上试出来的平衡点。低于 0.3 会冒出大量误检,尤其是把正常绝缘子的阴影当成破损;高于 0.5 又会漏掉一些对比度低的自爆点。--iou-thres 0.45控制 NMS 的合并力度,绝缘子串上相邻缺陷离得近时,这个值调太低会把两个缺陷合并成一个。

批量推理时,--source指向文件夹,--save-txt会把每个图的检测框存成 txt,格式和训练标签一样,方便后续做统计。

3.2 验证集评估与混淆矩阵分析

训练脚本在最后一轮会自动跑验证,输出confusion_matrix.png和PR_curve.png。绝缘子检测里,混淆矩阵最有价值的是看broken和flashover之间的误判。这两类在低分辨率下纹理相似,如果混淆矩阵显示它们互相误判超过 15%,说明 640 分辨率不够,得把--img-size提到 768 或者 896。

# 单独跑验证,输出详细指标 python test.py \ --weights runs/train/insulator_v7/weights/best.pt \ --data data/insulator.yaml \ --img-size 640 \ --batch-size 8 \ --device 0 \ --task val \ --verbose

--verbose会打印每一类的 P、R、mAP@0.5、mAP@0.5:0.95。我一般要求broken类的 Recall 不低于 0.85,因为漏检一个破损绝缘子的代价远大于误检。如果 Recall 不够,优先加数据,其次是调低conf-thres再配合后处理。

3.3 模型导出与低显存部署的取舍

如果要在 Jetson 或者低显存设备上跑,导出 ONNX 或 TensorRT 是常规操作。

# 导出 ONNX python export.py \ --weights runs/train/insulator_v7/weights/best.pt \ --img-size 640 640 \ --batch-size 1 \ --device 0 \ --include onnx \ --simplify

--simplify会调 onnx-simplifier 做图优化,导出后的 ONNX 在 TensorRT 里跑通常比 PyTorch 快 1.5 到 2 倍。但注意,YOLOv7 的某些算子(比如nn.Upsample的nearest模式)在 TensorRT 8.4 以下版本支持不好,导出后可能报 unsupported op。遇到这种情况,要么升级 TensorRT,要么在导出时把--grid关掉。

低显存运行模型时,除了换 tiny 版本,还可以把--img-size降到 416,但绝缘子缺陷小目标多,416 下 mAP 会掉 5 到 8 个点,这个取舍要看你的实际场景能不能接受。

4. 绝缘子缺陷检测的避坑与排查记录

4.1 训练 loss 正常但 mAP 始终为 0

现象:训练日志里 box_loss 和 obj_loss 都在降,但验证时 mAP@0.5 一直是 0 或者极低。

原因:最常见的是data.yaml里的val路径写错,验证集加载的是空目录或者图片和标签没对上。YOLOv7 在验证集为空时不会报错,只会输出 0。

解决:跑训练前先用ls dataset/images/val | wc -l和ls dataset/labels/val | wc -l确认两边数量一致,且文件名(除扩展名外)一一对应。

4.2 推理结果框全挤在图像左上角

现象:detect.py 输出的图里,所有检测框都堆在左上角,尺寸也不对。

原因:训练时的--img-size和推理时的--img-size不一致,或者导出 ONNX 时用了 letterbox 但推理端没做同样的预处理。

解决:训练和推理的 img-size 保持一致。如果用 ONNX 推理,预处理必须复现 YOLOv7 的 letterbox 逻辑:等比缩放后填充灰边到 640x640,推理完再把框映射回原图坐标。

4.3 自爆缺陷漏检严重

现象:正常绝缘子和掉串都能检出,但自爆点 Recall 只有 0.5 左右。

原因:自爆点在图像里往往只有 20 到 40 像素,YOLOv7 的 P3 特征图(80x80)下采样 8 倍后,这些小目标的信息已经丢了不少。

解决:把--img-size提到 896 或 1024,同时把hyp里的scale增强范围调窄(比如 0.2 到 0.5),避免小目标被过度缩放。另一个办法是在配置文件里把 P3 检测头的 anchor 换成更小的值。

4.4 训练到一半 loss 突然变 NaN

现象:前几十个 epoch 正常,突然某个 batch 后 loss 变成 NaN,之后再也降不下来。

原因:标注框越界导致负面积,或者学习率在 warmup 阶段跳得太猛。绝缘子数据集里如果有标注框的 xmax 小于 xmin,转换脚本没做校验就会出这个问题。

解决:在转换脚本里加一行校验,if xmax <= xmin or ymax <= ymin: continue。另外把hyp里的lr0从 0.01 降到 0.005,warmup_epochs 从 3 提到 5。

4.5 验证集 mAP 高但实际巡检图效果差

现象:验证集 mAP@0.5 有 0.9,但拿现场巡检图跑,误检和漏检都很多。

原因:验证集和训练集同分布,但现场图的背景、光照、拍摄角度和训练数据差异大。绝缘子检测里,背景从山林到农田到城市,变化极大。

解决:从现场视频里抽 200 到 300 帧,标一部分加入训练集,哪怕只标正常和明显缺陷两类,也能显著提升泛化。另一个办法是加背景图(无目标的负样本),比例控制在 10% 左右,能压误检。

5. 把绝缘子检测模型推到可用的几个进阶习惯

模型训出来只是第一步,真正让它在巡检场景里可用,我踩过的坑比训练本身还多。第一个习惯是永远保留一份「困难样本集」。每次现场跑完,把误检和漏检的图单独存一个文件夹,标好后加入下一轮训练。这个习惯坚持三个月,mAP 能再涨 5 到 8 个点,比调任何超参都管用。

第二个习惯是推理时开 TTA(Test Time Augmentation)。YOLOv7 的 detect.py 不直接支持,但可以自己包一层:对同一张图做水平翻转和 1.1 倍缩放,各跑一次推理,再用 NMS 合并三组框。代价是推理时间翻三倍,但在离线巡检场景里完全可接受,Recall 通常能提 2 到 3 个点。

# 简易 TTA 推理封装 import cv2 import numpy as np def tta_inference(model, img, conf=0.35, iou=0.45): results = [] # 原图 results.append(model(img, conf, iou)) # 水平翻转 flip = cv2.flip(img, 1) r_flip = model(flip, conf, iou) # 翻转回来再收集 for box in r_flip: box[0] = img.shape[1] - box[0] box[2] = img.shape[1] - box[2] results.append(box) # 缩放 1.1 倍 h, w = img.shape[:2] scaled = cv2.resize(img, (int(w * 1.1), int(h * 1.1))) r_scale = model(scaled, conf, iou) for box in r_scale: box[0] /= 1.1 box[1] /= 1.1 box[2] /= 1.1 box[3] /= 1.1 results.append(box) # 合并所有框做 NMS return nms(results, iou)

第三个习惯是给模型加一个「置信度分级」输出。巡检业务里,0.35 到 0.5 之间的框标记为「疑似」,0.5 以上标记为「确认」。这样现场人员可以优先处理确认缺陷,疑似缺陷二次复核,既不漏也不至于被误检淹没。

最后一个习惯,也是我血泪教训换来的:每次改完配置文件,先跑 1 个 epoch 的 sanity check,确认 loss 能正常降、验证能跑通,再开完整训练。YOLOv7 的配置文件层级深,改错一个数字可能要跑半天才发现,1 个 epoch 的检查能省下大量时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询