简介:这份资源面向无人机视觉与目标检测方向的开发者、学生及科研人员,提供一套真实场景下的车辆检测数据集,可用于无人机航拍车辆检测项目,也可作为通用车辆检测数据的场景补充。数据集共1000张高质量图片,覆盖城市道路行驶车辆、道边停车、停车场、小区车辆以及车辆遮挡、严重遮挡等多种情形,类别划分为轿车car、货车van和巴士bus三类,均采用labelimg标注,并提供VOC(xml)、COCO(json)、YOLO(txt)三种主流格式,可直接投入YOLO等算法训练。资源包为1个PDF文件,约2MB,内附数据集基本情况介绍与获取方式,并附赠YOLO11一键训练脚本,支持GPU、CPU及Mac(M芯片)多平台训练方案,同时提供博主训练结果日志供参考。目前已有261人学习下载,适合希望快速搭建无人机车辆检测训练流程、对照日志排查问题的读者使用。
1. 无人机视角下的车辆检测:这份 1000 张图的数据集到底能不能直接开训
拿到一份无人机场景的车辆检测数据集,第一反应通常不是看图片有多清晰,而是先翻标签格式全不全、类别定义合不合理、训练脚本能不能在我这台机器上跑起来。这份资源给的是 1000 张真实无人机航拍图,标注覆盖城市道路行驶车辆、路边停车、停车场、小区内部以及遮挡和严重遮挡场景,类别就三类:轿车 car、货车 van、巴士 bus。标注工具用的是 labelimg,导出 VOC xml、COCO json、YOLO txt 三套标签,还附带一个 YOLO11 一键训练脚本,声称 GPU、CPU、Mac M 芯片三平台都能跑。对做无人机视觉感知、小目标检测或者想补充通用车辆检测场景数据的人来说,这套东西的价值在于省掉了从原始视频抽帧到标注格式转换的整条链路。但能不能直接开训,取决于你对标签质量、类别平衡和脚本适配的预期。下面按我实际拆包和跑通的顺序,把这份资源从结构到训练再到踩坑讲清楚。
2. 拆开数据包先看什么:目录结构、标签格式与类别分布
2.1 三种标签格式的目录组织与互转逻辑
一份目标检测数据集能不能用,第一眼看目录结构。常见做法是根目录下分 images 和 labels,再按 train/val/test 切分。VOC 格式通常单独放 Annotations 文件夹,里面是每张图对应的 xml;COCO 格式是一个大的 json 文件,包含 images、annotations、categories 三个顶层字段;YOLO 格式则是每张图一个 txt,每行class_id x_center y_center width height,坐标全部归一化到 0 到 1 之间。
这份资源同时给了三套,意味着你不需要自己写转换脚本。但要注意,三套标签的类别索引必须一致。VOC xml 里写的是类别名 car、van、bus,COCO json 里 categories 的 id 通常从 1 开始,YOLO txt 里 class_id 从 0 开始。如果你混用,比如拿 COCO 的 id 去对 YOLO 的 txt,训练时类别就会整体偏移一位,模型把 car 学成 van。我一般会先跑一段校验脚本,确认三套标签的类别映射关系。
import json import xml.etree.ElementTree as ET from pathlib import Path # 校验 VOC 与 YOLO 类别索引是否一致 voc_dir = Path("dataset/voc/Annotations") yolo_dir = Path("dataset/yolo/labels") voc_classes = set() for xml_file in voc_dir.glob("*.xml"): tree = ET.parse(xml_file) for obj in tree.findall("object"): voc_classes.add(obj.find("name").text) # YOLO 类别索引从 data.yaml 或 classes.txt 读取 with open("dataset/yolo/classes.txt", "r") as f: yolo_classes = [line.strip() for line in f.readlines()] print("VOC 类别:", sorted(voc_classes)) print("YOLO 类别索引:", yolo_classes) # 如果顺序不一致,训练前必须重映射这段脚本的作用是暴露类别顺序差异。VOC 里类别出现顺序是随机的,YOLO 的 classes.txt 顺序决定了 class_id 0、1、2 分别对应谁。如果 classes.txt 写的是bus, car, van,而你的认知是car, van, bus,那训练出来的模型会把巴士认成轿车。参数上只需要改 voc_dir 和 yolo_dir 两个路径,跑一次就能看到实际映射。
2.2 1000 张图的场景覆盖与类别平衡判断
1000 张图在目标检测里不算大,尤其是无人机视角下车辆目标普遍偏小,单张图里可能同时出现几十辆车。这份数据的场景覆盖列得很细:城市道路行驶、路边停车、停车场、小区、遮挡、严重遮挡。从训练角度,遮挡样本和停车场密集样本对模型的泛化能力帮助最大,因为无人机实际作业时最怕的就是车挨着车、树挡住车。
但类别平衡需要你自己统计。car 通常占绝大多数,van 和 bus 可能只有几十个实例。如果 bus 实例太少,训练时模型会倾向于把 bus 也预测成 car,因为这样损失更小。我一般会先跑一个实例计数,看看三个类别的框数量比例。如果 bus 少于总实例的 5%,就要考虑用类别权重或者过采样。
from collections import Counter from pathlib import Path label_dir = Path("dataset/yolo/labels") counter = Counter() for txt_file in label_dir.glob("*.txt"): with open(txt_file) as f: for line in f: class_id = int(line.split()[0]) counter[class_id] += 1 total = sum(counter.values()) for cid, count in sorted(counter.items()): print(f"类别 {cid}: {count} 个实例, 占比 {count/total:.2%}")跑完这个统计,你就能判断要不要在 YOLO11 训练配置里加cls权重或者用copy_paste增强。参数说明:label_dir 指向 YOLO 格式的 labels 目录,class_id 就是每行第一个整数。如果发现某个类别实例数低于 100,建议在 data.yaml 里把nc设对之外,再在训练时开class_weights或者手动复制该类别图片。
2.3 标注质量抽查:遮挡框与截断框的处理
labelimg 标注的质量参差不齐,尤其是遮挡场景。常见问题是:车辆被树挡住一半,标注员只框了可见部分,还是框了完整车辆?这两种标法对训练的影响完全不同。如果只框可见部分,模型学到的是“看到半个车也算车”;如果框完整车辆,模型需要根据上下文推断被挡部分。无人机场景下我更倾向于框完整车辆,因为实际检测时你希望输出完整目标框。
抽查方法很简单,随机抽 20 张遮挡场景图,用 OpenCV 把 YOLO 框画出来看。重点看框是否超出图像边界、是否把相邻车辆框在一起、是否漏标远处小目标。如果发现大量框只覆盖可见部分,训练前要么重新标,要么在数据增强里加mosaic让模型适应截断。这份资源没有提供标注一致性报告,所以这一步必须自己做。
3. YOLO11 一键训练脚本怎么跑:GPU、CPU、Mac 三平台配置差异
3.1 脚本入口与 data.yaml 的关键参数
一键训练脚本的核心是一个 Python 文件加一个 data.yaml。data.yaml 里必须写清楚train、val、test三个路径,nc类别数,names类别名列表。这份资源给了三套标签,所以 data.yaml 也要对应三份,或者用脚本参数切换。常见做法是把 YOLO 格式作为默认训练输入,因为 YOLO11 原生吃 txt。
# data.yaml 示例 train: ../dataset/yolo/images/train val: ../dataset/yolo/images/val test: ../dataset/yolo/images/test nc: 3 names: ['car', 'van', 'bus']参数说明:nc必须等于 names 长度,否则训练启动就报错。names的顺序必须和 classes.txt 完全一致。路径可以用相对路径,但建议用绝对路径避免工作目录切换后找不到文件。如果 VOC 或 COCO 也要用,需要先转成 YOLO 格式,或者用 Ultralytics 的转换工具。
3.2 GPU 平台:CUDA 版本、batch size 与 AMP 开关
GPU 训练是最常见的场景。YOLO11 对 CUDA 版本有要求,常见做法是 CUDA 11.8 或 12.1 配 PyTorch 2.1 以上。一键脚本里通常会写device=0指定第一块 GPU,batch=16或batch=32取决于显存。1000 张图在 8G 显存上跑 batch=16 基本没问题,如果开 AMP 混合精度,显存还能再降一点。
# GPU 训练命令示例 yolo detect train \ data=data.yaml \ model=yolo11n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ amp=True \ workers=4 \ project=runs/drone_vehicle \ name=exp_gpu逻辑说明:model=yolo11n.pt是 nano 版本,适合快速验证;如果追求精度可以换yolo11s.pt或yolo11m.pt。imgsz=640是输入分辨率,无人机小目标多的话可以提到 1280,但显存占用会翻倍。amp=True开启自动混合精度,能省显存但偶尔会导致 loss 震荡,如果发现 loss 不收敛可以先关掉。workers=4是数据加载线程数,CPU 核心多可以加到 8。
3.3 CPU 与 Mac M 芯片:device 参数与线程数调整
CPU 训练只适合验证流程能不能跑通,1000 张图跑 100 epoch 可能要几个小时甚至一天。脚本里把device设成cpu就行,但要把batch降到 4 或 8,workers也降到 2,否则内存容易爆。Mac M 芯片用 MPS 后端,device=mps,PyTorch 从 2.0 开始支持。但 MPS 对某些算子支持不全,如果报错就回退到 CPU。
# Mac M 芯片训练命令 yolo detect train \ data=data.yaml \ model=yolo11n.pt \ epochs=50 \ imgsz=640 \ batch=8 \ device=mps \ workers=2 \ project=runs/drone_vehicle \ name=exp_mac参数说明:Mac 上imgsz不建议超过 640,M 芯片的显存和内存共享,分辨率太高会触发内存交换。epochs可以少一点,因为 M 芯片训练速度比 GPU 慢不少,先跑 50 轮看趋势。如果 MPS 报NotImplementedError,就在命令前加PYTORCH_ENABLE_MPS_FALLBACK=1,让不支持的算子回退到 CPU。
3.4 训练日志怎么看:loss 曲线、mAP 与过拟合信号
训练启动后,控制台会输出每轮的 box_loss、cls_loss、dfl_loss 和 mAP50、mAP50-95。box_loss 下降说明框回归在收敛,cls_loss 下降说明分类在收敛。如果 box_loss 一直震荡不降,可能是学习率太大或者标注框有问题。mAP50 到 0.5 以上算及格,0.7 以上算不错,但无人机小目标通常偏低。
过拟合的信号是:训练 loss 持续下降,但验证 mAP 在某个 epoch 后不再上升甚至下降。这时候要么早停,要么加数据增强。YOLO11 默认开了 mosaic、mixup、copy_paste,如果还过拟合,可以调低mosaic概率或者加dropout。这份资源附带了博主训练结果日志,可以对照自己的曲线看是否正常。
4. 避坑与排查:标签、环境、显存、类别映射的五个血泪经验
4.1 现象:训练启动报 “No labels found”
原因:data.yaml 里的train路径指向了 images 目录,但 YOLO 默认去同级 labels 目录找同名 txt。如果 labels 目录名不是labels,或者图片和标签不在同一级,就会找不到。
解决:确认目录结构是images/train/xxx.jpg对应labels/train/xxx.txt。如果标签在别处,用yolo detect train的--labels参数指定,或者写一个软链接。我一般会先跑ls labels/train | head确认 txt 存在。
4.2 现象:类别预测全错,car 被认成 bus
原因:YOLO txt 里的 class_id 和 data.yaml 的 names 顺序不一致。比如 classes.txt 是bus, car, van,但 data.yaml 写的是car, van, bus,模型学到的 0 是 bus,推理时却按 car 输出。
解决:统一以 data.yaml 的 names 为准,重写 classes.txt 或者重映射 txt 里的 class_id。重映射脚本很简单,读一行改一个数字再写回。改完再跑一次 2.1 的校验脚本确认。
4.3 现象:GPU 显存不足,batch=16 直接 OOM
原因:无人机图片分辨率高,1000 张图里可能有不少 4000x3000 的原图,YOLO 会先 resize 到 imgsz,但数据加载时仍然占内存。如果imgsz=1280且batch=16,8G 显存基本不够。
解决:先把imgsz降到 640,batch降到 8,开amp=True。如果还不够,用workers=2减少数据加载并发。实在不行就换yolo11n.pt,nano 版本参数量最小,显存占用最低。
4.4 现象:Mac M 芯片训练报 “Placeholder storage has not been allocated”
原因:MPS 后端对某些张量操作支持不完整,尤其是自定义算子或者非连续内存。
解决:设置环境变量PYTORCH_ENABLE_MPS_FALLBACK=1,让不支持的算子回退到 CPU。如果还报错,直接device=cpu,虽然慢但稳定。Mac 上训练建议用yolo11n.pt,别用大模型。
4.5 现象:验证集 mAP 很高但实际推理漏检严重
原因:验证集和训练集场景分布太接近,模型过拟合了。比如训练集全是城市道路,验证集也是城市道路,但实际无人机飞的是小区或者停车场。
解决:重新切分数据集,确保验证集包含遮挡、停车场、小区等不同场景。如果某类场景样本太少,用copy_paste增强或者手动补充。我一般会按场景分层抽样,而不是随机切分。
5. 从 1000 张图到实际部署:小目标增强与推理验证的一个具体技巧
1000 张图训练出来的模型,直接拿去无人机实时推理,大概率会在小目标和遮挡场景上翻车。我的习惯是在训练前先做一步小目标增强:把原图切成 2x2 或 3x3 的瓦片,每块单独作为训练样本,标签同步裁剪。这样模型能看到更多小目标细节,推理时再用滑窗或者 SAHI 拼接。YOLO11 本身支持imgsz=1280,但显存不够的话,切图是更划算的方案。
import cv2 from pathlib import Path def slice_image(img_path, label_path, out_img_dir, out_lbl_dir, grid=2): img = cv2.imread(str(img_path)) h, w = img.shape[:2] tile_h, tile_w = h // grid, w // grid with open(label_path) as f: labels = [line.strip().split() for line in f.readlines()] for i in range(grid): for j in range(grid): x1, y1 = j * tile_w, i * tile_h x2, y2 = x1 + tile_w, y1 + tile_h tile = img[y1:y2, x1:x2] tile_name = f"{img_path.stem}_{i}_{j}.jpg" cv2.imwrite(str(out_img_dir / tile_name), tile) # 裁剪标签:只保留中心点在瓦片内的框 with open(out_lbl_dir / f"{img_path.stem}_{i}_{j}.txt", "w") as f: for cls, xc, yc, bw, bh in labels: xc, yc, bw, bh = map(float, (xc, yc, bw, bh)) abs_x, abs_y = xc * w, yc * h if x1 < abs_x < x2 and y1 < abs_y < y2: new_xc = (abs_x - x1) / tile_w new_yc = (abs_y - y1) / tile_h new_bw = bw * w / tile_w new_bh = bh * h / tile_h f.write(f"{cls} {new_xc:.6f} {new_yc:.6f} {new_bw:.6f} {new_bh:.6f}\n")这段脚本的逻辑是:把每张图切成 grid x grid 块,标签只保留中心点落在瓦片内的框,坐标重新归一化到瓦片尺寸。参数grid=2表示 2x2 切分,1000 张图变成 4000 张训练样本。注意new_bw和new_bh可能超过 1,因为框可能跨瓦片边界,训练时 YOLO 会自动裁剪,但最好在写之前 clamp 到 0 到 1。
推理验证时,我一般会拿一段无人机实拍视频,抽 100 帧跑推理,统计漏检和误检。重点看三类:远处小目标、密集停车场、树荫遮挡。如果漏检集中在某个场景,就针对该场景补数据或者调conf阈值。YOLO11 默认conf=0.25,无人机场景可以降到 0.15 提高召回,但误检会增多,需要权衡。
从那以后我每次拿到新数据集,都强制先跑一遍类别映射校验和实例计数,再开始训练。这两个脚本花不了五分钟,但能省掉后面几小时的返工。希望帮到你。
本文还有配套的精品资源,点击获取