
无人机视角下的车辆检测是目标检测里一个很有实际价值的场景。和常见的路面监控视角不同无人机从高空俯视车辆在画面里往往只有几十个像素甚至更小背景从屋顶、树冠到道路、停车场全都有尺度、纹理、遮挡形态都产生了很大变化。平时用COCO或者OpenImages预训练权重直接去推理无人机画面漏检误检会非常明显。这不是模型结构的问题而是训练数据和推理场景的分布对不上。所以做车流量统计、无人机巡检、智慧停车这类项目时手里有一批高质量的俯视车辆数据往往比换什么模型骨架都更管用。这期分享的是目标检测数据集系列的第136期一套基于YOLO标注格式的无人机拍摄车辆检测数据集。和网上那些随手爬图、标签混乱的资源不同这套数据经历了筛选、清洗、人工标注、格式校验这几道工序标注格式统一为YOLO通用的txt可以直接配合YOLOv5、YOLOv8、YOLOv11这些常见框架使用。适合正在做车辆检测模型训练、手里又缺航拍数据的人也适合刚接触YOLO、想搞清楚标注格式和训练全流程的初学者。1. 数据集的出发点为什么无人机车辆检测不能直接套用地面模型1.1 无人机视角与地面视角的本质差异很多第一次接触航拍检测的人会问我都是车辆检测用路面监控的数据集训练出来的模型放到无人机画面上不也能凑合用吗我给出的回答很直接效果通常不好而且差距不是一点半点。原因可以从目标形态、场景分布、几何尺度三个层面来分析。地面监控摄像头通常安装在距离路面5到8米的高度车辆以侧视图为主车灯、车窗、轮毂这些特征很明显模型学到的是侧面车辆的特征组合。而无人机常见飞行高度在50米到150米之间看到的绝大部分是车顶车辆呈现俯视图轮廓更像一个矩形或椭圆形侧面的车窗、车灯细节几乎不可见模型之前学到的特征大部分用不上。再看目标尺寸。地面监控画面里一辆轿车可以占到全图面积的30%到40%模型“看”得很清楚。无人机低空拍摄时同样一辆车可能只占十几个像素到了高空四五个车位的一组车聚在一起也不过是一个小光斑。目标在小分辨率下缺乏足够的纹理信息检测器很容易漏检。背景也有很大区别。路面监控的背景相对单一主要是柏油路面、车道线、交通标志。无人机画面里的背景则包括建筑屋顶、树冠、绿地、水面、裸土、阴影、车辆密集的停车区等复杂度高出好几个量级。尤其是车辆聚集区域排列紧密、互相遮挡普通模型经常会给出成片的错误框。所以结论很明确面向地面视角训练的模型迁移到无人机视角之后精度下降是必然的。真正要把检测模型用在无人机业务上绕不开俯视样本的训练数据。1.2 公开数据集为什么不够用既然道理讲得通那直接用公开的航拍数据集不就行了我也试过但实际用下来会遇到几个很现实的问题。第一是格式分裂。有的公开数据给的是VOC XML有的是COCO JSON有的是RLE掩码还有的只有视频没有逐帧标注。要把这些数据统一成YOLO格式需要写一堆转换脚本每套数据都要单独处理一遍类别映射和坐标换算非常耗时。第二是类别定义不一致。同样是“卡车”有的数据集把厢式货车、皮卡、重型卡车全部并成一类有的则拆成好几类。标注不一致会让模型在分类边界上出现严重混乱。比如某套数据把面包车归为car另一套把面包车归为truck合并训练时同一类物体对应不同标签模型学的分类特征会被抵消。第三是质量参差不齐。公开数据集中经常出现框偏移、重复标注或类别标错的问题。之前处理过一份号称精标的数据可视化检查后发现大约8%的框存在明显偏移部分标注框甚至落在了车辆轮廓之外这样的数据根本没法直接用。这套数据集在整理时先把上述问题都规避掉了每张图片都经过人工审核类别定义有明确标准所有标签统一转为YOLO txt格式并提供类别配置文件。使用者拿到之后最耗时间的数据清洗工作已经跳过。1.3 数据集的实际定位它的用途集中在以下几个方面交通管理中用于车流量密度估算无人机巡检中用于发现车辆聚集和异常滞留智慧城市中用于停车区域饱和度分析以及作为目标检测框架学习的实操数据集。对刚入门的朋友来说这套数据也是一个合适的练手样本配合YOLOv8跑通训练、验证、推理全流程能比较清晰地理解检测任务的实现细节。2. 数据集的构成与整体设计思路2.1 基础信息一览先给一个整体画像方便大家对照判断是否适合自己的项目项目参数图像总数约12000张标注框总数约88000个图像尺寸1280x720 至 1920x1080标注类别轿车、卡车、公交车、摩托车、自行车标注格式YOLO txt图像格式jpg训练/验证划分约9:1图像尺寸没有统一缩成小图保留了原始分辨率。原因很直接无人机视角下的目标普遍偏小如果把图像压缩到640像素再进行标注小目标的信息会严重丢失标注框也很难画准。保留原始分辨率使用者可以根据自己显卡的显存情况决定训练时的输入尺寸要给模型多留一些底层的空间。2.2 场景覆盖与飞行高度数据采集时覆盖了多种飞行高度和拍摄角度。150米以上的高空视角以小型轿车为主目标小、分布密集适合练小目标检测能力30到80米的低空视角车辆轮廓更清晰偶尔会出现遮挡适合练习车辆形态识别另外还专门加入了倾斜视角的画面无人机在巡检时往往不是垂直向下拍摄模型如果只见过正俯视图像遇到斜视角就很容易漏检。场景类型上包含了城市主干道、高架匝道、立交桥、停车场、乡村道路、产业园区等光照上覆盖了晴天、阴天、强逆光和部分夜间红外画面。这些条件都是为了提升模型在不同业务环境里的泛化能力。实际项目中无人机拍摄的时间、季节、地点都会变化训练数据如果过于单一部署效果就不可控。2.3 类别体系的取舍类别只保留了5个大类没有继续细分成轿车、SUV、面包车等更细的类别。这样做的原因是在高空俯视画面里轿车和SUV的顶部轮廓差异有限细分下去会让标注人员频繁面对“无法确定”的情况徒增噪声。与其分类过细导致标注可靠性下降不如合并成大类让模型先稳定区分“车”的几种主要形态。摩托车和自行车单独列出是因为在无人机画面中这类目标小、长宽比明显一旦混入“轿车”类模型会把它们当成异常小目标严重影响检测稳定性。单独分类后在统计混合交通流的场景中也能获得更有用的数据。使用这套数据时建议不要随意修改类别ID保持与data.yaml中的映射一致否则训练时会直接报错或出现类别错位。3. YOLO标注格式解析与校验方法3.1 标注文件的内容格式YOLO格式的每个标签文件对应一张同名图片文件中每一行表示一个目标格式为类别ID x_center y_center width height其中类别ID是从0开始的整数四个坐标值都是归一化后的浮点数取值范围在0到1之间。例如下面这行3 0.418234 0.583216 0.214217 0.128350含义是一个目标属于类别3在归一化坐标中目标中心点位于图像横向41.82%、纵向58.32%的位置宽度占图像宽度的21.42%高度占图像高度的12.84%。转换成像素坐标就能画出标注框。3.2 为什么必须使用归一化坐标这个问题很多初学者问过。归一化是YOLO系列框架的统一约定也是多尺度训练的基础。训练阶段模型会随机把输入图像缩放到不同尺寸比如640x640、1024x1024。如果标签存的是绝对像素坐标缩放后所有框的位置和大小都要重新计算而归一化坐标只表示相对比例图像缩放后坐标值不需要任何变化模型可以直接读取。推理阶段输出的目标框也是归一化形式再乘以当前图像宽高还原到像素坐标。统一使用归一化坐标能让标签、模型输出、后处理三个环节无缝衔接减少一次不必要的坐标转换误差。3.3 用脚本验证标注是否可靠即使发布前做了多轮检查拿到数据后我仍然建议先执行一次快速验证。验证脚本通常做三件事检查坐标是否越界、检查类别ID是否合法、检查标签与图片文件是否一一对应。简单示例import os label_dir labels/train img_dir images/train num_classes 5 for f in os.listdir(label_dir): if not f.endswith(.txt): continue img_file os.path.join(img_dir, f.replace(.txt, .jpg)) if not os.path.exists(img_file): print(f[missing image] {f}) with open(os.path.join(label_dir, f), r) as fh: for line in fh: parts line.strip().split() if len(parts) ! 5: print(f[bad line] {f}: {line.strip()}) continue cls int(parts[0]) x, y, w, h map(float, parts[1:]) if cls 0 or cls num_classes: print(f[bad class] {f}) if x 0 or y 0 or w 0 or h 0 or x w / 2 1 or y h / 2 1 or x - w / 2 0 or y - h / 2 0: print(f[out of bounds] {f})再配合可视化检查随机抽取300张图依次解析标签并画出矩形框人工扫一遍。视觉验证主要是看框是否贴合目标、有没有明显偏移。经历过这种双重验证之后训练时因为标签问题导致的诡异错误会少很多这类问题排查起来最痛苦的一点是模型训练不报错但效果怎么调都不对。4. 目录结构、训练配置与数据增强4.1 标准的YOLO数据集目录下载解压后数据集的目录结构如下drone-vehicle/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml的内容path: /your/actual/path/drone-vehicle train: images/train val: images/val names: 0: car 1: truck 2: bus 3: motorcycle 4: bicycle使用时要重点注意path字段。很多人直接把示例路径照搬运行之后报找不到图片。正确做法是把path改成自己本机解压目录的绝对路径且建议统一使用正斜杠。4.2 训练集与验证集的划分逻辑这里有一个容易被忽略的关键点划分时不是简单随机洗牌而是按拍摄片段归属分组划分。同一个连续无人机片段的相邻帧场景高度相似如果一部分出现在训练集、另一部分在验证集模型就相当于提前见过“答案”验证结果会虚高代表不了真实能力。按片段分组后验证集里的画面和训练集完全来自不同时段评估指标才更有参考价值。4.3 YOLOv8实战训练命令推荐使用YOLOv8s或YOLOv8m作为初始权重配合下面的训练命令yolo detect train \ modelyolov8s.pt \ data/your/path/drone-vehicle/data.yaml \ imgsz1024 \ epochs150 \ batch16 \ patience20 \ cacheram \ projectdrone_vehicle_train单独解释一下imgsz1024这个参数。无人机画面的目标普遍偏小如果训练尺寸仍然用默认的640很多小目标在特征图上的尺寸只有一两个像素特征提取无从谈起。放大到1024或1280后目标占据的有效特征点明显增多小目标召回率会显著上升。代价是显存占用和训练时间同步增加如果显卡显存在12GB以下可以把batch调小到8或者先用imgsz896过渡。4.4 数据增强的实用配置训练前最好在ultralytics的配置中重点关注几个增强参数mosaic拼接增强默认1.0但在无人机场景下建议改为0.5到0.8拼接幅度太大时车辆密集区域可能被切碎反而增加标注噪声。hsv_h、hsv_s、hsv_v色彩增强幅度建议调低因为航拍图像颜色相对稳定过度调节色彩会破坏真实场景的分布。fliplr、flipud水平翻转和垂直翻转在无人机数据里几乎无副作用可以放心开。scale轻微缩放有助于适应不同飞行高度建议设置0.3到0.5。训练过程中如果发现验证集mAP继续下降但上升缓慢可以适当增加epochs并配合早停patience参数给模型更充分的收敛时间。5. 无人机视角下标注车辆的细节与避坑经验5.1 水平框的贴合原则无人机视角下的目标既可能是正俯视也可能是斜视。本数据集全部使用水平矩形框不采用旋转框。水平框的主流检测框架支持最广泛部署成本最低。标注时遵循一条原则框必须贴近车辆可见边缘不额外留白。如果车辆呈现明显倾斜角度框的长边应沿着车身主要方向对于密集停车场中斜向停放的车辆矩形框可能会同时框进邻近车辆的一部分。这种情况下我会优先保证覆盖目标本身宁可边框稍紧也不刻意扩框。5.2 遮挡与截断目标怎么处理无人机低空拍摄时树冠、电线杆、路灯都可能遮挡车辆。标注规则定为遮挡面积小于50%的目标按可见部分正常画框遮挡面积超过50%或目标可见像素过小长边小于20像素直接放弃标注。截断在图像边缘的目标框可以延伸到图像边界之外保持原来大小即可。如果强行把所有小目标或严重遮挡目标都标出来标签噪声会大幅增加模型训练得到的反而是混乱的监督信号。5.3 容易混淆的类别边界的处理涉及车辆分类时边界情况是最让人头疼的。整理的规范如下面包车和轻客类目标车身长度明显小于标准货车、形态接近于轿车的标注为car车身更长、车厢高度明显且呈封闭箱体形状的标注为truck。三轮摩托车和两轮摩托车统一标注为motorcycle不再拆分。遇到角度特殊、实在无法判断类型的优先按整体轮廓匹配最相近的类别并做出标注记录后期可以在类别分布统计中看到这些边界样本的数量。5.4 视觉复检的具体做法将标签绘制回原图的脚本可以这样写import cv2 def draw_labels(img_path, label_path): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: cls, x, y, bw, bh line.strip().split() x, y, bw, bh map(float, (x, y, bw, bh)) x1 int((x - bw / 2) * w) y1 int((y - bh / 2) * h) x2 int((x bw / 2) * w) y2 int((y bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, cls, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) return img抽取300张图批量生成预览按异常程度排序后人工过一遍。这一步能发现许多单纯靠统计脚本发现不了的问题比如框压在了错误目标上、类别标反等。实际标注中这类问题在密集停车场区域出现频率最高不要跳过复检环节。6. 常见问题与排查技巧实录6.1 标签与图片对应不起来怎么办YOLO要求标签文件名与图片文件名完全一致只是扩展名不同。解压后如果出现0字节标签、缺失标签或标签与图片数量不匹配优先写个快速脚本对比两边文件名前缀。常见原因是压缩包传输过程中丢失文件或系统解压不完全。排查手法如下先统计images/train和labels/train下的文件数量数量一致不代表全部正确还要遍历一遍所有前缀做配对检查。只多出一个文件都可能导致训练过程中出现“无标签”的异常提示。6.2 训练报No labels found出现这条提示时首先确认三件事data.yaml中path字段是否指向绝对正确路径labels目录是否位于images目录的同级位置路径中是否混入了无法识别的转义符。Windows环境下最容易出现路径问题把反斜杠统一改成正斜杠即可。另外还需要确认labels目录下是否有子目录。YOLO要求labels/train目录下直接存放txt文件不要再嵌套子目录嵌套之后检测器会找不到标签文件。6.3 航拍小目标漏检率偏高这个问题在真实部署中极为常见。建议按顺序尝试三个方案。第一推理时把imgsz提升到1280Le小目标在输入图像中的有效尺寸变大网络能够提取到更多特征。第二使用测试时增强对原始图像水平翻转后再推理一次将两次结果合并能稳定提升召回。第三对大幅面图片采用滑窗分割检测把原始图片切成若干子图依次推理后再把检测框坐标映射回原图。这个方法在面对4K甚至更大分辨率的无人机影像时尤其有效缺点是推理总耗时变长需要根据业务时延要求做取舍。6.4 类别不平衡怎么处理motorcycle和bicycle两类在数据集中占比相对低。如果发现模型在验证集上对这两个类别召回率明显偏低优先做法是修改损失权重让模型在训练中更加关注样本量少的类别。也可以通过数据增强重复采样把少数类图像在训练中进行多次增强增加该类别样本出现频次。我一般不会为了平衡数据而盲目复制原始图片因为高度相似的重复样本容易引发过拟合实际收益不如下调损失权重来得稳定。7. 这套数据的扩展价值与后续思路7.1 从检测框到业务指标的进阶YOLO输出的检测框只是基础结果真实业务中往往需要进一步计算。车流量统计时可以把检测框中心点按照道路网格聚合估算不同区域的车流密度违章分析场景中可以检测禁停区域内目标框持续存在的时间停车场管理则可以直接用检测框计算车位占用率。做好这类后处理数据集的业务价值会远远超过单纯训练一个模型。7.2 配合跟踪模块实现持续检测单帧检测结果只能看到某一瞬间的目标位置要做持续监控必须引入跟踪模块。ByteTrack这类纯检测框跟踪方案和YOLO配合度很高不需要额外训练ReID模型部署简单且帧率损耗低。无人机视频推理场景中跟踪不仅能解决计数重复统计问题还能平滑单帧漏检带来的抖动。如果视频帧间隔较大可以适当调低检测置信度阈值让跟踪器有更多候选框可用。7.3 模型训练之外的注意事项部署到不同区域、不同气候条件时务必先用少量本地数据做一次小规模测试再决定是否补充标注。无人机作业的洲际差异、季节差异、光照差异都很大单靠一套公开数据很难覆盖所有实际场景。把这套数据当作预训练基础再结合业务现场采集的数据做二次微调是比较稳妥的做法。8. 最后说一点个人体会我从整理这批数据到反复验证最深的感受是数据集的难点从来不在标注动作本身而在于标注规则的稳定性和场景多样性之间的平衡。不同的标注人员对同一辆车的判断可能不同如果规则模糊标签噪声就会悄悄渗透进训练集最后模型的表现会变得不可控。大家在用这套数据训练时别只盯着mAP看。建议把每个类别的PR曲线都拉出来对比尤其是摩托车和自行车因为它们对应了数据集中少样本和困难样本的典型状态。如果一个类别测试结果明显偏低大概率不是模型结构的问题而是该类别的场景覆盖和标注边界需要再深挖。这套数据集后续我还会补充不同气候条件的场景以及在遮挡环境下车辆检测的图像。现有版本的12000张图已经足够用于日常训练和模型验证。有需要的朋友可以直接拿这套数据跑通流程再针对自己的业务场景逐步扩充这样能少走很多弯路。