ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于YOLO的机动车乱停乱放检测系统实战:从训练到部署

基于YOLO的机动车乱停乱放检测系统实战:从训练到部署 简介这份资源是面向人工智能方向毕业设计与计算机视觉入门者的实战项目包聚焦城市交通管理中机动车乱停乱放的自动检测问题。项目以YOLO目标检测框架为核心覆盖从数据集准备、模型训练到监控视频流实时推理的完整链路帮助读者理解车辆识别与违规停放判定的实现思路。压缩包共16个文件约9.34MB以13张png与1张jpeg图片、1个py脚本、1个md说明文档为主图片多用于展示检测效果与项目结构脚本承担核心推理逻辑文档则提供部署与使用指引。目前已有214人学习下载。读者可获取可运行的源码、模型权重与部署教程结合目录中的模块划分理解预处理、推理与后处理流程并针对光照变化、遮挡等实际场景思考精度与效率的平衡适合作为毕业设计参考或目标检测落地的练手案例。1. 机动车乱停乱放检测从人工巡查到 YOLO 自动识别的落地路径小区消防通道被占用、商场门口双排停车堵住出口、学校周边上下学时段违停扎堆——这些场景靠人工巡查根本盯不过来。基于 YOLO 的机动车乱停乱放检测系统核心思路是用目标检测模型替代人眼在固定摄像头画面里持续识别车辆位置再结合停留时长和区域规则判断是否构成违停。这套方案适合有 Python 基础、手头有监控视频或摄像头、想快速跑通一套可演示可部署的检测系统的开发者。整套流程拆开看并不复杂环境配置、数据标注、模型训练、推理部署、规则判定每一步都有成熟的工具链支撑。真正花时间的不是写代码而是把“检测到车”和“判定违停”之间的逻辑理清楚以及处理实际场景里光照变化、遮挡、误检这些玄学问题。下面按我实际搭过的一套流程从零讲清楚怎么跑通。2. 环境配置与 YOLO 模型选型别在第一步就把自己坑了2.1 为什么选 YOLO 而不是两阶段检测器做违停检测实时性是硬指标。一个路口或停车场入口的摄像头通常要同时处理多路视频流如果单帧推理超过 100ms多路叠加就卡成幻灯片。YOLO 系列作为单阶段检测器在速度和精度之间取得了比较好的平衡。YOLOv5 和 YOLOv8 是目前社区里落地案例最多的两个版本v5 生态成熟、文档多、踩坑记录全v8 精度更高、API 更简洁。如果你刚接触这个方向我建议从 YOLOv8n 或 YOLOv5s 这种轻量模型起步先跑通全流程再根据实际精度需求换更大的模型。选型时还要考虑部署硬件。如果最终要跑在 Jetson Nano 或树莓派这类边缘设备上模型大小直接决定能不能跑起来。YOLOv8n 的权重文件只有 6MB 左右在 Jetson 上配合 TensorRT 能跑到 30FPS 以上换成 YOLOv8x 直接爆显存。所以选型顺序应该是先确定部署硬件再定模型规模最后调输入分辨率。2.2 环境配置的完整命令与常见报错处理我一般用 conda 建独立环境避免和系统 Python 打架。以下命令在 Ubuntu 20.04 和 Windows 11 上都验证过# 创建 Python 3.9 环境YOLOv8 对 3.8-3.10 支持最好 conda create -n yolo_parking python3.9 -y conda activate yolo_parking # 安装 PyTorch根据 CUDA 版本选对应命令 # CUDA 11.8 用下面这行 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 ultralyticsYOLOv8 官方包 pip install ultralytics # 验证安装 yolo checksyolo checks会输出当前环境、CUDA 可用性、模型版本等信息。如果显示 CUDA 不可用但你有 NVIDIA 显卡大概率是 PyTorch 版本和驱动不匹配。先nvidia-smi看驱动支持的 CUDA 版本再回 PyTorch 官网找对应安装命令别硬装。注意Windows 上如果报Could not locate zlibwapi.dll去 NVIDIA 官网下载 zlibwapi.dll 放到 CUDA 的 bin 目录下即可。这是 cuDNN 在 Windows 上的经典坑。2.3 预训练模型下载与目录结构规划YOLOv8 的预训练权重在首次运行时自动下载但国内网络经常卡住。我一般手动下载后放到项目根目录# 手动下载 YOLOv8n 预训练权重 wget https://github.com/ultralytics/assets/releases/download/v8.1.0/yolov8n.pt # 项目目录结构建议这样组织 parking_detection/ ├── datasets/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/ ├── weights/ │ └── yolov8n.pt ├── configs/ │ └── parking.yaml ├── train.py ├── detect.py └── requirements.txt目录结构看着简单但提前规划好能省很多事。datasets下 images 和 labels 必须严格对应图片名和标签文件名一致只是扩展名不同。configs/parking.yaml是数据集配置文件后面训练时要用。3. 数据集构建与标注违停检测的精度上限在这里就定了3.1 数据采集的四个来源和筛选标准违停检测的数据集和通用目标检测不太一样核心区别在于你不仅要标出车辆还要标出它所在的位置是否属于禁停区域。我一般从四个渠道收集数据第一是公开数据集比如 UA-DETRAC、BIT-Vehicle这些提供大量车辆标注但缺少违停场景的负样本。第二是实地拍摄用手机或摄像头在目标场景录 10-20 分钟视频按每秒 1 帧抽帧能拿到几百张真实场景图。第三是网络爬取从街景地图或公开监控截图中获取但要注意版权和隐私问题。第四是合成数据用 CARLA 等仿真器生成适合补充极端天气和夜间场景。筛选标准就三条车辆清晰可辨、场景与部署环境相似、正负样本比例控制在 1:2 到 1:3 之间。负样本就是正常行驶或合法停放的车辆不加负样本模型会把所有车都当成违停。3.2 标注规范什么算违停边界怎么画标注用 LabelImg 或 Roboflow 都行格式统一用 YOLO 的 txt 格式。关键在标注规范车辆标注框紧贴车身包含后视镜不包含阴影禁停区域单独标一个类别比如no_parking_zone用多边形或矩形框出违停车辆标为illegal_parking正常停放标为legal_parking遮挡超过 50% 的车辆不标低于 50% 的按可见部分标注# 标注文件示例每行格式为 class_id x_center y_center width height # 坐标全部归一化到 0-1 之间 # 0 legal_parking, 1 illegal_parking, 2 no_parking_zone # legal_parking 示例 0 0.4523 0.6120 0.0890 0.1230 # illegal_parking 示例 1 0.3210 0.5430 0.0950 0.1180 # no_parking_zone 示例区域框通常较大 2 0.5000 0.7000 0.4000 0.2000标注完成后用脚本检查一遍确保没有越界坐标、没有空标签文件、类别 ID 没有超出定义范围。我写过一个检查脚本跑一遍能省掉训练时才发现标签错误的麻烦import os import numpy as np def check_labels(label_dir, num_classes3): errors [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue path os.path.join(label_dir, fname) with open(path) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: errors.append(f{fname} 第{i1}行: 字段数不对) continue cls_id int(parts[0]) coords [float(x) for x in parts[1:]] if cls_id num_classes: errors.append(f{fname} 第{i1}行: 类别ID越界) if any(c 0 or c 1 for c in coords): errors.append(f{fname} 第{i1}行: 坐标越界) return errors errors check_labels(datasets/labels/train) print(f发现 {len(errors)} 个问题) for e in errors[:10]: print(e)这个脚本检查字段数、类别 ID 范围和坐标归一化范围。跑完没问题再进训练否则训练中途报错更浪费时间。3.3 数据集配置文件与划分策略configs/parking.yaml内容如下path: ./datasets train: images/train val: images/val nc: 3 names: 0: legal_parking 1: illegal_parking 2: no_parking_zone训练集和验证集按 8:2 划分如果数据量少于 500 张用 7:3 并做 5 折交叉验证。划分时注意同一段视频抽出的帧不能同时出现在训练集和验证集里否则验证精度虚高。我一般按视频来源划分不同视频的帧分到不同集合。4. 模型训练与调参损失函数不降反升的时候怎么办4.1 训练命令与关键参数含义YOLOv8 的训练入口很简洁yolo detect train \ dataconfigs/parking.yaml \ modelweights/yolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ device0 \ projectruns/train \ nameparking_v1逐个说参数imgsz640是输入分辨率违停检测里车辆目标通常占画面比例不大640 够用如果小目标多可以提到 1280 但显存翻倍。batch16根据显存调8G 显存跑 640 分辨率大概能到 16。lr00.01是初始学习率YOLOv8 默认用 SGD 时这个值比较稳。lrf0.01是最终学习率因子训练结束时学习率降到lr0 * lrf。patience20是早停耐心值20 轮验证精度不提升就停。4.2 损失函数曲线解读与 BN 崩溃处理训练启动后重点看三个损失box_loss边界框回归、cls_loss分类、dfl_loss分布焦点损失。正常情况三个都应该是下降趋势震荡正常但整体向下。如果 box_loss 不降反升先检查标注框有没有越界或宽高为负的情况。如果 cls_loss 居高不下大概率是类别不平衡违停样本远少于正常样本可以在 yaml 里加cls_pw参数或者对少数类过采样。BN 崩溃是训练中比较棘手的问题现象是 loss 突然变成 NaN终端报AssertionError: Torch not compiled with CUDA enabled或者直接崩掉。原因通常是 batch size 太小导致批归一化的统计量不稳定。解决办法有三个把 batch 调到 16 以上、改用rectTrue让同 batch 内图片尺寸一致、或者在模型配置里把 BN 换成 GN。我遇到过最隐蔽的一次是学习率设成 0.1 导致梯度爆炸降到 0.01 就稳了。4.3 训练过程监控与模型选择训练时开 TensorBoard 看曲线tensorboard --logdir runs/train重点看metrics/mAP50-95和metrics/precision、metrics/recall。违停检测里 recall 比 precision 重要漏检一辆违停车比误报一辆正常车后果更严重。如果 recall 偏低降低置信度阈值到 0.2-0.3 试试。训练完成后在runs/train/parking_v1/weights/下会有best.pt和last.pt部署用 best.pt。注意如果验证集 mAP 比训练集低超过 15 个百分点说明过拟合了。加数据增强、加 dropout、减小模型规模都是可选方案。我一般先加数据增强mosaic 和 mixup 对违停场景效果明显。5. 推理部署与违停判定逻辑检测到车不等于判定违停5.1 推理脚本与多路视频流处理单张图片推理很简单from ultralytics import YOLO model YOLO(runs/train/parking_v1/weights/best.pt) results model(test.jpg, conf0.3, iou0.5) for r in results: boxes r.boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f类别: {model.names[cls_id]}, 置信度: {conf:.2f}, 坐标: {xyxy})conf0.3是置信度阈值低于这个值的检测框丢弃。iou0.5是 NMS 的 IoU 阈值重叠度超过 0.5 的框合并。多路视频流处理时用 OpenCV 逐帧读取每 N 帧送一次模型推理中间帧用跟踪算法补上。我一般用 ByteTrack 做跟踪这样能拿到每辆车的轨迹 ID为后续停留时长判断打基础。5.2 违停判定的三层逻辑检测到车辆只是第一步判定违停需要三层逻辑叠加第一层是区域判定车辆检测框的中心点是否落在no_parking_zone区域内。用射线法或 OpenCV 的pointPolygonTest判断点是否在多边形内。第二层是时长判定同一辆车通过跟踪 ID 关联在禁停区域内连续停留超过阈值通常 3-5 分钟才判定为违停。这个阈值根据实际场景调整学校门口可以短一些小区内部道路可以长一些。第三层是状态判定车辆是否处于熄火状态。这个靠视觉很难判断实际落地中一般用区域时长就够了熄火状态需要结合地磁或雷达传感器。import cv2 import numpy as np from collections import defaultdict import time # 禁停区域多边形顶点根据实际画面标定 NO_PARKING_ZONE np.array([[100, 200], [500, 200], [500, 450], [100, 450]]) # 记录每辆车的停留信息 track_history defaultdict(lambda: {enter_time: None, in_zone: False}) def is_in_zone(center_point, polygon): 判断点是否在多边形区域内 return cv2.pointPolygonTest(polygon, center_point, False) 0 def check_illegal_parking(track_id, center, current_time, stay_threshold180): 判定违停在禁停区域内停留超过 stay_threshold 秒 stay_threshold 默认 180 秒3 分钟 info track_history[track_id] in_zone is_in_zone(center, NO_PARKING_ZONE) if in_zone and not info[in_zone]: # 刚进入区域记录时间 info[enter_time] current_time info[in_zone] True elif not in_zone and info[in_zone]: # 离开区域重置 info[enter_time] None info[in_zone] False if info[in_zone] and info[enter_time]: duration current_time - info[enter_time] if duration stay_threshold: return True, duration return False, 0这段代码的核心是track_history字典用跟踪 ID 做 key记录每辆车进入禁停区域的时间。stay_threshold默认 180 秒实际部署时根据场景调整。判定逻辑里有个容易忽略的点车辆短暂压线不算违停必须连续停留超过阈值。所以离开区域时要重置计时器避免误判。5.3 部署到边缘设备的优化手段如果最终要部署到 Jetson 或类似边缘设备模型需要转成 TensorRT# 导出 ONNX yolo export modelbest.pt formatonnx opset12 # 在 Jetson 上用 trtexec 转 TensorRT /usr/src/tensorrt/bin/trtexec \ --onnxbest.onnx \ --saveEnginebest.engine \ --fp16--fp16开启半精度推理速度能提升 30%-50%精度损失通常在 1% 以内。转完后用 TensorRT 的 Python API 加载推理配合多线程做视频解码和推理流水线。我实测在 Jetson Xavier NX 上YOLOv8n 跑 640 分辨率能到 25-30FPS够两路 1080P 视频流实时处理。6. 避坑与排查那些训练日志不会告诉你的问题6.1 夜间和逆光场景漏检严重现象白天 mAP 能到 0.85夜间直接掉到 0.4 以下车灯眩光和路面反光导致模型把光斑当成车辆。原因训练集里夜间样本太少模型没见过这种光照分布。另外车灯本身在图像里就是高亮区域和车辆特征混淆。解决训练集里夜间样本至少占 30%用随机亮度、对比度、gamma 变换做增强。推理时加一个简单的图像预处理用 CLAHE 做自适应直方图均衡化能明显改善暗部细节。如果还不行考虑用红外摄像头或补光灯。6.2 违停判定频繁误报现象正常行驶的车辆经过禁停区域边缘时被短暂判定为违停或者相邻车道的车被误判到禁停区。原因检测框中心点判定太粗糙车辆压线行驶时中心点可能短暂落入禁停区。另外跟踪 ID 在遮挡后切换导致同一辆车被当成两辆。解决把中心点判定改成检测框与禁停区域的重叠面积比重叠超过 60% 才认为在区域内。跟踪算法用 ByteTrack 的track_buffer参数调大默认 30 帧遮挡场景可以调到 60。另外加一个冷却时间同一位置 5 分钟内只报一次违停。6.3 模型训练 loss 震荡剧烈不收敛现象训练前 20 轮 loss 下降正常之后开始剧烈震荡mAP 忽高忽低。原因学习率太大或者 batch size 太小导致梯度噪声大。另外数据标注不一致也会导致这个问题同一类目标在不同图片里标注标准不统一。解决先用lr00.001跑 10 轮看 loss 是否平稳如果平稳再逐步加到 0.01。batch 尽量往大了调实在显存不够用梯度累积。标注问题只能返工把标注规范打印出来对照检查特别是边界框的松紧程度要统一。6.4 导出 ONNX 后推理结果和 PyTorch 不一致现象PyTorch 下检测正常的图片转成 ONNX 后框的位置偏移或者置信度变化很大。原因YOLOv8 导出 ONNX 时默认把后处理也包含进去了但不同版本的 opset 对某些算子支持不一样。另外输入图像的预处理归一化、通道顺序在导出前后必须完全一致。解决导出时加simplifyTrue参数用 onnx-simplifier 简化计算图。推理时确保输入图像的预处理和训练时一致特别是 BGR 和 RGB 的顺序。如果还是不一致用opset11试试兼容性更好。6.5 多路视频流下显存溢出现象单路推理正常同时跑 4 路视频流时显存爆了程序被 kill。原因每路视频流都加载了一份模型到显存4 路就是 4 份。另外 OpenCV 的 VideoCapture 缓冲区会占用内存。解决模型只加载一次用多线程共享同一个模型实例推理时加锁。OpenCV 的cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)把缓冲区降到 1 帧减少内存占用。如果还不够用批处理推理把多路视频的帧拼成一个 batch 送模型。7. 从能跑到好用违停检测系统的三个进阶技巧第一个技巧是用主动学习迭代模型。系统上线后把置信度在 0.3-0.5 之间的检测结果自动保存下来人工复核后加入训练集。这批样本是模型最不确定的加入后对精度提升最明显。我一般每两周做一次迭代三次迭代后 mAP 能涨 5-8 个百分点。第二个技巧是分时段设置不同的判定阈值。白天车流量大停留阈值设 3 分钟夜间车少设 5 分钟。节假日和上下学时段单独配置。这些参数放在配置文件里不用改代码就能调整。第三个技巧是用检测结果反推摄像头标定。如果画面里车道线或停车位线清晰可以用检测到的车辆位置和实际尺寸反推透视变换矩阵把像素坐标转成世界坐标。这样违停判定就从“在不在框里”变成“在不在实际禁停区域内”精度提升一个档次。# 分时段阈值配置示例 import datetime def get_stay_threshold(): now datetime.datetime.now() hour now.hour weekday now.weekday() # 早晚高峰和上下学时段用短阈值 if 7 hour 9 or 16 hour 18: return 120 # 2分钟 # 夜间用长阈值 elif 22 hour or hour 6: return 300 # 5分钟 # 周末放宽 elif weekday 5: return 240 # 4分钟 else: return 180 # 默认3分钟这个函数根据当前时间返回不同的停留阈值配合前面的判定逻辑使用。实际部署时把配置放到数据库或 Redis 里支持热更新不用重启服务。这套系统我从零搭到上线跑了三个月最大的教训是模型精度只是及格线真正决定好不好用的是判定逻辑和参数调优。同样的模型阈值调好了误报率能降一半。另外别指望一次训练就到位数据迭代是常态。希望帮到你。本文还有配套的精品资源点击获取
返回列表