自动驾驶YOLO数据集实战:八类目标检测与训练全攻略
简介这是一份面向自动驾驶感知与智慧交通场景的多类别目标检测数据集适合从事L2-L4级自动驾驶视觉感知、道路基础设施检测或边缘案例研究的开发者与算法工程师。数据集精选1397个txt标注文件、601张jpg图片并附1个yaml配置与1份docx说明文档压缩包共2000个文件整体约347.56MB可直接用于YOLO系列模型训练。涵盖自行车、汽车、摩托车、行人、交通标志、树木、犬类及隐蔽井盖共8个类别兼顾动态障碍物与静态设施其中井盖等隐蔽风险标注有助于安全冗余系统开发。数据按训练1137张、验证165张、测试95张严格划分便于完成训练、调参与评估全流程。目前已有49人学习浏览适合需要真实交通场景标注数据来验证算法效果的从业者。1. 自动驾驶多类交通目标检测数据集八类目标、1397 张图片、YOLO 格式开箱即训做自动驾驶目标检测最消耗耐心的事情不是调网络结构而是整理数据。网上的公共数据集要么只有车辆和行人要么标注格式得花一晚上转成 YOLO转完又发现类别不全。这份“自动驾驶多类交通目标检测数据集”把 1397 张图片切成训练集 1137 张、验证集 165 张、测试集 95 张YOLO 格式标注压缩后直接可下。它最值钱的地方是类别覆盖常规的 Car、Person、自行车、摩托车、交通标志都有还带了犬类和隐蔽井盖这两类真正会出现在道路上的突发障碍。想跑 YOLOv8 或 YOLOv12 起步、验证感知方案、补边缘案例的朋友这份数据可以让你跳过坐标转换和标注环节直接进训练流程。2. 数据集的真实结构.rf. 前缀背后是什么YOLO 标注怎么读数据集拿到手第一件事不是急着启动训练而是先摸清解压后的目录长什么样。很多跑过 Roboflow 的人在导入本地训练时会遇到文件层级混乱的问题图片和标注不在一个目录、图片名后缀带哈希、训练时报找不到标签。这一章把结构看清楚后面复现就不会被小问题绊住。2.1 文件名规律一张 jpg 对应一个同名 txt观察压缩包里的文件名能看到两类典型来源。一类来自 Unsplash 图库文件名保留原拍摄者信息比如basil-k-raju-nZTERwZJI9I-unsplash_jpg.rf.12e053efe82bfd5237ce56f2098e27f3.jpg另一类是车载或手机拍摄实景比如img-2854_jpg.rf.31dbfbc949f254fb3d401db1385e656e.jpg、img-3623_jpeg_jpg.rf.021d457e74ee4eff50791afaa26048ef.jpg。_jpg.rf.这一段是 Roboflow 导出的记号jpg表示原图格式.rf.后面跟着的 32 位十六进制字符串是平台生成的唯一哈希用来避免不同来源图片重名同时保证标注文件和图片打包在同一个命名空间里。以basil-k-raju-nZTERwZJI9I-unsplash_jpg.rf.12e053efe82bfd5237ce56f2098e27f3.jpg为例同目录下必然有一个同名 txt 文件后缀从.jpg换成.txt。YOLO 训练框架找标签就是靠这个同名匹配机制所以下载后不要批量重命名除非你连 labels 一起重命名否则图片和标注的对应关系会断裂。这一点在后续训练报错里经常成为元凶。有人觉得文件名太长不顺眼写个脚本把所有图片重命名成1.jpg、2.jpg但标签文件名没跟着变训练时框架每张图都找不到标签计算出来的 loss 从一开始就是错误的。处理这类 Roboflow 导出的压缩包我的习惯是文件名一个字符都不动。2.2 一行五列的标注类索引加归一化框坐标YOLO 格式的每个 txt 文件每一行代表一个目标固定为五列类别索引、x_center、y_center、width、height。类别索引是整数从 0 开始对应 yaml 文件里 names 列表的下标后面四个坐标全部归一化到 0 到 1 的范围内。和 VOC 的像素坐标不同这种格式不依赖图片实际分辨率同一份标注既可以喂给 416 输入尺寸也可以切到 640 甚至 1280不需要重新缩放标注框。容易混淆的地方是x_center、y_center 是边界框中心点相对图片宽高的比例width、height 是边界框宽高相对图片宽高的比例都不是像素值。我见过不少第一次接触 YOLO 标注的人把 width 直接当像素宽度用画出来的框要么跑到左上角要么覆盖半张图。想还原成像素坐标公式是 x1 (x_center - width/2) * image_widthy1 (y_center - height/2) * image_heightx2 (x_center width/2) * image_widthy2 (y_center height/2) * image_height。下面这段代码把一个标注文件解析成可读的内容from pathlib import Path # 类别顺序务必与数据集 yaml 的 names 保持一致 class_names [ Bicyclette, Car, Dog, Motorcycle, Person, Traffic sign, Tree, Undercover manhole, ] label_path Path(labels/train/19C0927_040_jpg.rf.35d966f1cc0953af9fb3efba72d9c116.txt) for line in label_path.read_text(encodingutf-8).strip().splitlines(): cls_id, xc, yc, w, h line.split() print(f{class_names[int(cls_id)]:18s} fx_center{float(xc):.3f} y_center{float(yc):.3f} fwidth{float(w):.3f} height{float(h):.3f})输出大概是Car x_center0.514 y_center0.327 width0.180 height0.120这种形式。解析成功后可以顺手做一次完整性检查统计各分割集里空标注文件的数量。YOLO 允许图片没有任何目标也就是 txt 文件为空但空文件太多说明导出时可能有部分目标被过滤掉了需要警惕。from pathlib import Path for split in [train, valid, test]: label_dir Path(flabels/{split}) txts list(label_dir.glob(*.txt)) empty [p.name for p in txts if p.stat().st_size 0] print(f{split}: {len(txts)} 个标注文件其中空文件 {len(empty)} 个)跑完如果发现空文件占比超过 2%我一般会回到原始导出设置去查而不是直接开训。空标签会让模型把背景当成正样本的概率变大后期误检会集中在无目标区域。2.3 类别语言差异与索引对照表另一个容易踩的小坑类别里 Bicyclette 是法语“自行车”Traffic sign 和 Undercover manhole 是英文整体命名并没有统一成英文。这是因为原始标注工程的规范就是这样对训练结果没有影响——模型训练时类别只有索引有意义yaml 里写什么名称只影响可视化输出和日志展示。如果实在觉得别扭改 yaml 的 names 即可不需要动标签文件。类别索引YAML names 显示名含义典型场景0Bicyclette自行车非机动车道、路边停车区1Car汽车主干道、停车场、十字路口2Dog犬类人行道、路口突然出现3Motorcycle摩托车机动车道、路边违停4Person行人人行横道、路边步行5Traffic sign交通标志路侧杆件、悬臂标志6Tree树木绿化带、路侧遮挡7Undercover manhole隐蔽井盖路面井盖、破损区域到这里数据集的静态结构已经清楚。下一章开始把这些文件组织成 YOLO 能直接训练的目录结构并把训练跑起来。3. 复现训练目录整理、yaml 配置与 YOLOv8/YOLOv12 启动命令这一章是落地重点。无论你打算用 YOLOv8 还是现在讨论度很高的 YOLOv12数据进入训练前都需要满足同一套目录约定图片目录与标签目录分离训练、验证、测试各自独立。下面从整理目录开始到跑通训练命令为止把中间涉及的配置参数讲透。3.1 目录整理把解压内容归一成标准 layout先认识一下常见解压布局。这份数据集压缩包内部结构接近 Roboflow 导出目录一般是 train、valid、test 三个子目录各自包含 images 和 labels有时压缩包里还会带一个 data.yaml。训练框架的约定是 images 和 labels 分开放所以第一步我建议做一个整理动作把它复制成最规范的 layout#!/bin/bash # 假设原始目录结构是 data/split/{images,labels}按你的实际解压路径修改 SRC SRC./data DST./dataset for split in train valid test; do mkdir -p $DST/images/$split $DST/labels/$split cp $SRC/$split/images/*.jpg $DST/images/$split/ 2/dev/null cp $SRC/$split/images/*.jpeg $DST/images/$split/ 2/dev/null cp $SRC/$split/labels/*.txt $DST/labels/$split/ 2/dev/null done total$(find $DST/images -name *.jpg | wc -l) echo 图片总数: $total期望 1397这段脚本的逻辑很简单按 train、valid、test 三个分割名循环在目标目录下建 images 和 labels 两层再用 cp 分别拷贝图片和标注。2/dev/null是丢弃通配符没匹配到文件时的警告不影响执行。最后用 find 统计 jpg 数量看到 1397 再继续。如果数字对不上比如少了二十几个文件最可能的原因是压缩包解压时因为长文件名被中断。文件名里那串 32 位哈希再加上原始名字总长度经常超过 100 个字符某些解压工具在 Windows 下会截断路径。这时候重新用完整路径解压或者换用对长文件名支持更好的工具基本能解决。3.2 dataset.yaml路径与类别顺序在 dataset 的同级目录放一个 dataset.yaml内容如下path: /absolute/path/to/dataset train: images/train val: images/valid test: images/test names: 0: Bicyclette 1: Car 2: Dog 3: Motorcycle 4: Person 5: Traffic sign 6: Tree 7: Undercover manholepath 写绝对路径这一点不要偷懒。train、val、test 建议写相对 path 的路径这样换机器时只需要改一行 path 就能复用。names 的顺序不是随便排的它必须严格对齐标注文件里的类别索引标签文件里出现1对应的就是 yaml 里下标为 1 的 Car不是 Bicyclette。如果压缩包自带 data.yaml直接拿来用也可以但使用前仍然要检查 names 长度是不是 8 个。3.3 启动训练命令与参数说明命令行跑最直观cd /absolute/path/to/dataset/.. yolo detect train \ modelyolo12n.pt \ datadataset.yaml \ imgsz640 \ epochs80 \ batch16 \ patience15 \ projectruns/detect \ nametraffic_autodrive \ mosaic0.5 \ close_mosaic10逐个说明参数的意义。model 指定预训练权重本地有 yolo12n.pt 就用它只想快速验证数据集可以换成 yolo8n.pt文件名按你实际下载的权重调整。imgsz 是训练输入尺寸640 对这个数据集是平衡的选择显存不够降到 512 或 416。batch 是每批次图片数16 在单卡 12GB 显存附近基本能跑改成 8 更稳妥。epochs 设 80对 1137 张训练图来说足够验证损失通常在 40 轮附近收敛。patience 是早停参数15 轮验证损失不下降就停止防止过拟合。mosaic 和 close_mosaic 是一对需要单独说明的参数。mosaic0.5 表示一半概率使用四图拼接增强对提升一般目标的泛化能力有帮助close_mosaic10 表示最后 10 轮关闭 mosaic让模型适应真实的单图分布。这两个参数对包含小目标的数据集影响很大第 5 章会展开讲它是怎么坑人的。第一次跑的时候建议先做 5 个 epoch 的 smoke test把 epochs 改成 5不设早停观察训练 loss 是否单调下降。这一步的作用是快速排除数据路径、类别配置、显存溢出等问题避免正式训练跑到 30 轮才发现配置错误白白浪费好几个小时。4. 数据分布与类别平衡165 张验证集够用吗低频类别怎么补拿到一份现成数据集不能上来就训练。数据分布里藏着后期翻车的大部分原因验证集太小、某些类别实例数量过少、背景样本偏多。这一章用脚本量化分布再讨论低频类别的处理策略。4.1 统计各类别实例数量化低频类标签文件里每行一个目标实例统计每个类别出现的次数即可。注意这里是按目标实例数统计不是按图片数一张图里出现三辆车在 Car 类别里会算三次。from pathlib import Path from collections import Counter class_names [ Bicyclette, Car, Dog, Motorcycle, Person, Traffic sign, Tree, Undercover manhole, ] for split in [train, valid, test]: counts Counter() label_dir Path(flabels/{split}) for txt in label_dir.glob(*.txt): for line in txt.read_text(encodingutf-8).strip().splitlines(): if not line: continue cls_id int(line.split()[0]) counts[cls_id] 1 print(f[{split}]) for cls_id in range(8): name class_names[cls_id] print(f {name:18s}: {counts[cls_id]})输出是一张三列分布表。我拿到这类数据后第一眼看的是 Dog 和 Undercover manhole 的实例数因为它们通常是公共数据集中数量最少、也是实际场景中最想补齐的类别。如果某个类别只有个位数实例训练出的模型对该类别基本不可用这是诚实预期不用幻想过拟合能救回来。4.2 验证集多大才够165 张对 8 类目标够吗验证集 165 张从数量占比看大约占全部数据的 12%是符合常规的划分。但对 8 类目标来说如果某个类别在验证集里只有五六个实例这个类的 AP 波动会非常大——一次漏检就掉十个百分点。所以验证集够不够不能只看图片总数还要看每个类别分到了多少实例。如果发现验证集里某些类别实例数太少我建议做一个重划分把所有图片和标签混在一起按新比例重新切分。下面这个脚本把数据按 70/15/15 划分import random import shutil from pathlib import Path src_img Path(data/all/images) src_lbl Path(data/all/labels) out_root Path(dataset_resplit) ratios {train: 0.70, valid: 0.15, test: 0.15} images list(src_img.glob(*.jpg)) random.seed(2025) random.shuffle(images) counts {k: 0 for k in ratios} for idx, img_path in enumerate(images): if idx len(images) * ratios[train]: split train elif idx len(images) * (ratios[train] ratios[valid]): split valid else: split test counts[split] 1 out_img_dir out_root / images / split out_lbl_dir out_root / labels / split out_img_dir.mkdir(parentsTrue, exist_okTrue) out_lbl_dir.mkdir(parentsTrue, exist_okTrue) shutil.copy(img_path, out_img_dir / img_path.name) label_path src_lbl / (img_path.stem .txt) if label_path.exists(): shutil.copy(label_path, out_lbl_dir / (img_path.stem .txt)) print(counts)脚本有两个值得注意的地方。第一划分比例 70/15/15 比原始 81/12/7 更保守对类别不均衡的评估更稳定第二复制时如果 label 文件不存在就跳过不会报错但这也意味着你要额外检查是否有图片漏了标注。重划分后验证集图片数会变成大约 210 张低频类别分到实例的概率更大评估指标也更有参考价值。4.3 低频类别处理增强策略与损失权重当 Dog 或 Undercover manhole 实例数不够时我的习惯是优先调整训练配置不急着额外收集数据。常用手段有三类适用的场景不太一样手段具体做法适合场景关闭 mosaic / mixup减少拼接引起的小目标截断井盖这类小目标检测提高 cls loss 权重训练命令里加 cls1.5类别实例数悬殊稀有类别增强对稀有类别做翻转和 HSV 扰动后重复加入训练集实例数少于 50 时对应的命令示例yolo detect train \ modelyolo12n.pt \ datadataset.yaml \ epochs100 \ imgsz640 \ batch16 \ cls1.5 \ mosaic0.0 \ mixup0.0关闭数据增强的代价是训练速度更慢但对小目标类别更稳。我的建议是先跑一版全默认记录下 Dog 和 Undercover manhole 单独类别的 AP再决定要不要开增强。这份资源最大的边际价值就在这两个类别上——如果只关心 Car 和 Person它和通用交通数据集差别不大把稀有类别跑出可用的 AP才真正体现这类数据在自动驾驶安全场景中的作用。5. 避坑与常见问题标注错位、类别索引和增强过度的排查记录这一章是实践血泪经验。无论数据从哪来跑目标检测训练时总会遇到几个反复出现的坑以下五条是我拿到这份数据集后实际会踩到的位置每一条都按现象、原因、解决的顺序写。5.1 训练时报告 image not found训练直接中断现象yolo detect train跑起来没几步日志里连续报 dataset image not found随后训练中断。原因九成是 dataset.yaml 的 path 写了相对路径而终端当前目录不在数据集上级另外一成是目录整理时只复制了 jpg 忘了把 labels 同步复制过去导致出现有图无标签的分割目录。解决path 一律写绝对路径训练前先在命令行执行find images -name *.jpg | wc -l和find labels -name *.txt | wc -l两个数字对不上就不启动训练。这个检查十秒钟能省掉的调试时间以小时计。5.2 可视化时框偏移所有框都往左上角跑现象用 OpenCV 画标注框框的位置和物体完全对不上而且所有框都偏向图片左上角。原因把归一化坐标直接当像素坐标用了。YOLO 标注文件里 x_center0.5 不代表 0.5 像素它表示物体中心在图片宽度方向的 50% 位置。解决先还原成像素框再画公式是 x1 (x_center - width/2) * image_widthy1 (y_center - height/2) * image_heightx2 (x_center width/2) * image_widthy2 (y_center height/2) * image_height。画图前打印一下图片实际尺寸确认读入的宽高和原图一致排除库函数自动缩放的影响。5.3 mAP 很高但推理结果里类别张冠李戴现象训练日志里 mAP50 到了 0.85看起来很不错一测实际图片框的位置挺准但类别名对不上比如框里明明是摩托车标签打的是汽车。原因yaml 的 names 顺序和标签文件里的类别索引不一致。数据集标签里第 3 类实际是 Motorcycle但 yaml 中下标 3 的位置写了 Car于是模型学到的映射关系整体错位。解决读取任意几个 txt 文件统计类别索引的范围和出现频次和 yaml 里的 names 一一比对。如果类别名本身写错了改 yaml 的 names 顺序就行不需要动任何标签文件。5.4 打开 mosaic 增强后小目标召回率大幅下降现象训练 60 轮后Car 和 Person 的 AP 都很健康但 Undercover manhole 这个类别的 AP 几乎为零可视化结果里井盖基本没被框出来。原因mosaic 会把四张图拼成一张再随机裁剪井盖这类像素占比很小的目标经过拼接压缩后经常被裁掉一半甚至完全裁掉模型很难学到完整的井盖特征。解决把 mosaic 调低到 0.3 或直接关闭close_mosaic 提前到第 20 轮评估时对八个类别分别看 AP不要只看总 mAP。总 mAP 会被大类目拉高掩盖小目标类别的问题。5.5 验证集 AP 波动大两次训练差了六个点现象同样的配置上一次训练验证集 mAP 0.85这一次只有 0.79代码没改数据没换。原因验证集 165 张图片里部分稀有类别的样本只有个位数随机性主导了评估指标。某次推理漏掉一张稀有类别的图片这个类的 AP 就会大幅跳水。解决固定随机种子多次实验取最优值或均值作为最终指标或者按第 4 章的方式重新划分一个 15% 的验证集保证每个类别在验证集里至少有十个以上实例。这样横向对比不同模型配置时结论才不会被随机噪声带偏。6. 把标注画出来才是真验收一个可视化脚本的固定习惯前面的检查都是间接的真正能确认这份数据是可用的资产办法是让边界框绘制在图片上肉眼扫一遍。下面这个脚本我每拿到一个数据集都会先跑它把每张图的标注用绿色框和类别名渲染出来输出到 out 目录两分钟扫完二十张就能发现绝大多数问题。import cv2 import random from pathlib import Path class_names [ Bicyclette, Car, Dog, Motorcycle, Person, Traffic sign, Tree, Undercover manhole, ] img_dir Path(images/train) label_dir Path(labels/train) out_dir Path(out) out_dir.mkdir(exist_okTrue) images list(img_dir.glob(*.jpg)) random.seed(42) sample random.sample(images, min(20, len(images))) for img_path in sample: img cv2.imread(str(img_path)) if img is None: print(fcannot read: {img_path.name}) continue h, w img.shape[:2] label_file label_dir / (img_path.stem .txt) if not label_file.exists(): print(fmissing label: {label_file.name}) continue for line in label_file.read_text(encodingutf-8).strip().splitlines(): cls_id, xc, yc, bw, bh line.split() cls_id int(cls_id) xc, yc, bw, bh map(float, (xc, yc, bw, bh)) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(str(out_dir / img_path.name), img) print(f已生成 {len(sample)} 张可视化结果到 out/)画框的换算公式就在脚本里中心点减半宽得左上角加半宽得右下角最后乘上图片宽高回到像素坐标。cv2.putText 的类别名显示在框上方六像素处如果显示中文乱码把 class_names 临时改成英文或者换支持中文的字体渲染即可。脚本跑完打开 out 目录的图片重点看三件事框有没有套住目标、类别名和实际物体对不对、明显的大目标有没有漏标。比如一张图里路边停着三辆车标注却只画了一个框这就是导出时漏标了。这类问题如果不在训练前发现会在训练后变成莫名其妙的漏检到时候再排查数据成本就高得多了。这个脚本的价值不在功能本身在于固化一个习惯。从那以后我无论用什么数据集训练拿到手第一件事永远是跑一遍可视化确认图片、标注和配置三者一致再开始调训练参数。这套流程帮我避开了大量解压错误、标注错位和类别映射问题训练过程中基本不会因为数据本身翻车。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →