☰
YOLOv5车牌识别实战:从环境搭建到训练部署全流程指南
2026/10/11 9:12:48 网站建设 项目流程

简介:基于YOLOv5的车牌识别项目,面向具备一定Python基础、希望掌握目标检测与字符识别完整实战流程的开发者。支持对指定图像完成车牌定位并输出文字结果,适用于停车场管理、交通监控、车辆出入登记等场景。压缩包共85个文件,包体约475.22MB,主要包含25个py源码(模型结构、训练与预测脚本)、7个yaml网络配置、5个xml标注文件、5个txt说明、3个pth/pt预训练权重、11张jpg测试图,以及依赖清单、中文字体、模型下载脚本等配套文件,目录按核心代码、权重、数据与工程配置分层组织,结构直观易于检索。项目集成LPRNet车牌识别模块,与YOLOv5/YOLOv4检测网络配合,可直接加载权重推理,也支持使用自带样张与权重进行二次训练,帮助理解从检测到字符识别的完整链路。已有2310人学习下载,特别适合AI竞赛、毕业设计或企业级车牌识别系统的工程实践。

1. 车牌识别用 YOLOv5:为什么这个组合最容易被复现

车牌识别这个需求,从停车场道闸到园区安防都有,看起来简单,真上手才知道坑都在细节上。用 Python 做 YOLOv5 车牌识别,是目前最容易被复现的一条路:YOLOv5 的训练和推理管线足够成熟,社区资料多,遇到问题搜得到;车牌这种目标形状规整、类别少,用默认的 COCO 预训练权重做迁移学习,几十张车牌图就能把 mAP 拉起来。这份资源的核心就是一条链路:用 YOLOv5 对指定图像做车牌检测,输出带框的可视化结果和结构化标签。适合正在做毕设、接小项目或者想快速落地车牌检测的开发者。接下来我会把环境配置、推理参数、数据集格式、训练调优和常见翻车点按实际操作的顺序过一遍。

2. 先看懂模型与数据集:网络结构、超参数与标注格式

2.1 YOLOv5 网络结构:从 CSPDarknet 到 Detect 头

YOLOv5 的主干网络是 CSPDarknet53,它把基础残差块改成了 C3 结构,也就是在残差连接两侧各加一个 1x1 卷积做通道压缩。这样做的直接收益是梯度回传路径更丰富,同时计算量比普通残差块低。颈部网络用的是 PANet,自顶向下和自底向上两条路径都有特征融合,小目标和大目标都能兼顾。检测头是三个不同尺度的输出,分别对应 80x80、40x40、20x20 的特征图,分别负责小、中、大目标。

车牌在图像里通常占的面积不大,尤其是远距离拍摄时,所以决定检测上限的往往是 80x80 那个分支的浅层特征。训练时如果发现小目标漏检,优先怀疑的不是网络结构,而是输入分辨率--img和训练数据里小目标的占比。

锚框这部分容易被忽略。YOLOv5 在训练前会自动基于你的数据集重新计算锚框尺寸,具体在train.py里调用check_anchors完成,不需要手动指定。但推理时权重文件里已经固化了锚框,所以换数据集训练后必须用新的权重文件去做推理,拿旧的yolov5s.pt直接替换数据集的,检测框尺寸会不贴合。

2.2 车牌数据集的标注与目录组织

做车牌检测有两个层级:检测车牌区域本身,以及识别车牌上的字符。资源标题说的是「识别车牌」,我通常理解为先框出车牌区域,如果要做字符级识别,需要在检测框基础上再叠加字符分类模型。绝大多数项目场景,比如道闸和安防,第一步先把车牌框准,准确率就能到九成。

数据集目录组织是固定的套路,YOLOv5 训练脚本默认按这个结构读取:

dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/

每个图像对应一个同名.txt标签文件,文件里每行代表一个目标,格式是五个数字:类别索引、归一化后的中心点 x、中心点 y、宽度 w、高度 h。比如一张 1920x1080 的图,某个车牌的像素坐标是左上角 (1200, 500)、宽 300、高 90,那对应的标签行就是:

0 0.703125 0.504630 0.156250 0.083333

这里的 0 是类别号,如果你是单类车牌检测,所有行都是 0;如果你把车牌字符分成省份汉字、字母、数字共 65 类,那类别号就从 0 到 64。算归一化坐标时用(x_center / width)和(y_center / height),宽度同理,注意一定是中心点坐标而不是左上角坐标,这是新手最容易标错的地方。

标注工具用 labelImg 或者 labelme 都行,labelImg 导出 YOLO 格式最省事。标注时框稍微贴近车牌边缘,留一点边距就行,框太大把车标或保险杠包进去会引入噪声。

