ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

无人机视角交通目标检测:YOLOv8小目标训练与OBB旋转框实战指南

无人机视角交通目标检测:YOLOv8小目标训练与OBB旋转框实战指南 简介这是一份面向智能交通、自动驾驶与智慧城市应用的无人机视角交通目标检测YOLO格式数据集。数据集包含956张训练航拍图与169张验证航拍图覆盖公交、轿车、厢式货车、摩托车、船舶、商业车辆、工程车辆、特种车辆及区域特色车辆共9类陆海交通工具标注采用YOLO格式并经航空影像专家双重校验。压缩包共2000个文件以1125个txt标注文件、873个jpg航拍图片为主体另含1个yaml配置与1个docx说明文档整体大小83.14MB目录结构清晰便于直接投入模型训练。该数据集突出高空视角特性涵盖城市道路、港口、建筑工地等多样环境及日间、黄昏、晴/多云等光照条件包含车辆转向、泊车、编队行驶等动态样本可有效补充传统地面视角数据提升目标检测模型的多维度泛化能力。已有125人学习适合用于车流监控、违章识别、应急救援等场景的开发与算法验证。1. 无人机视角交通目标检测数据集高空小目标不白嫖先搞清这批图怎么用做无人机视角的交通目标检测最烦的不是模型是数据。公开数据集里 KITTI、BDD100K 全是车载平视视角你拿它训出来的模型一放到高空俯视图上漏检率直接翻倍——因为目标尺度、遮挡模式、光照角度完全不是一回事。这个名为“无人机视角交通目标检测数据集.zip”的资源在 YOLO 生态里算是比较难得的垂直场景包图源是无人机航拍视角覆盖车辆、行人、骑行者和交通标志等类别标注格式能直接喂给 YOLO 系模型。适合谁你要做车流密度分析、违章抓拍、智慧园区巡逻或者想把手头的 VOC 模型迁移到高空场景这批数据就是替你省采集和标注时间的。它解决的核心问题只有一个把“从天上往下看交通”这件事的数据分布拉到你模型训练能用的水平。2. 数据集解剖目录结构、标注格式与类别分布拿到压缩包先别急着解压扔给训练脚本先花五分钟把目录和标注格式摸清后面能省半天排错时间。无人机视角数据集的坑往往不在模型而在标注文件和你预期不一致。2.1 目录结构与标注文件先看 json 里的坐标体系压缩包解压后常见做法是这么组织的unzip 无人机视角交通目标检测数据集.zip -d drone_traffic/ tree drone_traffic -L 2我一般会先跑上面这行命令看两层结构。正常的无人机交通数据集目录应该是drone_traffic/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── annotations/ │ ├── train.json │ ├── val.json │ └── test.json └── classes.txt如果 annotations 下是 json 而不是 xml那大概率是 COCO 格式或自定义 JSON 结构。这里有个高频分叉点很多从无人机视频抽帧做的数据集标注框是旋转框OBBOriented Bounding Box因为俯视图下车头朝向各异水平框会框进大面积背景。先读一个标注看看import json with open(drone_traffic/annotations/train.json, r, encodingutf-8) as f: data json.load(f) # COCO 风格的话keys 通常是 info/images/annotations/categories print(data.keys()) print(data[annotations][0])常见的两种格式区别如下格式类型坐标字段适合的检测方式COCO 水平框bbox: [x, y, w, h]水平框检测YOLOv8 默认OBB 旋转框segmentation四点或rbox: [cx, cy, w, h, angle]旋转框检测YOLOv8-obb / MMRotate如果看到segmentation字段是四个点坐标那这批标注很可能支持旋转框训练。你直接拿它当水平框训不是不行但密集车辆场景下精度上限会被框的“多余背景”按住后面第 4 章细说。2.2 类别分布与适用场景从车辆类型到小目标密度把类别统计跑一遍比看任何 README 都直观from collections import Counter cat_ids [ann[category_id] for ann in data[annotations]] counter Counter(cat_ids) print(counter) # 再看目标尺寸分布小目标占比 small 0 for ann in data[annotations]: w, h ann[bbox][2], ann[bbox][3] if w 32 and h 32: small 1 print(f小目标32px占比: {small / len(data[annotations]) * 100:.1f}%)这段代码的逻辑第一个 Counter 统计每个类别出现次数让你知道类别是否均衡第二个统计小目标占比这是无人机交通场景的命门——航拍高度 50 米以上一辆普通轿车的像素宽度经常只有 20 到 40 像素属于典型的小目标检测场景。这个统计结果直接决定你后面训练怎么配参小目标占比高img_size就不能设太低mosaic增强的比例也要谨慎调。另一个值得注意的点是类别定义交通标志和信号灯在部分数据集里会被合并成一个类如果你需要细分得自己改标注。一个判断技巧如果训练集图片数量在 5000 张上下、目标实例在 2 万到 5 万这个量级那这批数据对单卡训练来说刚够用不用急着上额外预训练。数量低于 1000 张就要考虑用预训练权重做迁移学习后面会具体讲参数。3. 训练自己的 YOLOv8 模型从数据划分到参数调试数据集看清了接下来走完整训练链路。YOLOv8 是现在跑这类数据集最省事的框架之一不只是因为它训练快还因为它的数据增强策略和处理小目标的逻辑比老版本 YOLOv5 成熟一些。下面从数据划分开始到训练参数的选择最后集中讲几个无人机场景的常见坑。3.1 数据划分与路径修正别被中文文件名坑了无论是直接提供 YOLO 格式还是 JSON 格式第一步保证路径正确。常见问题是Windows 解压出来的路径带反斜杠Linux 下训练脚本读不到文件名带中文或空格ultralytics 在解析时容易翻车。我一般把整个流程固定为两步import os import random random.seed(42) images os.listdir(drone_traffic/images/train) random.shuffle(images) train_ratio 0.8 train_files images[:int(len(images) * train_ratio)] val_files images[int(len(images) * train_ratio):] def write_split(file_list, out_path): with open(out_path, w) as f: for name in file_list: abs_path os.path.abspath(os.path.join(drone_traffic/images/train, name)) f.write(abs_path \n) write_split(train_files, train.txt) write_split(val_files, val.txt)这段脚本的作用是把图片绝对路径写进文本文件供后续 YOLO 格式的 txt 标注对齐使用。为什么要用绝对路径因为 YOLOv8 的 data.yaml 里如果是相对路径换一台机器跑就得改绝对路径至少在当前机器上不会出歧义。然后写 data.yamlpath: /home/user/drone_traffic train: images/train val: images/val test: images/test nc: 4 # 按类别数改 names: [car, truck, pedestrian, cyclist]参数说明path是数据集根目录的绝对路径train和val是相对路径nc是类别数量必须和标注文件的类别 id 对应上names顺序也直接影响训练日志里类别名的显示别排错。3.2 YOLOv8 训练参数img 尺寸、epochs 与优化器无人机视角的模型训练参数选择和普通目标检测有区别。我直接给一套可复现的配置from ultralytics import YOLO model YOLO(yolov8m.pt) results model.train( datadrone_traffic/data.yaml, epochs100, imgsz896, batch8, device0, lr00.001, optimizerAdamW, mosaic0.5, augmentTrue, patience20, )几个关键参数的选择逻辑imgsz896是无人机场景最重要的一个值。默认 640 对高空俯视图来说偏小一辆车在 640 分辨率下只有 20 像素传到网络深层特征图后可能只剩个把像素检测头根本没法回归。提到 896 能让小目标的特征保留多一倍显存不够就 768低于 640 就不要玩了。batch8是 896 分辨率下 24GB 显存的常见选择如果你的卡是 12GB 的batch 降到 4或者把 imgsz 降回 768。mosaic0.5要注意mosaic 增强对俯视小目标不是越多越好它把四张图拼在一起目标会被缩小四倍小目标更容易被增强成“看不见的噪声”。我一般会控制在 0.5 而不是默认的 1.0。优化器和学习率无人机场景下默认的 SGD 也能收敛但 AdamW 在批量较小、目标较小时更稳。lr00.001是迁移学习的一个折中点如果是从随机初始化开始训练调到 0.01 也行但没必要——你是在 YOLOv8m 的 COCO 预训练权重上继续训练初始学习率太大容易把前面的特征毁掉。patience20意味着 20 轮指标没提升就提前停止防止在数据不大时后段过拟合。3.3 避坑手册无人机视角目标检测的 5 条血泪经验这一节是我自己跑这类数据集和看别人翻车总结出来的高频问题按“现象 → 原因 → 解决”写。坑 1验证集 mAP 很高俯视真实场景却疯狂漏检现象模型在测试集上 mAP 超过 0.75部署到无人机拍摄的新视频里小目标漏检率超过 50%。原因数据集划分时有随机性但无人机航拍数据往往相邻帧高度相关随机划分会把相似图片同时放进训练和验证集分数虚高。解决划分时按“视频序列”或“拍摄区域”分组不要让同一个序列的图同时出现在训练和验证里。代码上可以先按文件名前缀分组再划分。坑 2中文或空格路径导致训练中断现象训练到一半报FileNotFoundError或者 ultralytics 直接卡在读图阶段。原因Windows 解压的路径含中文目录名或者文件名带空格Python 的 glob 和 YOLO 的 LoadImages 在某些场景下解析异常。解决解压后立即统一重命名全部转成英文和下划线训练前做一次路径校验with open(train.txt) as f: for line in f: line line.strip() if not os.path.exists(line): print(缺失:, line)坑 3小目标在标注里是“噪声”现象训练后 loss 降不下去val loss 在某个值附近震荡。画出来看小目标区域预测框乱飘。原因有些无人机数据集从视频按帧抽运动模糊和远距离小目标标注本身就不准框偏移几个像素对 loss 的影响相对大。解决训练前做标注清洗筛掉宽度或高度小于 5 像素的框或者用自动化工具重新标注一遍模糊帧。宁可少而精不要多而脏。坑 4车辆密集区域的 NMS 后框被误删现象路口的车辆挨得近预测框重叠度超过 NMS 阈值后处理阶段留下一个框把旁边的车丢了。原因俯视图下车顶视图相似度高多个目标的特征趋同NMS 只按 IoU 判重直接把低置信度的邻近目标删了。解决测试时调低conf阈值到 0.15 左右iou阈值从默认的 0.5 提到 0.6如果密集程度特别高考虑上旋转框方案第 4 章展开。坑 5模型在夜间和阴天识别崩塌现象白天训练的模型一遇到阴天或黄昏漏检率大幅上升。原因无人机数据集大多在白天采集光照分布单一模型学到的是“强光照下的纹理”不是“物体本身的结构特征”。解决训练时把光照增强打开包含亮度、对比度扰动如果有余力用简单色彩空间处理把部分训练图转成低光照效果做伪样本。4. 旋转框与多尺度用 MMRotate 处理密集排列目标如果你解压数据集时发现标注是四点格式或者训练后车辆边框重叠严重那就该考虑旋转框检测了。无人机俯视场景里目标在图像中的角度是任意的水平框表示会引入大量背景旋转框能把框紧贴目标显著降低误检和漏检。4.1 旋转框标注转换从 HBB 到 OBB 的代价水平框HBB转旋转框OBB不是公式调用就行要处理角度定义问题。常见做法是使用 OpenCV 的minAreaRect把水平框内所有标注点的最小外接矩形求出来import cv2 import numpy as np points np.array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]], dtypenp.float32) rect cv2.minAreaRect(points) (cx, cy), (w, h), angle rect # 转换为 MMRotate 或 YOLOv8-obb 的格式 if w h: w, h h, w angle 90 obb [cx, cy, w, h, angle] print(obb)这段代码的核心逻辑是先求四点的最小外接矩形再对角度做归一化因为minAreaRect返回的角度范围是 [-90, 0)转成训练需要的格式时要把长边对准 x 轴角度范围统一到 [0, 90) 或 [-90, 90)具体看训练框架读什么格式。注意四点坐标必须按顺序给出否则minAreaRect的结果是错的。用 MMRotate 时推荐直接用occluded_obbox格式更稳定。4.2 多尺度推理与切片把 896 分辨率用出性价比旋转框模型训练完推理阶段要解决另一个问题原始无人机图像往往超过 4000×3000 像素直接缩放整图再推理小目标基本废掉。两个方案方案一TTA 多尺度融合——测试时把图片同时缩放到 0.8、1.0、1.2 倍分别推理后融合结果。这个方法实现简单但对显存要求高耗时也长。方案二滑窗切片推理——更像是工程做法。把大图切成 896×896 的块每块重叠 10%推理后根据块的原始坐标把框映射回大图坐标系。切片的好处是把每辆车的像素面积放大模型能提取到更多特征。切片的代价是目标靠近边缘时会被切半需要靠重叠区域来兜底。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) img cv2.imread(drone_frame_001.jpg) H, W img.shape[:2] window 896 overlap 0.1 results_all [] for y in range(0, H - window // 2, int(window * (1 - overlap))): for x in range(0, W - window // 2, int(window * (1 - overlap))): crop img[y:y window, x:x window] results model(crop, conf0.2, iou0.5) for r in results: for box in r.boxes.data: x1, y1, x2, y2, conf, cls box.tolist() # 映射回原始坐标 results_all.append([x x1, y y1, x x2, y y2, conf, int(cls)])映射这段代码容易出错切片的坐标偏移量x和y在重叠区域不要累加过头循环步长用的是int(window * (1 - overlap))而不是window这样重叠区域的目标不会因为被切掉一半而丢失。如果目标的中心在重叠区域内会重复检测一次后处理时按置信度保留最高那个即可。MMRotate 和 YOLOv8-obb 怎么选我是这么判断的数据量大上万张、类别杂优先 YOLOv8-obb因为它训练速度快、生态成熟数据量中等但车辆密集度极高MMRotate 的 Oriented R-CNN 在精度上限上还是高一点但代价是配置时间长、跑一轮要看一堆报错。实际项目赶进度YOLOv8-obb 已经能覆盖 90% 的需求了。5. 进阶验证与调优技巧让小目标检测再涨两三个点模型训出来了mAP 也挺好看但部署前必须做一轮“对抗性验证”。这一章给两个具体技巧可视化找病灶、用数据增强边界换精度提升。5.1 可视化与错误分析先改 Loss 还是先加数据很多人的习惯是看 mAP 不好就换模型、调学习率但真正的病灶在看图才能发现。我每轮训练完都会强制跑一次批量推理存成图片逐张看。import os from ultralytics import YOLO model YOLO(best.pt) val_images os.listdir(drone_traffic/images/val)[:50] for name in val_images: path os.path.join(drone_traffic/images/val, name) model.predict(path, saveTrue, imgsz896, conf0.15, iou0.6)把预测结果和原图对齐翻一遍重点看三类错误漏检的是小目标还是遮挡目标误检集中在树荫、楼顶还是车辆阴影密集区域是漏检多还是框重叠多。这个判断直接決定下一步是调 NMS、加数据还是换 Loss。如果是密集场景漏检、同时单个目标的置信度并不低那就先从 NMS 入手调iou0.6后重测如果是小目标整体漏检MAE 类的 Loss 替代 BCE 也许是方向但在 YOLOv8 上直接改 Loss 是个大工程我更倾向于先补数据更稳。5.2 数据增强的边界Mosaic 在俯视图上的翻车现场上一章提到 Mosaic 会缩小目标这里展开讲边界。在无人机俯视图里mosaic 增强拼接四张航拍图的反应和地面视角不一样地面目标本来就在 40 到 100 像素区间缩小四倍后依然有 10 到 25 像素还能认但无人机小目标原本只有 20 到 40 像素缩小四倍后掉到 5 到 10 像素人眼都难分辨模型更学不到东西。我的做法是训练时开mosaic0.5让模型在前半段学多尺度后 20 轮把 Mosaic 关闭用原始尺度精修results model.train( datadrone_traffic/data.yaml, epochs80, imgsz896, batch8, mosaic0.5, close_mosaic15, )close_mosaic15的意思是在最后 15 个 epoch 自动关闭 Mosaic 增强。效果相当于前半段学抗尺度扰动后半段回归真实分布。这个参数在 ultralytics 里是原生支持的属于花小钱办大事的参数。完成这轮调优后我会把验证集重新跑一遍把每张图的置信度阈值、推理耗时报个表再决定要不要上 TensorRT。用 YOLOv8 训练无人机视角数据集这件事数据分布理解比模型选型更能决定最终效果。从那以后我每次拿到新的航拍数据集都强制先做类别统计和小目标占比分析再决定要不要上 OBB、Mosaic 要开多大——这个顺序反过来走十次有八次要返工重训。希望这篇笔记能帮你少走这几个来回。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进