
简介面向采用YOLOv5进行机器视觉识别的开发者这份已标注数据集子包聚焦非机动车违规停放场景包含三轮车第一类tricycle1的九百五十七张图片及对应标注文件适用于智慧城市与交通管理项目中的违规停放检测、模型微调或算法效果评估。压缩包共一千九百一十个文件其中九百五十七张JPG图片与九百五十三个XML标注文件总体积约八十二MB图片与标注一一对应可直接用于模型训练与验证目前已有二百九十五人学习或下载。三轮车数据集整体划分为八个类别此子包为其中第一类方便按需与其他类别组合扩充。数据已预先标注完成免去人工标注环节标注信息包含目标框与类别标签既可作为理解非机动车违规停放检测流程的入手指南也可作为更大规模识别任务的基础子集。1. 三轮车违停识别第一步957张已标注图够不够用把 yolov5 和非机动车违规停放做到能落地真正卡人的不是模型结构是数据。这份三轮车 tricycle1 数据集一共 957 张实拍图片每张带同名 XML 标注属于三轮车八个分类里的第一类拿过来就能直接进入 YOLOv5 训练流程。它解决的是违停识别最头疼的标注从哪来问题不用自己画框不用清洗格式按 VOC 结构组织好训练、验证、部署能一次打通。适合正在做非机动车违停识别、需要三轮车检测基线模型的从业者也适合刚上手 YOLOv5、想用真实标注数据跑通全流程的新手。下面按我实际拆这套数据的顺序从目录结构、标注转换、训练参数讲到违停判定和排坑。2. 拆解 tricycle1 数据集XML 标注结构与格式转换拿到资源第一步别急着训练先把目录和标注文件看明白。这份三轮车数据的图片是 jpg标注是同名 xml命名规则很简单三轮车_751.jpg 对应三轮车_751.xml。这种一一对应的命名是后期批量处理和划分训练集的前提。我一般会先写一段脚本统计总数、抽查标注内容确认没有空标注和损坏文件再进训练流程。如果这一步省略后面训练时经常出现找得到图找不到标签的怪问题排查起来比现在多花三倍时间。2.1 文件命名与 XML 关键字段文件名编号从三位数到四位数都有前缀统一是三轮车拉个列表能看到 957 张图片全部有配对 xml。这里有个容易忽略的细节文件名里的中文在 Linux 环境下是 UTF-8 编码直接 glob 匹配没问题但在 Windows 下用 GBK 编码写脚本有可能出现乱码导致找不到配对文件。我的习惯是训练前把所有文件重命名为纯数字编号顺便把中文路径兼容问题一次解决。特别是后续要合并八个分类一起训练时文件名前缀各不相同统一编号能避免不同子类之间的命名冲突。XML 标注是典型的 Pascal VOC 结构每个文件里包含这样几类关键字段字段路径含义使用注意filename/filename图片文件名要跟实际 jpg 对应否则找不到图width/size/width图片宽度像素归一化分母错了框全偏height/size/height图片高度像素同上name/object/name目标类别名tricycle1 里的值可能是三轮车或 tricycle打开一个 XML 确认bndbox/object/bndboxxmin/ymin/xmax/ymax左上右下坐标未归一化这里最关键的是 bndbox 的四个值它们是原始像素坐标YOLOv5 训练要的是归一化后的中心点坐标和宽高所以 XML 不能直接喂进去必须先转换。另外注意 name 字段整个资源是三轮车八个子分类的数据集每类有自己的类别名这份 tricycle1 只是第一类。后续如果要合并八个分类一起训类别名和 id 映射表必须提前统一不然后面改映射会非常痛苦我就是吃过这个亏才把它写在前面的。2.2 VOC 标注转 YOLO 格式一次跑通的转换脚本转换逻辑不复杂就是把 XML 的 bndbox 像素坐标换算成 0~1 的归一化值。我常用的脚本长这样import os import xml.etree.ElementTree as ET # 类别表tricycle1 按单类处理索引从 0 开始 # 后续合并八个分类时这里扩展成完整类别列表即可 classes [tricycle] def convert(xml_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: print(fskip unknown class: {name} in {xml_path}) continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 转成中心点坐标加宽高再归一化到 0~1 x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # YOLO 格式class_id x_center y_center width height lines.append(f{classes.index(name)} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) # 批量转换遍历 tricycle1 的 xml 目录 for xml_file in os.listdir(tricycle1/xmls): if not xml_file.endswith(.xml): continue base os.path.splitext(xml_file)[0] convert( os.path.join(tricycle1/xmls, xml_file), os.path.join(tricycle1/labels, base .txt), )逻辑说明脚本先取 size 字段定分母再遍历每个 object 取 bndbox算中心点与宽高后归一化最后按 YOLO 格式写 txt。参数说明里有两个坑一是 classes.index(name) 决定了类别 id多个类时一定要和后续 data yaml 里的 names 顺序一致否则类别错位模型会拿三轮车的数据去拟合错误标签二是浮点保留 6 位足够超过这个精度对 loss 没有实际帮助。转换完我建议随机打印三个 txt 的前几行人工核对坐标是否在 0~1 之间顺便确认没有 NaN 或者负数。提示转换脚本跑完后把 labels 目录和 images 目录放成同级yolov5 训练时才能自动按同名文件去 labels 里找标注。2.3 训练集与验证集划分957 张图不算多我习惯按 8:2 划训练集和验证集。有个原则划分前先按场景视频段分组如果同一段视频的连续帧既进了 train 又进了 valmAP 会虚高现场复用性反而差。简单做法是随机划分后用文件名序列检查连续编号的图片是否跨集发现连续 5 帧以上散布在两边就重新划过。这一步不写进训练脚本但直接影响你对模型真实水平的判断。划分脚本本身不复杂用 random.shuffle 加一个固定随机种子保证可复现。划完之后把 train.txt 和 val.txt 各存一份训练时用。另一个细节是模型训练完做验证时val 集合里要保留少量难例比如遮挡严重、逆光的图片不然验证结果一派祥和部署到现场就露馅。957 张图分出来的 val 集大约 190 张足够看出问题所在。3. YOLOv5 训练三轮车检测模型环境、参数与训练命令数据准备好之后训练本身是流水线活。yolov5 这个仓库维护比较成熟训练、验证、导出一条命令走完重点其实是参数选择和数据组织方式。这份三轮车数据集是单类检测目标尺寸偏大、特征明显训练门槛不高但环境配置和参数含义还是要理清楚不然报错都不知道往哪个方向查。3.1 环境准备与依赖安装环境上我建议直接用 Python 3.8 以上版本PyTorch 按显卡的 CUDA 版本装。我的习惯是先装 torch再装 yolov5 的 requirements避免依赖解析冲突# 拉取 yolov5 官方仓库常见做法是 clone 主分支 git clone https://github.com/ultralytics/yolov5.git cd yolov5 # 先装 PyTorch再装剩余依赖 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install -r requirements.txt参数说明--index-url 指定 CUDA 12.1 的 wheel 源如果你的显卡驱动是 CUDA 11.8 就换成 cu118装错版本的表现是 torch.cuda.is_available() 返回 False训练时直接掉回 CPU。requirements.txt 里包含 opencv、matplotlib、seaborn 这些库缺哪个补哪个不建议一次全部升级到最新yolov5 对某些库版本有隐性要求升级后偶发报错反而不好排查。机器上没 GPU 也能训用 CPU 就是慢957 张单类数据 CPU 训 100 轮大概要十几个小时有 GPU 的话一张 3060 大约 40 分钟就能跑完。3.2 数据集配置与训练参数调优yolov5 训练前要把数据描述文件写好这个 yaml 决定了训练集、验证集路径和类别数。我的 tricycle.yaml 长这样# 路径相对于 yolov5 仓库根目录 train: data/tricycle/images/train val: data/tricycle/images/val nc: 1 names: [tricycle]参数说明train 和 val 指向存放 jpg 的目录注意这里指的是图片目录yolov5 会自动去同名 labels 目录找 txt所以 labels 目录必须和 images 目录同级且前缀一致。nc 是类别数单类就是 1。names 顺序必须和转换脚本里的 classes 顺序一致这一点错位了检测结果就会张冠李戴。然后是训练命令。最省事的配置是先用 COCO 预训练权重做迁移学习python train.py \ --data data/tricycle.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --img 640 \ --device 0 \ --name tricycle_run参数说明--weights 用 yolov5s.pt 是平衡速度和精度的选择三轮车目标大、特征明显s 模型足够如果追求更高精度可以换 yolov5m但显存和推理时间都会涨。--img 640 是输入分辨率训练和推理要保持一致现场摄像头画面如果是 1080p推理时也会缩到 640。--batch-size 根据显存调16 在 8GB 显存上比较稳显存小就降到 8。训练过程重点看两个指标val 的 mAP0.5 是否在 60 轮后进入平台期以及 loss 曲线是否持续下降。三轮车这类目标不算难一般 80~100 轮就能收敛。超参数这块yolov5 默认的 hyp.scratch-low.yaml 在单类检测上通常够用。如果发现小目标漏检我一般会动两个值mosaic 保持 1.0 别关它让模型看到更多裁剪组合再用 --hyp 指定一份改过的 yaml 把 hsv_h、hsv_s 调低一点因为三轮车颜色是品牌识别的特征色相增强太猛会把颜色信息洗掉。这个属于玄学区域调之前先跑一版默认参数有明确短板再动不要一上来就改一堆超参改了也不知道是哪个起的作用。4. 违停判定后处理从检测框到违规结论训练出模型只是第一步违停识别要的是这辆车停在了不该停的位置这个结论。yolov5 输出的是检测框违规判定必须自己在后处理里实现。这里的设计直接决定误报率同一个模型装到两套后处理逻辑里效果能差出一倍。4.1 禁停区域划定矩形框还是多边形实际场景里禁停区域很少是正矩形人行道、消防通道、弯道附近都是不规则多边形。我一般用 OpenCV 先画多边形标定禁停区存成顶点数组。判定时用 pointPolygonTest 判断点是否落在区域内import cv2 import numpy as np # 在画面坐标系里手工标定的禁停多边形顶点像素坐标 # 顶点顺序要按顺时针或逆时针统一不然判定结果会混乱 forbidden_zone np.array( [[210, 380], [520, 360], [560, 720], [180, 750]], dtypenp.int32, ) def point_in_zone(px, py, polygon): # 返回 0 表示点在多边形内部或边界上 return cv2.pointPolygonTest(polygon, (px, py), False) 0逻辑说明pointPolygonTest 的第三个参数传 False 表示只返回位置关系不计算距离速度快适合视频帧实时处理。参数说明里要注意坐标系的统一如果检测框坐标来自模型输出的原始像素坐标多边形顶点也要用同一分辨率下的坐标标定现场部署时摄像头安装角度变了多边形要重新标这是最容易漏的一步。我见过有人标定完禁停区换了个摄像头分辨率所有判定全部失效就是因为坐标系没跟着换算。4.2 违规判定逻辑中心点判定与多帧确认单帧检测直接判违规会带来抖动问题目标被遮挡一帧、置信度波动一下画面里违停告警就会闪。我的做法是先取检测框中心点做落区判定再加多帧确认窗口def judge_violation(frame_id, dets, forbidden_polygons, history): results [] for det in dets: # det 结构: x1, y1, x2, y2, conf, cls x1, y1, x2, y2, conf, cls det cx (x1 x2) / 2 cy (y1 y2) / 2 in_zone any( cv2.pointPolygonTest(poly, (cx, cy), False) 0 for poly in forbidden_polygons ) # 中心点落在禁停区作为候选条件置信度再卡一道 if in_zone and conf 0.35: history[frame_id % 30] 1 else: history[frame_id % 30] 0 # 连续 5 帧命中才输出违规滤掉瞬时误检 if sum(history) 5: results.append((cx, cy, conf)) return results逻辑说明中心点判定比整框 IoU 判定更稳因为框的边界受遮挡影响大中心点反而稳定。多帧确认的窗口按 30 帧1 秒滚动连续 5 帧命中才告警能滤掉大部分遮挡导致的闪烁误报。参数说明里 conf 阈值 0.35 是个经验值训练集质量好可以放宽到 0.3场景复杂就提到 0.45。另外如果现场画面里三轮车会移动建议叠一个简单的 IoU 跟踪记录同一目标的中心点位移位移大于一个车身宽度时判定为行驶中不触发违停。这个逻辑对车停在禁停区和车刚好路过禁停区的区分非常关键不做区分的话早晚高峰的误报能把值班室淹了。注意多帧确认的窗口长度要和摄像头帧率匹配15 帧的摄像头用 30 帧窗口就是 2 秒告警延时会比 1 秒的方案慢一倍。实时性要求高的场景窗口缩到 10 帧以内。5. 三轮车检测训练与部署避坑五条血泪经验这部分每一条都是我实际跑这套数据时踩过的按现象 → 原因 → 解决列出来遇到同症状可以直接对症下药。有些问题在官方文档里根本找不到属于典型的数据和场景耦合出来的坑。5.1 训练与部署中的五个常见问题现象一训练跑起来了但 mAP 一直是 0val 阶段没有检测框输出。 原因XML 转 txt 时类别 id 和 names 列表对不上或者有人把 bndbox 的 xmin、ymin、xmax、ymax 直接当 x_center、y_center 写进去了归一化后的值大量大于 1。 解决转换完先 cat 一个 txt 看内容所有值应在 0~1 之间再跑一段校验脚本把 txt 的框画回原图肉眼比对偏移情况。画框这一步花五分钟能省后面一小时的排查我现在每次转换完都强制做。现象二训练到 30 轮左右 CUDA out of memory。 原因batch-size 16 在显存 6GB 的卡上超出上限或者开了 --cache 把图片全量缓存进显存957 张图在高分辨率下缓存量不小。 解决batch-size 降到 8 或 4img 尺寸从 640 降到 512如果必须用 640就把 --cache 改成 --cache ram缓存到内存而不是显存。3080 级别的显卡跑这套单类数据batch 32 都毫无压力瓶颈通常出在显存小的卡上。现象三模型训练正常但检测框明显偏大把旁边的电动车也框进来了。 原因XML 里 bndbox 标注本身框得松标注员把车身外围的阴影或背景也划进去了。这种问题在人工标注的数据里很常见框松紧不一是常态。 解决这是数据质量问题不是训练问题。我一般会统计所有框的宽高比找出偏离均值超过两个标准差的样本重点检查再统一对 bndbox 做 2%~5% 的内缩。框稍微收紧后mAP 反而会涨因为 IoU 计算时正样本的框更准了。现象四现场部署后行驶中的三轮车频繁误报违停。 原因单帧检测没有运动判定只要检测框中心点落在禁停区就触发而过路的车在画面里停了一两帧就会被算法当成停放。监控场景里车速慢一帧一帧看每帧都有框人眼觉得明显在动算法却不知道。 解决按第 4 章的多帧确认逻辑连续 5 帧以上中心点稳定在禁停区才告警再叠加中心点位移阈值帧间位移超过车身宽度的 1/3 判定为运动目标直接跳过违规判断。这两层叠加之后误报基本能压到可接受范围。现象五同一套模型换了个摄像头角度漏检率明显上升。 原因训练数据视角比较单一大多为平视或略俯视新摄像头是高位俯视三轮车的投影形状和训练样本差异大。模型学到的是训练视角下的特征分布换视角就是换了一个分布。 解决收集新角度的图片做增量训练不需要重新标 957 张挑 200 张新视角图片标注后在现有模型权重上继续 fine-tune 50 轮。这是成本最低的兜底方案。另外推理时开启 --augment 做左右翻转和尺度增强能稍微弥补视角差异但会加推理时间实时性要求高的场景慎用。6. 模型验证的一线手法mAP 之外还要看什么模型训完val 集上 mAP 看着不错不等于现场能用。我验证这套三轮车模型的固定流程有三步第一步用脚本跑一遍全部验证集图片把每个漏检和误检的框画出来存图人工翻一遍确认漏检集中在哪些场景——比如逆光、车身被遮挡、远处小目标第二步跑一段现场禁停区的视频统计 10 分钟内误报的次数和每次持续时间误报率超过每 3 分钟一次就不合格第三步把模型导出成 TensorRT 或 ONNX 做推理时间测试确认帧率满足现场摄像头并发路数。这里有个实用技巧yolov5 的 val.py 会输出 confusion matrix 图重点看 tricycle 这一类对角线上的值。如果误检那一列有值说明把背景或者其他物体认成了三轮车如果漏检行有值说明三轮车被当成背景忽略。这两类问题处理方法完全不同前者要调置信度阈值后者要补数据或增强样本。我还习惯用 --save-txt 把检测结果落成文件写一段统计脚本算检测框中心点在画面里的分布热力图如果大量框集中在画面角落多半是训练样本里目标位置分布不均模型有位置偏好。从那以后我每次拿到新数据集都强制先跑一遍标注合法性与一致性校验再做转换和训练最后用现场视频做验收测试这套流程让翻车率低了很多。这份 tricycle1 的 957 张标注图作为三轮车检测的起点够你完整走一遍 yolov5 违停识别链路后续八个分类合并、品牌细分的扩展空间也是现成的。希望帮到你。本文还有配套的精品资源点击获取