基于YOLOv5与E_bicycle7数据集的非机动车违规停放识别实战

发布时间:2026/10/2 14:51:05
基于YOLOv5与E_bicycle7数据集的非机动车违规停放识别实战 简介本资源面向从事机器视觉与目标检测的开发者及学生聚焦非机动车违规停放场景下的电动车识别任务提供YOLOv5可直接训练使用的已标注数据。包内为电动车数据集中的第七类——雅迪电动车共864张实拍图片并配有858个XML标注文件压缩包整体约100MB文件总数1722个jpg用于模型训练与验证xml对应目标框与类别信息可直接接入YOLOv5训练流程。该数据属于更大规模非机动车数据集的一部分同类还涵盖自行车、电动车、三轮车等多个细分车型分类清晰、标注规范便于扩展多类别检测实验。目前已有2330人学习下载适合用于课程设计、毕业项目或算法验证帮助读者省去繁琐的采集与标注环节快速搭建违规停放识别模型并完成训练与评估。1. 非机动车违规停放识别从 E_bicycle7 数据集到 YOLOv5 落地消防通道被电动车堵死、人行道被横七竖八的共享单车占满、小区单元门口停了一排电动三轮——这些场景每天都在发生靠人工巡查根本盯不过来。用机器视觉识别非机动车违规停放核心思路是把车停在哪转化为目标检测问题先检测出画面里所有非机动车再通过区域判定逻辑判断它是否停在禁停区内。YOLOv5 是目前这条路径上工程化最成熟的方案之一推理速度快、部署链路短、社区踩坑记录多。E_bicycle7_images_xmls 这个已标注数据集正好覆盖了电动车、自行车等非机动车的标注需求省去了从零标数据的苦力活。这套方案适合有基本 Python 能力、想做智慧社区或园区安防的工程师也适合想拿一个完整项目练手 YOLOv5 全流程的开发者。2. 数据集拆解与 YOLOv5 训练环境搭建2.1 E_bicycle7_images_xmls 的数据结构长什么样拿到一个标注数据集第一件事不是急着训练而是搞清楚它的目录结构和标注格式。E_bicycle7_images_xmls 从命名就能看出两个关键信息images 目录存图片xmls 目录存 XML 格式的标注文件。这是典型的 Pascal VOC 格式每张图片对应一个同名 XML里面记录了目标类别和边界框坐标。常见做法是先跑一遍统计脚本看看类别分布和框的尺寸分布。这一步能帮你判断数据集是否均衡、有没有需要过滤的异常标注。import os import xml.etree.ElementTree as ET from collections import Counter xml_dir E_bicycle7_images_xmls/xmls img_dir E_bicycle7_images_xmls/images # 统计类别分布和每张图的框数量 class_counter Counter() boxes_per_image [] missing_pairs [] for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() objs root.findall(object) boxes_per_image.append(len(objs)) for obj in objs: name obj.find(name).text.strip() class_counter[name] 1 # 检查图片和标注是否配对 img_name xml_file.replace(.xml, .jpg) if not os.path.exists(os.path.join(img_dir, img_name)): missing_pairs.append(xml_file) print(类别分布:, dict(class_counter)) print(平均每图框数:, sum(boxes_per_image) / len(boxes_per_image)) print(最大框数:, max(boxes_per_image)) print(缺失配对:, missing_pairs[:5])这段脚本做了三件事统计每个类别的标注数量、计算每张图的平均目标数、检查图片和 XML 是否一一对应。如果某个类别只有个位数标注训练时大概率学不好需要考虑补充数据或做数据增强。缺失配对的情况也要提前处理否则训练时会直接报错。注意XML 里的 filename 字段不一定和实际文件名一致以实际文件名为准做配对检查。2.2 把 VOC 格式转成 YOLOv5 需要的 txt 格式YOLOv5 不认 XML它需要每张图片对应一个 txt 文件每行格式是class_id x_center y_center width height坐标全部归一化到 0~1。转换脚本不难写但有几个边界情况容易翻车。import os import xml.etree.ElementTree as ET # 类别映射根据实际数据集的类别修改 classes [e_bicycle, bicycle, tricycle] def convert_bbox(size, box): VOC的xmin,ymin,xmax,ymax转YOLO的x_center,y_center,w,h归一化 dw, dh 1.0 / size[0], 1.0 / size[1] x_center (box[0] box[2]) / 2.0 * dw y_center (box[1] box[3]) / 2.0 * dh w (box[2] - box[0]) * dw h (box[3] - box[1]) * dh return x_center, y_center, w, h def convert_xml(xml_path, out_dir, img_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w_img int(size.find(width).text) h_img int(size.find(height).text) lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in classes: continue cls_id classes.index(cls_name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 边界裁剪防止坐标越界 xmin max(0, min(xmin, w_img)) ymin max(0, min(ymin, h_img)) xmax max(0, min(xmax, w_img)) ymax max(0, min(ymax, h_img)) # 过滤掉宽高为0的无效框 if xmax xmin or ymax ymin: continue xc, yc, bw, bh convert_bbox((w_img, h_img), (xmin, ymin, xmax, ymax)) lines.append(f{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) if lines: out_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines)) # 执行转换 os.makedirs(labels, exist_okTrue) for xml_file in os.listdir(E_bicycle7_images_xmls/xmls): if xml_file.endswith(.xml): convert_xml( os.path.join(E_bicycle7_images_xmls/xmls, xml_file), labels, E_bicycle7_images_xmls/images )转换逻辑本身不复杂但有几个参数和边界必须注意。classes列表的顺序决定了 class_id训练时的 data.yaml 必须和它完全一致否则类别全乱。坐标裁剪那两行是后悔药——有些标注框会超出图片边界不裁剪的话归一化后坐标可能小于 0 或大于 1YOLOv5 虽然不报错但训练效果会受影响。宽高为 0 的框直接丢弃这种通常是标注时误操作产生的。2.3 conda 环境配置与 YOLOv5 源码拉取环境配置这一步血泪经验是不要用最新版的 PyTorch也不要混用 pip 和 conda 装同一个包。我一般用 conda 建一个 Python 3.8 或 3.9 的环境PyTorch 选 1.12 到 2.0 之间的稳定版本。# 创建conda环境 conda create -n yolo_bike python3.9 -y conda activate yolo_bike # 安装PyTorch以CUDA 11.8为例根据自己显卡驱动调整 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 拉取YOLOv5源码 git clone https://github.com/ultralytics/yolov5.git cd yolov5 # 安装依赖 pip install -r requirements.txt装完之后跑一行python -c import torch; print(torch.cuda.is_available())确认输出是 True。如果是 False先检查显卡驱动版本和 CUDA 版本是否匹配别急着往下走。requirements.txt 里锁定的包版本是经过验证的不要手动升级某个包很容易出现版本冲突导致训练中途报错。2.4 data.yaml 配置与首次训练跑通YOLOv5 训练需要一份 data.yaml告诉它图片在哪、类别有几个、类别名是什么。# data_bike.yaml train: /path/to/images/train val: /path/to/images/val nc: 3 names: [e_bicycle, bicycle, tricycle]nc是类别数names的顺序必须和转换脚本里的classes完全一致。图片目录建议按 train/val 分好比例大概 8:2 或 9:1。如果数据集本身没有划分用脚本随机拆分。# 首次训练先用小epoch跑通流程 python train.py \ --data data_bike.yaml \ --weights yolov5s.pt \ --epochs 50 \ --batch-size 16 \ --img-size 640 \ --project runs/train \ --name bike_v1第一次训练不要直接上 300 epochs先用 50 轮跑通确认 loss 在下降、验证集指标在涨。--weights yolov5s.pt是加载预训练权重做迁移学习小数据集上这比从零训练效果好得多。--img-size 640是默认值如果图片里目标普遍偏小可以提到 1280但显存占用会翻倍。--batch-size根据显存调8G 显存跑 640 尺寸大概能到 16不够就降到 8。3. 违规停放判定从检测框到业务逻辑3.1 为什么检测到车不等于违规停放YOLOv5 只负责告诉你画面里有没有非机动车、在哪。但违规停放是一个业务判断需要额外的空间逻辑。常见做法是在摄像头画面里预先画好禁停区域多边形或矩形当检测框的中心点落在禁停区内且持续超过一定时间才判定为违规。这个逻辑听起来简单但实际部署时会遇到几个问题。摄像头角度不同禁停区在画面里的坐标完全不同每路摄像头都要单独标定。检测框的中心点可能因为遮挡而偏移导致误判。短暂经过禁停区的车辆不应该被判定为违停需要加时间窗口过滤。3.2 用多边形区域做违停判定的代码实现import cv2 import numpy as np from shapely.geometry import Point, Polygon # 定义禁停区域多边形顶点按实际画面标定 no_parking_zone Polygon([(100, 300), (500, 300), (500, 600), (100, 600)]) # 时间窗口连续N帧在禁停区内才判定为违停 VIOLATION_FRAMES 30 violation_counter {} def check_violation(detections, frame_id): detections: list of (track_id, x_center, y_center, cls_name) 返回当前帧的违停目标列表 current_violations [] for track_id, xc, yc, cls_name in detections: point Point(xc, yc) if no_parking_zone.contains(point): violation_counter[track_id] violation_counter.get(track_id, 0) 1 if violation_counter[track_id] VIOLATION_FRAMES: current_violations.append((track_id, cls_name)) else: # 离开禁停区计数清零 violation_counter[track_id] 0 return current_violations这段代码的核心逻辑是用 shapely 的 Polygon.contains 判断检测框中心点是否在禁停区内再用一个计数器做时间过滤。VIOLATION_FRAMES这个参数需要根据实际帧率调整——如果摄像头 25fps30 帧就是 1.2 秒太短容易误报太长会漏报。我一般会设 2 到 3 秒对应的帧数。注意检测框中心点不一定代表车辆的实际位置尤其是车辆被部分遮挡时。如果精度要求高可以用检测框底边中点代替中心点更接近车辆与地面的接触位置。3.3 多路视频流下的推理性能调优单路视频跑 YOLOv5s 在 GPU 上轻松到 50fps 以上但如果你要同时处理 8 路、16 路摄像头性能就会成为瓶颈。几个实用的调优方向调优手段效果代价降低推理尺寸到 416速度提升约 40%小目标漏检率上升跳帧推理每 3 帧处理 1 帧速度提升 3 倍快速移动目标可能漏检转 TensorRT速度提升 2~3 倍需要额外转换步骤用 yolov5n 替代 yolov5s速度提升约 2 倍精度下降 3~5 个点实际项目中我一般先用跳帧推理把吞吐量拉上来再根据漏检情况决定是否要上 TensorRT。如果业务对实时性要求不高比如每 5 秒分析一帧那跳帧是最省事的方案。4. 避坑与排查训练和部署中最容易翻车的五个点4.1 训练 loss 不下降mAP 一直卡在低位现象训练跑了 100 轮box_loss 和 cls_loss 波动但不下降mAP0.5 始终在 0.1 以下。原因最常见的原因是 data.yaml 里的nc和names与转换脚本不一致导致类别标签错乱。其次是图片路径配置错误YOLOv5 找不到图片时会静默跳过实际参与训练的样本极少。解决先检查 data.yaml 的类别数和名称再确认 train/val 路径下的图片数量。可以在 train.py 里加--verbose看每轮实际加载了多少张图。如果图片数远小于预期就是路径问题。4.2 验证集 mAP 正常但实际推理效果差现象训练日志里 mAP0.5 到了 0.85但拿实际场景的视频去测漏检和误检都很严重。原因训练集和实际场景的数据分布不一致。E_bicycle7 数据集里的图片可能是特定角度、特定光照条件下拍的而你的摄像头角度、光照完全不同。另一个常见原因是验证集和训练集来自同一批数据验证集指标虚高。解决从实际摄像头截取一批图片人工标注后作为测试集用这个测试集评估模型。如果指标明显下降说明需要补充实际场景的训练数据。至少要从每路摄像头截 50 到 100 张图覆盖不同时段和天气。4.3 推理时检测框抖动严重现象视频里同一个目标相邻帧的检测框位置跳来跳去导致违停计数频繁清零。原因YOLOv5 本身没有跟踪能力每帧独立检测框的微小变化是正常的。但如果没有做跟踪关联同一个目标在相邻帧可能被分配不同的 track_id计数器就乱了。解决在检测后加一个轻量跟踪器比如 ByteTrack 或 DeepSORT。YOLOv5 官方仓库里就有 ByteTrack 的集成示例。加上跟踪后同一个目标在整个视频片段里保持同一个 ID违停计数才能正确累积。4.4 模型部署到边缘设备后帧率骤降现象在服务器 GPU 上跑得好好的模型部署到树莓派或 Jetson 上帧率掉到个位数。原因边缘设备的算力和显存远不如服务器。直接拿 PyTorch 模型在 CPU 上推理yolov5s 在树莓派 5 上大概只有 1~2fps。解决导出 ONNX 或 TensorRT 模型用 ONNX Runtime 或 TensorRT 做推理。树莓派 5 上建议用 yolov5n 加 ONNX Runtime能到 5~8fps。如果必须用 yolov5s考虑加一个 Coral USB 加速棒。导出命令是python export.py --weights best.pt --include onnx然后用 onnxruntime 加载推理。4.5 违停判定误报率高把正常停放的车辆也判成违停现象系统上线后大量正常停放在划线区域内的车辆被判定为违停。原因禁停区域的多边形标定不准确或者摄像头有轻微位移导致标定区域偏移。另一个原因是检测框中心点落在禁停区边缘但车辆实际大部分在区域外。解决标定禁停区时留出一定的缓冲边界不要贴着划线边缘画。判定逻辑从中心点在内改为检测框与禁停区的交并比超过阈值比如 IOU 0.3 才判定。这样能有效减少边缘误判。5. 进阶技巧用 TensorRT 加速和置信度调优把误报压下去模型训练完之后真正决定落地效果的是推理端的后处理参数。YOLOv5 默认的置信度阈值是 0.25NMS 的 IOU 阈值是 0.45这两个值在非机动车违停场景下往往需要调整。置信度阈值调高到 0.5 以上能过滤掉大部分低质量的误检框但会漏掉一些被遮挡的目标。我的经验是如果场景里非机动车比较密集置信度设 0.4 左右比较平衡如果场景空旷、目标清晰可以提到 0.6。NMS 的 IOU 阈值在车辆密集停放时建议降到 0.3 到 0.35避免相邻车辆被合并成一个框。TensorRT 加速的流程分三步先把 best.pt 导出为 ONNX再用 trtexec 转成 TensorRT engine最后用 TensorRT 的 Python API 加载推理。导出 ONNX 时注意加上--dynamic参数支持动态 batch size否则部署时只能固定 batch。# 导出ONNX python export.py --weights runs/train/bike_v1/weights/best.pt --include onnx --dynamic # 用trtexec转TensorRT engineFP16精度 trtexec --onnxbest.onnx --saveEnginebest.engine --fp16 --workspace4096转完之后用--fp16精度在大多数场景下精度损失不到 1 个点但速度能提升 2 倍以上。如果显卡支持 INT8还可以做量化校准速度再翻一倍但需要准备校准集流程更复杂。验证 TensorRT 模型精度是否达标不能只看 mAP还要在实际视频上跑一遍对比 PyTorch 模型和 TensorRT 模型的检测结果差异。我一般会抽 100 帧逐帧对比两个模型的检测框数量和位置如果差异超过 5%就要检查导出过程是否有问题。最后说一个我踩过的坑TensorRT engine 是和显卡型号绑定的在 A 卡上转的 engine 拿到 B 卡上跑不了。部署时要么在目标设备上现场转要么确保所有设备同型号。这个限制在批量部署时特别容易忽略等到上线才发现就来不及了。希望帮到你。本文还有配套的精品资源点击获取