☰
车辆检测数据集与YOLO11训练全流程指南
2026/10/5 10:33:24 网站建设 项目流程

简介:面向交通道路监控场景的车辆检测数据集,现整理为一份PDF指引文档。数据集包含1000张真实场景高质量图片,覆盖城市道路、高速公路、农村道路及车辆遮挡、严重遮挡等复杂情况,并划分为Auto、Bus、Car、LCV、Motorcycle、Multi-Axle、Tractor、Truck共8个类别,既适合目标检测算法研究与模型迭代,也可作为监控场景通用车辆检测项目的数据补充。文档详细说明采用labelimg标注的高质量标签体系,配套提供VOC(xml)、COCO(json)、YOLO(txt)三种常见数据集格式,可直接接入YOLO等算法训练;同时附赠YOLO11一键训练脚本,支持GPU(GPUs)、CPU、Mac(M芯片)多平台,并附博主训练结果日志供参考,便于对比调参。资源为1个PDF文件,大小7.19MB,内附数据集基本情况介绍、labelimg标注截图及百度网盘获取方式;目前已有1333人浏览学习,适合需要真实交通车辆数据做训练、验证或业务落地的开发者。

1. 目标检测的第一步:先要一份能直接开训的车辆数据集

目标检测项目里最耽误进度的往往不是模型调参,而是数据准备——格式不一致、类别ID对不上、训练脚本跑不通,任何一环都能卡住大半天。这份车辆检测数据集一共1000张真实场景图片,覆盖城市道路、高速道路、农村道路,还专门包含车辆遮挡和严重遮挡样本,标注分为Auto、Bus、Car、LCV、Motorcycle、Multi-Axle、Tractor、Truck共8类,每张图同时提供VOC(xml)、COCO(json)、YOLO(txt)三套标签。它另外附带一套YOLO11一键训练脚本,GPU(GPUs)、CPU、Mac(M芯片)三个平台都能直接跑。适合两类人:做交通道路监控、通用监控场景车辆检测的从业者,以及刚接触YOLO11训练、想跳过数据准备直接跑通完整流程的开发者。资源以PDF索引文件形式分享,里面是数据集说明和网盘链接,拉到文末能看到获取方式。

2. 车辆检测数据集拆解:8类标注与真实场景覆盖度

2.1 八个类别怎么划分:Auto、Bus、Car到Multi-Axle

拿到数据集先看类别清单,这份数据沿用了道路监控里比较常见的分类习惯。Auto对应小型轿车,Bus是公交/客车,Car这里更多指微型车或两厢小车,LCV是轻型商用车(面包车、轻卡一类),Motorcycle覆盖摩托车和电动两轮,Multi-Axle指多轴挂车或重载牵引车,Tractor是拖拉机/农用牵引车,Truck是标准卡车。8个类别的设定比只分“轿车/卡车”两类的粗标签要实用得多,因为监控场景里货车和挂车的形态差异非常大,合并成一个大类会让模型对轴数多的车识别不稳定。

类名对应车型高频出现位置
Auto小型轿车(三厢/两厢)城市道路、小区出入口
Bus公交、客车主干道、公交站
Car微型车、小型车停车场、狭窄道路
LCV轻型商用车(面包车/轻卡)物流园区、城乡结合部
Motorcycle摩托车、电动两轮车非机动车道、路口
Multi-Axle多轴挂车、重载牵引车高速、物流枢纽
Tractor拖拉机、农用牵引车农村道路、田间路口
Truck标准卡车/货车高速、国道

这8类里Auto和Car的边界最容易让人困惑。我在几个交通数据集里见过类似命名,有的项目把Car直接并入Auto,有的把Car当成“小尺寸轿车”单独保留。实际使用时要看你自己的业务需求,如果监控画面里只需要区分“小客车”和“大货车”,可以把Auto、Car合并后重新映射ID,后面避坑章节我会专门说这个事。

2.2 labelimg标注的成色:遮挡车、密集车与框质量

数据集说明里明确写了用labelimg标注。labelimg是目标检测标注里最常用的图形化工具,导出VOC格式的xml是一键操作,绝大多数标注团队都用它,所以标注文件结构非常规整。从质量角度看,我更关注两个信息:一是标注框是否贴合目标边缘,二是严重遮挡场景下有没有坚持标注。很多低成本数据集的标注员遇到两车交叠时直接跳过,导致模型在遮挡场景里彻底失灵。这份数据专门列出“车辆遮挡、车辆严重遮挡”,说明这两类样本是被刻意采集和标注过的,对监控场景很关键。