2.3 超参数文件:想改训练效果先改这里

YOLOv5 的超参数集中在data/hyps/hyp.scratch-low.yaml这类文件里,训练时用--hyp指定。资源里如果用默认配置训练,大概率能出结果,但效果上限取决于这几个参数:

参数默认值作用车牌场景建议
lr00.01初始学习率数据量小时降到 0.005
mosaic1.0马赛克增强启用概率数据少时保持 1.0 提泛化
hsv_h/hsv_s/hsv_v0.015/0.7/0.4颜色增强幅度车牌颜色敏感,s 降到 0.4
fliplr0.5水平翻转概率汉字会翻转,降到 0.0
degrees0.0旋转增强角度可设 5.0,模拟视角偏差

这里有个细节:车牌是强颜色特征的目标,蓝底白字、绿底黑字、黄底黑字,颜色本身是判别信息。HSV 增强调太高,会把蓝色车牌增强成紫色,反而干扰特征学习。我一般把hsv_s从 0.7 降到 0.4,保留一点颜色抖动用来应对不同曝光,但不至于让色相漂移。

水平翻转对车牌识别是双刃剑。如果只做车牌区域检测,翻转没问题;如果做字符识别,翻转后 "京A12345" 变成 "54321A京",字符顺序语义全乱了。所以做字符级识别时fliplr直接设 0。

3. 环境搭建到首次推理:从 python 环境变量到 detect.py 跑通

3.1 Python 环境与依赖安装

先把 Python 环境弄干净。Windows 上装 Python 时记得勾选 "Add Python to PATH",不然后面pip命令全是"不是内部或外部命令"。装完后在命令行验证:

python --version pip --version

如果python能执行但pip报错,常见原因是 Scripts 目录没进环境变量,手动把C:\Users\你的用户名\AppData\Local\Programs\Python\Python311\Scripts加到 PATH 里就行。Linux 上则注意系统自带的 Python 版本,Ubuntu 20.04 默认是 Python 3.8,直接装依赖通常没问题,但如果是 Ubuntu 22.04 的 Python 3.10,个别旧版本 YOLOv5 会有兼容问题。

推荐用虚拟环境隔离项目,避免把系统 Python 搞乱:

python -m venv venv source venv/bin/activate # Windows 上执行 venv\Scripts\activate pip install -r requirements.txt

YOLOv5 的requirements.txt里核心依赖是 torch、torchvision、opencv-python、numpy、pandas 和 matplotlib。CPU 环境跑推理没问题,训练建议先确认有没有 NVIDIA GPU。用nvidia-smi看驱动,再装对应版本的 PyTorch。我踩过最大的坑就是 torch 和 CUDA 版本对不上,训练时直接报CUDA error: no kernel image is available,这个后面避坑章细说。

3.2 下载模型权重与首次推理

权重文件建议从官方仓库的 release 页面下载,资源里如果没有附带训练好的权重,先用yolov5s.pt做基线测试。下载后放在项目根目录,然后准备一张包含车牌的图片作为测试输入。

推理命令最简版是这样:

python detect.py --weights yolov5s.pt --source test.jpg --conf-thres 0.25

跑完后在runs/detect/exp目录下会生成标注了检测框的test.jpg和对应的test.txt标签文件。第一次跑通就算链路通了。

这个命令做了四件事:读取权重文件构建网络、加载test.jpg、前向推理得到候选框、过滤后把结果画到图上。--source支持单张图、目录、视频流和摄像头 ID,比如传--source 0就是用默认摄像头实时检测。

3.3 推理参数详解:conf-thres、iou-thres、device

--conf-thres是置信度阈值,默认 0.25。意思是一个检测框的概率低于 0.25 就丢弃。车牌检测场景建议从 0.5 起步调试。阈值设太低,背景区域会被误判成车牌,尤其是深色车身上的反光区域;设太高,模糊的远距离车牌就会被滤掉。这个参数没有标准答案,取决于你的图像来源,入口道闸近景可以拉到 0.6,监控杆远景 0.3 可能才不漏检。

--iou-thres是 NMS 的 IoU 阈值,默认 0.45。它决定两个重叠框是否合并。车牌目标不会密集堆叠,这个值保持默认就行,除非你在一张图里同时检测多块重叠的临时车牌。

--device选择推理设备,--device cpu指定 CPU,--device 0指定第一块 GPU。CPU 推理一张 640x640 的图大约 200 到 400 毫秒,GPU 能到 20 到 40 毫秒。部署到 RK3568 这类边缘板子上时,因为硬件指令集差异,一般要先做 ONNX 导出再量化成 int8,直接跑 .pt 文件性能很差。

