资讯动态

基于YOLO与DeepSORT的实时违停检测系统设计与实现

发布时间:2026/8/31 12:50:20 来源:尧图企业网站定制
简介本资源是一套面向本科毕业设计与课程设计的计算机视觉实战项目聚焦城市智能监管场景中的非法停车识别问题。系统融合YOLO实时目标检测与Deep SORT多目标跟踪算法实现对视频流中车辆的持续ID追踪、禁停区域入侵判定及超时违停自动告警适用于校园、商场、政务区等封闭/半封闭场景的自动化巡检需求。压缩包共7个文件3个核心Python脚本负责检测、跟踪与逻辑判定1个INI配置文件支持参数调优1个MP4演示视频直观展示运行效果1份Markdown说明文档含部署步骤与原理简述1张系统架构PNG图整体仅1.79MB轻量易上手。已有89人学习下载提供完整可运行源码、结构清晰的模块划分、禁停判定阈值可配置机制及违规事件的JSON日志JPEG截图双存档功能是理解目标检测与多目标跟踪工程落地的优质入门范例。1. 项目整体设计与技术选型思路1.1 为什么是 YOLO DeepSORT 这个组合先聊一个很多刚接触这个方向的人都会问的问题做实时违停检测为什么一定要用目标检测加多目标跟踪的组合而不是直接用训练好的检测模型一帧一帧地判原因其实挺直接的。如果你只靠单帧检测来做违停判断模型看到一辆车停在禁停区域就会立刻把它标记为违停。但现实中的画面是动态的一辆车正常减速、靠边、礼让行人甚至只是堵车停了一下在单帧画面里看起来都是“停着的”。单帧检测根本分不清这辆车是短暂停留还是长时间违停。引入 DeepSORT 做跟踪之后系统能够给每一辆车分配一个唯一的 ID然后在连续的视频帧里追踪这个 ID 的运动轨迹和停留时长只有停留时间超过预设阈值的车辆才会被判定为违停。这一下就把问题从“空间上的判断”升级成了“空间加时间上的联合判断”误报率能降一个量级。再说为什么选 YOLO 而不是其他检测器。YOLO 系列在工业项目里的统治力核心就是速度和精度的平衡点太好。以 YOLOv5s 为例在 RTX 3060 级别的显卡上做 1080P 推理能跑到 60 FPS 以上而 mAP 依然能维持在 70% 以上。DeepSORT 本身也要占一部分算力两部分叠加之后整套系统跑在消费级显卡上仍然能保持实时。如果换成两阶段检测器比如 Faster R-CNN精度虽然更高但速度撑不起实时场景违停检测又是典型的需要 7x24 小时盯着视频流的应用帧率一旦掉到 10 FPS 以下跟踪算法的表现也会跟着劣化——ID 跳变、轨迹断裂这些问题会集中爆发。1.2 违停检测的核心难点拆解这个项目看起来简单实际上手之后会发现水很深。结合我做过的实际案例违停检测的核心难点主要体现在三个方面第一是场景复杂度。真实的道路监控画面不像公开数据集那样干净车辆之间互相遮挡、树影晃动、光照剧烈变化尤其是傍晚和夜间、雨天反光都会导致检测框抖动甚至漏检。检测框一旦不稳定DeepSORT 的级联匹配就会受影响直观表现就是同一个车 ID 变了或者轨迹中间断掉。第二是违停区域的标定。大多数毕业生第一次做这个项目会把违停区域想成一个规则的矩形框。但实际场景里禁停区域往往是多边形甚至是不规则形状——比如公交站台周边、消防通道入口、弯道内侧。如果你的代码只支持矩形 ROI到了现场调试的时候会非常难受。第三是判定逻辑的粒度。什么叫“违停”是车轮压线算还是整个车身进入区域算是一旦进入就开始计时还是等车辆完全静止后才开始计时这些规则看起来是小事但直接决定了误报率和漏报率。我见过不少项目模型训练得挺好最后死在判定逻辑写得太粗糙上——比如把路上等红灯的车全部判成了违停。所以在整个项目的设计阶段先别急着写代码。把视频流采集、车辆检测、目标跟踪、违停判定、告警输出这五个模块的边界理清楚再动手后面会省很多事。2. 核心模块解析与关键实现要点2.1 车辆检测模块YOLO 模型的选型与改造车辆检测是整个系统的基石这个模块的输出质量直接决定了上层跟踪和判定逻辑的效果。选型的时候我建议从 YOLOv5s 或者 YOLOv8n/s 里挑一个作为基线原因很简单这两个版本在 GitHub 上的生态最成熟遇到问题能找到的参考资料最多对于做毕设或者课设来说这比那一点精度差异重要得多。如果你用的是 YOLOv5需要注意一个关键改造点——类别过滤。COCO 数据集中和车辆相关的类别有 car汽车、bus公交车、truck卡车、motorcycle摩托车这几类但有的时候还会检测出 bicycle自行车、person行人。在违停检测场景里行人和自行车不应该参与判定所以后处理阶段要把这几个类别的索引过滤掉。用 YOLOv5 的话类别 ID 是car 为 2motorcycle 为 3bus 为 5truck 为 7。YOLOv8 的类别顺序保持一致处理方式相同。还有一个细节容易被忽略检测框的置信度阈值不能拍脑袋随便定。阈值设得太高比如 0.8夜间或者遮挡场景下车辆会被漏掉设得太低比如 0.2大量误检框会让 DeepSORT 的匹配矩阵混乱出现鬼影轨迹。我实测下来白天场景 0.45 比较合适夜间可以放宽到 0.35 左右。如果你做的是实时视频流可以做一个简单的策略按时间段自动切换阈值或者用自适应阈值让系统在白天和夜间都能保持稳定的检测率。2.2 跟踪模块DeepSORT 的原理与参数调节DeepSORT 这个算法用一句话概括就是在 SORT 的基础上加了一个 ReID重识别特征提取分支用来解决目标重新出现后 ID 丢失的问题。SORT 只靠卡尔曼滤波预测位置加匈牙利算法做框的关联一旦车辆被遮挡或者短暂离开画面再回来就变成一个新 ID 了。DeepSORT 额外比较每一个检测框和已有轨迹的表观特征appearance feature特征相似度足够高的话即使位置预测有偏差也能正确关联回原来的 ID。参数调节上我挑几个在实际项目中影响最大的说一下max_dist最大余弦距离控制的是表观特征匹配的阈值默认值 0.2实际调高到 0.3 会更适合车辆场景因为车辆的 ReID 特征本身不如行人区分度高阈值太严会导致同一辆车仅仅因为角度变化就匹配失败。max_iou_distance 默认 0.7这个可以保持。max_age 是轨迹丢失后保留的最大帧数默认 70对于车辆违停场景建议调大到 90 甚至 120因为一辆车在一个位置停很久期间可能被其他车辆短暂遮挡如果 max_age 太小遮挡结束之后这辆车就会被当成一辆新车停留时长计算就会重置违停判定直接失效。n_init 这个参数表示轨迹需要连续多少帧命中检测框才算确认状态默认 3这个不用动。还有一个容易被忽略的点DeepSORT 里卡尔曼滤波的状态变量是基于中心点坐标、宽高比和高度做的归一化处理如果原始视频分辨率是 1920x1080输入给跟踪器之前最好先做一次坐标归一化不然不同分辨率视频之间的跟踪效果会差很多。源码包里如果带了 utils 工具函数一般会有对应处理但如果你是自己手动搭的流程记得检查这一步。2.3 违停判定逻辑状态机设计这个模块是整个系统的灵魂。我见过很多实现最简单粗暴的是一旦检测框中心进入 ROI 就开始计时超过阈值就触发告警但这样做误报率极高。推荐用状态机来做核心逻辑是四个状态的流转空闲无车、进入有车进入ROI、停留车辆在ROI内且静止、离开车辆驶出ROI。具体实现时要同时维护两个参数车辆是否在 ROI 内空间条件、车辆移动速度是否低于阈值时间条件。只有当两个条件同时满足且持续时间超过设定阈值时才判定为违停。比如你可以设定车辆中心点在 ROI 内持续超过 30 秒且在这段时间内位移不超过 20 像素连续 30 帧满足条件后触发告警。判定结束后要把这个目标的 ID 放进一个“已处理”列表避免同一个 ID 持续触发重复告警。停车时间阈值怎么定看场景。学校门口、医院门口这种即停即走的地方可以设定 60 秒消防通道、公交站牌这类严格禁停区域30 秒比较合适。这个参数建议直接放进配置文件里方便根据需要调整不要硬编码在代码里。3. 数据准备、模型训练与实战调优3.1 数据集的选择与预处理做这个项目到底要不要自己标注数据集我的建议是分两步走。第一步用公开数据集把整个流程跑通比如 UA-DETRAC 或者 BDD100K这两个数据集在车辆检测方向非常经典直接转成 YOLO 格式就能用。第二步如果想让系统在你自己选的场景比如校园门口、某个特定的路段表现更好再自己采集几百张到一千张现场截图做微调fine-tune这个数据量在 YOLO 上已经能带来肉眼可见的提升。自己标注的时候有个工具推荐用 LabelImg虽然老但胜在稳定且网上教程多。标注时注意几个规范车辆的检测框尽量贴合车身不要太大也不要太小被遮挡严重的车辆可以不标不要硬标否则会引入噪声摩托车和小电驴如果也要检测单独建一个类别。数据预处理这一步YOLO 对输入图片会自动做 letterbox 缩放不需要手动 resize但建议把训练图片统一放到一个目录下标签文件放另一个目录保持文件名一一对应。数据集划分上train / val 建议 8:2 或者 9:1随机划分即可。3.2 训练参数配置与常见坑这里直接给一套经过验证的参数组合以 YOLOv5 为例# 数据集配置文件 train: ./datasets/custom/images/train val: ./datasets/custom/images/val nc: 4 # car, bus, truck, motorcycle names: [car, bus, truck, motorcycle]训练命令python train.py --data custom.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --device 0batch size 的设置取决于显存。6G 显存建议 168G 可以试 32再往上就要用更大的显存了。训练轮次方面100 个 epoch 基本够用再多容易过拟合。如果你用的是 YOLOv8命令大同小异只是类的组织方式略有区别。训练过程中最常见的坑有两个。一个是学习率问题如果你在预训练权重的基础上做微调默认的 lr 是能够正常收敛的但如果你从零训练不推荐要把 lr 调小 10 倍否则 loss 会直接飞掉。另一个是类别不均衡比如你的数据里 car 占了 90%bus 只有 2%训练出来的模型对 bus 的召回率会非常难看。解决办法是在数据层面做增广——把少类别的样本复制几份或者用 mosaic 增强时给少类别加权重。训练完成后用val.py看一下各类别的 mAP 和召回率。如果所有类别的 AP 都在 0.85 以上这个模型拿来跑项目已经够用了。3.3 部署环境与推理速度优化训练和推理的环境建议分开。训练用带 GPU 的机器推理部署则看你要跑在什么设备上。如果是在 PC 上做演示RTX 3060 以上就非常流畅如果要部署到 Jetson Nano 这种边缘设备需要做一些特殊的优化比如用 TensorRT 加速或者把输入分辨率从 640 降到 480。推理流程里的一个性能瓶颈是很多人会在每一帧都做一次全流程的检测加跟踪这样其实很浪费。如果摄像头帧率是 30 FPS你可以让检测器跑在 10 FPS每三帧检测一次跟踪器在中间帧用卡尔曼预测来补位。这种方式叫检测间隔跳帧实测下来系统吞吐量能提升接近两倍而跟踪效果几乎没有明显下降。DeepSORT 源码里的detection输入如果控制好了帧率跟踪质量并不会有大的损伤。视频流接入方面OpenCV 的VideoCapture读取 RTSP 流时建议开启硬件解码并关闭自动缓冲否则延时会有 1 到 3 秒实时性完全无法保证。正确做法是用一个单独的子线程去拉流把最新的帧存到队列里主线程做检测跟踪这样即使算法处理不过来视频流也不容易卡死。4. 系统集成与完整流程实现4.1 整体代码流程梳理整套系统跑起来之后的流程大概是这样的读入视频帧 - YOLO 做目标检测 - 对检测框做类别过滤和置信度过滤 - 送入 DeepSORT 做关联更新 - 对确认态的轨迹做违停判定 - 可视化输出画框、画轨迹、标记违停状态- 触发告警并截图保存。这里我把核心代码骨架写出来基于项目源码简化后的版本大家可以直接参考import cv2 import torch from deep_sort import DeepSort def main(video_path): # 初始化检测器和跟踪器 detector torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) deepsort DeepSort(deep_sort_pytorch/configs/deep_sort.yaml) # 初始化ROI区域多边形顶点坐标 roi_polygon [(100, 500), (800, 500), (800, 700), (100, 700)] cap cv2.VideoCapture(video_path) stay_start_time {} while True: ret, frame cap.read() if not ret: break # 1. 检测 results detector(frame) detections [] for *xyxy, conf, cls in results.xyxy[0]: # 只保留车辆类别 if int(cls) in [2, 3, 5, 7]: detections.append([xyxy[0], xyxy[1], xyxy[2], xyxy[3], float(conf)]) # 2. 跟踪 bbox_xywh convert_to_xywh(detections) outputs deepsort.update(bbox_xywh, frame) # 3. 违停判定 for track in outputs: track_id track[4] cx, cy (track[0] track[2]) / 2, (track[1] track[3]) / 2 in_roi is_point_in_polygon(cx, cy, roi_polygon) if in_roi and track_id not in stay_start_time: stay_start_time[track_id] time.time() if track_id in stay_start_time: duration time.time() - stay_start_time[track_id] if duration 30: cv2.rectangle(frame, (track[0], track[1]), (track[2], track[3]), (0, 0, 255), 2) cv2.putText(frame, fILLEGAL PARKING {track_id}, (track[0], track[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imshow(Violation Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码把整个流程最核心的链路串起来了但实际项目里会有很多细节需要补充比如视频流断线重连、告警记录的日志写入、截图保存到本地、以及远端的告警推送通过 HTTP 请求或者消息队列。这些功能源码包里一般有实现大家根据自己的场景扩展即可。4.2 ROI 区域的交互式标定我在第一节就提到过ROI 标定对实际效果影响很大。如果你想做一个交互式的标定工具可以基于 OpenCV 的鼠标回调函数来实现用户点击画面上的点连成一个闭合多边形然后保存到配置文件里。这个功能代码量不大但能让你在换场景时省掉改代码的麻烦。import cv2 points [] def on_mouse(event, x, y, flags, param): if event cv2.EVENT_LBUTTONDOWN: points.append((x, y)) print(fPoint {len(points)}: ({x}, {y})) elif event cv2.EVENT_RBUTTONDOWN: points.clear() print(Points cleared) cv2.namedWindow(calibrate) cv2.setMouseCallback(calibrate, on_mouse) while True: frame param[frame].copy() if len(points) 1: for i in range(len(points) - 1): cv2.line(frame, points[i], points[i 1], (0, 255, 0), 2) cv2.polylines(frame, [np.array(points)], True, (0, 255, 0), 2) cv2.imshow(calibrate, frame) if cv2.waitKey(1) 0xFF ord(s): np.save(roi_points.npy, np.array(points)) break这一个功能模块做好之后整套系统的场景迁移能力会强很多。你带着程序到新场地花两分钟重新标一下禁停区域就能直接跑而不是回办公室改代码。4.3 告警机制的实现方式告警模块是违停检测系统从“能看”到“能用”的分水岭。最基础的实现是程序检测到违停后在画面上画框、打印日志。稍微进阶一点的是把告警截图保存到本地同时调用一个 Webhook 推送到钉钉、企业微信或者短信服务商。再高级一些的可以在告警信息里附带该车辆的跟踪轨迹截图和进入违停区域的时间方便现场管理人员快速判断。告警逻辑里要注意去重。一辆车在违停期间如果系统每帧都触发告警会产生海量冗余消息。正确做法是维护一个已告警列表当某个 track_id 已经触发过告警后在它离开 ROI 或者停止跟踪之前不再重复触发。有的场景还要求违停车辆离开后自动解除告警这也是通过状态机来判断的。5. 常见问题排查与调优经验5.1 跟踪 ID 频繁跳变怎么办这是做 DeepSORT 项目最常遇到的问题表现是同一辆车在画面里 ID 从 3 跳到 17 再跳到 42轨迹断成好几段。排查路径按顺序来第一步看检测框稳不稳定。把检测结果可视化出来按帧播放如果发现同一辆车在某一帧检测丢失或者出现两个重叠框那问题在检测器而不是跟踪器。解决办法是调低置信度阈值或者把 NMS 的 IoU 阈值稍微调高一点比如从 0.45 调到 0.5。第二步看 ReID 特征。DeepSORT 默认用的特征提取模型是在行人数据集上训练的直接用在车辆上效果一般。如果环境允许建议换用车辆 ReID 数据集比如 VeRi训练的权重或者至少对特征做 L2 归一化后重新计算距离矩阵。这一步能大幅减少因为外观变化导致的 ID 切换。第三步看 max_age 和 n_init 的参数设置具体数值上一节已经介绍过这里不重复。如果前面的检查都没问题那就把 max_age 再往上调调通常能缓解大部分场景下的轨迹断裂问题。5.2 夜间检测效果差夜间是违停检测系统的阿喀琉斯之踵。车灯眩光、光照不足、夜间色偏都会让 YOLO 的检测精度明显下降。我实测过 YOLOv5s 在夜间场景的 mAP 会比白天掉 10 到 15 个百分点。对策有几个一是做数据层面的增强在自己标注的数据里加入夜间样本并且训练时开启 HSV 增广让模型见过更多光照变化的情况。二是在图像预处理阶段做自适应直方图均衡化CLAHE把暗部细节拉出来。三是如果条件允许直接用红外摄像头或者带星光级传感器的摄像头硬件层面的提升永远比算法层面来得直接。5.3 系统长时间运行的稳定性毕设答辩的时候可能只演示十分钟但如果系统要真正在校园里部署长时间稳定性是必须考虑的问题。内存泄漏是最常见的问题——OpenCV 的 Mat 对象、PyTorch 的 Tensor、DeepSORT 的内部缓存如果代码里不注意释放跑几个小时之后内存会慢慢涨上去。建议在核心流程外加一个定时重启机制或者对内存做监控超过阈值后自动重启推理进程。DeepSORT 内部的tracker.tracks列表会随着运行时间变长而积累大量已经失活的轨迹定期调用清理函数非常有必要。视频流方面RTSP 流经常会出现断流情况代码里要写好异常处理断线后自动重连不要让程序直接崩溃。另外一个实际部署中常见的坑是时间同步。如果你用time.time()计算车辆停留时长而部署机器的时钟不准比如时间被回拨到达时间和停留时间就都会出错。建议用视频帧的时间戳或者是视频流自带的绝对时间戳来做计算而不是依赖系统时钟。5.4 误报漏报速查表最后把常见问题的排查思路整理成一张表方便大家在实际调试时快速定位问题现象可能原因排查与解决办法同一辆车 ID 频繁变化检测框不稳定或 ReID 特征区分度差检查检测置信度阈值更换车辆 ReID 权重车辆进入 ROI 但长时间不触发告警停留时间判定逻辑未生效或 max_age 太小检查状态机逻辑调大 max_age 参数路边静止车辆全部误报判定逻辑只判断位置没有判断是否静止增加位移速度判断只在速度和位置同时满足时计时夜间大量漏检光照变化导致检测率下降训练时加夜间数据增强预处理加 CLAHE视频流延迟越来越大视频缓冲堆积或推理速度不匹配独立线程拉流检测间隔跳帧关闭 OpenCV 缓冲程序运行几小时后内存溢出内部缓存未释放或轨迹列表持续增长定期清理失活轨迹监控内存并自动重启多个摄像头同时接入时卡顿单线程推理成为瓶颈使用多进程或批处理推理每路视频独立跟踪器这些内容基本覆盖了我在实际开发中遇到的大部分问题。如果你跟着本文的思路把检测、跟踪、判定、告警这条链路走通再针对你自己的场景微调参数这个项目就能从“能跑起来”进化到“真正能用的状态”。之前带过几个学生做过类似的课题最大的感受是模型训练反而是整个项目里最不费力的部分真正花时间的全在工程细节上——如何让跟踪稳定一点如何让判定逻辑不误报如何让告警不刷屏。这些东西没有哪个模型能直接给你答案只能一个坑一个坑地踩过来。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价