我建议你做一次抽检:用labelimg打开任意20张图,重点看三类画面——遮挡车、夜间车、密集排队车。遮挡车的框如果出现大范围包住两个目标的情况,说明标注员在靠猜测画框;密集车流里如果大量漏标,训练出来的模型在早晚高峰就会疯狂漏检。这类检查不做,后面迁移到自己的业务场景时容易翻大车。

2.3 这份数据能用在哪,又在哪里露怯

适用场景非常明确:交通道路监控下的车辆检测,包括城市道路卡口、高速收费站、路口信号灯抓拍这类固定机位画面,以及作为通用监控场景车辆检测数据的补充。所谓“补充”的意思是,如果你手头已经有一套监控数据集但车辆类别太少,可以拿这1000张图把类别覆盖补全,尤其是Multi-Axle和Tractor这两类在普通城市数据里很稀缺的样本。

边界也很清楚:航拍视角(无人机俯拍)、夜间红外、雨雾天气、车内视角这四类不在这份数据的覆盖范围内。如果你要做的是开阔路面上的平视监控,这份数据的场景吻合度很高;如果是俯拍或恶劣天气场景,建议把这份数据当作预训练基础集,再叠加你自己采集的几百张目标场景图,而不是直接拿来做最终模型。

3. VOC、COCO、YOLO三套标签:从目录结构到坐标换算的完整对比

3.1 三套格式各自长什么样

同一批标注给三种格式,看起来是体贴,实际是给自己留了一条转换后路。VOC格式每张图对应一个xml文件,XML里写的是目标的类名和边界框绝对像素坐标(xmin、ymin、xmax、ymax),目录结构通常是JPEGImages、Annotations、ImageSets三件套。COCO格式把整个数据集的标注汇总到一个json文件里,每条标注记录用[x, y, width, height]表示边界框,注意这里的x、y是框左上角坐标,不是中心点。YOLO格式每张图对应一个txt文件,每行是一个目标,格式是“类别ID x_center y_center width height”,四个坐标全部归一化到0到1之间。

