简介:这份输电线路金具数据集面向电力行业机器视觉研究者、算法工程师及高校相关专业学生,用于训练和验证金具检测与分类模型,解决输电线路运维中人工巡检效率低、危险性高的问题。压缩包内共2000个文件,全部为xml格式的标注文件,整体约497.28MB,每份xml对应一张金具图像中目标的位置与类别信息,覆盖绝缘子、螺栓销钉、防振锤、耐张线夹等典型部件。数据采集时以蓝色垫子为统一背景,并从多个角度拍摄,有效降低环境干扰、提升目标辨识度,为算法训练提供稳定输入。目前已有342人学习下载,适合作为目标检测、图像分类等任务的实验素材。使用者可基于标注文件构建分类模型区分金具类型,或开发定位算法精确框出金具位置,也可用于远程监控与状态评估等场景,为电力系统智能化运维提供数据支撑。
1. 输电线路金具数据集-zip:从一堆航拍图到可训练标注的完整路径
拿到「输电线路金具数据集-zip」这个标题的人,多半正卡在同一个地方:手头有一批无人机巡检拍回来的绝缘子、防振锤、线夹、均压环照片,想训一个金具缺陷检测模型,却不知道标注该怎么组织、类别该怎么定、zip 解压之后那堆文件夹到底怎么喂给 YOLO 或 MMDetection。这个数据集方向解决的不是「有没有数据」,而是「数据能不能直接进训练管线」。它适合两类人:一类是做电力巡检算法、需要快速搭起 baseline 的工程师;另一类是接手了别人标注成果、要判断这批金具标注值不值得投入训练的学生和初级算法岗。金具本身目标小、背景杂、遮挡多,数据集的组织方式直接决定后面模型是能收敛还是全程玄学。这一章先把「金具数据集到底长什么样、为什么值得单独做」讲清楚,后面几章再落到解压、清洗、转换、训练和排错。
2. 金具数据集的类别体系与标注格式选型
2.1 金具类别怎么定才不会被返工
输电线路金具的种类比很多人想的多,常见的有悬垂线夹、耐张线夹、防振锤、间隔棒、均压环、重锤片、接续金具、联板等。数据集里如果一开始就把类别定得太细,比如把「防振锤」拆成「音叉式防振锤」「扭绞式防振锤」「预绞式防振锤」,标注成本会翻倍,而且不同电压等级、不同厂家的金具外形差异大,标注员很容易标错。我一般建议第一版数据集按「功能 + 外形」粗分,控制在 8 到 12 类,把容易混的合并,比如所有线夹先归到「线夹」大类,等模型能稳定检出后再做子类拆分。
另一个坑是缺陷类别和金具类别混在一起。有些数据集把「金具」和「金具锈蚀」「金具破损」放在同一层级,这会让检测头很困惑:一个防振锤既属于「防振锤」又属于「锈蚀」,多标签还是单标签?常见做法是分两套标签体系,一套做金具定位,一套做缺陷分类,或者用「金具_缺陷」的复合类别,但复合类别数量会爆炸。我的血泪经验是:第一版只做金具定位,缺陷交给后续分类模型,这样标注一致性和训练稳定性都好很多。
2.2 VOC、COCO、YOLO 三种格式的取舍
金具数据集 zip 解压后,标注格式常见三种:Pascal VOC 的 XML、COCO 的 JSON、YOLO 的 txt。选哪种取决于你后面用什么框架。如果打算用 MMDetection 或 Detectron2,COCO JSON 最省事;如果直接用 Ultralytics 的 YOLO 系列,YOLO txt 最直接;如果数据集原始标注是 LabelImg 出的 XML,那大概率是 VOC 格式,需要转换。
下面是一个把 VOC XML 转成 YOLO txt 的脚本,处理金具数据集时经常用到:
import os import xml.etree.ElementTree as ET # 类别映射,必须和 data.yaml 里的 names 顺序一致 CLASS_MAP = { "insulator": 0, "vibration_damper": 1, "clamp": 2, "grading_ring": 3, "spacer": 4, } def voc_to_yolo(xml_path, img_w, img_h, out_txt_path): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in CLASS_MAP: continue # 跳过未定义类别,避免训练时报 index 越界 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) # YOLO 格式要求归一化到 0-1,且是中心点 + 宽高 x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h # 裁剪到 [0,1],防止标注越界导致训练异常 x_center = min(max(x_center, 0.0), 1.0) y_center = min(max(y_center, 0.0), 1.0) w = min(max(w, 0.0), 1.0) h = min(max(h, 0.0), 1.0) lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(out_txt_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) # 批量处理,图片尺寸从 PIL 读取,避免 XML 里尺寸字段缺失 from PIL import Image def batch_convert(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue stem = os.path.splitext(xml_file)[0] img_path = os.path.join(img_dir, stem + ".jpg") if not os.path.exists(img_path): print(f"missing image: {stem}") continue with Image.open(img_path) as im: img_w, img_h = im.size voc_to_yolo( os.path.join(xml_dir, xml_file), img_w, img_h, os.path.join(out_dir, stem + ".txt") )这段脚本的关键点有三个。第一,CLASS_MAP必须和训练配置里的类别顺序完全一致,否则模型学到的类别会整体错位,这种错误在验证集上表现为「所有框都偏到另一个类」,很难排查。第二,归一化前一定要用真实图片尺寸,不能信 XML 里的size字段,很多金具数据集在裁剪或缩放后没有同步更新 XML 尺寸,直接用会得到一堆越界框。第三,越界裁剪是必要的,金具标注里经常出现框超出图像边界的情况,不裁剪会让 YOLO 训练时 loss 直接 NaN。
2.3 数据集划分:别让同一基杆塔的图同时进训练和验证
金具数据集有一个很隐蔽的泄漏:同一基杆塔、同一拍摄架次的多张图,如果随机划分,训练集和验证集会包含几乎相同的背景和光照,验证指标虚高。正确做法是按杆塔 ID 或拍摄架次分组划分,确保验证集里的杆塔在训练集中没出现过。如果 zip 里没有杆塔 ID 字段,至少按文件名前缀或拍摄日期分组。常见比例是训练 7、验证 2、测试 1,金具这种小目标场景,验证集不能太小,否则 mAP 波动大到没法判断模型好坏。
3. 从 zip 解压到可训练目录:目录结构与清洗步骤
3.1 解压后先别急着训练,先做完整性检查
金具数据集 zip 解压后,最常见的目录结构是images/和annotations/两个文件夹,或者按类别分文件夹。不管哪种,第一步都是检查图片和标注是否一一对应。缺失标注的图片如果直接进训练,YOLO 会把它当负样本,导致模型对金具的响应被压制。下面这段检查脚本我几乎每个数据集都会跑一遍:
import os def check_pairs(img_dir, label_dir, img_ext=".jpg", label_ext=".txt"): imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(img_ext)} labels = {os.path.splitext(f)[0] for f in os.listdir(label_dir) if f.endswith(label_ext)} only_img = imgs - labels only_label = labels - imgs print(f"images: {len(imgs)}, labels: {len(labels)}") print(f"image without label: {len(only_img)}") print(f"label without image: {len(only_label)}") # 输出前 20 个,方便人工核查 for name in list(only_img)[:20]: print(" missing label:", name) for name in list(only_label)[:20]: print(" missing image:", name) check_pairs("dataset/images", "dataset/labels")参数说明:img_ext和label_ext按实际格式改,有些数据集图片是.png或.JPG,大小写敏感在 Linux 上会直接导致漏检。only_img里的图片要么补标注,要么移到background/目录当负样本,不能留在训练集里当无标注正样本。only_label里的标注直接删掉,没有对应图片的标注文件会让训练脚本报错。
3.2 金具小目标的清洗:剔除无效框和极端尺寸
金具在航拍图里往往只占几十个像素,标注质量参差不齐。清洗时要重点处理三类框:宽或高小于 4 像素的框、宽高比超过 10:1 的框、以及面积占整图超过 80% 的框。前两类多半是标注员手抖或把一条线当成了金具,第三类通常是把整张图框成了「线路」。这些框不清理,训练时 anchor 匹配会非常混乱。
import os def clean_labels(label_dir, min_size=4, max_ratio=10.0, max_area_ratio=0.8): removed = 0 for fname in os.listdir(label_dir): if not fname.endswith(".txt"): continue path = os.path.join(label_dir, fname) kept = [] with open(path, "r", encoding="utf-8") as f: for line in f: parts = line.strip().split() if len(parts) != 5: removed += 1 continue cls_id, x, y, w, h = parts x, y, w, h = map(float, (x, y, w, h)) # 还原到像素尺度做判断,这里假设输入图约 1920x1080 pw, ph = w * 1920, h * 1080 if pw < min_size or ph < min_size: removed += 1 continue ratio = max(pw / max(ph, 1e-6), ph / max(pw, 1e-6)) if ratio > max_ratio: removed += 1 continue if w * h > max_area_ratio: removed += 1 continue kept.append(f"{cls_id} {x:.6f} {y:.6f} {w:.6f} {h:.6f}") with open(path, "w", encoding="utf-8") as f: f.write("\n".join(kept)) print(f"removed {removed} invalid boxes") clean_labels("dataset/labels")这里的min_size、max_ratio、max_area_ratio要根据你的图像分辨率调整。如果图片是 4K 的,min_size可以放到 8;如果是 640 裁剪图,4 已经偏大。清洗完一定要重新跑一遍 3.1 的配对检查,因为有些标注文件被清空后变成空文件,YOLO 会把空 txt 当负样本,这本身没问题,但你要知道哪些图变成了负样本。
3.3 生成 data.yaml 和目录软链接
YOLO 训练需要标准的images/train、images/val、labels/train、labels/val结构。如果原始数据已经分好,直接写data.yaml;如果没分,用软链接避免复制大文件:
# 假设原始结构是 dataset/images 和 dataset/labels mkdir -p yolo_dataset/images/train yolo_dataset/images/val mkdir -p yolo_dataset/labels/train yolo_dataset/labels/val # 按 8:2 划分,这里用文件名列表控制,实际按杆塔分组更稳 ls dataset/images | head -n 800 | while read f; do ln -s "$(pwd)/dataset/images/$f" "yolo_dataset/images/train/$f" ln -s "$(pwd)/dataset/labels/${f%.jpg}.txt" "yolo_dataset/labels/train/${f%.jpg}.txt" done ls dataset/images | tail -n 200 | while read f; do ln -s "$(pwd)/dataset/images/$f" "yolo_dataset/images/val/$f" ln -s "$(pwd)/dataset/labels/${f%.jpg}.txt" "yolo_dataset/labels/val/${f%.jpg}.txt" done对应的data.yaml:
path: /abs/path/to/yolo_dataset train: images/train val: images/val nc: 5 names: - insulator - vibration_damper - clamp - grading_ring - spacerpath必须是绝对路径,YOLO 对相对路径的处理在不同版本里不一致,用绝对路径最稳。nc和names的顺序必须和转换脚本里的CLASS_MAP完全一致,这是金具数据集训练翻车最常见的原因之一。
4. 用 YOLOv8 在金具数据集上跑通第一个 baseline
4.1 环境与训练命令
金具检测属于小目标密集场景,YOLOv8 的m或l版本比较合适,n版本在防振锤这种小目标上召回率会明显偏低。环境用 PyTorch + Ultralytics 即可,训练命令:
yolo detect train \ data=/abs/path/to/data.yaml \ model=yolov8m.pt \ epochs=150 \ imgsz=1280 \ batch=8 \ device=0 \ workers=4 \ patience=30 \ project=runs/jinju \ name=baseline参数说明:imgsz=1280是关键,金具目标小,640 下很多防振锤只剩几个像素,mAP 会低得没法看;1280 显存不够就降到 1024,但不要再低。batch=8是 1280 下 24G 显存的保守值,显存够可以上 16。patience=30表示 30 轮没提升就早停,金具数据集容易过拟合,早停能省时间。workers在 Windows 上设 0 或 2,设大了容易卡死。
4.2 训练中要盯的三个指标
第一个是cls_loss,如果它一直不降,大概率是类别映射错了或标注里有大量错类。第二个是box_loss,金具小目标的 box_loss 通常比常规 COCO 任务高,但如果在 50 轮后还在 1.5 以上,检查 anchor 和 imgsz。第三个是验证集的mAP50-95,金具场景 mAP50 能到 0.6 以上就算可用,mAP50-95 通常只有 0.3 到 0.4,不要拿 COCO 的标准来要求。
4.3 推理和可视化验证
训练完先用yolo detect predict在验证集上跑一遍,把预测框和原图叠出来看:
yolo detect predict \ model=runs/jinju/baseline/weights/best.pt \ source=/abs/path/to/yolo_dataset/images/val \ imgsz=1280 \ conf=0.25 \ save=True \ project=runs/jinju \ name=val_predconf=0.25是金具检测的常用起点,漏检多就降到 0.15,误检多就升到 0.4。重点看两类错误:把间隔棒和防振锤混检,以及把背景里的螺栓、塔材误检成金具。前者靠增加这两类的区分性样本,后者靠加负样本。
5. 金具数据集训练避坑:5 个真实踩坑记录
5.1 现象:训练 loss 正常但 mAP 一直是 0
原因:data.yaml里的names顺序和标注文件里的类别 ID 不一致,模型学到的类别和验证时的类别对不上。解决:用脚本统计标注文件里出现的所有类别 ID,和names逐一对齐,确保没有 ID 超出nc范围。
5.2 现象:验证集 mAP 很高,实际巡检图上几乎检不出金具
原因:数据集划分时同一杆塔的图同时进了训练和验证,验证集本质是「见过的背景」。解决:按杆塔 ID 或拍摄架次分组划分,验证集杆塔不出现在训练集。如果 zip 里没有杆塔信息,至少按文件名前缀分组。
5.3 现象:训练到一半 loss 突然变 NaN
原因:标注里有越界框或宽高为 0 的框,归一化后出现非法值。解决:跑 3.2 的清洗脚本,重点检查w或h为 0 的行,以及x_center加减w/2超出 [0,1] 的框。
5.4 现象:小目标金具召回率极低,大金具正常
原因:imgsz太小或 anchor 不匹配。解决:把imgsz提到 1280,YOLOv8 会自动调整 anchor,但如果还不行,检查是不是把imgsz设成了 640 却在data.yaml里写了 1280 的路径混淆。另外确认没有在预处理里把图缩放到 640 再送进模型。
5.5 现象:模型把整张图检成一个金具框
原因:标注里存在面积占比超过 80% 的框,通常是标注员把整张图框成了「线路」。解决:跑 3.2 的清洗,把max_area_ratio设到 0.5 甚至更低,金具不可能占半张图。
6. 进阶:用切片推理和类别平衡把金具召回再提一截
金具检测的难点在小目标和长尾类别。如果 baseline 已经跑通,下一步可以试两个技巧。第一个是 SAHI 切片推理,把 1280 的图切成 640 的块分别推理再合并,对小目标召回提升明显,代价是推理时间翻倍。用法很简单:
from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model = AutoDetectionModel.from_pretrained( model_type="yolov8", model_path="runs/jinju/baseline/weights/best.pt", confidence_threshold=0.25, device="cuda:0", ) result = get_sliced_prediction( "test.jpg", model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2, ) result.export_visuals(export_dir="sahi_pred")overlap设 0.2 是为了避免金具正好落在切片边界被切断,设太小会漏,设太大推理变慢。第二个技巧是类别平衡采样:金具数据集里绝缘子数量往往是均压环的十几倍,训练时对稀有类别过采样,或者用copy_paste增强把稀有金具贴到不同背景上。我一般会先统计每个类别的框数量,低于 500 的类别就要考虑增强。
验证改进是否有效,不能只看整体 mAP,要按类别看 AP。如果整体 mAP 涨了但稀有类别 AP 没动,说明涨的是头部类别,实际巡检里该漏的还是漏。我自己的习惯是每次改完只认稀有类别的 AP,整体指标当参考。金具数据集这个方向值得做,但前提是标注体系和划分方式从一开始就立住,否则后面调参全是白费功夫。希望帮到你。
本文还有配套的精品资源,点击获取