
简介这份数据集面向从事目标检测算法学习与智能交通应用开发的工程师和研究者围绕骑手安全监管场景提供骑手、头盔佩戴状态与车牌号码三类目标的标注图像可用于训练和测试YOLO等实时检测模型解决头盔违规识别与车辆号牌采集的实际问题。压缩包共253个文件约74.67MB其中121张jpg与3张png为原始图像126个txt为对应标注文件另含yaml配置文件与cache缓存文件目录结构清晰便于直接接入YOLO训练流程。目前已有43人学习下载。数据集覆盖骑手、戴头盔、不戴头盔、车牌号等类别标注信息完整适合作为课程设计、毕业设计或算法验证的实证素材帮助读者快速搭建检测实验、评估模型在头盔违规与车牌识别任务上的表现并理解数据标注与模型训练之间的衔接关系。1. 骑手头盔车牌数据集为什么“戴没戴”和“车牌号”要放在同一套 YOLO 里外卖骑手违规抓拍这件事单看某一帧画面其实很简单一个人、一顶头盔、一块车牌。但真把它做成能跑的 YOLO 目标检测系统麻烦立刻翻倍——头盔是“戴在头上”还是“挂在车把上”车牌是“被骑手腿部遮挡”还是“逆光过曝”这两类目标的尺度、遮挡模式、标注粒度完全不同。我见过太多团队先做一个纯头盔检测上线后发现要联动车牌才能定责于是回头重构数据集标注白干一遍。这套“骑手、戴头盔、不戴头盔、车牌号数据集”要解决的就是这个联动问题在同一套标注体系里同时给出骑手人体框、头盔状态框和车牌框让一个 YOLO 模型一次前向就输出三类结果。它适合做智慧交通、园区骑行管理、外卖平台合规抽检的工程师也适合刚接触目标检测、想找一个“类别少但场景真实”的数据集练手的人。核心难点不在模型而在标注口径和类别定义——这两件事定错后面训练再久都是玄学。2. 数据集拆解与 YOLO 标注口径三类目标怎么框才不返工2.1 骑手、头盔、车牌三类的边界定义先说结论这套数据集里最容易被低估的是“骑手”这一类。很多人以为骑手框就是人体框但实际场景里骑手坐在电动车上下半身被车体挡住如果按标准人体检测去框全身会框进大量车体导致模型学到“电动车骑手”的错误关联。我一般建议骑手框只框到可见躯干加头部不强行补全被遮挡的腿部让框紧贴可见像素。头盔类要拆成“戴头盔”和“不戴头盔”两个独立类别而不是一个“头盔”类加一个状态属性。原因很直接YOLO 的输出是类别加框状态属性需要额外分支而拆成两类后模型在分类头就能直接区分训练和部署都简单。代价是两类样本必须均衡否则“不戴头盔”这种少数类会被压制。车牌类的坑在于“车牌号”这三个字。如果标题里的“车牌号”指的是要识别字符那这是检测加识别的两阶段任务YOLO 只负责框出车牌位置字符识别要另接 OCR。如果只是检测车牌区域那 YOLO 单阶段就够。从数据集命名习惯看绝大多数“车牌号数据集”实际提供的是车牌区域框字符识别是下游的事。这一点必须在动手前确认否则标注粒度会错。类别名建议英文标签框选范围常见误标骑手rider可见躯干加头部不含被遮挡腿部框进整辆电动车戴头盔helmet_on头盔可见轮廓含帽檐把挂在车把的头盔标成戴不戴头盔helmet_off骑手头部裸露区域漏标低头、侧头场景车牌plate车牌完整矩形含边框框进车牌支架或车身贴纸2.2 从原始标注到 YOLO txt 的转换脚本拿到手的标注大概率是 VOC XML 或 LabelMe JSONYOLO 要的是每张图一个 txt每行“类别索引 中心x 中心y 宽 高”全部归一化到 0 到 1。下面这个脚本处理 VOC XML 转 YOLO txt是我常用的版本加了类别映射和越界裁剪。import os import xml.etree.ElementTree as ET # 类别顺序必须和训练时的 data.yaml 完全一致 CLASS_MAP { rider: 0, helmet_on: 1, helmet_off: 2, plate: 3, } def voc_to_yolo(xml_path, out_dir, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASS_MAP: continue # 未定义类别直接跳过避免污染训练 cls_id CLASS_MAP[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, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) if xmax xmin or ymax ymin: continue # 宽高为0的脏框丢弃 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) base os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, base .txt), w) as f: f.write(\n.join(lines))逻辑说明脚本先按 CLASS_MAP 过滤类别保证只有四类进入训练再做坐标裁剪这一步能救回不少因为标注工具拖拽越界产生的脏框最后归一化保留六位小数精度足够且文件不会过大。参数上img_w 和 img_h 必须和实际图片尺寸一致如果你用了统一缩放这里要填缩放后的尺寸否则框会整体偏移。常见错误是直接拿 XML 里的 size 字段但有些标注工具的 size 字段和真实图片对不上稳妥做法是用 PIL 或 OpenCV 读一次真实尺寸。2.3 数据集划分与 data.yaml 配置划分比例我一般用 8:1:1但这个数据集有个特殊点不戴头盔样本天然少如果随机划分验证集里可能一个不戴头盔都没有mAP 直接失真。正确做法是按类别分层抽样保证每个 split 里四类都有。下面是对应的 data.yaml。path: /data/rider_helmet_plate train: images/train val: images/val test: images/test nc: 4 names: 0: rider 1: helmet_on 2: helmet_off 3: plate参数说明path 是数据集根目录train/val/test 写相对路径即可nc 必须等于 names 的长度写错会在训练启动时报维度不匹配names 的顺序必须和转换脚本里的 CLASS_MAP 完全一致顺序错一位模型学到的就是错位标签这种错误在混淆矩阵上表现为两类互相混淆很难从 loss 上看出来。建议转换完先跑一遍校验脚本统计每个类别的框数量数量为 0 的类别要么补样本要么从 names 里去掉。3. YOLO 训练配置与参数调优从预训练模型到收敛3.1 环境搭建与预训练模型选择环境这块CUDA 版本和 PyTorch 版本对不上是最高频的翻车点。我一般用 conda 建独立环境先装 PyTorch 再装 ultralytics避免依赖冲突。预训练模型直接用官方 COCO 权重不要用自己之前在小数据集上训过的权重因为 COCO 里有人和车迁移到骑手和车牌场景比随机初始化快得多。如果显存只有 8G选 n 或 s 版本显存 16G 以上可以上 m 或 l。conda create -n yolo_rider python3.10 -y conda activate yolo_rider pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python yolo checks # 确认环境和GPU识别正常逻辑说明先建环境再装包避免污染 base 环境torch 用官方 index 保证 CUDA 匹配yolo checks 会打印 GPU 是否可用、版本号这一步能提前发现驱动问题。参数上cu118 对应 CUDA 11.8如果你的驱动只支持到 11.7就换成 cu117不要硬上。3.2 训练命令与关键参数含义训练命令本身很短但每个参数都影响收敛。下面这条是我在这个数据集上跑通的配置。yolo detect train \ data/data/rider_helmet_plate/data.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience30 \ mosaic1.0 \ mixup0.1 \ degrees5.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4 \ projectruns/rider \ nameexp1参数说明imgsz640 是速度和精度的平衡点车牌这种小目标如果低于 640 会丢细节batch16 在 8G 显存上跑 s 模型基本安全爆显存就降到 8lr0 是初始学习率0.01 对迁移学习偏大如果 loss 前几个 epoch 震荡明显降到 0.005patience30 表示 30 轮没提升就早停避免过拟合mosaic1.0 开启马赛克增强对小目标和遮挡场景帮助很大但最后 10 轮建议关掉让模型适应真实分布degrees5.0 做小角度旋转增强模拟骑手车身倾斜hsv 三个参数做颜色扰动应对不同光照下的车牌反光。3.3 训练过程监控与指标解读训练启动后重点看三个东西box_loss、cls_loss 和 mAP50。box_loss 下降但 cls_loss 不降通常是类别不均衡不戴头盔样本太少两个都降但 mAP 不涨可能是验证集划分有问题。混淆矩阵是排查类别混淆的利器如果 helmet_on 和 helmet_off 互相混淆严重说明两类视觉差异不够需要补充侧头、低头等难样本。另外注意YOLO 的混淆矩阵在类别数少时偶尔出现总和不为 1 的情况这是归一化方式导致的不影响判断趋势不用慌。yolo detect val \ modelruns/rider/exp1/weights/best.pt \ data/data/rider_helmet_plate/data.yaml \ conf0.25 \ iou0.5逻辑说明val 命令用 best.pt 在验证集上跑一遍输出每类的 P、R、mAP。conf0.25 是置信度阈值车牌这种小目标可以降到 0.2 看召回iou0.5 是 NMS 的 IoU 阈值骑手和头盔框重叠度高这个值不要调太低否则头盔框会被骑手框抑制掉。4. 推理部署与车牌联动把检测结果变成可用数据4.1 单图与视频推理的最小命令训练完的模型要落到实际抓拍流程里先跑通单图推理确认效果再上视频流。from ultralytics import YOLO model YOLO(runs/rider/exp1/weights/best.pt) results model.predict( sourcetest.jpg, conf0.25, iou0.5, imgsz640, saveTrue, ) for r in results: for box in r.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(model.names[cls_id], round(conf, 3), [round(v, 1) for v in xyxy])逻辑说明predict 返回的结果里boxes 包含类别、置信度和坐标model.names 是类别索引到名称的映射直接打印可读性好。参数上saveTrue 会把画框后的图存到 runs 目录方便肉眼检查如果做视频source 换成视频路径再加 streamTrue 逐帧处理避免一次性加载爆内存。4.2 骑手与头盔、车牌的关联逻辑检测出框只是第一步业务要的是“这个骑手戴没戴头盔、车牌是多少”。关联逻辑我一般用空间包含关系头盔框的中心点落在哪个骑手框内就归属哪个骑手车牌框和骑手框做 IoU 匹配取 IoU 最大的骑手作为归属。如果一辆车上有多个骑手按框面积排序面积大的优先。这套逻辑不复杂但边界情况多比如两个骑手挨得很近头盔框可能同时落在两个骑手框内这时候用中心点距离最近的那个。def associate(riders, helmets, plates): result [] for r in riders: rx1, ry1, rx2, ry2 r[xyxy] item {rider: r, helmet: None, plate: None} for h in helmets: hx (h[xyxy][0] h[xyxy][2]) / 2 hy (h[xyxy][1] h[xyxy][3]) / 2 if rx1 hx rx2 and ry1 hy ry2: item[helmet] h break best_iou, best_p 0, None for p in plates: iou compute_iou(r[xyxy], p[xyxy]) if iou best_iou: best_iou, best_p iou, p item[plate] best_p result.append(item) return result逻辑说明先按中心点判断头盔归属再按 IoU 判断车牌归属。参数上头盔归属用中心点而不是 IoU是因为头盔框远小于骑手框IoU 天然很低车牌归属用 IoU 是因为车牌和骑手框在空间上不一定包含但位置接近。compute_iou 需要自己实现标准交并比公式即可。4.3 车牌字符识别的衔接方式如果业务需要车牌号检测出的车牌框要裁出来送 OCR。常见做法是裁图后做透视矫正再送轻量 OCR 模型。这里不展开 OCR 训练但要注意一点YOLO 检测的车牌框如果偏大OCR 会把边框字符也读进去导致识别错误。建议裁图时向内收缩 5% 到 10%去掉边框干扰。另外逆光、污损车牌的识别率会明显下降如果业务对车牌号准确率要求高检测阶段就要把低质量车牌框过滤掉而不是指望 OCR 硬扛。5. 避坑与排查这个数据集上最容易翻车的五件事5.1 不戴头盔样本太少导致漏检现象训练完 mAP 看着还行但实际推理时几乎检测不到不戴头盔的骑手。原因不戴头盔在真实数据里本来就是少数随机划分后训练集里更少模型被多数类带偏。解决先统计每类框数量对不戴头盔做过采样或复制粘贴增强把比例拉到至少 1:3同时在 loss 里给少数类加权或者用 focal loss 思路调整。5.2 车牌框被骑手框抑制现象推理结果里车牌框大量消失调低 conf 也出不来。原因车牌框和骑手框重叠NMS 阶段 IoU 超过阈值时车牌被当成冗余框抑制。解决把 iou 阈值从 0.5 提到 0.6 到 0.7或者对车牌类单独设置 NMS 阈值更彻底的做法是训练时用类别独立的 NMSYOLO 默认就是类内 NMS如果还抑制检查是不是车牌被标成了骑手类。5.3 标注顺序错位导致类别全乱现象训练 loss 正常下降但混淆矩阵里所有类别互相混淆推理结果类别随机。原因data.yaml 的 names 顺序和转换脚本的 CLASS_MAP 不一致标签整体错位。解决转换后跑校验脚本随机抽 20 张图把 txt 画回图上肉眼确认框和类别对应names 顺序一旦确定训练中途不要改。5.4 图像尺寸不一致导致框偏移现象训练时 box_loss 居高不下推理框整体偏移。原因转换脚本用的 img_w、img_h 和实际图片尺寸不一致归一化坐标算错。解决转换前用 OpenCV 批量读一遍真实尺寸写进脚本如果数据集里图片尺寸混杂先统一 resize 再标注或者转换时按每张图的实际尺寸单独计算。5.5 验证集划分泄漏导致指标虚高现象验证集 mAP 很高上线后效果差一大截。原因同一段视频的连续帧被分到了训练集和验证集模型见过近似画面。解决按视频源或时间段划分而不是按帧随机划分如果数据来自多个摄像头按摄像头划分更稳妥。这个坑最隐蔽因为指标好看容易让人误以为模型很强。6. 进阶技巧用切片推理把车牌小目标召回拉上来车牌在这个数据集里属于典型小目标640 输入下可能只占几十个像素召回率上不去。我常用的进阶手段是切片推理SAHI 思路把原图切成带重叠的小块每块单独推理再把结果映射回原图做 NMS 合并。这样车牌在小块里相对变大召回明显提升代价是推理耗时增加。实操上不用引入完整 SAHI 库自己写切片逻辑就够。import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/rider/exp1/weights/best.pt) def sliced_infer(img, slice_size640, overlap0.2): h, w img.shape[:2] step int(slice_size * (1 - overlap)) all_boxes [] for y in range(0, h, step): for x in range(0, w, step): patch img[y:yslice_size, x:xslice_size] if patch.shape[0] 32 or patch.shape[1] 32: continue res model.predict(patch, conf0.2, verboseFalse)[0] for box in res.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() all_boxes.append([ x1 x, y1 y, x2 x, y2 y, float(box.conf[0]), int(box.cls[0]) ]) return all_boxes逻辑说明切片步长由 slice_size 和 overlap 决定overlap 取 0.2 能保证目标不被切断每块推理后把坐标加回原图偏移最后所有框送一次 NMS 合并。参数上conf 可以比整图推理低一点因为切片后目标更清晰误检不会明显增加slice_size 不建议小于 512太小会丢失上下文骑手和车牌的关联会断。验证切片推理有没有效果别只看 mAP要单独统计车牌类的召回率。我一般会准备一批车牌密集的测试图分别跑整图推理和切片推理对比车牌召回。如果召回提升不到 5 个百分点说明切片收益不大可能是车牌本身标注质量有问题先回去查标注。另外切片推理的耗时大约是整图的 3 到 5 倍实时抓拍场景要评估算力必要时只对含骑手的区域做切片而不是全图切。这套流程我踩过最深的坑是切片边界处的车牌被切成两半两边各检出一半NMS 又合并不了最后输出两个残缺框。后来把 overlap 从 0.1 提到 0.2并在 NMS 前加了一步框合并才稳定下来。做小目标检测宁可多花点推理时间也别在切片参数上省。希望帮到你。本文还有配套的精品资源点击获取