格式标注文件坐标形式是否归一化典型目录
VOCxml(每图一个)xmin, ymin, xmax, ymax否,绝对像素JPEGImages + Annotations
COCOjson(整个集一个)x, y, width, height否,绝对像素annotations/*.json
YOLOtxt(每图一个)x_center, y_center, width, height是,0~1images/ + labels/

三种格式之间转换是目标检测训练的日常操作,因为Ultralytics的YOLO系列训练框架只认YOLO格式,即便你用COCO预训练权重,数据入口也建议统一转成txt。转换脚本网上很多,但能一次跑通不出错的很少,主要坑在坐标公式和类别映射上。

3.2 从VOC(xml)转YOLO(txt):中心点坐标换算

VOC转YOLO的公式很简单:x_center = (xmin + xmax) / 2 / 图片宽,y_center对高度同理,框宽高则用(xmax - xmin)/宽。公式不难,难的是“图片宽高从哪来”。VOC的xml里本来就有size节点,但标注工具写入的size可能和实际图片尺寸不一致,比如图片被预处理脚本压缩过但xml没更新,这时候按xml里的size做归一化,所有框都会偏移。

import xml.etree.ElementTree as ET from PIL import Image from pathlib import Path CLASS_NAMES = ["Auto", "Bus", "Car", "LCV", "Motorcycle", "Multi-Axle", "Tractor", "Truck"] def voc_to_yolo_txt(xml_file, img_path, out_txt): # 宽高以原图为准,不要直接信任xml里的<size>节点 w, h = Image.open(img_path).size tree = ET.parse(xml_file) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in CLASS_NAMES: continue cls_id = CLASS_NAMES.index(name) box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) xc = (xmin + xmax) / 2.0 / w yc = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") with open(out_txt, "w") as f: f.write("\n".join(lines) + "\n")

这段脚本的关键在CLASS_NAMES列表的顺序,它直接决定输出txt第一列的类别ID。YOLO的类别编号完全由这个列表和训练配置文件里的names数组决定,两边必须完全一致。我在刚接触YOLO时吃过这个亏:转换脚本里按类名字母序排列,训练yaml里按自己方便的顺序写,训练出来loss正常但mAP是0,血泪经验后面细说。另外要注意xmin可能为负数或xmax大于图片宽度,这类越界框转出来后坐标值超过1,虽然训练框架会自动裁剪,但最好在转换时做一次过滤。

3.3 从COCO(json)转YOLO(txt):bbox字段与类别映射

COCO的json里类别信息在categories数组,标注信息在annotations数组,每条annotation的bbox字段是[x, y, width, height],这个和VOC的[xmin, ymin, xmax, ymax]完全是两套语义,转换公式也要变:x_center = x + width/2,再除以图片宽。

import json from pathlib import Path def coco_to_yolo_txt(coco_json, out_dir): with open(coco_json, encoding="utf-8") as f: data = json.load(f) images = {img["id"]: img for img in data["images"]} # 类别映射不允许直接enumerate,必须按数据集本身的顺序来 cat2cls = {cat["id"]: i for i, cat in enumerate(data["categories"])} anns_by_img = {} for ann in data["annotations"]: anns_by_img.setdefault(ann["image_id"], []).append(ann) for img_id, img in images.items(): w, h = img["width"], img["height"] lines = [] for ann in anns_by_img.get(img_id, []): x, y, bw, bh = ann["bbox"] cls_id = cat2cls[ann["category_id"]] xc = (x + bw / 2) / w yc = (y + bh / 2) / h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {bw / w:.6f} {bh / h:.6f}") stem = Path(img["file_name"]).stem out_file = Path(out_dir) / f"{stem}.txt" out_file.write_text("\n".join(lines) + "\n" if lines else "")

这里有个很隐蔽的问题:data["categories"]的顺序不一定是0到7排列好的。有些数据集工具导出json时会把类别按字母序排,有些按标注时间排,直接enumerate拿到的ID顺序和你的业务分类对不上。正确做法是先打印categories数组看一眼原始顺序,再和YOLO训练时的names顺序对齐。COCO的file_name字段有时带子目录前缀,用Path(...).stem取文件名时不会有问题,但如果图片实际在别的目录,输出txt的路径要自己拼。

4. YOLO11一键训练脚本:GPU、CPU、Mac三平台的启动与调参

4.1 一键训练脚本在你跑之前做了什么

这份资源里附带的不只是一个训练脚本,而是一套启动流程:检查目录结构、校验图片和标签是否一一对应、统计每类目标数、生成data.yaml、加载YOLO11预训练权重、启动训练。这套流程的价值在于它把最容易出错的数据校验环节自动化了。如果你拿到的是原始数据集,第一步永远是把图片文件和txt标签放在同一级目录,确保文件名一一对应,否则训练时大量图片没标签,模型什么也学不到。

训练配置文件的格式是Ultralytics的YAML标准,path指向数据集根目录,train和val填相对路径或绝对路径,names列表的顺序就是类别ID编号。下面这段是典型的vehicle.yaml内容,路径替换成你自己的实际目录即可:

from ultralytics import YOLO import yaml data_cfg = { "path": "/path/to/vehicle_dataset", # 数据集根目录 "train": "images/train", # 训练图片目录(相对path) "val": "images/val", # 验证图片目录 "names": ["Auto", "Bus", "Car", "LCV", "Motorcycle", "Multi-Axle", "Tractor", "Truck"] } with open("vehicle.yaml", "w") as f: yaml.safe_dump(data_cfg, f, allow_unicode=True) model = YOLO("yolo11n.pt") # 自动下载预训练权重 results = model.train( data="vehicle.yaml", epochs=100, imgsz=640, batch=16, device="0", # 指定GPU设备 workers=4, cache=True # 小数据集可直接缓存到内存 )

注意path如果用相对路径,Ultralytics会以yaml文件所在目录为基准解析,我习惯写成绝对路径,省去一级一级找目录的麻烦。yolo11n.pt是YOLO11的Nano版本,权重文件几十MB,第一次运行会自动从官方源下载,之后离线也能跑。

4.2 GPU(GPUs)平台:多卡参数与batch分配

单卡训练时device填"0"即可,多卡填"0,1"或"0,1,2,3",Ultralytics会自动拉起分布式训练。一个关键认知是:batch参数在多卡下是“每卡”的batch_size,不是总batch。两卡各16等于总batch 32,学习率策略会自动适应,不需要手动按卡数放大。显存方面,yolo11n在640分辨率、FP16精度下占显存大约4-6GB,yolo11s涨到8GB左右,yolo11m要12GB往上,按自己的卡决定选哪个规格。

我一般用这套基准参数起步:batch=16、imgsz=640、workers=4、cache=True,Nano模型跑100个epoch在单张RTX 3060上大约一个多小时。训练过程中重点盯四个字段:train/box_loss要稳定下降,val/box_loss不能和train差太多,mAP50决定是否可用,mAP50-95反映框的精确度。这份数据集附带博主训练日志,你可以对照日志看每个epoch的指标走势,判断自己的loss算不算正常。

4.3 CPU平台:先确认要等多久

CPU训练纯属耐力活,但1000张图的小数据集不是不能跑。关键在参数压缩:imgsz从640降到544,batch降到8,epochs先设10个试跑,看单epoch耗时再决定总量。我实测一台8核笔记本纯CPU跑yolo11n、imgsz=640、1000张图,单epoch大约6到8分钟,30个epoch要3个多小时,能接受就铺开跑,不能接受就再降imgsz。Windows上特别注意workers必须设为0或2,设8大概率报DataLoader worker exited unexpectedly,这是Python多进程在Windows下的老毛病。

model.train( data="vehicle.yaml", epochs=50, imgsz=544, batch=8, device="cpu", workers=0, # Windows设0,Linux可以设2 cache=True )

CPU模式下cache=True特别重要。1000张图全缓存到内存可能占用几个GB,但能省掉每个epoch重复读盘的IO时间,整体提速明显。如果内存紧张,可以只缓存部分数据,Ultralytics也支持cache="ram"或cache="disk"两种策略。

4.4 Mac(M芯片)平台:mps设备与torch版本

M系列芯片跑YOLO11要用Apple的MPS后端,对应device="mps"。Ultralytics从8.3.0版本开始完整支持yolo11,PyTorch需要2.0以上,建议直接装当前最新稳定版。一个已知的坑是mps对部分算子的支持不完整,运行到某些层会报NotImplementedError,后面避坑章节会展开。这里先给一组相对稳的参数组合:关闭自动混合精度,因为mps的half支持不稳定,开了容易训着训着出现NaN loss。

model.train( data="vehicle.yaml", epochs=50, imgsz=640, batch=8, device="mps", # M1/M2/M3芯片走mps workers=2, cache=True, amp=False # mps后端建议关掉amp )

我自己的体验是M芯片跑yolo11n比同价位Intel CPU快一倍以上,但和NVIDIA独显比还有明显差距。如果你在公司有GPU机器,Mac可以只做数据预处理和结果可视化,训练放GPU上跑;如果只有Mac,1000张图50个epoch一晚上也能跑完,不算太离谱。

5. 避坑手记:YOLO11车辆检测训练中的4个典型翻车现场

5.1 现象:loss正常下降,验证集mAP却是0

训练到第10个epoch,train/box_loss掉到了0.05以下,val/box_loss看着也正常,但每个epoch结束打印的mAP50一栏全是0。这个现象我第一次遇到时差点把数据集删了重标。实际原因是类别ID错位:转换脚本里CLASS_NAMES和训练yaml里names的顺序不一致,比如转换脚本按字母序排了Auto在前,而yaml里把Truck排在了第0位,模型学到的类别编号和真实标签对不上。解决方法是统一一个class_names列表,转换脚本和yaml都从同一个地方生成。排查时打开一个txt文件看一眼首列数字,再对照xml里的类名,几秒钟就能确认。

head -n 5 labels/train/0001.txt

如果首列出现大于7的数字,说明ID越界;如果首列数字对应的类名和xml里框的类名对不上,说明排序错乱。从那以后我要求任何数据集交付,必须附带一份类别映射说明文档。

5.2 现象:Mac的mps训练到一半报not implemented

用device="mps"启动训练,第一个epoch正常,跑到第二或第三个epoch中断,报错信息类似“Could not run 'aten::_slow_conv2d_forward' with arguments from the 'MPS' backend”。这不是你代码写错了,是PyTorch的mps后端对某些卷积算子的支持还没覆盖全,同一个版本下yolo11n可能跑通、yolo11m跑到某个层就崩。解决按优先级尝试:先关amp用fp32跑,大多数情况下能解决;还崩就把imgsz降到544,减少某些层的计算路径;再不行退到device="cpu",M芯片跑CPU也不慢。升级PyTorch到nightly版本能覆盖更多算子,但可能引入新问题,我一般不用。

5.3 现象:CPU平台一个epoch要跑半小时

1000张图,8核笔记本,yolo11n,imgsz=640,单epoch半小时不一定是机器问题,多半是数据加载在拖后腿。Windows下workers设了8,每个epoch结束都要等数据加载,多次报DataLoader worker exited unexpectedly。另外一个常见原因是没开cache,每次迭代都从机械硬盘或网络盘读图片,IO成为瓶颈。解决方法是workers=0或2,cache=True,imgsz降到544。还有一个小技巧:先用10个epoch跑一遍,看单epoch耗时是否线性稳定,如果前几个epoch慢、后面突然变快,大概率是系统在做磁盘缓存预热,不用太担心。

5.4 现象:验证集指标好看,实拍视频里全是漏检

mAP50到了0.87,把模型接到实拍视频流上,白色货车漏检、两车交叠时漏检、黄昏光线下一辆都测不准。这个问题分两方面看:一是验证集本身“简单”了——如果数据划分时把同一场景的连续帧同时分进train和val,验证集和训练集高度相似,指标虚高;二是遮挡样本占比太低,模型对遮挡目标的泛化能力本来就不足。解决方法是把数据集里标注明确为“车辆遮挡”“严重遮挡”的图单独挑出来做测试集,用这个子集重新评估;同时检查Auto和Car的标签一致性,如果两个类在部分样本里标注语义重叠,模型在边界样本上就会摇摆。

6. 拿到数据后先跑这三段检查:类别分布、空标签与验证集可视化

6.1 类别分布扫描与空标签检测

训练前花10分钟跑一个统计脚本,能避免训练到一半才发现数据问题的尴尬。这段脚本读取labels目录下所有txt,统计每个类别的目标总数,同时找出空标签文件。对1000张图的数据集,扫描时间在几十秒以内,建议直接复用。

from pathlib import Path from collections import Counter def scan_labels(label_dir): counter = Counter() empty_files = [] for txt in Path(label_dir).glob("*.txt"): lines = [ln.strip() for ln in txt.read_text().splitlines() if ln.strip()] if not lines: empty_files.append(txt.stem) continue for line in lines: cls_id = int(line.split()[0]) counter[cls_id] += 1 return counter, empty_files label_dir = "vehicle_dataset/labels/train" counter, empty_files = scan_labels(label_dir) print("类别分布:", dict(sorted(counter.items()))) print("空标签文件数:", len(empty_files))

运行后先看有没有空标签文件,再看每个类别的目标数是否均衡。如果Motorcycle只有几十个目标而Truck有几百个,说明这个类本身稀缺,训练时可能欠拟合,要考虑加数据增强或降低类别损失权重。类别分布表也建议存档,训练结束后和验证集指标放一起分析,能看出哪一个类拉低了整体mAP。

6.2 验证集可视化与训练日志留存

第二个固定动作是跑一次验证集批量预测,把预测结果存成带框图片,肉眼检查有没有漏检和错类。

yolo predict model=runs/train/exp/weights/best.pt source=vehicle_dataset/images/val save_txt=true save_conf=true project=visual_check

重点关注两类错误:一是大量漏检,说明模型对某些形态的车还没学会;二是Auto和Car互相误判,说明这两个类在标注时边界就没划清楚,考虑合并类别重新训练。训练日志也不要只留在Ultralytics的runs目录,我习惯把每次实验的yaml、类别分布表、最终mAP指标汇总到一个文本文件里,换数据集或者调参时翻出来对比特别有用。

从那以后我拿到任何目标检测数据集,不管标注多“官方”,第一件事永远是跑一次类别分布扫描加一次验证集可视化,两步做完再开训练。这个习惯已经救过我至少两次翻车,一次是空标签文件混进训练集,一次是类别ID错乱但loss曲线看起来完全正常。完整的转换脚本和训练日志都在分享包里,获取方式在文末,拉到页面底部就能看到网盘链接,下载后记得先跑一遍第6章的检查脚本,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询