简介:中国车辆车牌号识别数据集面向计算机视觉目标检测项目开发者与学习车牌识别的初学者,可用于训练YOLOv11等模型,重点覆盖中国车牌数字与字母的识别任务。资源包共2000个文件,核心构成为1457个txt标注文件和542张jpg图片,另含1个yaml配置文件;txt文件保存每一个车牌字符框的类别与坐标,jpg为真实拍摄场景下的车牌图像,yaml内置类别名、训练/验证路径等关键设置,下载后稍作调整即可接入常见YOLO训练流程,省去手动整理数据集的麻烦。压缩包仅17.18MB,体量轻巧,便于快速开展模型训练与效果验证。目前已有950人学习下载,适合毕业设计、智能交通、停车场管理等应用场景参考;无论是初学目标检测还是部署车牌识别功能,都能有效减少前期数据准备成本。通过该数据集,读者可获得一批标注完整的车牌样本与开箱即用的配置参考,并借助规范的目标框信息快速评估模型性能,为后续调参与优化打下扎实基础。
1. 拿到中国车辆车牌号识别数据集:先别急着训,先看清这1458张图
做车牌识别的人最容易犯的错,是拿到一套标注好的 zip 就解压开跑训练,跑完发现模型在测试视频里各种翻车。中国车辆车牌号识别数据集这名字听起来完整,但真正决定模型能不能用的,是里面 1458 张标记图片的标注口径:标的是整块车牌,还是标到每个数字字母字符。这个口子没对齐,后面训练、调参、部署全是白费。这套数据价值不在于张数,而在于它直接给的是 yolov11 格式的 txt 标记,意味着你可以跳过最枯燥的格式转换,直接进训练管线。适合谁用?正在做停车出入口、高速卡口或者路侧巡检的工程师,手里有 YOLOv11 基础,缺一批能快速验证流程的车牌数据。
2. 拆开这张1458张标记图片.zip:目录结构、标签格式与规模判断
2.1 车牌检测和车牌识别是两条路,目录决定了你走哪条
我先说一个行内经常混淆的问题:“可识别车牌数字和字母”这句话,在不同数据集里实现方式完全不同。第一种做法是整牌检测,模型只负责输出一个框,把整块车牌框住,框里面的字符识别交给 OCR 模块去做,这类数据集的 labels 里只有一个类别,比如0。第二种做法是字符级检测,模型输出一串框,每个框对应一个数字或字母,类别 id 有几十个,比如0到9代表数字,10到35代表字母,训练完成后直接按坐标排序读出车牌号。
标题说的是“可识别车牌数字和字母”,你拿到手的第一件事,不是训练,而是用文本编辑器打开任意一个.txt标签文件看一眼。YOLOv11 的标签格式是每行五个数:class x_center y_center width height,坐标全部归一化到 0 到 1 之间。如果每行 class 是 0,x、y、w、h 算出来的框接近整车牌的宽高比,那就是整牌方案;如果一行里有多个框且每个框近似正方形,那就是字符级检测方案。
这两种方案的后续技术路线完全不同。整牌方案要接 OCR,字符级方案要处理字符排序逻辑,尤其是一块车牌上汉字、字母、数字混排的情况。你先按这个思路去判断数据集属于哪种,再决定下一步写什么配置文件,比盲目解压运行靠谱得多。
2.2 用脚本核对labels:归一化坐标、类别数量、图片对应关系
拿到 dataset 目录后,我会按下面的顺序做三件事:确认目录结构、统计类别数量、检查标签坐标有没有越界。目录结构先看有没有images/train和labels/train这种标准划分,如果图片和标注混在一个文件夹里,你需要自己按比例拆一次。拆的时候注意,必须让图片和标签文件保持同名同路径,YOLOv11 是按文件名自动找对应标签的,名字对不上,训练时会直接把这张图当无目标图跳过,而且不报错。
确认完目录,跑一段最简单的 Python 脚本,抽查五个标签文件的结构:
from pathlib import Path labels_dir = Path("labels/train") for txt in sorted(labels_dir.glob("*.txt"))[:5]: print(f"文件: {txt.name}") for line in txt.read_text().strip().splitlines(): c, x, y, w, h = map(float, line.split()) # 归一化坐标允许 0~1,越界说明标注或转换环节出了问题 if not (0 <= x <= 1 and 0 <= y <= 1 and 0 <= w <= 1 and 0 <= h <= 1): print(f" 警告: 坐标越界 {c} {x} {y} {w} {h}") else: print(f" class={int(c)} x={x:.4f} y={y:.4f} w={w:.4f} h={h:.4f}")这段脚本会逐行读取每个标注文件,把归一化坐标打印出来。注意w和h是目标的相对宽度和高度,不是右下角坐标,很多人第一次接触 YOLO 标签容易把这两个值当成绝对尺寸,后续在写后处理代码时算错框的位置。另外,如果发现坐标有极少量的越界,可能只是某一张图标注时手滑,直接过滤掉那张图或那条标注都可以,数量少不影响整个训练。
类别数量统计也建议一次做完。命令行一条 awk 就够了:
find labels/train -name "*.txt" -exec cat {} + | awk '{print $1}' | sort | uniq -c输出结果会列出每个类别 id 出现了多少次。如果类别 id 只有0一个,说明是整牌检测;如果 id 范围从0到33甚至更多,那说明字符类别已经平铺开了。这个数字决定了你后面写data.yaml时的nc参数,填错类别数,训练不会报错,但模型输出维度和你预期对不上。
2.3 1458张图能训出什么效果:迁移学习与边界的判断
1458 张图放在检测任务里属于偏小的规模。一张图里如果只有一块车牌,那正样本数量也就是 1458 个左右,这个量级从头训练一个 YOLOv11 是完全不够的,但如果你用官方预训练权重做微调,情况就不同了。常见的做法是加载yolov11s.pt或者yolov11n.pt,前几十层直接复用在大规模数据集上学到的通用特征,只让后面几层去适应车牌这个新类别。
那 1458 张图能到什么效果?我的经验是,在场景相对固定的卡口、停车场出口,验证集 mAP 做到 0.85 以上并不难;但如果数据里包含了不同省份的蓝牌、新能源绿牌、不同光照和角度,模型泛化会明显变差。不要指望一套 1458 张的数据集覆盖全国各种极端场景,它的正确用途是帮你把整套训练、验证、部署链路跑通,先拿到一个能工作的基线模型,再用现场数据持续补充。
这里还有个小技巧:数一数数据集中有没有同一辆车、同一个背景在不同帧里反复出现的情况。如果全是连拍帧,实际信息量可能只相当于几十个独立场景,训练时模型容易对背景过拟合,验证集分数虚高。遇到这种情况,我一般会先把疑似重复的帧按时间间隔抽稀,再划分训练集和验证集,保证验证集里没有和训练集同框的车牌。
3. 用YOLOv11把车牌模型跑起来:环境配置、训练命令与曲线判读
3.1 YOLOv11环境配置:ultralytics与CUDA版本的一次性对齐
现在到了真正动手的环节。先说环境,YOLOv11 在ultralytics这个 Python 包里直接支持,不需要你手动搭网络。常见的一步安装命令是:
pip install ultralytics如果机器上有 NVIDIA 显卡,建议先装好匹配的 CUDA 版 PyTorch,再装 ultralytics。判断是不是匹配,有个很简单的办法:装完后跑一行python -c "import torch; print(torch.cuda.is_available())",输出True说明 GPU 能用,输出False说明 PyTorch 装成了 CPU 版,后面训练会慢到怀疑人生。这一步也是很多人口中的玄学区域,同一个机器,有人训练飞快,有人 CPU 硬扛,问题基本都出在这行检查没做。
CPU 机器能不能训?能,但 1458 张图、800 分辨率下,哪怕选最小的 yolov11n,一个 epoch 也可能要几分钟到十几分钟,整个训练流程走下来需要一晚上。如果你只是想验证数据集格式对不对,先用 CPU 跑 10 个 epoch 是没问题的;想拿到能上线的权重,还是建议上 GPU 或者云主机。
环境配好之后,验证一下版本一致性。YOLOv11 是ultralytics官方版本直接支持的,不要太旧,建议pip show ultralytics看一下版本号,2025 年之后发布的版本基本都带 yolov11 模型定义。版本太旧会报KeyError: 'yolov11s.pt'这类错误,解决办法就是升级包,不用改代码。
3.2 写car_plate.yaml并用预训练权重启动训练
准备训练的第二步是写数据集配置文件。在项目根目录新建一个car_plate.yaml,内容大致如下:
# 训练集和验证集的图片路径,用绝对路径最省心 train: /home/user/datasets/car_plate/images/train val: /home/user/datasets/car_plate/images/val # 类别数量:根据第2章统计结果确定 nc: 1 # 类别名称:整牌检测写 plate,字符级检测就写 0~9 和 A~Z 的映射 names: 0: plate注意nc和names必须和标签里的 class id 严格对应。前面用 awk 统计过类别 id 的范围,如果统计出来只有 0,那nc就写 1;如果统计出来有一堆 id,names就要把每个 id 对应字符列全,比如数字从0到9,字母从A到Z。写错nc不会让程序崩溃,但训练出来的模型类别输出维度是错的,推理结果会乱套。
配置文件写好后,启动训练。我的常用命令是这样的:
yolo train \ data=car_plate.yaml \ model=yolov11s.pt \ epochs=100 \ imgsz=800 \ batch=16 \ device=0 \ patience=30 \ project=car_plate_runs \ name=exp1逐项解释一下参数含义。model=yolov11s.pt是加载官方预训练权重,然后在你的数据集上微调,而不是随机初始化从头训。imgsz=800是输入分辨率,车牌字符属于小目标,640 会明显吃亏,800 是一个兼顾速度和精度的起点。batch=16根据显存调整,显存不够就降到 8。patience=30表示连续 30 个 epoch 验证集指标没有提升就提前停止,数据集小的时候有这个参数能省不少时间。
这里有个容易忽略的点:训练命令里没有单独指定model=yolov11s.pt的类别数,YOLOv11 会自动根据你的nc调整最后一层输出维度。所以即使你下载的预训练权重是 COCO 80 类的,加载到你的单类车牌任务里也不会报维度错误,这个由框架内部自动处理,不用手改权重。
3.3 训练日志怎么读:loss、mAP和过拟合的判读方法
训练启动后,控制台会滚动输出每个 epoch 的指标。先看box_loss和cls_loss的趋势,这两个值应该稳步下降。如果cls_loss一直降不下去,大概率是类别定义有问题,比如前面说的汉字没标注,模型被逼着把汉字区域当背景学,但字符的纹理和背景差异又大,损失很难收敛。
再看mAP50和mAP50-95。mAP50是 IoU 阈值 0.5 下的平均精度,车牌检测任务主要看这个。mAP50-95要求更严格,数值通常会低一截,它反映的是框的定位精度。对车牌识别来说,mAP50高但mAP50-95低,说明框大方向对但边缘贴得不准,后续如果要做字符裁剪,会影响字符级的分类精度。
过拟合怎么判断?看验证集 loss。如果训练集 loss 一直降,验证集 loss 在某个点开始反弹,mAP 也不再上升,就是典型的过拟合。1458 张数据训练到 100 个 epoch,通常在第 40 到 60 个 epoch 之间就会看到这个拐点。不要等 100 个 epoch 全跑完,patience参数会帮你停,你只要盯住 mAP 曲线有没有平台期就行。
我自己习惯在训练完做一件事:把验证集里 mAP 最高那一轮的权重拿出来,跑一次验证集推理,直接看图而不是只看数字。看图的目的是确认框是不是真贴合车牌,有没有把车灯、保险杠装饰条误检成车牌。数字会骗人,框不会。
4. 车牌识别训练避坑:五个直接影响mAP的标注与调参陷阱
4.1 只有数字和字母的标签集:车牌汉字被模型当成了背景
现象:训练过程 loss 正常下降,验证集 mAP 也不低,但把模型放到现场视频里测,发现车牌第一个汉字的位置完全没有检测框,框出来的车牌老是缺一个角。
原因:如果数据集只标注了数字和字母,那每张图里汉字区域就是背景。模型学到的规律是“数字和字母是车牌,汉字不是”,它当然不会对汉字产生响应。中国车牌的结构是汉字加字母加数字,例如“京A12345”,你把“京”忽略了,识别结果天然不完整。
解决:先确认你的任务边界。如果业务只需要识别车牌后五位数字,那这个数据集没问题,你的模型定位就是“字符检测器”。如果业务需要输出完整车牌号,你有两条路:一是换用整牌检测方案,只标plate一个类别,字符交给 OCR;二是给汉字补齐标注,把“京、津、沪、渝、冀”等汉字类别加进names重新训练。补齐标注的工作量不大,但分类器需要重新训,注意不要漏掉某个省份简称,否则那个省份的车牌一定会翻车。
4.2 标注框包住铆钉和边框:loss在降,OCR在哭
现象:训练曲线看起来一切正常,但预测出来的框比字符实际区域大一圈,实测时把框内的图裁出来送给 OCR,识别率反而很低。
原因:标注时把整个白色车牌底板或周围铆钉、边框一起圈进去了,框内混入了大量非字符像素。对检测模型来说 IOU 可能还行,但对下游 OCR 来说,这些背景像素直接干扰了字符分割的阈值计算。
解决:打开标签文件,抽查十张图,把归一化坐标换算回像素坐标,在图上画框看看贴合度。整牌检测场景,框应该紧贴车牌四边,不要包含外围的黑色边框;字符级检测场景,框应该紧贴每个字符的外接矩形,不要把字符间距也算进去。标注口径统一后,重新训练,你会发现mAP50-95有明显提升,OCR 的输入质量也上来了。
4.3 蓝牌数量压过绿牌:类别不平衡让新能源车隐身
现象:蓝牌识别效果不错,新能源绿牌几乎检不到,或者置信度很低。
原因:中国车牌里蓝牌比例远大于绿牌,1458 张图里如果只有几十张绿牌,模型没见过足够的绿色底色样本,自然学不到绿牌特征。这不是模型的问题,是样本分布的问题。
解决:先统计一下数据集中绿牌的占比。如果低于百分之十,有几个调整方向:一是对绿牌图片做数据增强,把曝光、色温、对比度做随机扰动;二是把绿牌样本复制几份混合到训练集里,虽然会引入一定的重复,但比没有强;三是在损失函数上调整类别权重,让模型对少样本类别更敏感。最有效的还是去现场补拍绿牌数据,哪怕补五十张,效果都立竿见影。
4.4 imgsz=640的小目标困境:字符小于8像素怎么救
现象:验证集 mAP 五六十,实际测试时发现摄像头画面里稍远一点的车,车牌字符只有十几个像素,模型完全检不出。
原因:YOLOv11 的检测头在深层特征图上预测,输入分辨率 640 时,字符区域可能被压缩到几个像素大小,特征早就丢失了。车牌是大场景里的小目标,这个矛盾在卡口场景特别常见。
解决:把imgsz从 640 提到 800 或 960,字符特征会清晰很多。代价是推理速度下降,显存占用上升,需要在部署时权衡。另一个思路是用yolov11n这种更轻量的模型配合imgsz=960,精度可能比yolov11s + imgsz=640更好,速度还不慢。如果你做的是 Jetson 这类边缘设备,优先考虑提高输入分辨率配合轻量模型,这个组合在车牌场景下通常是最优解。
4.5 按文件名随机划分数据集:验证集得分虚高
现象:训练时验证集 mAP 能到 0.9,换一个停车场摄像头实测,直接掉到 0.5 甚至更低。
原因:数据集里如果包含同一场景的连拍帧,随机划分时同一块车牌会同时出现在训练集和验证集里,模型等于提前见过答案,验证分数虚高。这种问题在自采数据里特别常见,按文件名排序后直接切片划分,同时间段的数据全落到一边了。
解决:划分数据集之前,先按拍摄时间、地点或视频片段分组,确保同一个场景的数据只出现在训练集或验证集中的一个里。如果数据来源不明确,至少按文件名前缀分桶,比如同一个摄像头目录下的图不进同一个集合。验证集分数低一点不可怕,可怕的是虚高之后你信心满满地上线,然后在真实场景被用户吐槽识别不准。
5. 从验证到落地:保存推理结果、导出引擎与阈值校准
5.1 用best.pt批量预测并保存带框图片
训练完成后,project/car_plate_runs/exp1/weights/目录下会生成best.pt和last.pt。我一直用best.pt,last.pt只在你打算继续训练时才有价值。先跑一批测试图,把预测结果保存下来,确认真的没问题再做导出:
from ultralytics import YOLO model = YOLO("car_plate_runs/exp1/weights/best.pt") # save=True 会把带检测框的结果写到 save_dir 指定目录 results = model.predict( source="test_imgs/", conf=0.45, imgsz=800, save=True, save_dir="test_results", )这里conf=0.45是置信度阈值,低于这个值的检测框会被过滤掉。车牌检测任务我一般先用 0.45 跑一轮看效果,如果漏检多就调到 0.3,如果误检多就调到 0.6,具体要看场景。save_dir控制输出目录,不设置时默认写到runs/detect/predict。
预测输出的不仅仅是图片,results对象里包含了每个框的坐标、置信度和类别 id。后续做车牌识别业务,你需要的是这些结构化数据,比如把坐标传给 OCR 模块,或者写入数据库。这里的一个细节是:结果坐标是原始图像分辨率下的像素坐标,不是归一化坐标,做后处理时不要忘记乘回图像宽高。
5.2 导出ONNX和TensorRT,在Jetson设备上跑检测
训练只是第一步,真实项目里模型最终要跑在推理环境里。常见落地路径是导出 ONNX,再根据硬件转成 TensorRT 引擎或 RKNN 格式。导出命令很简单:
yolo export model=car_plate_runs/exp1/weights/best.pt format=onnx imgsz=800 dynamic=True导出后可以用onnxruntime做 CPU 推理验证,确认导出的 ONNX 和 PyTorch 输出一致。dynamic=True让输入尺寸可变,部署时灵活性大一些,代价是某些加速设备上的兼容性变差,如果你确定固定 800 分辨率,dynamic可以关掉。
Jetson 设备上部署通常用 TensorRT,YOLOv11 官方支持直接把权重导出为engine格式:
yolo export model=best.pt format=engine device=0 half=True这里half=True是开启 FP16 精度,推理速度能快一倍左右,精度损失在车牌检测任务里几乎感知不到。在Jetson Nano这类设备上部署时要注意的是:TensorRT 引擎和 CUDA、TensorRT 版本强绑定,换一台机器需要重新导出,不能像 ONNX 那样直接拷贝使用。这是一个经常被忽略的坑,现场部署时临时导引擎会白折腾半天。
5.3 最后一道工序:按置信度曲线定阈值
很多人在推理时随便填一个conf=0.25,然后发现误检一堆。我习惯在正式部署前,用验证集把不同置信度下的指标算一遍,选一个“漏检和误检平衡”的点。做法是在 0.3 到 0.7 之间每隔 0.05 跑一次验证,记录误检率和漏检率。
如果你的业务是停车场出入口,车必须被识别出来,漏检代价远高于误检,阈值就往低了设,0.3 左右。如果你的业务是高速卡口抓拍,错检会录进不存在的车,阈值就往高了设,0.55 甚至 0.6 都可以接受。这个选择没有标准答案,完全由业务兜底能力决定。
我做车牌识别项目多了之后,养成的习惯是:每拿到一批数据,先花一小时看标签,再花一小时跑基线,最后才讨论调参和部署。数据集的标注质量决定模型的天花板,YOLOv11 只是把地板抬高了。这套 1458 张的数据集能帮你把整个流程走通,但真正要上线,还需要你持续补拍现场数据、迭代标签。希望这个方向能帮你省掉一些无用功,把时间花在真正影响业务的地方。
本文还有配套的精品资源,点击获取