简介:这是一套基于yolov5+lpr3+deepsort+pyqt5的交通识别检测系统源码,面向计算机视觉、人工智能、自动化等相关专业的学生、教师及从业者,适用于课程设计、大作业或毕业设计等场景。项目实现了带GUI的车辆行人检测与计数、deepsort目标跟踪、车牌识别、多种车型识别及车速识别,功能覆盖交通场景多目标感知主要环节。源码为作者的毕业设计,答辩评审分95分,代码经过调试测试可运行,具备较高学习与二次开发价值。资源共2000个文件,包含1127个txt(标签或数据说明)、764个py(核心源码与GUI逻辑)、63个yaml(模型与训练配置)、21个xml及14个sh辅助脚本等,另含md与json文档,压缩包约605.6MB。目前已有1117人浏览学习,适合需要完整可运行项目作为参考,或希望在此基础上二次开发的读者深入借鉴。
1. 交通识别检测系统全栈拆解:先跑通,再谈改装
这套基于 YOLOv5 的交通识别检测系统,把检测、跟踪、车牌识别、车速估算全部串成了一条完整的流水线,还套了一层 PyQt5 图形界面。对做毕设、课程设计或者想快速搭建一个城市交通监控演示原型的人来说,它最大的价值不是某一个算法有多先进,而是把多模型协同工作的骨架搭好了。你拿到的不只是一堆训练好的权重,而是一个能直接启动、能出检测框和车牌号的桌面程序。
我拆这套源码最直观的感受是它把 deepsort 的目标跟踪和 lpr3 的车牌识别做了关联绑定——同一个车辆 ID 在视频帧里持续跟踪,帧间位移除以时间就是车速。整体技术栈不算深,但工程细节不少:模型权重转换、PyQt5 界面线程卡顿、deepsort 的级联匹配阈值,任何一个环节配置不对,整套系统跑起来就像幻灯片。适合有一点点 YOLO 基础、但没做过完整前端界面的开发者入手。只懂图像处理还不够,得能把模型推理、目标关联、UI 刷新放在同一个进程里协调,这套资源正好帮你跳过这些整合的黑匣子。
2. 架构剖析:YOLOv5 检测、DeepSORT 跟踪与 LPR3 车牌识别的三层协同
2.1 检测层:YOLOv5 的模块划分与输出结构
这套系统的检测层用的是 YOLOv5 系列的预训练权重,默认覆盖车辆、行人等常见交通目标类别。你打开源码根目录下的models/文件夹,能看到yolov5s.pt、yolov5m.pt这类权重文件,官方针对 COCO 数据集训练的权重直接拿来做候选框提取。它的核心输出是每个目标的类别、置信度和边界框坐标,格式是[x_center, y_center, width, height],注意这里用的是归一化坐标,不是像素坐标。这个细节在后续与 deepsort 对接、把检测框映射到视频画面上时尤其关键。
# 示例:解析 YOLOv5 检测输出,生成统一的检测结果结构 import torch import numpy as np # 模型输出 shape 通常为 [num_detections, 6]:x1, y1, x2, y2, confidence, class_id def parse_yolo_output(pred, orig_img_shape, conf_threshold=0.4): """ pred: YOLOv5 推理结果,非归一化坐标(像素) orig_img_shape: 原始图像尺寸 (height, width) """ boxes = pred[:, :4] # 前4列是边界框坐标 scores = pred[:, 4] # 第5列是置信度 classes = pred[:, 5].astype(int) # 第6列是类别ID # 过滤低置信度目标 mask = scores > conf_threshold boxes = boxes[mask] scores = scores[mask] classes = classes[mask] # YOLOv5 输出是 xyxy 格式,转成 xywh 供 deepsort 使用 xywh_boxes = np.zeros_like(boxes) xywh_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2 # cx xywh_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2 # cy xywh_boxes[:, 2] = boxes[:, 2] - boxes[:, 0] # w xywh_boxes[:, 3] = boxes[:, 3] - boxes[:, 1] # h return xywh_boxes, scores, classes这里conf_threshold是个值得反复调的参数。设低了,大量低置信度框会涌入 deepsort 的匹配流程,轨迹数量和计算量线性上升,视频会明显掉帧;设高了,远距离的小目标容易漏检。我用下来的经验是 0.35 到 0.45 之间比较平衡,雨天或逆光场景适当往 0.5 靠。xywh格式转换是硬性要求,deepsort 的卡尔曼滤波器默认按中心点加宽高做状态估计,直接喂xyxy坐标会导致滤波器预测异常,跟踪框会出现高频抖动。
2.2 跟踪层:DeepSORT 的卡尔曼滤波与级联匹配逻辑
跟踪层做的事是给检测框分配稳定的 ID。单帧检测结果不携带时序信息,上一帧的车辆和下一帧中同一辆车在画面里的位置发生了变化,DeepSORT 通过两个机制解决:卡尔曼滤波做运动状态预测,级联匹配做外观特征与运动信息的联合关联。卡尔曼滤波器维护每个轨迹的状态向量,包含位置、速度信息,配合观测值做最优估计。
# 示例:deepsort 核心匹配逻辑 —— 级联匹配与 IoU 匹配 from deep_sort_realtime.deepsort_tracker import DeepSort # 参数说明: # max_age:轨迹丢失多少帧后删除,我一般设 30,车被遮挡后还能重新接上 # n_init:连续多少帧成功匹配才算确认轨迹,默认 3 足够 # nn_budget:外观特征队列大小,控制内存消耗,视频长时间跑要注意这个值 tracker = DeepSort( max_age=30, nn_budget=100, n_init=3, embedder="mobilenet" # 外观特征提取网络,轻量且适合实时场景 )这里的nn_budget很容易被忽略。它控制每个轨迹保存的历史外观特征数量,如果跑长视频,特征队列会持续累积,占用大量内存。但设置太小时,外观匹配的判别能力下滑,车辆换道后容易丢失 ID。跑城市路口监控,我一般保持在 100 到 150 之间,超过这个量就开始出现 ID Switch 问题。embedder参数也值得留意,源码里默认用的可能是clip或mobilenet,如果你机器没有 GPU,后者能明显省推理时间。级联匹配的代码逻辑里还有个隐藏参数match_threshold,控制外观特征余弦距离的阈值,我在自定义场景里调到 0.3 左右,能显著减少误关联。
跟踪层还有个容易被忽略的点:检测框的置信度会直接影响跟踪效果。如果每一帧都做完整推理,把全部候选框丢给跟踪器,低置信度框干扰会导致轨迹分裂;正确做法是保留一个检测器高置信度分支和一个低置信度分支,高置信度框优先匹配,低置信度框做备选。这套源码在tracker.update()的调用前,其实只传了过滤后的检测结果,这点自己扩展时千万别改错位置。
2.3 车牌层:LPR3 识别流程与关联策略
LPR3 是这套系统里相对独立的模块,输入的是车头或车尾的局部图像,输出的是车牌字符串。它的流程分成两步:先用检测模型定位车牌位置,再把裁剪后的图像送入识别模型。这一步对图像的清晰度要求较高,远距离抓拍的车牌在图像里只占几十个像素,识别失败率会直线上升。源码里一般会留一个plate_detector和plate_recognizer的接口,前者的权重可能用的还是轻量级检测模型。
# 示例:lpr3 识别调用链 —— 车脸区域裁剪 + 车牌识别 import cv2 def recognize_plate(frame, car_box, plate_model): """ car_box: 检测层输出的车辆边界框,格式 xyxy """ x1, y1, x2, y2 = [int(v) for v in car_box] # 适当外扩裁剪区域,避免车牌被截断。外扩比例一般取 0.1 h, w = frame.shape[:2] margin_x = int((x2 - x1) * 0.1) margin_y = int((y2 - y1) * 0.1) x1 = max(0, x1 - margin_x) y1 = max(0, y1 - margin_y) x2 = min(w, x2 + margin_x) y2 = min(h, y2 + margin_y) car_roi = frame[y1:y2, x1:x2] # 转灰度 + 直方图均衡化,能明显提升弱光环境识别率 gray = cv2.cvtColor(car_roi, cv2.COLOR_BGR2GRAY) gray = cv2.equalizeHist(gray) # 推理结果:一般是 (plate_text, confidence) 的元组 plate_text, conf = plate_model.predict(gray) return plate_text, conf灰度转换和直方图均衡化这两个预处理步骤,在 LPR3 识别链路里作用比预想大得多。直接拿彩色图识别,面对金属车牌的强反光,识别置信度普遍掉 10 到 15 个百分点。equalizeHist对光照不均的补偿效果立竿见影,特别是傍晚或阴雨天气,车牌字符对比度不足的问题能明显缓解。注意这里margin外扩不能设置太大,否则会把旁边车道的车脸也包进来,车牌定位模块反而会困惑。
2.4 三层数据的串接:目标 ID、车牌绑定与车速计算
三层模型各自跑完后,真正的工程挑战在多路数据的对齐。YOLOv5 给出目标框和类别,DeepSORT 给每个目标框分配了一个稳定 ID,LPR3 给出车牌文本,但这三组数据之间没有天然的关联索引。源码的解决思路是以 DeepSORT 的 track ID 为主键,维护一个全局字典——每当新的 track ID 出现,就在接下来的若干帧里尝试识别该 ID 对应车脸区域的车牌,识别结果以投票或最高置信度方式存入字典。车速计算则在跟踪链路上完成:利用卡尔曼滤波输出或原始检测框中心点的位移差,除以每帧的时间间隔,再用像素与物理距离的标定系数换算成真实速度。
# 示例:基于跟踪轨迹的车速估算逻辑 from collections import deque class VehicleSpeedEstimator: def __init__(self, fps, pixel_per_meter=5.0): self.fps = fps self.pixel_per_meter = pixel_per_meter # 标定系数:1米对应多少像素 self.track_history = {} # track_id -> deque([x_center, y_center]) def update(self, track_id, bbox): if track_id not in self.track_history: self.track_history[track_id] = deque(maxlen=10) cx = (bbox[0] + bbox[2]) / 2 cy = (bbox[1] + bbox[3]) / 2 self.track_history[track_id].append((cx, cy)) def get_speed(self, track_id, time_interval=0.5): """time_interval: 速度计算的滑动窗口时长,单位秒""" history = self.track_history[track_id] if len(history) < 2: return 0.0 # 取窗口起止位置做位移计算,避免帧间抖动影响 start = history[0] end = history[-1] dx = end[0] - start[0] dy = end[1] - start[1] pixel_dist = (dx**2 + dy**2) ** 0.5 real_dist = pixel_dist / self.pixel_per_meter elapsed = len(history) / self.fps speed = real_dist / elapsed * 3.6 # m/s -> km/h return speed这里pixel_per_meter是个非常敏感的参数。固定摄像头安装高度和角度下,画面近处的像素密度和远处完全不同,单一标定系数只能做粗略估算。源码默认给了一个参考值,实际部署必须现场标定:在画面里找一个已知长度的参照物,比如车道虚线,测一下它在画面中占据的像素数,算出这个系数。滑动窗口长度time_interval也影响速度稳定性,窗口太短,检测框抖动会直接反映在速度读数上;窗口太长,车辆急加速或减速的变化被抹平。0.5 秒到 1 秒之间是需要实测折中的范围。
3. 环境搭建与依赖安装:从零到能跑完的完整流程
3.1 虚拟环境创建与关键依赖的版本匹配
这套源码对依赖版本非常敏感,我最开始图省事直接在全局环境里跑,Common 报出各种奇奇怪怪的 AttributeError。后来老老实实建虚拟环境,从 requirements.txt 逐项安装,一次通过。务必使用 conda 或 venv 隔离环境,Python 版本建议 3.8 或 3.9,YOLOv5 的老版本代码在 3.10 以上容易遇到torch算子兼容问题。
# 创建独立虚拟环境,指定 Python 版本 conda create -n traffic_det python=3.9 -y conda activate traffic_det # 安装 PyTorch 系列,建议先装 CPU 版跑通流程,再换 CUDA 版 pip install torch==1.12.1 torchvision==0.13.1 --index-url https://download.pytorch.org/whl/cpu # 安装核心依赖 pip install -r requirements.txtrequirements.txt一般会锁定 pytorch、torchvision、opencv-python、numpy 的具体版本。如果直接pip install不指定版本,最新版 numpy(比如 2.x)会和旧版 YOLOv5 产生冲突,最常见的报错是np.int属性不存在——新版 numpy 把这个别名删了。我在复现时发现,numpy==1.x是最稳的选择。torch 版本上不要追求最新,1.12 和 2.x 在多数机器上都能正常跑,但 1.x 的 API 更贴合老代码的写法,不用额外适配。
3.2 PyQt5 界面框架安装与常见故障排查
许多人在 PyQt5 安装这一环节直接翻车。如果你用 Python 3.9 及以上版本,pip install pyqt5通常会顺利,但如果你不小心装成了PyQt5-sip版本不匹配,启动时会报AttributeError: module 'PyQt5.sip' has no attribute 'setapi'。这个报错看起来诡异,本质是 sip 与 PyQt5 主包版本不对齐。解决办法很简单,强制重新安装匹配版本。
# 安装 PyQt5 及其基础组件 pip install pyqt5==5.15.9 pip install pyqt5-tools==5.15.4.3 # 含 designer 工具,修改界面布局用的 # 验证是否安装成功 python -c "import PyQt5; from PyQt5.QtWidgets import QApplication; print(PyQt5.QT_VERSION_STR)"如果你的 Linux 环境缺少图形接口库,import cv2和import PyQt5都可能报错,原因是libGL.so缺失。这是很多人忽略的系统依赖,与 Python 包无关。换个说法,PyQt5 的安装坑不在 PyQt5 本身,而在围绕它的系统库和 sip 依赖。安装完成后,先用简单的 QMainWindow 跑一个空窗口,确认环境无误再启动整套系统。我在 Ubuntu 上遇到过QXcbConnection: Could not connect to display的报错,这个常见于无图形界面的服务器环境,但也可能出现在DISPLAY环境变量未正确导出的 Docker 容器里。
# 处理 Linux 下 libGL 缺失问题 sudo apt update sudo apt install -y libgl1-mesa-glx libglib2.0-03.3 源码目录结构与主入口层解析
刚解压资源包时,别急着运行main.py,先把目录结构看一遍。一个规范的 YOLOv5 交通检测项目通常包含以下模块:
| 目录/文件 | 作用 |
|---|---|
models/ | 检测、车牌识别、跟踪器的模型定义与权重文件 |
tracker/ | deepsort 相关封装代码 |
ui/ | PyQt5 界面文件(.ui 或 .py) |
utils/ | 坐标转换、绘制工具、速度计算等辅助函数 |
weights/ | 训练好的各模型权重 |
main.py | 程序入口,串联 GUI 与算法链路 |
config.yaml | 全局配置文件,摄像头源、模型路径、参数阈值 |
主程序入口main.py的启动逻辑一般是:初始化 QApplication -> 加载配置 -> 创建主窗口 -> 启动视频流线程。注意主界面卡顿问题几乎都出在把推理放到了 GUI 线程——视频画面刷新频率 25 FPS 以上,推理一帧如果耗时 100ms,界面刷新就只剩 10 FPS,肉眼可见的掉帧。这个源码通常已经用QThread把视频采集和推理放到了子线程,主线程只做 UI 更新。如果你发现自己的改法里推理和渲染在同一线程,尽早改成双线程架构。
# 启动入口示例(源码不同文件名可能不同) python main.py --config config.yamlconfig.yaml是这个项目的总控开关。摄像头源可以是本地视频文件、网络视频流 RTSP、或 USB 摄像头,源码多数支持0表示默认摄像头。模型权重路径也用相对路径,注意如果把项目移到其他目录,路径解析失败会直接崩溃。
3.4 CUDA 与 ONNX 推理模式的选择
这套系统支持 PyTorch 原生推理和 ONNX 精简推理两种模式。如果部署机器没有 NVIDIA GPU,ONNX 模式配合 CPU 运行是唯一现实的选择。源码里一般有--export参数把 PyTorch 权重导出为 ONNX。我在 low 配置的机器上试过,ONNX 模式推理速度比原生 PyTorch CPU 模式快 30% 到 50%,主要收益来自算子的融合优化。
# 导出ONNX格式权重(以yolov5s为例) python export.py --weights weights/yolov5s.pt --include onnx --simplify--simplify参数值得每次都加,它调用 onnx-simplifier 对计算图做常量折叠,减小模型体积的同时也提升推理速度。如果导出时报opset版本错误,说明你安装的 onnxruntime 不支持过高的算子集合。两个都升到最新,或者显式指定--opset 12,这个版本在 CPU 推理时兼容性最好。ONNX 模式的加载代码和 PyTorch 模式完全不同,源码里一般通过engine参数区分,改动点集中在推理函数的前向调用部分。
4. 模型训练与核心参数调整:让系统适配自己的场景
4.1 数据集准备:YOLOv5 格式的标注规范
如果你打算用这套系统识别特定区域的特殊车辆,比如渣土车或工程机械,直接用源码自带的预训练权重肯定不够用。你得准备自己的数据集并重新训练检测模型。YOLOv5 的数据集标注格式是每个图片对应一个相同文件名的.txt文件,每一行代表一个目标框,格式为class_id cx cy width height,坐标全部是归一化的浮点数。用 LabelImg 或 Labelme 标注时要留意导出格式,LabelImg 默认导出的是 PASCAL VOC 的 XML 格式,需要转换脚本才能和 YOLOv5 兼容。
# 用官方 weight 转成 yolo 训练格式(以VOC XML为例) python voc2yolo.py --xml_dir ./Annotations --txt_dir ./labels --img_dir ./images标注工具的选择影响效率。我习惯用 LabelImg 配合auto_save功能,标注完一张图片立即写入 XML,不会因为忘记保存而丢失。标注类别要从 0 开始连续编号,YOLOv5 在读取类别名时会以data.yaml文件里的列表顺序作为索引依据。这里有个容易踩的坑:data.yaml里的类别顺序和训练时数据集的类别顺序必须严格一致,否则模型预测出的类别 ID 对应错误,等于白训。每次标注完做数据校验,检查标注文件的类别 ID 是否超过nc数目。
# data.yaml 示例 —— 训练配置 train: ./datasets/images/train val: ./datasets/images/val nc: 2 names: ['vehicle', 'pedestrian']4.2 训练命令与超参数逻辑:从源码到自定义训练
YOLOv5 的训练入口是train.py,常用参数包括--img(输入图像尺寸)、--batch(批大小)、--epochs(训练轮数)、--data(数据集配置文件路径)。训练前有几项参数值得结合自己的硬件条件做调整:--batch太大容易显存溢出,太小则收敛慢,显存 8GB 的卡一般 16 到 32 比较稳;--img默认 640,输入尺寸增大能提升小目标检测能力,但训练耗时和显存消耗也同步上升。
# 训练自定义数据集 python train.py \ --data datasets/data.yaml \ --weights weights/yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --patience 20 \ --cache--patience是早停机制的等待轮数,验证集指标持续不提升时就停止训练,省时间的好帮手。--cache会将训练图像提前缓存到内存,极大加快数据加载速度,第一次使用时这参数能把训练效率提升约 60%。如果训练中途显存溢出,除了减小 batch,还可以开启--noval跳过验证阶段,同时在训练结束时再统一验证。数据增强方面,YOLOv5 内置了马赛克增强(mosaic)、随机仿射变换和色彩空间扰动,你在源码的datasets.py里能看到具体的增强参数。马赛克增强在训练初期能显著提升模型对多尺寸目标的适应性,但训练后期建议关闭,让模型在更接近真实分布的图像上微调。
4.3 车速识别的标定参数与精度权衡
车速识别模块不是机器学习模型,而是一个基于跟踪轨迹的几何计算。它的精度上限完全由标定质量决定。视频画面里的透视效应导致远处 1 像素对应的物理距离更大,近处更小。如果只用一个pixel_per_meter,近处车辆的速度会被高估、远处则被低估。较严谨的做法是使用标定曲线或分段常量:在画面不同位置放置参照物,测量多组像素与距离的对应关系,拟合一条二次曲线。源码的能力边界在于它只实现了单标定系数,工程上我更推荐你至少做分区域标定——把画面按纵向高度划分为近、中、远三段,每段设置独立的pixel_per_meter。
# 示例:分段标定系数在车速计算中的使用 class SegmentedCalibration: def __init__(self): # 按图像y轴坐标分段,每段对应一个像素/米系数 self.segments = [ (0, 200, 8.5), # 远端区域:1米 = 8.5像素 (200, 400, 5.2), # 中段区域:1米 = 5.2像素 (400, 720, 3.1) # 近端区域:1米 = 3.1像素 ] def get_pixel_per_meter(self, y_center): for y_min, y_max, factor in self.segments: if y_min <= y_center < y_max: return factor return 5.0调整车速模块时,滤波平滑是另一个值得关注的细节。单帧位移计算的速度波动很大,常见做法是引入移动平均或一阶低通滤波。但平滑窗口过大会带来滞后效应,车辆急刹车时车速曲线回落缓慢。源码如果用的是简单平均窗口,我建议你能改成加权平均——近期数据权重更高,既有平滑效果又能保持一定响应速度。
# 示例:基于指数加权移动平均的速度平滑 import numpy as np alpha = 0.3 # 平滑系数,越大越贴近原始数据,越小越平滑 def smooth_speed(new_speed, prev_speed): return alpha * new_speed + (1 - alpha) * prev_speedalpha系数需要依据你的视频帧率微调。25 FPS 视频流下 0.2 到 0.4 区间比较合理;帧率更高时适当调小alpha,避免平滑过于迟钝。速度显示之后还要注意数据单位——源码输出的是 m/s 还是 km/h 需要看清楚,UI 界面和日志如果混用单位,读数会直接乱了套。
5. 实际运行中的避坑指南:我踩过的六个问题
这套系统跑通不难,但跑稳不容易。以下问题我全部实际遇到,按出现频率排序。
5.1 界面卡死与视频不同步
现象:PyQt5 界面启动后,拖动窗口时画面冻结,视频播放变成 PPT 效果,CPU 占用率 100%。 原因:视频采集和模型推理全部放在 GUI 主线程执行。Qt 的事件循环被推理阻塞,无法处理用户交互。YOLOv5 单帧推理在 CPU 上约需 100 到 200ms,主线程一帧推理期间整个界面不可响应。 解决:将视频流读取和算法推理放入QThread子线程,主线程通过信号槽机制接收推理结果,绘制完画面后再更新 QLabel。这个改动涉及源码的线程模型重构,工作量约一小时,但效果立竿见影。具体改动位置是main.py中主窗口的start_video()方法——你需要新建一个继承QThread的 QVideoThread,在run()方法里执行cv2.VideoCapture的读取和推理,并通过自定义信号把帧传回 GUI。
# 示例:PyQt5 双线程处理视频流 from PyQt5.QtCore import QThread, pyqtSignal import cv2 class VideoThread(QThread): frame_signal = pyqtSignal(object) # 发送处理后的帧 def __init__(self, video_source=0): super().__init__() self.cap = cv2.VideoCapture(video_source) self.running = True def run(self): while self.running: ret, frame = self.cap.read() if not ret: break # 此处调用检测 + 跟踪 + 车牌识别函数 self.frame_signal.emit(frame) cv2.waitKey(1) def stop(self): self.running = False self.cap.release()5.2 DeepSORT 跟踪 ID 频繁跳变
现象:车辆在画面里行驶,ID 从 12 突然跳到 37,或者两辆车交会时互相交换 ID。 原因:外观特征提取网络在检测框内出现遮挡时特征质量下降,级联匹配阈值设置过松,导致同一目标的轨迹被打断、新轨迹误建。 解决:调整nn_budget与max_age的组合。max_age增大可以让短暂遮挡的轨迹存活更久,但如果目标彻底离开画面,轨迹延迟删除也会产生幽灵 ID。我在交叉测试后发现max_age=30, nn_budget=150的组合最稳。另外,检测框抖动也会诱发 ID 切换——每个轨迹的外观特征队列不断积累质量参差不齐的历史特征,拉低匹配精度。配合检测层面的平滑也能间接改善跟踪稳定性。
5.3 车牌识别对光源条件极度敏感
现象:白天车牌识别率高,黄昏和夜间识别率直接腰斩,中文车牌文字出现错字。 原因:LPR3 在训练时主要用的是白天的车牌图像,夜间车牌过曝或欠曝、字符边缘对比度下降,识别模型的特征提取能力失效。 解决:在进入识别模型前增加图像预处理增强,除了equalizeHist之外,试试CLAHE(限制对比度自适应直方图均衡),它对局部光照不均匀的处理效果比全局直方图均衡更好。夜间场景配合伽马校正调整整体亮度更有效。如果有能力进一步优化,可以单独收集夜间车牌数据集对 LPR3 做 finetune,所有整车的夜间图像都挂上标签微调。这个系统在这方面提升空间不小,也是后续迭代的重点。
# 如需重训 lpr3,数据目录应形如: # datasets/lpr_dataset/ # train/ # images/ (车牌局部裁剪图) # labels/ (文本标签) # val/5.4 车速读数剧烈跳动
现象:静止的车辆速度显示十几个数字不停跳动,行驶中的车辆速度一会显示 60 一会显示 120。 原因:检测框的像素抖动被速度公式放大。一两个像素的检测偏移,在pixel_per_meter比例下会产生相当大的速度误差。 解决:对检测框中心点增加平滑,比如用卡尔曼平滑或移动平均。源码实际输出框时应该已经有跟踪预测的平滑框,但如果你用的是原始检测框,一定要施加时间维度滤波。另外引入速度置信度——只有当轨迹长度超过一定帧数才显示速度,初始帧的速度直接置零。
5.5 主程序启动即崩溃,报错提示模块不存在
现象:python main.py启动瞬间报ModuleNotFoundError: No module named 'utils.plots'或类似错误。 原因:源码依赖相对路径导入,但当前工作目录不在项目根目录。很多项目代码里使用了from utils.plots import ...形式的导入,需要把项目根目录加入sys.path。 解决:启动时先cd到项目根目录,并设置PYTHONPATH。或者用sys.path.append(os.path.dirname(__file__))在入口文件顶部添加路径。
# main.py 或入口文件的顶部加这段 import sys, os current_path = os.path.dirname(os.path.realpath(__file__)) sys.path.append(current_path)5.6 视频文件可读但网络摄像头 RTSP 拉流失败
现象:本地视频能正常检测,换成 RTSP 流地址后画面黑屏或直接异常退出。 原因:OpenCV 的VideoCapture内置对 RTSP 的支持依赖 FFmpeg 后端,版本不匹配导致握手失败。或者摄像头地址本身需要身份认证。 解决:检查 OpenCV 是否带有 FFmpeg 支持——cv2.getBuildInformation()输出里查找FFMPEG项,确认结果是YES。如果被标记为NO,需要重装 opencv-python。RTSP 地址后添加?tcp参数也能提升稳定性,默认 TCP 传输比较可靠。
6. 界面集成与工程化的三个进阶技巧
这套源码最后落地的效果,很大程度上取决于 GUI 界面层的工程处理,而不只是算法性能。三个进阶技巧直接决定了系统的可用性。
第一个技巧是显示帧率的准确控制。视频流源和推理耗时都是波动的,如果每一帧都更新界面,画面时序是乱的。我习惯用 QTimer 将 UI 刷新频率锁到固定值:主界面显示使用 30 FPS 定时器,定时读取最新的推理结果并刷新画面,视频采集线程无限制抓帧,但只保留最新帧。这样界面刷新节奏稳定,CPU 占用也得到控制。具体改法是在MainWindow中设置timer = QTimer(self); timer.timeout.connect(self.update_frame); timer.start(33)。
第二个技巧是区域屏蔽和车道分区的实现。交通监控往往只需要关注特定区域——比如仅检测车道内的目标,忽略人行道上的行人。源码里如果预留了 DrawROI 函数,直接在其上画多边形坐标即可;如果没有,你自己写一个cv2.fillPoly蒙版配合bitwise_and叠加到检测区域。更大的价值在于可以在此处配合分段速度标定,让每个车道区域独立计算车速,避免跨车道误关联带来的速度跳变。
第三个技巧是日志与指标的可视化。检测系统长时间无人值守运行时,不能只靠肉眼看画面。把每帧的检测目标数量、平均推理耗时、跟踪 ID 总数、车牌识别置信度写入 CSV 或日志,再用 UI 侧边栏折线图展示。PyQt5 里用pyqtgraph比 matplotlib 更轻量,刷新速度更快。我一般把这类统计做成可折叠的侧栏面板,不干扰主画面显示。
关于模型热切换,还有个不算技巧但容易踩坑的习惯:每次替换模型权重前,先在配置里改路径并重启主程序,确认权重加载成功后,再继续跑场景。如果在程序运行中直接替换权重文件,Python 已经加载到内存的对象不会自动更新,你可能始终感觉改动不生效。从那以后,我每次调整模型或参数都会强制走一遍完整的重启流程,确认启动日志里展示的权重路径与配置文件一致再开始测试。希望帮到你。
本文还有配套的精品资源,点击获取