基于YOLOv5的电动自行车头盔佩戴识别:从数据集标注到部署实战
简介这份资源面向计算机视觉方向的本科生与研究生提供一套可直接运行的电动自行车头盔佩戴识别检测方案适用于毕业设计、期末大作业或课程实践。项目基于YOLOv5算法构建包含完整源码与训练好的模型文件代码注释详尽新手也能快速理解检测流程与训练逻辑。压缩包共187个文件约134.13MB其中55个Python脚本承载数据加载、模型训练与推理主流程22个yaml配置文件定义数据集与网络结构7个pt权重文件可直接用于检测另有png效果图、js前端页面及Dockerfile等部署辅助文件目录结构清晰。目前已有385人学习下载。资源附带项目手册与说明文档读者可据此完成环境搭建、模型推理与结果可视化并参考前端页面实现检测结果展示快速复现一个导师认可的高分项目。1. 电动自行车头盔佩戴识别从毕设选题到能跑通的检测系统电动自行车头盔佩戴识别检测说白了就是给摄像头装一个自动抓拍未戴头盔的大脑。它要解决的核心问题很具体在早晚高峰的非机动车道上从密集的车流里把骑行人框出来再判断他头上有没有头盔。这件事在一盔一带政策落地后成了刚需物业、园区、交管辅助场景都有真实需求也是 yolov5 训练自己数据集里最典型的二分类落地案例。很多人冲着高分毕设四个字来但真正卡住他们的从来不是模型结构而是数据集怎么标、头盔这种小目标怎么调参、模型文件怎么在普通显卡上跑出可用帧率。这篇笔记就按我实际做过的路径把源码、模型文件、训练参数和部署踩坑一次讲透适合要交毕设的学生也适合想把这套东西接到自己项目里的工程师。2. 头盔识别为什么选 YOLOv5 而不是别的检测框架2.1 从任务特性反推框架选型头盔佩戴识别本质是一个小目标 二分类 实时性的组合任务。说它是小目标是因为头盔在整张图里占比往往不到 5%尤其是远景摄像头拍到的骑行人头部区域可能只有几十个像素。说它是二分类是因为我们通常只关心戴了和没戴两种状态不需要区分头盔品牌、颜色、款式。说它要实时是因为路口抓拍场景下30 帧的视频流你至少得跑到 15 帧以上才有实用价值。YOLOv5 在这三个维度上的平衡是它成为主流选择的原因。它的 Anchor 机制对小目标有专门的多尺度检测头P3 层特征图专门负责小物体它的单阶段结构保证了推理速度它的工程化程度高数据增强、超参数进化、模型导出这些环节都有现成脚本。相比之下Faster R-CNN 精度可能略高但速度跟不上YOLOv8 更新但对毕设场景来说生态资料反而没 v5 全遇到问题不好搜。所以基于 yolov5 实现头盔佩戴识别这个选题选型上是站得住的。2.2 数据集构建头盔识别的标注规范数据集是这套系统里最容易被低估的部分。我见过太多人直接拿网上的头盔数据集开训结果模型在自家场景里翻车。原因很简单不同场景的摄像头角度、光照、头盔样式差异极大通用数据集覆盖不了你的实际分布。自己标数据时类别设计有两种常见方案。第一种是两类helmet戴头盔和no_helmet未戴头盔直接对头部区域做检测。第二种是三类person、helmet、no_helmet先检测人再判断头部状态。我一般推荐第一种因为两类方案标注量小、模型收敛快而且推理时不需要做人和头盔的关联匹配后处理简单。缺点是当画面里人很多时头部区域密集容易漏检这时候可以配合人体检测做级联。标注工具用 labelImg 或 X-AnyLabeling 都行导出 YOLO 格式的 txt。每条标注是类别id 中心x 中心y 宽 高坐标都归一化到 0~1。这里有个血泪经验头盔框一定要贴着头部轮廓标不要框到肩膀也不要只框头盔顶。框太大会让模型学到人头盔的混合特征框太小会丢失头盔边缘信息两种情况都会让验证集 mAP 虚高但实际场景拉胯。数据划分按 8:1:1 走训练/验证/测试。如果样本量少于 2000 张建议把验证集比例提到 0.15因为小数据集上验证集太小会导致指标波动大你根本判断不出模型到底有没有变好。2.3 目录结构与配置文件落地YOLOv5 的数据配置靠一个 yaml 文件驱动。假设你的项目根目录是helmet_detection标准结构如下helmet_detection/ ├── datasets/ │ ├── images/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ └── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── data/ │ └── helmet.yaml ├── models/ │ └── yolov5s_helmet.yaml └── runs/helmet.yaml的内容# 数据集路径建议写绝对路径避免相对路径踩坑 path: /home/user/helmet_detection/datasets train: images/train val: images/val test: images/test # 类别数两类方案就是 2 nc: 2 # 类别名称顺序必须和标注时的 id 对应 names: 0: helmet 1: no_helmet这里有个容易翻车的点names的顺序必须和标注时用的类别 id 严格一致。如果你标注时 0 是 no_helmet、1 是 helmet但 yaml 里写反了训练照样能跑loss 照样下降但推理结果会完全颠倒——戴头盔的判成没戴。这种错误不看可视化结果根本发现不了属于典型的黑匣子式 bug。模型配置文件yolov5s_helmet.yaml直接复制官方的yolov5s.yaml把nc改成 2 即可。如果你想用更大的模型提升小目标精度可以换成yolov5m.yaml或yolov5l.yaml但要注意显存和速度的权衡。我实测在 1080p 输入下yolov5s 在 RTX 3060 上能跑到 60 FPSyolov5m 大概 35 FPSyolov5l 只有 20 FPS 左右。头盔识别这种场景yolov5s 通常够用除非你的摄像头特别远、头盔特别小。3. 训练参数怎么调头盔小目标的超参数实战3.1 从预训练权重开始的迁移学习头盔识别数据集通常不大从零训练几乎不可能收敛到可用精度。标准做法是加载 COCO 预训练权重做迁移学习。YOLOv5 官方提供了 yolov5s.pt 等权重文件直接指定即可python train.py \ --weights yolov5s.pt \ --cfg models/yolov5s_helmet.yaml \ --data data/helmet.yaml \ --epochs 100 \ --batch-size 16 \ --imgsz 640 \ --device 0 \ --workers 8 \ --project runs/train \ --name helmet_exp1参数逐个说清楚。--weights yolov5s.pt加载预训练权重这是收敛速度的关键没有它你可能要训 300 轮才看到效果。--epochs 100是训练轮数头盔这种二分类任务通常 80~150 轮就收敛了看验证集 mAP 曲线如果连续 20 轮不涨就可以停。--batch-size 16受显存限制8G 显存跑 640 分辨率大概能到 16显存不够就降到 8但 batch 太小会让 BN 层统计不稳定建议配合--accumulate做梯度累积。--imgsz 640是输入分辨率头盔是小目标理论上 1280 能提升精度但速度会掉一半以上640 是精度和速度的平衡点。--workers 8是数据加载线程数设成 CPU 核心数就行设太大反而会因为线程切换开销变慢。--device 0指定第一块 GPU多卡用0,1。3.2 针对小目标的三个必调参数头盔识别的难点在小目标有三个参数直接决定你能不能检出远处的骑行人。第一个是--anchor。YOLOv5 默认的 anchor 是基于 COCO 数据集聚类的对头盔这种小目标偏大。正确做法是用你的数据集重新聚类 anchorpython utils/autanchor.py --data data/helmet.yaml --imgsz 640 --device 0这个脚本会输出 9 组 anchor 尺寸把它们替换到模型 yaml 里的anchors字段。我实测重新聚类后小目标召回率能提升 5~8 个百分点。注意聚类时--imgsz要和训练时一致否则 anchor 尺度对不上。第二个是--hyp。YOLOv5 的超参数在data/hyp.scratch.yaml里头盔场景我一般改这几个mosaic: 0.5马赛克增强概率太高会让小目标被裁切得太碎、scale: 0.3缩放增强幅度小目标经不起大比例缩放、fl_gamma: 1.5focal loss 的 gamma缓解正负样本不平衡因为大部分头部区域是戴头盔的no_helmet 是少数类。第三个是--img-weights。这个参数控制训练时多尺度训练的概率默认 0.0 关闭。头盔识别建议开到 0.5让模型适应不同距离的目标尺度对远景小目标提升明显。3.3 训练过程监控与早停判断训练启动后重点看runs/train/helmet_exp1/下的几个文件。results.csv记录了每轮的 loss 和 mAPresults.png是可视化曲线。判断训练是否正常看三条线train/box_loss应该稳定下降如果震荡剧烈说明学习率太大或 batch 太小val/box_loss如果先降后升说明过拟合了该早停metrics/mAP_0.5是主指标头盔识别通常能到 0.85 以上算合格0.92 以上算优秀。早停用--patience 30意思是验证集指标连续 30 轮不提升就停止。这个值别设太小小数据集上指标波动大设 10 可能会在正常波动时误停。训练完成后最优权重在runs/train/helmet_exp1/weights/best.pt最后轮次的权重是last.pt。部署用 best.pt继续训练用 last.pt。4. 模型推理与部署从 detect.py 到实际场景4.1 用 detect.py 快速验证效果训练完第一件事是拿测试集跑一遍确认模型没训废python detect.py \ --weights runs/train/helmet_exp1/weights/best.pt \ --source datasets/images/test \ --img-size 640 \ --conf-thres 0.4 \ --iou-thres 0.45 \ --save-txt \ --project runs/detect \ --name helmet_test--conf-thres 0.4是置信度阈值低于这个值的框会被丢弃。头盔识别建议设 0.4~0.5设太低会有一堆误检设太高会漏掉远处的小目标。--iou-thres 0.45是 NMS 的 IoU 阈值控制重叠框的合并头盔密集场景可以降到 0.4 减少漏检。--save-txt会把检测结果存成 txt方便你统计准确率。跑完后去runs/detect/helmet_test/看可视化图片。重点检查三类错误远景小头盔漏检、相似颜色误检比如白色帽子被当成头盔、密集场景框重叠。这三类错误分别对应不同的调参方向漏检调低 conf 或重新聚类 anchor误检补充负样本重叠调 NMS 阈值。4.2 导出 ONNX 与 TensorRT 加速PyTorch 模型直接部署速度不够生产环境一般导出 ONNX 或 TensorRT。导出 ONNXpython export.py \ --weights runs/train/helmet_exp1/weights/best.pt \ --include onnx \ --img-size 640 640 \ --batch-size 1 \ --opset 12--opset 12是 ONNX 算子集版本12 兼容性最好别用太新的版本很多推理引擎还不支持。--batch-size 1是部署时的批大小如果你要做多路视频可以设成实际并发数。导出 TensorRT 需要先装 TensorRT 和 pycuda然后python export.py \ --weights best.pt \ --include engine \ --img-size 640 640 \ --batch-size 1 \ --device 0 \ --half--half开启 FP16 半精度速度能提升 1.5~2 倍精度损失通常在 1% 以内头盔识别这种任务完全可以接受。TensorRT 引擎和硬件绑定换显卡要重新导出这是部署时容易忽略的点。4.3 视频流推理的工程化改造detect.py 是单张图片推理实际场景要处理视频流。核心改造是把推理循环抽出来配合 OpenCV 的 VideoCaptureimport cv2 import torch import numpy as np # 加载模型注意 map_location 防止 GPU 模型加载到 CPU 报错 model torch.hub.load(./, custom, pathbest.pt, sourcelocal) model.conf 0.4 model.iou 0.45 cap cv2.VideoCapture(rtsp://your_camera_stream) # 设置缓冲区为 1避免处理旧帧导致延迟累积 cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) while cap.isOpened(): ret, frame cap.read() if not ret: break # YOLOv5 接受 RGB 输入OpenCV 读的是 BGR必须转换 img_rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results model(img_rgb, size640) # 渲染结果 annotated np.squeeze(results.render()) cv2.imshow(helmet, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的关键在CAP_PROP_BUFFERSIZE设为 1。默认缓冲区会缓存多帧导致你处理的是几秒前的画面实时抓拍场景下这是致命的。另外torch.hub.load的sourcelocal参数必须加否则会尝试联网下载内网环境直接卡死。如果要接多路摄像头别用多线程各自加载模型显存会爆。正确做法是一个模型实例配一个推理队列多路视频帧进队列单线程批量推理。批量大小根据显存调RTX 3060 上 640 分辨率大概能 batch 8。5. 头盔识别部署避坑五条踩坑记录5.1 验证集 mAP 很高但实际场景全错现象训练完 mAP_0.5 到 0.95信心满满拿去实测结果画面里戴头盔的全判成没戴。原因最常见的是类别 id 映射反了yaml 里 names 顺序和标注时不一致。其次是数据集分布和实际场景差异太大比如训练集全是白天正脸实测是夜间侧脸。解决先拿一张训练集里的图跑推理如果训练集都对但实测错就是分布问题补充实际场景数据重训。如果训练集也错检查 names 顺序用model.names打印确认。5.2 小目标漏检严重远处骑行人完全检不到现象近处的人能检出超过 15 米的骑行人一个框都没有。原因默认 anchor 尺寸偏大P3 检测头对小目标特征提取不足。另外输入分辨率 640 时远处头盔可能只有 10 几个像素特征图上下采样后信息基本丢失。解决重新聚类 anchor把输入分辨率提到 960 或 1280或者在数据增强里加copy-paste小目标增强。如果硬件允许换 yolov5m 或加一个 P2 检测头专门处理超小目标。5.3 推理速度远低于预期1080p 只能跑 5 帧现象论文里写的 60 FPS实际部署只有 5 FPS。原因没导出 TensorRT用的还是 PyTorch 原生推理。或者视频解码占用了大量 CPUGPU 在等数据。解决导出 TensorRT FP16 引擎速度能提升 3~5 倍。视频解码用硬解码OpenCV 的CAP_PROP_HW_ACCELERATION或者直接用 GStreamer 管道。另外检查是不是每帧都在做 cvtColor 和 resize这些操作能放到 GPU 上就放 GPU。5.4 夜间和逆光场景误检率飙升现象白天正常晚上路灯下把树影、反光当头盔。原因训练集缺少夜间样本模型没学过暗光下的特征分布。逆光时头部区域过曝头盔和背景对比度消失。解决补充夜间和逆光数据至少占训练集的 20%。数据增强里加hsv_v调整亮度范围开到 0.6。如果条件允许用红外摄像头做夜间补充但要注意红外图像和可见光图像的域差异最好分开训练或做域适应。5.5 模型文件换机器后加载失败现象在自己电脑上跑得好好的 best.pt拷到服务器上torch.load报错。原因PyTorch 版本不一致或者模型保存时用了 GPU 而加载环境只有 CPU。另外 YOLOv5 的torch.hub.load依赖目录结构单独拷一个 pt 文件可能找不到模型定义。解决加载时加map_locationcpu跨版本用weights_onlyFalse。部署时把整个 YOLOv5 仓库一起拷过去或者导出 ONNX/TensorRT 这种自包含格式彻底摆脱 PyTorch 环境依赖。6. 把头盔识别做到能交付的几个进阶技巧模型训出来只是起点真正决定这套系统能不能用的是后处理和数据闭环。分享几个我实际项目里验证过的技巧。第一个是基于跟踪的投票机制。单帧检测难免有抖动同一辆车这帧判戴、下帧判没戴抓拍系统就没法用。做法是接一个轻量跟踪器比如 ByteTrack给每个骑行人分配 ID累计连续 N 帧的检测结果做多数投票只有超过 70% 的帧判为 no_helmet 才触发抓拍。这样误报率能降一个数量级。代价是引入 2~3 帧延迟但抓拍场景完全能接受。第二个是置信度分级输出。别只输出戴/没戴把置信度也带上。conf 0.8 直接判定0.5~0.8 标记为待人工复核 0.5 丢弃。这样既保证了自动化率又给人工留了兜底。实际部署时待复核的样本正好是难例定期拿回来重新标注加入训练集形成数据闭环模型会越用越准。第三个是输入分辨率自适应。固定 640 在近景浪费算力、远景不够用。可以根据画面中检测到的人体框大小动态调整如果上一帧检测到的头部框平均小于 32 像素下一帧切到 1280 输入大于 64 像素就切回 640。这样在保证远景精度的同时不牺牲近景速度。实现上用一个滑动窗口统计最近 30 帧的框尺寸加个滞回区间防止频繁切换。第四个是模型量化到边缘设备。如果要在 RK3568 或树莓派上部署PyTorch 模型跑不动需要量化成 INT8。YOLOv5 支持导出 RKNN 格式流程是 PyTorch → ONNX → RKNN量化时准备 200~500 张校准图覆盖各种光照场景。INT8 量化后模型体积缩小 4 倍速度提升 2~3 倍精度损失控制在 2% 以内。注意量化后的模型对输入归一化方式敏感预处理必须和量化时一致否则精度会崩。最后说个习惯每次训练完我都会把这次的数据集版本、超参数、mAP、实际场景抽检结果记到一个表格里。头盔识别这种任务调参的收益往往不如补数据但如果你不记录就永远不知道上次那个 0.93 的 mAP 是怎么来的也没法复现。我吃过这个亏一个效果很好的模型因为没记录 hyp 配置后来怎么调都调不回去只能重训。希望这些经验能帮你少走点弯路把这个方向真正做扎实。本文还有配套的精品资源点击获取