尧图精选

发电厂指针仪表数据集:XML标注与YOLO训练全流程

🕒 发布时间:2026/10/2 22:00:44 📁 来源:尧图网络
简介这份资源是面向电力行业智能化改造与计算机视觉学习者的发电厂指针仪表数据集适用于目标检测模型训练、仪表自动读数研究及工业监控系统开发等场景。包内共2000个文件以1214个xml标注文件和785张jpg仪表图像为主另含1个说明文件压缩包约84.72MBxml标注可直接对接YOLO等主流检测框架jpg图像则覆盖发电厂实际环境中的多种指针仪表形态。目前已有100人学习下载。数据集图像均经人工精心标注标签规范、可用性高读者可据此完成从数据加载、模型训练到读数提取的完整流程并参考YOLOv5训练方案快速验证效果既适合作为算法测试与优化的基础数据也能为发电厂自动化监控项目提供实践支撑。1. 发电厂指针仪表数据集从表盘到 XML 标注一条能跑通的落地路径发电厂集控室里那些老式指针仪表——温度表、压力表、电流表——读数还靠人眼抄一个班下来巡检工要走两万步。想把这件事自动化第一步不是选模型而是先有一份能直接喂给检测框架的发电厂指针仪表数据集。市面上公开的仪表数据大多针对单一日光场景表盘类型杂、指针细、刻度密直接拿来训发电厂场景的模型mAP 掉得让人怀疑人生。这份数据集用xml 格式标注意味着它能无缝对接 PASCAL VOC 体系也能一条脚本转成 YOLO 的 txt省掉大量清洗时间。它适合两类人一是做工业巡检落地的算法工程师二是想拿真实工业场景练手的学生。下面把我从拿到 xml 到训出可用检测器的完整路径拆开讲包括标注结构、转换脚本、参数设置和几个让我翻过车的坑。2. 先看懂 xml 标注发电厂指针仪表数据集的结构与字段含义2.1 一份 VOC 风格 xml 里到底存了什么发电厂指针仪表数据集采用 PASCAL VOC 的 xml 结构每个表盘图片对应一个同名 xml 文件。核心节点是object里面记录类别名和边界框坐标。指针仪表的特殊性在于标注框通常只框住表盘外圆而不是整块仪表外壳因为后续读数识别只需要表盘区域。下面是一个典型 xml 的骨架我删掉了无关节点只留关键字段。annotation foldergauge_001/folder filenamegauge_001.jpg/filename size width1920/width height1080/height depth3/depth /size object namepressure_gauge/name !-- 类别名按仪表类型区分 -- poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin412/xmin !-- 左上角 x像素坐标 -- ymin236/ymin !-- 左上角 y -- xmax688/xmax !-- 右下角 x -- ymax512/ymax !-- 右下角 y -- /bndbox /object /annotationname字段决定了你后面能训出几类仪表。常见做法是按仪表功能分压力表、温度表、电流表、电压表也有按表盘形状分圆表和方表的。difficult标记为 1 的样本在评估时会被跳过工业场景里反光严重、指针被遮挡的图建议标 1否则模型会被这些噪声带偏。truncated表示目标是否被截断边缘表盘标 1。2.2 为什么发电厂场景的标注框要卡紧表盘外沿很多人标框习惯留一点余量但在指针仪表上这是坏习惯。指针读数的本质是角度回归如果框里混进了仪表外壳、管道、背景螺丝后续做指针分割或关键点检测时ROI 里会多出一堆干扰边缘。我一般要求标注框紧贴表盘玻璃外圆误差控制在 5 像素以内。对于倾斜拍摄的表盘框仍然是水平矩形但要在pose里记录拍摄角度方便后续做透视矫正时筛选样本。另一个细节是类别命名不要用中文。xml 里写中文类别转 YOLO 时容易因为编码问题变成乱码训练时类别名对不上报错信息还很难查。统一用英文小写下划线比如pressure_gauge、temp_gauge。如果数据集里已经混了中文用下面这段脚本批量清洗。import os import xml.etree.ElementTree as ET # 批量把 xml 里的中文类别名替换成英文映射 name_map { 压力表: pressure_gauge, 温度表: temp_gauge, 电流表: current_gauge, 电压表: voltage_gauge } xml_dir ./annotations for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue path os.path.join(xml_dir, fname) tree ET.parse(path) root tree.getroot() for obj in root.findall(object): name_node obj.find(name) if name_node.text in name_map: name_node.text name_map[name_node.text] tree.write(path, encodingutf-8, xml_declarationTrue)这段脚本遍历目录下所有 xml按映射表替换类别名最后以 utf-8 重新写回。注意xml_declarationTrue会补上声明头避免某些解析库读中文报错。跑之前先备份一份原始标注改错了还能回滚。3. 把 xml 转成 YOLO 格式坐标归一化与类别索引对齐3.1 转换脚本的核心逻辑与四个易错点YOLO 训练需要每张图对应一个 txt每行格式是class_id x_center y_center width height全部归一化到 0 到 1。xml 给的是绝对像素坐标转换时要除以图像宽高。下面是我在发电厂指针仪表数据集上实际用过的转换脚本处理了类别索引、坐标裁剪和空标注三个问题。import os import xml.etree.ElementTree as ET # 类别列表顺序必须和训练时 data.yaml 里的 names 完全一致 classes [pressure_gauge, temp_gauge, current_gauge, voltage_gauge] class_to_id {c: i for i, c in enumerate(classes)} xml_dir ./annotations out_dir ./labels os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, fname)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_to_id: continue # 跳过未定义类别避免索引错位 bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 裁剪到图像边界内防止标注越界导致归一化后为负 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_to_id[cls_name]} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) txt_name fname.replace(.xml, .txt) with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines))四个易错点第一类别索引必须和data.yaml的names顺序严格一致差一位模型就学错类第二坐标要裁剪到图像范围内有些标注员手抖把 xmax 写到图像宽度之外归一化后大于 1YOLO 会直接报错第三空标注文件要保留YOLO 把空 txt 当作负样本删掉反而会让模型在无目标图上乱检第四浮点精度保留 6 位足够写太多位没必要还拖慢读取。3.2 data.yaml 怎么写才不出错转换完 txt还需要一个data.yaml告诉 YOLO 去哪找图和标签。发电厂指针仪表数据集一般按 8:1:1 切分训练、验证、测试。目录结构建议这样组织dataset/ images/ train/ val/ test/ labels/ train/ val/ test/对应的 yaml 内容path: ./dataset train: images/train val: images/val test: images/test nc: 4 names: [pressure_gauge, temp_gauge, current_gauge, voltage_gauge]nc是类别数必须和 names 长度一致。path用相对路径时训练命令要在 dataset 的上一级目录执行否则找不到图。我见过有人把train写成绝对路径但漏了盘符Windows 下直接报路径不存在查了半天。4. 训练参数怎么设指针仪表检测的调参与增强策略4.1 输入分辨率与 batch size 的取舍发电厂指针仪表在整张图里占比通常不大1920x1080 的原图里表盘可能只占 300x300 像素。如果直接 resize 到 640指针细到只剩几个像素模型根本学不到。我一般把输入设到 960 或 1280显存不够就降 batch。以 YOLOv8 为例单卡 12G 显存下imgsz960配batch8比较稳imgsz1280只能开到batch4。yolo detect train \ data./dataset/data.yaml \ modelyolov8s.pt \ imgsz960 \ batch8 \ epochs200 \ patience30 \ lr00.01 \ lrf0.01 \ mosaic0.5 \ degrees10.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4patience30表示 30 轮验证指标不升就早停工业数据集样本量通常不大早停能省时间。mosaic0.5是马赛克增强概率指针仪表场景我建议降到 0.5 甚至 0.3因为四图拼接会破坏表盘完整性指针被拼到别的图上模型学到的角度关系是错的。degrees10.0做小角度旋转增强模拟安装倾斜但别开太大超过 15 度表盘刻度就失真了。4.2 针对反光和低对比度的增强发电厂现场光照复杂表盘玻璃反光、背光、油污都会让指针和刻度对比度下降。除了常规 HSV 增强我还会加一点随机亮度对比度扰动。YOLO 内置的hsv_v0.4控制亮度变化幅度hsv_s0.7控制饱和度这两个值在仪表场景可以适当调高让模型对褪色表盘更鲁棒。如果数据集里反光样本特别多建议单独统计一下反光严重的图在标注时把difficult标 1训练时用--ignore跳过否则模型会把反光当成目标特征。另一个技巧是 CLAHE 预处理。在数据加载阶段对图像做限制对比度自适应直方图均衡能显著提升指针与表盘的局部对比度。这个操作不改变标注框可以在dataset.py的__getitem__里加也可以用离线脚本先处理一遍存成新图。离线处理更省事但会多占一份磁盘。5. 避坑与排查指针仪表数据集训练中最容易翻车的五件事5.1 现象训练 loss 正常下降但 mAP 一直为零原因通常是类别索引错位。xml 转 txt 时类别映射表和data.yaml的names顺序不一致模型学的是 A 类评估时按 B 类算mAP 自然为零。解决方法是打印一份 txt 里的 class_id 分布和 yaml 的 names 逐一对齐。另一个可能是标注框坐标归一化后宽高为负检查转换脚本有没有做 xmin/xmax 排序。5.2 现象模型把表盘玻璃反光框成仪表反光区域和表盘外圆在灰度上接近模型分不清。解决分两步一是在标注阶段把反光严重的图标为 difficult 或直接剔除二是在增强里加入随机遮挡模拟反光被遮挡的情况。YOLO 的erasing参数可以随机擦除图像块概率设 0.2 到 0.3能逼模型关注指针和刻度而不是反光。5.3 现象小表盘漏检严重原图里表盘小于 100 像素时resize 到 640 后几乎消失。解决办法是提高输入分辨率到 1280或者在数据加载时对小目标做过采样。YOLOv8 的copy_paste增强可以把小表盘复制粘贴到其他位置增加小目标出现频率但指针仪表场景要慎用粘贴后的表盘背景不自然可能引入噪声。5.4 现象验证集指标波动大每次训练结果差很多样本量小加上随机种子不固定。发电厂指针仪表数据集如果只有几百张验证集可能就几十张一两张图检错指标就抖。解决办法是固定seed42同时用 K 折交叉验证代替单次切分。另外把deterministicTrue打开让 cuDNN 走确定性算法虽然慢一点但结果可复现。5.5 现象转 YOLO 后图片和标签对不上常见于文件名有空格或特殊字符。xml 里filename写的是gauge 001.jpg实际文件是gauge_001.jpg转换脚本按 xml 里的名字找图就找不到。解决方法是统一用文件名主干匹配忽略 xml 里的 filename 字段直接用 xml 文件名替换后缀去对应图片。批量重命名时把空格换成下划线避免后续路径解析出问题。6. 从检测框到指针读数一个可验证的进阶技巧检测框只是第一步真正要读数还得定位指针和刻度。我常用的做法是在检测框内做两步先用霍夫圆变换找表盘圆心和半径再在圆内做指针分割。指针通常是深色细长条用自适应阈值加形态学细化能提取出指针骨架然后算骨架两端到圆心的角度映射到刻度范围。这套流程不需要额外标注但依赖检测框足够准。验证读数是否靠谱我习惯用「回读法」把预测角度反算成刻度值和人工读数比误差超过满量程 5% 的样本挑出来看。如果误差集中在某个角度区间多半是刻度非线性或指针安装偏心需要在标定阶段做分段线性拟合。下面这段代码演示怎么在检测框内算指针角度。import cv2 import numpy as np def read_gauge_angle(img, box): x1, y1, x2, y2 map(int, box) roi img[y1:y2, x1:x2] gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) # 霍夫圆找表盘圆心 circles cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp1, minDistroi.shape[0], param1100, param230, minRadiusint(roi.shape[0]*0.3), maxRadiusint(roi.shape[0]*0.5)) if circles is None: return None cx, cy, r circles[0][0] # 自适应阈值提取指针 thresh cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 15, 5) # 只保留圆心附近的连通域过滤刻度文字 mask np.zeros_like(thresh) cv2.circle(mask, (int(cx), int(cy)), int(r*0.8), 255, -1) thresh cv2.bitwise_and(thresh, mask) # 细化后拟合直线算角度 lines cv2.HoughLinesP(thresh, 1, np.pi/180, threshold30, minLineLengthint(r*0.4), maxLineGap5) if lines is None: return None # 取最长线段作为指针 longest max(lines, keylambda l: (l[0][2]-l[0][0])**2 (l[0][3]-l[0][1])**2) x1l, y1l, x2l, y2l longest[0] angle np.degrees(np.arctan2(y2l - y1l, x2l - x1l)) return angle, (cx, cy, r)这段代码先找圆再找指针param230是霍夫圆的累加器阈值值越小越容易检出圆但也更容易误检工业表盘一般设 25 到 35。minLineLength设成半径的 0.4 倍能过滤掉刻度短线。返回的角度是图像坐标系下的弧度转角度实际映射到刻度还要根据表盘零点和满量程做线性变换。这套方法在表盘清晰、指针无遮挡时误差能控制在 2% 以内但遇到指针弯曲或表盘破损就得换关键点检测方案。我踩过最深的坑是拿检测框直接当表盘区域去算角度结果框里混进了管道边缘霍夫圆找错圆心读数全乱。后来养成习惯检测框出来后先做一次圆拟合校验圆心偏离框中心超过 20% 就丢弃该样本。这个习惯帮我省了大量返工时间。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →