首页 / 资讯中心 / 文章详情

光伏板缺陷检测实战:数据集构建、YOLOv8训练与部署全解析

光伏板缺陷检测实战:数据集构建、YOLOv8训练与部署全解析 ★ FEATURED ARTICLE
简介这是一套面向光伏板缺陷检测的完整资源包适合光伏运维、工业质检、无人机巡检以及AI视觉研究的技术人员使用。数据集覆盖裂纹、脏污、热斑、遮挡、破损等多类常见缺陷包含已标注图像并配套YOLO模型配置、训练脚本与权重文件可直接用于模型训练、迁移学习或算法验证。资源共2000个文件压缩包约43.16MB其中jpg/png为缺陷样本图像txt为标注框信息yaml为模型配置文件py为检测与训练代码pt为预训练模型权重csv为训练结果统计文件组织清晰便于按需取用。目前已有61人学习下载。通过该资源可快速搭建光伏板缺陷检测流程结合无人机巡检图像分析电站组件状态有助于提高检测效率、降低人工成本为光伏电站智能化运维和科研实验提供可复用的基础支撑。1. 光伏板缺陷检测为什么数据集比模型更值钱代码怎么跑起来如果你的工作是把“光伏板缺陷数据集、模型含检测代码”这套东西落到电站巡检里先别急着训练。我先说一个反直觉的现状决定上线效果的往往不是模型有多新而是你的光伏板缺陷数据集是不是干净、有没有红外图和可见光图混着、标签框是不是准确。同样一份检测代码换一批标注乱七八糟的照片mAP能从0.8掉到0.3。标题里的三件套本质是一条从“缺陷影像”到“可部署模型”的流水线先整理热斑、隐裂、断栅这些缺陷的照片和标注再选一个对小目标友好的检测模型最后用Python把推理、批量报告、缺陷定位跑通。这篇文章适合做无人机巡检、光伏运维系统集成或者刚入门视觉检测的开发者。我会把数据集怎么攒、模型怎么选、检测代码怎么写以及最容易翻车的几个地方一次讲清楚。2. 打开光伏板缺陷数据集公开资源、自建标注与格式转换脚本2.1 公开数据集的现状与三个格式坑光伏板缺陷不像COCO那样有大规模统一数据集。你能在GitHub或Kaggle上搜到“solar panel defect dataset”相关资源多数来自论文附带数据、电站巡检项目脱敏后的零星照片以及一些竞赛数据。常见缺陷类别包括热斑、隐裂、断栅、蜗牛纹和旁路二极管区域异常。公开数据集普遍有三个问题第一数量少很多只有几百到上千张而且类别分布极度不均。热斑照片多隐裂和断栅少因为现场本来就难采集到足够样本。第二成像条件不统一。有的数据是红外热像仪拍的有的是普通可见光相机拍的同一个检测模型很难同时适配两种域差异很大的图像。第三标注格式五花八门。Pascal VOC的XML、COCO的JSON、YOLO的txt混着给批量训练前必须做一次统一转换。拿到一份公开数据集后我会先做三件事解压后统计所有图片尺寸打印每张图的标注类别分布再随机抽10张图把标注框画出来人眼看一遍。这一步能挡住大部分脏数据。如果图片尺寸差异超过3倍训练时imgsz参数会很难选建议用切图或缩放到统一尺寸。标注类别名称同样要统一“thermal spot”和“hot spot”明明是同一个东西会因为命名不一致被当成两类。2.2 自建数据集拍摄、切图与标注流程公开数据不够时现场自建是最靠谱的路。拍摄设备一般是无人机挂载红外热像仪加可见光摄像头红外分辨率常见640×512可见光分辨率高一些但缺陷对比度低。拍的时候应尽量让光伏板平面充满画面避免斜着拍导致几何畸变。原始图片是一整片电站阵列缺陷在图上只占很小区域直接训练会让模型变成“背景识别器”。所以需要切图把大图切成640×640或800×800的小块并保留一部分重叠区域防止缺陷正好被切在两条边的交界处。下面这段切图脚本适合放在数据集预处理的第一个环节import cv2 import glob import os SAVE_DIR slices SLICE_SIZE 640 # 切图边长 OVERLAP 80 # 重叠像素避免缺陷被切断 os.makedirs(SAVE_DIR, exist_okTrue) for img_path in glob.glob(raw/*.jpg): img cv2.imread(img_path) h, w img.shape[:2] base os.path.splitext(os.path.basename(img_path))[0] step SLICE_SIZE - OVERLAP for y in range(0, max(h - SLICE_SIZE, 1), step): for x in range(0, max(w - SLICE_SIZE, 1), step): crop img[y:y SLICE_SIZE, x:x SLICE_SIZE] cv2.imwrite(f{SAVE_DIR}/{base}_x{x}_y{y}.jpg, crop) # 初始化同名空label文件后续用标注工具打开填入 with open(f{SAVE_DIR}/{base}_x{x}_y{y}.txt, w) as f: pass逻辑说明step SLICE_SIZE - OVERLAP控制滑动步长等于切块时每张图与相邻图重叠80像素。之所以要重叠是因为缺陷假如横跨切图边界两个半块各带半个目标标出来会让模型学到“残缺目标”。循环里先写空txt文件是为了强迫自己在标注阶段逐张打开而不是漏掉某些小图。切图尺寸建议640起步显存紧张再降到512如果热斑往往只有10几个像素800比640更稳代价是训练显存和耗时上升。切完图后用LabelImg或者任何VOC格式标注工具逐张标注。重点提示对光伏场景不要把整块电池片框进去只框真正的缺陷本体。热斑框要贴合高温区域边缘隐裂框要覆盖裂纹整条走向。类别名统一用英文小写加下划线比如hot_spot、crack、ribbon_break、snail_trail后面配置YAML时直接对应。2.3 样本划分与不平衡处理自建数据往往会遇到热斑3000张、隐裂只有200张这种极端不平衡。直接按全量随机划分会让验证集里隐裂样本过少评估指标失真。常见做法是按“图片包含的缺陷类别组合”做分层抽样保证每张图携带的类别列表在训练、验证、测试集中分布一致。from pathlib import Path from sklearn.model_selection import train_test_split all_images list(Path(slices).glob(*.jpg)) samples, labels [], [] for img_file in all_images: txt_file img_file.with_suffix(.txt) if not txt_file.exists(): continue with open(txt_file, r, encodingutf-8) as f: cls_list sorted({line.split()[0] for line in f if line.strip()}) if not cls_list: continue samples.append(str(img_file)) labels.append(_.join(cls_list)) train_files, temp_files, train_labels, temp_labels train_test_split( samples, labels, test_size0.3, stratifylabels, random_state42 ) val_files, test_files train_test_split( temp_files, test_size0.5, stratifytemp_labels, random_state42 )逻辑说明labels.append(_.join(cls_list))把一张图的所有类别组合成一个字符串作为分层标签比单类别分层更细。例如某张图同时有热斑和隐裂它的分层标签是crack_hot_spot这样划分后多缺陷组合图不会全跑到训练集而验证集只剩单类图。对稀有类别比如只有100张的断栅我会先做轻度数据增强水平翻转、亮度抖动、小角度旋转5度以内。不要复制粘贴相同图片进数据集那只会让模型记住这张图的噪声不会泛化。生成增强样本时用imgaug或albumentations在标签上同步做仿射变换注意旋转后标注框必须重算不能只转图片而框不动。3. 选模型与训练从 YOLOv8 到 transformer 检测头的取舍3.1 为什么默认选 YOLOv8n/s低显存更友好光伏板缺陷检测本质是中小目标检测问题。一块640像素的切图里光伏板占满画面而热斑可能只有20×20像素到60×60像素。目前最稳的模型选择是YOLOv8的nano和small档位。原因有三个一是YOLOv8在Ultralytics生态里训练和导出非常顺一条命令跑完训练、验证、导出ONNX二是nano档参数量约300万在6GB显存的笔记本上也能训练非常适合低显存运行模型这类场景三是PANet特征融合结构对小目标比早期YOLOv5更友好。Transformer检测头近两年很火DETR、RT-DETR这类模型理论上能建模全局上下文但对光伏缺陷这种小目标且背景高度重复的场景优势并不明显。它们的训练需要更长warmup和更多epoch在小数据集上收敛不稳显存占用也高。我一般只会在缺陷类别大于5类、数据规模到数万张时才考虑做一组RT-DETR和YOLOv8的对比实验。至于模型融合工程上常见做法是把YOLOv8和RT-DETR的预测框做WBF合并但它没法弥补数据本身的缺陷反而让推理链路复杂早期不建议碰。模型参数量典型显存占用batch16, 640适用场景YOLOv8n约3M4~6GB边缘盒子、低显存开发机YOLOv8s约11M8~12GB服务器批量检测、精度优先RT-DETR等Transformer模型十几M到几十M12GB以上大规模数据、复杂背景对比实验3.2 训练命令和 6 个必调参数我习惯先用YOLOv8n跑一个快速实验看数据有没有问题再换YOLOv8s追求更高精度。数据集配置文件photovoltaic.yaml是第一步。# photovoltaic.yaml path: ./datasets/defect train: images/train val: images/val test: images/test nc: 4 names: 0: hot_spot 1: crack 2: ribbon_break 3: snail_trail这个文件让训练脚本知道图片和标签在哪、类别数量是多少。nc必须与标注txt里的类别编号一一对应。接下来是训练命令yolo detect train \ dataphotovoltaic.yaml \ modelyolov8n.pt \ epochs200 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ optimizerAdamW \ degrees10 \ fliplr0.5 \ device0 \ seed0参数说明这6个是最容易影响结果的imgsz640输入分辨率。光伏缺陷小建议不要低于640显存只有4GB时降到512比强行减小batch更稳。batch16批量大小。太小会导致BN统计量抖动训练loss像锯齿显存不够时优先开ampTrue它是Ultralytics默认开启的混合精度训练能在不牺牲太多精度的情况下省约30%显存。lr00.01迁移学习时的初始学习率适合在COCO预训练权重上微调。如果是从头训练我会调到0.001~0.005否则很容易发散。lrf0.01最终学习率是初始学习率的百分之一训练后期用余弦退火降到这个值帮助收敛到更平的极小值。degrees10训练时允许图片随机旋转10度以内。光伏板阵列有方向性但无人机拍摄角度会变适度旋转能提升泛化。fliplr0.5水平翻转概率。光伏板左右对称翻转不会破坏物理含义等于凭空多了一倍样本。训练结束后看runs/defect/yolov8n_pt/weights/best.pt这是300轮里验证集表现最好的权重不是最后一轮的权重。如果训练过程中loss正常下降但mAP长时间卡在0.4以下优先怀疑数据问题而不是模型问题。3.3 当数据量不够迁移学习与轻量蒸馏光伏缺陷数据集很难做到几千张自带同分布场景。这时候最实用的手段是迁移学习和轻量蒸馏。迁移学习方面直接加载yolov8n.pt预训练权重大多数情况下比随机初始化好。但对红外图像ImageNet和COCO预训练的特征与热成像差异很大所以我会先保留全部层一起训练用较小学习率lr00.005跑100轮让模型自己适应红外纹理。如果红外图很少可以冻结前10层特征提取层只训练检测头代码里加上freeze10参数。轻量蒸馏是我在低显存场景下常用的做法。假设你有能力先训练一个YOLOv8s教师模型再想部署到边缘盒子用YOLOv8n可以先用教师模型给训练集做预测把高置信度的预测框当作“软标签”然后训练学生模型时在原始GT损失上叠加一个蒸馏损失让学生模型学教师模型的输出分布。Ultralytics没有一行命令直接支持我一般用蒸馏库或者自己写一个简单的损失加权total_loss loss_gt alpha * loss_distillalpha从0.5开始调。数据量小的时候这个操作比直接换模型有效得多。4. 检测代码怎么写单张推理、批量导出与坐标还原4.1 单张图片推理脚本训练完成后最常用的是单张图片快速验证。下面这段代码适合写进任何Python项目里作为推理入口from ultralytics import YOLO model YOLO(runs/defect/yolov8n_pt/weights/best.pt) img_path IMAGES/IMG_4321.jpg results model.predict( img_path, conf0.4, iou0.5, imgsz640, device0, verboseFalse ) for r in results: boxes r.boxes names model.names # 逐框打印类别、置信度和坐标 for cls_id, conf, xyxy in zip(boxes.cls, boxes.conf, boxes.xyxy): label names[int(cls_id)] x1, y1, x2, y2 [float(v) for v in xyxy.tolist()] print(f{label} conf{float(conf):.3f} bbox({x1:.1f},{y1:.1f},{x2:.1f},{y2:.1f}))逻辑说明results是列表每张输入图对应一个ultralytics.engine.results.Results对象。boxes.cls是类别编号张量boxes.conf是置信度张量boxes.xyxy是左上角和右下角坐标。打印时先转换成Python标量避免在模板字符串里直接拼接Tensor。conf0.4表示置信度阈值低于0.4的框不会输出iou0.5是NMS的IoU阈值两个重叠框的交并比超过0.5会保留置信度更高的那个。单张图跑通后建议立刻测试两张反面样本一张完全没有缺陷的正常光伏板一张有大量灰尘干扰的照片。正常板应该输出空列表灰尘图应该不报或只报低置信度框。如果灰尘图出来一堆假框说明conf阈值要往上调。4.2 批量推理导出 CSV 报告工程上要交付的不是在终端打印坐标而是一个可追溯的缺陷清单。我会批量读一个目录下的所有图片把每张图的检测结果写入CSV方便后续和运维工单对接。import csv import glob from ultralytics import YOLO model YOLO(best.pt) image_paths sorted(glob.glob(./test_images/*.jpg)) rows [[image, class, confidence, x1, y1, x2, y2]] for img_path in image_paths: results model.predict( img_path, conf0.45, iou0.5, imgsz640, device0, verboseFalse ) for r in results: for cls_id, conf, xyxy in zip(r.boxes.cls, r.boxes.conf, r.boxes.xyxy): x1, y1, x2, y2 [round(float(v), 2) for v in xyxy.tolist()] rows.append([ img_path, model.names[int(cls_id)], round(float(conf), 4), x1, y1, x2, y2 ]) with open(defect_report.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerows(rows) print(fdone, total boxes: {len(rows) - 1})我在实际项目中会把CSV里的坐标再换算成原始大图坐标因为上游输入可能是切图。假设小图来自原图坐标(offset_x, offset_y)CSV里的x1、y1加上偏移量即可。这个映射逻辑容易错我在日常项目中用命名约定IMG_4321_x1024_y1536.jpg保存切图文件名本身就是坐标解析时用正则取出来后面第4.3节会专门说。4.3 切图推理的坐标还原与可视化切图推理最常见的坑是模型在小图上框出了缺陷但运维要的是在整块电站大图上定位缺陷。坐标还原其实很简单但必须保证与切图时使用同一套offset。我写完切图脚本后会写一个推理映射函数import re import cv2 # 解析文件名里的原图坐标 def parse_offset(filename): m re.search(r_x(\d)_y(\d)\.[a-z]$, filename) if m: return int(m.group(1)), int(m.group(2)) return 0, 0 def draw_with_offset(img_path, results, offset_x, offset_y): img cv2.imread(img_path) for r in results: for cls_id, conf, xyxy in zip(r.boxes.cls, r.boxes.conf, r.boxes.xyxy): x1, y1, x2, y2 [int(round(v)) for v in xyxy] # 映射回原图 x1, y1 x1 offset_x, y1 offset_y x2, y2 x2 offset_x, y2 offset_y color (0, 0, 255) if int(cls_id) 0 else (0, 255, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) label f{r.names[int(cls_id)]} {float(conf):.2f} cv2.putText(img, label, (x1, max(0, y1 - 4)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 1) return img这帧代码的重点在parse_offset和draw_with_offset两个函数配合。前者从切图文件名中拆出偏移量后者把每个框的坐标分别加上偏移量画到原图上。如果用小图直接可视化缺陷看起来没问题但叠加到原图后框会偏斜原因往往就是漏加偏移量。有了这个函数缺陷定位报告可以直接生成大图标注版给现场运维看的时候不需要再解释坐标概念。5. 光伏板缺陷检测避坑指南漏检、误检与训练不收敛的排查5.1 热斑漏检红外图和可见光图的通道差异现象用普通可见光训练出来的模型去跑红外热像仪拍摄的现场图热斑几乎全部漏检一张都框不出来。原因热斑在可见光图像里没有明显的形状和颜色特征人眼都看不出异常模型自然无从学起。红外图是单通道温度分布图和可见光的像素分布不是一个域。如果直接把可见光模型当通用模型用等于拿一个只会看自然光图的模型去分析热成像数据必然彻底翻车。解决热斑检测必须用红外图单独训练或者让训练数据至少包含大量红外域样本。我一般会把红外图像做一下预处理再进模型对单通道红外图做灰度拉伸、归一化到0到255然后复制成三通道输入或者用伪彩色映射cv2.applyColorMap后再训练。注意如果用伪彩图训练和推理时必须用同一套映射关系否则模型看到的输入完全不同。实在只有少量红外图可以用CycleGAN或简单的直方图匹配把可见光图迁移成红外风格但不要指望这招替代真实数据。5.2 误检灰尘、水渍、鸟粪被当成缺陷现象模型把光伏板上的灰尘阴影、水渍痕迹、鸟粪甚至杂草阴影框成热斑或隐裂置信度还高达0.6以上。原因数据集里几乎没有“负样本”类别模型没见过干扰物只能把它们归到最相似的缺陷类别。另一个原因是推理阈值设得太低比如conf0.25导致模型把很多低置信度的背景响应也输出。解决分两步走。第一步在数据集中增加一个negative或disturbance类别把现场拍到的灰尘、水渍、鸟粪、杂草阴影单独标注让模型明确知道这些东西不是缺陷。本来分类不足4类的话这类负样本要少收够让模型“见过”就行通常100到200张。第二步推理时把conf从默认0.25提高到0.4或0.5然后在验证集上看F1曲线找最合适的阈值。实验经验是0.4通常能让误检下降一半同时热斑召回率只掉两个百分点。5.3 训练损失卡住不下降的排查顺序现象loss在训练集上降到0.05左右就再也不动验证集mAP一直卡在0.4到0.5多训练几百轮也没变化。原因这个现象背后可能有好几个叠加原因。最常见的是输入分辨率太低热斑在缩小后只剩5到8个像素特征提取器无从学习。其次是数据增强过猛比如degrees180让光伏板旋转到上下颠倒缺陷的形状被完全破坏。再就是类别不平衡使得大头类别主导梯度小类别学不充分。解决按顺序排查。先把imgsz从640提到800看loss是否继续下降。然后把degrees降到5、去掉hsv_v的强变化因为红外图像的色彩抖动意义不大。如果还不行检查训练集里每类的标注框数量对稀有类做复制粘贴增强时加上小幅平移和缩放。最后一个办法是训练时把mosaic1.0关掉光伏板缺陷覆盖面积太小mosaic拼图反而会让目标尺寸进一步缩小对小目标训练不利。5.4 标签错位和数据集污染现象训练流程没有报错loss也正常下降但验证集里检测框总是偏到电池片边缘或组件缝隙上看起来模型在“找边框”而不是找缺陷。原因这是典型的标签错位。切图后txt文件与图片没有一一对应或者标注时在LabelImg里误操作导致框坐标偏移。数据污染还包括图片被不小心旋转了90度但标注框没有旋转或者前后两轮的类别编号不一致。解决训练前必须跑一次可视化校验脚本把每张训练图连同标注框画出来每个类别抽10张人工过目。重点检查两部分一是框是否贴合目标边缘二是类别编号是不是和YAML配置一致。另一个有效脚本是统计所有txt框面积占图片总面积的比例如果某个类别平均框面积异常小或异常大基本就是标注出了问题。我在好几个项目里靠这个检查躲过了“垃圾进垃圾出”。5.5 部署到边缘设备显存不足与推理延迟现象模型在服务器GPU上单张只要几毫秒换到Jetson或工控机上变成几百毫秒偶尔还报显存不足硬退出。原因训练时用的FP32权重在边缘设备上直接加载显存和算力都吃不消。输入分辨率还是训练时的800边缘设备CPU解码和预处理开销巨大。另外批量推理时batch设得太大边缘设备内存扛不住。解决先把模型导出为TensorRT的FP16引擎或者ONNX做INT8量化绝大多数场景精度损失在1到2个点以内。导出时固定imgsz为640和训练保持一致。推理代码里加一个前后处理耗时统计如果瓶颈在图像解码上就改用OpenCV的imdecode而不是Ultralytics默认的PIL。最后把predict的batch设为1device设置为GPU索引实测往往能省掉一半显存。如果用了YOLOv8s还是慢退回YOLOv8n并配合蒸馏比盲目调部署参数更有效。6. 验证模型该不该上线用 F1、IoU 和混淆矩阵说话模型不是跑通就算完事光伏电站场景下漏检和误检的成本差别很大。漏检的热斑可能直接导致组件烧毁误检太多会让运维人员失去对系统的信任。所以我会用三个指标综合判断模型能不能上线各类别的召回率、精确率和F1分数加一个混淆矩阵查看误分类的方向。只依赖验证集全体的mAP很容易被整体趋势骗过去。比如热斑类别样本占90%隐裂只有5%mAP即使到0.8隐裂可能一塌糊涂。我用一段脚本在每个类别上单独计算指标from sklearn.metrics import classification_report, confusion_matrix # valid_results 是验证集每张图的预测 y_true [] y_pred [] for result in valid_results: gt_classes set([int(c) for c in result[gt_cls]]) pred_classes set([int(p) for p in result[pred_cls] if p[conf] 0.4]) for c in range(num_classes): y_true.append(1 if c in gt_classes else 0) y_pred.append(1 if c in pred_classes else 0) print(classification_report(y_true, y_pred, target_namesclass_names, digits3)) print(confusion_matrix(y_true, y_pred))逻辑说明这里用“图像级别”的类别判断而不是逐框计算mAP。对运维来说一张图里有没有漏掉隐裂比框坐标偏移几个像素更重要。classification_report输出每个类别的精确率、召回率和F1confusion_matrix能反映模型是否总把隐裂错认成热斑。如果某类召回率低于0.7我会单独收集更多该类样本或调低该类别的conf阈值。上线前我还会做一次“阈值扫描”画出每个类别的confidence-F1曲线找F1最高的点。比如热斑阈值0.35隐裂阈值0.5是不同的因为隐裂误检代价高。Ultralytics的model.predict支持conf单个值但如果你对每个类别设置不同阈值需要对模型输出的原始boxes按类别过滤代码改动量不大但运维效果提升很明显。去年我在一个项目里只盯着整体mAP结果模型上线后被现场人员拉着看两周记录才发现隐裂的漏检率远比mAP显示的严重。后来我们改成按缺陷类别分开评估把阈值各自调一遍才真正达到上线标准。这也是我现在坚持在验证阶段用F1、IoU和混淆矩阵一起说的原因。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站