
拿到一批光伏电站EL检测图像的第一天我对着屏幕上密密麻麻的电池片格子愣了很久。每一块组件由几十到上百片电池片串联而成EL图上隐约的黑色条带、点状暗斑有的肉眼一扫就能发现有的要放大到200%再调节对比度才能勉强看到。我当时最大的想法是如果靠人工这样一张张看一个百MW电站动辄上万块组件的巡检数据看下来眼睛不废才怪。所以后来做这个项目时我直接锁定了深度学习目标检测路线具体用了大家常说的YOLO26也就是YOLOv6系列神经网络把离线大批量EL/红外图像里的隐裂、断栅、热斑等缺陷用目标框标出来配合训练好的模型权重、标注过的数据集以及一套用PyQt写的GUI交互界面让不写代码的运维人员也能打开界面、点一下按钮、导出检测报表。这篇文章就把我完整走通这套流程的经验复盘一遍包括数据怎么整理、模型怎么调、GUI怎么做、哪些坑绝对不能踩。内容主要面向正在做光伏组件质检、电站巡检相关技术落地的人以及想拿工业级目标检测练手的开发者。无论你手里是EL图像、红外热像图还是可见光下的外观缺陷图这套思路都可以平移过去用。1. 项目背景光伏板缺陷检测到底在检测什么1.1 从人工看图到算法识别痛点在哪光伏板的缺陷检测并不是一个“有没有问题”的二分类那么简单它分布在三个典型场景里组件出厂质检生产线上每一块组件都要做EL检测通过给组件通正向电流让电池片自身发光再拍摄高分辨率图像从亮暗差异中找出隐裂、断栅、碎片等问题。一个车间一天产出几百上千块组件每块组件对应一张或多张EL图。电站现场巡检运营中的电站更依赖红外热像仪因为带电状态下缺陷部位会发热热斑、接线盒故障、二极管失效都会在红外图上形成明确的高温区域。无人机巡检、人工手持设备巡检都会积累大量照片。实验室失效分析组件拆解、售后质量争议、质保判定时需要精准识别缺陷形态和分布这个场景对定位精度要求更高。人工看图的痛点我实际统计过一块组件的EL图经验丰富的质检员最快也要40秒才能下结论遇到低对比度的细微隐裂看两分钟都不奇怪。而且标准一致性很难保证同一个隐裂上午判定是缺陷下午可能就判成可疑。电站巡检的红外视频抽帧更夸张一整个扫描任务可能产生几千上万张图靠人眼逐张排查效率和可靠性都不行。这正好是目标检测模型的用武之地——它把“缺陷在哪、是什么缺陷、置信度多高”一次性输出人工只需要复核模型标记出来的少量疑似区域就行。1.2 光伏缺陷有哪几类为什么它们很难检测我把项目中实际用到的缺陷类别整理了一下这几种在业内最常见隐裂电池片内部硅晶体产生的微裂纹EL图上表现为沿晶界走向的暗色条带细的时候跟头发丝差不多和背景亮度差非常小。断栅/碎片主栅线或细栅线断开或者电池片局部碎裂EL图上表现为发黑的细线中断或块状暗区容易与隐裂混淆。热斑红外图像中的局部高温区域常见于电池片失效、旁路二极管异常、局部遮挡后长期运行的位置。温度场边界是渐变的没有锐利的边缘。接线盒/二极管故障通常表现为整串电池片亮度偏低在EL图上是连续几列电池片明显发暗大面积分布而不是小区域。黑斑、污染、蜗牛纹等外观异常这些更多出现在可见光图像中属于另一类缺陷但在工程上常与电学缺陷一起纳入巡检体系。为什么这些缺陷很难检测三个原因第一是小目标。一块组件照片里电池片本身已经不大隐裂可能只占图像宽度的百分之几甚至更小用640分辨率输入时隐裂区域往往只有十几个像素非常容易漏检。第二是低对比度。EL图像整体偏暗缺陷和正常区域的灰度差别常常只有几个灰度级肉眼都看不清楚模型如果不做对比度处理很难学到有效特征。第三是背景干扰。电池片上的栅线、焊带、旁栅反射以及四周的封装边框都会形成类似缺陷的纹理导致误检率居高不下。所以在做这个项目时我把任务定义成“多类别目标检测”而不是简单搞一个“有缺陷/无缺陷”分类器——因为后续质检流程需要知道具体是什么问题并且要快速定位到组件上的哪个位置。2. 技术选型为什么最终落到YOLOv6目标检测方案2.1 目标检测方案与其他CV路线的横向对比做这个项目之前我不是没想过用其他方案。这里把几种路线的对比摆出来能帮你少走很多弯路方案能解决什么问题实际效果阈值分割、形态学处理找出灰度异常区域光伏EL图灰度受电流、曝光影响很大根本找不到固定阈值噪声严重边缘毛刺多图像分类好/坏判断组件有没有缺陷能定性但给不出位置产线没法定位维修意义不大目标检测YOLO系列定位缺陷位置识别缺陷类别精度和速度都在可用范围工程部署最成熟实例分割输出缺陷像素级轮廓精度最高但标注成本极高一个隐裂的像素级轮廓能标到怀疑人生推理资源也翻倍最终选择目标检测核心原因是“性价比”。产线质检和电站巡检需要的不是像素级分割而是一个带位置、带类别的框人工复核和系统打标都方便。如果要精确统计缺陷面积完全可以在目标框基础上做二次的局部分割没必要一开始就上大而全的模型。2.2 YOLOv6的核心优势不是单纯快而是部署链路完整标题里写的YOLO26其实就是YOLOv6系列一些复现工程和资料里习惯把第六代直接简写成YOLO26。我在选型时对比过YOLOv5、YOLOv6、YOLOv7和YOLOv8最后实操下来用YOLOv6最顺手原因是这几个方面Anchor-Free设计缺陷目标的长宽比很不规律隐裂可以是一条细长的斜线热斑又是接近圆形的块状。Anchor-Free省去了手动调anchor尺寸的过程对多样化的缺陷目标更友好。解耦检测头Decoupled Head分类和回归分支分离收敛速度和精度都更好。实际训练时我能明显感觉到val mAP的上升曲线比某些耦合头的模型更稳定。部署工具链完整从训练到导出ONNX再到TensorRT量化整个链路都有配套工具。工业落地场景里最终大概率要跑到Jetson或工控机上这一步顺畅与否直接决定项目能不能交付。性能均衡YOLOv6在小模型和大模型之间有多个档位可选我用的是中等体积档位在单张消费级显卡上就能完成训练推理也能达到实时水平。另外这类模型对预训练权重的依赖度不低。建议直接用公开的COCO预训练权重做迁移学习从零训练一个几千万参数的目标检测网络没有足够的算力和数据基本不现实。2.3 小目标缺陷对输入分辨率与模型结构的隐性要求光伏缺陷检测和通用目标检测最不一样的地方就是对小目标特别敏感。我实际测下来同一个模型输入尺寸640和输入尺寸1280小隐裂的召回率差别非常大。显然输入分辨率越高模型能看到的细节越多。这里有个简单的估算分辨率从640提升到1280相当于边长放大2倍像素数量放大4倍理论上计算量也大约变成原来的4倍。所以不能无脑拉高分辨率要结合显存和推理速度来权衡。实际操作中的经验是显存足够12GB以上用1280输入训练推理时也用1280检测效果最稳。显存中等8GB左右用960输入配合光照增强和对比度增强也能保证基本的小目标召回率。图像本身是2000×2000以上的大图不建议全图直接缩放喂给模型会丢失太多细节更推荐切片推理后面详聊。模型结构上如果用的工程支持P6输出头大分辨率特征层对小目标检测也会有帮助我在项目中开过P6确实减少了一些微小隐裂的漏检。代价是模型体积和推理耗时增加属于“用算力换指标”的选项。3. 数据集与标注决定检测效果上限的“隐形战场”3.1 原始图像采集EL与红外图各自的特点很多新手拿到开源数据集就开始训练但工业项目里99%的数据要自己攒。我先说说两种主要图像模态的特点。EL图电致发光图拍摄时给光伏板通正向电流电池片正常区域发光呈亮色缺陷区域电流通道受阻呈暗色。EL图通常是8位或16位灰度图整体背景暗、电池片亮缺陷呈现为暗线、暗斑。分辨率常见1024×1024到2048×2048甚至更高。拍摄时电流大小、曝光时间直接影响明暗分布不同厂家的组件、不同批次的工艺也会导致图像风格差异。红外热像图缺陷部位如热斑、失效的接线盒温度异常在热像图上呈现为高亮/高色温区域。红外图可能是伪彩色图也可能是灰度图。不同温标设置下同一缺陷的颜色映射可能完全不同所以对红外图做灰度归一化时必须特别注意。采集时的核心原则是保持统一。同一个项目里的训练数据和最终部署数据最好来自同一批设备、同一套拍摄参数设置。模型对图像风格非常敏感A电站的图像风格训练出来的模型直接去测B电站图像效果往往会打折扣。3.2 标注规范与数据增强策略标注决定模型上限训练只是逼近这个上限。我踩过的最大一个坑就是标注不统一。这里给出我最终沉淀下来的规范类别不要分太细。初期数据量少时建议只设4类左右比如crack隐裂、fragment断栅/碎片、hotspot热斑、other其他异常。等数据积累多了再把“断栅”和“碎片”拆开否则两个近似的类别会让模型无所适从。标注框要紧贴缺陷但留2到4像素余量。这个余量给网络一些上下文信息反而有助于收敛。同一个连续缺陷区域不要拆成多个框。如果裂纹呈簇状分布间距又很近就合并成一个目标框。否则模型会困惑为什么同一个东西一会儿是一个框一会儿是四个框。千万不要把正常焊带、栅线、边框阴影标成缺陷。这是误检率高的头号原因。判断标准是拿不准的宁可不标标为正样本的必须是毫无争议的缺陷区域。数据增强方面我针对光伏图像做了这些调整小幅旋转±15度以内、水平翻转增加目标姿态多样性。亮度抖动、对比度调整模拟不同曝光参数下的EL图差异。轻微高斯噪声、中值模糊增强对传感器噪声的鲁棒性。Mosaic增强可以用但对小目标密集场景要降低概率因为Mosaic会进一步缩小每个目标在画面中的尺度反而让本就微小的隐裂更难学。另外有一个容易被忽略的点加入负样本。在训练集里放一批完全正常的组件图像让模型见过足够多的正常背景能显著降低误检率。我当时在数据里混了大约20%的负样本效果立竿见影。3.3 训练集、验证集划分与数据泄漏的坑数据划分看起来简单但工业数据集里有个典型陷阱数据泄漏。同一块光伏板的多个子图像或者同一缺陷在不同曝光时间下拍摄的多张照片如果一部分放在训练集、另一部分放在验证集验证集指标会虚高因为模型相当于已经“见过”这块板的特征了。等部署到新电站时真实性能会断崖式下跌。正确做法是按“组件ID”或“采集批次”分组。比如有100块组件的图像按组件ID随机抽80块作为训练集20块作为验证集这样验证集里的缺陷都是模型完全没见过的组件特征更能反映真实场景。数据集目录结构我建议这样组织EL_Dataset/ images/ train/ val/ labels/ train/ val/labels里每个txt文件对应一张图片每行格式是class_id x_center y_center width height所有坐标值归一化到0到1之间。这个格式是YOLO生态通用的后续训练、推理、评估工具都认它。4. 训练全流程实录从配置文件到权重导出的一步步操作4.1 环境配置与数据集准备训练环境我建议直接用PyTorch CUDA这套组合版本对照选稳定的搭配。比如Python 3.8/3.10、PyTorch 1.13或2.0、CUDA 11.7/11.8。装好之后把开源社区的YOLOv6训练工程拉下来主要用到这几个模块train.py、tools/以及configs目录下的模型配置文件。数据准备就是把上一节的EL_Dataset目录结构放好确保images和labels里文件名一一对应。在开始训练之前一定要做一次数据校验把找不到标注文件或标注越界的图片过滤掉训练中途再发现数据问题会非常浪费时间。4.2 训练配置与关键超参数解析训练靠两个核心配置文件数据配置和模型配置。数据配置data.yaml长这样train: /path/to/EL_Dataset/images/train val: /path/to/EL_Dataset/images/val nc: 4 names: [crack, fragment, hotspot, other]模型配置文件里需要关注的超参数有几个epochs我建议300起步。光伏缺陷特征比较细微往往要训到150个epoch之后val mAP才会明显上升。但也不是越多越好要配合早停策略。img-size训练输入分辨率前面说过显存够就用1280不够就960或640。batch-size由显存决定。1280分辨率下单卡8GB显存只能跑4到816GB显存可以到16。batch太小会导致训练不稳定batch太大又容易过早过拟合需要自己权衡。learning rate初始学习率0.003左右配合余弦退火策略。学习率过高模型会发散loss直接冲到NaN过低则收敛太慢。weight decay一般取0.0005如果发现过拟合趋势可以调大到0.001。实际训练命令类似这样python train.py \ --data data.yaml \ --weights yolov6s.pt \ --epochs 300 \ --batch-size 16 \ --img-size 1280 \ --device 0如果是从COCO预训练权重迁移默认的全量微调方案在数据量够用超过2000张标注图的情况下表现最好。数据量较少时我会冻结backbone前几层只训练深层参数训练更稳定。4.3 训练过程监控与Loss曲线怎么看训练日志里会有好几条lossbox_loss回归损失、cls_loss分类损失、obj_loss目标置信损失等。新手最容易犯的错误是看到训练loss很高就慌了实际上前期loss高太正常了尤其从COCO权重迁移到光伏领域时前20个epoch的loss下降速度不明显很正常。我习惯重点盯两个指标val/box_loss验证集上的回归损失如果这个值持续下降说明定位能力在变好。val/mAP_50在IoU阈值0.5下的平均精度这是工业项目最常看的指标稳定在预期值以上就说明模型可用。什么时候停我的经验是val指标连续30到40个epoch不再提升就可以停了。继续硬训最后得到的权重往往是过拟合状态。权重文件选best_ckpt.pt而不是last_ckpt.pt用验证集选出来的权重在未知数据上通常更可靠。训练过程中有几个让我印象很深的问题mAP一开始是0持续了几十个epoch——这往往不是模型坏了而是正负样本分配不均衡或者数据集中某个类别数量太少网络在一开始的策略是“全预测为背景”最安全等它慢慢学到少数类的特征后指标才会爬升。这时候耐心比调参更重要。4.4 模型评估、导出与部署格式选择训练完成后不能用“感觉效果不错”来交付要用评估指标说话。在测试集上跑一遍重点看mAP_50、mAP_50:95、precision、recall这几项。我这边最终得到的参考数据是在EL测试集上mAP_50约0.87到0.90左右隐裂这类小目标的recall会比热斑低一些单帧推理耗时在消费级显卡上约15到30毫秒视输入分辨率而定。导出时PyTorch的.pt权重适合继续训练和调试直接部署推理时我一般会导出ONNXpython deploy/ONNX/export_onnx.py --weights best_ckpt.pt --img-size 1280如果需要极致推理速度再把ONNX转成TensorRT engine用FP16精度跑速度还能再快一截。工业现场如果对延迟要求没那么苛刻ONNX在常规工控机上已经能满足需求。这里必须强调一句部署时的预处理必须和训练时完全一致。缩放尺寸、归一化方式、letterbox填边策略任何一步不一致模型精度都会肉眼可见地下降。5. GUI交互界面把模型能力交到一线人员手上5.1 界面功能规划一线用户需要什么功能模型训好了但对产线质检员和运维工程师来说命令行是行不通的。他们需要的是一个打开就能用的窗口。我设计的GUI功能清单如下模型加载启动时默认加载上一次使用的权重文件也支持手动选择新权重。单张图片检测打开一张EL图/红外图显示原图和检测结果图。文件夹批量检测选择整个目录自动遍历所有图片并逐张检测结果集中展示。检测结果统计显示当前图片中各类缺陷的数量汇总整个批次的缺陷分布。结果导出把每张图片的检测结果导出为CSV或Excel报告包含文件名、缺陷类别、置信度、框坐标同时把带标注的结果图保存到指定目录。设计原则就一条少弹窗、大按钮、操作一步是一步。这个界面是给非技术用户用的任何需要二次确认的操作都是负担。5.2 推理线程与UI线程分离的设计GUI开发最大的一个坑是界面假死。如果你在UI主线程里直接跑模型推理模型处理一张大图需要几百毫秒甚至更久这期间窗口会卡住鼠标点了也没反应用户会以为程序崩了。正确的架构是UI主线程只负责渲染和响应点击事件推理放到后台子线程。PyQt里的经典搭配是QThread配合Worker对象。我大致是这样设计的待检测图片路径放进一个队列queue.Queue主线程往队列里喂任务。后台Worker线程从队列取出图片调用模型推理推理完成后通过signal把结果发回主线程。主线程收到signal后更新界面上的图像和统计信息。批量检测时同样走这个队列并支持取消清空剩余任务。这里有个细节模型要一次性加载到显存常驻不要在每张图片推理前都重新加载权重否则速度会慢到让人崩溃。显存不够时批量检测容易触发OOM程序要有捕获异常并给出明确提示的逻辑不能直接闪退。5.3 核心推理代码骨架与结果可视化GUI里的检测核心逻辑我把骨架代码贴出来直接就可以用def detect_single_image(model, img_path, device, img_size1280, conf_thres0.35, iou_thres0.45): img0 cv2.imread(img_path, cv2.IMREAD_COLOR) # letterbox保持长宽比缩放并填充灰色边缘 img, ratio, (pad_w, pad_h) letterbox(img0, new_shape(img_size, img_size)) # BGR转RGBHWC转CHW方便模型输入 img img[:, :, ::-1].transpose(2, 0, 1) img np.ascontiguousarray(img) tensor torch.from_numpy(img).to(device, dtypetorch.float32) / 255.0 tensor tensor.unsqueeze(0) with torch.no_grad(): preds model(tensor) # NMS后处理滤掉低置信度和重复框 det non_max_suppression(preds, conf_thresconf_thres, iou_thresiou_thres)[0] # 将letterbox后的坐标还原回原图尺寸 boxes scale_coords((img_size, img_size), det[:, :4], img0.shape) confs det[:, 4].cpu().numpy() cls_ids det[:, 5].cpu().numpy().astype(int) return boxes, confs, cls_ids, img0letterbox这一步很容易被忽略但不做的话不同宽高比的图片会被强行拉伸变形检测精度会明显下降。标注框画到图上时用OpenCV的rectangle和putText即可不同类别用不同颜色区分并在图的左上角汇总显示各类别的统计数。5.4 批量导出与报告整理批量检测的结果导出我推荐直接生成CSV因为Excel能直接打开还可以二次筛选排序。字段列这样设计就够了文件名缺陷类别置信度x1y1x2y2更完整的报告我还会在CSV之外把每一张检测结果图保存成PNG文件名加一个_detected后缀这样产线人员可以快速翻阅。这里有个经验EL图如果是16位灰度保存检测结果图时要先转成8位再保存否则图像窗口打开是黑的。推荐的格式是PNG而不是JPG避免重复压缩导致细小缺陷视觉上被抹平。6. 踩坑记录与问题排查速查表6.1 训练阶段loss不降、mAP一直为0怎么办我遇到过几次训练loss完全不动的情况排查下来基本都是数据问题而不是模型问题。常见的原因有这几个标注格式错误YOLO格式要求坐标是归一化后的cx, cy, w, h如果误写成像素坐标模型永远学不对。类别ID越界比如实际只有4类但某个标注文件里写了个class_id7或者ID从1开始写而模型约定从0开始导致有空类。数据路径配错data.yaml里的train和val路径指向了同一个目录或者指向了空目录模型等于白训。学习率太大Loss曲线剧烈震荡甚至跳到NaN说明学习率过了临界点需要调低。训练过程中val mAP一直是0还有个隐蔽原因某个类别在验证集中的样本数量极少比如总共就一两张图指标自然上不去。这时候要看类别分布而不是纠结模型能力。6.2 推理部署阶段漏检、误检与效率瓶颈部署时最容易翻车的就是训练和推理预处理不一致。我做过一个反面案例训练时输入1280部署时图省事用了默认的640结果小隐裂几乎全部漏检。排查了很久才发现是这个问题浪费了整整两天。误检率高的场景主要集中在正常焊带和栅线纹理被识别成缺陷。解决思路有三条训练集里加入负样本模型见过足够多正常纹路误判概率自然下降。提高置信度阈值从默认0.25调到0.4甚至0.5能滤掉大部分低置信度误检。复核标注数据看看是否有把焊带阴影标成正样本的低级错误。大图推理慢的问题推荐用切片推理SAHI思路把2000×2000的大图切分成多个640或960的小图每张小图独立推理再把结果坐标映射回原图重叠区域用NMS合并。这个方案比单纯缩小整图效果要好很多能兼顾速度和细节。6.3 快速排查速查表症状可能原因解决办法训练loss始终居高不下标注格式错误、类别ID越界、学习率过高校验数据集先跑一个很小的1 epoch测试val mAP一直为0验证集样本过少或某类别无样本检查类别分布增加验证集多样性训练指标好但实际推理差数据泄漏同组件跨集、采集环境不一致按组件ID分组划分数据统一采集参数小目标漏检输入分辨率太低、模型结构性弱提升到1280输入开启P6头或用切片推理误检焊带/栅线负样本不足、置信度阈值低、标注不规范加入正常样本图提升conf阈值复查标注推理卡顿/UI假死推理跑在主线程改为QThread子线程推理批量检测显存溢出单张显存累积未释放控制批处理数量显存不够时改用CPU分片跑最后说点实在话这套流程完整跑通之后我最大的体会是模型结构本身并不难难的是数据质量和任务定义。YOLO26这个级别的模型已经足够成熟真正决定项目能不能落地的是你有没有把标注规范定清楚、把训练和推理的预处理对齐、把界面做到让非技术用户真的愿意用。还有一点想分享给正在做类似项目的人如果打算把这套东西往产线或电站推一定要提前想好和现有系统的衔接——图像从哪个文件夹来、检测结果如何回流到数据库、报告格式是什么这些“不性感”的接口问题往往决定项目最终能不能稳定运行。我的一个小习惯是每次改完数据集或者训练参数都固定全流程的随机种子并把训练配置、权重版本、对应数据集快照一起存档。这样任何一个时间点的模型效果异常都能追溯原因比事后拍脑袋改参数有用得多。这套方法论放之四海而皆准做其他工业视觉项目也一样适用。