基于YOLO的机动车乱停乱放检测系统:从模型训练到逻辑判定的完整工程实践
简介本资源为基于YOLO的机动车乱停乱放检测系统完整项目包面向人工智能、计算机视觉方向的学生与开发者尤其适合作为毕业设计或课程实践参考。项目利用YOLO目标检测框架识别车辆并判断违规停放行为涵盖数据预处理、模型训练、推理与后处理等核心模块帮助读者理解深度学习在智能交通场景中的落地方式。压缩包共16个文件以13张png与1张jpeg图片、1个py脚本、1个md说明文档为主整体约9.34MB图片可用于展示检测效果与训练过程脚本与文档则提供代码入口和部署指引。目前已有214人学习下载。通过该资源读者可获得可运行的源码、模型权重与部署教程掌握YOLO模型训练、数据增强及视频流实时检测的完整流程并了解光照变化、遮挡等实际问题的处理思路对完成毕业设计或开展相关研究具有较高参考价值。1. 从一段路口监控说起这套 YOLO 乱停乱放检测系统到底能干什么路口摄像头拍了一整天真正需要人工回看的违停片段可能就那么几十秒但你要在十几个小时的视频里把它找出来。这套「基于 YOLO 的机动车乱停乱放检测系统」解决的正是这件事它把目标检测模型和一套判定逻辑绑在一起让机器先替你把画面里的车框出来再根据车辆停留时长、是否压线、是否占用禁停区域来判断「这辆车是不是乱停」。源码包里包含训练脚本、推理脚本、权重文件和一份部署教程属于典型的毕业设计级完整工程但功能链路是通的拿来做课程设计、二次开发或者当成一个可跑通的 YOLO 落地样板都合适。适合谁手上有 Python 基础、装过 PyTorch、想找一个「检测 业务判定」闭环项目练手的人。如果你只是想跑个官方 demo 看框那这个包对你偏重如果你要的是从数据到部署的完整流程它刚好卡在这个位置上。2. 拆开这个包YOLO 检测与乱停判定的技术链路2.1 为什么是 YOLO而不是两阶段检测器乱停乱放检测的本质是「先找到车再判断这辆车的行为」。第一步是目标检测第二步是时序和空间逻辑。选 YOLO 做第一步核心原因是速度。路口视频通常是 25fps 起步如果用 Faster R-CNN 这类两阶段检测器单帧推理在普通显卡上就可能吃掉几十毫秒多路视频一叠加实时性直接崩掉。YOLO 把检测当成回归问题一次前向就出框和类别工程上更容易做到「边拉流边推理」。另一个原因是部署友好。YOLO 系列从 v5 开始权重导出 ONNX、TensorRT 的链路非常成熟源码包里如果带的是.pt权重你几乎可以无痛转成 ONNX 再上 TensorRT 或 OpenVINO。两阶段检测器导出和量化时踩的坑通常更多对毕业设计这种「能跑起来比极致精度更重要」的场景YOLO 是更稳的选择。至于选哪个版本常见做法是如果源码包给的是 YOLOv5 或 YOLOv8直接用不要自己换版本。YOLOv5 的生态最全网上能搜到的部署教程最多YOLOv8 的 API 更干净训练脚本更短。两者在乱停检测这种「车 背景」的二分类或三分类任务上精度差距远没有工程便利性差距大。2.2 乱停判定不是检测是逻辑层很多人第一次看这类系统会误以为「检测到车 乱停」这是最大的误解。检测模型只负责输出[x1, y1, x2, y2, conf, cls]它不知道这辆车停了多久、停在哪。乱停判定是在检测结果之上加的一层业务逻辑通常包含三个维度停留时长对同一辆车做跨帧跟踪常见做法是 ByteTrack 或简单的 IOU 匹配记录它进入画面的时间超过阈值比如 30 秒才判定为「停」。空间位置判断车辆框的中心点或底边是否落在预设的禁停区域内。禁停区域一般用多边形标注存在一个 JSON 或 YAML 配置里。状态过滤排除正在行驶的车。如果车辆在两帧之间位移超过阈值说明它在动不参与乱停判定。这三条逻辑组合起来才是「乱停乱放检测」。源码包里如果有一个judge.py或logic.py之类的文件大概率就是干这个的。理解这一点你才知道为什么检测框画得再准系统还是可能误报——问题往往出在逻辑层的阈值上而不是模型。2.3 环境配置从零把依赖装对部署教程里一般会给requirements.txt但直接pip install -r经常翻车因为 YOLO 对 PyTorch 和 CUDA 版本很敏感。我一般会按下面的顺序来先确认显卡驱动和 CUDA再装 PyTorch最后装 YOLO 本体。# 1. 确认显卡和驱动能看到 CUDA Version 说明驱动没问题 nvidia-smi # 2. 建一个干净的虚拟环境别在 base 里装 conda create -n yolo_park python3.9 -y conda activate yolo_park # 3. 装 PyTorch版本要和你的 CUDA 对应 # CUDA 11.8 的常见组合其他版本去 PyTorch 官网查对应命令 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 4. 装 YOLO 本体如果源码包自带就不装 pip install ultralytics # 5. 装其余依赖 pip install opencv-python numpy pyyaml tqdm逻辑说明第一步nvidia-smi是排错起点如果这里就报错后面全白搭。第二步用 conda 隔离环境是因为 YOLO 项目经常和别的项目抢 PyTorch 版本混装是血泪经验里最常见的翻车点。第三步的--index-url是关键不加这个参数pip 会去默认源拉 CPU 版装完发现torch.cuda.is_available()返回 False很多人卡在这里半天。第四步如果源码包已经带了ultralytics目录就不要重复装否则版本冲突。参数说明python3.9是兼容性最好的版本3.10 以上有些旧版 YOLOv5 会出问题torch2.0.1对应 CUDA 11.8如果你显卡驱动较新可以上 CUDA 12.x 配 torch 2.1但源码包如果写死了旧版本就跟着源码走。2.4 推理脚本怎么跑单图、视频、摄像头三种模式装完环境下一步是让模型动起来。源码包里的推理入口通常是一个detect.py或main.py参数大同小异。下面是一个典型的调用方式# 单张图片推理结果存到 runs/detect/exp python detect.py --source test.jpg --weights best.pt --conf 0.4 # 视频文件推理 python detect.py --source parking.mp4 --weights best.pt --conf 0.4 --save-txt # 调用本地摄像头实时看效果 python detect.py --source 0 --weights best.pt --conf 0.4 --view-img逻辑说明--source决定输入源可以是图片路径、视频路径、摄像头编号0 是默认摄像头或者一个目录。--weights指向训练好的权重源码包一般会带一个best.pt如果没带你需要自己训练或者下载预训练权重。--conf是置信度阈值默认 0.25乱停检测场景我一般调到 0.4因为误检一辆不存在的车比漏检更烦人。--save-txt会把检测框坐标存成 txt方便后面接逻辑层。--view-img是实时预览调试时用正式跑批处理时关掉。参数说明--conf调高会减少误检但可能漏检调低反之0.4 是一个偏保守的起点--iou控制 NMS 的 IoU 阈值默认 0.45车辆密集时如果发现框被吞可以调到 0.5--imgsz是推理分辨率默认 640如果画面里车很小调到 1280 能提升召回但速度会掉。3. 训练自己的乱停数据集标注、配置与调参3.1 数据标注只标车还是标状态这是训练前必须想清楚的问题。有两种标注策略只标车辆所有车都标成car一个类乱停判定完全交给逻辑层。优点是标注快模型泛化好缺点是模型不区分状态逻辑层压力大。标状态把车标成car_normal和car_illegal两类。优点是模型直接输出状态逻辑层简单缺点是标注成本高而且「乱停」是一个时序概念单帧标注很难标准。常见做法是第一种只标车。因为乱停的本质是「车 时间 位置」单帧里一辆静止的车和一辆乱停的车长得一模一样强行让模型学状态只会让它学偏。源码包如果用的是单类car说明作者走的是这条路你跟着走就行。标注工具用 LabelImg 或 Roboflow 都行输出 YOLO 格式的 txt每行是cls x_center y_center width height坐标归一化到 0-1。标注时注意车辆被遮挡超过一半的不要标夜间模糊的不要标这些样本会拉低模型质量。3.2 数据集目录结构与 data.yamlYOLO 对目录结构有固定要求放错了训练直接报错找不到图片。标准结构如下dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml是数据集配置文件内容大致是# 训练集和验证集路径可以是绝对路径也可以是相对路径 train: ./dataset/images/train val: ./dataset/images/val # 类别数量 nc: 1 # 类别名称顺序要和标注时的 cls 编号对应 names: [car]逻辑说明train和val指向图片目录YOLO 会自动去找同级的labels目录。nc是类别数单类就是 1。names的顺序必须和标注时用的编号一致如果标注时car是 0这里就写[car]写反了模型会把类别学乱。这个文件最容易出的错是路径用了 Windows 反斜杠YOLO 在 Linux 下读不了统一用正斜杠。3.3 训练命令与关键参数配置好之后训练命令通常是这样# 从预训练权重开始训练epochs 100batch 16图片尺寸 640 python train.py --data data.yaml --weights yolov5s.pt --epochs 100 --batch 16 --imgsz 640 --device 0逻辑说明--weights yolov5s.pt是从预训练权重开始这叫迁移学习比从零训练收敛快得多也是 yolo 预训练模型下载 这个热搜词背后的实际需求——没人从零训。--epochs 100是训练轮数乱停数据集通常几千张图100 轮够用太多会过拟合。--batch 16是批大小取决于显存8G 显存跑 640 尺寸大概能到 16不够就降到 8。--device 0指定第一块显卡多卡用0,1。参数说明--imgsz 640是训练分辨率和推理保持一致--lr0是初始学习率默认 0.01如果 loss 震荡厉害可以降到 0.001--patience是早停轮数默认 50验证集指标 50 轮不提升就停省时间。3.4 训练过程看什么loss、mAP 和混淆矩阵训练开始后控制台会打印每一轮的box_loss、obj_loss、cls_loss和mAP0.5。重点看两个box_loss框回归损失应该持续下降。如果它不降反升多半是学习率太大或者标注有问题。mAP0.5平均精度越高越好。乱停检测单类任务mAP 到 0.85 以上就算能用0.9 以上算好。训练结束后会生成混淆矩阵。这里有个热搜词叫「yolo 混淆矩阵总合不唯一」说的就是混淆矩阵对角线之外还有值说明模型把某些类分错了。单类任务里混淆矩阵应该接近对角阵如果出现大量非对角值检查是不是标注时类别编号写错了。另一个常见现象是「yolo 训练中 bn 崩溃」表现为 loss 突然变 NaN原因是 batch 太小或者学习率太大解决办法是把--batch调大或者加--lr0 0.001。4. 乱停判定的逻辑层实现从检测框到报警4.1 用 IOU 做简易跟踪检测模型每帧输出一堆框但系统需要知道「这个框和上一帧的哪个框是同一辆车」。最轻量的做法是 IOU 匹配计算当前帧每个框和上一帧所有框的 IOU取最大值超过阈值就认为是同一辆车。import numpy as np def iou(box1, box2): # box 格式 [x1, y1, x2, y2] x1 max(box1[0], box2[0]) y1 max(box1[1], box2[1]) x2 min(box1[2], box2[2]) y2 min(box1[3], box2[3]) inter max(0, x2 - x1) * max(0, y2 - y1) area1 (box1[2] - box1[0]) * (box1[3] - box1[1]) area2 (box2[2] - box2[0]) * (box2[3] - box2[1]) union area1 area2 - inter return inter / union if union 0 else 0 def match_tracks(current_boxes, prev_boxes, iou_thresh0.3): # 返回 current_boxes 中每个框匹配到的 prev 索引-1 表示新目标 matches [] for cb in current_boxes: best_iou, best_idx 0, -1 for i, pb in enumerate(prev_boxes): v iou(cb, pb) if v best_iou: best_iou, best_idx v, i matches.append(best_idx if best_iou iou_thresh else -1) return matches逻辑说明iou函数算两个框的交并比match_tracks给当前帧每个框找上一帧里最像的那个。iou_thresh0.3是经验值太低会把两辆车混成一辆太高会频繁断跟踪。这个简易跟踪不完美车辆交叉时会跟丢但对乱停检测够用因为乱停的车通常不动跟踪压力小。参数说明iou_thresh在车辆密集场景可以调到 0.4稀疏场景 0.2 也行如果发现同一辆车被反复分配新 ID说明阈值太低。4.2 停留时长与禁停区域判定有了跟踪 ID就可以记录每辆车的首次出现时间并判断它是否在禁停区域内。import time from shapely.geometry import Point, Polygon # 禁停区域实际项目里从配置文件读 no_park_zone Polygon([(100, 200), (500, 200), (500, 600), (100, 600)]) # 记录每辆车的首次出现时间和位置 track_history {} def judge_illegal(track_id, box, fps25, stay_seconds30): cx (box[0] box[2]) / 2 cy (box[1] box[3]) / 2 point Point(cx, cy) if track_id not in track_history: track_history[track_id] {start: time.time(), frames: 0} return False info track_history[track_id] info[frames] 1 duration time.time() - info[start] # 在禁停区域内且停留超过阈值 if no_park_zone.contains(point) and duration stay_seconds: return True return False逻辑说明no_park_zone是一个多边形实际项目里应该从 JSON 配置读方便不同路口切换。judge_illegal用车辆框中心点判断是否在区域内同时累计停留时间。两个条件同时满足才报警避免把路过禁停区的车误判。stay_seconds30是阈值路口场景一般 30 到 60 秒小区门口可以短一点。参数说明用中心点判断简单但不够准车辆压线时中心点可能在区域外更严谨的做法是用框的底边中点或者计算框与区域的重叠面积stay_seconds要根据场景调太短会误报等红灯的车太长会漏报。4.3 把逻辑层和检测层接起来最后一步是把检测输出喂给逻辑层形成一个完整循环import cv2 from ultralytics import YOLO model YOLO(best.pt) cap cv2.VideoCapture(parking.mp4) prev_boxes [] while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, conf0.4)[0] current_boxes results.boxes.xyxy.cpu().numpy().tolist() matches match_tracks(current_boxes, prev_boxes) for idx, box in enumerate(current_boxes): track_id matches[idx] if matches[idx] ! -1 else len(track_history) if judge_illegal(track_id, box): # 画红框并标注 cv2.rectangle(frame, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 0, 255), 2) cv2.putText(frame, ILLEGAL, (int(box[0]), int(box[1]) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2) prev_boxes current_boxes cv2.imshow(result, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明每帧先跑检测拿到框列表然后和上一帧做匹配给每个框分配 track_id再交给judge_illegal判断。判定为乱停的画红框。prev_boxes在每帧末尾更新形成跨帧跟踪。这个循环是整套系统的骨架源码包里的主程序基本就是这个结构。参数说明conf0.4和前面推理一致cv2.waitKey(1)控制播放速度1 毫秒接近实时调大可以慢放调试。5. 避坑与排查那些让系统跑不起来的常见问题5.1 现象torch.cuda.is_available()返回 False原因装成了 CPU 版 PyTorch或者 CUDA 版本和 PyTorch 不匹配。这是最高频的翻车点十个人里有六个卡在这。解决先nvidia-smi看驱动支持的 CUDA 版本然后去 PyTorch 官网查对应安装命令务必带--index-url。装完在 Python 里跑import torch; print(torch.cuda.is_available())返回 True 才算过。如果还是 False卸载重装别试图修。5.2 现象训练 loss 变成 NaN或者 mAP 一直是 0原因学习率太大、batch 太小、标注格式错误三者之一。loss 变 NaN 通常是学习率问题mAP 为 0 通常是标注问题。解决先把--lr0降到 0.001 试一轮如果还不行检查标注文件用labelimg重新打开几张图确认框没画反、类别编号没写错。标注里最常见的错是坐标没归一化YOLO 要求 0-1 之间如果写成像素值训练直接崩。5.3 现象推理时框画出来了但乱停判定从不触发原因禁停区域坐标和视频分辨率不匹配。很多人标注区域时用的是原图坐标但推理时视频被 resize 到 640坐标对不上车辆中心点永远落在区域外。解决统一坐标系。要么把禁停区域坐标按推理分辨率缩放要么在推理前把帧 resize 回原尺寸再判定。我一般会在配置里存原图坐标推理时按比例换算这样换分辨率不用重标。5.4 现象同一辆车被反复分配新 ID停留时长永远累计不起来原因IOU 匹配阈值太低或者检测框抖动太大。车辆静止时框应该稳定但如果模型置信度在阈值边缘反复横跳框会时有时无。解决把iou_thresh从 0.3 提到 0.4同时把检测conf从 0.4 降到 0.35让框更稳定。如果还不行加一个「丢失容忍」机制车辆连续 5 帧没检测到才认为它离开而不是一帧没检测到就删记录。5.5 现象部署到服务器后速度很慢达不到实时原因没用 GPU或者没用半精度或者视频解码成了瓶颈。解决确认--device 0生效推理时加halfTrue用 FP16速度能快近一倍如果视频解码慢用cv2.CAP_FFMPEG或者先把视频转成低分辨率。另一个常见做法是把 PyTorch 权重导出成 ONNX 或 TensorRTTensorRT 在 NVIDIA 卡上通常比原生 PyTorch 快 2 到 3 倍。6. 进阶技巧把检测精度和部署效率再压一压模型能跑通之后真正拉开差距的是细节。分享几个我在实际项目里反复验证过的做法。第一用切片推理处理小目标。路口摄像头架得高车辆在画面里可能只有几十像素直接 resize 到 640 后车更小召回率掉得厉害。常见做法是 SAHI 切片推理把大图切成带重叠的小块分别检测再合并结果。代价是速度慢但召回能提升十几个点。如果源码包没带这个功能可以自己加核心就是把model(frame)换成对切片列表的循环。第二导出 ONNX 再上 TensorRT。这一步对部署效率提升最明显。导出命令# 导出 ONNXopset 12 兼容性最好 python export.py --weights best.pt --include onnx --opset 12 --imgsz 640 # 用 trtexec 转 TensorRTFP16 精度 trtexec --onnxbest.onnx --saveEnginebest.engine --fp16逻辑说明ONNX 是中间格式TensorRT 是最终部署格式。--opset 12是算子集版本太低不支持某些算子太高有些推理引擎不认12 是稳妥选择。--fp16开启半精度精度损失通常不到 1 个点速度提升明显。转完之后用 TensorRT 的 Python API 加载best.engine推理延迟能压到原生 PyTorch 的三分之一左右。第三用配置文件管理场景参数。禁停区域、停留阈值、置信度这些参数不要写死在代码里。我一般会建一个config.yamlconf_threshold: 0.4 iou_threshold: 0.4 stay_seconds: 30 no_park_zones: - name: 路口A points: [[100, 200], [500, 200], [500, 600], [100, 600]] - name: 路口B points: [[600, 300], [900, 300], [900, 700], [600, 700]]这样换一个路口只需要改配置不用动代码。多场景部署时这个习惯能省大量时间。第四验证方法要固定。每次改完参数不要凭感觉说「好像准了」。固定一段测试视频跑完之后统计三个指标误报数把正常车判成乱停、漏报数乱停车没判出来、平均判定延迟从车停下到报警的秒数。这三个数才是判断改动好坏的依据。我见过太多人调了一下午参数结果只是换了个视频看根本没量化。最后说个我自己的教训。早期做这类系统时我总想着把模型精度往上堆换更大的模型、加更多数据结果部署时发现推理速度根本达不到实时整个方案推倒重来。从那以后我每次做检测项目都强制先跑一遍端到端延迟测试再决定模型规模。模型不是越大越好能稳定跑在目标硬件上的才是好模型。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →