无人机目标检测数据集全流程:格式转换、划分与YOLO训练实战

发布时间:2026/9/11 19:39:59
无人机目标检测数据集全流程:格式转换、划分与YOLO训练实战 简介YOLO无人机目标检测数据集包含5000张真实场景下的无人机视角图片画面丰富、标注质量高面向需要训练无人机目标检测模型的学生、科研人员和开发者能够直接用于YOLO系列算法训练。资源采用LabelImg标注提供VOC、COCO、YOLO三种格式标签对应xml、json、txt文件分别存放在独立文件夹内省去格式转换的额外步骤同时附带数据划分脚本可以一键生成训练集、验证集和测试集。压缩包共2000个文件以1986个xml标签文件为主体另含5个txt文件、3个Python划分脚本和6个html教程文档整体大小301.59MB目录结构清晰。配套教程均以网页形式提供覆盖Linux与Windows环境下的YOLO环境搭建、训练案例修改还补充了Linux系统安装教程帮助零基础用户从零跑通全流程。目前已有433人浏览学习适合课程设计、毕业设计以及无人机巡检、航拍检测等实际项目的快速落地。1. 无人机目标检测数据集为什么 5000 张图不是先跑模型而是先理格式无人机视角下的目标检测和街道监控、行车记录完全是两个物种。视角高、目标小、密度大一张 4K 航拍图里塞几十个目标很正常5000 张图片解压出来对应的目标实例数量可能达到几十万级。这份标题里的 rar 把 VOC、COCO、YOLO 三种标签格式都配齐了还带了划分脚本和训练教程看起来是“解压即用”。但格式齐不等于能用三种格式对应三条完全不同的训练链路划分脚本的参数乱调val 集合可能根本代表不了真实分布训练出来的指标好看一部署就露馅。这篇文章按格式差异、划分逻辑、训练参数、验证分析四个环节往下走每一步都给出可复现的命令和脚本。适合准备训练无人机检测模型的人也适合想一次性搞清楚 VOC、COCO、YOLO 三种标注格式底层差异的工程师。2. VOC、COCO、YOLO 三种标签格式的存储差异与转换脚本2.1 VOC 格式一张图片对应一个 XML 标注文件解压一份无人机数据集如果看到Annotations目录里全是.xmlJPEGImages目录里是.jpg这就是最经典的 VOC 布局。每张图片对应一个同名 XML 文件里面用size记录图片宽高用object块记录每一个目标。每个 object 里包含类别名和bndbox绝对像素坐标格式是xmin, ymin, xmax, ymax。VOC 最大的优点是“人可读”。打开 XML 就能直接看出来哪个目标叫什么名字、框大体在什么位置排查标注错漏时非常直观。缺点也随之而来没有统一的总表类别列表要么写在代码里要么单独放在classes.txt多数据集合并时容易出现同名不同序的冲突。对于无人机场景一个object块通常只有两行有效信息但一颗 4K 图上几十个目标XML 文件会变得很长。好在解析效率不是瓶颈Python 端用xml.etree.ElementTree批量读取转成内存里的 list 也就几百毫秒。2.2 COCO 格式一个 JSON 把类别与全部标注串在一起COCO 不是按图片拆文件而是用一个 JSON 总表管理整个数据集。核心字段是images、annotations、categories三块images记录每张图片的 id 和宽高annotations记录每个目标框categories记录类别名称与 id 的映射关系。COCO 的 bbox 是[x, y, width, height]同样是绝对像素值但注意它是 float 类型不像 VOC 那样天然是整数。如果训练流程里要做裁剪或者数据增强这个 float 精度反而要格外小心取整时机不对会累积出几个像素的偏移。无人机数据里一张图几十个目标意味着annotations数组长度会非常可观但 JSON 的结构化程度高后面按image_id做聚合、抽样、统计都非常顺手。2.3 YOLO 格式归一化中心点坐标与训练流程距离最近YOLO 的标签是纯文本 txt每行代表一个目标五列数据class_id, x_center, y_center, width, height其中坐标全部除以图片宽高做了归一化。这个设计让模型训练完全不用关心输入图片的实际分辨率。对无人机数据集要特别注意小目标占比高归一化之后的width和height可能是0.0023这种极小数值。格式本身没问题但转换脚本里如果只保留 4 位小数误差就会被放大导致框明显偏移。我一般保留 6 位小数既不会写出超长浮点串也不会产生可见误差。YOLO 格式最不舒服的地方是脱离原图之后完全不可读。一个 txt 文件里几十行数字没法肉眼判断对不对。所以实际工作流里标注阶段用 VOC 或 COCO训练前再转成 YOLO是很多团队的通用做法。2.4 格式转换脚本VOC 转 YOLO 的最小实现解压 rar 后的第一步是确认三种格式的目录结构然后决定训练走哪条链路。如果最终用 YOLO 训练就需要准备一个 VOC 转 YOLO 的脚本下面是一个直接可用的最小实现import xml.etree.ElementTree as ET import os # 这里的类别顺序必须和后续训练 data.yaml 中的 names 完全一致 classes [car, truck, bus, person, cyclist] def voc_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) out_lines [] for obj in root.iter(object): cls_id classes.index(obj.find(name).text) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h out_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) base os.path.splitext(os.path.basename(xml_path))[0] out_path os.path.join(out_dir, base .txt) with open(out_path, w) as f: f.write(\n.join(out_lines))这个函数接收单个 XML 文件路径和输出目录遍历所有object节点把绝对像素坐标换算成归一化中心点坐标。注意classes.index()依赖类别名精确匹配无人机数据里常见的问题是标注时有时写car有时写vehicle转换前做好标签清洗否则这里会直接抛 ValueError。实际转换时用os.listdir遍历整个 Annotations 目录python -c import os from voc_to_yolo import voc_to_yolo xml_dir Annotations out_dir labels os.makedirs(out_dir, exist_okTrue) for f in os.listdir(xml_dir): if f.endswith(.xml): voc_to_yolo(os.path.join(xml_dir, f), out_dir) 2.5 COCO 转 YOLO 与格式选型建议COCO 转 YOLO 的核心逻辑是从 JSON 里按image_id分组再把bbox的 xywh 绝对坐标转换成归一化中心点格式import json with open(annotations.json) as f: coco json.load(f) img_id_to_info {img[id]: img for img in coco[images]} cat_id_to_cls {cat[id]: i for i, cat in enumerate(coco[categories])} grouped {} for ann in coco[annotations]: img_id ann[image_id] grouped.setdefault(img_id, []).append(ann) for img_id, anns in grouped.items(): img img_id_to_info[img_id] w, h img[width], img[height] lines [] for ann in anns: cls_id cat_id_to_cls[ann[category_id]] x, y, bw, bh ann[bbox] x_center (x bw / 2) / w y_center (y bh / 2) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw / w:.6f} {bh / h:.6f}) with open(flabels/{img[file_name][:-4]}.txt, w) as f: f.write(\n.join(lines))这段代码里先建了两个映射表再按图片聚合所有标注最后直接写出 txt。注意 COCO 的file_name可能带子目录前缀写输出文件时最好用os.path.basename处理一下。选型时可以参考下表训练目标推荐格式理由YOLO 系列训练YOLO txt无需转换训练直接读MMDetection / Faster R-CNNCOCO JSON框架原生支持评估工具链齐全标注可视化与人工校验VOC XML可读性最好单文件独立多数据集合并管理COCO JSON统一 id 映射去重方便3. 划分脚本按图切分 train/val/test 的三个关键点3.1 划分粒度是图片而不是标注文件数据集划分最常见也最致命的错误是拿标注文件当划分单位。一个图片有 20 个目标被拆到训练集和验证集各 10 个模型等于在验证集里见过同一场景的不同目标val 指标虚高得一塌糊涂部署到新场景立刻现原形。正确做法是严格按图片 ID 切分图片去哪边它对应的所有标注就跟着去哪边。划分比例按使用场景选择比例适用场景7 : 2 : 1需要独立 test 集做最终评估8 : 2只需要 train/val数据集本身不大9 : 1数据量紧张以训练为主要目的无人机数据集一般建议保留独立的 test 集。航拍场景变化大val 集只用来调参test 集模拟真正部署时遇到的新场地这样才能看出模型会不会过拟合到某些固定航线。3.2 划分脚本的最小实现下面是一个按图片文件名切分的脚本同时处理图片和对应的 YOLO 格式 txt 文件import os import random from shutil import copy2 random.seed(42) image_dir images_all label_dir labels_all out_base dataset images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(images) n len(images) n_train int(n * 0.7) n_val int(n * 0.2) splits { train: images[:n_train], val: images[n_train:n_train n_val], test: images[n_train n_val:], } for split_name, split_images in splits.items(): img_out os.path.join(out_base, images, split_name) lbl_out os.path.join(out_base, labels, split_name) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for img_name in split_images: base os.path.splitext(img_name)[0] copy2(os.path.join(image_dir, img_name), os.path.join(img_out, img_name)) label_file os.path.join(label_dir, base .txt) if os.path.exists(label_file): copy2(label_file, os.path.join(lbl_out, base .txt))脚本逻辑分三层先random.shuffle打乱图片列表再按比例切片最后逐张复制图片和同名 txt。固定random.seed(42)很重要否则每次跑出来的划分结果都不同复现实验结果时会很被动。copy2会保留文件元数据但会占用双倍磁盘空间。如果原始数据集在机械盘、训练在 SSD 上我更推荐用os.symlink建软链接省空间且速度快。代价是数据集目录不能随意移动移动后所有链接失效。这一条根据自己环境取舍。3.3 类别分布要跟着划分走纯随机划分在类别均衡的数据集上问题不大但无人机数据几乎都是长尾分布汽车可能占 80%行人占 5%骑车人占 1%。随机切分后val 集里骑车人可能只有几个样本算出来的 per-class mAP 抖动剧烈根本无法判断模型改进是否有效。简单有效的做法是按主类别分层抽样。给每张图片分配一个主类别标签面积最大的目标类别然后在这个类别组内做随机划分。代码改动很小在random.shuffle之前多一次按类别分组即可。划分完以后打印各 split 的类别直方图确认比例一致性这一步值得养成习惯。3.4 为 YOLO 输出 train.txt / val.txt 索引早期 Darknet 框架和部分自定义训练管线不认目录结构而是要求一个 txt 文件里逐行写图片绝对路径。如果后面要跑这类流程还需要一个索引生成脚本find dataset/images/train -name *.jpg train.txt find dataset/images/val -name *.jpg val.txtultralytics 的 YOLO 训练不需要这些索引文件直接扫目录。但如果同时要跑 mmdetection 或者 darknet这份train.txt就派上用场了。留意路径要写绝对路径相对路径在部分框架里会因工作目录不同而解析失败。4. 用 YOLO 训练无人机数据集data.yaml、训练参数与损失函数排查4.1 把解压后的目录收敛成标准结构不管 rar 里原始的目录长什么样训练前的最后一步都是收敛成 YOLO 约定俗成的结构dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/图片和标签必须严格同名只是扩展名不同。YOLO 训练时是按图片路径去对应目录找同名 txt 的任何一级目录对不上都会报image without label。无人机数据里偶尔会有标注缺失的图片我一般写个脚本先把无标注图片剔除而不是让训练带着空标签跑。4.2 data.yaml 的正确写法与类别顺序陷阱以当前常见的 ultralytics 训练流程为例data.yaml是唯一的数据入口path: /data/dataset train: images/train val: images/val test: images/test names: 0: car 1: truck 2: bus 3: person 4: cyclistpath建议写绝对路径相对路径在某些回调逻辑里会计算错误。names的顺序必须和第 2 章转换脚本里的classes列表完全一致。类名和顺序有一个错位训练过程完全正常loss 照降mAP 照算但推理输出时所有类别标签都是歪的这类问题最难排查。4.3 训练命令与关键参数表yolo detect train \ datadata.yaml \ modelyolov8s.pt \ imgsz640 \ epochs100 \ batch16 \ optimizerAdamW \ lr01e-3 \ patience20 \ device0参数推荐值说明imgsz640 / 1280无人机小目标多建议起步 1280epochs100 - 300以 val loss 不再下降为准batch8 - 32受显存限制越大越稳optimizerAdamW / SGD小数据集 AdamW 收敛快lr01e-3 / 5e-4AdamW 搭配小学习率patience20验证集指标连续不升则早停无人机数据的特殊性主要体现在imgsz上。航拍目标经常只有几十个像素640 输入会把目标缩得更小特征图上的有效信息只剩下几个点。显存允许的情况下优先上 1280没有硬件条件就用 640 训练推理时再上更高分辨率。4.4 小目标场景怎么调损失函数与数据增强训练日志里每轮会输出三组 lossbox_loss、cls_loss、dfl_loss。box_loss 反映框的回归误差dfl_loss 是 distribution focal loss直接影响边界精度。对无人机小目标如果 box_loss 掉得很快但 cls_loss 卡住不降问题通常出在数据侧——细粒度类别之间外观差异太小比如行人和骑车人在低分辨率下几乎分不开。数据增强这里有个常见认知偏差mosaic 对小目标不一定友好。拼图操作把多张图缩放到一个小图里小目标被进一步缩小反而更难学。如果小目标检测效果不理想可以调低mosaic0.5同时把scale0.3限制在更小范围避免目标缩放过于剧烈。关注每个 epoch 结束后输出的 per-class mAP比盯着总 mAP 更能看出增强策略的真实效果。5. 用验证集做漏检分析反哺数据质量与标注策略5.1 第一件事看混淆矩阵和每类指标训练结束后先跑一遍验证yolo detect val datadata.yaml modelruns/detect/train/weights/best.pt输出里除了总 mAP50 和 mAP50-95还会给出每个类别的 Precision、Recall。无人机数据里最常见的现象是总 mAP 很好看但某一两个稀有类别 Recall 极低。这个信号说明模型没有学会该类别的特征而不是框不准。再看confusion_matrix.png重点观察两类错误一是行人被预测成骑车人这是外观相似类别的混淆需要更多对比样本二是大量背景区域被预测成某类目标说明标注时漏标严重模型把未标注的目标当成了假正例。5.2 漏检回放把预测结果画回去指标只能告诉你哪里有问题不能告诉你为什么。把 val 集的预测结果可视化回原图直接看漏检目标长什么样yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcedataset/images/val \ saveTrue \ conf0.25一张张翻预测图漏检目标通常集中在三种情况目标尺度小于 15×15 像素、目标被树冠或楼顶阴影遮挡、相邻目标间距极近且类别不同。翻完图以后手动切几个典型区域统计漏检目标的平均像素面积这个数值直接指导下一轮是提升imgsz还是补采集低空数据。5.3 数据清洗四步验证集暴露的问题最终要反哺到数据集本身常见做法是按顺序做四轮清洗删除损坏和重复图片按文件 md5 去重删除面积小于 10×10 像素的标注这类目标噪声大于信号合并类别语义重叠的标注比如car和vehicle统一成一个类别对 val 中样本量最少的两个类别单独补充数据第四步最容易犯的错是为凑数量把相似场景大量重复加入。无人机数据要的是场景多样性同一片区域反复采样的边际收益远低于换一片新场地。补充后重新划分数据集再训练一轮用同一份 test 集做对比。如果 val 提升但 test 不动说明之前的提升来自数据分布偏移而不是模型能力变化。本文还有配套的精品资源点击获取