简介基于Python实现的无人机光伏面板故障检测项目为答辩评审98分的个人毕业设计代码均经过调试测试可稳定运行。资源主要面向计算机、人工智能、自动化等相关专业的学生与从业者适用于期末课程设计、大作业或毕业设计也适合小白的进阶学习。压缩包共409个文件、约106.71MB核心为289个Python源文件同时提供运行所需的dll、pyd、exe动态库与工具以及txt、xml、cfg等配置和说明文档目录结构清晰便于逐模块阅读和复用。项目包含卷积神经网络模型与TensorFlow训练日志完整覆盖数据集准备、模型训练、故障检测流程能够直观理解无人机视角下光伏面板异常识别的实现思路具有较强的工程参考价值。已有59人学习下载基础扎实者还可在源码基础上扩展适配更多检测场景。1. 无人机光伏面板故障检测用 Python 做是个被忽略的落地机会无人机在光伏电站飞一圈热红外镜头扫过一排排光伏板回到地面上Python 脚本自动把影像里的畸变和噪声处理掉再用深度学习模型找到每一块热斑和隐裂最后生成带经纬度标注的故障图——这正是“基于 python 实现无人机的光伏面板故障检测”这套项目要做的事。和常见的行人检测、车牌识别不同光伏面板故障检测的难点不在模型结构而在怎么把无人机视角变成模型能理解的训练数据以及怎么让少量标注样本把召回率稳住。它适合两类人一类是做毕业设计的学生需要可复现、有工程背景、能写进论文的实验过程另一类是光伏电站的运维或算法入门者想用现成方案替代人工巡检。源码和文档说明的作用是让你不用从零搭框架而是把精力花在数据采集、调参和报告上。2. 无人机光伏面板故障检测为什么先选红外与 YOLO从成像特征到模型选型2.1 热斑、隐裂、遮挡在图像上的差异决定了整个方案做光伏面板故障检测第一件事不是装模型而是搞清楚你要检测的故障在哪一种图像里可见。如果选错成像方式后面再多模型技巧都白费。最常见的故障是热斑。光伏板被鸟粪、树叶、灰尘遮挡后整串电池片里被遮挡的那一片会从发电变成耗电温度显著升高在红外热像仪下就是一块亮斑或暗斑。热斑在 RGB 图像里几乎不可见只有在热红外波段才有稳定的对比度所以做无人机巡检优先配置热成像相机。第二个常见故障是隐裂也就是电池片内部的细微裂纹。理想检测手段是电致发光EL成像但 EL 需要给光伏板通电并在暗室拍摄无人机根本做不到。比较现实的折中方案是用可见光拍摄电池片表面的纹理变化比如栅线断掉、电池片颜色发暗、表面出现异常线痕。第三个是遮挡和污染这部分在可见光图像里非常直观边界清晰是深度学习最容易学会的一类故障。也就是说一个真正能落地的无人机检测方案通常做成“红外检测热斑 可见光检测表面缺陷”的双光结构。如果条件有限只做热斑检测也完全成立因为热斑是电站最容易引发火灾的故障也是运维方最关心的指标。在毕业设计里先抱住热斑这一条线把项目做完整远比贪多做得七零八落要好。这里有一个常见的认知误区有人会把红外图像直接转成灰度图再套用普通 RGB 预训练模型。这样不是不行但要注意红外图像的灰度分布和 visible 图像完全不同预训练模型在 ImageNet 上学到的纹理特征并不完全适用。因此训练阶段应当从随机权重开始或者在一个小的预训练权重基础上只训练红外数据避免迁移学习带来的偏差。2.2 深度学习模型选型YOLO 系列是这个场景的性价比之王传统图像处理不是不能做热斑检测。用 Otsu 阈值分割红外图像晴天中午拍的热斑确实能被分离出来但太阳角度一变、相机自动增益一动灰度分布就会漂移阈值马上失效。光伏巡检测试需要面对不同时间、不同辐照度、不同组件表面状态传统方法很难建立一个稳定可靠的产品级方案。深度学习方案里YOLO 系列是实际工程中使用最多、最适合无人机光伏场景的选择。原因有几个第一光伏故障目标通常只占整张无人机图像的很小一部分YOLO 能做到端到端的回归和分类不像两阶段检测器那样先提候选框再细分类对小目标更友好第二YOLO 系列在推理阶段速度极快即便是没 GPU 的笔记本电脑用 YOLOv8s 也能在 Jetson 或低功耗设备上跑出可用帧率第三生态成熟标注格式、预训练权重、部署工具链都很全源码改起来省事。具体到 YOLOv5 和 YOLOv8 的选择我一般推荐 YOLOv8。YOLOv8 是 anchor-free 的检测头少了锚框设计这一层回归目标变得直接在光伏面板这类长宽比比较固定的目标上反而更好调它的命令行接口也简单训练、验证、导出 ONNX 一条命令都能完成。YOLOv5 的优势是资料多很多老博客和开源部署教程都基于它如果你的任务是去修改一个现成仓库YOLOv5 也够用。但从毕业设计写论文的角度讲YOLOv8 能拿出的对比实验和可视化结果更丰富答辩时更好讲。模型规模的底线也要说清楚。光伏故障检测的目标非常小YOLOv8n 这个 nano 级别虽然在 COCO 上速度最快但在面板边缘裂纹这类目标上很容易欠拟合。我实际项目中至少用 YOLOv8s显存允许就上 YOLOv8m。模型大小多出的几毫秒推理时间在无人机巡检场景里根本不敏感因为一遍巡检要处理几千张图真正耗时的是预处理而不是单张推理。2.3 检测系统整体架构一条链路从无人机影像到故障报告源码里的程序结构应当围绕一条完整链路来组织否则很容易变成“只有训练代码的残废项目”。常见的做法是无人机按规划航点飞行自动采集红外与可见光影像落地后把影像按航点编号导出到文件夹Python 端先做预处理比如 jpg 格式转换、镜头畸变校正、图像去条纹、按光伏阵列方向旋转然后把原图切成小块送进 YOLO 模型推理推理结果经过 NMS 和规则过滤后把热斑框叠加到原图上生成可视化报告和 CSV 表格。实时处理在无人机光伏巡检里其实不是硬需求。无人机飞行速度、相机帧率、存储卡写入速度决定了数据量一般一块 2 平方公里的光伏电站飞完一次会产生几千张 640×512 或 5120×3840 的图片。真正的瓶颈是后处理阶段能否在半小时内跑完所以架构上不追求实时流式处理而是先落盘、后批处理这样也能避免飞机飞行途中算力不足的问题。无人机路径规划算法在这个项目里是加分项。如果你用大疆等商业飞控可以直接用地面站软件生成弓字形自动巡检航线确保相邻航线的重叠率达到 70% 以上这样后期做图像拼接或者切片才不会出现漏检。如果自己写航线规划则需要把飞行高度、云台俯仰角、航向重叠率写成一个配置文件让后续的影像解析脚本能拿到每张图对应的 POS 信息。这部分逻辑看似简单但直接影响后面把像素坐标换算成经纬度坐标的精度。3. 把无人机影像做成 YOLO 数据集标注格式转换与四个增强手段3.1 采集规范高度、重叠率、时段对检测效果影响很大很多人在源码跑通后第一反应是拿网上公开数据集来训练但这些数据集里的光伏板是屋顶组件或者地面电站的远景图跟无人机俯拍视角完全不同。光伏面板故障检测是强视角相关的任务俯拍和斜拍下面板边框的形状、热斑的纹理完全不同。因此第一步是自建数据而自建数据的质量由采集参数直接决定。飞行高度是最先要固定的参数。我一般把无人机保持在离光伏阵列平面 30 到 60 米的高度。太低导致单张图覆盖面积小数据量爆炸太高导致热斑在图像上只占几个像素模型学不到有效特征。60 米高度用 640×512 红外相机拍摄时一块 2 米乘 1 米的光伏板在图像里大约只有 40×20 像素此时热斑区域已经小于 32×32对检测器算是小目标。如果继续升高到 100 米目标直接变成 8×8 像素这时候人类肉眼都无法确认热斑只能对大范围组件整体温度做统计不适合做端到端的框检测。重叠率也是个不能省的数字。相邻航片之间的航向重叠率和旁向重叠率都要保持在 70% 以上这样既方便后期用 OpenCV 拼接也能确保同一个故障至少出现在两张图里。不然热斑恰好落在两张影像的边缘一半在上一张、一半在下一张模型对这类边缘目标天然不敏感。飞行时段最好选在正午前后两个小时内此时太阳辐照度高且相对稳定正常电池片温度高被遮挡的电池片发热明显红外温差最大。阴天或者清晨时段热斑温差只有两三度红外图上灰度对比非常弱模型基本学不出来。采集完看一遍原始数据我建议先用软件把红外视频抽帧保存成 jpg同时检查有没有拖影。无人机快速转向时云台跟不上画面会模糊这类图片直接删掉不要混进训练集否则模型会学到“模糊区域 故障”这种错误关联。采集参数推荐值影响飞行高度30–60 米决定 GSD 与目标像素尺寸航向重叠率≥70%影响故障是否被完整拍到旁向重叠率≥70%影响拼接和切片时的连续拍摄时段10:00–14:00保证热斑与正常区温差最大红外图像分辨率640×512 以上太低则热斑面积不足3.2 标注与格式转换把 VOC XML 转成 YOLO txt 的脚本自建数据最常见的标注工具是 LabelImg 或 X-AnyLabeling。LabelImg 导出的是 Pascal VOC 格式的 XML 文件而 YOLO 训练需要的是每张图对应一个 txt 文件里面每行是“类别 中心点x 中心点y 宽 高”坐标都是归一化到 0 到 1 的浮点数。这里给一个可复用的转换脚本用于把 XML 批量转成 YOLO 格式的 txt。import xml.etree.ElementTree as ET import os def voc_xml_to_yolo_txt(xml_path, output_dir, class_mapNone): 将 LabelImg 导出的 VOC XML 转换为 YOLO 格式 txt。 xml_path : 单个 XML 文件路径 output_dir : 输出 txt 的目录 class_map : 类别映射字典例如 {hotspot: 0, crack: 1, dirt: 2} if class_map is None: class_map {hotspot: 0} tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_map: continue box obj.find(bndbox) x_min float(box.find(xmin).text) y_min float(box.find(ymin).text) x_max float(box.find(xmax).text) y_max float(box.find(ymax).text) # 计算中心点和归一化宽高 x_center (x_min x_max) / 2.0 / img_w y_center (y_min y_max) / 2.0 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h # 防止标注框边界溢出图像 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(w, 1.0 - x_center) h min(h, 1.0 - y_center) lines.append(f{class_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path os.path.join(output_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: # 用法示例遍历某个目录下所有 XML逐个转换 xml_dir label_xml txt_dir yolo_labels os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): voc_xml_to_yolo_txt(os.path.join(xml_dir, xml_file), txt_dir)脚本逻辑不复杂但两个参数值得说明。一是root.iter(object)比root.findall(object)更健壮因为 XML 里 object 可能嵌套在其它节点里用 iter 可以遍历所有层级避免漏标。二是坐标归一化时务必拿 XML 里的原始图片宽高不要拿程序里读取的图像宽高两者在 JPEG 旋转 EXIF 信息存在时可能不一致。转换完成后必须抽查几张图把 txt 里的坐标还原成矩形画到原图上确认没有错位。这个验证步骤很多人省掉结果训练时 loss 降不下去最后发现是 300 张标注文件里面 50 张的宽高搞反了。这类低级错误花费的时间远超写脚本的时间。3.3 数据增强哪些操作有效哪些操作会把红外图搞坏光伏面板是高度规则的几何对象增强操作不能盲目照搬 COCO 那套。Mosaic 增强会把四张不同场景的光伏板拼在一起模型容易学到拼接边界上的错误纹理MixUp 叠加后热斑和正常区域灰度混在一起标注语义被稀释。因此我更推荐用几何变换和亮度对比度变换这两种对红外图相对安全。import albumentations as A def build_transforms(modetrain, img_size640): if mode train: return A.Compose([ A.RandomResizedCrop( heightimg_size, widthimg_size, scale(0.5, 1.0), ratio(0.8, 1.2), p0.8, ), A.HorizontalFlip(p0.5), A.VerticalFlip(p0.3), A.RandomBrightnessContrast( brightness_limit0.15, contrast_limit0.15, p0.7, ), A.Rotate(limit15, p0.3), ]) else: return A.Compose([ A.Resize(heightimg_size, widthimg_size), ])参数上要注意RandomResizedCrop的scale不要低于 0.5。光伏板上的热斑本来就只有几十像素如果随机裁剪到 0.3 倍再放大回来目标规模严重失真等于人为制造了一个“大目标质检器”去检测小目标训练和推理尺度不一致。Rotate的旋转角度限制在正负 15 度以内也很有必要因为无人机巡检时习惯让光伏板边缘与图像坐标轴大致平行旋转角太大会让边框变成斜线模型被迫去学各种倾角数据效率反而变差。亮度对比度增强对红外图是双刃剑。热斑的本质是局部灰度异常适度调整亮度和对比度可以模拟不同季节、不同太阳高度角下的红外灰度变化增强模型对灰度偏移的鲁棒性。但幅度过大会把正常电池板之间由于安装角度造成的温度差异也放大产生大量伪热斑。实践下来brightness_limit0.15是一个比较安全的经验值再大就要用真实拍摄不同时段的数据来补而不是靠增强硬凑。4. 训练 YOLOv8 检测光伏故障一组能收敛的参数与三种调优手段4.1 从 YOLOv8 最小训练命令到超参表环境准备这里不多展开但需要提醒的是ultralytics 包对 Python 版本有要求建议先看官方文档把 python 版本和依赖装齐再用下面命令验证环境yolo detect predict modelyolov8s.pt sourcetest.jpg能正常出框说明安装没有问题。接下来开始训练下面这组参数是我在光伏缺陷数据集上测试过、能稳定收敛的起点yolo detect train \ datadataset.yaml \ modelyolov8s.pt \ epochs150 \ batch8 \ imgsz640 \ lr00.005 \ lrf0.01 \ patience30 \ cacheTruedataset.yaml需要把路径和类别写清楚常见的错误是train路径写成了绝对路径换机器跑就得改一遍建议写成相对路径并和数据集放在同一个工程目录下。modelyolov8s.pt表示从 COCO 预训练权重继续训练训练会快很多如果你想验证红外图像迁移学习的影响可以改成modelyolov8s.yaml从零训练做对比实验。参数推荐值作用解读epochs150光伏数据集通常只有几千张训练 150 轮足够拟合太多容易过拟合batch8感知显存大小可调显存不够时优先降 batch而不是降 imgszimgsz640640 是速度与精度的平衡点裂缝检测可试 1280但需要更高显存lr00.005比默认 0.01 更保守小数据集防止训练初期发散lrf0.01让学习率在最后阶段衰减到初始值的 1/100收敛更稳patience30验证集指标连续 30 轮不涨就提前停止省时间cacheTrue把图片提前缓存到内存减少磁盘 IO数据集不大时强烈建议开启如果你只有 6G 显存batch 改成 4imgsz 可以保持 640。cacheTrue在内存小于 8G 的机器上不要开否则会触发内存交换训练速度反而更慢。训练结束后到runs/detect/train目录下看results.png和weights/best.ptbest.pt是按验证集指标选出来的最优权重后续推理统一用它。4.2 小目标检测专项优化切片训练与锚框调整在无人机光伏巡检里目标小是固有属性。一个 640×640 的红外图像块里热斑有时只占 20×15 像素隐裂线更是只有几个像素宽。此时直接套用默认训练策略模型会把整个光伏板判为背景漏检率很高。我一般先做一个标注框面积统计分析确认小目标占比再决定要不要切片。import os def analyze_boxes(label_dir, img_size640): total 0 small_area 0 tiny_area 0 for txt in os.listdir(label_dir): if not txt.endswith(.txt): continue with open(os.path.join(label_dir, txt), r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 5: continue _, _, _, w, h parts w float(w) * img_size h float(h) * img_size area w * h total 1 if area 32 * 32: small_area 1 if area 8 * 8: tiny_area 1 print(ftotal boxes: {total}) print(fsmall boxes (32x32): {small_area / total:.3f}) print(ftiny boxes (8x8): {tiny_area / total:.3f})如果 small_area 占比超过 40%最直接的办法是把图切成 320×320 的小块再做训练和推理。这样原本 20×15 像素的热斑在切片后变成 40×30 像素目标相对尺寸放大了两倍模型学起来轻松得多。切片时注意两个细节一是相邻切片要有 10% 的重叠防止目标正好卡在切片边界二是标注框在切片后的坐标要重新计算保留那些中心落在切片内的框删除已经被边缘截断大半的框。另一种可选方案是提升训练分辨率到 1280 而不是 640但这对显存的要求高不少而且训练和推理都变慢。在毕业设计的算力限制下切片训练通常是性价比最高的选择既不需要换显卡也不需要改模型结构。4.3 评估指标不要只盯 mAP要看被漏掉的小目标训练完成后用下面命令评估验证集yolo detect val \ modelruns/detect/train/weights/best.pt \ datadataset.yaml \ imgsz640输出会显示每个类别的 mAP50、mAP50-95、precision、recall。我一般重点关注 recall 而不是 mAP因为在光伏运维场景里漏掉一个热斑可能导致整串组件烧毁漏检的代价远比误检大。如果召回率只有 0.6说明一半多的热斑根本没被模型看见这时再调置信度阈值也无济于事应该回到数据层面做切片或补充样本。看 PR 曲线时密切关注曲线左侧的下降段。热斑类别如果 precision 高、recall 低说明模型只在非常有把握的时候才输出这类高判别的行为在巡检中并不好用。相反如果 recall 高、precision 低误检多一些反而更好办因为可以加后处理规则把边框、支架等误检过滤掉。在答辩汇报里这样一个“先拉召回、再压误检”的调优故事比直接交一个 mAP 数字更有说服力。5. 无人机采集与模型部署避坑记录五个让人想删库重来的瞬间5.1 红外与可见光没对齐标注数据白做了现象双光相机拍的同一块光伏板在红外图和可见光图上位置相差几十个像素。你按可见光标注转成红外图后框就落在错误区域模型学到的特征完全是噪声。原因双光相机物理位置不同导致视差两个镜头的焦距和畸变也不一样而且无人机云台在飞行中会轻微抖动两路画面的平移关系不是固定常数。解决在标注前先做一次配准。常见做法是在每段视频开始前拍一张棋盘格或光伏阵列支架的角点图用四点单应性变换把红外图映射到可见光坐标系import cv2 import numpy as np H, _ cv2.findHomography(src_pts, dst_pts, methodcv2.RANSAC) aligned_ir cv2.warpPerspective(ir_img, H, (vis_img.shape[1], vis_img.shape[0]))findHomography的src_pts和dst_pts是你在两张图上手工选取的至少 4 组对应点优先选光伏板四角、支架关节等角点明显的特征。配准后一定要把两张图叠在一起半透明检查肉眼看到边缘重合后再开始标注。血泪经验不要相信相机厂商给出的“出厂已对齐”温度变化和云台震动都会让出厂标定失效。5.2 光伏板边框被识别成裂缝误报率飙到 30%现象模型训练完验证集 mAP 不错但拿到实拍大图上跑铝合金边框和光伏板之间的接缝被大量标成 crack误报率直接飙到 30%。原因边框在可见光图像中是高亮长直线和隐裂在边缘、灰度、长宽比上高度相似训练阶段负样本太少模型没机会见到“边框”这样的干扰项。解决三步同时做。第一标注时不要把边框标成故障类别同时在边框区域附近多截取一些背景块加入训练集第二训练时用负样本挖掘把误报高的图片单独挑出来继续训练几个 epoch第三后处理阶段用 OpenCV 的直线检测过滤掉与边框重合的检测框def filter_by_edge_overlap(boxes, edge_map, overlap_thresh0.3): keep [] for box in boxes: x1, y1, x2, y2 box region edge_map[y1:y2, x1:x2] if region.mean() overlap_thresh: continue keep.append(box) return keepedge_map可以通过 Canny 边缘检测得到然后算检测框内部边缘像素的平均强度。光伏板边框区域边缘密度远高于正常的电池片表面这个简单规则就能过滤掉大部分边框误检而且不影响真实裂缝检测。5.3 飞行高度一变检测效果断崖式下降现象在 45 米高度拍的数据上训练模型mAP 达到 0.82。换到 80 米高度重新飞一遍召回率直接掉到 0.3很多热斑漏检。原因目标尺度变了。45 米高度热斑在图像里约 30×30 像素80 米高度只剩 15×15 像素模型学习到的特征尺度完全不匹配。深度学习模型对尺度变化比人类敏感得多尤其是热斑这种纹理信息很少的目标。解决要么固定飞行高度要么在训练时打开多尺度训练。ultralytics 默认有 scale 增强但不一定覆盖到无人机高度变化这么大的尺度差。最稳妥的办法是把训练图片按 0.5 到 1.5 倍缩放后分别训练再把测试时飞行高度作为配置文件里的固定参数不允许操作人员随意改。如果部署时换了大疆等不同机型的飞机镜头焦距不同GSD 也会变需要重新采集少量数据做微调不要指望一个模型通吃所有平台。5.4 训练 Loss 不降标注框太小或样本不均衡现象训练开始 loss 在 0.6 左右跑了 30 个 epoch 基本不变验证集 precision 和 recall 都是 0。原因检查标注后发现问题出在标注框过小。比如有些隐裂标注只有 3×20 像素经过 YOLO 的 8 倍下采样后特征图上的响应只有不到 3 个像素模型根本无法学习另外hotspot 类别只占全部标注框的 2%模型倾向于把所有区域都预测为背景。解决先用标注分析脚本把面积小于 8×8 像素的框找出来。对于这些目标要么把图像切片放大后再标注要么直接剔除。类别不平衡可以给少数类设置更高的 loss 权重ultralytics 中可以通过在dataset.yaml里配置不同类别的权重但更有效的方法是采集更多缺陷样本做过采样。如果样本量实在不足用 5.1 的数据增强对少数类单独做几何变换扩充到 3 倍以上。5.5 文档说明是毕设的隐藏加分项记得把实验表写进去现象答辩老师问“超参数怎么来的你试过哪几种组合为什么最终选这组”很多同学答不上来只说“参考开源默认值”。原因训练过程全在命令行里跑没有记录每次实验的配置和结果。代码再漂亮讲不出调参过程答辩一样吃亏。解决从第一次训练开始就建一个实验记录表至少包含模型版本、imgsz、epochs、batch、是否切片、mAP50、recall、训练时间、备注。每跑完一组实验花三分钟填一行最后写文档时直接把这些数据转成论文里的对比表。这套“源码 文档说明”的交付物在毕设评审里往往比模型本身更能撑场面因为老师能清楚看到你的实验逻辑而不是只能看到一段能跑的代码。6. 让检测结果落到地图上SAHI 切片推理与 POS 坐标定位的落地技巧6.1 用 SAHI 对大图做切片推理训练阶段的切片优化解决了小目标问题但推理阶段如果直接把大图缩放到 640热斑仍然会被压缩丢失。SAHISlicing Assisted Hyper Inference是专门解决这类大图小目标推理的工具它会把原图切成多个重叠切片分别推理然后再合并结果。YOLOv8 模型接 SAHI 的用法如下from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathruns/detect/train/weights/best.pt, confidence_threshold0.3, image_size1280, ) result get_sliced_prediction( thermal_full.jpg, model, slice_height320, slice_width320, overlap_height_ratio0.2, overlap_width_ratio0.2, postprocess_class_agnosticFalse, )slice_height和slice_width决定了切片大小320 在大多数情况下够用overlap_height_ratio0.2让相邻切片有 20% 重叠避免缺陷被切断。postprocess_class_agnosticFalse表示不同类别分别做 NMS防止热斑和裂缝两类目标互相压制。用 SAHI 处理 5120×3840 的大图时效果通常比直接整图缩放提升明显代价是推理时间变长但巡检场景完全能接受。6.2 结合 POS 数据输出故障经纬度检测出故障框只是第一步运维人员真正关心的是故障在哪一排光伏板、哪个经纬度。无人机 POS 文件里记录了每张影像的经纬度、高度、飞行器偏航角、云台俯仰角。在相机近似垂直向下拍摄的前提下可以用像素坐标换算地理坐标import math def pixel_to_geo(cx_px, cy_px, img_w, img_h, pos, gsd_m_per_px, yaw_deg): dx (cx_px - img_w / 2) * gsd_m_per_px dy (img_h / 2 - cy_px) * gsd_m_per_px yaw math.radians(yaw_deg) east dx * math.cos(yaw) - dy * math.sin(yaw) north dx * math.sin(yaw) dy * math.cos(yaw) lat pos[lat] north / 111320.0 lon pos[lon] east / (111320.0 * math.cos(math.radians(pos[lat]))) return lat, lon这个公式把像素坐标按 GSD 等比缩放到地面距离再按偏航角旋转到地理坐标系。它是垂直拍摄假设下的近似换算飞控云台俯仰角不为 0 时会引入误差误差大约在 2 到 5 米范围但对于标定“哪一排组件出了问题”已经足够。如果毕设时间充足可以用相机内参做更严格的共线方程投影但最终呈现出来的效果差别不大优化性价比不高。6.3 用温度差验证检测结果模型输出的热斑框是否真正对应故障可以用图像内温度场做二次校验。热斑本质是温度异常红外图在未标定温度时也能反映相对灰度差异。对每个检测框计算框内平均灰度与整张图背景平均灰度的差异如果差异低于一个阈值说明这个框大概率是误检可以过滤掉。这个规则在白天强辐照时尤其可靠因为正常电池片之间温差通常很小。经过这一道校验误检率能再降一截。我现在做这类项目习惯第一周不写训练代码先把航线设计、数据采集规范、标注格式、实验记录表四样东西定下来。数据、实验、文档各留出足够时间后面返工的几率会小很多。希望这套从数据到部署的路线对你有帮助。本文还有配套的精品资源点击获取