新闻详情

新闻详情

首页 / 资讯中心 / 详情

YOLOv5目标检测实战:三轮车违规停放识别数据集训练与部署

发布时间:2026/9/28 17:52:31来源:尧图网络
YOLOv5目标检测实战:三轮车违规停放识别数据集训练与部署
简介围绕YOLOv5非机动车违规停放识别任务的三轮车图像与标注数据集专供机器视觉检测模型训练使用。压缩包内包含1021张三轮车实景图片JPG与988个对应的XML标注文件构成三轮车八分类数据中的第七类tricycle7每张图片均已标注目标框整理后可直接接入YOLOv5训练流程。整包共2009个文件压缩后约87.52MB除本类外作者还按类别单独上传了自行车含山地车、公路车、共享单车等共8000张、电动车含绿源、雅迪等共8000张以及三轮车其余分类的数据包可按需组合以扩充训练集。目前已有257人学习下载适用于非机动车违停检测、三轮车车型识别、城市交通场景目标检测等算法的训练与验证也可作为毕业设计或算法竞赛的补充数据。1. YOLOv5非机动车违规停放识别先把三轮车数据集跑起来做非机动车违规停放识别卡脖子的往往是数据而不是模型。YOLOv5这种机器视觉目标检测方案检测效果一大半由标注质量决定——三轮车这类载货车型尤其容易被漏标因为车斗、车篷遮挡多一个框稍偏训练出来的模型就会在真实监控里丢目标。这份资源把三轮车数据里的第七类 tricycle7 单独打包了1021张JPEG图片加对应的XML标注文件图片以凤凰、飞鸽等品牌电动三轮车实拍为主XML里每个目标框的bndbox坐标和类别都标好了。把它喂给YOLOv5训练可以直接得到三轮车检测模型再叠加禁停区域判定就能做成非机动车违规停放识别。适合正在做城市管理、园区停车治理项目需要尽快拿到已标注数据做训练复现的工程师。2. 把已标注XML转成YOLO训练格式目录、归一化与质量检查拿到手的数据是图片加XML标注这是典型的VOC格式而YOLOv5训练要求的是和图片同名的txt文件每行一个目标类别id、归一化中心点坐标、归一化宽高。直接拿XML去训练不行需要先做一次格式转换。转换本身不难但这里面有几个细节会直接影响训练能不能收敛我习惯先把目录结构和检查脚本定下来再批量转格式。2.1 数据里有什么XML标注结构与多目标框先打开一张标注文件看结构一张图里可能标了多辆三轮车每辆车一个object节点核心信息都在bndbox里。annotation folderimages_xmls/folder filename凤凰电动三轮车_403.jpg/filename size width1920/width height1080/height depth3/depth /size object nametricycle7/name bndbox xmin412/xmin ymin236/ymin xmax987/xmax ymax763/ymax /bndbox /object /annotation注意object节点里的name字段不同标注工具写出来的可能不一样有的直接写tricycle7有的写中文品牌名。转换前先统计一遍所有XML里的name取值确认类别列表再动手别想当然。还有一点图片尺寸以size节点为准但部分标注工具会把宽高写反所以更稳的做法是转换时直接用PIL读真实图片尺寸后面代码里我会这么处理。2.2 目录规划与train/val划分先定框架再动手YOLOv5的dataset目录结构是约定俗成的train和val下各放images和labels训练时data.yaml里写四个路径。先在项目根目录建好这个结构。dataset/ images/ train/ val/ labels/ train/ val/解压后原始文件在images_xmls目录下jpg和xml混在一起先用脚本把所有图片和XML按同名配对再做train/val划分。1021张图建议抽150张做验证集差不多15%对单类检测来说够用。划分时用固定随机种子保证每次跑出来的训练集一致方便排查问题。import random, shutil from pathlib import Path random.seed(42) src_images Path(images_xmls) train_images Path(dataset/images/train) val_images Path(dataset/images/val) train_labels Path(dataset/labels/train) val_labels Path(dataset/labels/val) for d in [train_images, val_images, train_labels, val_labels]: d.mkdir(parentsTrue, exist_okTrue) stems [p.stem for p in src_images.glob(*.jpg)] random.shuffle(stems) val_stems set(stems[:150]) for stem in stems: img_src src_images / f{stem}.jpg xml_src src_images / f{stem}.xml if stem in val_stems: shutil.copy(img_src, val_images / img_src.name) shutil.copy(xml_src, Path(temp_xml_val) / xml_src.name) else: shutil.copy(img_src, train_images / img_src.name) shutil.copy(xml_src, Path(temp_xml_train) / xml_src.name)这段代码先把xml文件按划分复制到临时目录等txt转换完再统一清理。实际做的时候我会直接在一个循环里完成复制和转换避免频繁读写。划分的关键点是保证验证集里不出现训练集同图否则mAP会虚高。2.3 XML转YOLO txt归一化坐标的转换脚本转换脚本的核心是坐标归一化。VOC存的是绝对像素坐标YOLO要的是相对图片宽高的值公式是中心点x除以图片宽度、框宽除以图片宽度。图片尺寸这里用PIL读真实值不信任XML里的size节点。import xml.etree.ElementTree as ET from pathlib import Path from PIL import Image class_names [tricycle] # 单类检测三轮车 def convert_xml(xml_path, img_path, label_out): tree ET.parse(xml_path) root tree.getroot() img Image.open(img_path) img_w, img_h img.size lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_names: 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) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) cls_id class_names.index(tricycle) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_path Path(label_out) / (Path(xml_path).stem .txt) txt_path.write_text(\n.join(lines)) # 批量转换把 dataset/images/train 和 val 下的 jpg 对应的 xml 全转一遍 for img_path in list(Path(dataset/images/train).glob(*.jpg)) list(Path(dataset/images/val).glob(*.jpg)): xml_path Path(temp_xml_train) / (img_path.stem .xml) label_dir Path(dataset/labels/train) if train in str(img_path) else Path(dataset/labels/val) convert_xml(xml_path, img_path, label_dir)这段脚本有两个关键点。第一class_names顺序一旦定下来就不要改训练和推理必须共用一个类表后面第5章会专门讲顺序错位有多坑。第二归一化后加了越界裁剪因为部分XML标注框四角会超出图片边界不裁剪的话会把负数坐标写进txt训练时容易出NaN。单类检测时cls_id恒为0但保留class_names列表是为了以后扩到八类三轮车时不用重写脚本。2.4 标注质量检查跑完转换先别急着训练转换完不是直接开训先花两分钟做一轮质量检查。常见问题包括txt文件是空的、类别id超出了nc范围、框宽高是负数、图片和XML对不上。我把检查逻辑写成一个小函数。import cv2 from pathlib import Path def check_label(txt_path): img_path Path(dataset/images/train) / (txt_path.stem .jpg) if not img_path.exists(): return fmissing image: {img_path.name} img cv2.imread(str(img_path)) h, w img.shape[:2] bad [] for line in txt_path.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: bad.append(format error) continue cls, xc, yc, bw, bh map(float, parts) if cls not in (0,): bad.append(finvalid class {cls}) if bw 0 or bh 0: bad.append(non-positive size) if xc 0 or xc 1 or yc 0 or yc 1: bad.append(center out of range) return bad if bad else None for txt in Path(dataset/labels/train).glob(*.txt): result check_label(txt) if result: print(f{txt.name}: {result})检查逻辑很简单类别只能出现0框宽高必须大于0中心点必须在0到1之间。真正容易翻车的是图片和txt数量对不上少转一个XML不会报错但训练的时候YOLOv5会随机挑一张图上采样缺失的框不会报出来。所以再跑一句对比命令ls dataset/images/train/*.jpg | wc -l ls dataset/labels/train/*.txt | wc -l两边数量一致才继续。这也是机器视觉项目里最容易被忽略的一步数据集质量比模型结构更影响最终效果我每接手一批标注数据都会先做这一轮检查。3. 训练配置与超参数取舍单类三轮车模型怎么训不翻车数据准备好之后下一步是训练。YOLOv5的训练入口很成熟真正需要花心思的是data.yaml、模型配置和超参数这三件事。这份资源是tricycle7单类数据只有1021张图训练策略和全量八类不一样我一般按“单类先收敛再考虑扩展”的思路走。3.1 data.yaml与类别映射单类还是八类训练前先在项目里建data.yaml指向刚才划分好的目录。train: dataset/images/train val: dataset/images/val nc: 1 names: 0: tricycle这里有个选择要提前做是直接识别三轮车这一个类别还是把八个分类都作为独立类别。我的建议是如果手头只有tricycle7这一份数据就先合并成单类“三轮车”。八类品牌三轮车的差别主要在车头和车贴对违规停放识别来说没有业务价值强行分成八类反而会把数据打散每类只有几百张模型容易过拟合。等以后拿到全量八个分类的数据再把nc改成8names列表一次性补齐。3.2 模型规模选型yolov5s还是yolov5m模型规模的选择要看数据量和部署目标这不是玄学是显存和召回率之间的权衡。配置depth_multiplewidth_multiple适用场景yolov5s0.330.50单类1000张图训练快边缘部署友好yolov5m0.670.75八类全量数据精度更高yolov5l1.01.0大分辨率监控画面但显存要求高如果你做的是违停识别漏检比误检更麻烦。漏一辆三轮车就是一次违规事件没抓到误检顶多多推一条告警。所以我的习惯是单类用yolov5s打底如果验证集recall低于0.9再换yolov5m。1021张图像对yolov5s来说大概率能收敛没必要一上来就用大模型。3.3 训练命令与超参数哪些参数值得先动训练命令保持标准写法重点看几个参数。python train.py --data data.yaml --cfg models/yolov5s.yaml \ --weights yolov5s.pt --batch-size 16 --epochs 100 \ --img 640 --cache --patience 20--cache是把图片预加载到内存1021张小图完全没压力训练时省去磁盘IO等待。--patience 20表示验证集连续20轮不提升就早停避免挂机空跑。--img 640是训练分辨率三轮车在监控画面里通常占比较大640够用不建议为提速直接降到320框会小到难以辨认。超参数里我一般只动三个。lr0默认0.01小数据集可以降到0.005因为样本少学习率大了容易震荡。mosaic默认1.0对小数据集它既是增强也是干扰三轮车经常停在画面边缘mosaic会把车切半我习惯改成0.5。warmup_epochs默认3.0保持不动。训练时加一行--multi-scale让模型适应不同距离下三轮车大小变化这个对路侧监控特别有用。3.4 训练过程监控results.txt怎么看训练过程中YOLOv5会在runs/train目录下生成results.txt每行一列指标看这一列就够。指标含义期望表现box_loss框回归损失持续下降最终低于0.05obj_loss置信度损失下降后趋平mAP0.5IoU阈值0.5下的平均精度三轮车单类95%以上mAP0.5:0.95严格IoU下的平均精度通常比mAP0.5低一截我判断过拟合的标准是box_loss还在降但val mAP不再涨甚至开始掉。出现这个信号就直接取best.pt别等epochs跑完。YOLOv5训练的默认输出里有best.pt和last.pt两个权重best.pt是按验证集mAP选的部署时只用best.pt。4. 推理端到违停判定置信度、IoU与停留时间三段式训练拿到best.pt之后整个任务才完成一半。检测模型输出的是“画面里有三轮车”而违停识别要的是“这辆车停在禁停区域且持续了一段时间”。这一段是项目能落地的关键也是机器视觉项目里最容易被低估的部分。4.1 加载权重做目标检测torch.hub还是detect.py快速验证权重可以直接用detect.py但要在自己的服务里集成我一般用torch.hub加载。import torch model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt, sourcelocal) results model(frame, size640) df results.pandas().xyxy[0] print(df[[xmin, ymin, xmax, ymax, confidence, class]])sourcelocal很关键它让torch.hub直接读本地YOLOv5源码不每次联网拉仓库。size参数必须和训练时的--img保持一致不一致会导致框偏移。pandas接口返回的DataFrame里xmin/ymin/xmax/ymax是像素坐标confidence是置信度class是类别id。YOLOv5的前处理和后处理在这个接口里已经封装好了包括letterbox填充和NMS所以这里不需要自己实现后处理。4.2 禁停区域判定中心点法与面积占比法判定违停的第一步是把检测框和禁停区域做空间关系判断。最粗暴的做法是算检测框中心点是否落在禁停多边形内但实际效果不好三轮车车斗长中心点可能落在禁停区外车头却已经压线。我一般用面积占比法。from shapely.geometry import Polygon roi Polygon([(100, 200), (500, 200), (500, 800), (100, 800)]) # 禁停区域人工标定 def is_violation(row, roi, overlap_thr0.3): box Polygon([ (row.xmin, row.ymin), (row.xmax, row.ymin), (row.xmax, row.ymax), (row.xmin, row.ymax) ]) inter box.intersection(roi).area return inter / box.area overlap_thr violations [row for _, row in df.iterrows() if is_violation(row, roi)]overlap_thr是核心参数表示检测框面积的30%进入禁停区域就算违规。这个值需要按监控视角调俯视角度建议0.5平视角度建议0.3。因为平视时车身拉长只有车头进禁停区也算违规俯视时车辆完整可见框的30%已经说明车停进来了。还有一个隐藏坑禁停区域是在原始1080p画面上人工圈的推理时输入模型的是640分辨率两者坐标系不同。解决方法是把ROI坐标按比例缩放到640坐标系或者在原图上检测后再用原分辨率做判断。4.3 停留时间判定用IoU做轻量跟踪单帧判断违停会把正常驶过禁停区的车辆也报上来所以必须加时间维度。不引入DeepSORT这类重跟踪方案只做IoU帧间匹配就够了。tracks {} lost_frame_count {} def match_track(box, tracks, iou_thr0.5): best_tid, best_iou None, 0 for tid, prev_box in tracks.items(): iou compute_iou(box, prev_box) if iou best_iou: best_tid, best_iou tid, iou return best_tid if best_iou iou_thr else None def update(detections, tracks, dwell_thr30): for det in detections: tid match_track(det, tracks) if tid is None: tid len(tracks) tracks[tid] det lost_frame_count[tid] 0 else: tracks[tid] det lost_frame_count[tid] 0 if detect_time[tid] - first_seen[tid] dwell_thr: alarm(tid)IoU匹配阈值0.5是经验值三轮车相邻帧移动量小IoU不会太低。dwell_thr根据业务定义我做过的大多数项目设30秒也就是车辆在禁停区停留超过30秒才告警。如果设太短堵车场景会连续误报设太长违停证据采集又不够实时。连续丢失超过10帧的track直接丢弃防止车辆驶离后还留着旧状态重复告警。4.4 视频流实时调用OpenCV读帧与多线程实际项目里输入不是单张图片而是RTSP视频流直接用OpenCV读帧加检测逻辑。import cv2 cap cv2.VideoCapture(rtsp://your_camera_stream) fps cap.get(cv2.CAP_PROP_FPS) frame_interval 3 # 每3帧检测一次 frame_id 0 while True: ret, frame cap.read() if not ret: break frame_id 1 if frame_id % frame_interval ! 0: continue results model(frame, size640) rows results.pandas().xyxy[0] violations [row for _, row in rows.iterrows() if is_violation(row, roi)]这里有个实用技巧视频流检测不需要每帧都跑模型三轮车是慢速目标每3帧检测一次完全够。中间的帧直接用上一次的检测结果。监控摄像头常会有解码卡顿直接在主循环里跑模型会拖垮实时性成熟做法是抓帧线程和检测线程分离用队列解耦。后面第6章会讲怎么把帧率压到边缘设备能接受的范围。5. 避坑与排查五个让数据集白标的典型问题这五个问题是我在标注数据训练YOLOv5过程中实际踩过的每一个都实打实浪费过时间。5.1 类别id错位预测框全对不上的翻车现场现象训练loss正常下降但推理时预测框要么全漏要么框的位置完全不对和一个三轮车都对不上。原因转换脚本里class_names的顺序在训练前后不一致。比如转换时把tricycle7放在了索引0而训练完推理时又改了类表顺序模型输出的类别编号和类名对不上。解决固定一份class_names列表训练、推理、评估共用同一个配置文件。我在转换脚本里加一行校验assert class_names.index(tricycle) 0, class order changed!从那以后我都是把类别表写成独立的classes.yaml三个环节都从它读。这种低级错误不报错只会让你的模型看起来完全失效。5.2 CUDA out of memory显存不够先别降分辨率现象训练跑到一半报RuntimeError: CUDA out of memory进程直接退出。原因显存不够通常不是显卡太差而是batch-size和img-size同时开太高。特别是--cache把图片全缓存到显存之后显存占用会再涨一截。解决先降batch-size8GB显存跑yolov5s时batch-size设8--img设640。不要一上来就降分辨率分辨率降了框就变小对三轮车这种中尺寸目标不友好。如果batch-size降到4还爆检查缓存模式把--cache去掉让它走磁盘读取。5.3 mAP高但漏检训练尺寸和推理尺寸不一致现象训练时mAP0.5已经到0.95放到真实监控画面上检测近处的三轮车能检到远处的小目标全部漏掉。原因训练时--img 640推理时为了帧率调到416甚至320。模型没见过这么小的目标尺度远端三轮车的特征全被压没了。解决推理尺寸和训练尺寸保持一致。如果监控画面大、目标远近差异明显就用--img 640训练配--multi-scale让模型见过不同尺度推理时再保持640。帧率不够优先走跟踪和隔帧检测不要牺牲分辨率。5.4 验证集mAP虚高重复图片泄漏现象训练时mAP一路冲到0.98但拿到新的现场图片一测recall明显对不上这个精度。原因数据集里有重复图片划分train/val时又没去重同一张图出现在两边模型等于直接背答案。解决先算图片MD5去重再划分数据集。这个数据集在说明里也标了“极个别重复”所以不能跳过。去重后重新训练验证mAP会回归正常。import hashlib from pathlib import Path seen {} for img_path in Path(images_xmls).glob(*.jpg): md5 hashlib.md5(img_path.read_bytes()).hexdigest() if md5 in seen: print(fduplicate: {seen[md5]} {img_path.name}) else: seen[md5] img_path.name5.5 部署后速度暴跌权重没导出就直接上线现象训练环境里单帧推理200ms部署到边缘设备后变成800ms实时性完全不可用。原因直接把PyTorch的best.pt扔到边缘设备跑还是FP32精度。边缘设备的算力本来就比训练显卡弱再叠加PyTorch框架的运行时开销速度自然崩。解决导出成优化格式再部署。这一步放第6章细说核心是ONNX加半精度必要时转TensorRT。上线之前先量一轮FPS达不到项目要求的先优化格式别急着改模型参数。6. 进阶验证三个指标验收与边缘部署加速模型训练完别直接丢到现场先用一段真实监控视频做验收。我自己习惯统计三个指标精确率、召回率、误报率。在测试视频里人工标注出每一段“三轮车进入禁停区并停留超过30秒”的事件跑完算法逐帧对比。精确率低说明误报多调高conf_thres和overlap_thr召回率低说明漏检先看置信度阈值是否过高再看训练尺寸。置信度阈值是zui直接的旋钮我一般这样标定。conf_thres表现0.25偏灵敏检得多但误报会变多0.40均衡建议先用这个值0.50偏严格漏检风险上升边缘部署的加速手段里性价比最高的是导出ONNX加半精度。YOLOv5自带导出脚本一条命令搞定然后在推理代码里指定用半精度跑。如果设备支持TensorRT再转一步引擎文件FPS通常还能翻一倍。监控视频里的三轮车移动慢隔3帧检测一次的方案配合轻量跟踪基本能覆盖整个违停判定流程。最后提一个我自己的血泪教训有一次训练好的模型在测试集上表现很好换了一个角度相近的现场摄像头后误报率飙升排查了半天发现是那天下午阳光角度变了禁停区域的标定多边形偏移到了阴影边缘。从那以后我每次换摄像头、换机位都强制走一遍完整流程重新截帧标定ROI确认推理分辨率再验证一次三个指标。机器视觉项目里数据集、超参数、ROI标定这些环节环环相扣任何一个细节没锁住后面都得用加倍的时间来还。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Codex 通过 cc-switch 连接大模型辅助编程:config.toml 配置与验证 2026/9/28 18:36:48

