无人机巡检销钉故障检测:级联检测与分类的工程实践

发布时间:2026/9/19 1:38:41
无人机巡检销钉故障检测:级联检测与分类的工程实践 简介这是一份系统研究基于深度学习的无人机巡检图像销钉故障检测问题的学术论文适合电力行业运维人员、计算机视觉工程师及高校相关专业师生阅读用于掌握智能巡检与目标检测算法在工业场景中的应用方法。包内为单个PDF文件大小1.67MB内含完整的期刊论文覆盖中文摘要、关键词、引言、算法原理、实验对比、结论及参考文献等结构。论文发表于《计算机测量与控制》2019年第27卷第11期。文中围绕输电线路销钉脱落故障采用基于区域提议网络的快速卷积神经网络检测算法对无人机拍摄的巡检图像进行目标识别并与聚合通道特征加自适应提升、霍夫变换加直线段检测等传统方法进行系统比较。实验结果显示该方法对销钉脱落故障的识别率达到96%对正常销钉的识别率最高可达98%验证了深度学习方案在细小部件故障检测中的可行性与优越性。读者通过全文可系统了解数据集构建、模型训练与评估、分类器对比等关键环节也可将其作为课题研究或论文写作的参考文献。目前已有277人学习下载。1. 为什么无人机巡检图像里的销钉故障检测这么难跟很多计算机视觉项目不同基于深度学习的无人机巡检图像销钉故障检测卡点几乎不在模型精度而在目标尺度与样本分布。一张 8192×5460 的巡检图里销钉往往只占 20×20 像素缺钉样本一整条航线可能只有几十个正常销钉却有数万个。这种极端长尾让直接训练的检测器会朝“少报错”的方向走全部预测成正常准确率依然很高这恰恰是巡检系统不能接受的失败。更麻烦的是“缺失”没有实体轮廓边界框的学习目标天然模糊。要做成能过验收的系统得把任务拆成检测加分类把裁剪、样本平衡、推理后处理都当成算法的一部分来设计。下文从这套方案拆开讲适合刚开始接巡检项目的算法工程师也适合替系统把关的运维技术人员。2. 销钉故障检测的模型选型与数据准备2.1 检测加分类级联为什么比端到端更稳常见做法是拆成两个模型先用一个目标检测器输出销钉位置再用一个轻量分类器判断每个框是“正常”“缺失”还是“松动”。这个结构在销钉场景得到的实际效果通常优于单阶段模型直接输出三个类别。原因在于检测与分类的学习目标差异。检测器只要回答“这个位置有没有销钉”背景是唯一的负样本分类边界清晰一旦要求检测器同时区分“正常”和“缺失”它就要在没有实体轮廓的区域学出类别差异输出框的偏移和置信度都会不稳定。分类器则不同输入被裁剪成固定尺寸的小图可以放大到 64×64 甚至 96×96 再看小目标信息量不足的问题被大大缓解。表里列了常见方案的差别方案缺陷类别怎么得到小目标召回部署成本适合阶段YOLO 直接出三类检测头预测中低小目标和易混淆类互相干扰单个模型快速出基线Faster R-CNN 直接出三类RoI head 预测中高单模型但推理慢故障样本充足时YOLO 检测 ResNet 分类级联分类器对检测框判类高两个模型可合并导出缺钉样本少、漏检要求严需要注意级联方案的上限由检测框决定。框如果偏了 5 个像素以上分类器看到的区域就对不上故障特征怎么调分类模型都补不回来。所以检测器阶段的置信度阈值不要太激进召回可以高一些把不好判断的样本留给分类器。2.2 数据标注规范与坐标边界数据准备阶段最常见的问题出在“缺失”类怎么标。正确做法是让标注员参考相邻销钉的位置、大小和方向在缺失位置补一个假想框并允许极小的偏移容差。如果这个框画偏了分类器会把邻近正常销钉的特征当成故障特征学进去训练集里的标签噪声直接变成误检。“松动”类要更谨慎。图像上的松动通常表现为角度偏转、垫片位置变化或阴影差异不到 40×40 像素很难看准。我一般只标注那些语义明确的样本拿不准的一律不标避免类别内部噪声比类别间差异还大。坐标系上有个容易忽略的点无人机 EXIF 里的云台姿态和焦距不要直接拿来做像素级测算只用来估算单像素对应物理尺寸辅助决定切图窗口大小。云台抖动、曝光差异会带来明显误差按 EXIF 精确计算反而会切出很多不合适的图块。所有标注和模型输入统一用相对坐标切图后同步做坐标变换。2.3 滑窗切图与样本平衡原始图像直接缩放做输入销钉基本只剩几个像素任何深度学习模型都无能为力。常见做法是把高分辨率巡检图切成 1024×1024 的图块重叠 200 像素保留包含目标的图块参与训练。切图代码并不复杂import numpy as np from PIL import Image TILE_SIZE 1024 OVERLAP 200 MIN_SIDE 12 # 小于该尺寸的截断目标丢弃 def slide_crop(img_path, labels, out_dir): img Image.open(img_path) w, h img.size step TILE_SIZE - OVERLAP tiles [] for y0 in range(0, h, step): for x0 in range(0, w, step): x0 min(x0, w - TILE_SIZE) y0 min(y0, h - TILE_SIZE) crop img.crop((x0, y0, x0 TILE_SIZE, y0 TILE_SIZE)) rel_boxes [] for cx, cy, bw, bh, cls in labels: # YOLO 归一化坐标 x, y (cx - 0.5) * w, (cy - 0.5) * h iw0, ih0 bw * w, bh * h ix0, iy0 max(x - iw0 / 2, x0), max(y - ih0 / 2, y0) ix1, iy1 min(x iw0 / 2, x0 TILE_SIZE), min(y ih0 / 2, y0 TILE_SIZE) iw, ih ix1 - ix0, iy1 - iy0 if iw MIN_SIDE and ih MIN_SIDE: rel_boxes.append(( (ix0 iw / 2 - x0) / TILE_SIZE, (iy0 ih / 2 - y0) / TILE_SIZE, iw / TILE_SIZE, ih / TILE_SIZE, cls)) if len(rel_boxes) 0: tiles.append((x0, y0, crop, rel_boxes)) return tiles这里 TILE_SIZE 决定单张模型输入的信息量图块越大完整销钉越多但下采样后小目标越吃亏OVERLAP 控制同一个目标出现在几张图块里推理时需要用 NMS 把重复框合并回去MIN_SIDE 用来过滤被切掉一半的目标避免训练数据里出现大量不完整实例。样本平衡方面建议先按绝缘子串、金具类型做分层统计正常销钉按比例欠采样缺钉样本复制后加光照抖动再进入训练集不要直接重复拷贝。3. 用 YOLOv8 检测器加 ResNet 分类器跑通销钉故障检测训练3.1 环境与数据集组织训练环境常见组合是 PyTorch 2.1、CUDA 11.8 和 Ultralytics YOLOv8conda create -n bolt python3.10 -y conda activate bolt pip install torch2.1.2 torchvision0.16.2 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics8.2.0 opencv-python数据集按 YOLO 格式组织标签是归一化坐标类别只保留一个 bolt故障类别放到分类器阶段处理datasets/bolt/ images/train/ images/val/ labels/train/ labels/val/ bolt_det.yamlyaml 内容很直接path: datasets/bolt train: images/train val: images/val nc: 1 names: 0: bolt验证集划分按航线而不是按单张图随机抽才能反映真实部署时飞行架次之间的差异。3.2 检测器训练命令与关键参数训练命令用 YOLOv8s 起步重点参数是 imgsz 和 mosaicyolo detect train \ databolt_det.yaml \ modelyolov8s.pt \ imgsz1024 \ epochs120 \ batch16 \ lr00.005 \ lrf0.01 \ optimizerAdamW \ mosaic0.5 \ close_mosaic10 \ patience20 \ projectruns/bolt_det参数取值影响imgsz1024目标下采样后保留像素过小召回暴跌mosaic0.5降低上下文丢失close_mosaic10最后 10 轮关闭拼图增强batch16显存不足降为 8优先保 imgszimgsz 开到 1024 而不是默认的 640是因为销钉目标太小。640 输入经过 32 倍下采样后一个 20×20 的销钉在特征图上只剩 0.6×0.6 像素检测头基本无法学习1024 时目标区域还有约 1×1 像素配合浅层特征才有效果。mosaic 调成 0.5 而不是默认 1.0巡检图里销钉与绝缘子串的位置语义强相关过度拼图会把上下文关系打乱训练后期还要用 close_mosaic 关闭增强让模型回到真实分布。显存不够时优先降 batch 并用梯度累积输入尺寸不要随便降到 640。3.3 分类器的输入构造与训练检测器收敛后把每个检测框从原图按 1.2 倍向外扩大裁出统一缩放到 96×96送入 ResNet-18 分类器。类别设置四类正常、缺失、松动、其他其中“其他”非常重要专门收集检测器误检的背景图块没有这一类所有误检都会落到前三个类别里。数据增强要贴合无人机图像特点from torchvision import transforms normalize transforms.Normalize( mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225] ) train_tf transforms.Compose([ transforms.Resize((96, 96)), transforms.RandomResizedCrop(96, scale(0.85, 1.0)), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.2), transforms.RandomGrayscale(p0.05), transforms.ToTensor(), normalize, ])亮度扰动可以做得大一些无人机一日之内光线变化很剧烈色调扰动反而要小绝缘子串与金具的颜色本身是判别信息。分类训练时给缺失类较高权重正常与缺失按 1:3 到 1:5 配比即可太高会加剧过拟合。3.4 推理时跨图块合并滑窗推理的常见坑是同一个销钉在图块边缘被切开两次检测框都偏小位置不在图块中心单看每次预测置信度都不高。解决方法是推理时额外做一次偏移半窗的切图对每个候选框映射回原图坐标后用中心点聚类合并。不依赖开源库的简单版本是对候选框按中心点距离和尺寸相似度分组每组保留最高分。合并代码大致长这样def merge_boxes(boxes, center_dist30): boxes: (x, y, w, h, score)坐标已映射回原图 boxes sorted(boxes, keylambda b: b[4], reverseTrue) merged [] for box in boxes: cx, cy box[0] box[2] / 2, box[1] box[3] / 2 for g in merged: gx g[cx] if abs(cx - gx) center_dist: g[boxes].append(box) break else: merged.append({cx: cx, boxes: [box]}) return [max(g[boxes], keylambda b: b[4]) for g in merged]合并后每个物理销钉只保留一个框再做一次低置信度过滤比直接对图块预测做全局 NMS 在边界处稳定得多。这也是检测器能保持高召回的关键一环。4. 销钉检测系统的评估指标、消融实验与部署落地4.1 别只报 mAP要按故障类型分桶看召回mAP 在长尾数据上是出了名的乐观把几十个缺失全判成正常mAP 依然不低。巡检项目真正要盯的是两类错误故障漏检和单张误报。给一个实际可用的指标表指标计算口径用途故障召回率标注为缺失/松动的样本中正确检出的比例发现漏检安全关键单张误报数平均每张巡检图产生的假阳性数量评估人工复核成本定位 IoU检测框与标注框的交并比决定分类器输入质量F1召回与精确的调和平均模型横向对比测试集要按“绝缘子串型号”“拍摄距离”“光照条件”分成若干 bucket分别统计。同一个模型远距离 bucket 的召回可能只有近距离一半这直接决定飞控系统下次按什么高度什么角度采集。4.2 值得做的几组消融实验模型能跑通以后别急着把 YOLO 换成更大的版本。小目标检测场景里常见做法的消融顺序是每次只改一个变量记录故障召回和单张误报两个核心数字看哪一步改动真正带来提升再决定要不要换结构。改动变量基线值对比值输入尺寸6401024滑窗切图原图直接缩放1024 tilemosaic1.00.5分类器输入6496有无“其他”类三类四类跨 tile 合并无中心点聚类这组实验做下来通常会发现输入尺寸和滑窗切图的收益远大于模型换大。小目标场景里样例有效分辨率永远先于模型结构起作用。4.3 导出与 TensorRT 部署巡检边缘设备常见的是 GPU 盒子导出和加速流程比较成熟yolo export modelruns/bolt_det/weights/best.pt formatonnx dynamicTrue imgsz1024 trtexec --onnxbolt_det.onnx --saveEnginebolt_det.engine --fp16dynamicTrue 保留动态 batch 和输入尺寸方便同一引擎兼容 1024 与 1280 两种切图。fp16 在小目标检测上通常能保持精度但不建议直接上 int8缺钉这类目标信息量太小量化误差容易把尾部特征抹掉。分类器是 ResNet-18量化和导出方式相同两个模型可以用同一份 TensorRT 工程管理。部署 pipeline 的常见瓶颈反而在图像解码与 RGB 转换上滑窗切图会产生大量重复像素拷贝。建议预处理阶段用单次解码把整图留存在显存或共享内存里切图只做指针级 ROI 引用而不是反复 jpeg 解码。5. 困难样本挖掘让漏检收敛到几类可解释的样本上模型上线前最值得花时间的是困难样本挖掘拿当前最好的模型跑一遍训练集和验证集把预测分数接近阈值的样本全抽出来看。销钉场景里这些样本通常集中在三种被阴影挡住一半的销钉、与绝缘子颜色相近的锈蚀销钉、图块边缘被截断的目标。做完这一步问题就从“模型不行”变成“某一类样本没覆盖”改进路径立刻清晰。具体实现可以做在线难例重加权按 loss 对 batch 内样本排序def weighted_train_step(batch, model, optimizer, alpha0.7): losses [] for img, target in batch: loss model.loss(img, target) losses.append((loss.detach(), img, target)) losses.sort(keylambda x: x[0], reverseTrue) keep max(1, int(len(losses) * alpha)) total 0.0 for loss_val, img, target in losses[:keep]: l model.loss(img, target) total total l l.backward(retain_graphTrue) optimizer.step() optimizer.zero_grad() return total.item()这段逻辑是每个 step 先算一遍所有样本的 loss把 loss 最大的前 70% 回传梯度简单样本只做基准统计。示例里用 retain_graph 连续回传真实工程里显存开销很大正确做法是做成 mask 或按梯度累积拆开。在线重加权一定在训练中后期再开前 30 个 epoch 关闭否则模型容易被噪声样本带着跑。困难样本稳定后另一个衍生技巧是给分类器加“退路”。把“松动与正常”这种人工都可能看走眼的类别合并成“可疑”算法输出四类之外再输出“人工复核”档。无人机巡检从来不是追求完全自动判断把 0.1% 的样本标成可疑交给复核员比死磕那 0.1% 的分类准确率划算得多。抽样脚本要放进代码库定期跑每个新批次数据进来后都做一次困难样本统计把样本池反哺训练集。顺带一提远距离和遮挡图块往往会让人想试图像超分辨率重建实际 2 倍超分在小目标上多数没有收益因为学习出来的纹理被当成噪声用多数情况下把切图重叠率从 200 提到 300边界召回提升更直接。本文还有配套的精品资源点击获取