☰
无人机航拍数据集实战:从解压到YOLO检测与ByteTrack跟踪全流程
2026/10/2 3:27:13 网站建设 项目流程

简介:这份drone-AI_make.zip是面向计算机视觉初学者与算法工程师的无人机目标检测与跟踪数据集,可用于YOLO系列模型训练及deepsort多目标跟踪实验,覆盖安防监控、航拍巡检等场景。压缩包共10113个文件,约144.55MB,其中3371张jpg图像记录无人机在不同尺度、角度与环境下的形态,3371个txt提供YOLO格式边界框坐标,3371个xml给出更详细的对象位置与类别信息,两种标注可直接对接主流检测框架。目前已有2576人学习下载,样本涵盖大中小多种尺度,有助于提升模型泛化能力与复杂场景适应性。读者可据此完成从数据解析、模型训练到跟踪验证的完整流程,快速搭建无人机识别与轨迹追踪的基线系统。

1. 拆开 drone-AI_make.zip:无人机数据集到底能喂给哪些模型

拿到drone-AI_make.zip这个包名,第一反应不该是「解压看看」,而是先判断它属于哪一类无人机数据集。无人机视觉这个方向,数据集大致分三种:纯航拍图像做目标检测、带时序的视频帧做目标跟踪、以及多传感器融合(可见光+红外+IMU)做感知。从标题里「目标检测和跟踪」两个任务并列来看,这个包大概率是图像帧序列加标注文件的结构,标注格式可能是 VOC XML、COCO JSON 或 YOLO txt 中的一种。

它解决的核心问题是:你手头没有真实无人机航拍数据,又想跑通一套检测+跟踪的流程。适合两类人——一类是想验证 YOLO 系列模型在航拍视角下表现的算法工程师,另一类是要做无人机视觉感知原型、但预算不够买商业数据集的学生或小团队。航拍视角和地面视角的差异比想象中大:目标尺度小、背景杂、俯仰角变化剧烈,拿 COCO 预训练权重直接推理,mAP 经常掉一大截。所以这个数据集的价值不在于「有多少张图」,而在于它是不是真实航拍视角、标注是不是覆盖了小目标。

我一般拿到这类包,先做三件事:看目录结构判断标注格式、统计类别分布判断是否长尾、抽几帧看分辨率和对齐情况。这三步决定了后面能不能直接喂给 YOLO,还是得先做一轮清洗和格式转换。

2. 先搞清楚包里的结构:目录、标注格式与类别分布

2.1 解压后先看目录树,别急着写训练脚本

拿到压缩包,第一步永远是看结构。不同标注格式对应的目录组织完全不同,搞错了后面转换脚本全白写。

# 解压到独立目录,避免污染当前工作区 unzip drone-AI_make.zip -d drone-AI_make # 看两层目录树,判断是 images/labels 分离还是混放 find drone-AI_make -maxdepth 2 -type d | head -30 # 统计文件类型分布,快速判断标注格式 find drone-AI_make -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn

如果输出里.xml占多数,基本是 VOC 格式;.json且只有一个大文件,多半是 COCO;.txt和图片一一对应且每行五个数,就是 YOLO 格式。这一步花两分钟,能省掉后面半小时的调试。

2.2 用脚本统计类别分布和框的尺度

航拍数据集最怕类别极度不均衡,或者框小到模型根本学不到。写个统计脚本,把这两件事一次看清。

import os, glob from collections import Counter import xml.etree.ElementTree as ET # 假设是 VOC,其他格式换解析器 ann_dir = "drone-AI_make/annotations" cls_counter = Counter() area_bins = Counter() for xml_path in glob.glob(os.path.join(ann_dir, "*.xml")): tree = ET.parse(xml_path) root = tree.getroot() for obj in root.findall("object"): name = obj.find("name").text cls_counter[name] += 1 bbox = obj.find("bndbox") w = float(bbox.find("xmax").text) - float(bbox.find("xmin").text) h = float(bbox.find("ymax").text) - float(bbox.find("ymin").text) area = w * h # 按面积分箱,看小目标占比 if area < 32*32: area_bins["small"] += 1 elif area < 96*96: area_bins["medium"] += 1 else: area_bins["large"] += 1 print("类别分布:", cls_counter.most_common()) print("尺度分布:", area_bins)

