简介:YOLO无人机目标检测数据集提供1000张真实航拍场景下的高质量图片,全部经过LabelImg精细标注,框选质量可靠,并同步生成VOC(xml)、COCO(json)与YOLO(txt)三种主流格式标签,分别存放在独立目录中,可无缝接入YOLOv5、YOLOv8等系列模型训练与评测流程。整套压缩包共2000个文件,除图片与三种标签外,还包含990个txt格式的标签索引文件、Python划分脚本、YAML配置模板及HTML格式的环境搭建与训练教程,资源总体积约121.2MB。这些脚本覆盖训练集/验证集/测试集划分、ImageSets下txt生成等常见需求,支持按个人数据规模灵活调整;配套教程详细展示了Linux和Windows环境下YOLO的搭建、训练以及替换为自己的数据集进行修改的完整过程,特别适合目标检测入门者、无人机视觉方向学生以及需要快速搭建实验环境的开发者。目前已有654人学习下载,体系完整、目录清晰,既可直接用于模型训练,也可作为课程设计、毕业设计的数据与操作参照,省去大量数据采集和标注时间。
1. YOLO无人机目标检测数据集:先把这个包当成一条数据流水线来验收
标题里这包「YOLO无人机目标检测数据集」,一句话说,就是给无人机视角目标检测准备的:1000 张图片,同时配好 VOC、COCO、YOLO 三种格式标签,外加划分脚本和训练教程。很多人拿到手就解压开训,结果卡在第一步——标签格式和训练框架对不上,白费一下午。
我建议先别急着训,把这个包当成一条完整的数据流水线来验收。它适合做毕业设计、打比赛,或者想用 YOLOv8 训练自己的数据集但缺数据的从业者,能帮你跳过数据标注和格式转换两个最枯燥的环节。前提是搞清三套标签怎么用、划分脚本怎么执行、类别编号怎么对齐,这正是这篇笔记要按步骤拆开的部分。
2. 从压缩包到可训练状态:把 VOC、COCO、YOLO 三套标签的差异一次讲清
解压完成后别急着看图片,先看目录结构。这类数据集包通常是 images(有时叫 JPEGImages)目录装着 1000 张 jpg,旁边按格式分目录放标签:VOC 是 XML、COCO 是 JSON、YOLO 是 txt,外加一个划分脚本和一份训练教程。三套标签描述的是同一批目标,只是存储方式和坐标写法不同。把这三套的差异在动手前讲清楚,后面所有操作都会省力很多。
2.1 VOC、COCO、YOLO 的坐标体系与文件组织:一张表看懂本质差异
VOC 格式最容易认:每张图片对应一个同名 XML 文件。XML 里<size>记录图片宽高,<object>段里<name>写类别名,比如 car、person,<bndbox>里 xmin、ymin、xmax、ymax 是像素坐标,以图片左上角为原点。它的好处是人工可读性好,坏处是一个目标要写一大段标签,文件冗余,解析速度也比纯文本慢。
COCO 格式是另一个极端:整个数据集打包成一个 JSON 文件。images 数组存每张图的 id、file_name、width、height;annotations 数组存每个目标的 image_id、category_id、bbox 和 area;categories 数组定义类别 id 和 name。bbox 写成 [x, y, w, h],x、y 是左上角像素坐标。这里有个容易被忽略的细节:COCO 的 category_id 从 1 开始,而且允许跳号,比如三个类别 id 可能是 1、3、5,而不是 1、2、3。这个细节是后面转 YOLO 格式时类别错位的主要来源。
YOLO 格式最简洁:每张图片对应一个同名 txt 文件,每行一个目标,写成 class_id cx cy w h 五个数字。cx、cy 是目标中心点坐标分别除以图片宽高后的归一化值,w、h 是目标宽高分别除以图片宽高后的比值,范围都在 0 到 1 之间。class_id 从 0 开始且必须连续,不能跳号。YOLO 系列训练时原生读的就是这种格式,读取快、存储小,这也是它成为训练主格式的原因。
| 标签格式 | 文件组织 | 坐标写法 | 类别编号 | 典型使用场景 |
|---|---|---|---|---|
| VOC | 每图一个 XML | xmin,ymin,xmax,ymax(像素) | 用类别名,无编号 | 标注工具、通用存档 |
| COCO | 整个数据集一个 JSON | x,y,w,h(像素) | 从 1 起,可跳号 | 多任务训练、预训练模型 |
| YOLO | 每图一个 txt | cx,cy,w,h(归一化 0~1) | 从 0 起,需连续 | YOLO 系列直接训练 |
选型逻辑上,经常有人问「到底该用哪套」。答案取决于你的下游工具链:训练 YOLO 系列就用 YOLO 格式;用 MMDetection 做多任务,或者想拿 COCO 预训练模型做迁移,COCO 格式更顺;想用 LabelImg 这类工具人工复查标注质量,VOC 更直观。这个包把三套都配齐了,好处是你在任意环节都能交叉验证,坏处是不少人被三套格式绕晕。我的习惯是只用 YOLO 格式作为训练输入,VOC 和 COCO 留着当校验参照,不参与训练链路,这样能少一层出错面。
2.2 解包后的第一步:抽 3 张图做标签可视化对照
既然三套标签描述同一批目标,第一步应该是验证它们是否真的对齐,而不是直接拿去训练。常见做法是写一个几十行的脚本,把 YOLO 标签和 VOC 标签同时画回原图,看看框是不是落在同一个目标上。
"""把 YOLO 和 VOC 标签同时画回原图,交叉验证两种格式是否对齐""" import cv2 import xml.etree.ElementTree as ET from pathlib import Path img_path = Path("images/000001.jpg") # 换成你要抽查的图片名 img = cv2.imread(str(img_path)) h, w = img.shape[:2] # YOLO 标签:每行 class_id cx cy w h,均为归一化值 with open("labels_yolo/000001.txt") as f: for line in f: cls, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # VOC 标签:从 XML 的 bndbox 读像素坐标 tree = ET.parse("labels_voc/000001.xml") for obj in tree.findall("object"): x1 = int(obj.find("bndbox/xmin").text) y1 = int(obj.find("bndbox/ymin").text) x2 = int(obj.find("bndbox/xmax").text) y2 = int(obj.find("bndbox/ymax").text) cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 0), 2) cv2.imwrite(f"check_{img_path.stem}.jpg", img)这段脚本的逻辑是:把 YOLO 标签画成绿色框,VOC 标签画成红色框。如果两种格式描述的是同一目标,红绿框应该基本重合。如果某一类框整体偏移,问题多半出在归一化时用了错误的图片宽高;如果框大小对但位置偏,检查是不是中心点公式写错,中心点应该是 (xmin + xmax) / 2 而不是其他系数。
参数上注意两点:一是图片名和标签名要精确一致,包括前缀和扩展名,000001.jpg 对 000001.txt,多一个空格都读不到;二是 cv2.imwrite 输出在当前目录,抽查完记得把 check_*.jpg 删掉,别让它们混进训练数据目录,否则后面划分脚本会把这些检查图也当成训练样本。
COCO 格式的核对路径稍有不同:用 json.load 读整个 JSON,先打印 categories 数组看类别列表,再按 image_id 过滤出当前图片的 annotations,把 bbox 画到图上。COCO 的 bbox 是 [x, y, w, h],画框时直接用这几个值,不需要像 YOLO 那样反算,但要注意 category_id 必须对到 categories 里的名字才能打对标签文字。这类数据集包因为三套格式齐全,做一轮三方对照很有必要,抽 3 张图就够发现问题了。
3. 训练前的关键一步:用划分脚本把 1000 张图拆成训练、验证、测试集
数据集划分看起来简单,却是影响实验可复现性的第一道关。同样的数据集,训练集和验证集内容不同,最终指标可以差 5 个点以上。包里自带的划分脚本,核心做一件事:把图片和对应标签按比例拆到 train、val、test 三个目录,并保证图片和标签始终成对出现。拿到脚本后先别直接跑,打开看一眼它的输出目录结构是不是你想要的,这一步花两分钟,能避免后面 data.yaml 写错路径。
3.1 划分脚本的常见写法:随机种子、比例与复制策略
常见做法是写一个 Python 脚本,读取图片目录,shuffle 后按比例切片,再把图片和同名标签一起复制到目标目录。下面这段是这类脚本的标准骨架,目录名和路径换一下就能直接套用:
"""按 7:2:1 划分训练/验证/测试集,图片和标签同步复制""" import random import shutil from pathlib import Path random.seed(42) # 固定随机种子,保证划分结果可复现 src_img = Path("images") # 原始图片目录 src_lbl = Path("labels_yolo") # YOLO 格式标签目录 out_root = Path("datasets/drone") # 输出根目录 ratio = (0.7, 0.2, 0.1) # train / val / test 比例 images = sorted(src_img.glob("*.jpg")) random.shuffle(images) n = len(images) n_train = int(n * ratio[0]) n_val = int(n * ratio[1]) groups = { "train": images[:n_train], "val": images[n_train:n_train + n_val], "test": images[n_train + n_val:], } for split_name, img_list in groups.items(): out_img = out_root / split_name / "images" out_lbl = out_root / split_name / "labels" out_img.mkdir(parents=True, exist_ok=True) out_lbl.mkdir(parents=True, exist_ok=True) for img in img_list: shutil.copy(img, out_img / img.name) label_file = src_lbl / (img.stem + ".txt") if label_file.exists(): shutil.copy(label_file, out_lbl / label_file.name) else: print(f"警告: {img.name} 缺少对应标签")三个关键参数值得单独说。第一个是 random.seed(42),种子不固定,每次划分结果都不同,前后两次实验的数据都不一样,指标差异说不清是模型变化还是数据变化引起的。第二个是比例,1000 张图的体量,7:2:1 或 8:2 都合理;验证集低于 10% 时,每个 epoch 的验证指标抖动会非常大,无人机场景目标小且分布不均,20% 是稳妥下限。第三个是复制策略,我坚持用 shutil.copy 而不是 move,原始包保留一份,相当于给自己留了后悔药,划分脚本出 bug 也不至于毁掉原始数据。
3.2 划分后必做的一次体检:图片数、标签数与空文件核对
划分完不算完,还得做一次体检。最常见的翻车是只复制了图片没复制标签,或者标签里有空文件。空文件在 YOLO 训练里意味着这张图没有任何目标,会被当成纯负样本,数量多了会明显拉低召回。
"""检查划分后每个子集的图片数、标签数和空标签数""" from pathlib import Path for split in ["train", "val", "test"]: img_dir = Path(f"datasets/drone/{split}/images") lbl_dir = Path(f"datasets/drone/{split}/labels") imgs = list(img_dir.glob("*.jpg")) labels = list(lbl_dir.glob("*.txt")) empty = [p for p in labels if p.stat().st_size == 0] print(f"{split}: 图片 {len(imgs)} 张, 标签 {len(labels)} 个, 空标签 {len(empty)} 个")如果输出里「图片数不等于标签数」,先确认是不是有些图确实没有目标,还是划分逻辑写漏了。空标签数量不为 0 时,建议直接把空文件删掉,一张完全没有目标的图对检测任务没有正面价值,留着只会让训练多出无意义的负样本。三个子集加起来图片数应该正好等于 1000,这个数对不上说明脚本处理时有文件被漏掉或重复统计。
另外补一个无人机场景的细节:如果原始数据类别分布很不均匀,比如车辆占 80%、行人占 15%、建筑占 5%,纯随机划分可能让验证集里某个类别一张都没分到。这时可以按类别分层划分:先按类别把图片分组,再在每个组内按同样比例抽取,保证验证集覆盖所有类别。1000 张的数据量不大,多花十分钟做分层划分,后面训练时验证指标的抖动会小很多。
4. 用 YOLOv8 把数据跑起来:data.yaml 与训练命令的最小闭环
数据就绪之后,就进入「处理数据集用于 yolov8 训练」的正式环节。当前社区里用 YOLOv8 训练自己的数据集已经是主流路径,不是说它一定比其他版本强多少,而是 ultralytics 库把数据加载、训练、验证、导出集成得很顺,对 1000 张这种小体量数据集,从配环境到出指标通常一小时内能走通。
4.1 data.yaml 的三处一致性:路径、类别数、类别名
YOLOv8 用 data.yaml 描述数据集。这个文件有三个地方最容易写错:路径、类别数 nc、类别名 names。三者必须和你的目录结构、标签内容完全对齐,任何一个对不上,训练就会出问题。
# drone.yaml path: /home/user/datasets/drone train: train/images val: val/images test: test/images nc: 3 names: 0: car 1: person 2: buildingpath 是数据集根目录,建议直接用绝对路径。相对路径在命令行当前目录和训练教程脚本的工作目录不一致时经常失效,报错还很隐晦。train 和 val 填相对于 path 的路径,注意指向 images 目录,YOLOv8 会自动在同级找 labels 目录,所以划分脚本的输出目录结构必须严格保持 images 和 labels 平级。nc 是类别总数,names 的编号顺序必须和标签文件里的 class_id 一一对应。类别名本身叫什么不重要,编号顺序错了,模型学出来就是把 A 类当 B 类,这是最隐蔽的错误之一。
如果你不确定实际类别,打开任意一个 YOLO 标签 txt,看第一列的最大值,加 1 就是类别总数;需要类别名时去 VOC 的 XML 里翻<name>标签,把去重结果按编号排好。这一步别偷懒,类别错位的问题越早发现代价越小。核对完数据,再往下走训练才安心。
4.2 训练命令与四个必调参数:epoch、batch、imgsz、预训练权重
安装 ultralytics 后,最小训练命令是这样:
pip install ultralytics yolo detect train \ data=drone.yaml \ model=yolov8n.pt \ epochs=100 \ batch=16 \ imgsz=640 \ project=runs/drone \ name=exp1如果更喜欢在 Python 脚本里控制流程,等价写法如下:
from ultralytics import YOLO model = YOLO("yolov8n.pt") # 加载预训练权重 model.train( data="drone.yaml", epochs=100, batch=16, imgsz=640, project="runs/drone", name="exp1", )model 参数传的 yolov8n.pt 需要先下载,这就是常说的 yolo 预训练模型下载。预训练权重是迁移学习的核心,1000 张图从零训练基本学不动,加载在 COCO 上训好的权重再微调,效果差距非常大。显存充足时换 yolov8s.pt,精度更高,代价是训练时间变长。
imgsz 对无人机目标检测尤其关键。无人机视角下的目标普遍偏小,一辆车可能只有几十个像素,640 是基线;如果显卡是 V100 或 4090 这类大显存卡,提到 960 甚至 1280,小目标的 mAP 提升肉眼可见,但显存和训练时间几乎按分辨率平方增长,需要自己权衡。跑训练前先执行 nvidia-smi 确认显存余量,batch 从 16 往上加时 out of memory 是最常见报错,出现时优先降 imgsz 一档或 batch 减半,比换模型省事。
batch 还直接影响训练稳定性。8G 左右显存用 16 是稳妥值,batch 太小会让 BN 统计量不稳定,这也是 yolo 训练中 bn 崩溃的诱因之一。epochs 不用贪,1000 张图 100 轮足够看到收敛趋势,训练时盯住 val loss,连续 20 轮不降就可以手动停掉,没必要把 100 轮全部跑完。
5. 避坑指南:无人机数据从解压到训练的 5 个真实翻车现场
这一章集中写训练链路上最容易翻车的 5 个位置,每一条都按「现象 → 原因 → 解决」的结构展开,照着核对一遍能省下好几个晚上。
5.1 现象:训练日志出现 0 labels,loss 恒为 0
训练刚启动,终端打印 "0 labels found" 或 "All labels are empty",loss 一直停在 0。
原因基本是三个:data.yaml 里 train/val 路径不对,指向了没有图片的目录;标签文件后缀是大写 .TXT,Linux 下大小写敏感读不到;标签文件名和图片名前缀不一致,比如图片叫 000001.jpg,标签叫 1.txt。
解决:先用 ls 逐层确认目录存在;再执行 find . -name "*.TXT" 找出大写后缀文件并批量改成小写;最后随机挑一张图确认图片和标签前缀完全一致。这三步走完,0 labels 的问题基本能消失。如果还不行,把 data.yaml 里 path 换回绝对路径再试一次,这是最常被忽略的一招。
5.2 现象:可视化画框时框整体偏移或宽高比不对
把 YOLO 标签画回原图,框明显没有罩住目标,或者宽高比被压扁,看起来框是歪的。
原因是归一化分母用错。常见的有两种:把 xmax/width 当成中心点 cx,但中心点应该是 (xmin + xmax) / 2 再除以 width;或者图片被 resize 过,标签仍按原始尺寸归一化。第二种在数据预处理阶段最容易发生,resize 之后标签必须按相同的比例重新计算。
解决:先用 2.2 节的对照脚本画一遍 VOC 和 YOLO 的框,偏差一眼就能看出来;如果确认是 resize 导致的,回到预处理脚本里把缩放比例同步到标签上,重新导出后再画一遍验证。可视化是这类问题最直接的排查手段,比对着公式看半天管用得多。
5.3 现象:VOC/COCO 转 YOLO 后类别整体错位
训练能正常跑,但验证时发现模型把行人识别成车辆,或者所有预测类别都比真实类别往后移了一位。
原因是 COCO 的 category_id 从 1 开始且允许跳号,YOLO 的 class_id 从 0 开始且必须连续。直接拿 category_id 当 class_id,所有类别就整体错位。VOC 转 YOLO 也一样,XML 里只有类别名没有编号,如果用字典默认顺序排列类别,顺序和训练数据里约定的不一致就出错。
解决:转换前先建一张显式的类别映射表,比如 {"car": 0, "person": 1, "building": 2},再用映射表生成 class_id,不要依赖任何默认排序。转换完成后用 2.2 节的可视化脚本抽 5 张图,把类别名字打印出来逐一对一遍,确认编号写进 txt 的和显示的是同一类。
5.4 现象:训练中段 loss 突然变 NaN,即 yolo 训练中 bn 崩溃
训练跑到第 20 到 40 轮,loss 从正常值突然跳到 nan,之后再也回不来。网上常说的 yolo 训练中 bn 崩溃,本质是 BN 层的统计量被极端输入破坏。
原因是三个因素叠加:学习率偏高、batch 太小、数据里有脏标注。脏标注里最典型的是宽或高为 0 的框,这类框会让损失函数计算出异常梯度,一次更新就能把 BN 统计量带崩。用这份数据集训练时,先把标签清洗一遍最稳妥:
"""清洗标签:过滤掉宽或高为 0 的无效框""" from pathlib import Path for lbl in Path("datasets/drone/train/labels").glob("*.txt"): lines = lbl.read_text().strip().splitlines() valid = [] for line in lines: cls, cx, cy, w, h = map(float, line.split()) if w > 0 and h > 0: valid.append(line) if len(valid) != len(lines): lbl.write_text("\n".join(valid) + "\n") print(f"清洗 {lbl.name}: {len(lines)} -> {len(valid)}")清洗完再把学习率从默认值降到 0.001 量级,同时检查预训练权重和当前类别数是否匹配。类别数不一致时不要直接加载 .pt 再改 nc,而是用 yolov8n.yaml 重新初始化结构再加载预训练权重。
5.5 现象:验证集 mAP 很高,实拍视频里漏检严重
val 指标 0.8 以上,部署到真实无人机画面后几乎不识别目标。
原因是 1000 张图的数据集视角和场景太单一。如果训练集里全是白天、固定高度、单一背景的俯视图,模型学到的其实是这一套固定分布的统计规律,换到低光照、雾天、不同飞行高度就失效。这是小样本无人机检测的典型过拟合,属于数据集本身的边界问题。
解决:训练时打开 mosaic、fliplr、随机光照等数据增强参数;推理时把置信度阈值从默认 0.25 降到 0.1,看召回是否恢复,如果恢复说明模型没崩只是阈值卡得太严;最彻底的办法是去目标场景补拍 100 到 200 张图做增量训练,把新数据并进训练集重新训一轮,比任何参数调整都有效。
6. 验证模型真的能落地:从 mAP 指标到一次实拍测试
训练完成后,别只盯着终端里最后一个 mAP 数字。先跑一遍标准验证命令,拿到混淆矩阵和 PR 曲线,再从静态指标走到实拍测试,整条链路才算闭环。
yolo detect val \ data=drone.yaml \ model=runs/drone/exp1/weights/best.pt输出里重点看两个数:mAP@0.5 和 mAP@0.5:0.95。前者是 IoU 阈值 0.5 下的平均精度,后者是从 0.5 到 0.95 每隔 0.05 取一次 IoU 再平均,后者更严格,也更贴近无人机小目标的真实难度。混淆矩阵则直接告诉你错在哪:对角线亮不亮、哪两类互相混淆。如果车辆和建筑经常混淆,多半是俯视视角下两者形状纹理确实接近,这时补数据比换模型更有效。
静态指标只说明模型在验证集上的表现,更接近真实的是把模型部署到实拍素材上跑一次推理,保存结果逐帧检查:
yolo detect predict \ model=runs/drone/exp1/weights/best.pt \ source=drone_test.mp4 \ conf=0.25 \ save=True实拍测试通过后再谈 yolo 部署到边缘设备上的细节。常见做法是导出 ONNX 再转 TensorRT,放到 Jetson 这类设备上,此时帧率成为新瓶颈,通常要把输入分辨率从 1280 降到 960 或 640 来换帧率,这个取舍也要在验证阶段就想清楚。
我自己养成的习惯是:训练完不急着发结论,先挑验证集里最难的 10 张图跑一遍 predict,肉眼看一遍漏检和误检分别长什么样,再决定下一轮是补数据还是调阈值。这个习惯帮我避免了很多次「指标好看、落地翻车」的尴尬,希望帮到你。
本文还有配套的精品资源,点击获取