简介这套资源围绕无人机目标检测应用构建定位为面向YOLO模型训练与评测的标注图像数据集适合计算机视觉算法工程师、研究人员以及无人机安防、巡检、低空管理等方向的开发者学习使用。压缩包内共1894个文件包含797张JPG原始图像与1097个XML标注文件文件总大小约96.11MB其中XML为LabelImg工具生成的VOC格式标签记录了无人机目标的边界框与类别信息并附带可将XML转为TXT或JSON的脚本方便转换后直接对接YOLO要求的标签结构。该资源目前已有515人学习下载。借助这套数据使用者可以快速获得高质量的无人机检测训练样本省去自行采集与标注的时间在训练环节可结合转换脚本完成数据预处理并基于标签文件开展模型验证与性能指标评估如精确率、召回率与mAP等适合在本地GPU环境中迭代调试YOLO系列模型也可用于教学演示或课题研究。整体规模适中适合快速跑通从数据准备到模型落地的完整流程。1. 无人机目标检测识别无人机到底难在哪先看清“小、快、变”三个字无人机目标检测识别无人机这行字放在搜索引擎里通常对应着一个明确需求机场净空、电力巡检、大型活动低空安保都要在视频流里把多旋翼无人机从天空和复杂背景中框出来。做过真实项目就知道难的不是调用 YOLO而是模型在目标只有二十来个像素、和鸟长得差不多、还带着旋翼模糊的情况下既不漏检又不虚警。这类目标有个专门叫法——低空慢速小目标简称低慢小。目标检测和目标识别这两个词在安防语境里经常混用实际落地时看的是检测框加分类结果。下面按数据构建、模型选型、训练调参、部署验证展开每个环节都给出可以直接抄的参数和命令适合正在搭反无人机方案或者准备做小目标检测的工程师。2. 数据先行无人机检测数据集怎么搭、标注怎么做2.1 三种数据来源实拍、公开数据集、合成数据怎么配比低慢小目标检测的数据集和普通行人检测不太一样。行人数据集随便找几万张公开图就能训无人机不行——真实场景里无人机出现频率低、目标小、拍摄视角又刁钻网上能直接用的公共数据往往来自红外跟踪任务或者单一蓝天背景。所以做这个项目的第一个决定不是选模型而是先想清楚数据从哪里来。常见做法是三种来源混着用。第一是实拍找一架无人机当靶机另一路设备在地面仰拍或者用另一架飞机平行视角拍距离从近到远分几档覆盖晴、阴、逆光、清晨、傍晚。第二是公开数据集业内用的比较多的是 Anti-UAV 和 Drone-vs-Bird 这两个基准前者偏跟踪、后者偏检测特点是比较干净但场景覆盖窄。第三是合成数据把无人机三维模型扣到真实天空背景里或者直接用游戏引擎渲染优势是天气、高度、机型、角度都能随意控制缺点是风格差异大模型容易学出“图片感”。来源优点主要坑建议配比实拍和部署场景分布最接近成本高视角容易单一60%公开数据集起步快类别干净红外帧多场景窄直接训会过拟合20%合成数据可控天气高度机型渲染风格和真实图有差距20%配比不是固定的如果你的部署区域就是一片空旷草地实拍占比可以拉到 80%。但有一条红线必须守住数据集里一定要包含鸟这个类别最好还有风筝、塑料袋这类干扰物。只标无人机不标鸟训练出来的模型第一天上云就被鸟打爆这种事我在现场见过不止一次。提示公开数据集的标注格式五花八门拿到先统一格式别急着训练。格式转换是下一个坑。2.2 小目标标注的四个细节框紧、去模糊、分鸟、留负样本无人机检测的标注规范和普通目标检测不一样最直接的差异就是目标太小。COCO 把 32×32 以下算小目标而无人机出现在画面里经常只有 10×10 到 30×30 像素标注时一个像素的偏差都会显著改变 IoU。四个细节值得单独盯第一框要紧贴目标轮廓。低速悬停时目标边缘还算清晰旋翼高速转动时会出现一圈虚影。我见过不少人把虚影也框进去等于给模型灌入了错误的目标范围。正确做法是只框机身主体旋翼虚影宁可漏掉也别包进去。第二运动模糊严重的样本要么单独成一类要么直接删掉。把模糊目标混进正常类别模型会学到“无人机就是一团糊”的错误关联推理时置信度普遍偏低。如果模糊样本占比超过 10%建议建一个 drone_blur 类训练完再在输出阶段把两类合并。第三鸟必须单独建类。视觉上鸟和无人机在低分辨率下几乎无法区分硬把它们都归为背景只是把问题藏起来。把 bird 类别加进去模型至少学会了先判断“这是不是鸟”不然后处理阶段没有任何信息可以做区分。第四纯背景负样本要有。大量不含无人机的天空、云、建筑物边缘、树冠视频帧不需要标任何框直接以图片形式混进训练集。YOLO 会把没有对应标签的图当作背景参与训练用于压低虚警。以前有人用“空 txt 文件”表示负样本ultralytics 里不需要图片没有标签文件就行。2.3 把 VOC 标注转成 YOLO 格式转换脚本与四个边界标注工具的选择上LabelImg 和 X-AnyLabeling 都能直接导出 VOC 或 YOLO 格式。X-AnyLabeling 上手更快支持矩形和多边形对目标检测够用。但团队协作时经常有人导出 VOC而训练需要 YOLO 的 txt这中间就要过一次转换脚本。下面这段代码是每次新项目我都会复用的一段处理了四个边界情况。import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, classes): 解析 VOC XML返回 YOLO 格式的标注行。 图片宽高直接从 XML 的 size 节点读取避免外部传入写死。 classes: 类别列表列表下标就是类别 id必须和训练配置一致。 root ET.parse(xml_path).getroot() size root.find(size) img_w float(size.find(width).text) img_h float(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes: continue # 不在白名单里的类别直接跳过 cls_id classes.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 边界 1个别标注工具会产出坐标逆序先归一化再处理 xmin, xmax min(xmin, xmax), max(xmin, xmax) ymin, ymax min(ymin, ymax), max(ymax, ymax) # 边界 2退化成点或线的框IoU 计算时会出零除直接丢弃 if (xmax - xmin) 1 or (ymax - ymin) 1: continue # YOLO 格式类别中心x 中心y 宽 高全部归一化到 0~1 cx ((xmin xmax) / 2) / img_w cy ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return lines # 批量转换 xml_dir Path(Annotations) label_dir Path(labels) label_dir.mkdir(exist_okTrue) for xml_path in xml_dir.glob(*.xml): lines voc_to_yolo(xml_path, classes[drone, bird]) if lines: (label_dir / (xml_path.stem .txt)).write_text( \n.join(lines), encodingutf-8 )逻辑说明转换后的每一行是“类别 id、中心点 x、中心点 y、宽、高”前四个坐标都已经除以图片宽高做了归一化所以不同分辨率图片可以混在一个批次里训练。参数说明classes 列表的下标就是训练配置里的类别 id这段代码里 drone 是 0、bird 是 1后面第 3 章的 drone.yaml 必须和这里保持一致否则类别错位会让你怀疑人生。另外两个边界是VOC 坐标是像素值YOLO 需要归一化别漏除如果一张图没有任何有效框这段代码不生成 txtultralytics 会把这张图当成背景样本行为正确。2.4 数据增强的取舍mosaic 对小目标不一定是加速器数据增强是训练阶段最容易“好心办坏事”的一环。YOLO 系列默认带 mosaic把四张图拼成一张好处是丰富背景、减少对大目标的过拟合但拼完后每张子图都缩小到原来的四分之一。对无人机这种本来就只有十几像素的目标mosaic 一拼目标直接变成三四像素标签框甚至比特征图上的一个格子还小模型学到的东西基本是噪声。我的经验是把 mosaic 调到 0.5同时把 close_mosaic 设成 20意思是最后 20 轮训练完全关闭 mosaic、只保留原图和轻微增强让模型在正常尺度下收尾。这个参数组合在好几个无人机检测项目里都比默认配置的 mAP 高两个点左右不是玄学是小目标尺度的数学事实。其他增强里有用的是 HSV 扰动无人机在不同天色下颜色变化很大HSV 增强能模拟傍晚和清晨的色偏。轻微仿射旋转也建议开量级控制在 5 度以内无人机在画面里很少大角度翻转。不建议开的是大幅随机裁剪裁剪很容易把目标切掉半边。3. 模型选型与训练YOLOv5、YOLOv8、YOLO11 怎么选、参数怎么调3.1 无人机检测难点的根源小目标特征少、运动模糊、背景复杂选模型之前先看清楚难点在哪里。普通目标检测面对的行人、车辆都有稳定的纹理和足够大的像素面积骨干网络下采样 32 倍后目标在特征图上仍然有几个像素。无人机不一样1080p 画面里一架 200 米外的四旋翼可能只有 15 个像素宽下采样之后特征图上连一个完整格子都占不满深层特征对这个小目标的响应几乎为零。这就是低慢小检测和小目标识别最核心的障碍。第二个难点是运动模糊。旋翼转速通常在一分钟几千转以上目标中速飞行时画面里的旋翼就是一团透明虚影轮廓边界又被压缩。很多模型能把机身框住但置信度只有 0.3 上下阈值一卡就漏掉。第三个难点是背景复杂。城市里的楼顶天线、山体边缘、云层纹理在滤波之后看起来都像“一个深色小块飘在天上”和远处的无人机非常相似。这三点叠加决定了纯粹堆模型参数量不是优先方案优先方案是提高输入分辨率、加浅层特征输出、控制增强策略。3.2 最小训练流程用 YOLOv8 跑通无人机检测数据准备齐了训练环节我一般直接用 ultralytics 的 YOLOv8 起步命令行工具成熟换数据路径就能跑。新项目不建议从零训练用官方在 COCO 上预训练过的 yolov8s.pt 做微调。先建环境安装依赖# 创建虚拟环境避免污染系统 Python conda create -n drone python3.10 -y conda activate drone # 官方包会带 CPU 版 torch训练前建议按自己显卡重装 CUDA 版 pip install ultralytics数据集目录按下面结构放images 和 labels 同级mkdir -p datasets/drone/images/train datasets/drone/images/val mkdir -p datasets/drone/labels/train datasets/drone/labels/val在项目根目录写 drone.yamlpath: datasets/drone # 相对当前命令运行的路径 train: images/train # 训练集图片目录 val: images/val # 验证集图片目录 names: 0: drone 1: bird然后执行训练yolo detect train modelyolov8s.pt datadrone.yaml \ epochs200 imgsz1280 batch16 device0 \ mosaic0.5 close_mosaic20 patience30逻辑说明这条命令做了三件关键的事。第一imgsz 直接提到 1280输入分辨率对低慢小目标的影响比换任何模型都大。第二mosaic 降到 0.5 并关闭尾段保护小目标标注不被拼图稀释。第三patience 设为 30验证集指标连续 30 轮不提升就早停。参数说明batch16 是我在 24G 显存显卡上的常用值如果你的卡只有 8Gbatch 降到 4、imgsz 降到 960 也能跑但小目标效果会打折。epochs 写 200实际配合早停一般跑到 80 到 120 轮就停了这很正常。3.3 模型大小怎么选n/s/m/l/x 不是越大越好很多人上来就贪大直接 yolov8x结果小数据集上过拟合得一塌糊涂训练时间还长。模型容量和训练数据量要匹配无人机检测这种几千到两三万张图的数据规模s 和 m 是甜点区。型号推理速度精度推荐场景n最快勉强够用嵌入式原型验证s快大多数项目够用安防项目首选m中等中上有 GPU 算力盒子的项目l / x慢最好离线分析、做教师模型蒸馏这里有一个容易被忽略的点YOLOv5 是 anchor-based训练时要检查预设 anchor 能不能覆盖小目标YOLOv8 和 YOLO11 是 anchor-free对小尺度变化更宽容这也是我推荐新项目直接上 v8 的原因。v5 也不是没有价值很多设备端 SDK 只适配了 v5 的 ONNX 输出格式如果硬件平台代码是现成的沿用 v5s 反而省事。3.4 网上流传的 yolov26 项目源码包为什么我劝你少碰搜索目标检测相关源码时经常能看到冠名“yolov26”的压缩包标题一个比一个响解压后才看出门道。我解压过几个流传较广的包发现它的训练脚本、模型 yaml、甚至日志输出都带着 v8/v11 的痕迹属于典型的“换皮打包”。不是说所有版本号都是假的而是这种打包项目真正值钱的往往只是收集好的数据集和后处理脚本版本号本身不产生精度。判断一个开源检测项目值不值得读我一般先看两个文件requirements.txt 和模型 yaml 配置。换皮的包连依赖都懒得更新yaml 里还是旧结构。更关键的是看它支不支持导出 ONNX、能不能复用官方 val 流程这两个能力决定了你落地时要重写多少东西。版本号可以追但别被版本号带着走。3.5 训练跑到一半该看什么loss 曲线和验证集指标训练不是敲完命令就等结果。每 10 轮左右打开 runs/detect/train 下的 results.png重点看两件事train/box_loss 是否在下降val/box_loss 是否拐头。如果训练 loss 一直降但 val loss 开始回升就是过拟合的明确信号不用等 200 轮直接停了改增强参数。小目标数据集的 box_loss 绝对值会比行人数据集高原因是坐标框小、损失对像素偏差更敏感看到 loss 降不到 0.0x 不必慌张。另一个要对照的是每轮验证的 PR 曲线曲线右下角面积越大越好。如果 mAP50 不错但 mAP50-95 很低说明框定位不准典型的坐标偏移或增强过头优先排查标注框是不是太松。提示训练中途改 imgsz 会导致之前的学习率曲线全部作废规格参数尽量训练前一次定死别中途手痒。4. 避坑无人机检测最常见的 5 类翻车现场4.1 把鸟当成无人机虚警刷屏现象白天晴天模型把远处的鸟、飘动的塑料袋框成无人机置信度还不低监控平台的告警窗口被刷爆。原因单帧静态画面里鸟和无人机的形状特征高度重叠尤其目标只有十几像素时算法看到的就是一个深色小斑块分类器被“深色加天空背景”这个组合骗了。鸟的扇翅动作在低帧率视频里根本体现不出来。解决必须把 bird 建成独立类别用包含不同姿态的鸟样本训练至少保证验证集里有 20% 鸟样本。部署时加一层时序投票下面第 6 章会专门讲。预算允许时加一路红外相机鸟的体温和环境温差远不如无人机电机发热明显双光融合能把这类虚警压到个位数。4.2 远处小目标漏检现象两百米外的无人机只有十五像素时训练集里明明有类似样本验证集 mAP 也有 0.9一到现场还是漏。原因验证集和训练集来自同一天同一地点分布太像指标虚高。训练时增强又把目标剪没输入分辨率不够毁了好几层特征。核心问题不是模型笨是数据分布和输入尺度没伺候好小目标。解决imgsz 从 640 提到 1280这是见效最大的一步。用 YOLOv8 官方的 P2 结构或者切图推理P2 会把高分辨率浅层特征引入检测头专门补小目标。独立做一套不同高度、不同距离、不同日期采集的测试集别用训练集同源数据验收。4.3 旋翼模糊导致置信度崩掉现象目标中速飞行时旋翼转成虚影检测框能框住目标但置信度只有 0.2输出阈值一卡就消失。原因模型没被教会“模糊的无人机也是无人机”。相机自动快门模式下光线稍差就会把旋翼拍成一团雾推理端碰到训练分布之外的外观直接没信心。解决训练数据增强里加运动模糊模糊核大小 3 到 7覆盖 20% 样本。采集端把快门设为 1/2000 以上宁可拉高 ISO 也要保证短曝光。推理置信度阈值从 0.5 降到 0.15 至 0.25但必须配合时序投票否则虚警同时上身。4.4 夜间和逆光直接躺平现象太阳落山后可见光置信度掉到 0.1 以下逆光时目标淹没在天空亮度里模型输出空框。原因可见光传感器依赖反射光夜间无人机不一定开灯逆光时目标和背景对比度接近零。单靠可见光模型这个场景无解。解决设备层面用热成像或双光融合无人机电池和电调工作时发热热像上目标明显。算法层面训练时加对比度拉伸和 gamma 变换模拟逆光压暗的效果。双光系统要小心另一个坑红外模型在正午会把太阳晒热的屋顶误报成目标所以需要可见光这一路做交叉确认。4.5 NMS 之后还是一堆重复框现象同一个目标叠了三四个框IoU 都在 0.6 以上框的坐标来回跳跟踪模块被带偏。原因小目标会在多个相邻特征点上同时激活默认 NMS 的 IoU 阈值 0.7 对这种密集小框不够狠。加上天空杂波局部响应偏高一个目标被输出成多个候选。解决后处理里把 NMS IoU 阈值降到 0.5ultralytics 导出 ONNX 后的推理代码要同步改。对每个目标按中心距离合并距离小于目标框宽度的候选只保留置信度最高那个。再加一个 5 帧的中值滤波坐标抖动会平滑很多。5. 部署验证从模型导出到边缘设备跑起来5.1 导出 ONNX、TensorRT、RKNN 的取舍训练完的 best.pt 只是半成品部署时要导出成对应平台的推理格式。三条路线各有各的坑。先在 PC 上导出 ONNX统一检查模型结构yolo export modelbest.pt formatonnx imgsz1280 opset12说明opset 用 12 是为了兼容大多数 arm 平台的推理库太新的 opset 在老旧 SDK 上会报不认识的操作符。imgsz 必须和训练时一致1280 的模型导出成 640 是个隐蔽的错误看起来能跑但输出特征图错位会直接导致 mAP 崩盘。NVIDIA 平台走 TensorRT在 Jetson 上执行trtexec --onnxbest.onnx --saveEnginebest_fp16.engine --fp16这是我做了三次部署后的血泪经验INT8 量化在低慢小目标上要非常谨慎。小目标本身的特征幅度就小量化损失把剩下的一点纹理也吃掉了我实测过 AP_small 掉一半的情况。FP16 基本无损优先 FP16。平台推理方式注意事项Jetson Orin 系列TensorRT FP16显存带宽决定帧率batch 1 即可RK3588 系列RKNN用 RKNN-Toolkit2 转量化后必须重新验证小目标x86 NVIDIATensorRT / ONNX开发验证最方便和 Jetson 共用一套代码5.2 最小推理代码把 ONNX 跑在视频帧上验证 ONNX 效果时我会写一个最短的推理脚本不依赖训练框架逻辑更透明。YOLOv8 的 ONNX 输出是一个三维张量需要自己拆解。import cv2 import numpy as np import onnxruntime as ort sess ort.InferenceSession(best.onnx, providers[CUDAExecutionProvider]) img cv2.imread(frame.jpg) # 注意这里做了简化实际部署要用和训练一致的 letterbox 保持长宽比 img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) input_tensor cv2.resize(img_rgb, (1280, 1280)) input_tensor input_tensor.astype(np.float32) / 255.0 input_tensor np.transpose(input_tensor, (2, 0, 1))[None] # NCHW outputs sess.run(None, {sess.get_inputs()[0].name: input_tensor}) # outputs[0].shape [1, 4num_classes, anchor总数]转置后逐行解析 preds outputs[0][0].T # (anchor总数, 4num_classes)逻辑说明ONNX 输出的每个候选行是“中心坐标、宽高、各类别置信度”解析时先做阈值过滤再做 NMS坐标系是 1280×1280 的映射回原图时乘上缩放比例还要加上 letterbox 的偏移否则框全部偏右下角。参数说明providers 里显式指定 CUDAExecutionProvider没 GPU 的机器改成 CPUExecutionProvider。生产环境建议用 C 写推理内存和延迟都可控python 只适合快速验证效果。批量验证视频也直接用官方命令生成带框视频肉眼过一遍比看数字更直观yolo predict modelbest.pt sourcetest_video.mp4 imgsz1280 save_txtTrue5.3 验证指标AP_small 比 mAP 更重要模型训练完先跑官方验证再决定要不要上线yolo detect val modelbest.pt datadrone.yaml imgsz1280输出结果里会有一张 per-class 指标表重点看 metrics/small 这一列。无人机检测领域AP_small 比 mAP50 重要得多mAP50 高但 AP_small 只有 0.3说明模型学的是近处大目标远处小目标照样丢。另一个要记录的是 PR 曲线的平衡点现场阈值就设在那里。现场验收我用一套内部口径供你参考指标参考线说明AP_small大于 0.6小目标不能丢误报率小于 1 次/小时含鸟、无人机以外干扰最远有效距离按业务定与采集距离强相关输出延迟小于 300ms含相机与后处理整链路这些数字不通用但方向是对的模型指标只是中间目标现场误报率和有效距离才是甲方真正验收的东西。模型在实验室 mAP 刷得再高现场被鸟刷屏一样算失败。6. 进阶一招用滑窗投票把单帧漏检补回来单帧检测做得再稳也扛不住强逆光、瞬时遮挡和微风引起的几帧模糊。给检测加一道时间维度是我做反无人机项目时性价比最高的一次改动。思路就一句话无人机不会瞬移真目标在一段时间里会反复出现在相近位置而鸟、云影和噪点虚警是断续的。维护一个长度为 5 帧的滑窗同一位置在至少 3 帧里都被检出才判定为有效目标。class VoteTracker: def __init__(self, win5, hit3, dist_scale1.5): self.win win self.hit hit self.frames [] def update(self, boxes): # boxes: 当前帧检测框 [(cx, cy, w, h, conf)] self.frames.append(boxes) if len(self.frames) self.win: self.frames.pop(0) ok [] for cx, cy, w, h, conf in self.frames[-1]: d w * dist_scale # 匹配距离按目标宽度缩放 votes sum( 1 for fr in self.frames if any(abs(x - cx) d and abs(y - cy) d for x, y, _, _, _ in fr) ) if votes self.hit: ok.append((cx, cy, w, h, conf)) return okwin 取 5 而不是更长是因为安防告警延迟要可控窗口越长误报越少但目标已经飞走一段距离。hit 取 3允许连续两帧漏检也能补回来。匹配距离按目标框宽度缩放近处目标位移大、远处位移小比固定像素更贴合投影关系。这个方法有两个边界两架无人机贴很近时会把它们合并成一个把 dist_scale 从 1.5 降到 1.2 能缓解目标快速机动导致相邻帧位移超过匹配距离时会断开那要靠卡尔曼跟踪补运动预测投票解决不了。我第一次做无人机识别时在 NMS 阈值和置信度阈值上折腾了三天虚警还是压不下去最后花半天写了这个几十行的投票世界清净了。那次之后我养成了一个习惯任何检测模型上线前先想清楚后处理能不能把单帧的不确定性兜住再想换不换网络。希望帮到你。本文还有配套的精品资源点击获取