推理时还有一个值得改的参数是--img,默认 640。你的测试图如果是 1920x1080,按 640 输入推理,等效于把原图缩到 640,远处小尺寸车牌可能丢细节。把--img 1280加上去,小目标召回会明显改善,但推理时间也会涨约两倍。

4. 训练自己的车牌模型:数据准备、标签修正与训练命令

4.1 数据集划分与 yaml 配置

用资源自带的数据集或者自己标的数据,第一步是划分训练集和验证集。划分不是随便拆,要避免同一辆车出现在两边。真实场景里同一辆车的车牌在训练集和验证集各出现一次,验证集的 mAP 会被虚高,因为模型记住了这辆车的特征而不是车牌特征。最简单的方式是按拍摄时段或按图片所在目录划分。

划分完数据集,写一个data.yaml:

train: dataset/images/train val: dataset/images/val nc: 1 names: ['license_plate']

nc是类别数,这里是单类;names列表里的名称要跟训练时的类别索引一一对应。如果你要做 65 类字符识别,names就是 31 个省份简称加 10 个数字加 24 个字母的完整列表,顺序一旦定下来不能再改,改了对不上标签就全乱。

标签文件本身也要抽查。我每次训练前必跑一段校验脚本,看标签坐标是否越界、是否有负值:

import os label_dir = 'dataset/labels/train' for f in os.listdir(label_dir): with open(os.path.join(label_dir, f)) as fp: for line in fp: parts = line.strip().split() cls, x_c, y_c, w, h = int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if w <= 0 or h <= 0: print(f'{f}: 非法宽高') if x_c < 0 or x_c > 1 or y_c < 0 or y_c > 1: print(f'{f}: 中心点越界') if x_c + w / 2 > 1 or x_c - w / 2 < 0: print(f'{f}: 边界溢出')

坐标系归一化后,中心点坐标和宽高都必须落在 0 到 1 区间。边界溢出表现在检测框会往图外延伸,训练时损失函数照常计算,但推理结果会在图像边缘出现半截框。

4.2 训练命令与超参数调整

训练命令最常用的是:

python train.py --data data.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640 --hyp data/hyps/hyp.scratch-low.yaml

--weights yolov5s.pt是迁移学习的起点。COCO 预训练权重里已经有通用的特征提取能力,车牌这种目标虽然 COCO 里没有,但底层的边缘、纹理、颜色特征是可迁移的,这比从头训练收敛快得多。如果你的数据集很小,比如只有几百张图,强烈建议保留--weights参数,从头训练几百张图基本过拟合。

--batch-size受显存限制。16G 显存跑 640 分辨率、yolov5s 模型,batch 16 没问题;8G 显存就降到 8。显存不够时会报CUDA out of memory,这时候不是调代码能解决的,老老实实降 batch 或者换yolov5n这种更小的模型。

训练日志里每轮结束会保存权重到runs/train/exp/weights/,last.pt是最后一轮的,best.pt是验证集 mAP 最高的。断点续训用--resume runs/train/exp,这个参数很关键,训练到一半断电或者崩了,不需要从头再来。

4.3 训练结果解读:从 result.png 判断欠拟合还是过拟合

训练结束后,runs/train/exp/目录下有一张results.png,里面有训练过程的损失曲线和验证集指标曲线。这是判断训练状态的核心依据,不要只看最终 mAP 数字。

看这张图有三个重点。第一看train/box_loss和val/box_loss两条曲线,如果训练损失持续下降但验证损失在第 40 轮左右拐头上升,典型过拟合,对策是增加数据增强强度、加 dropout 或者缩小模型。第二看metrics/mAP_0.5曲线是否还处于上升趋势,如果训练到 100 轮还在涨,说明没训满,加大--epochs继续训。第三看metrics/precision和metrics/recall的相对关系,precision 高 recall 低说明模型保守,框出来的基本都对但漏检多;反过来则是误检多。

还有一个黑匣子一样的问题:训练损失曲线震荡幅度大。这通常是 batch size 太小或者学习率偏高,把lr0从 0.01 降到 0.002 再训一轮,曲线会平滑很多。车牌检测的收敛速度比通用目标检测更快,类别少、目标尺度集中,一般 50 到 80 轮就能稳定。

5. 避坑:车牌识别里最容易翻车的五个问题