Codex 通过 cc-switch 连接大模型辅助编程:config.toml 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
2026年Cursor使用指南:从入门到高效开发,TaoToken统一Key接入与settings.json配置实战 2026/9/28 18:36:48

2026年Cursor使用指南:从入门到高效开发,TaoToken统一Key接入与settings.json配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
企业级 AI Agent Harness 工程落地 5 步走:从 config.toml 骨架到里程碑验收 2026/9/28 18:36:48

企业级 AI Agent Harness 工程落地 5 步走:从 config.toml 骨架到里程碑验收

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
agent-skill 与 subagent 到底怎么分?用 TaoToken 统一 Key 跑通两种调用链 2026/9/28 18:36:48

agent-skill 与 subagent 到底怎么分?用 TaoToken 统一 Key 跑通两种调用链

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Cursor 遍历方法配 TaoToken:settings.json 骨架与验证动作 2026/9/28 18:36:47

Cursor 遍历方法配 TaoToken:settings.json 骨架与验证动作

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
深度解析:双审时代,一站式学术辅助平台 Okbiye 如何重构毕业论文写作逻辑 2026/9/28 18:36:41

深度解析:双审时代,一站式学术辅助平台 Okbiye 如何重构毕业论文写作逻辑

随着国内高校毕业论文双审机制全面落地,学位论文的审核标准已经发生根本性变化。过去很长一段时间,毕业生只需要重点关注论文重复率,只要查重结果达标,基本就能顺利进入送审环节。而在当前的审核体系下,重复率、AIGC 生…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