简介本资源是一套基于YOLOv5实现的中文车牌端到端检测与识别完整方案面向计算机视觉初学者、智能交通系统开发者及高校课程实践者解决真实场景下多类型中文车牌含普通蓝牌、新能源绿牌、警车、军车、使馆车等12类及双层车牌的精准定位与字符识别难题。压缩包共146个文件涵盖45个Python源码含训练/推理/预处理脚本、20个YAML配置文件模型结构与超参定义、15张JPG/PNG实测图像如police.jpg、single_green.jpg等典型样本、7个Shell部署脚本及5份Markdown使用文档辅以Dockerfile、.pt模型权重与字体资源整体大小24.91MB。已有166人学习下载提供从环境配置、数据准备、模型训练到单图/视频推理的全流程可运行代码与详细说明特别适配中文字符复杂结构与双层遮挡场景开箱即用且便于二次开发与迁移学习。 车牌识别这个方向做过的人都知道真正落地的时候坑远比你想象的多。尤其是中文车牌字符集里有汉字、字母、数字还得面对蓝牌、绿牌、黄牌、白牌、黑牌再加上大型货车挂车常见的双层车牌一套模型要全部吃下来不是简单拿个开源检测模型就能搞定的。我最近在整理一套基于YOLOv5的车牌检测与中文车牌识别方案整套源码加模型加使用文档都打包好了标题里写得很清楚支持12种中文车牌双层车牌也能处理。这篇文章我就把整个方案的思路、核心细节、部署步骤、训练调优以及我实际踩过的坑完整拆开讲一遍。如果你正准备做停车场道闸、卡口监控、高速收费这种场景下的车牌识别或者你只是想把这套流程跑通作为毕设、项目演示这篇东西应该能帮你少走不少弯路。1. 项目概述与整体设计方案1.1 项目定位检测识别两条线分别处理这套项目在最开始设计的时候我就定了一个原则检测是检测识别是识别两个环节彻底分开。为什么这么干因为这两个任务的目标完全不同。检测模型要回答的是车牌在哪它需要在整张图里找目标哪怕是远距离、小目标、光线昏暗也得尽量把位置框准。而识别模型要回答的是车牌是什么它只需要拿到一块裁剪好的车牌图像把里面的字符逐个读出来。一个是全局任务一个是局部任务混在一起反而互相干扰。常见的端到端方案比如直接把OCR大模型套在车牌上效果不是不行但在实际部署时你会发现太重了推理速度、显存占用、成本都压不住。而用YOLOv5做检测再单独接一个轻量的车牌识别网络两阶段各管各的精度和速度都更容易调到理想状态。这套项目就是采用了两阶段设计YOLOv5负责检测背后再接一个专门为中文车牌字符集设计的识别模型。1.2 为什么选YOLOv5而不是YOLOv8或更重的模型很多人问我现在YOLOv8、YOLOv9都出来了你干嘛还用YOLOv5说实话YOLOv5确实是老了一点但它有两个优势是很多新模型比不了的一是生态成熟网上的资料、预训练权重、部署案例极多遇到问题基本一搜就有解法二是它对硬件的要求比较友好从GPU到CPU从PyTorch到ONNX再到TensorRT整个转换链路我都跑过很多遍非常稳。另外车牌检测本质上是一个单类别检测任务背景相对单一目标纹理特征明显YOLOv5s这个体量完全够用。我在这套项目里默认用的是YOLOv5s作为检测模型如果你想在嵌入式设备上跑换成YOLOv5n帧率还能再往上走。没必要一上来就上大模型算力是要花钱的。1.3 识别端为什么不能直接用通用OCR有朋友图省事想直接拿PaddleOCR或者Tesseract来识别车牌。我只能说通用OCR在车牌场景下不太聪明。通用OCR训练数据里车牌图像占比极少它擅长的是印刷体文本面对车牌这种特殊字体、特殊背景、特殊字符排列识别率会掉得很厉害。车牌识别必须走专用模型。中文车牌的字符集是固定的省份简称汉字、大写字母、数字字符数量有限且完全封闭。所以识别端的模型设计更适合用轻量的CNNRNNCTC结构字符表就写死在代码里模型只需要在这几十个类别里做分类准确率自然要高得多。这套项目里识别端就是按照这个思路做的训练完成后单张车牌识别耗时在毫秒级。2. 核心技术细节拆解12种车牌与双层牌处理2.1 12种中文车牌类型都是哪来的支持12种中文车牌不是营销话术这是国内路面上常见的车牌类型的合集。我在这套项目里按底牌颜色和车辆属性分别统计过主要的车牌类型包括以下几类车牌类型底色字符颜色适用范围小型汽车蓝牌蓝白普通燃油小型车大型汽车黄牌黄黑大型客车、货车新能源小型车绿牌绿黑/白纯电、混动小车新能源大型车绿牌绿黑/白纯电、混动大车教练车黄牌黄黑驾校教练车警用汽车白牌白黑公安、司法警车军用汽车白牌白黑/红军车驻华使馆黑牌黑白使馆车辆涉外外企黑牌黑白外资企业车辆港澳入出内地车牌黑白粤Z港/澳挂车黄牌黄黑半挂车农机/其它专用牌绿/黄黑农用、专项作业不同车牌的底牌颜色差异很大这对检测模型来说是个天然的挑战。YOLOv5在训练时如果用了足够的颜色增强和曝光增强模型会慢慢学会不看颜色看结构因为车牌本身的字符排版、边框比例、反光特征是有共性的。2.2 双层车牌为什么那么多项目翻车双层车牌主要集中在大型货车、挂车上常见形式是上面一行是省份简称和发牌机关代码下面一行是号码整体高度比普通车牌高一倍左右宽高比例和普通蓝牌完全不一样。很多开源项目翻车不是模型能力不够而是数据处理阶段就没考虑双层牌的情况。如果把双层车牌和单层车牌混在一起做检测YOLOv5默认的anchor对长条形目标不友好很容易出现漏检或者框不完整的情况。而且识别模型如果不单独针对双行字符做训练直接把整块双层车牌压成一张小图送进识别网络字符挤在一起CTC解码效果会非常差。这套项目里我做了两个关键处理。检测端我在anchor设置里加入了一组长宽比比较大的先验框专门适配双层车牌同时在训练数据增强步骤中对双层车牌做了单独的尺寸扰动避免模型只见过一种尺度。识别端我实现了一个双行判断逻辑当前面检测输出的目标框高宽比超过阈值时自动将车牌图像按中间线切分成上下两部分分别送进识别网络再把结果拼成最终字符串。这样双层车牌从一张图硬识别变成了两行分别读识别率一下就提上来了。2.3 字符集设计不只31个省份简称识别端默认支持的字符集大概是这么个构成全国31个省级行政区简称汉字、24个大写英文字母I和O容易和数字混淆通常剔除或做特殊处理、10个数字再加上少量特殊字符比如挂车用的挂字、警车用的警字、使馆用的领字整体字符表大约在65个上下。设计字符集的时候有一个细节很多人会忽略汉字部分必须跟实际业务场景匹配。比如你做的是某个省的本地项目理论上可以只保留部分省份简称来减小分类难度但如果你做的是通用闸机、全国道路监控字符表就不能砍必须全量保留。这套项目默认是全量字符表但代码里预留好了配置接口你可以在字符表文件里自由增删。2.4 识别模型结构CNNRNNCTC三板斧识别端的核心网络结构我用了类似LPRNet和CRNN结合的一套轻量方案。输入是一张固定尺寸的车牌图像比如宽94高24或者宽168高48先经过几个卷积层提取特征再经过两到三层双向LSTM建模序列上下文最后接CTC损失函数完成字符序列对齐。CTC这个损失函数是整个识别链路里最关键的一环。车牌字符间距不固定不需要像传统OCR那样先做字符分割CTC允许模型为每个位置输出一个字符概率分布然后通过动态规划寻找到最可能的字符序列。这就省去了字符分割这个最大的误差源。我试过先分割字符再逐个识别的方案遇到字符粘连、铆钉遮挡、边框干扰的时候直接崩换了CTC之后稳了很多。3. 环境部署与快速上手3.1 环境依赖与版本匹配拿到压缩包解压之后第一步是把环境搭起来。这套项目基于PyTorchPython版本建议3.8以上PyTorch建议1.8到2.x之间都行CUDA版本看你手里的显卡驱动来定。装依赖的时候有个坑YOLOv5的requirements.txt里torch版本如果跟你的CUDA不匹配import的时候就会报错。我建议先单独装好PyTorch再装其它依赖。如果你只是推理不打算重新训练CPU版本也完全可以跑只是速度会慢一些。整个依赖清单大概包括torch、torchvision、numpy、opencv-python、matplotlib、pandas、pyyaml、tqdm、seaborn如果要用到模型导出还需要onnx和onnxruntime。3.2 源码目录结构解读解压后你会看到这样一个目录结构plate_project/ ├── detect.py # 检测入口YOLOv5风格的推理脚本 ├── train.py # 训练入口支持训练检测模型 ├── recognize.py # 识别入口加载识别模型输出车牌字符串 ├── run_pipeline.py # 检测识别串联脚本 ├── models/ # YOLOv5网络定义 │ ├── yolov5s.yaml │ ├── yolo.py │ ├── common.py │ ├── plate_rec.py # 车牌识别网络定义 ├── weights/ │ ├── plate_detect.pt # 检测模型权重 │ └── plate_rec.pth # 识别模型权重 ├── configs/ │ ├── plate.yaml # 检测数据集配置 │ └── rec_chars.txt # 识别字符表 ├── datasets/ # 训练数据路径 ├── utils/ │ ├── anchors.py # anchor计算工具 │ └── plate_utils.py # 双层车牌判断与切分工具 ├── docs/ │ └── 使用文档.pdf └── requirements.txt这个结构其实已经把YOLOv5官方仓库的工程习惯继承下来了。detect.py和train.py的调用方式跟官方非常像你如果跑过YOLOv5上手几乎零成本。识别网络的代码单独放在plate_rec.py里和检测部分解耦方便你替换成自己的网络结构。3.3 用自带权重串起整个流程跑通整个项目非常简单。先用预训练检测权重跑YOLOv5的推理脚本输入一张测试图片python detect.py --weights weights/plate_detect.pt --source test.jpg --conf 0.5 --img 640这个命令会输出检测到的车牌位置生成标注好的图片。如果你只想看检测框效果这一步就够了。但要输出车牌字符串还得接着跑识别python recognize.py --det-weights weights/plate_detect.pt --rec-weights weights/plate_rec.pth --source test.jpg如果你嫌两步麻烦直接跑串联脚本python run_pipeline.py --source test.jpg它会自动完成全图检测 - 切出车牌 - 判断单双层 - 识别字符 - 输出结果这一整条链路最终在终端打印出类似粤B12345这样的字符串。3.4 模型文件必须注意的加载细节模型文件这块有几点经验要分享。第一检测权重是.pt格式它就是PyTorch的checkpoint里面除了模型状态字典之外还保存了训练时的超参数、类别名称、anchor配置等元信息所以用YOLOv5的官方工具加载非常方便。第二识别权重是.pth格式它只存了模型参数你需要保证网络定义里的字符表顺序和训练时完全一致否则识别结果就是乱的。我见过最多的错误就是有人自己改了rec_chars.txt里的字符顺序但没重新训练模型结果模型还是按原来的顺序输出对不上。所以记住一句话改字符表不重训等于白改。4. 训练自己的数据集与模型优化4.1 数据采集与标注规范如果你需要适配特定的场景比如某个停车场出入口角度特别刁钻或者想提升夜间识别率就得准备自己的数据去微调。数据采集时我建议一个场景至少拍3000到5000张覆盖不同时间段、不同天气、不同距离、不同角度。标注工具我用的是LabelImg标注输出格式选YOLO格式也就是每张图片对应一个同名txt文件文件里每行是类别序号 中心点x坐标 中心点y坐标 框宽 框高。这套项目里检测类别就一个所以类别序号永远是0。标注的时候要特别注意框要贴着车牌边缘不要留太多背景也不要切掉字符。框的质量直接决定检测精度这一点我在实战中反复验证过。4.2 数据集目录组织与配置文件YOLOv5的约定是images和labels两个权重目录分别存放图片和标注文件train和val拆开。目录结构大概是这样的datasets/plate/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/然后在configs/plate.yaml里写上train: datasets/plate/images/train val: datasets/plate/images/val nc: 1 names: [plate]这里nc是类别数量因为车牌检测只有一个类别所以是1。names里给一个名字就行。如果你把检测任务自己扩展成车头车尾车牌多类别再在这里加名字。4.3 训练参数设置与迁移学习一切就绪后用预训练权重做迁移学习python train.py --img 640 --batch 16 --epochs 200 --data configs/plate.yaml --weights yolov5s.pt --device 0几个关键参数说一下。img是训练分辨率我默认用640如果你的场景里车牌普遍很小可以提到768或960但显存占用会涨。batch在16G显存上跑yolov5s最多16到32建议16。epochs如果你是微调100个epoch就够如果是从头训练至少300。weights这里可以指向YOLOv5官方在COCO上预训练好的yolov5s.pt它已经学会了通用的视觉特征能显著加速收敛比随机初始化好太多。训练过程中YOLOv5会自动生成训练曲线图重点关注mAP0.5这个指标。如果检测场景不复杂mAP0.5一般能到98%以上。到不了的话大概率是数据问题而不是模型问题。4.4 数据增强与双层车牌专项优化YOLOv5内置了很多数据增强策略其中Mosaic增强是把四张图拼成一张训练对小目标检测尤其友好。车牌检测场景里小目标特别多如果是远距离卡口抓拍车牌可能只占整张图几十个像素没有Mosaic增强模型很难学到这种特征。针对双层车牌我在plate_utils.py里加了一个专门的增强逻辑训练阶段会把双层车牌的样本以一定概率随同单层车牌进行混合缩放保证模型见过的双层车牌尺寸分布足够多样。另外对双层车牌样本我额外增加了水平翻转和轻微旋转变换因为大车挂的双层车牌往往带有一定倾斜角特别是行驶状态下拍摄时只做垂直矫正是不够的。4.5 模型导出从PyTorch到ONNX再到TensorRT训练好检测模型之后部署一般不会直接拿.pt跑尤其是生产环境。常规做法是先导出ONNX再转换成TensorRT的engine文件或者用OpenVINO跑CPU推理。导出ONNX可以用官方脚本python export.py --weights weights/plate_detect.pt --include onnx --img 640导出的时候要注意opset版本和动态维度设置。用TensorRT部署时建议把batch固定为1关闭动态shape这样能拿到最大的优化空间。识别模型也可以导出成ONNX整个流程跑下来速度提升非常明显。我的实测数据是同样的显卡上PyTorch直接推理单张图像全程大约12毫秒TensorRT优化后大约5毫秒差别还是很大的。5. 常见问题与排查技巧实录5.1 高频问题速查表我把这套项目在实际运行中最高频的问题整理成了一个表格都是真实踩过的坑照着排查能省不少时间。现象可能原因解决方案检测不到远处的小车牌输入分辨率太低anchor尺寸过大推理时提高img分辨率到960以上或对anchor做重聚类检测框比车牌大一圈标注框不贴边数据不干净清理标注数据重新标注蓝牌识别成黄牌数据不均衡蓝牌样本占比过高增加黄牌、绿牌样本数量或用类别平衡采样双层车牌识别成乱码识别端没有走双行切分逻辑检查plate_utils.py中双行判断阈值是否匹配实际数据绿牌前两位字母反复错新能源车牌字符间距与蓝牌不同单独对绿牌样本做字符间距归一化增强夜间图片完全识别不出训练数据缺少夜间样本采集夜间数据或在增强中增加随机亮度和对比度扰动推理速度只有几帧在CPU上跑大模型换yolov5n检测权重识别端也换成轻量配置输出字符串多了一个字CTC解码参数设置有误检查识别代码里beam search或greedy decode的参数5.2 检测框不准的排查顺序很多人一上来就调模型参数其实方向错了。检测框不准首先检查的是标注文件。我从数据里随机抽几张图把标注框画出来看经常发现标注框要么偏大要么偏小。标注质量决定了模型能力的上限模型再怎么调参也突破不了数据本身的天花板。标注修一遍效果立竿见影。如果标注没问题再看anchor。YOLOv5官方在COCO数据集上聚类得到的anchor是通用物体尺寸比如人要站直、车是长方体跟车牌这种扁平目标的尺寸差异很大。用官方anchor不用重新训练但如果你想榨干精度可以用工具在训练集上重新聚类anchor这一项通常能提升1到2个点的召回率。5.3 双层车牌识别乱码的详细处理双层车牌识别乱码90%的原因是没有正确触发双行切分逻辑。我的实现里判断依据是检测框的高宽比经验阈值是高度除以宽度大于0.35就认为是双层车牌。不同相机分辨率下这个阈值可能需要微调。切分的时候要注意上下两行不是简单从中间一刀切。大型货车的双层车牌经常上少下多比如上面是粤B下面12345挂中间分隔线不一定落在正中。我的解法是先对车牌图像做水平投影统计每一行的字符像素密度然后根据投影谷底找到两行的分界线。这样比固定切分稳健很多。切分完成之后把上下两行各自送进识别网络如果某一行太窄需要先做等比例放大统一缩放到训练时使用的输入尺寸。最后拼接字符串时注意挂字的位置挂车车牌通常在第二行的末尾。5.4 数据不平衡怎么解决车牌识别项目里数据不平衡几乎是必然的。蓝牌最多绿牌其次白牌、黑牌本来就少双层大车车牌更是稀缺样本。如果不做处理模型会对少数类别欠拟合识别率上不去。我用过比较有效的手段是复制粘贴增广把少数类别的车牌图像通过缩放、旋转、透视变换之后粘贴到真实道路背景图上生成大量合成训练样本。这种方法的收益比单纯重复采样高很多因为模型不仅能见到更多目标实例还能见到不同的环境上下文。另外训练时也可以用类别加权采样让batch里少数类别的出现概率提高。YOLOv5官方训练脚本里没有直接提供这个功能但我在自定义dataset里简单扩展了一下效果不错。5.5 多角度、透视畸变下的识别技巧车牌识别最怕的不是模糊而是透视畸变。车辆从侧面驶过相机斜着拍车牌在图像里是梯形甚至更扭曲的形状直接送进识别网络的时候字符都歪了。我的标准做法是检测出车牌后先做透视校正也就是常说的四点对齐。用车牌检测框投影到原图的四个角点再利用OpenCV的getPerspectiveTransform和warpPerspective把车牌区域矫正成规则的矩形。这一步对识别率提升非常明显。我在测试集上做过比对不做校正的识别率大概88%做了之后能到95%以上。如果你用的是这套项目的run_pipeline.py里面已经把透视校正作为默认选项开启了。但如果你打算串到自己的代码里一定不要漏掉这一步。6. 一些诚实的总结与扩展建议这套项目跑下来我个人最大的体会是车牌识别难的不是某个单一模型而是整套链路里每个环节的配合。检测要稳切图要准透视矫正不能省识别模型要贴字符集双行判断要灵活。任何一个环节掉链子最终的用户感知都是识别不对但你永远不知道是哪一环出了问题。所以做这类项目时我强烈建议你把每个模块独立测试一遍再串起来整体测试这是排查问题最省时间的方式。如果你想在这个基础上继续扩展我建议关注两个方向。一个方向是把检测模型替换成更轻量的版本比如用YOLOv5n微调配合TensorRT部署到Jetson NX或者RK3588这类嵌入式设备上实现一个纯边缘端的车牌识别盒子。另一个方向是增加一个车辆跟踪模块用ByteTrack或者DeepSORT把视频流里同一辆车的不同帧关联起来多帧投票决定最终的车牌字符这样能大幅降低单帧误识别对结果的影响。这两个方向都是我实测过有效果的遇到具体问题可以再单独聊。本文还有配套的精品资源点击获取