现象一:训练正常,但推理时蓝牌全检出来了,绿牌一个不漏地漏检。原因:数据集中蓝牌样本占比超过九成,模型把「蓝色区域」当成了最强特征,新能源绿牌的颜色特征没有被充分学习。解决:按颜色类别重新平衡数据集,蓝牌和绿牌比例控制在 3:1 以内,或者用数据增强把绿色车牌的样本多复制几份(注意是复制并做轻微仿射变换,不是简单重复)。我在一个停车场项目里就是这么翻车的,甲方给的图片九成是蓝牌,绿牌检出率只有 20%。

现象二:同一张图,--img 640检测不到车牌,--img 1280能检测到。原因:输入分辨率过低,车牌在缩放过程中丢失了细节特征。远距离监控画面里车牌可能只有 20x6 像素,缩到 640 分辨率下连 6 个像素都不到,特征图上的响应极弱。解决:推理时固定用--img 1280,但训练时也要用匹配的分辨率。训练用 640、推理用 1280 会引入尺度偏移,mAP 会有损失,最好训练时就设成--img 1280。

现象三:推理结果里一个车牌出了两个框。原因:--iou-thres阈值设置不当,NMS 没有把重叠框合并干净。常见做法是排查是不是同一个目标的两个高置信度框,如果是,把--iou-thres从 0.45 降到 0.3 试试。另外还有一种可能是数据集标注里同一样本出现了重复框,模型学会了同时输出两个框,这种只能在数据层面清理。

现象四:训练没几个 epoch 就报NaN loss。原因:学习率过大导致梯度爆炸,或者数据里有异常的标签值,比如归一化坐标出现 1e5 这样的野值。先检查标签文件,再降学习率。YOLOv5 默认带 warmup 机制,前三轮学习率是线性上升的,如果lr0设置过高,warmup 结束后直接爆炸。遇到 NaN 先看hyp文件里lr0,从 0.01 降到 0.001 能解决大部分情况。

现象五:RK3568 等边缘设备上部署后帧率只有个位数。原因:直接拿 PyTorch 的 .pt 权重在端侧跑,设备不支持 CUDA,算子也做了很多动态分支,推理效率极低。解决:先导出 ONNX,再量化为 int8。YOLOv5 官方仓库里export.py支持--include onnx,导出后再用 RKNN Toolkit 做转换。量化后 mAP 会有 1% 到 3% 的下降,但推理速度能提升一个数量级。另外需要注意的是,量化前用验证集做 int8 校准,校准数据集最好是你真实场景的图,拿 COCO 图校准车牌模型会掉点严重。

6. 进阶:批量识别与错误样本复盘

6.1 写一个批量识别脚本

单张图测试通过后,真实使用场景一定是批量处理。YOLOv5 的detect.py本身支持--source传目录,但如果你想在批量处理时做一些额外操作,比如把车牌区域裁剪出来存档,那就需要自己写脚本调用模型接口:

import cv2 import torch import os model = torch.hub.load('ultralytics/yolov5', 'custom', path='best.pt', force_reload=True) model.conf = 0.5 model.iou = 0.45 model.img_size = 1280 input_dir = 'test_images' output_dir = 'output_crops' os.makedirs(output_dir, exist_ok=True) for img_name in os.listdir(input_dir): img = cv2.imread(os.path.join(input_dir, img_name)) results = model(img) boxes = results.xyxy[0].cpu().numpy() for i, (x1, y1, x2, y2, conf, cls) in enumerate(boxes): crop = img[int(y1):int(y2), int(x1):int(x2)] cv2.imwrite(os.path.join(output_dir, f'{img_name}_plate_{i}.jpg'), crop)

torch.hub.load加载本地权重时,path参数直接指向你的best.pt;model.conf和model.iou对应于命令行里的--conf-thres和--iou-thres。results.xyxy[0]返回的是检测框的左上角右下角坐标、置信度和类别索引,用切片把它转成 NumPy 数组是为了方便直接索引坐标值。

6.2 用错误样本反推阈值调整

批量识别完不要急着收工,把漏检和误检的图单独拉出来看一遍。我的习惯是建一个bad_cases/目录,每次跑完批量识别,把所有conf低于 0.2 或者明显框错的图丢进去。看一段时间就能发现规律:如果漏检集中在远距离小目标,优先调--img;如果误检集中在车灯、保险杠等圆形区域,优先把conf阈值往上提;如果蓝牌绿牌识别率失衡,回到数据集层面做平衡。

从那以后我每次训练完都会强制走一遍这套流程:先单图验证、再批量跑测试集、最后人工复盘错误样本。阈值参数不能靠猜,每一版模型的置信度分布都不一样,换一次数据集就要重新看一遍分布曲线再定阈值。希望帮到你。

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

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

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

立即咨询