
简介9类道路车辆识别检测数据集面向计算机视觉与智能交通场景覆盖自行车、公交车、汽车、电动自行车、吉普尼、摩托车、三轮车、卡车、面包车等9类常见车型可用来做道路交通违规检测、车流量统计、车辆属性识别等项目也适合本科课设、毕业设计与科研训练。数据来自监控视角图片与标注质量较高标签已整理为YOLO格式的txt文件并配有yaml配置可直接适配YOLOv3到YOLOv10系列算法无需格式转换训练集、验证集、测试集已预先划分基于YOLOv9训练准确率可达92.3%。压缩包共2000个文件包括1999个txt标注文件和1个yaml配置文件包体634.3MB属于两大部分中的第1部分适合需要较大规模车辆检测数据的开发者与研究者。目前已有574人学习下载可用于科研、课程设计、毕业设计及实际落地项目。1. 从2534张到9类车辆这个数据集能解决什么不能解决什么做路口监控、园区车辆统计、停车场道闸识别的人大概率遇到过同一个尴尬COCO预训练模型能认出轿车货车却认不出电动自行车和三轮车因为公开数据集里压根没有这两个类别。标题里这份「9类道路车辆识别检测数据集2534张含yolo格式txt标签」就是冲着这个缺口来的集合了自行车、电动自行车、摩托车、三轮车、面包车等9类常见路侧车辆每张图都配好YOLO格式的txt标签解压就能开始训练。2534张的规模不算大但胜在类别贴近国内真实道路场景比拿通用检测模型硬调参要省事得多。适合两类人一是需要快速给项目出检测baseline的工程团队二是做课程设计或竞赛的学生。说句实在话它的价值在于把标注这个最耗时的环节省掉了。但也要提前有个预期——「第1部分」这个后缀说明训练数据可能被拆成多卷发布先把手头的用扎实别指望一次拿全。2. 拆开第1部分.ziptxt标签结构、9类定义与可视化验证拿到压缩包的第一件事不是直接训练而是先搞清楚解压后里面是什么、标签格式是不是严格符合YOLO要求。这一步懒不得很多翻车现场都是因为标签和图像对不上、坐标归一化反了等到训练跑起来才发现后悔药都没得吃。2.1 zip解压后应该看到的目录与txt标签格式常见做法是解压到一个独立目录比如vehicle_data。我先用一条命令看一眼目录层级unzip 9类道路车辆识别检测数据集2534张含yolo格式txt标签第1部分.zip -d vehicle_data tree vehicle_data -L 2预期结果是这样一组并列目录vehicle_data/ ├── images/ │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... └── labels/ ├── 000001.txt ├── 000002.txt └── ...这是YOLO系列最主流的组织方式images目录放原图labels目录放同名txt一张图对应一个txt。如果压缩包里还带了classes.txt或者data.yaml那最好直接用它们确认类别顺序如果没有就得像下面这样从标签数值推导。先抽一张png或jpg的txt看内容cat vehicle_data/labels/000001.txt每行格式是YOLO标准的五段式0 0.621094 0.488281 0.312500 0.283333 1 0.152344 0.546875 0.140625 0.391667五个数字分别对应类别ID、目标中心点x坐标、目标中心点y坐标、目标宽度、目标高度。这里最关键的细节是四个坐标值全部做了归一化取值范围在0到1之间x和宽度除以图像宽度y和高度除以图像高度。好多人训练黑匣子里跑出离谱框就是栽在这把像素坐标直接当归一化坐标塞进去框飞得横跨整张图。另外注意txt文件名里不会带jpg或png后缀必须和图像文件完全同名。解压后顺手统计一下文件数量ls vehicle_data/labels | wc -l ls vehicle_data/images | wc -l两个数字应该一致都是2534左右。如果labels数量少于images说明有一部分图没有标签这部分图训练时会被跳过需要提前从images里剔除。2.2 9个类别的约定与最容易被误标的三个边界标题里点名了9类但实际写出的只有自行车、电动自行车、摩托车、三轮车、面包车剩下几类通常就是这类路侧数据集里的常客轿车、卡车、客车、行人或工程车。具体哪7类以压缩包内的classes.txt或data.yaml为准我只强调一个原则类别ID的顺序是训练和推理时的唯一依据txt里的第一个数字对应的必须是你写入data.yaml的同一下标。常见误标边界有三个属于血泪经验类别容易和谁混比较可靠的区分特征电动自行车摩托车是否有脚蹬后轮是否带电机车身宽度更窄三轮车三轮摩托车是否有车棚结构轮距和车厢高度是否带货斗面包车小型客车/MPV车头发动机舱是否明显侧滑门设计整体方正程度这三个边界直接影响模型精度。如果原标注质量一般你会发现训练完这几个类别互相串框原因很简单标注员自己都分不清。这种场景下我先不做合并而是把错分案例整理出来后面通过增补侧视角样本和调整细粒度特征来缓解。2.3 用不到30行Python把标签画回图上标签好不好光看数字没用必须把框画到图上肉眼看。下面这段脚本是我每次拿到新数据集必跑的它能把前10张图的标注框原样绘出并写回新文件import cv2 import os img_dir vehicle_data/images label_dir vehicle_data/labels out_dir check_vis os.makedirs(out_dir, exist_okTrue) # 类别顺序以包内classes.txt为准这里先放标题提到的5类占位 class_names [bicycle, ebike, motorcycle, tricycle, van] img_files sorted(os.listdir(img_dir))[:10] for img_name in img_files: stem os.path.splitext(img_name)[0] img_path os.path.join(img_dir, img_name) label_path os.path.join(label_dir, stem .txt) img cv2.imread(img_path) if img is None: print(f无法读取图像: {img_path}) continue h, w img.shape[:2] if not os.path.exists(label_path): print(f标签缺失: {label_path}) continue with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f非法行: {line.strip()} in {label_path}) continue cls_id int(parts[0]) x_c float(parts[1]) y_c float(parts[2]) bw float(parts[3]) bh float(parts[4]) x1 int((x_c - bw / 2) * w) y1 int((y_c - bh / 2) * h) x2 int((x_c bw / 2) * w) y2 int((y_c bh / 2) * h) color (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) label_text class_names[cls_id] if cls_id len(class_names) else str(cls_id) cv2.putText(img, label_text, (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) cv2.imwrite(os.path.join(out_dir, img_name), img) print(f已保存: {os.path.join(out_dir, img_name)})这段代码的逻辑不复杂先按文件名找同名txt然后读取每一行的五个数字把归一化坐标乘回图像的宽高还原成左上角和右下角的像素坐标最后用OpenCV画框和类别名。重点看三个地方一是class_names的顺序一定要和txt里的类别ID对应上否则画出来的名字是错的二是打印非法行的位置正常标签不会漏掉任何一个值三是如果框明显偏移比如跑到物体旁边半个车身那基本可以断定这个数据集的标注坐标不是YOLO归一化格式而是别的格式需要先做转换再训直接训的话模型学到的框中心永远是偏的。这个可视化检查坚持做能避免后面所有跟标签有关的玄学问题。3. 用YOLOv8训练这个数据集划分、data.yaml与首轮参数确认标签没有硬伤之后就可以进入训练环节。现在从业者们用得最顺手的是ultralytics的YOLOv8命令简洁、预训练模型下载也省心。下面按一条最短路径把流程串起来同时讲清楚每个参数背后的意图。3.1 数据划分按时间段而不是按图片随机分很多教程上来就让你把图像随机按8:2分成train和val但这个数据集如果来自视频抽帧随机分会造成严重的信息泄漏——同一辆车的前后两帧可能一帧在训练集、一帧在验证集val mAP虚高到吓人上线后立刻现原形。所以划分前先看文件名如果文件名自带时间戳或序号或者文件名前缀相同优先按前缀分组。我一般会写一段简单的划分脚本先把所有标签集打成列表再按文件名前缀分桶import os import random import shutil label_dir vehicle_data/labels img_dir vehicle_data/images train_ratio 0.9 label_files sorted(os.listdir(label_dir)) random.seed(42) # 如果文件名形如 cam01_000123.jpg则前缀为 cam01 buckets {} for label_file in label_files: prefix label_file.split(_)[0] buckets.setdefault(prefix, []).append(label_file) train_labels [] val_labels [] for prefix, files in buckets.items(): random.shuffle(files) split_idx int(len(files) * train_ratio) train_labels.extend(files[:split_idx]) val_labels.extend(files[split_idx:]) for split, flist in [(train, train_labels), (val, val_labels)]: os.makedirs(fvehicle_data/{split}/labels, exist_okTrue) os.makedirs(fvehicle_data/{split}/images, exist_okTrue) for label_file in flist: stem os.path.splitext(label_file)[0] shutil.copy(os.path.join(label_dir, label_file), os.path.join(fvehicle_data/{split}/labels, label_file)) img_file stem .jpg if os.path.exists(os.path.join(img_dir, img_file)): shutil.copy(os.path.join(img_dir, img_file), os.path.join(fvehicle_data/{split}/images, img_file)) print(ftrain: {len(train_labels)} 张val: {len(val_labels)} 张)脚本的逻辑是先把每个摄像头前缀的视频片段作为一个桶再在桶内按比例划分这样连续帧不会跨数据集。做过一次之后你就会发现val mAP会变丑但那才是真实水平。如果你的图像文件名没有可用的前缀退而求其次可以用sorted(os.listdir())后每隔9取1作为val也比重置随机数强。3.2 写data.yaml与启动训练的完整命令划分完后在vehicle_data下新建一个data.yaml内容这样写path: ./vehicle_data train: train/images val: val/images names: 0: bicycle 1: ebike 2: motorcycle 3: tricycle 4: van 5: car 6: bus 7: truck 8: personyolo train modelyolov8s.pt datavehicle_data/data.yaml \ epochs100 imgsz640 batch16 device0 \ projectruns namevehicle_9cls patience20这里train和val填的是相对路径path指定了项目根目录所以后续不用再去改文件位置。如果解压后发现类别不是上面这个顺序务必把names改成你看到的classes顺序同时同步修改上一节可视化脚本中的class_names。命令里的modelyolov8s.pt会触发预训练模型自动下载国内网络可能需要等一会儿。要是你机器显存只有6G把yolov8s换成yolov8n或者把batch降到8。训练结束后模型权重会存在runs/vehicle_9cls/weights/best.pt和last.pt建议只用best.pt做后续推理。3.3 六个训练参数的初始值及调整信号第一次训练建议按下面这张表设参数不要一上来就追求极致。参数初始值调整信号modelyolov8s.pt显存不够换n数据量大且类别复杂换mepochs100看val mAP是否提前饱和patience会帮你停imgsz640小目标多可尝试768但训练时间变长batch16显存吃紧降到8batch太小容易BN崩溃patience20连续20轮val不再提升就早停lr00.01默认val loss震荡时降到0.005重点说下patience和imgsz。2534张图说多不多说少不少100个epoch如果硬跑完可能要十几个小时patience20能帮你提前锁住最佳权重省下的时间足够你再跑一轮消融。imgsz640是速度和精度的折中点这9类车在画面里通常不算小目标如果原图分辨率是1920x1080可以把imgsz拉到768小三轮车的召回率会有肉眼可见的提升代价是训练时间几乎翻倍。我习惯先640跑一轮拿baseline确认没有bug后再决定要不要加分辨率。4. 训练这个道路车辆数据集的5个踩坑记录这个数据集训练过程中的坑多数不在算法而在数据和训练环境本身。下面这5条是我把它当基准测试跑了一遍后整理出来的按「现象→原因→解决」写能帮你少走弯路。4.1 现象训练集val mAP奇高上线后立刻打回原形现象是训练时val mAP能到0.85可导出到真实路口摄像头上一测漏检一堆感觉模型像是瞎的。原因大概率是数据划分做了随机分割2534张图里如果有连续视频帧同一个目标被同时分进train和val模型等于提前见过答案val mAP自然是虚高的。解决方法是回到3.1的按前缀划分逻辑重新分或者缩短验证集间隔。另外要注意如果压缩包本身就是「第1部分」你从这部分里分出的val只能代表这一部分的数据分布不代表全部场景。真正的验证还得到第2部分数据上重跑这属于后话。4.2 现象电动自行车和摩托车框体互相跳变现象是推理时电动自行车框和摩托车框频繁乱跳上一帧是ebike下一帧变了motorcycle帧间不稳定后端的计数逻辑根本没法用。原因是这两类在视觉上太接近尤其当目标处于侧面小角度、且图像分辨率不高时标注本身的边界就模糊。框架会倾向于学到一个「折中特征」导致置信度在两个类别间徘徊。解决方法是分两步走第一训练前把两轮车的标签统计一下如果电动自行车和摩托车数量悬殊考虑暂时合并成一个two_wheeler类别保证召回第二如果业务必须区分那就去采集更多侧视角样本并在数据增强时略微降低scale和hsv变换强度避免车漆颜色和整体比例被过度扭曲。4.3 现象txt标签空文件与归一化坐标越界现象是训练时某些图片没有读取到任何目标或者loss在个别iter上出现异常大值自查发现部分txt是空文件还有一些txt里的坐标值大于1。原因是在转换或整理过程中个别标注框贴近图像边缘处理脚本把边界外的部分一起写进了标签归一化时出现了超过[0,1]的数值空文件则多半是原标注被删但文件没清理。解决方法是写一个清洗脚本过滤掉空文件和越界行import os label_dir vehicle_data/labels bad_files [] for label_file in os.listdir(label_dir): path os.path.join(label_dir, label_file) new_lines [] with open(path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: continue values list(map(float, parts)) if not all(0.0 v 1.0 for v in values[1:]): continue if values[3] 0 or values[4] 0: continue new_lines.append(line) if len(new_lines) ! len(lines): bad_files.append(label_file) with open(path, w) as f: f.writelines(new_lines) print(清理过的文件:, len(bad_files))能被这个脚本救回来的标签都救回来了如果某个文件被过滤得只剩0行我建议直接把这个文件连同对应图像从训练集里删掉空标签在YOLOv8里会跳过没必要留。4.4 现象三轮车recall明显低于其他类现象是混淆矩阵里三轮车那一列的对角线值很低大量三轮车被识别成面包车或卡车跟行业里常说的长尾分布一致。原因是2534张图里三轮车样本量本来就少有的类别可能只有几十个实例。模型见过少自然学不牢。解决快的做法是先看每类bbox数量分布识别出哪几类是少数类。对少数类做简单过采样即可把包含三轮车、三轮摩托车的图片复制几份同时做水平翻转和亮度扰动注意翻转时要同步修改txt里的x坐标。代码不复杂但收益很直接比调loss function来得更快。另一个选择是提高cls损失的权重ultralytics里对应的是cls1.5但这个方法在样本量极悬殊时的效果不如过采样稳定。4.5 现象训练中途loss突然变NaNBN崩溃现象是训练跑到40多轮loss突然打印成nan后台直接崩之前跑的几十小时白费很多刚接触YOLOv8的人都会撞上这堵墙。原因通常是batch size设太小BN层统计量不稳定或者混合精度训练时半精度浮点溢出。2534张图的训练大家图省事经常把batch调到4或2显存不够硬跑BN就来造反了。解决方法是先把batch调到至少8如果显存仍不够就换更小的模型而不是硬保batch同时在训练命令里加ampFalse关掉混合精度。这两个动作基本能压住BN崩溃。如果已经崩过记住last.pt还在可以减少学习率从断点续训而不是从头再来。5. 在真实路口验证模型混淆矩阵、置信度阈值和补样本的优先次序模型训练完我现在的习惯是坚决不看val mAP这一个数字。mAP是把所有置信度阈值下的表现平均起来的分数实战里你需要一个固定阈值所以要看的是混淆矩阵和置信度相关的曲线。先看混淆矩阵它由YOLOv8训练完自动生成在runs/vehicle_9cls/confusion_matrix.png。对角线亮是好事但更值得看的是矩阵里非对角线的亮点。比如摩托车那一行的格子如果亮在电动自行车列说明这两类的区分度不够。你会很直观地感受到这9类里到底哪些是亲兄弟。再看F1-confidence曲线这张图可以从results.csv里提取或直接用yolo val重新计算。它告诉你设定的置信度阈值在哪个区间时F1最高。对这个道路车辆识别场景我会把阈值选在比最高F1略低一点的位置宁可多出几个低置信度框也不放过远处的面包车。这个取舍在监控场景里特别重要——漏检一辆车比多框一个误报更难承受。验证时要拿真实路口视频跑一次yolo predict modelruns/vehicle_9cls/weights/best.pt \ sourcestreet_video.mp4 conf0.25 imgsz640 save_txtTrue save_confTrue跑出来的结果如果觉得框抖动优先检查前面说的类别平衡问题如果觉得框偏小说明标注的框本身偏紧这会在后处理中影响NMS。最后一件事是补数据。补样本的顺序我建议按下面这个优先级来先补视频里频繁漏检的类别再补被错检成其他类的案例最后才是补正常环境下的多样化样本。每轮训练完把val集里置信度在0.3到0.6之间的检测结果截图存档下一轮补数据时直接对着这些错例找素材比无目的爬图高效得多。这个做法也是我从一次次翻车里总结出来的。现在的我已经养成条件反射拿到任何带txt标签的数据集先可视化、再划分、再清洗每一步都留痕。这份2534张的数据集本身是个好起点但真正的精度还是靠你在真实场景里一轮轮迭代出来。希望帮到你。本文还有配套的精品资源点击获取