
做目标检测的人应该都有过类似的体验跑通官方示例容易真正想训练自己的场景数据时第一个拦路虎往往是“没有合适的数据集”。尤其是无人机视角下的车辆检测很多朋友习惯直接拿地面相机的公开车辆数据集来训练结果一到航拍画面上漏检、误检、置信度飘忽不定问题层出不穷。这期整理的就是专门针对无人机拍摄场景的车辆检测数据集采用YOLO标注格式图片和标签文件都处理得规规矩矩拿到手就能直接开练。我会把这份数据集的细节、使用方式以及训练中容易踩的坑一并讲清楚希望能帮正在做智慧交通、安防巡检、车辆统计这类项目的朋友省下整理数据的时间。1. 为什么无人机视角的车辆检测数据集这么难找很多初学者会问车辆检测的数据集不是很多吗怎么还要专门整理无人机视角的这里面的差别非常大。地面视角的车辆检测数据车辆在画面里往往占据几十甚至上百像素轮廓清晰能看到车头、车尾、车门这些“侧面特征”而无人机拍摄的画面是从上往下看的俯视图车辆在画面里就是一堆“车顶”目标小、密度高、互相遮挡也很常见。如果直接把地面数据训练的模型搬到航拍图上模型学到的是侧面视觉线索遇到俯视场景自然就不认识了。1.1 一张航拍图和一张地面照片的差别到底在哪先说尺度。以常见的1080p航拍画面为例一辆普通轿车在画面里通常只有40到80像素的宽度有些偏远区域的车甚至只有20个像素左右。地面数据里常见的车辆目标动辄200像素以上这两种尺度下模型提取的特征完全不同。小目标在卷积神经网络的高层特征图中会被压缩到几乎没有信息这也是很多地面模型在航拍图上表现差的核心原因之一。再看观察角度。地面拍摄时车身的侧面、轮胎、车窗反射这些特征是判据无人机俯拍时看到的主要是车顶轮廓、天窗、车顶颜色和周围阴影。数据分布差异这么大模型没有见过这样的特征组合强行迁移结果自然不理想。还有一个经常被忽视的因素光照和阴影。无人机一般在白天作业阳光直射下车辆会形成明显的投影阴影和车身边界混在一起非常干扰检测。地面数据集里阴影问题相对轻因为拍摄角度让阴影大多落在车体侧后方不会把车的轮廓吞掉。俯拍时阴影可能把相邻车辆连成一片这对标注和检测都是额外的挑战。1.2 航拍数据的标注难点与数据集价值无人机画面里车辆又小又多标注一枚框往往需要把图片放大好几倍才能看清楚边界。原始画幅里有上百辆车的情况下标注员得一个个放大、确认、拉框工作量比地面数据大得多。这也是市面上免费航拍数据集稀少的主要原因——不是没人拍而是没人愿意花时间精标。这期分享的数据集就是在这个背景下整理的目标是做一个“拿到就能用”的基础版本。它适合三类人一是想验证自己在航拍场景下算法效果的研究者二是做交通流量统计、停车场监测等项目的开发者三是还在学习目标检测流程、想找一份真实场景数据练手的新手。用这份数据先把整体流程跑通再根据自己业务补场景数据会比自己从零采集、标注高效得多。2. 数据集全景与YOLO标注格式解析这份数据集我事先整理成了标准的目标检测工程结构图片集和标签集分开存放同时附上了一份配置文件。先别急着打开训练脚本花几分钟把数据集的“长相”看清楚会帮你省下后面很多排查时间。2.1 数据集里有什么规模、场景与类别分布整个数据集包含约3200张无人机拍摄的图片覆盖城市道路、高速互通、十字路口、园区停车场、乡村公路等常见场景。图片以1920×1080分辨率为主少部分来自4K航拍设备尺寸不统一这一点和实际使用情况很吻合。标注框累计约18000个共4个类别car、truck、bus、van。需要提醒一下这4个类别的样本数量并不均匀car占了绝大多数truck和bus相对少一些。这种不均衡在真实采集数据里非常普遍后面我会单独讲怎么处理。压缩包里包含三个部分images存放图片、labels存放同名的txt标注文件、data.yaml是描述数据集信息的配置文件。如果你拿到手的是某个平台上的分享压缩包解压后建议先不要直接开训按下面几步做一次体检。数据集的完整性决定了训练的成败这一步是最值得花时间的。2.2 YOLO标签文件到底怎么读YOLO格式的标注文件是纯文本每张图片对应一个同名txt文件文件里每一行代表一个目标物体。标准格式是类别编号、归一化后的中心点x、中心点y、归一化后的宽度w、归一化后的高度h。举个例子假设类别定义是0为car、1为truck、2为bus、3为van那么某张图里有四辆车它的label文件内容可能是这样的0 0.532345 0.671023 0.043210 0.051232 0 0.123456 0.456789 0.033890 0.042154 1 0.803412 0.392001 0.071234 0.062178 3 0.615123 0.223411 0.028345 0.036201第一行的含义是类别为car目标中心点在图片横向53.23%、纵向67.10%的位置宽度约占图片4.32%高度约占5.12%。所有坐标都缩放到0到1之间这样做的好处是和图片分辨率无关1920×1080和3840×2160可以混在一起训练模型不需要关心输入尺寸。但代价是精度依赖浮点数一旦标注工具导出时没有归一化坐标直接写成了像素值训练就会出大问题这一点在后面的踩坑部分细说。2.3 拿到数据集后的第一件事完整性体检不要因为数据集是别人整理好的就跳过检查。实际操作中我最常遇到的情况是图片后缀大小写不一致导致标签匹配不上或者某些txt是空文件、里面什么目标都没有或者类别编号超出了配置文件里的类别总数。这些隐藏问题不会让训练直接报错而是会不动声色地拖低精度排查起来最费时间。我通常用下面这个Python脚本快速体检标准库就能完成不需要额外装依赖import os from collections import Counter img_dir images label_dir labels img_exts {.jpg, .jpeg, .png, .bmp} imgs set() for f in os.listdir(img_dir): ext os.path.splitext(f)[1].lower() if ext in img_exts: imgs.add(os.path.splitext(f)[0]) label_exts {.txt} labels set() for f in os.listdir(label_dir): ext os.path.splitext(f)[1].lower() if ext in label_exts: labels.add(os.path.splitext(f)[0]) print(无标签的图片:, len(imgs - labels)) print(无图片的标签:, len(labels - imgs)) all_classes Counter() bad_lines [] for f in labels: path os.path.join(label_dir, f .txt) with open(path, encodingutf-8) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: bad_lines.append((f, 字段数不为5)) continue cid float(parts[0]) if cid ! int(cid) or int(cid) 0 or int(cid) 3: bad_lines.append((f, 类别编号异常)) continue coords list(map(float, parts[1:])) if max(coords) 1.0001 or min(coords) -0.0001: bad_lines.append((f, 坐标越界)) continue all_classes[int(cid)] 1 print(标签行总数:, sum(all_classes.values())) print(类别分布:, dict(sorted(all_classes.items()))) print(异常行数:, len(bad_lines))跑一遍下来图片和标签数量是否对得上、类别分布是否合理、有没有坐标越界基本就一目了然了。我见过不少人跳过这一步直接开训后面损失震荡怎么调都调不好最后发现是标签文件里混进了几千个像素坐标的脏数据。这种问题越早发现越好。3. 从下载到训练把数据集喂给YOLO的完整流程确认数据集没有明显问题之后接下来就是把它组织成目标检测框架能直接读取的工程结构。不同框架对目录结构的要求大同小异只要抓住核心规则一通百通。3.1 目录结构与data.yaml配置YOLO系训练框架通常要求图片和标签分开存放并且训练集、验证集都要有对应的图片目录和标签目录。推荐的结构是这样的dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamlimages/train里放训练图片labels/train里放对应对应的txt标注文件文件名必须完全一致包括后缀名的大小写。val目录同理。data.yaml放在dataset根目录下内容很固定train: ./images/train val: ./images/val nc: 4 names: [car, truck, bus, van]路径我用的是相对路径这样整个dataset文件夹挪到哪里都能用。如果你用的是线上训练环境或者路径容易变也可以改成绝对路径。nc是类别数量names是类别名字列表顺序必须和标注文件里的数字编号一一对应这一点一旦错位模型训练出来就是“张冠李戴”检测结果全部错乱。3.2 训练集和验证集怎么划分才合理很多朋友拿到数据后直接按比例随机打乱划分比如8:2切分。对普通图片数据集这么做没问题但对无人机航拍数据这个做法有一个容易被忽略的隐患无人机拍摄的连续帧之间高度相似。同一个架次绕着一个路口飞一圈可能拍下几百张图随机划分后某条路的画面会同时出现在训练集和验证集里模型等于提前“看过”验证题的答案指标自然好看但一换到新场景就露馅。我建议按采集场景来划分而不是按文件随机切。比如压缩包里如果有多个子目录尽量保证同一条路、同一个地块的图片只进入训练集或只进入验证集不要混用。这样验证集才有真正的泛化意义。如果没有场景信息也可以对图片做抽帧式的划分每隔几帧取一张进验证集效果比纯随机好不少。3.3 开训前自检清单即便数据集已经整理好我还是建议在启动训练前花30秒过一遍自检清单。这里直接把我的习惯列出来你可以对照检查图片数量与标签文件数量一致没有“孤儿”图片或“孤儿”标签所有txt里的坐标都在0到1之间类别编号是0到3之间的整数与data.yaml的names顺序一致没有内容是空的txt文件如果存在空文件确认该图确实无目标删掉txt即可训练集和验证集的图片不存在文件名重复各类别在训练集里都有一定数量的样本像bus、van这类少数类最少也要有几十个实例检查完这些再开训基本能保证问题出在模型调参而不是数据格式上。数据格式问题上花的时间永远是最值得的。4. 训练实测记录三个高频问题与完整排查链路我自己在拿类似无人机数据训练时踩过的坑比想象中多。下面这三个问题基本是每次都会碰到的我把完整的排查链路写出来让你能复现思路而不是只得到一个结论。4.1 问题一损失函数前期震荡结果发现是标签遗漏第一次用这份数据训练时我开了300轮结果前20轮loss像心电图一样上蹿下跳。一开始我怀疑是学习率太大调低之后有所缓和但验证集精度仍然异常mAP0.5一直卡在0.3上下。排查到后面我翻看训练日志发现每个batch的target数量时多时少平均每张图的标注数只有0.8个左右而实际画面里平均应该有两到三辆车。这说明大量图片的标签文件根本没有对应的目标行。排查过程是这样的先统计标签文件的行数分布发现很多txt文件大小只有几个字节打开一看是空的再往前查发现数据集压缩包里的“空标注”图片在转换格式时被标注工具跳过但没有删掉对应的txt占位文件。对于某些目标检测框架空txt不会被当成错误只是这张图变得没有任何目标参与训练数据量就这样悄悄缩水了。解决办法分两步第一步写脚本把所有内容为空的txt文件移出标签目录避免它占用训练样本第二步如果某些图确实有目标但txt为空就需要重新标注。对这类数据来说删除空文件之后训练数据有效量大约提升了15%到20%loss曲线立刻平滑了很多。这个坑的教训是训练日志里每个batch的target数量一定要看它是数据质量最直接的晴雨表。4.2 问题二小目标漏检严重mAP数值却不错另一件让我困惑的事是验证集mAP已经到0.5以上了结果拿一段无人机实拍视频一测小目标漏得乱七八糟。后来我把验证集的情况单独剖开看发现mAP高是因为验证集里如果包含了大量相对较大、清晰的车辆或者和训练集场景重叠的图片整体指标被“拉高”了。一旦单独统计图像中小于32×32像素的目标检测率惨不忍睹。排查的方法是把验证集按目标尺寸分成几个区间分别算AP目标宽度小于32像素的AP32到64像素的AP大于64像素的AP。对比之后发现差距悬殊问题就清楚了。模型不是不会检测车辆而是对小目标没有足够的分辨能力。这类问题的通用解法是提升输入分辨率、增加小目标增强策略或者用切图推理的思路把大图切成小块逐块检测再合并结果。切图方案在无人机场景尤其有效因为原图画幅大、目标小直接缩放到模型输入尺寸时小目标丢失严重切块之后相当于变相放大了目标。4.3 问题三类别分布严重不均如何用采样和增强拉齐数据集中car约占总标注量的七成以上truck和bus占比很小。直接训练出来的模型往往car的AP很高truck、bus的AP低得没法看。盲目对少数类重复采样会让模型反复看到同一批样本结果是过拟合训练集AP漂亮一换场景就崩。我的做法是组合拳首先给少数类复制粘贴增强从已有标注框里抠出truck、bus的目标经过缩放、旋转后随机粘贴到没有目标或者目标稀疏的道路区域同时生成对应的标注行其次在训练配置里增大对少数类的采样权重让每个epoch里少数类参与训练的次数更多最后验证集使用最原始的分布不做任何过采样这样看到的指标才真实。这套组合拳跑下来truck和bus的AP大约能提升10个点左右car的AP基本不受影响。5. 别止步于跑通把数据集用到自己业务里的几个建议数据集的最终价值在于能用起来。这份无人机车辆检测数据集作为基础数据完全可以支撑很多实际项目但直接照搬训练结果去部署还远远不够下面这几个方向是我实操后觉得最值得投入的。5.1 模型选型小目标优先的几条路线先给结论小目标占比高的场景不一定是模型越大越好而是要看输入分辨率和特征图的匹配关系。大模型参数量多、特征提取能力强但如果输入图被缩到640×640远处的小车可能只占两三个像素再强的模型也无米下锅。我测试过几条路线一是用高分辨率输入直接把模型输入从640提升到1280小目标召回率明显上升代价是显存占用翻倍推理速度下降二是切图推理把原图切成若干个带重叠的块每块单独检测再合并速度快、显存友好是实现成本最低的方案三是使用带小目标检测层的检测器结构这类模型在特征金字塔上额外增加了针对小目标的浅层特征输出对于航拍数据比较友好。如果你的项目对实时性要求高建议用轻量模型加切图推理的组合。我在一个车辆计数项目中用过这套方案无人机图传分辨率为1920×1080切成三四块并行推理整体帧率还能维持在可接受的范围内。5.2 数据增强俯拍场景哪些增强真正有效无人机俯拍图的一个特点是旋转不变性很强——车顶朝上还是朝左对检测任务来说没有语义差异。因此水平翻转、垂直翻转、90度旋转这类几何增强可以大胆开几乎不会破坏标注语义。我一般会把翻转增强概率开到0.5旋转也开到较大值这相当于不花成本扩充了小目标样本的朝向多样性。相比之下马赛克增强要谨慎一点。马赛克拼接对提升模型鲁棒性很有用但无人机原图里的目标本身很小拼接时如果把大量目标裁切到图像边缘很多小目标只剩下半个框模型学到的是残缺特征反而有害。我的经验是把马赛克概率调低到0.3左右并配合更大范围的随机缩放让拼接后的目标尺度分布更接近真实场景。色彩类的增强对无人机数据也有用。航拍图经常遇到曝光不均、云影遮挡等问题适当的HSV扰动可以提升模型对光照变化的耐受度。但要注意饱和度扰动幅度不要过大否则车体颜色被改得面目全非反而干扰类别判断。5.3 如果拍摄场景变化大怎么扩充和再标注实际项目里你手里的无人机拍摄高度、云台角度、传感器型号都可能和这份数据集不同。一个非常高效的做法是先用这份基础数据训练一个初版模型然后对你自己采集的视频做预标注生成初步标签再由人工修正。别一上来就手动在几千张图上画框那样太浪费人力。预标注加人工修正时有个经验视频抽帧不要逐帧标每隔2到4帧抽一帧即可。相邻帧之间的画面几乎没变化逐帧标注纯属重复劳动。抽帧频率取决于无人机飞行速度和车辆运动速度飞得越快抽帧间隔越小。还有一个小细节预标注结果一定要按前面提到的坐标归一化规则检查很多自动标注工具会导出像素坐标混进训练集后会让损失函数突然恶化这种问题几乎每批新数据都会遇到。如果项目场景跨越很大比如除了白天还要用夜间红外数据基础数据集就只能作为热身。更合理的做法是在这份数据上先确定好网络结构和训练配置然后引入少量目标场景的真值数据做微调这种“基础数据定框架、场景数据做微调”的路径投入产出比是最高的。最后再分享一个个人习惯我每次把数据集发给朋友或同事都会提醒他们先跑一个50轮的mini实验。不是为了看精度而是看数据管线通不通畅、loss曲线稳不稳定、标签有没有脏数据。等mini实验确认没有低级错误再上全量数据调参数。数据集的规模固然重要但标注质量的一致性才是决定模型上限的命门。希望这份数据集能帮你少走几步弯路把时间花在真正有价值的算法优化上。