简介:面向无人机视觉与高空目标检测场景的飞机目标检测数据集,适用于航空器识别、机场空域监测、无人机交通管理以及小目标检测算法研究。压缩包共1054个文件,包含526张jpg航拍图片、526个YOLO格式txt标注文件,另附1个yaml文件与1个docx说明文档,整体仅15.63MB。数据划分清晰:训练集479张、验证集31张、测试集16张,覆盖多种高度、角度、光照条件及云层、城市、旷野等不同背景,单张图片最多标注20余个飞机实例,适合密集目标检测训练。目前已有202人学习下载。资源即用性突出,标注格式可直接适配YOLOv5/v7/v8等主流检测框架,方便快速开展模型训练与效果验证;对于需要高空视角小目标样本或密集标注数据的开发者和研究人员,是一份轻量而实用的数据集。
1. 无人机视角飞机目标检测数据集:为什么通用检测模型会在这里集体失灵
无人机视角飞机目标检测数据集,听起来只是把普通飞机检测换成无人机拍摄,但真正跑过一次就会发现:在 COCO 上表现很好的通用检测模型,放到无人机拍机场的画面里会漏检得厉害。原因很直接——无人机视角下的飞机往往只占几十个像素,有俯冲、盘旋、停靠各种姿态,跑道、车辆和建筑还在不停干扰。
专门整理这类数据集,意义就在于让目标检测模型适应这种远距离、小尺度、背景杂乱的真实分布。它适合做机场智慧巡检、低空安防、无人机航测复核的人。这里从拿到包开始,重点聊检查、训练、避坑和验证。
2. 数据集的真实构成:从目录结构到标注格式的拆解
拿到一个无人机视角飞机目标检测数据集.zip,最忌讳的事是解压后直接丢进训练脚本。多数包内部其实是两种形态之一:已经转好的 YOLO 格式(images/与labels/同名 txt),或者给你 VOC/COCO 原始标注(xml/json)。先花十分钟把目录和标注格式摸清楚,能省后面两三个小时的排错。
2.1 先跑一遍配对检查:图片和标签缺一不可
解压后第一步是用命令看目录,再用脚本统计配对。这是每个数据集我都必跑的检查,先摸清目录层级,再确认图片与标注文件是否一一对应。
unzip -q 无人机视角飞机目标检测数据集.zip -d aircraft_dataset find aircraft_dataset -maxdepth 2 -type d | sort如果解压后出现images和labels两个目录,继续往下走;如果每个样本是单独文件夹,优先用find看层级。配对统计用 Python:
from pathlib import Path dataset = Path("aircraft_dataset") images = list(dataset.rglob("*.jpg")) + list(dataset.rglob("*.png")) labels = list(dataset.rglob("*.txt")) img_names = {p.stem for p in images} lab_names = {p.stem for p in labels} print("图片数:", len(img_names), "标签数:", len(lab_names)) print("有图无标签:", len(img_names - lab_names)) print("有标签无图:", len(lab_names - img_names))统计用的stem是去掉后缀后的文件名,这样一张DJI_0001.jpg能对应DJI_0001.txt。labels只匹配 txt,适合 YOLO 格式;如果包内是 VOC/COCO,txt 为 0 是正常的,需要用别的脚本检查 xml/json。配对检查的意义在于:YOLO 训练时图片缺标签会静默丢样本,标签缺图片则可能报错或产生空标签文件,这两种情况都不报错,但会让验证 mAP 悄悄失真。这里发现数量对不上,优先回到数据分割来源找原因,不要急着训练。
2.2 三种标注格式对比:YOLO 的 txt 为什么最省事
不同数据集会按不同习惯整理标注,最常遇到三种:
| 格式 | 文件形式 | 坐标表示 | 典型工具 | 是否适合直接训练 |
|---|---|---|---|---|
| YOLO | 每张图一个 .txt | 类别 id + 归一化中心 (x, y, w, h) | Ultralytics YOLO | 最省事 |
| VOC | 每张图一个 .xml | 像素左上/右下 (xmin, ymin, xmax, ymax) | mmdetection、detectron2 | 需转换 |
| COCO | 单个 annotations.json | 像素左上角 (x, y, w, h) + mask | 学术实验、mmdetection | 需转换 |
大多数无人机视角飞机检测数据集以 YOLO 或 VOC 形式发布。如果拿到的是 VOC,我一般会先用脚本转成 YOLO 格式再开始训练。转换脚本的关键部分如下:
import xml.etree.ElementTree as ET from pathlib import Path xml_dir = Path("annotations") out_dir = Path("labels") out_dir.mkdir(exist_ok=True) class_map = {"airplane": 0, "helicopter": 1} # 按实际类别调整 for xml_file in xml_dir.glob("*.xml"): tree = ET.parse(xml_file) root = tree.getroot() size = root.find("size") img_w, img_h = float(size.find("width").text), float(size.find("height").text) lines = [] for obj in root.iter("object"): cls = obj.find("name").text if cls not in class_map: continue box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) x = (x1 + x2) / 2.0 / img_w y = (y1 + y2) / 2.0 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h lines.append(f"{class_map[cls]} {x:.5f} {y:.5f} {w:.5f} {h:.5f}") out_path = out_dir / (xml_file.stem + ".txt") if lines: out_path.write_text("\n".join(lines))这段脚本的核心是坐标归一化:VOC 给的是像素坐标,YOLO 给的是相对原图的比例。转换时重点检查两处:第一,class_map是否覆盖数据集内全部类别,否则漏类会静默消失;第二,确保x2-x1和y2-y1为正,很多标注软件导出时会把左上右下写反。脚本跑完别急着训练,抽两张图把预测框画出来叠在原图上看看,确认坐标正反,再继续走。
2.3 先看类别平衡,再看目标尺度:这决定训练分辨率
数据集里每类有多少框、框在图片里一般多大,这些信息比类别名还有用。用 Python 统计 YOLO 标签:
import numpy as np from glob import glob box_w, box_h = [], [] for txt in glob("labels/**/*.txt", recursive=True): for line in open(txt): parts = line.strip().split() if len(parts) != 5: continue box_w.append(float(parts[3])) box_h.append(float(parts[4])) box_w = np.array(box_w) box_h = np.array(box_h) print("框宽占比 中位/90分位: {:.4f}/{:.4f}".format( np.median(box_w), np.percentile(box_w, 90))) print("框高占比 中位/90分位: {:.4f}/{:.4f}".format( np.median(box_h), np.percentile(box_h, 90)))如果中位数低于 0.03,也就是目标宽度不足原图 3%,按 1080p 算只有 30 像素左右,属于典型的移动小目标检测场景。这时训练分辨率别再用 640,优先用 1280 甚至原图直接训练。类别平衡则决定要不要做重采样:如果airplane有 1 万框,而某个稀有类别只有 50 框,那后者在训练里会被淹没,我一般会为稀有类别做复制增强或给大类降采样。遥感领域里常用 HRSC2016 做舰船检测,但那是水平大目标分布,无人机视角更接近移动小目标检测,别把普通数据集的缩放策略照搬过来。统计脚本遇到空标签文件会在np.median上报错,这正好提醒你先按第 5 章的清洗脚本把空标签清理或归类。
3. 用 YOLO 训练飞机检测:从解压到出模型的最小命令序列
数据集检查完,下一步是让训练跑起来。我习惯用 Ultralytics YOLO 系列,v8、v11 共用同一套yolo命令,环境配置和数据集 yaml 不用改。这节按“环境→数据划分→训练参数”三步走,每一步都可以直接抄。
3.1 环境配置:v8/v11 共用的 Ultralytics 训练命令
“目标检测:yolov11(ultralytics) 环境配置”是很多人卡住的地方,其实化成命令就三条:
conda create -n uav_aircraft python=3.10 -y conda activate uav_aircraft pip install ultralyticsPyTorch 建议单独装。如果机器已有可用的 CUDA,用 PyTorch 官方加速安装方式先装带 CUDA 的版本,回头再装 ultralytics,可以避免 ultralytics 装出一个只用 CPU 跑的 torch。装完后跑python -c "import torch; print(torch.cuda.is_available())",输出 True 再往下走。这里不写死版本号,因为 Ultralytics 的 v8 和 v11 在检测任务上的命令几乎一致,跑通训练后再考虑要不要升级。
3.2 数据划分与 dataset.yaml:让训练器认识你的目录
数据集目录通常是images与labels同级。先把它们按约定拆成 train/val 两个子集。为了复现稳定,我不用随机 seed,而用文件名哈希划分:
import hashlib, shutil from pathlib import Path images = Path("images") labels = Path("labels") img_train = Path("images/train"); img_val = Path("images/val") lbl_train = Path("labels/train"); lbl_val = Path("labels/val") for img_dir, lbl_dir in [(img_train, lbl_train), (img_val, lbl_val)]: img_dir.mkdir(parents=True, exist_ok=True) lbl_dir.mkdir(parents=True, exist_ok=True) for img in list(images.glob("*.jpg")) + list(images.glob("*.png")): lbl = labels / (img.stem + ".txt") if not lbl.exists(): print("缺少标签:", img.name) continue h = hashlib.md5(img.stem.encode()).hexdigest()[0] dest_img = img_train if h < "8" else img_val dest_lbl = lbl_train if h < "8" else lbl_val shutil.copy(img, dest_img) shutil.copy(lbl, dest_lbl)哈希划分的优点是:只要文件名不变,每次划分结果完全一致,训练、复现、回头再查都方便。缺点在于它不能按视频片段隔离,如果你知道哪些帧来自同一段视频,优先用第 5 章的视频ID分组方案。写完划分脚本后,在数据集根目录创建aircraft.yaml:
path: /绝对路径/aircraft_dataset train: images/train val: images/val names: 0: airplane 1: helicopter如果数据集中只有一类飞机,names只写一行。Ultralytics 优先读 yaml 里的 names,不会去看 labels 里的 names 或 classes.txt,所以这是唯一直接影响训练类别数的文件。写错类别数最常见的报错是“标签类别超过 names 长度”,遇到就回这里查。
3.3 训练命令与关键参数:imgsz、batch、epochs、workers 怎么给
最小训练命令长这样:
cd aircraft_dataset yolo task=detect mode=train model=yolov8s.pt \ data=aircraft.yaml \ imgsz=1280 batch=16 epochs=100 workers=8 \ patience=20 save_period=10几个参数按项目调整:imgsz=1280对无人机飞机目标检测几乎是必选项。前面统计过尺度占比之后,如果小目标多,用 640 会把飞机缩成 5-8 像素,模型根本学不到特征。显存不够时,先保 imgsz,把 batch 降到 8 或 4。batch=16是 24G 显存左右的经验值,OOM 表现是训练中断,把 batch 和 imgsz 任选一个降下来即可。epochs=100对几千张图片的数据级够用,数据量小可以降到 60,但别低于 50;数据集很大可以跑到 200,配合patience=20早停。workers=8是 Linux 常规值,Windows 上建议改成 4 或 2,否则经常遇到 dataloader 报错。
如果想用 YOLOv11,把model=yolov8s.pt换成model=yolo11s.pt,其他参数位置不变。训练中断后,用yolo resume可以继续训练。训练完成后会自动执行验证流程,runs/detect/train/weights/best.pt是验证集上指标最好的权重;除非你明确想用最后一次续跑结果,否则部署和落地一律用best.pt。
4. 无人机视角飞机检测的 5 个经典翻车点:现象、原因与排查清单
这一章是血泪经验集中地。无人机视角和普通行车记录仪视野有一个本质区别:相机离目标远、角度高、目标小,造成很多在常规目标检测里不是问题的问题。如果你训练时发现 loss 一路下降但验证效果很差,或者推理视频时出现诡异误检,按下面几个点逐一排查,大多数情况能在半天内定位。
4.1 漏检率高:小目标被 imgsz=640 抹掉了
现象:训练曲线正常,但推理时距离稍远的飞机全部漏检,验证集 recall 也很低。
原因:无人机视角下很多飞机的标注框占原图不到 3%,而默认imgsz=640会把原图缩到一半或更小,目标变成只有十几个像素。网络下采样后目标特征直接消失。
解决:把训练和推理的 imgsz 提到 1280,如果显卡不够,先减小 batch;推理端同样用imgsz=1280,不要只改训练不改验证。另一个常用做法是切片推理(SAHI),把大图切成 640 或 960 的子图,让模型在子图尺度上检测后再拼回结果。
yolo predict model=best.pt source=test.jpg imgsz=1280 conf=0.25这条命令里的conf=0.25先按默认走,具体阈值看 4.6 的调法。
4.2 斜着停的飞机框不准:水平框的固有限制
现象:机头朝前的检测还可以,但跑道上斜停的飞机,预测框和真值 IOU 很低,看起来框多了一大块背景。
原因:VOC/YOLO 默认用轴对齐水平框,只能表达矩形区域,不能表达朝向。斜停的飞机水平框自然的包含大量地面背景,训练时模型学到的是“一个横向拉长的框”,不能区分机头方向。
解决:如果数据集准备长期用,先检查原始标注里有没有角度向量。如果原始标注是polygon或rotated_rect,但转成 YOLO 时把角度丢了,可以用 mmrotate 或 Ultralytics 的 OBB 模式,把框定义成(cx, cy, w, h, angle)。如果原始只有水平框,就不要硬上旋转框模型,而是把评估指标从严格 IOU 放宽成中心点命中率:预测框中心落入真值框内即算命中,这种评估方式对地勤场景更实际。
4.3 训练集和验证集永远是亲戚:mAP 虚高
现象:训练时验证 mAP 到了 0.9,但拿着模型去测一段全新无人机视频,漏检和误检非常严重,和验证结果完全不符。
原因:很多人把连续视频转帧后随机划分 train/val,相邻帧画面一样,模型在验证时看见的其实是“训练集邻居”。模型记忆背景纹理就能拿高分,没有真正学习飞机特征。
解决:按视频片段划分数据。从文件名里抽出视频 ID(比如DJI_20240101_001_001.jpg取前三个字段),保证同一个视频的所有帧只出现在 train 或只出现在 val。第 5 章会给一个按视频前缀划分的脚本。如果数据集的文档没说怎么分,至少要把“连续编号连续帧”的先挑出来放验证集,再随机补全。
4.4 误检一堆机场车辆和建筑:模型把特征学成了轮廓
现象:验证集 precision 低,输出结果里经常出现机场大巴、塔台、地面标志线被框成飞机。
原因:飞机在俯视画面中的特征是矩形轮廓和纹理,而地面车辆、建筑也有类似边缘结构。如果训练负样本不足,模型用一个粗轮廓区分正负类,自然会把大型轮廓当作飞机。
解决:收集一批不含飞机的无人机机场场景图做负样本,放到 train 目录里给空标签文件,或者作为背景图像。在训练时把scale和translate增强打开,让模型见过飞机在不同尺寸和位置的情况,弱化对固定位置的依赖。如果误检还是集中在车辆,可以在后处理逻辑中按目标长宽比过滤,比如飞机宽度与高度比例通常不会超过 3,长条目标直接丢弃。
4.5 白天训练完,逆光傍晚视频全挂
现象:白天验证效果很好,一到傍晚或逆光场景,recall 直接掉一半。通常数据集拍摄时段集中在上午 9 点到下午 3 点。
原因:无人机视角的光照变化比地面更大。太阳位置低时,飞机背光面与地面颜色接近,模型没有见过这种光照,目标边缘特征大幅减弱。
解决:在训练增强里加入光照扰动,命令行末尾追加:
yolo task=detect mode=train model=yolov8s.pt data=aircraft.yaml \ imgsz=1280 batch=16 epochs=100 workers=8 \ hsv_h=0.015 hsv_s=0.4 hsv_v=0.4hsv_v调高一点相当于模拟不同亮度,hsv_s模拟色彩饱和度下降。也可以在数据集里补 5%~10% 的阴天、黄昏、逆光样本,不要只依赖增强。如果项目上线时段明确是清晨傍晚,建议训练时就用混合光照,而不是等模型上线后才发现没覆盖。
4.6 排查顺序:先看失败图,再跑 PR 曲线
遇到验证分数和直觉不符,我一般按“先数据后模型”的顺序排查:第一,确认 train/val 没有视频帧串(4.3);第二,在验证集上用best.pt跑一次,把置信度在 0.3 以下但仍命中的框、以及置信度高于 0.9 却框错的目标导出来看图;第三,按 4.1~4.5 归类现象。看失败图这一步最花时间但最值得,它往往比调一次学习率更能说明问题。如果你换一套数据后指标崩了,优先怀疑 4.2 和 4.3,而不是模型结构。
5. 数据清洗与增强:把样本少、框不准的短板补上
无人机视角数据集不像 COCO 那样经过严格清洗,尤其从视频转帧的包,常常带着越界框、空标签和因抖动产生的错误标注。这些脏数据不清理,训练再久都在不干净的地基上跑。我习惯在每次训练前做一遍自动筛查,再决定增强策略。
5.1 自动筛查标注异常:越界、空标签、类别错配
写一个能扫全量标签的脚本,放在数据集根目录直接跑。假设标签是 YOLO 格式:
from pathlib import Path for label_dir in [Path("labels/train"), Path("labels/val")]: if not label_dir.exists(): continue for txt in sorted(label_dir.glob("*.txt")): lines = txt.read_text().strip().splitlines() if not lines: print(f"空标签: {txt}") continue for idx, line in enumerate(lines): parts = line.split() if len(parts) != 5: print(f"列数错误 {txt} 第{idx+1}行: {line}") continue cls = int(parts[0]) cx, cy, w, h = map(float, parts[1:]) if w <= 0 or h <= 0: print(f"尺寸异常 {txt} 第{idx+1}行: {line}") if cx - w/2 < 0 or cx + w/2 > 1 or cy - h/2 < 0 or cy + h/2 > 1: print(f"越界 {txt} 第{idx+1}行: {line}")这段脚本只做告警,不做自动删除。越界框可能是标注时手动加了外扩,直接删会丢目标;正确做法是先看告警数量。如果只有几十个,手工检查;如果有几百个,多半是标注工具导出时坐标系写错,回头检查 VOC 转 YOLO 脚本的系数。空标签对应的图片如果是纯地面背景,建议保留作为负样本;如果是漏标,需要补标。一张图完全没有飞机但带空标签文件,在 YOLO 训练里是合法负样本,我一般不会删。
5.2 按视频片段划分数据:避免相邻帧串组
4.3 提过的问题,这里直接给脚本。假设文件名风格是DJI_20240101_001_001.jpg,前三个字段代表“机型+日期+片段号”:
from collections import defaultdict from pathlib import Path img_dir = Path("images") groups = defaultdict(list) for img in list(img_dir.glob("*.jpg")) + list(img_dir.glob("*.png")): prefix = "_".join(img.stem.split("_")[:3]) # DJI_20240101_001 groups[prefix].append(img) items = sorted(groups.items()) cut = int(len(items) * 0.8) train_groups = {k for k, _ in items[:cut]} val_groups = {k for k, _ in items[cut:]} print("片段数:", len(items), "训练片段:", len(train_groups), "验证片段:", len(val_groups))得到片段前缀后,把每张图连同标签按前缀搬进 train/val 目录即可。关键点在于:哪怕有些片段只有几十帧,也要整段划分,而不是把片段内部分帧放训练、部分留验证。因为无人机视频相邻帧之间目标位置变化很小,模型只要记住上一帧就赢了。如果文件名没有统一前缀,可以按帧的秒数或者拍摄时间戳聚类;如果都没有,只能用第 3 章的 MD5 哈希划分,但不要指望它消除视频帧泄漏。
5.3 增强策略:不是所有增强都适合小目标
Ultralytics 的增强默认在超参里已有基础值,但针对无人机小目标,几个参数值得单独调。常见做法是训练命令覆盖:
yolo task=detect mode=train model=yolov8s.pt data=aircraft.yaml \ imgsz=1280 batch=16 epochs=100 workers=8 \ mosaic=0.6 hsv_v=0.3 flipud=0.2 scale=0.5 translate=0.1参数说明:mosaic=0.6对中大型目标明显,但对小于 20 像素的目标,拼接后可能被裁剪掉一半。如果目标是极小,建议把 mosaic 降到 0.5 或 0.6,给模型看到完整小目标的机会更多。flipud=0.2允许上下翻转,对无人机视角有意义:同一架飞机在画面上可能机头朝上或朝下,翻转可以模拟不同航向。但如果你数据里飞机带阴影,上下翻转后阴影方向改变,需要观察是否引入混淆。hsv_v=0.3模拟不同亮度,是应对 4.5 逆光问题的关键。scale=0.5让目标缩放范围更大,防止模型只见过固定尺寸的飞机。
除了这些内置增强,如果样本量特别小,可以试试复制粘贴增强:把不同图片中的飞机实例抠出来,以随机位置贴到背景图上,同时自动生成 YOLO 标签。这对无人机俯视图效果好,因为飞机实例相对独立、旋转角度自由,但要注意不要贴到不可能出现飞机的位置。复制粘贴增强的关键在于保证贴图后阴影和光照协调,否则模型的泛化收益会被伪影抵消。
6. 用最佳置信度和失败样本判定模型能否上线:一个交付前检查习惯
真正决定这个数据集值不值得被你留下继续投入的,不是训练 loss 也不是验证 mAP,而是验证集上的 PR 曲线和失败样本分布。训练结束后我会先不看 mAP,而是做两件事:第一找出 F1 最大时对应的置信度阈值;第二把所有“置信度高但预测错”的样本拼成一张大图扫一遍。
Ultralytics 训练结束后会在runs/detect/train/留下results.csv和val_batch_pred.jpg。部署之前,我会把验证集上每个置信度阈值下的 precision 和 recall 拉出来,找到最佳 F1。更关键的是观察不同阈值下 recall 的突变点:如果置信度从 0.1 提到 0.3,recall 掉了 10%,说明大量漏检来自模型本身没有自信,而不是阈值挑剔;这时加增强或调数据权重比调阈值有效。如果 PR 曲线在高置信度区间的回退很陡,则说明模型对某些样本过于自信,需要重点看失败图。
真正让我养成这个习惯的是一次翻车:那时项目时间紧,验证集 mAP 0.93,我直接按 0.5 置信度上线,结果傍晚逆光视频几乎全漏。后来排查发现验证集是白天拍的,PR 曲线在低置信度区间一路平坦,我根本没意识到模型在低光照下完全没有信心。从那以后,每次交付前我都会跑一遍失败样本图,并顺手把验证集按“光照/背景/目标大小”分层各抽样 100 张,分别看准确率。这一步比多训练 20 轮更值。
希望这个例子能帮你在拿到无人机视角飞机目标检测数据集后,少走一次“mAP 漂亮但现场翻车”的路。
本文还有配套的精品资源,点击获取