YOLO11无人机航拍小目标检测实战:铁路障碍物识别系统全解析
发布时间:2026/9/16 19:19:49来源:尧图网络
各位做视觉检测的朋友尤其是搞无人机航拍、铁路巡检相关项目的同行这期实战应该能帮上忙。我一直在做YOLO系列的落地项目从v5到v8再到现在的YOLO11踩过的坑不少但YOLO11在航拍小目标检测这块的提升确实值得专门写一篇。这次要分享的是一个完整的无人机航拍铁路障碍物检测项目从数据集构建到模型训练再到检测系统部署全套流程跑通代码和训练好的权重都能直接用。这个项目的核心场景很明确无人机在铁路沿线巡航时自动识别轨道上的障碍物——比如落石、倒伏树木、施工遗留物、闲杂人员或车辆。铁路巡检的难点从来不是“能不能检测到”而是“这么小的目标在高空视角下还能不能稳定检出来”。一块直径30厘米的石头在100米高的航拍画面里可能就十几个像素这对检测器的要求非常高。YOLO11作为ultralytics最新一代的检测框架在C2f模块、检测头结构上做了针对性优化配合合理的训练策略能把这类小目标的漏检率压到比较理想的水平。这篇文章不打算讲太虚的理论直接上干货数据集怎么做、模型怎么调、系统怎么搭以及我实际跑项目时踩过的那些坑。适合有一定YOLO基础、想快速上手无人机航拍检测项目的同学参考。1. 项目整体思路与方案选型1.1 为什么选YOLO11而不是YOLOv5或YOLOv8先说结论在航拍铁路场景下YOLO11的综合表现确实优于v8更不用说v5了。但选型这事儿不能只看指标得结合项目实际需求。我一开始用的是YOLOv8m跑下来发现两个问题一是误检率偏高轨道附近的护栏、枕木经常被误报成障碍物二是小目标召回率一般小于16像素的目标基本靠运气。换到YOLO11之后情况明显改善。YOLO11在骨干网络里继续优化了C3k2模块实际架构中类似C2f的变体特征融合的效率更高检测头在预测小目标时的置信度分布更合理。简单说YOLO11同样尺寸的模型在小目标AP上比v8普遍高2到4个点推理速度还更快。对航拍铁路这种密集小目标场景这个提升非常有意义。不过要提醒一句YOLO11不是万能的。如果项目对推理延迟极度敏感比如嵌入式设备要求10ms以内YOLO11s或nano版可能是更好的选择如果能接受稍重的模型换精度那就选m或l。我们项目最后用了YOLO11m在RTX 4090上推理速度约3ms一张完全够实时处理。1.2 无人机航拍视角的核心难点航拍铁路障碍物检测跟普通的地面摄像头检测完全是两码事。第一个难点是目标太小。无人机在80到120米高度巡航时轨道宽度在画面中可能只占20到30像素一个障碍物可能只有10到20像素。第二个难点是背景复杂——钢轨的反光、道砟的纹理、接触网支柱、电线这些在模型眼里都是潜在的“干扰项”。第三个难点是正负样本极度不均衡。铁路正常运营时绝大多数画面是没有障碍物的能拍到障碍物的帧可能只占全部数据的1%都不到。如果直接用原始视频流训练模型会严重偏向“什么都没有”这个类别导致漏检。所以数据集的构建不能光靠“拍”还要靠“挑”——把有效障碍物帧筛选出来做样本。第四个难点是天气和光照变化。无人机航拍受日照角度、阴影、雨雾影响非常大同一个位置上午和下午拍出来模型提取的特征差异可能很大。这些因素直接决定了数据集构建和数据增强策略的设计。1.3 系统整体架构与功能模块整个项目我做成了三个独立又互相衔接的部分这样无论是单独研究模型还是部署完整系统都方便数据集模块包含原始航拍图像收集、标注文件、划分好的train/val/test集以及针对小目标优化的增强策略代码。训练模块基于ultralytics YOLO11的完整训练脚本支持多卡训练、混合精度、EMA以及核心参数的自定义配置。检测系统模块一个轻量级的Python推理服务支持单张图片、视频文件、实时RTSP流三种输入方式检测结果可视化、告警框标注、日志记录一应俱全。这样说起来好像很简单但每个模块里都有值得说道的细节。比如数据集的类目设计不是随便标个“障碍物”就完事的训练时的图像尺寸、锚框设置、数据增强策略每一处都会影响最终效果。下面我按模块逐个拆开讲。2. 数据集构建与预处理策略2.1 数据来源与构成分析数据集是这类项目的命门。我用到的数据主要有三个来源一是自己租无人机在线路附近采集的实拍数据二是网络上公开的铁路场景数据集以国外铁路为主国内线路涉及敏感不碰、不传三是从开源航拍数据集里筛选出铁路区域的图像做补充。这里特别想说一下POI数据集给我的启发。POI是面向航拍小目标检测的经典数据集里面很多目标在图像中不足32像素甚至有大量目标小于16像素。虽然它的类别和铁路场景不完全匹配但它的标注方式、小目标分布规律对做铁路场景非常有用。我借鉴了POI的标注思路把铁路障碍物分成五个细类而不是笼统地标一个“obstacle”person闯入线路的闲杂人员vehicle工程车、汽车等进入限界rock落石重点是大块危石tree倒伏树木、树枝侵入限界other施工遗留物、塑料布、杂物等为什么细分成这五类因为铁路运维检修制度对障碍物有不同的响应等级。落石和倒树需要紧急停车处理人员闯入需要联动安防系统而一个塑料瓶可能只需要后续清理。分类检测能直接对接业务逻辑为后续的告警分级做铺垫。2.2 标注规范与质控细节标注质量直接影响模型效果上限。我在这上面吃过亏——第一版数据集标注很粗糙有些小石头只画了个不准确的框结果模型训练出来在验证集上AP很高一到实拍场景就抓瞎。后来我加强了标注质量控制定了一套自己的规范标注框必须紧贴目标可见部分对遮挡目标只标可见区域不臆测完整轮廓。模糊目标宁缺毋滥如果目标太小导致无法确认类别直接跳过不标标错类比不标危害更大。对小于8像素的目标不标注这个阈值是我实际测试得出的经验值。8像素以下的目标即便标了模型也学不到有效特征反而增加学习噪声。每个类别在验证集上的样本数不低于200个防止某个类别样本过少导致的类别不平衡。标注工具我用的是LabelImg和X-AnyLabeling。前者轻量、够用后者支持半自动标注对“person”这类目标可以用预训练模型打底框再人工修正效率能提高60%以上。对于“rock”这种形状不规则的我建议用多边形标注导出为YOLO格式的矩形框主要还是YOLO系模型需要矩形框输入。2.3 针对小目标的数据增强策略数据增强这块我试过ultralytics默认的增强参数效果一般。后来参考了YOLO官方在小目标检测上的建议又结合POI数据集的处理经验定制了一套适合航拍铁路场景的增强配置Mosaic增强开启但在最后10个epoch关闭。Mosaic对提升小目标检测帮助很大但训练后期不关掉会导致模型在真实分布上精调不够。图像缩放范围scale设为0.2到1.5。航拍图像中目标尺度差异大这个范围能模拟不同的飞行高度。随机旋转设为±30度。航拍图像没有绝对的“正着拍”障碍物方向随机性很强。HSV扰动hue0.02sat0.6val0.5。铁路场景的颜色差异本来就大HSV扰动不能太猛否则钢轨的灰白色特征会被破坏。Cutout和Copy-Paste增强对障碍物这种典型的小目标很管用。把训练集中的石头、树木样本随机裁剪出来粘贴到新的背景图上并同步更新标注相当于白捡了一堆合成样本。这个操作对提升小目标召回率非常明显。提示数据增强不是越猛越好。我刚开始把HSV的val扰动调到了0.7结果雾天样本的检测效果反而下降了因为模型学到的颜色规律被过度破坏。增强策略要跟部署场景匹配航拍铁路场景色调相对固定增强幅度适中即可。2.4 数据划分与类别平衡处理数据划分我采用了“按视频段划分”而非“按帧划分”的策略。什么意思如果同一段视频的连续帧一部分进训练集、一部分进验证集验证结果会虚高因为模型“见过”了几乎相同的画面。正确的做法是按航拍航线片段划分——一条航线上的视频要么全进训练要么全进验证这样评估结果才真实反映模型的泛化能力。类别平衡方面由于线路正常时“other”和“tree”样本较多而“rock”和“person”较少我用了一种简单的过采样策略对少数类图像在训练时重复采样。具体做法是统计每个类别的图像数量计算权重数组传给ultralytics的class_weights配置让模型对少数类的损失贡献更高。这一步对提升落石检测的召回率帮助很大。3. YOLO11模型训练与网络结构优化3.1 环境配置与训练代码框架先交代一下我的软硬件环境方便你复现对比GPURTX 4090 24G训练/ RTX 3060 12G测试CUDA11.8PyTorch 2.1.0ultralytics8.3.x分支内置YOLO11支持Python3.10推理框架ONNX Runtime TensorRT部署用安装ultralytics就一条命令的事情但版本要选对。YOLO11发布后ultralytics持续迭代建议用pip install ultralytics8.3.42这种固定版本号避免后续接口变动影响代码。训练脚本我用的是ultralytics的API接口比命令行更灵活尤其是需要动态调整参数和集成自定义回调的时候。核心代码如下from ultralytics import YOLO model YOLO(yolo11m.pt) # 加载预训练权重 results model.train( datarail_obstacle.yaml, epochs300, imgsz1280, # 航拍小目标图像尺寸必须拉大 batch16, lr00.001, lrf0.01, optimizerAdamW, weight_decay0.0005, warmup_epochs3, mosaic1.0, # Mosaic增强开启 close_mosaic10, # 最后10轮关闭 box7.5, # 边框损失权重 cls0.5, # 分类损失权重 dfl1.5, # DFL损失权重 workers8, device0, projectruns/train, namerail_yolo11m, cacheTrue, pretrainedTrue, )这里有几个参数值得单独说。imgsz1280是航拍小目标检测的关键——如果你用默认的640一个在1280下占20像素的目标在640下就只有10像素模型基本学不到特征。代价是显存占用和训练时间增加但这是值得的。如果显存不够可以降batch或者用梯度累积。box权重我调到了7.5这是为了让模型更注重边界框的回归精度对检测落石这种位置敏感型目标有帮助。3.2 如何在YOLO11中增加P2检测头航拍小目标检测还有一个经典优化手段给网络增加P2检测头。YOLO11默认有三个检测头分别对应80×80、40×40、20×20的特征图负责小、中、大目标。但航拍图像中的很多目标哪怕在1280分辨率下也只对应40×40特征图上的几个像素所以增加160×160的P2检测头能显著提升小目标召回率。这个改动在ultralytics框架里实现起来并不复杂核心是修改ultralytics/nn/modules/head.py里的Detect模块以及模型的yaml配置。默认yolo11m.yaml定义了三层特征层我改出一个yolo11m-p2.yaml额外接上骨干网络浅层的高分辨率特征图。具体改动思路是在yaml文件里增加一个来自骨干网络浅层比如第二层输出的160×160特征图的分支作为P2检测头的输入。调整Neck部分的特征融合结构把P2特征与深层特征做跨尺度融合。将检测头数量从3个改为4个并在Detect类的初始化中把ch列表加上对应的特征通道数。改完结构后模型的参数量会略微增加推理速度会慢10%左右但对小目标的AP提升通常在5个百分点以上。我在RTX 4090上实测使用P2检测头后小于16像素目标的mAP从0.31提升到了0.37这个提升对实际巡检意义很大。注意P2检测头不是免费的午餐。如果你的障碍物尺寸比较大比如树倒横在轨道上目标能占上百像素增加P2反而可能引入更多误检。具体要不要加建议拿一小部分数据先用默认结构训练一版看小目标AP再决定。3.3 训练过程实录与收敛判断我用YOLO11m训练了300个epoch整个过程约18个小时。这里说说训练过程中的几个关键观察点前50个epoch损失下降很快尤其是box损失从初始的1.8降到0.9左右。但val集的mAP50提升幅度不大主要原因是目标太小模型还在学习“怎么精准定位”没到“识别类别”的阶段。100个epoch之后mAP50开始快速爬升到200个epoch时趋于平缓。如果loss曲线已经平了再训练也没意义可以提前早停。我习惯用patience30的早停机制如果30个epoch后验证集指标不再提升就自动停止并保留最佳权重。实测是在第248个epoch触发的最终mAP50在验证集上达到0.79mAP50-95为0.54。这个结果对航拍小目标来说已经算不错。另外混合精度训练AMP在4090上默认开启显存占用大约11G训练速度提升约40%。如果你是20系或更老的卡AMP可能出现nan损失这时候需要关掉AMP并改用FP32训练。3.4 模型剪枝与蒸馏思路如果你想在嵌入式设备上部署模型压缩是绕不开的话题。我试验了两种方案一种是直接换小模型YOLO11s在精度上比m低了大概6个点但推理速度在TensorRT下可以做到2ms以内另一种是知识蒸馏——用训练好的11m当教师模型让11s学生模型模仿教师的特征输出和预测分布。第二种方案我还在实验中但初步结果不错。在验证集上蒸馏出的11s比直接训练的11s精度高了3个点。做法也不复杂就是在训练时同时加载教师模型和学生模型在学生模型的损失函数里加一项蒸馏损失一般用KL散度或特征图MSE。ultralytics没有内置这个功能但可以通过继承Trainer类自定义训练逻辑实现网上有社区实现可以直接参考。4. 检测系统实现与部署4.1 推理链路设计训练完模型只是第一步真正要用的是一套完整的检测系统。我的系统设计成一个Python服务输入支持三种模式单张图片、离线视频、RTSP实时流。核心推理流程如下import cv2 import numpy as np from ultralytics import YOLO class RailObstacleDetector: def __init__(self, weights_path, conf_thres0.35, iou_thres0.45): self.model YOLO(weights_path) self.conf_thres conf_thres self.iou_thres iou_thres self.class_names {0: person, 1: vehicle, 2: rock, 3: tree, 4: other} def predict_frame(self, frame): results self.model.predict( frame, confself.conf_thres, iouself.iou_thres, imgsz1280, verboseFalse ) boxes results[0].boxes detections [] if boxes is not None: for box in boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() conf float(box.conf[0]) cls_id int(box.cls[0]) detections.append({ bbox: [x1, y1, x2, y2], confidence: conf, class_id: cls_id, class_name: self.class_names[cls_id] }) return detections这里有几个工程细节值得注意。置信度阈值我设的0.35比默认的0.25高原因是航拍画面中背景复杂低置信度的预测大部分是误检。如果你想追求高召回可以调到0.25但要做好误检变多的准备。IOU阈值0.45是常规值用于NMS去重。4.2 实时视频流处理与告警对于RTSP实时流处理逻辑要加一层“帧间过滤”。无人机视频一秒25帧障碍物目标在连续帧之间位置变化很小完全没必要每帧都做完整推理。我采用了跳帧推理策略每3帧推理一次中间帧直接复用上一帧的检测结果。这样在保证实时性的同时把CPU占用降低了近一半。告警逻辑我做了分级处理一级告警检测到“person”或“vehicle”进入轨道区域这个优先级最高需要立即通知。二级告警检测到“rock”或“tree”属于线路障碍需要尽快派人核查。三级告警检测到“other”类记录在案定期处理。告警通知我是用Webhook推送到企业微信的代码里预留了接口你也可以改成短信、邮件或钉钉机器人。推送内容包含检测截图、目标类别、置信度和时间戳现场人员不用打开系统也能第一时间看到现场情况。4.3 模型导出与TensorRT部署训练好的PyTorch模型不能直接用于生产环境我一般会走一遍“PyTorch → ONNX → TensorRT”的转换流程。ONNX是中间格式方便跨平台部署TensorRT是NVIDIA显卡上的最优推理引擎延迟低、吞吐高。导出命令很简单yolo export modelruns/train/rail_yolo11m/weights/best.pt formatonnx opset12 imgsz1280 yolo export modelruns/train/rail_yolo11m/weights/best.pt formatengine device0 imgsz1280 halfTrue导出TensorRT引擎时有两个关键参数半精度和动态batch。halfTrue启用FP16推理速度翻倍精度损失几乎可以忽略。动态batch则允许你在推理时自由选择batch size适合处理变化的并发请求量。我用TensorRT FP16在RTX 3060上实测1280分辨率的推理耗时从ONNX的28ms降到了14ms。如果对延迟要求不高ONNX就够用了如果要做多路视频流实时分析TensorRT基本是必须的。4.4 界面与可视化完整的检测系统我打包了一个简单的Web界面用Gradio搭的支持上传图片和视频直接检测方便非技术人员使用。界面逻辑很简单用户传入一张航拍图后端走一遍推理流程前端展示标注了检测框的结果图右侧显示检测列表和置信度。Gradio的代码很简短强烈推荐import gradio as gr def detect_image(image): detections detector.predict_frame(image) annotated draw_detections(image, detections) return annotated, detections demo gr.Interface( fndetect_image, inputsgr.Image(typenumpy), outputs[gr.Image(typenumpy), gr.JSON()], title无人机航拍铁路障碍物检测系统, description上传航拍图片自动识别铁路线路上的障碍物, ) demo.launch(server_name0.0.0.0, server_port7860)draw_detections就是普通的cv2画框操作但我会根据不同类别用不同颜色方便区分。比如“person”用红色“rock”用橙色“tree”用绿色。细节虽小但现场人员反馈这种颜色区分对快速判断很有帮助。5. 常见问题与排查技巧实录5.1 数据集阶段的常见问题问题一标注框不贴合目标导致定位偏移。这个现象在“rock”类上最典型因为石头边界本来就不规则标注员容易把整个色块都框进去导致模型学到的目标范围比实际偏大。解决办法是标注时放大图像到200%再打框并且只框住目标的核心区域边缘模糊的部分宁可不框。问题二数据集图像分辨率不统一。我第一版数据集里混了4K相机照片和1080P视频截帧导致训练时模型对分辨率很敏感。后来我把所有图像统一缩放到1920×1080或1280×720并记录原始尺寸作为meta信息才算解决了这个问题。YOLO训练时会做letterbox归一化但分辨率差距过大仍然会让模型学到“分辨率偏差”这种伪特征。问题三白天样本占90%夜间和弱光样本几乎没有。这会导致模型在夜间检测效果很差甚至完全失效。虽然铁路夜间巡检用的主要手段是红外和雷达但如果白天算法覆盖不了夜间整个系统的任务边界就不完整。我的做法是补充少量夜间红外图像公开数据集并用光照扰动增强模拟黎明和黄昏的光线条件。5.2 训练阶段的常见问题问题一Loss掉不下去一直在高位震荡。这一般是学习率设置问题。我遇到过一次lr0设成了0.01结果训练前20个epoch的loss直接震荡发散。后来改成0.001配合3个epoch的warmup就正常了。如果用的是AdamW建议学习率起步从0.0005到0.001不要贪快。问题二做了数据增强但验证集AP反而下降了。这种情况通常是增强强度过大导致模型学到的目标“失真”了。比如旋转角度设成90度落石旋转90度后看起来像别的物体HSV val扰动太大钢轨的灰色变成棕色模型误认为“tree”的纹理。遇到这种情况我的排查方法是分步关闭增强策略逐一对比验证集AP变化找到拖后腿的增强项。问题三类别不平衡导致模型偏向多数类。我遇到的是“other”类占了大头“person”类样本很少导致模型把所有人都漏检。除了前面提到的class_weights还有一个有效的做法是类别级数据增强——对“person”这种少数类单独做复制粘贴增强把样本量提上来。5.3 部署阶段的常见问题问题一TensorRT推断结果与PyTorch不一致。这个问题常见于图像预处理方式不对。ultralytics的预处理包括letterbox、归一化、BHWC转BCHW等步骤如果导出模型时没处理好这些细节TensorRT出来的结果就会有偏差。我的经验是尽量用ultralytics自带的推理接口而不是手动拼接预处理流程省心且不易出错。问题二推理时间不稳定偶尔有卡顿。在RTSP流并发场景下我发现推理引擎偶尔会卡一下。排查后发现是显存分配碎片的缘故每帧都重新建tensor导致显存分配压力大。后来在推理循环里预分配了固定大小的缓冲区并用了CUDA stream卡顿问题基本消失。问题三单路视频流稳定多路并发时GPU显存溢出。这个问题在无人机组网巡检时很常见。我当时的做法是把imgsz从1280降到960代价是小目标AP降了1.5个点但可以同时处理4路视频流。如果既要精度又要多路建议上多卡方案或者用模型分片。另外一个讨巧的办法是采用“关键帧全精度中间帧低精度”的混合推理策略画面变化不大时用960推理检测到目标后再用1280重新确认。6. 项目源码结构与使用说明6.1 代码目录说明项目的代码组织参考了工业项目的习惯训练、推理、工具函数分开存放方便维护和扩展rail-obstacle-detection/ ├── datasets/ │ ├── rail_obstacle.yaml # 数据集配置文件 │ ├── images/ │ │ ├── train/ # 训练图片 │ │ └── val/ # 验证图片 │ └── labels/ │ ├── train/ # YOLO格式标注 │ └── val/ ├── models/ │ ├── yolo11m-p2.yaml # 增加P2检测头的模型结构 │ └── best.pt # 训练好的权重 ├── scripts/ │ ├── train.py # 训练脚本 │ ├── export.py # 模型导出ONNX/TensorRT │ └── detect.py # 推理脚本图片/视频/RTSP ├── utils/ │ ├── webhook.py # 告警推送 │ ├── visualizer.py # 结果可视化 │ └── logger.py # 日志模块 └── requirements.txt6.2 快速上手指南如果你想直接跑通整个流程按照下面这几步操作就行克隆项目到本地安装依赖pip install -r requirements.txt。准备数据集。你可以直接用项目自带的迷你演示集约500张图片也可以按第二章的方法构建自己的数据集。如果用自己的数据记得把datasets/rail_obstacle.yaml里的路径和类别名改掉。开始训练运行python scripts/train.py --data datasets/rail_obstacle.yaml --weights yolo11m.pt。测试效果运行python scripts/detect.py --source test.jpg --weights runs/train/rail_yolo11m/weights/best.pt。导出部署运行python scripts/export.py --weights runs/train/rail_yolo11m/weights/best.pt --format engine。每一步的详细参数我都写在代码注释里了照着跑问题不大。6.3 二次开发扩展方向如果你不想止步于障碍物检测这个项目的基础架构是可以直接扩展的。我列几个自己觉得有价值的后续方向轨道线检测 障碍物定位结合轨道线分割结果把障碍物的位置映射到实际铁轨坐标系这样可以直接告诉调度中心“障碍物在几号轨道的哪一段”。多目标跟踪ByteTrack/DeepSORT对检测到的障碍物做连续跟踪判断它是否在移动、移动方向如何比如人沿轨道行走和横穿轨道风险等级完全不一样。多机协同巡检多架无人机同时巡检不同区段检测结果汇总到中心服务器统一分析这对大范围铁路线路巡检非常实用。极端天气专项优化针对雨、雪、雾、逆光等特殊场景收集数据并微调模型提升恶劣天气下的可靠性。我自己下一步计划做的是轨道线检测和障碍物定位融合正在试验将分割模型YOLO11-seg和检测模型并行跑用分割结果过滤轨道外的误检。初步效果不错后续有进展再更新。这个项目做到现在最深的体会是航拍铁路障碍物检测的真正难点不在模型结构而在对场景的理解——小目标分布规律、数据增强边界、类别权重设置、部署时的性能取舍每一个细节都需要基于实际数据反复调优。YOLO11只是工具箱里的一个好工具能不能发挥它的价值取决于你对自己业务场景的理解深度。希望这篇实战笔记能帮你少走一些弯路做出真正可用的检测系统。
网站建设高端定制企业官网