简介:面向自动驾驶感知与智能交通应用的多类车辆检测数据集,包含1251张道路场景图片,覆盖自行车、公交车、轿车、摩托车、卡车五类交通参与者,适用于YOLO系列目标检测模型的训练与评估。数据采用YOLO格式标注,提供归一化中心坐标与边界框尺寸,可直接接入YOLOv3/v5/v7/v8等主流框架;标注经专业团队验证,类别准确率达99.2%,训练/验证/测试集按90%/9%/1%划分,便于模型效果对比。整个压缩包共2000个文件,主要为1251个txt标注文件、747张jpg道路图像、1个yaml配置及1份docx说明文档,整体约68.64MB,结构清晰,即下即用。目前已有98人学习,适合自动驾驶感知、智能交通管理、车载安全预警等方向的开发者与研究者用于算法验证、数据集扩展及多类别检测任务落地;五类交通物体的检测能力还可延伸至智慧城市、物流管理等垂直领域。
1. 自动驾驶多类交通车辆检测数据集:带标注的千张道路图,解压就能开训
自动驾驶数据集一直有个尴尬:公开的大项目动辄几十万张,下载和清洗就要折腾一整天;朋友分享的爬虫图又往往标注错漏百出,训出来的模型没法上线验收。这份《自动驾驶多类交通车辆检测数据集》属于第三种——开箱即用。1,251张道路场景图,YOLO格式标注直接给好,解压后把路径填进配置文件就能开始训练,省掉从数据清洗、格式转换到类别映射的几天时间。
我去年做盲区监测预研时,就靠这类带标注的数据集快速验证了算法链路。整套数据覆盖Bicycle、Bus、Car、Motorcycle、Truck五个类别,正好是城市道路最常见的几类参与者,标注说明中声称准确率99.2%。适合三类人:做自动驾驶感知开发的工程师,需要一份能立刻跑通流程的基准数据;做智能交通流量统计的团队,拿它训练识别模型;还有刚上手YOLO目标检测的新手,用它走一遍“数据—训练—验证—导出”全流程。这份资源是纯数据资产,不带训练脚本,但这恰恰是它的优势——YOLOv3/v5/v7/v8都能直接接。
2. 解压后的目录与YOLO标注格式:训练前先弄懂那六个数字
拿到zip后别急着开训,先花十分钟把目录结构和标注文件看明白。YOLO系框架对数据集目录有约定俗成的期待,目录放错了,后面所有训练命令都要跟着改,返工成本很高。我在第一次拿到这份数据时,就踩过“label路径写错,训练跑了两个小时全在空标注上”的坑,所以这一章值得细看。
2.1 目录结构与图片命名规则
数据集解压后是典型的images和labels双目录布局。图片目录下按train、val、test三个子目录划分,分别为1,129张、114张、8张;标注目录下同样有这三个子目录,存放与图片同名的txt文件。图片和标注的一一对应关系全靠在文件名上:535_jpg.rf.638508aabd4df6c8057bd5bc3035ab9b.jpg对应535_jpg.rf.638508aabd4df6c8057bd5bc3035ab9b.txt,仅扩展名不同。
文件名里那串_jpg.rf.是RoboFlow平台导出时留下的痕迹,不影响使用,但要注意两个坑:第一,不要用通配符*.jpg去做按序配对,这份数据的文件名前缀虽然看着像数字,实际经过平台改名后并不是严格连续的编号,按字典序配对会错位;第二,txt和jpg必须逐一对应,我一般会先跑一遍批量检查,确认没有“有图无标注”或“有标注无图”的情况,再进入训练环节。
2.2 六个数字的含义:类别ID与归一化坐标
每个txt文件里,一行代表一个目标框,格式是class_id x_center y_center width height,五个字段之间用空格分隔。坐标全部是归一化值,范围在0到1之间,含义是相对图片宽高的比例,而不是绝对像素。例如2 0.5432 0.4871 0.2310 0.1788,表示这是类别2(Car),中心点位于图片宽度方向的54.32%、高度方向的48.71%处,框宽占全图宽度的23.10%,框高占全图高度的17.88%。
默认情况下这个数据集的类别ID是按英文字母顺序排的,Bicycle是0,Bus是1,Car是2,Motorcycle是3,Truck是4。这个顺序后面写data.yaml时必须和txt里的ID严格一致,否则会出现“训练时loss下降正常,预测时把公交车标成卡车”的错乱。
可以用下面这段脚本快速读一个标注文件:
from pathlib import Path txt_path = Path("labels/train/535_jpg.rf.638508aabd4df6c8057bd5bc3035ab9b.txt") for line in txt_path.read_text().strip().splitlines(): if not line.strip(): continue parts = list(map(float, line.split())) class_id = int(parts[0]) x_cen, y_cen, w, h = parts[1], parts[2], parts[3], parts[4] print(f"类别ID={class_id} " f"中心点=({x_cen:.3f}, {y_cen:.3f}) " f"框宽={w:.3f} 框高={h:.3f}")这段代码把txt每一行按空格拆成五个数值,前一个转成整数作为类别ID,后四个保持浮点数。打印结果时格式化为三位小数,方便人工核对量级。正常来说x_center应该落在0到1之间,width不会大于1;如果发现width超过0.9或者x_center接近0或1,说明这个框可能已经贴着图像边缘,需要进一步检查是否越界。
2.3 从归一化坐标回像素坐标:可视化验证标注效果
只看数字很难判断标注质量,把框画回原图是最直接的验收手段。归一化坐标换算回像素坐标的公式是:像素值 = 归一化值 × 图片对应边的长度。
import cv2 from pathlib import Path CLASS_NAMES = ["bicycle", "bus", "car", "motorcycle", "truck"] def draw_yolo_boxes(img_path, txt_path): img = cv2.imread(str(img_path)) ih, iw = img.shape[:2] for line in Path(txt_path).read_text().strip().splitlines(): if not line.strip(): continue parts = list(map(float, line.split())) class_id = int(parts[0]) x_cen, y_cen, w, h = parts[1], parts[2], parts[3], parts[4] x1 = int((x_cen - w / 2) * iw) y1 = int((y_cen - h / 2) * ih) x2 = int((x_cen + w / 2) * iw) y2 = int((y_cen + h / 2) * ih) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, CLASS_NAMES[class_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite("check_visual.jpg", img) print(f"已输出 check_visual.jpg,共绘制 {len(list(Path(txt_path).read_text().strip().splitlines()))} 个框") draw_yolo_boxes( "images/train/535_jpg.rf.638508aabd4df6c8057bd5bc3035ab9b.jpg", "labels/train/535_jpg.rf.638508aabd4df6c8057bd5bc3035ab9b.txt", )脚本先把图片读进来,获取高度和宽度;然后用中心点坐标减去一半框宽得到左上角x坐标,加上一半得到右下角x坐标,y方向同理。最后用OpenCV把框画上去,类别名写在框的左上角。建议随机挑train、val、test各五张图跑一遍,重点看两类问题:框是否明显偏离车辆轮廓、小目标(自行车、摩托车)是否漏标。这一步五分钟就能看完,却能在训练前挡住八成标注问题。
3. 用YOLOv8把千张图跑成模型:环境、配置与训练命令
目录和标注都确认无误后,就可以进入训练环节。我用的演示框架是YOLOv8,因为ultralytics的接口对新手最友好,一条命令行就能完成训练和验证。当然这份数据集的YOLO格式标注同样能直接喂给YOLOv3、v5、v7,核心流程不变,只是个别参数名有差异。
3.1 环境安装与标签完整性检查
先装好ultralytics包,它会自动拉取依赖的PyTorch版本。
pip install ultralytics然后执行一个标签完整性检查,把“有图无标”的文件列出来,这些文件如果不处理,训练时会报找不到对应标注的警告,白白浪费算力。
for img in images/train/*.jpg; do name=$(basename "$img" .jpg) if [ ! -f "labels/train/$name.txt" ]; then echo "缺少标注: $img" fi done这段bash脚本遍历train目录下所有jpg,取出不带扩展名的文件名,再去labels/train下查同名的txt是否存在。如果输出为空,说明标注文件齐全。val和test目录用同样逻辑再跑一遍即可。如果确有缺失,删除对应图片,或者把缺失文件清单记录下来,训练后分析漏检时重点看这些图。
3.2 编写data.yaml配置文件
YOLOv8训练时通过一个YAML文件告诉框架数据在哪、类别有哪些。文件内容如下:
path: /home/user/autodrive_dataset # 改成你解压后的实际路径 train: images/train val: images/val test: images/test names: 0: bicycle 1: bus 2: car 3: motorcycle 4: truckpath是数据集的根目录,建议用绝对路径;train、val、test是相对path的子目录,直接填目录名即可。names部分最关键,键是类别ID,值是对应的类别名,顺序必须与标注txt里的ID一一对应。这里我按字母序写,和2.2节解析时看到的一致,如果你自己改了标注,这个文件里的ID顺序也要同步改。
3.3 训练命令与超参数取舍
数据量只有1,251张,模型体量没必要选最大的,我习惯从yolov8m.pt起步,兼顾精度与训练速度。
yolo detect train \ model=yolov8m.pt \ data=dataset.yaml \ epochs=120 \ imgsz=640 \ batch=16 \ patience=20 \ workers=4 \ project=output逐项说明关键参数。epochs=120是训练轮数,对于千张级数据集,120轮足够让loss收敛,再多容易过拟合;patience=20表示连续20轮验证集指标没有提升就早停,省显存也省时间。imgsz=640是输入分辨率,这份数据里的自行车和摩托车属于中小尺寸目标,640是一个合理的下限,再低到416会丢细节,再高到1280则显存压力大且训练时间翻倍。batch=16按显存来定,8GB显存跑不动就降到8。workers=4是数据加载进程数,Linux下可以调到8,Windows下保持4以内。
训练结束后,在output/train/目录下会生成best.pt和last.pt,前者是验证集指标最好的权重。跑一次验证命令,确认最终指标:
yolo detect val \ model=output/train/weights/best.pt \ data=dataset.yaml验证完成后,终端会打印mAP50和mAP50-95两项指标,这两者怎么解读,我在第6章展开。这里先记住一件事:第一次训练的目标不是追求指标多高,而是确认loss曲线正常下降、五类都有可观的AP值,链路就通了。
4. 五类标注分布与数据划分:训练前先做一次数据体检
拿到一份新数据集直接开训是很多人的习惯,但我建议先花五分钟做一次数据体检,看看五类各自的样本量、类别分布是否均衡、训练验证测试三份数据是否真有区分度。这一步能看到后面训练中很多“莫名其妙”的问题。
4.1 类别ID映射与道路角色对照
这份数据的五类对象,覆盖了城市道路最常见的参与者。整理成表格如下:
| 类别ID | 英文标签 | 中文名称 | 道路角色定位 |
|---|---|---|---|
| 0 | Bicycle | 自行车 | 非机动车,骑行者和车辆体积小 |
| 1 | Bus | 公交车 | 大型公共交通工具,车身长 |
| 2 | Car | 轿车 | 最常见的乘用车,数量最多 |
| 3 | Motorcycle | 摩托车 | 两轮机动车,目标尺寸小 |
| 4 | Truck | 卡车 | 货运重型车辆,车身带货箱 |
Bicycle和Motorcycle是这五类里最容易混淆的难例,两者轮廓相近、尺寸接近,尤其当遮挡过半时,人眼都要辨认一下。Truck与Bus在侧面视角下也容易混,但Bus通常有车窗和规整的车身比例,Truck后部货箱与驾驶室之间有明显分段。这些类间相似性决定了模型在mAP上的瓶颈,不是说准确率99.2%的标注就不会让模型犯错,标注正确和类间可分是两回事。
4.2 统计各类别实例数量的脚本
第一份体检报告应该是各类别的实例数统计。写一个Python脚本,遍历labels/train下所有txt,统计每个类别出现的总次数:
from pathlib import Path import collections counter = collections.Counter() txt_files = list(Path("labels/train").glob("*.txt")) for txt_path in txt_files: for line in txt_path.read_text().strip().splitlines(): if not line.strip(): continue class_id = int(line.split()[0]) counter[class_id] += 1 print(f"train标注文件数: {len(txt_files)}") for class_id in sorted(counter.keys()): print(f"类别 {class_id}: {counter[class_id]} 个实例")讲解一下:glob("*.txt")拿到train标注目录下所有文件,每个文件逐行读取,取每行第一个数字作为类别ID,用Counter累加。结果能看出两点:一是哪类样本最多,一般是Car;二是哪类样本偏少,往往是摩托或公交。如果最少的一类不到最多一类的十分之一,训练时需要对该类额外做增强,或者用更大的epoch让模型多“看”几遍少样本。
4.3 数据划分比例与重新划分脚本
官方划分是训练集1,129张、验证集114张、测试集8张,占比约为90%、9%、1%,符合机器学习常规做法。训练集与验证集之比接近10比1,对千张级数据来说验证集偏小,114张图平均到每个类别也就二十来张,这带来的直接后果是模型验证指标波动大,同一份权重跑两次验证,mAP可能差出两三个点。8张测试集则更多是演示用途,真正评估模型泛化能力,我建议训练完自己从业务侧重新拍一个测试集。
如果不想用官方划分,可以按自己的比例重分:
import random from pathlib import Path import shutil random.seed(42) imgs = list(Path("images/all").glob("*.jpg")) random.shuffle(imgs) n_val = int(len(imgs) * 0.1) n_test = int(len(imgs) * 0.01) val_imgs = imgs[:n_val] test_imgs = imgs[n_val:n_val + n_test] train_imgs = imgs[n_val + n_test:] for split_name, split_imgs in [("train", train_imgs), ("val", val_imgs), ("test", test_imgs)]: img_out = Path(f"images/{split_name}") label_out = Path(f"labels/{split_name}") img_out.mkdir(parents=True, exist_ok=True) label_out.mkdir(parents=True, exist_ok=True) for img in split_imgs: shutil.copy(img, img_out / img.name) txt = img.with_suffix(".txt") if txt.exists(): shutil.copy(txt, label_out / txt.name)这段脚本把所有图片打乱后按90%、9%、1%切分,固定随机种子保证可复现。它的价值在于让你根据自己的训练目标调整验证集大小,比如把验证集扩到15%,指标会更稳定。注意重分后data.yaml里train、val、test的路径不用改,但目录内容已经变了。
5. 避坑指南:五个在YOLO数据集训练中常见的翻车现场
这一章是我把这份数据集从“能跑通”到“跑得稳”过程中遇到的真实问题。每一条都是先给现象,再分析原因,最后给解决办法,照着查就能省掉半天调试时间。
5.1 训练损失值异常升高或直接变成NaN
现象:训练到第几十轮时,loss不是平稳下降,而是突然反弹,甚至变成nan,验证集mAP跟着归零。
原因:最常见的是标注框越界。这份数据集从RoboFlow导出时,归一化坐标经过四舍五入,极端情况下会出现x_center + width/2超出1.0的框;训练时YOLO要把这些坐标映射回特征图网格,越界坐标会导致锚点匹配到非法位置,梯度跟着异常。
解决:在训练前对标注做一次清洗,把越界框裁回图片范围内:
from pathlib import Path def clamp_label(txt_path): lines = [] for line in txt_path.read_text().strip().splitlines(): if not line.strip(): continue c, xc, yc, w, h = map(float, line.split()) x1 = max(0.0, xc - w/2) y1 = max(0.0, yc - h/2) x2 = min(1.0, xc + w/2) y2 = min(1.0, yc + h/2) if x2 - x1 >= 0.05 and y2 - y1 >= 0.05: new_xc = (x1 + x2) / 2 new_yc = (y1 + y2) / 2 new_w = x2 - x1 new_h = y2 - y1 lines.append(f"{int(c)} {new_xc:.6f} {new_yc:.6f} {new_w:.6f} {new_h:.6f}") txt_path.write_text("\n".join(lines) + "\n") for txt in Path("labels/train").glob("*.txt"): clamp_label(txt)脚本把每个框的上下左右边界分别夹到0和1之间,然后重新计算中心点和宽高;宽度或高度小于0.05的框说明已经被裁得只剩一条缝,直接丢弃。跑完再看训练曲线,loss基本就正常了。
5.2 某一类别的mAP始终为0
现象:训练结束,五类里其他四类指标都正常,只有Motorcycle或者Bicycle的AP是0,好像模型完全没学会这一类。
原因:我的第一反应是标注有问题,查了一圈发现是空标注文件。部分图片对应的txt是空文件,里面一个目标都没有;如果这类空文件恰好集中出现在某一类的样本上,训练时模型看不到这个类的正样本,AP自然为0。
解决:写一个检查脚本,把空txt和它们对应的图片一起删掉:
for f in labels/train/*.txt; do if [ ! -s "$f" ]; then img="images/train/$(basename "$f" .txt).jpg" echo "发现空标注: $f,删除对应图片 $img" rm -f "$f" "$img" fi done-s判断文件是否非空,空文件说明这张图没有任何标注目标。删除后重新统计类别数量,如果少样本类的实例数明显下降,再考虑用复制增强补齐。
5.3 预测结果类别错乱:轿车被标成卡车
现象:训练过程一切正常,loss下降漂亮,验证指标也不差,但把模型放到真机上跑,明明是一辆Car,预测结果却显示Truck。
原因:这是data.yaml的names顺序和标注文件里的class_id不一致造成的。标注txt里的第一列可以是0到4任何一个数字,数字本身没有语义,是data.yaml把数字映射成了类别名。如果你把names列表顺序写错,比如把truck写在第二位,那么标注里所有的类别1都会显示成Truck。
解决:先跑第2章的2.3可视化脚本,把框和标签名画在原图上,再过一遍data.yaml的names顺序。我个人的习惯是每次训练前做一次全量随机抽检,画20张图直接看,不要嫌麻烦,这类错乱有后悔药,但最好别等训练完才发现。
5.4 验证集指标高得离谱,现场实测却一塌糊涂
现象:训练时mAP50冲到0.98,感觉模型已经无敌了,拿行车记录仪拍的视频一测,漏检一大片。
原因:数据划分时训练集和验证集之间出现了“近亲”图片。这份数据集的图片来自连续场景抓拍,如果是按文件名排序后顺序划分,同一个路口连续帧可能同时出现在train和val里,验证集和训练集高度相似,指标必然虚高。
解决:对三个目录做一次MD5去重,把完全相同的图片排查出来:
import hashlib from pathlib import Path def file_md5(path): return hashlib.md5(path.read_bytes()).hexdigest() index = {} for img in Path("images").rglob("*.jpg"): digest = file_md5(img) index.setdefault(digest, []).append(str(img)) for digest, paths in index.items(): if len(paths) > 1: for p in paths: print(f"重复图片: {p}")这段脚本对images下所有jpg计算MD5,同一MD5出现多次说明图片重复。发现重复后,优先保留train目录下的版本,删除val和test里的副本。注意MD5只查完全相同的图片,同场景不同帧查不出来,如果怀疑现场效果差是数据同源性导致,更稳妥的做法是按场景分组划分,或者重新采集一批与训练集场景差异更大的验证数据。
5.5 同参数下两次训练,mAP波动两个点
现象:同样的命令、同样的参数,跑两次训练,得到的mAP50差了0.03以上,让人怀疑训练过程有“玄学”成分。
原因:验证集只有114张,每类平均二十来张。验证时的批次采样顺序不同,或者数据增强的随机性稍有差异,一张摩托车的漏检就能让该类别AP掉好几个点,整体mAP自然跟着波动。
解决:不要拿单次训练的指标下结论。我一般用三个不同随机种子各跑一次,取中间值作为该配置的真实水平;对比不同配置时,也保持相同的随机种子。如果你追求更稳定的评估,可以把官方验证集的114张和训练集的1,129张合并,重新按95比5划分,验证集会大一些,指标波动能明显收窄。
6. 进阶:数据增强参数调优与ONNX导出部署验证
训练链路跑通只是第一步,把模型从实验结果变成能部署的产物,还需要调增强参数和导出格式。这两件事对千张级的小数据集尤其重要。
6.1 增强参数怎么设
YOLOv8内置了丰富的数据增强策略,命令行可以直接覆盖默认值:
yolo detect train \ model=output/train/weights/best.pt \ data=dataset.yaml \ epochs=80 \ imgsz=640 \ batch=16 \ hsv_h=0.015 \ hsv_s=0.7 \ hsv_v=0.4 \ flipud=0.0 \ fliplr=0.5 \ mosaic=1.0 \ scale=0.5 \ translate=0.1对自动驾驶场景,我的习惯是保持fliplr=0.5水平翻转,但flipud=0.0不做垂直翻转,因为真实道路中车辆不会头朝下出现;mosaic=1.0保留默认,四张图拼一张能显著提升模型对遮挡和小目标的鲁棒性。hsv_h颜色扰动不要开太大,0.015足够,路灯和阴影下的颜色偏移本来就小,改动太大反而让模型对颜色信息不敏感。这套参数适合千张级场景数据,样本量如果上万,mosaic可以降低到0.8,减少合成图和真实分布的偏差。
6.2 mAP50与mAP50-95怎么读
训练日志里的mAP50是IoU阈值0.5下的平均精度,任务简单时数值会很好看;mAP50-95在0.5到0.95之间按0.05步长取十个阈值求平均,对框的位置精度要求苛刻得多。这份数据集上,五类目标轮廓规则、背景单一,好的结果应该是mAP50在0.90以上,mAP50-95在0.65到0.75之间。如果mAP50高但mAP50-95只有0.4,说明框大体位置对但边缘贴合差,需要调高锚框尺寸或用更大imgsz重新训练。
6.3 导出ONNX做部署验证
验证完指标,最后一步是导出ONNX格式,方便后续用TensorRT或OpenVINO做推理加速:
yolo export \ model=output/train/weights/best.pt \ format=onnx \ imgsz=640 \ dynamic=Truedynamic=True开启动态输入尺寸,部署时允许不同分辨率的输入;如果部署的摄像头固定1080p,可以填imgsz=1280让模型在此分辨率下优化。导出后会生成同名的.onnx文件,用Netron打开或者用onnxruntime跑一遍推理,确认输出结果的维度是[1, 5, 8400]这样的格式,才算闭环。
从那以后,我每次拿到新数据集,无论标注说明写得多么可信,都强制走一遍2.3的框可视化,再跑一遍4.2的类别统计。这两步加起来不到五分钟,却能在训练前把八成标注问题和配置问题挡在门外,省下的是以小时计的排查时间。希望帮到你。
本文还有配套的精品资源,点击获取