area的分箱阈值参考 COCO 定义:32×32 以下算小目标,96×96 以上算大目标。航拍数据里 small 占比超过 60% 是常态,这意味着你后面选模型时,输入分辨率不能压得太狠,否则小目标直接消失。类别分布如果出现某个类占 80% 以上,训练时要么做重采样,要么在 loss 里加类别权重。

2.3 判断能不能直接用于跟踪任务

检测和跟踪对数据的要求不一样。跟踪需要同一目标在连续帧里有稳定 ID,如果这个包只有单帧标注、没有帧间关联信息,那它只能做检测,跟踪得靠 SORT、ByteTrack 这类算法在推理阶段自己关联。

# 看文件名是否带帧序号,判断有没有时序结构 ls drone-AI_make/images | head -20 # 如果文件名是 000001.jpg 000002.jpg 这种连续编号,可能有序列 # 如果是随机哈希名,基本就是独立帧

文件名连续编号不代表有跟踪标注,但至少说明采集时有序列关系。真正做跟踪,你需要的是每帧里同一目标的 ID 一致,这个信息通常在单独的 gt.txt 或 MOT 格式文件里。如果包里没有,就老老实实把它当检测数据集用,跟踪部分用检测结果加卡尔曼滤波自己搭。

3. 转成 YOLO 格式:转换脚本、划分策略与三个必调参数

3.1 VOC 转 YOLO 的完整脚本

假设上一步确认是 VOC 格式,接下来转成 YOLO 的 txt。YOLO 格式每行是class_id cx cy w h,全部归一化到 0~1。

import os, glob import xml.etree.ElementTree as ET classes = ["drone", "bird", "aircraft"] # 按你实际的类别顺序改 cls_to_id = {c: i for i, c in enumerate(classes)} def convert(xml_path, out_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in cls_to_id: continue # 跳过不在类别表里的标注 bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 归一化并转中心点格式 cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h # 裁剪到 [0,1],防止标注越界导致训练报错 cx, cy = min(max(cx, 0), 1), min(max(cy, 0), 1) w, h = min(max(w, 0), 1), min(max(h, 0), 1) lines.append(f"{cls_to_id[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines)) # 批量转换,图片尺寸从 XML 的 size 节点读,别硬编码 for xml_path in glob.glob("drone-AI_make/annotations/*.xml"): tree = ET.parse(xml_path) size = tree.getroot().find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) stem = os.path.splitext(os.path.basename(xml_path))[0] convert(xml_path, f"drone-AI_make/labels/{stem}.txt", img_w, img_h)

三个关键点:类别表顺序必须和后面训练配置里的names完全一致,错一位所有标签全乱;归一化前一定要读 XML 里的真实宽高,不能假设所有图同尺寸;裁剪到 [0,1] 是防越界标注,航拍数据里框超出边界的比例不低。

3.2 训练集/验证集划分:别用随机划分

航拍数据如果来自连续视频帧,随机划分会导致相邻帧同时出现在训练集和验证集,验证指标虚高。正确做法是按序列划分,或者按时间切分。

import os, random, shutil random.seed(42) # 固定种子,保证可复现 img_dir = "drone-AI_make/images" lbl_dir = "drone-AI_make/labels" stems = [os.path.splitext(f)[0] for f in os.listdir(img_dir)] stems.sort() # 按文件名排序,保持序列顺序 # 按 8:2 切分,前 80% 训练,后 20% 验证 split = int(len(stems) * 0.8) train_stems, val_stems = stems[:split], stems[split:] for subset, items in [("train", train_stems), ("val", val_stems)]: os.makedirs(f"dataset/images/{subset}", exist_ok=True) os.makedirs(f"dataset/labels/{subset}", exist_ok=True) for s in items: shutil.copy(f"{img_dir}/{s}.jpg", f"dataset/images/{subset}/{s}.jpg") shutil.copy(f"{lbl_dir}/{s}.txt", f"dataset/labels/{subset}/{s}.txt")

如果文件名本身没有时序含义,那就随机打乱再切,但种子要固定。切完之后检查一下两个子集的类别分布是否接近,差太多说明划分有问题。

3.3 三个必调参数:imgsz、batch、mosaic

转到 YOLO 之后,训练配置里有三个参数直接决定航拍小目标能不能学出来。

参数建议值原因
imgsz1024 或 1280航拍小目标多,640 下采样后目标只剩几个像素
batch8~16高分辨率下显存吃紧,batch 太大直接 OOM
mosaic1.0 起步,后期关增强小目标召回,但最后 10 epoch 关掉提升精度

imgsz是最容易被忽视的。很多人拿默认 640 跑航拍数据,mAP 死活上不去,换成 1280 直接涨十几个点。代价是显存和推理时间翻倍,但检测任务里这个 trade-off 通常值得。mosaic在训练后期关掉(close_mosaic=10),能让模型适应真实分布,这是 YOLO 系列里比较稳的一个技巧。

4. 检测跑通之后接跟踪:ByteTrack 与航拍场景的适配

4.1 检测输出怎么喂给跟踪器

跟踪不是重新训一个模型,而是在检测框基础上做帧间关联。以 ByteTrack 为例,它吃的是每帧的检测结果(框+置信度+类别),输出带 track_id 的框。

from ultralytics import YOLO import numpy as np model = YOLO("runs/detect/train/weights/best.pt") # 对视频或帧序列逐帧推理 results = model.predict(source="drone-AI_make/video_frames", imgsz=1280, conf=0.3) # results 里每帧的 boxes 可以直接喂给跟踪器 # ByteTrack 的核心逻辑:高分框先关联,低分框补漏 for r in results: boxes = r.boxes.xyxy.cpu().numpy() scores = r.boxes.conf.cpu().numpy() # 这里接 ByteTrack 的 update 接口

conf=0.3是跟踪场景的常用阈值,比纯检测低一些。原因是跟踪器有帧间关联能力,低分框里可能藏着被遮挡的目标,丢掉就断了轨迹。但也不能太低,否则误检框会污染轨迹。

4.2 航拍跟踪的三个特有难点

第一是尺度突变。无人机俯冲或拉升时,同一目标在两帧之间尺度可能差一倍,IOU 关联直接失效。缓解办法是用中心点距离做辅助匹配,或者上 ReID 特征。

第二是背景运动。无人机自身在动,背景整体位移,光流法会把背景当成运动目标。这时候要么做全局运动补偿,要么用检测框做关联、不依赖光流。

第三是目标出画再入画。航拍视角下目标经常飞出画面又飞回来,跟踪器需要处理 ID 切换。ByteTrack 本身不做 ReID,长时遮挡后 ID 大概率会变,如果业务对 ID 稳定性要求高,得加一个 ReID 模块。

4.3 用 MOT 指标验证跟踪质量

跟踪跑通不等于跑对,得用指标量化。MOTA、IDF1、ID Switch 是三个核心指标。

# 假设你有 MOT 格式的 gt 和预测结果 # 用 motmetrics 或 py-motmetrics 计算 import motmetrics as mm acc = mm.MOTAccumulator(auto_id=True) # 逐帧 update,传入 gt 框、预测框、距离矩阵 # 最后 compute 出 MOTA / IDF1 / IDS

航拍场景下 IDF1 比 MOTA 更能反映 ID 保持能力。如果 IDF1 低于 0.5,说明 ID 切换太频繁,得回头查关联阈值或者加 ReID。别只看 MOTA,它被检测精度主导,跟踪问题会被掩盖。

5. 避坑记录:从解压到训练踩过的五个坑

5.1 标注越界导致训练 loss 变 NaN

现象:训练几个 epoch 后 loss 突然变 NaN,或者框回归 loss 异常大。 原因:VOC 标注里框的坐标超出图片边界,归一化后 cx/cy/w/h 出现负值或大于 1。 解决:转换脚本里加裁剪,cx = min(max(cx, 0), 1),同时检查 w/h 是否大于 0。转换完抽样验证几个 txt,确认所有值都在 [0,1]。

