
简介基于无人机场景的光伏面板缺陷检测项目面向新能源运维工程师、算法研究者及计算机视觉学习者解决传统人工巡检效率低、精度不稳定等痛点。整套资源共405个文件压缩包约106.72MB核心为289个Python脚本及配套的模型权重文件pth、pb、checkpoint等另有可执行文件、动态库、配置文件等覆盖从无人机图像采集到缺陷分类的完整算法流程代码结构清晰便于直接运行与二次开发。资源实现了图像预处理、分割、特征提取及SVM/RF分类等关键环节利用OpenCV、TensorFlow、Keras等主流框架并附带实际应用场景说明与操作指南用户可结合实际巡检图像快速上手。项目还保留了良好的模块化扩展性可替换滤波算法、尝试不同特征描述子或引入更多分类模型适应不同质量图像与检测需求。目前已有282人学习下载适合具备一定Python基础、希望深入工程实战或快速搭建光伏缺陷检测原型的读者。1. 无人机拍下的光伏板缺陷为什么反而不容易找到无人机巡检光伏电站这几年成了运维标配但真正上手做过光伏面板缺陷检测的人会发现最不缺的是能看清楚的航拍图最缺的是缺陷在像素里能占多少地盘。一块 1.5m×1m 的光伏板从 30m 高度拍下来隐裂这种线状缺陷往往只有十几个像素宽。深度学习算法在这类图像上的头号问题不是识别准确率而是小目标经过骨干网络三四个 stride 之后基本被抹平。所以这个标题的完整解法要拆成四段算法怎么选、数据怎么标、小目标怎么训、检测结果怎么定位到具体面板。文章会按这条线走完适合正在被测试集精度卡住的算法工程师也适合准备接无人机光伏项目的开发和售前团队。2. 光伏面板缺陷检测的算法选型从可见光、红外到 EL做光伏面板缺陷检测第一步不是选模型而是先弄清楚用哪个波段去看哪一类缺陷。可见光图对用户最直观红外图对热斑最敏感EL 电致发光图对隐裂最可靠但 EL 设备在无人机场景里要接触供电、还要低照度同步拍摄工程代价明显比前两种高。项目里最现实的组合是可见光负责隐裂、碎片、脏污红外负责热斑EL 只在少数慢速悬停场景里作为复核手段。2.1 先按模态分缺陷再谈算法无人机单次巡检拿到的实际上是两路数据一路是普通 RGB 图一路是红外热像图。两路数据不能混在一个模型里训练因为缺陷表现完全不同。先把常见缺陷按“可检测模态”拆开训练时候的标签和模型数量也就清楚了。缺陷类型常见成因可见光表现红外表现无人机落地重点隐裂搬运、热应力细线状易被玻璃反光掩盖早期温差不明显高分辨率可见光模型要能召回细长目标热斑遮挡、电池片内阻异常表面无明显痕迹局部高温亮点红外图像直接检测温度规则做二次过滤断栅焊接工艺、老化周期性细横线不明显可见光需要图像分辨率足够高碎片冰雹、异物冲击裂纹网络、碎块错位局部温差可见光即可形状特征明显脏污鸟粪、积灰不规则斑块容易被误判为热斑可见光要和红外结果交叉复核表格里第五行特别容易出问题。鸟粪在红外图里往往显示为高温点单独跑热斑模型会把脏污当成 hot spot。项目上我一般把脏污和热斑分开建模最后在结果合并时用空间位置对齐同一个坐标如果同时命中“dirty”和“hotspot”优先按脏污处理并转人工复核。2.2 为什么是 YOLOv8 而不是 Halcon 或 Faster R-CNN工业缺陷检测里很多人会先想到 Halcon 的方案但 Halcon 缺陷检测更适合固定工位光源稳定、位姿固定、产品在视野中的尺寸基本不变。无人机航拍图的光照、视角、离地高度一直在变再加上光伏阵列的金属边框、跟踪支架、拉线、玻璃反光传统形态学模板很难维护成一套能交付的算法。两阶段检测器 Faster R-CNN 在小目标召回上确实有优势但机载设备和边缘盒子上的推理速度撑不住整站巡检。实际工程取舍下来YOLOv8 是当前性价比最高的基线训练生态完整导出 TensorRT 方便还有 P2 检测头和 SAHI 切片工具可以补小目标短板。如果追求更强的小目标性能可以再看 RT-DETR但推理耗时和部署复杂度要重新评估。我的建议是先用 YOLOv8s 跑通全流程出基线后再决定往轻了走还是往精度走。2.3 标数据与增强把无人机大图喂给模型之前的最后一道工序标注规范上类别控制在四个到五个最稳crack、hotspot、broken_cell、dirty。不要一上来就分“横向隐裂”“纵向隐裂”“网状碎片”类别太细会让每个类别的样本量都被稀释模型学不出共性。标注工具用 CVAT 或 LabelMe 都可以导出的标签统一转成 YOLO 格式再进训练。无人机原始图动辄 4000×3000不能整张直接训练通常先在标注阶段按 1280 裁剪出候选区域再做增强。下面这段用 Albumentations 做增强并保持 YOLO 标签对齐的代码是数据处理脚本里最常被复用的部分import albumentations as A import cv2 # 对裁剪后的1280图做增强RandomSizedBBoxSafeCrop保证标注框不越界 augmentor A.Compose([ A.RandomSizedBBoxSafeCrop(width1280, height1280, erosion_factor0.2, p0.7), A.RandomBrightnessContrast(brightness_limit0.3, contrast_limit0.3, p0.5), A.HueSaturationValue(hue_shift_limit5, sat_shift_limit20, val_shift_limit20, p0.3), A.RandomGamma(gamma_limit(80, 120), p0.4), A.HorizontalFlip(p0.5), ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels], min_visibility0.3)) # image_files/label_files 由你按训练集文件生成 for img_path, label_path in zip(image_files, label_files): image cv2.imread(img_path) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) bboxes, class_labels [], [] with open(label_path) as f: for line in f: cls, cx, cy, w, h map(float, line.split()) bboxes.append([cx, cy, w, h]) class_labels.append(int(cls)) out augmentor(imageimage, bboxesbboxes, class_labelsclass_labels) with open(label_path.replace(.txt, _aug.txt), w) as f: for box, cls in zip(out[bboxes], out[class_labels]): f.write(f{int(cls)} { .join(map(str, box))}\n)这段代码的要点是bbox_params里的min_visibility0.3。它控制裁剪后至少保留原目标 30% 的可见面积低于这个比例的目标直接丢弃避免模型反复去看只剩一个角的缺陷。erosion_factor0.2表示裁剪框最多缩小到原始尺寸的 20%这个值如果设得太小会把一个缺陷切成两个残块反而不利于训练。另外如果数据源是红外单通道图HueSaturationValue要移除保留亮度对比度增强就好否则单通道数据会被随机处理成奇怪的颜色空间。3. 用 YOLOv8 在无人机影像上训练缺陷检测模型数据管线跑通后的下一步就是模型训练。无人机光伏缺陷检测和一般目标检测场景最大的区别在于分辨率缺陷小、图像尺寸大、背景纹理复杂。普通目标检测默认用 640 输入在光伏场景下几乎必翻车。这里给出的训练配置和调参思路都是围绕“把细节留住”展开的。3.1 最小训练命令与必调参数先准备数据集配置文件类别名要和标注顺序严格一致path: ./datasets/pv_defect train: images/train val: images/val nc: 5 names: [crack, hotspot, broken_cell, dirty, disconnected]然后直接跑训练yolo detect train \ datacfg/dataset.yaml \ modelyolov8s.pt \ imgsz1280 \ epochs200 \ patience30 \ batch16 \ device0 \ optimizerAdamW \ lr01e-3 \ cos_lrTrue \ projectoutputs \ namepv_yolov8s几个参数我在光伏项目里的固定选择如下参数建议值理由imgsz1280640 下隐裂只有几个像素特征在骨干网络里直接消失patience30无人机数据集通常几千张早停太严容易错过后期收敛的权重batch16 起步显存允许就加大batch 太小加 AdamW 容易训练震荡lr01e-3迁移学习微调可以降到 5e-4数据少时更稳cos_lrTrue训练后期学习率平滑下降收敛更干净mosaic1.0保持默认最后 20 个 epoch 可以改成 0.3 做精调imgsz1280是这个任务里最值的参数。如果你的源图更大也不建议继续往 1472、1536 上顶因为显存开销曲线很陡收益递减明显。实测中 1280 搭配后期 mosaic 调小已经能覆盖大多数隐裂样本。3.2 小目标缺陷的两个处理技巧P2 检测头与 SAHI 切片如果训练完发现小缺陷召回还是不稳第一个手段是换 P2 检测头。P2 会把特征图下采样到原图的四分之一再接检测头浅层特征里保留了更多边缘细节。用 YOLOv8 官方的yolov8s-p2.yaml替换 model 参数即可加载预训练权重用pretrainedyolov8s.pt。代价是显存和训练时间上涨工程上如果显存只够 16G我建议优先保住 1280 的输入分辨率P2 作为可选增强。第二个手段是推理阶段用 SAHI 做切片预测。SAHI 把大图切成瓦片检测完再合并结果专门解决“目标太小”的问题。下面这段是推理调用示例from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model AutoDetectionModel.from_pretrained( model_typeultralytics, model_pathoutputs/pv_yolov8s/weights/best.pt, confidence_threshold0.25, image_size1280, devicecuda:0, ) result get_sliced_prediction( flight_20250310/A1/images/IMG_0421.JPG, model, slice_height1280, slice_width1280, overlap_height_ratio0.2, overlap_width_ratio0.2, postprocess_typeNMM, postprocess_match_metricIOS, postprocess_match_threshold0.5, )切片尺寸我直接给 1280和训练分辨率保持一致。postprocess_match_metricIOS值得多说一句裂纹这类目标长宽比经常超过 5两个重叠切片对同一个裂纹预测出来的框形状差异很大IOU 匹配会把真阳性拆成两条IOU 的变体 IOS交集面积除以较小框面积对这种细长目标更友好。overlap_height_ratio0.2是底线低于 0.1 时跨切片区经常把裂纹切断导致目标被拆成两半。3.3 评估别被 mAP50 骗了训练结束先看 mAP50再看 mAP50-95最后单独做一次小目标统计。光伏缺陷检测里经常出现 mAP50 很高、实际跑航线却到处漏检的情况原因就是隐裂只贡献了很少的像素在 50% IoU 下勉强算对IoU 阈值一旦拉高定位精度不足的缺陷全部被打回。所以 mAP50-95 比 mAP50 更有参考价值如果两者差超过 0.15说明模型是“框得着但不稳”优先去查标注框是否贴边、裂纹是否被裁断。另一个必做的统计是查看训练集中小目标占比。用下面这个脚本快速算一下from pathlib import Path # 假设训练分辨率为1280统计面积小于32x32像素的目标占比 small total 0 for label_file in Path(datasets/pv_defect/labels/train).glob(*.txt): for line in open(label_file): _, cx, cy, w, h map(float, line.split()) px_area (w * 1280) * (h * 1280) total 1 if px_area 32 * 32: small 1 print(fsmall ratio: {small / max(total, 1):.2%})如果小目标占比超过一半那训练阶段的所有参数都要默认往小目标方向倾斜P2 检测头、更高的输入分辨率、更保守的 mosaic 后期策略。此时模型最终的 mAP 值不是一个确定数而是一个随随机种子波动几个点的区间每轮训练前固定好seed否则没办法比较实验组差异。4. 光伏面板缺陷检测的坐标定位与多帧去重模型已经能在单张图里给出“这张图里第 1200 像素处有一条裂纹”但运维要的是“3 号方阵第 12 串第 5 块板”。无人机巡检的视频流里同一块板会被拍若干次同一个缺陷如果不做多帧去重最终报告里的缺陷数量会变成一张没法看的天文数字。这一章把“像素坐标转换成经纬度坐标”和“多帧去重”两步直接做掉。4.1 无人机 POS 与相机参数是坐标定位的前提每一帧图像拍摄时飞控和云台都会记录一包 POS 数据。常见格式如下时间戳纬度经度相对高度(m)航向yaw(°)云台俯仰(°)2025-03-10 10:22:31.20030.284210120.12172080.5172.3-90.02025-03-10 10:22:31.40030.284215120.12173580.4172.5-90.0这里云台俯仰 -90° 表示镜头垂直向下是光伏巡检航线最常用的角度。垂直下视可以把投影模型简化成一个针孔相机加一次平面旋转计算量很小。要注意的是相对高度不是海平面高度而是飞行器相对起飞点的高度如果电站地面有明显坡度那这个高度本身就有误差定位精度会先受制于 POS 数据质量。4.2 垂直下视模型下把像素坐标换算成经纬度在垂直下视、地面近似水平的前提下像素坐标到经纬度的换算分两步先把像素点从图像中心偏移量换算成机头朝北时的北向/东向距离再按航向角旋转到真实朝向。下列代码实现了这个过程import math EARTH_R 6378137.0 def pixel_to_latlon(u, v, lat0, lon0, alt, yaw, fx, fy, cx, cy): # 针孔模型归一化坐标 dx (u - cx) / fx dy (v - cy) / fy # 机头朝北时像面上方对应北右方对应东 N0 -dy * alt E0 dx * alt # 旋转到真实航向yaw为弧度北偏东为正 cos_y, sin_y math.cos(yaw), math.sin(yaw) N N0 * cos_y - E0 * sin_y E N0 * sin_y E0 * cos_y dlat N / EARTH_R dlon E / (EARTH_R * math.cos(math.radians(lat0))) return lat0 math.degrees(dlat), lon0 math.degrees(dlon) # 示例分辨率4000x3000焦距像素约2400中心(2000,1500) lat, lon pixel_to_latlon(2000, 3000, 30.2842, 120.1217, 80.0, 0.0, 2400.0, 2400.0, 2000.0, 1500.0) print(lat, lon)这段代码里的fx、fy是像素单位焦距不是毫米单位。可以从相机对角视场角和分辨率换算fx (0.5 * 图像宽度) / tan(水平视场角 / 2)。yaw0表示机头朝北图片上方对应北无人机航线如果沿光伏阵列方向飞行yaw 会稳定在某一个固定角度附近。更严谨的工程做法是放弃这套简化公式直接在巡检场地测 3 到 5 个地面控制点用 OpenCV 的findHomography把像素平面映射到地面平面那个单应矩阵还能顺带校正轻微倾斜。本节的公式适合作为无控制点时的兜底方案。4.3 多帧去重与按面板归并无人机以固定间隔拍照同一个缺陷可能出现在连续 3 至 5 帧中。每帧过一个模型会产生大量重复检测所以推理后要接一个轻量级目标跟踪/归并模块。一个简单的按空间距离聚类的实现如下import math class DefectTracker: def __init__(self, dist_thresh2.0): self.dist_thresh dist_thresh self.defects [] def add(self, lat, lon, cls, conf): for d in self.defects: if d[cls] ! cls: continue if self._meter_distance(lat, lon, d[lat], d[lon]) self.dist_thresh: n d[count] d[lat] (d[lat] * n lat) / (n 1) d[lon] (d[lon] * n lon) / (n 1) d[count] n 1 d[conf] max(d[conf], conf) return self.defects.append({lat: lat, lon: lon, cls: cls, conf: conf, count: 1}) def _meter_distance(self, lat1, lon1, lat2, lon2): lat_avg math.radians((lat1 lat2) / 2) dx (lon1 - lon2) * 111320.0 * math.cos(lat_avg) dy (lat1 - lat2) * 110540.0 return math.hypot(dx, dy)dist_thresh2.0表示两个检测框中心点距离在 2 米以内的同类缺陷视为同一个对象。归并后的缺陷坐标是多次观测的均值比单帧定位更稳。最终报告里还可以把经纬度再做一次“最近面板匹配”把光伏电站的面板中心点坐标建一张表缺陷坐标落在哪块板的边界框内就归属到哪块板输出格式直接给运维工单用。5. 边缘部署与光伏面板缺陷检测项目源码的组织技巧前面的训练和定位逻辑都跑通之后落地就只看两件事模型在机载或边缘设备上能否稳定跑起来以及这套代码交到别人手里能不能快速复用。这两件事都会被源码工程结构直接放大或拖垮。5.1 导出 TensorRT engine 时的两个纪律YOLOv8 导出 TensorRT 引擎很简单yolo export modeloutputs/pv_yolov8s/weights/best.pt formatengine halfTrue imgsz1280 device0第一个纪律是imgsz必须和部署时的输入尺寸完全一致。先定推理尺寸再导出 engine不要拿了 engine 随意改输入分辨率改了就要重新导出。第二个纪律是优先用 FP16不要图推理速度一上来就 INT8。光伏缺陷数据的显著特征是缺陷像素占比极低INT8 量化如果校准集没有覆盖到足够的裂纹和热斑量化误差会直接让这些细微特征失真。真要上 INT8就自己从验证集里挑 1000 张带缺陷的图做校准拿 COCO 预训练校准集在光伏场景上不适用。5.2 一套可交接的光伏缺陷检测项目源码目录拿到一个标注“优质项目实战”的源码包第一步不是找训练脚本而是看目录是否把这四件事分开配置、数据、算法、工具。一个能长期维护的项目建议按下面这个结构组织pv_defect_project/ ├── configs/ │ ├── dataset.yaml │ └── export.yaml ├── data/ │ ├── flights/ │ │ ├── 20250310_A1/ │ │ │ ├── pos.csv │ │ │ └── images/ │ │ └── 20250311_A2/ │ ├── annotations/ │ └── dataset_splits/ ├── src/ │ ├── train.py │ ├── infer.py │ ├── localize.py │ └── postprocess.py ├── scripts/ │ ├── split_dataset.py │ ├── check_labels.py │ └── merge_results.py ├── outputs/ └── README.mdflights目录按架次归档每架次的照片和 POS 文件放一起这是后续做坐标定位的前提localize.py负责把像素坐标转经纬度postprocess.py专门放多帧去重、长宽比过滤、温度规则这类业务逻辑。把代码按“标注、训练、推理、定位、报告”五层拆开换数据或换机型时只改配置不用重写业务逻辑。5.3 后处理阶段就能压掉的两种典型误检后处理不是模型之后可有可无的一步而是光伏缺陷检测里最容易被忽略的高杠杆环节。第一个典型误检是光伏板金属边框在特定光线下和隐裂很像。模型对于长宽比大于 3 的细长目标会输出 crack 类别但边框也满足这个几何特征。处理方法是把置信度阈值从 0.25 提到 0.45 后再对 crack 类别的预测框做长宽比过滤若检测框长宽比小于 2.5直接丢弃。这个后处理不要放在 0.25 阈值之前否则会把真正的短裂纹一并滤掉。第二个典型误检在红外热斑上检测器报出 hotspot 后要读取热像图上对应像素的温度值如果该点温度与周围环境的温差小于 3℃就判定为误报不生成缺陷记录。这个规则在夏季强阳光场景下尤其有效能把热斑误报率压到很低。如果模型在你自己数据上仍然有误检可以把每一帧的预测框、类别、置信度、长宽比连同原始图导出成 CSV按误检位置的光照和坐标去分析多数问题都能在数据或后处理里找到答案。本文还有配套的精品资源点击获取