资讯动态

光伏板缺陷检测数据集处理与YOLO训练实战指南

发布时间:2026/9/8 14:33:59 来源:尧图企业网站定制
简介一套面向光伏板/太阳能板表面缺陷检测的数据集适用于计算机视觉目标检测与缺陷识别场景覆盖划痕crack、断栅grid和脏污spot三类典型缺陷可支撑光伏电站巡检、组件产线质检等落地应用。整体包含2400张光伏板图像样本并提供Pascal VOC与YOLO两种格式的标注文件矩形框总数2628个由labelImg工具标注完成每类缺陷独立成类标注框位置信息完整可满足模型训练、格式转换和算法验证等需求。压缩包大小约296.73MB共2000个文件其中1999个为XML标注文件、1个为说明TXTXML文件采用标准VOC结构命名规律清晰便于直接接入主流检测框架或批量转换为其他标注格式。目前已有1924人学习/下载类别框数分布清晰crack 892框、grid 884框、spot 852框适合需要规范数据集的入门与进阶开发者能显著降低数据采集和人工标注成本实现开箱即用的模型实验。1. 拿到数据集的第一步解压、校验和目录摸底1.1 7z文件解压实操标题里写着“.7z”很多人第一次接触这种压缩格式就直接卡在解压这一步。Windows下最简单装一个7-Zip右键选择“解压到当前文件夹”就行Linux下需要先确认有没有装p7zip工具包很多服务器默认不带。# Ubuntu/Debian sudo apt install p7zip-full # 先查看压缩包内容不解压 7z l 光伏板太阳能板缺陷检测数据集VOCYOLO格式2400张3类别.7z # 解压到指定目录-o参数后面直接跟路径没有空格 7z x 光伏板太阳能板缺陷检测数据集VOCYOLO格式2400张3类别.7z -o./solar_dataset这里要区分两个命令7z x会保留压缩包内部的目录结构7z e则会把所有文件平铺到当前目录没有任何层级。对数据集来说我强烈建议用7z x因为一个规范的数据集必然有images、labels这类子目录平铺出来会导致几百个文件混在一起后续划分训练验证集简直就是灾难。如果压缩包设置了密码命令后面加-p密码即可不过公开数据集一般不会这么干。解压过程中如果看到“Unexpected end of data”或者CRC校验失败的提示别急着重新解压先按照下面一节的方法检查文件完整性。1.2 用哈希校验确认数据完整性很多从网盘下载的数据集解压到一半突然报错或者解压成功但训练时某张图怎么都加载不了多半是下载过程丢包导致的。这里就要用到哈希校验这也是下载大文件的基本素养。# Linux下计算SHA256 sha256sum 光伏板太阳能板缺陷检测数据集VOCYOLO格式2400张3类别.7z # Window PowerShell Get-FileHash -Algorithm SHA256 光伏板太阳能板缺陷检测数据集VOCYOLO格式2400张3类别.7z拿到哈希值后去数据集发布页面比对一下。如果作者提供了官方哈希值一致就说明文件没问题如果没提供也可以用压缩包自带的校验功能测试一下7z t xxx.7z这个命令会逐文件测试压缩包的CRC校验和几秒钟就能确认整体文件是否损坏。我实际踩过坑一个从网盘拉下来的数据集压缩包解压时看着一切正常但训练到第200个epoch突然报图片解码错误最后排查发现就是压缩包里有几个文件损坏了重新下载校验后才解决。所以现在拿到任何数据包第一件事永远是7z t或者哈希比对别嫌麻烦。1.3 解压后先摸清目录结构一个规范的VOCYOLO双格式数据集目录通常长这样solar_dataset/ ├── images/ # 图像文件jpg或png ├── annotations/ # VOC格式xml标注 ├── labels/ # YOLO格式txt标注 ├── classes.txt # 类别列表 ├── train.txt # 可选官方划分好的训练集文件列表 └── val.txt # 可选验证集文件列表不同来源的数据集目录命名差异很大有的沿用VOC经典叫法JPEGImages、Annotations有的直接把yolo标签放进label目录。不管名字怎么变先确认三件事图像数量是否为2400张、每张图是否都有对应标签、类别文件里是不是3个类别。echo images数量: $(ls images | wc -l) echo annotations数量: $(ls annotations | wc -l) echo labels数量: $(ls labels | wc -l) cat classes.txt如果发现labels数量少于images说明有部分图像没有对应的标注文件训练时YOLO会静默忽略这些无标签图像。但“忽略”不代表安全很可能是标注遗漏最好是人工复查一遍别把问题留到训练阶段才发现。2. 读懂VOC与YOLO两种标注格式2.1 光伏板缺陷的3个类别怎么看标题只写了“3类别”没有公布类别名单所以解压后第一件事就是打开classes.txt。以光伏板常见缺陷来推断常规的三分类基本离不开隐裂crack、热斑hot spot、脏污或异物dust/foreign matter这三种。如果这套数据集是可见光图像热斑一类可能偏少裂纹和脏污会更突出如果是红外热成像图热斑就是核心类别缺陷形态又完全不同。我经常提醒做缺陷检测的朋友不要拿到类别名就开训先花二十分钟把每个类别的样本挑出几十张从头到尾看一遍搞清楚缺陷长什么样、尺度多大、背景是什么样。光伏板表面本身就有细密的栅线纹理和隐裂在视觉上非常接近如果不先建立直观认知后面看训练结果、调阈值都无从下手。2400张图不算多但足够让你对数据分布建立起一个基本判断。2.2 VOC XML标签怎么解析VOC格式是从PASCAL VOC数据集沿用下来的老牌标注格式每个xml文件对应一张图核心信息是图像尺寸和目标框的像素坐标。典型结构长这样annotation folderimages/folder filenameimg_0001.jpg/filename size width1280/width height960/height depth3/depth /size object namecrack/name bndbox xmin120/xmin ymin300/ymin xmax500/xmax ymax620/ymax /bndbox /object /annotation解析时最需要注意的是size字段。后面转YOLO格式时要靠width和height做归一化如果图被人为resize过但xml没同步更新所有坐标都会偏移。还有的数据集把宽高写反了也是常见的坑。我建议转格式前随机挑几张图用OpenCV或标注工具把bndbox画出来人工对一眼这一步只要两分钟但能拦住后面至少半天定位问题的功夫。2.3 YOLO txt标签与VOC格式互转YOLO格式每一行表示一个目标类别id x_center y_center width height五个数全部归一化到0到1之间。归一化就是除以图像的宽和高目的是让标签与图像尺寸无关。比如上面那个xml如果图像宽1280、高960目标框像素坐标是xmin120, ymin300, xmax500, ymax620转出来的YOLO标签就是0 0.2421875 0.4791667 0.296875 0.3333333计算过程x_center (120 500) / 2 / 1280 0.2421875 y_center (300 620) / 2 / 960 0.4791667 box_w (500 - 120) / 1280 0.296875 box_h (620 - 300) / 960 0.3333333新手最常见的错误就是忘了除以图像尺寸直接把像素坐标存进去训练时要么报“boxes0”要么loss爆炸。下面给一个完整的批量转换脚本适用VOC annotations目录转YOLO labels目录import os import xml.etree.ElementTree as ET voc_dir annotations yolo_dir labels os.makedirs(yolo_dir, exist_okTrue) class_names [crack, hot_spot, dust] # 以classes.txt为准 for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) out_lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h out_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) base os.path.splitext(xml_file)[0] with open(os.path.join(yolo_dir, base .txt), w) as f: f.write(\n.join(out_lines))脚本不大但覆盖了全部逻辑。跑完随便抽一个txt文件打印出来看数值是否都在0到1之间这是最直接的检测手段。这里再补充一点如果数据集本身已经附带了YOLO格式你反而要会YOLO转VOC因为有些时候做可视化验证、接其他检测框架VOC格式更方便两套互转的脚本都应该保留这是做数据工程的基本功。3. 用这套数据集训练YOLO的完整流程3.1 划分train/val/test并保持文件对应2400张图按8:1:1划分就是1920张训练、240张验证、240张测试。划分的时候最忌讳按文件名顺序直接切因为采集数据时同一批光伏板、同一角度的照片往往连在一起顺序切会让验证集和训练集高度相似测试结果虚高换个现场数据直接现原形。我的做法是写脚本做随机划分并尽量保证每个类别在各集合中的占比接近。核心逻辑先读取所有标签文件统计每张图包含哪些类别然后按类别分组对每一类单独按比例随机抽样到各自集合最后合并去重。import os import random from collections import defaultdict random.seed(42) image_dir images label_dir labels train_files, val_files, test_files [], [], [] # 按标签所属类别建立索引同一张图可能属于多个类别 class_to_images defaultdict(list) all_images set() for label_file in os.listdir(label_dir): if not label_file.endswith(.txt): continue base os.path.splitext(label_file)[0] img_file f{base}.jpg if not os.path.exists(os.path.join(image_dir, img_file)): continue with open(os.path.join(label_dir, label_file)) as f: content f.read().strip() if not content: continue cls_ids {line.split()[0] for line in content.splitlines()} all_images.add(img_file) for c in cls_ids: class_to_images[c].append(img_file) # 每个类别都做一次分层划分去重放到最终集合 for c, imgs in class_to_images.items(): random.shuffle(imgs) n_val max(1, int(len(imgs) * 0.1)) n_test max(1, int(len(imgs) * 0.1)) val_files.extend(imgs[:n_val]) test_files.extend(imgs[n_val:n_val n_test]) train_files.extend(imgs[n_val n_test:]) # 去重并保留原始顺序 def dedup(seq): seen set() out [] for x in seq: if x not in seen: seen.add(x) out.append(x) return out train_files dedup(train_files) val_files dedup(val_files) test_files dedup(test_files) # 把没有任何类别标签的图像单独处理随机分到训练集 background list(all_images - set(train_files) - set(val_files) - set(test_files)) random.shuffle(background) n_train_bg int(len(background) * 0.8) train_files background[:n_train_bg] val_files background[n_train_bg:] with open(train.txt, w) as f: f.write(\n.join(train_files)) with open(val.txt, w) as f: f.write(\n.join(val_files)) with open(test.txt, w) as f: f.write(\n.join(test_files))注意脚本里设置了random.seed(42)这样别人复现你的结果时不会偏差。写完之后检查一下三个txt文件的数量验证集有240张左右就合理。很多人直接用一个现成脚本把图分走了但忘了把标签文件同步移动导致训练时找不到标签这类问题其实靠文件列表就完全可以避免。3.2 编写data.yamlYOLOv5、YOLOv8系列的训练配置都走data.yaml。内容很少但每一个字段都不能写错path: /home/user/solar_dataset train: train.txt val: val.txt test: test.txt names: 0: crack 1: hot_spot 2: dustpath建议写绝对路径写相对路径时如果启动命令的工作目录不对会直接报“No labels found”。names的顺序必须和标签txt中的类别id严格一致因为我上面转换脚本里class_names按字母排过序这里也要按同样的顺序写。经常有人训练完发现类别张冠李戴排查到最后就是names顺序和标签id对不上。3.3 训练命令与参数调整心得安装ultralytics包后训练命令非常简单pip install ultralytics yolo detect train datadata.yaml modelyolov8n.pt epochs200 imgsz640 batch16 device0第一次跑建议用yolov8n这个最轻量的权重先验证全流程能跑通再考虑换m或者l版本。很多人一上来就跑yolov8x显卡不好卡个半天其实全流程通不通跟模型大小没什么关系先用小模型节省时间确认能正常出loss、box、mAP指标再换大模型慢慢调。关于batch如果你的显卡是老一代的消费级显卡显存只有8G左右imgsz640时batch设8比较稳妥实在不够把imgsz降到512再用yolov8n。插一句题外话之前有人问AMD老显卡能不能跑yoloultralytics底层依赖PyTorchPyTorch对NVIDIA的CUDA支持最好AMD显卡可以用DirectML或ROCm方案但兼容性和加速效率完全看环境入门阶段用CPU都能跑只是慢。先确保流程跑通再考虑硬件事。mosaic增强默认是开启的对常规目标检测很有用。但光伏裂纹这种细长目标mosaic拼接后目标容易被切碎前期训练可能学不到完整特征。我的做法是前50个epoch保持默认如果后面发现小目标漏检严重再把mosaic调低或者关掉yolo detect train datadata.yaml modelyolov8n.pt epochs200 imgsz640 batch16 mosaic0.53.4 训练常见报错排查跑yolov8训练时常见报错和排查思路我整理成了一张速查表基本覆盖了95%的入门问题报错现象常见原因处理方式No labels found图像目录和标签目录不对应或yaml路径写错检查data.yaml中train/val路径确认标签文件存在All labels are empty某个txt内容是空文件检查是否漏标删除或补标后重训boxes0标签坐标不合法越界、未归一化、class_id超界检查txt数值是否都在0-1内类别id小于names数量CUDA out of memorybatch或imgsz太大降低batch改小imgsz换更小模型BrokenPipeErrorWindows下多进程DataLoader问题训练命令加workers0或调小workers多数训练失败都不是模型结构问题而是数据问题。开局先把标签、路径、配置这些基础工作做扎实后面基本是一路顺风。4. 2400张小数据集的训练技巧与效果提升4.1 小样本如何用好数据增强2400张图对深度学习来说不算多尤其是3个类别样本分布不均时某个类可能只有300张左右非常容易过拟合。我的建议是先做一轮离线增强水平翻转、垂直翻转、90度旋转、随机亮度对比度调整把每类不足的样本扩到至少600张。光伏板的缺陷相对比较“对称”翻转和旋转不会破坏语义但这里有个前提——如果裂纹总是呈现固定方向比如统一从左下到右上盲目旋转会改变方向分布反而削弱模型的真实判别能力。离线增强要注意一个关键点增强后的图像必须同步生成对应的标签不能只增强图像。最简单的方式是用Albumentations库它支持bbox参数同步处理标注框。import albumentations as A import cv2 aug A.Compose([ A.HorizontalFlip(p0.5), A.VerticalFlip(p0.5), A.RandomBrightnessContrast(p0.5), ], bbox_paramsA.BboxParams(formatyolo)) image cv2.imread(images/img_0001.jpg) with open(labels/img_0001.txt) as f: boxes [list(map(float, line.split()[1:])) for line in f.read().strip().splitlines() if line.strip()] class_ids [int(line.split()[0]) for line in f.read().strip().splitlines() if line.strip()] augmented aug(imageimage, bboxesboxes, class_labelsclass_ids)这里注意读取boxes和class_ids时如果一次性读文件内容前面读过一次后第二次就是空的了要把读取动作合并到一次循环里实际使用时我一般把解析封装成函数避免这种低级错误。另一个实用技巧是切块训练。如果原图是1280x1280完整光伏板照片裂纹可能只有几十个像素目标占比太小模型学起来非常吃力。把每张图切成4块640x640的patch目标相对占比变大检测效果通常有肉眼可见的提升。代价是样本数乘以4训练时间变长但在小数据集场景下非常值得尝试。4.2 针对细长裂纹的检测优化光伏隐裂、栅线裂纹这类缺陷有一个共同特点细长、长宽比悬殊、边界模糊。YOLO系列用矩形框回归对这类目标天生不友好——一条对角线走向的细长裂纹它的gt框面积可能不小但真正有判别力的像素只占框内很小比例这会让损失函数被大量“无特征”区域稀释。针对这个问题我常用的招有三招。第一训练时把imgsz调大到960或者1280让细长特征在早期下采样时不至于直接消失。第二如果用yolov8在neck层尝试添加P2输出层让浅层细粒度特征参与目标检测。第三后处理阶段把NMS的IoU阈值适当调低比如从默认0.7降到0.5减少细长目标的框在NMS阶段被错误抑制的情况。这些调整不用一上来全用每加一个改动就在验证集上对比各类别AP尤其关注裂纹那一类的单独AP。很多时候总mAP没怎么变但裂纹类的AP从0.3涨到0.5这在实际光伏板缺陷检测项目里才是最有价值的提升。4.3 类别不均衡的处理3个类别的样本数很可能差异明显脏污样本占了一大半裂纹只有一小撮。表现就是loss下降正常但少数类的AP惨不忍睹模型大部分精力都在拟合多数类。处理方式有三条路。第一是给少数类更高的loss权重ultralytics里可以通过配置项调整第二是数据增强时对少数类做过采样每轮迭代多抽这些样本第三是自己改损失函数用Focal Loss思路让模型更关注难分类样本。我的经验是先用过采样代码改动最小效果相对可控。训练前先跑一个统计命令看看各类别样本量差距到底多大awk {print $1} labels/*.txt | sort | uniq -c如果某个类数量是另一类的3倍以上就认真做均衡处理。2400张的训练集里类别不均衡如果不处理最后你得到的模型基本就是个“多数类检测器”这在工业场景上是不能接受的。5. 最后再分享几点实操心得5.1 先小规模跑通再全量训练拿到数据集后不要急着全量训练先从每个类别抽30张图凑成90张左右的小数据集把训练流程完整跑一遍确认数据加载、标签读取、loss计算、验证评估全部正常。这一步十几分钟就能搞定但能避开95%的低级错误。全量训练时再翻车一次就要等几个小时成本完全不一样。5.2 验证集一定要人工复核mAP是个漂亮的数字但光伏缺陷检测在工程上要的是稳定输出。训练结束后把验证集预测结果可视化导出来一张张看尤其关注漏检和误检。我遇到过模型mAP有0.85结果抽检发现同一块裂纹在不同光照背景下识别忽好忽坏这就是验证集评估过的场景太少、模型对光照敏感导致的。人工复核虽然费时间但能帮你发现指标层面完全看不见的问题。5.3 后续可以继续扩展的方向这套流程跑完之后可以往几个方向继续挖一是换更大的预训练模型做迁移学习比如yolov8m、yolov9系列小数据集上也能受益于预训练特征二是从检测延伸到分割做光伏板电池片的像素级缺陷定位三是如果数据里有红外和可见光两个模态的图试试多模态融合检测这在光伏电站巡检里非常实用。我个人在工业缺陷检测项目里最深的一点体会是小数据集的瓶颈从来不在模型调参而在数据质量和标签规范。把VOC转YOLO、标签校验、数据划分这些基本功练扎实后面换任何检测框架、任何数据集你都会比大多数人快一步。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价