5.2 类别 ID 从 1 开始导致全部错位

现象:训练能跑,但推理时类别全错,明明是无人机标成鸟。 原因:VOC 里类别名从 1 开始编号,YOLO 要求从 0 开始,转换时没减一。 解决:用cls_to_id字典显式映射,别依赖 XML 里的顺序。转换后打印前几行 txt,人工核对类别 ID。

5.3 图片和标签文件名不匹配

现象:训练时提示找不到标签,或者大量图片被跳过。 原因:图片是.jpg,标签是.txt,但文件名前缀不一致(一个有后缀一个没有,或者大小写不同)。 解决:转换时统一用os.path.splitext取 stem,生成标签时用同一个 stem。转换完做一次集合比对,找出没有对应标签的图片。

5.4 高分辨率下 dataloader 成瓶颈

现象:GPU 利用率只有 30%,训练速度远低于预期。 原因:imgsz 设到 1280 后,数据增强和 CPU 解码跟不上,GPU 在等数据。 解决:开cache=True把图片缓存到内存(数据集不大时),或者增加workers数量。如果内存不够,用cache='disk'做磁盘缓存。另外 mosaic 增强本身耗 CPU,可以适当降低 mosaic 概率。

5.5 验证集指标虚高

现象:验证 mAP 很高,但实际部署效果差。 原因:连续帧随机划分,训练集和验证集有高度相似的帧,模型在「背答案」。 解决:按序列或时间切分,确保验证集的场景和训练集不重叠。切分后可以算一下训练集和验证集的图片相似度,如果大量帧的直方图几乎一样,说明划分有问题。

6. 把检测和跟踪串成一条可复现的验证流水线

前面几章拆开讲了数据、转换、训练、跟踪,这一章把它们串成一条能反复跑的流水线,顺便说一个我常用的验证技巧。

流水线的顺序是:解压 → 结构检查 → 格式转换 → 划分 → 训练 → 推理 → 跟踪 → 指标计算。每一步的产物都落盘,方便回滚。我一般会写一个run.sh把命令串起来,但每个阶段单独可执行,出问题不用从头跑。

#!/bin/bash set -e # 任何一步失败就停,别带着错误往下跑 python scripts/check_structure.py --root drone-AI_make python scripts/voc2yolo.py --ann drone-AI_make/annotations --out drone-AI_make/labels python scripts/split_dataset.py --seed 42 --ratio 0.8 yolo detect train data=drone.yaml model=yolov8n.pt imgsz=1280 batch=8 epochs=100 close_mosaic=10 yolo detect predict model=runs/detect/train/weights/best.pt source=drone-AI_make/video_frames imgsz=1280 conf=0.3 save_txt=True python scripts/track_bytetrack.py --det_dir runs/detect/predict/labels --out track_results.txt python scripts/eval_mot.py --gt drone-AI_make/gt.txt --pred track_results.txt

set -e是血泪经验,不加的话中间某步失败,后面拿空文件继续跑,最后指标全是 0 还找不到原因。

验证技巧方面,我习惯在训练前先做一次「过拟合测试」:拿 10 张图,关掉所有增强,训 50 个 epoch,看模型能不能把 loss 降到接近 0。如果连 10 张图都过拟合不了,说明数据管道有问题——大概率是标签格式错了或者类别映射错了。这个测试花不了几分钟,但能挡掉一大半低级错误。

另一个习惯是每次改完数据管道,先跑一遍推理可视化,把预测框画到图上存下来。数字指标会骗人,但框画出来对不对,一眼就能看出来。航拍小目标如果框飘得厉害,先别调模型,回头查标注质量和输入分辨率。

这套流程跑通之后,drone-AI_make.zip就不只是一个数据集,而是一个可以反复迭代的基线。换模型、换跟踪器、加 ReID,都在这个基线上做对比,每次只改一个变量,指标才有可比性。我踩过最大的坑就是一次改好几个地方,结果指标涨了不知道是谁的功劳,跌了也不知道该回滚哪个。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询