
简介面向能源行业的YOLOv11光伏板表面污渍检测与清洁机器人路径规划PDF文档适合从事光伏电站运维、计算机视觉算法研究及清洁机器人开发的技术读者。文档共30页压缩包内仅1个PDF文件大小1.88MB已生成完整目录支持章节快速跳转与大纲定位。内容围绕光伏板污渍检测与清洁任务展开从能源行业背景、YOLOv11网络架构与损失函数到污渍检测数据集的采集、标注与划分再到模型训练优化、路径规划算法选型贪心算法、A*算法、遗传算法以及检测结果与路径规划的结合实现系统测试、沙漠与沿海光伏电站应用案例和未来展望一并覆盖。目前已有153人浏览学习该文档既可作算法应用参考手册也能为光伏检测与清洁机器人方案落地提供结构化思路。1. 光伏板污渍检测为什么非用YOLOv11不可从巡检成本到清洁调度的现实账光伏电站运维里最磨人的不是组件故障而是面板表面那层说不清道不明的污渍。鸟粪、灰尘、落叶堆积在钢化玻璃表面轻则影响单块组件功率重则形成热斑直接威胁组串寿命。围绕“能源行业应用YOLOv11光伏板表面污渍检测与清洁机器人路径规划”的落地方案核心是用YOLOv11把污渍识别从“人眼巡检”变成算法实时输出再把检测框转成清洁机器人能执行的清扫路线让设备只去扫有污渍的区域而不是按固定周期盲扫整片阵列。这篇笔记适合光伏电站运维、清洁机器人厂商和做智能巡检系统的工程师通篇按环境配置、模型训练、坐标映射和路径规划的顺序讲每条命令都按0基础能复现的标准写。2. 把YOLOv11跑起来之前环境配置、权重下载与数据集准备2.1 Ultralytics环境配置的版本坑与CUDA选型很多新人拿到的是一台已经装好显卡驱动的Windows机器或者一台无GPU的服务器。YOLOv11对CPU也能做推理但训练会很吃力。我一般会建议先确认三件事Python版本、PyTorch版本、CUDA运行时版本。这三者有一个对不上后面全是报错。在写“超详细环境配置”这件事上我习惯用conda把环境隔离干净避免把系统Python搞乱。常见做法是新建一个Python 3.10的虚拟环境再指定PyTorch的CUDA版本安装conda create -n yolo11 python3.10 -y conda activate yolo11 pip install ultralytics8.3.60 torch2.4.1 torchvision0.19.1 --index-url https://download.pytorch.org/whl/cu121ultralytics8.3.60是YOLOv11对应的稳定版本锁版本是给自己留后悔药后面导出推理结果时不会因为小版本升级导致输出格式变化。Python 3.10是为了兼容后续的TensorRT导出链路。cu121表示CUDA 12.1如果显卡驱动只支持到CUDA 11.8就把这个参数换成cu118完全没有GPU的机器直接删掉--index-url参数PyTorch会自动装CPU版训练慢但能跑通流程。装完以后一定要验证一下环境python -c import ultralytics; print(ultralytics.__version__)这一步能打印出版本号环境基本就通了。很多人卡在torchvision和torch版本不匹配ImportError直接报出来还有人torch.cuda.is_available()返回False表面上看是环境问题实际是显卡驱动太老CUDA运行时压根起不来。2.2 YOLOv11权重文件下载与结构选型YOLOv11在Ultralytics里不是一个单独的模型而是n、s、m、l、x一整族按深度和宽度区分的网络结构。对光伏板污渍检测这种单类别目标我一般从yolo11n.pt起步先把整个流程跑通数据量大、精度要求上去了再换yolo11m.pt。这里要区分两个文件yolo11n.yaml只有网络结构没有权重训练等于从头学yolo11n.pt是预训练权重可以基于COCO数据集学到的纹理特征做迁移学习。光伏板污渍和COCO里的常见物体差异不小但浅层卷积提取的边缘、颜色、纹理特征仍然非常有用所以强烈建议用.pt而不是.yaml起步。权重文件下载不需要手动找Ultralytics会在第一次调用时自动缓存到本地。如果公司内网机器不能联网需要先在有网的机器上执行一次YOLO(yolo11n.pt)再把缓存的pt文件拷贝到内网路径下放在项目目录里直接用。from ultralytics import YOLO model YOLO(yolo11n.pt) # 加载预训练权重 results model.train( datapv_stain.yaml, epochs120, imgsz640, batch16, device0, workers4, valTrue, )参数说明epochs120对光伏板这种单类目标来说偏长一般60到80轮就会收敛设120是为了留出余地早停。imgsz640是输入分辨率如果污渍目标很小可以改成960但显存占用明显上升。workers4是数据加载线程数Windows上这个值超过0有时会报Dataloader错误遇到就改成0。valTrue表示每个epoch结束都跑一次验证集方便观察mAP曲线决定要不要提前停。2.3 光伏板污渍数据集的组织与半自动标注光伏板表面污渍没有公开大库基本都是电站现场攒图。我建议按YOLO标准目录结构组织datasets/ pv_stain/ images/ train/ val/ labels/ train/ val/训练集放80%图片验证集放20%。拍摄时尽量覆盖清晨、正午、傍晚三个时段因为阳光角度会让污渍的对比度完全不一样训练数据只覆盖一个时段模型现场大概率翻车。标注格式是YOLO的txt每行一个目标class x_center y_center width height坐标全部归一化到0到1。第一版标注建议用LabelImg手工框标完200到300张后先训一个粗糙模型再用这个模型去预标注剩余几千张图片人工只修正漏框和错框。这个半自动流程能把标注时间压缩到三分之一左右。污渍这类目标的边界本身模糊标注时我会把框稍微往外扩一点让模型多学一点背景纹理比切掉污渍边缘更稳。2.4 推理结果保存save_txt与置信度阈值的取舍训练完的模型最终要给现场用。YOLOv11的推理和保存一条命令搞定from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcedatasets/pv_stain/images/val/, conf0.25, saveTrue, save_txtTrue, projectruns/infer, namepv_stain_test, )saveTrue保存带框的可视化图片方便人眼复查save_txtTrue才是给路径规划用的核心它会把每个检测框的类别和归一化坐标写进txt文件。光伏场景的置信度阈值建议从0.25起步低于0.25会出现大量误检光伏板边框、逆变器阴影都被当成污渍高于0.5会漏掉浅色鸟粪或干涸水痕这些目标跟蓝黑色光伏板背景的纹理对比度实在太低。如果你的验证集mAP50已经到0.9以上可以把conf调高到0.4减少误检带来的无效清扫。3. 把污渍坐标变成清洁指令相机标定与栅格地图的构建3.1 像素坐标到光伏板物理坐标的单应性变换检测模型输出的是图像里的像素坐标清洁机器人要动的是光伏板平面上的物理坐标。两者之间用一个3x3的单应性矩阵H关联。常见安装方式是清洁机器人头部装朝下的广角相机或者场站固定机位俯拍整块组件无论哪种第一步都是标定。标定的本质是找图像平面到板面平面的投影关系。光伏板本身是一个平面所以单应性矩阵完全够用。如果板面有弧度单应性就不成立得改成相机内参加外参的位姿估计工程复杂度高一整个量级。import cv2 import numpy as np # 图像上的四个角点像素坐标 img_pts np.array([[856, 633], [2350, 548], [1330, 1680], [130, 1490]], dtypenp.float32) # 对应的光伏板平面坐标毫米 board_pts np.array([[0, 0], [1200, 0], [0, 800], [1200, 800]], dtypenp.float32) H, status cv2.findHomography(img_pts, board_pts, cv2.RANSAC, 5.0) pixel_point np.array([[[640, 360]]], dtypenp.float32) world_point cv2.perspectiveTransform(pixel_point, H) print(板面坐标(mm):, world_point[0][0])findHomography用RANSAC剔除外点5.0是重投影误差阈值单位是像素。阈值调大能容忍更多误匹配但坐标换算精度下降。标定时至少取6到8组点对做RANSAC估计比4组点更稳。这里有一个我在现场踩过好几次的坑标定板必须放在光伏板平面上而不是地面上。光伏板本身有倾角地面标定的H矩阵最多只能保证机器人走到大致位置误差累计起来会偏出五六厘米。3.2 把光伏板切成栅格分辨率与代价地图路径规划通常不在连续坐标上做而是在栅格地图上做因为栅格能把“能不能走、脏不脏”这些信息直接编码成代价。栅格分辨率直接决定规划效果和计算量5mm一格一块2米乘1米的板就要8万个格子A*搜索明显变慢50mm一格又太粗机器人对清扫宽度的感知不足路径容易压到边缘。我一般用10mm到20mm的栅格具体看机器人最小转弯半径。栅格值0表示可通行1表示障碍边框、支架0.5表示轻度污染0.8表示重度污染。把YOLOv11的检测框映射到栅格时检测框中心点对应的格子被标记为对应污染等级同一格被多个检测框覆盖时取最大值。import numpy as np grid_res 0.02 # 20mm每格 board_width 2000 board_height 1000 grid_w int(board_width / grid_res) grid_h int(board_height / grid_res) costmap np.zeros((grid_h, grid_w), dtypenp.float32) detections [ {cx_mm: 500, cy_mm: 300, w_mm: 180, h_mm: 120, conf: 0.83}, ] for det in detections: cx int(det[cx_mm] / grid_res) cy int(det[cy_mm] / grid_res) w max(1, int(det[w_mm] / grid_res)) h max(1, int(det[h_mm] / grid_res)) x0 max(0, cx - w // 2) y0 max(0, cy - h // 2) cost 0.8 if det[conf] 0.7 else 0.5 costmap[y0:y0h, x0:x0w] np.maximum(costmap[y0:y0h, x0:x0w], cost)np.maximum保证重叠区域取最高污染等级max(0, ...)做边界裁剪防止检测框靠近板边缘时索引越界。这个代价地图就是后续路径规划的“黑匣子”里面既有障碍信息又有脏污程度规划器读它决定怎么走。3.3 覆盖优先还是能耗优先清洁策略的选型逻辑有了代价地图路径规划要回答“怎么扫”。两种典型策略覆盖优先和能耗优先。覆盖优先适合污渍面积占比大、组件已经积灰已久的场景机器人沿弓字形走完全部清扫带保证每寸板面都被刷到。能耗优先适合日常保洁型场景污渍点稀疏机器人只需要做点到点的最短路径规划本质上是一个带避障的旅行商问题。很多团队一上来就做全覆盖路径规划结果日常清洁时机器人绕一大圈电耗翻倍。我的习惯是让业务目标决定策略而不是让算法决定业务。对光伏组件来说通常做法是“日常覆盖加重点二次清扫”先用覆盖路径保证基本清洁度再用局部螺旋路径处理重污渍区。这样既不会漏扫也不会在脏污稀疏时白白浪费电量。4. 清洁机器人的路径规划实现弓字形覆盖、螺旋清扫与避障4.1 弓字形覆盖路径的生成逻辑和参数弓字形路径Boustrophedon是覆盖式清扫最常见的基础路径。核心思路沿一个方向直线扫过去到头后垂直平移一个清扫宽度再反向扫回来所有区域都被覆盖且转弯最少。def generate_boustrophedon(width_m, height_m, sweep_m): paths [] y 0.0 direction 1 while y height_m: if direction 1: paths.append([(0, y), (width_m, y)]) else: paths.append([(width_m, y), (0, y)]) y sweep_m direction * -1 return paths waypoints generate_boustrophedon(2.0, 1.0, 0.45) for row in waypoints: print(row)sweep_m0.45是有效清扫宽度我一般取毛刷物理宽度的80%这样相邻两次清扫之间至少有20%重叠避免漏扫。这个参数是整条路径规划里最值得调的设大了省时间但可能漏扫设小了清洁时间长但覆盖密。对2米乘1米的板45cm有效宽度会生成三行路径机器人沿这三条线走完整块板面全部覆盖。生成的路径点送到机器人的底层运动控制模块按直线段依次执行。弓字形逻辑简单现场调试时不容易出问题这也是很多清洁机器人厂商把它作为默认方案的原因。4.2 污渍密集区的局部螺旋清扫参数怎么定覆盖路径保证的是“都扫到”但重度污渍一次扫不干净。鸟粪堆积或油污这类目标检测置信度通常很高我拿0.7做分界大于0.7的污渍区在覆盖路径走完后切入一个局部螺旋清扫子程序以污渍框中心为圆心生成一条半径略大于污渍框尺寸的螺旋路径。import math def generate_spiral(center_x_m, center_y_m, max_radius_m, pitch_m0.05): points [] theta 0.0 r 0.0 while r max_radius_m: r pitch_m * theta / (2 * math.pi) x center_x_m r * math.cos(theta) y center_y_m r * math.sin(theta) points.append((x, y)) theta 0.05 return points spiral generate_spiral(0.8, 0.5, 0.15)pitch_m0.05是螺旋线的间距每转一圈半径增加5cm这个值必须小于毛刷有效清扫宽度才能保证重叠。max_radius_m0.15是螺旋清扫范围半径重度污渍检测框的宽高通常不会太大0.15m足够覆盖。theta 0.05是角度步长步长越小路径点越密、运动越平滑但点太多会加重底层控制器的计算负担。中度污渍置信度0.4到0.7不需要单独走螺旋覆盖路径通过时把行进速度降下来让毛刷多接触一会儿即可。4.3 动态避障与路径重规划高代价区域怎么融合场内可能有杂草、石头、检修工具这些障碍物路径规划不能只离线生成一条弓字形路径还得在行进中实时避障。常见做法是把A*搜索放在代价地图上如果前方被障碍物挡住就触发局部重规划。动态避障小车路径规划在ROS2生态里用得最多的是Nav2我的做法是把YOLOv11的检测结果作为“动态高代价区域”融合进代价地图。机器人接近重度污渍区时会减速接近真正的障碍物时会绕行而不是把污渍当障碍直接绕开。import numpy as np def merge_costmap(base_grid, stain_grid, obstacle_grid): # 障碍物区域保持1.0不可通行 combined np.maximum(base_grid, obstacle_grid) # 污渍区域叠加为高代价但不会被当成硬障碍 combined np.maximum(combined, stain_grid * 0.8) return combined这段代码的逻辑是分层融合障碍物格的代价是1.0A*直接视为不可通行污渍格的代价是0.8规划器能走但会优先绕行。实际接入Nav2时把这张合并后的地图发布给代价地图层全局规划器就会在避开障碍物的同时把重污渍区视为高代价区域。如果你用的是自己的规划框架思路完全一样只是数据接口不同。4.4 A与混合A什么时候换算法弓字形覆盖路径和螺旋清扫都不需要A参与因为它们本身就是确定性的几何路径。但“从当前位置移动到下一个污渍点”这类点到点任务A是首选。光伏板场景的地图通常不算大A在20mm栅格下完全够用不需要上RRT或混合A这些更复杂的算法。混合A适合的是带阿克曼转向模型的车辆转弯半径大需要让路径满足运动学约束。清洁机器人如果是差速驱动原地转弯很轻松普通A就够如果是类车底盘最好直接用混合A*否则规划出来的路径机器人根本转不过去。选型时先确认底盘模型再选规划器这个顺序不能反。5. 光伏板污渍检测与路径规划的避坑5个高频翻车现场5.1 翻车现场一换台机器后权重文件无法加载现象训练正常完成把best.pt拷到另一台机器上推理时报RuntimeError: Unknown torch version或者权重能加载但输出结果完全不对。原因训练机和推理机的ultralytics或torch版本不一致序列化格式变了。解决训练和推理用同一个版本环境锁进requirements.txt部署时用pip install -r requirements.txt恢复。如果环境实在无法统一在训练机上把权重导出为ONNX格式用ONNX Runtime推理直接绕开PyTorch版本问题model.export(formatonnx, imgsz640, dynamicTrue)dynamicTrue让ONNX支持动态输入尺寸现场换相机分辨率时不用重新导出。5.2 翻车现场二小目标漏检实测检出率比验证集差很多现象验证集mAP50有0.9现场一跑小块鸟粪和远处灰尘大面积漏检。原因imgsz640输入下小污渍块只占几个像素经过连续下采样后特征被丢掉。解决两条路。一是把imgsz改成960或1280小目标保留的像素更多二是做切片推理把大图按网格切成小块每块单独送进模型再把结果拼回原图坐标。YOLOv11的小目标优化方向一般就是这两条路调anchors没用因为模型本身是anchor-free的。5.3 翻车现场三规划的路径穿过光伏板边缘现象机器人的路径从板面边缘穿出去或者直接压过边框支架。原因栅格地图没有做障碍物膨胀机器人尺寸没建模规划器以为贴着障碍物走也没问题。解决按机器人半径做膨胀。常见做法是用scipy.ndimage.binary_dilation把障碍物栅格向外扩一圈from scipy.ndimage import binary_dilation robot_radius_cells int(0.2 / grid_res) inflated binary_dilation(costmap 1, iterationsrobot_radius_cells)0.2是机器人半径单位米grid_res是栅格分辨率。膨胀后的障碍区单独存一层规划器就不会贴着边框走。这个参数设太小会刮擦组件设太大又会让机器人离可清扫区域太远建议按机器人实际外径的110%来取。5.4 翻车现场四同一块污渍上午检得到下午检不到现象模型在早晚表现稳定中午强光下漏检率飙升同一块鸟粪时有时无。原因训练数据集中在少数时间段模型学到的是“特定光照下的纹理”而不是污渍本身。解决训练集补入不同日照角度的图片同时做亮度增强import cv2 def augment_brightness(img): hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hsv[:, :, 2] cv2.add(hsv[:, :, 2], 30) return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)这个增强会让模型见过更宽的亮度范围而不是依赖某个固定光照条件。现场如果中午强光下仍然漏检最直接的办法是给相机加偏振片滤掉玻璃表面的反光污渍纹理一下就清楚了。5.5 翻车现场五坐标误差累计机器人扫偏了位置现象检测框明明标在污渍处机器人走过去却偏了5到10厘米螺旋清扫中心对不准。原因单应性矩阵是用地面上的标定板算的光伏板有仰角标定平面和实际工作平面不重合或者机器人运动时相机高度发生轻微变化。解决把标定点贴在光伏板表面再算H矩阵。最好让机器人在每次开工前花10秒做一次在线标定用固定在板面角落的标定点重新计算H修正机械振动带来的漂移。这个习惯救了我很多次坐标对齐的精度从厘米级直接回到毫米级。6. 进阶把YOLOv11检测结果做成清洁机器人的实时决策闭环6.1 用置信度动态调整清扫动作把YOLOv11输出的置信度直接当作“污渍严重程度”的代理指标按阈值设定三档清洁策略大于0.7的重度污渍执行螺旋清扫0.4到0.7的中度污渍覆盖路径经过时减速慢扫低于0.4的轻微污渍正常速度带过。if det.conf 0.7: follow_spiral(center, radiusdet.w_mm * 0.8) elif det.conf 0.4: set_speed(0.3) else: set_speed(0.8)det.w_mm是检测框映射到物理坐标系后的宽度螺旋清扫半径取宽度的0.8倍保证覆盖范围完全包住污渍扩散区。这样检测结果直接变成执行参数而不是只输出一个“有没有框”的信号。6.2 导出TensorRT引擎把推理时间压进实时区间现场清洁机器人大多用嵌入式GPU比如Jetson系列。PyTorch直接推理的延迟通常不够建议用Ultralytics一键导出TensorRT引擎model.export(formatengine, imgsz640, halfTrue, device0)halfTrue表示FP16精度显存占用和推理延迟都明显下降。导出后再加载.engine文件做推理速度一般能到实时。这里有一件必须注意的事TensorRT引擎和GPU型号强绑定换一块卡就要重新导出不能直接拷贝到别的机器上。整条链路到这里已经打通摄像头采集画面YOLOv11输出检测框单应性矩阵把像素坐标转成板面物理坐标栅格代价地图记录污渍等级弓字形覆盖路径打底螺旋路径处理重度污渍置信度反过来调节清扫强度。我自己的习惯是每走一步都单独写验证脚本检测模型单独看mAP单应性标定单独量坐标误差路径规划单独跑仿真。集成之后出问题八成不是哪个模块的算法错了而是坐标系没对齐或者阈值调得太随意。这个顺序能帮你少走很多弯路希望帮到你。本文还有配套的精品资源点击获取