简介面向智慧交通场景的电动车骑行人员头盔佩戴检测系统基于YOLOv9目标检测框架提供完整源码、训练好的模型权重及评估曲线。系统适用于毕业设计、课程设计及实际项目开发支持自定义数据集训练与迁移学习能够解决道路安全监控中头盔佩戴状态的自动识别问题帮助用户在真实道路场景下高效部署。资源包共一百八十八个文件涵盖八十余个Python脚本、三十个YAML配置、测试图片、编译文件以及三个模型权重文件等总大小约六十九点三二MB目录结构清晰便于按模块查阅。压缩包内还附有详细运行教程、评估曲线和结果统计数据可对照文档在主流Python环境中完成环境配置、数据准备、模型训练与检测推理全流程。目前已有238人学习下载适合计算机、人工智能、电子信息等相关专业的学生或开发者作为毕业设计、课设或项目预研的完整参考方案。1. 不只是跑通 YOLOv9这份头盔检测资源先要看清三个前提做道路电动车头盔佩戴检测最花时间的从来不是模型训练本身而是数据对不对、路径全不全、参数适不适合你的显卡。这套基于 YOLOv9 的 Python 检测源码集成了官方训练流程、可运行的train_dual.py/detect_dual.py脚本、配套权重与评估曲线拿到的第一反应不该是直接点运行而是先确认三件事预训练权重是哪个版本、自带的 yaml 数据集是不是头盔数据、你的机器显存能承受多大的 batch。搞清楚这三点你才能真正把这份资源用成一套能复现的检测系统。适合做毕业设计快速起步也适合刚接触目标检测的工程师当工程模板改。2. 环境复现与目录结构train_dual.py、detect_dual.py、yolov9-s.pt 各司其职2.1 先把 Python 环境装到不会翻车的状态这份项目是标准的 PyTorch 工程依赖集中在requirements.txt里。我一般建议用 Anaconda 建独立环境不要直接装到 base 环境里否则后面装 torch 版本冲突时你会很被动。conda create -n yolov9 python3.9 -y conda activate yolov9 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里我额外解释两点。第一Python 版本选 3.9 而不是 3.11。YOLOv9 官方代码对 Python 3.10 以上存在一些隐性的 torch 算子兼容问题碰上了报错不好查3.9 是官方测试最多的版本。第二-i后面跟清华源是因为默认 PyPI 源在国内慢且容易中断这个不是你网速的问题是源的问题换源能少等十分钟以上。装完后验证一下 torch 能不能调用 GPUpython -c import torch; print(torch.__version__, torch.cuda.is_available())如果输出True说明 CUDA 可用如果是False说明你装的是 CPU 版 torch后续--device 0会直接报错。这时候要重装pip uninstall torch torchvision -y pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118提示NVIDIA 驱动版本建议在 450 以上CUDA 版本装 11.8 或 12.1 都行。这一步别省训练一半才发现显卡没调用上那才是真的浪费时间。2.2 理清目录里各文件的作用再动手环境配好后先别急着跑花两分钟认清项目的几个关键文件到底干什么。很多新手翻车就是因为把训练脚本和推理脚本的参数搞混了。文件/目录作用关键参数train_dual.py训练入口支持从零训练和微调--weights、--data、--batch、--epochsdetect_dual.py推理入口图片/视频/摄像头均可--weights、--source、--conf-thresdata/banana_ripe.yaml示例数据集配置train/val 路径、类别名称models/detect/yolov9-c.yaml模型结构定义depth_multiple、width_multiplehyp.scratch-high.yaml超参数配置lr、mosaic、hsv 增强参数reparameterization.ipynb重参数化转换部署前必跑加载训练权重并合并分支这里最容易被忽略的是reparameterization.ipynb这个 Notebook。YOLOv9 训练时网络带有辅助分支推理时如果不先做重参数化合并速度会慢一截。后面我会专门讲这个文件怎么用现在你只需要知道训练完生成 best.pt 后不要直接拿去部署要先过一遍重参数化。2.3 模型选型和权重文件的坑项目默认用yolov9-s.pt对应配置文件是models/detect/yolov9-c.yaml。这里有个容易迷惑的地方yolov9-s.pt是官方在 COCO 上预训练好的权重你下载后直接把它当作初始权重微调即可。但要注意s是小模型c 是全尺寸模型e是超大模型——如果你显存只有 6G用yolov9-e.pt连训练都会爆显存。我的经验是6G 显存用yolov9-sbatch 设 48G 显存用yolov9-sbatch 设 812G 以上再考虑yolov9-c。别一上来就追求高精度先把流程跑通再换大模型提升指标。3. 从 banana_ripe.yaml 换成头盔数据YOLO 标注格式与数据集配置的四个注意点3.1 先看清自带数据集不是头盔打开data/banana_ripe.yaml你会看到train: data/images/train val: data/images/val names: 0: very-ripe 1: immature 2: mid-ripe这是香蕉成熟度检测的数据集。如果你直接用这份配置训练头盔检测模型只会学会分辨香蕉的成熟阶段和头盔没有半点关系。下载资源里如果自带的 best.pt 是在这 3 个类别上训出来的你拿它去测头盔图片输出的一定是混乱的分类结果。拿到资源的第一步是确认权重和数据集的类别定义是否匹配你的场景。头盔检测需要的是helmet和no_helmet两个类别或者更细分为person、helmet、no_helmet取决于你的需求。3.2 YOLO 标注格式与 labelimg 使用头盔数据集需要准备 YOLO 格式的标注文件。每个.jpg图片对应一个同名.txt文件里面每一行代表一个目标class_id x_center y_center width height注意四个坐标值都是归一化到 0~1 的浮点数不是像素坐标。我用 labelimg 标注时会先在PascalVOC模式下标矩形框然后再批量转成 YOLO 格式。其实 labelimg 直接支持 YOLO 模式保存时选择YOLO格式即可坐标自动归一化不需要手动算。标注时的遗漏提醒电动车骑行人员的头部无论是否戴头盔都要标注出来。如果只标注戴了头盔的模型会把「没戴头盔」当作背景测试时永远检测不到违规目标。正确做法是0 x_center y_center width height # 戴了头盔 1 x_center y_center width height # 没戴头盔类别 0 是 helmet类别 1 是 no_helmet。哪个在前面无所谓但必须和 yaml 文件里的 names 顺序保持一致。3.3 自建 yaml 文件的四个注意点参考 banana_ripe.yaml在data/下新建helmet.yamltrain: D:/helmet_dataset/images/train val: D:/helmet_dataset/images/val names: 0: helmet 1: no_helmet第一个注意点train 和 val 路径写绝对路径。相对路径在你自己电脑上能跑换一台机器就报 FileNotFoundError。第二个注意点names里的类别顺序必须和标注文件里的 class_id 严格对应如果你标注时 0 是 no_helmet 而这里 0 是 helmet训练不会报错但测试时输出的标签全错。第三个注意点每个类别图片数量要在 300 张以上且正负样本比例别过分失衡我建议头盔和未戴头盔的样本比例控制在 1:1 到 1:1.5 之间。第四个注意点val目录不能和train目录重叠否则评估曲线会虚高测出来的 mAP 不具备参考价值。3.4 数据校验训练前用脚本查一遍标注自建数据集最容易翻车的是标注文件写错坐标越界、类别 id 超范围都是重灾区。训练前先跑一段校验代码import os def check_labels(img_dir, label_dir, num_classes2): for f in os.listdir(label_dir): if not f.endswith(.txt): continue img_file f.replace(.txt, .jpg) if not os.path.exists(os.path.join(img_dir, img_file)): print(f[WARN] {img_file} 缺少对应图片) with open(os.path.join(label_dir, f), r) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: print(f[ERROR] {f} 某行不是5个字段: {line}) continue cls, cx, cy, w, h int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if cls num_classes: print(f[ERROR] {f} 类别ID越界: {cls}) if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): print(f[ERROR] {f} 坐标越界: {line}) check_labels(helmet_dataset/images/train, helmet_dataset/labels/train)逻辑很简单逐行校验每行 5 个字段、类别在合法范围内、归一化坐标不越界。出现任何[ERROR]都先修好再训练不然后面 loss 异常你都不知道该查数据还是查模型。4. 训练参数别照抄epochs、batch-size、close-mosaic 背后的取舍逻辑4.1 用命令行方式训练而不是改脚本项目提供了两种训练方式直接运行train_dual.py或命令行传参。我强烈建议用命令行因为参数语义更清晰且不会因为改了脚本后忘记改回来而影响后续推理。python train_dual.py \ --weights yolov9-s.pt \ --cfg models/detect/yolov9-c.yaml \ --data data/helmet.yaml \ --hyp hyp.scratch-high.yaml \ --epochs 100 \ --batch-size 8 \ --device 0 \ --img 640 \ --close-mosaic 15 \ --name helmet_run这套参数的核心含义--weights yolov9-s.pt加载 COCO 预训练权重做迁移学习。yoloes-s.pt不写的话默认从零训练收敛速度会慢很多。--epochs 100头盔检测是二分类任务helmet / no_helmet100 epoch 在小数据集上足够如果你的数据量和场景复杂夜间、雨天、多角度建议 200。--batch-size 88G 显存跑yolov9-s的推荐值。如果你只有 6G改成 4有 12G可以上 16。--img 640输入分辨率。头盔头部目标相对较小不建议降到 416但显存实在不够时可以降到 512。--close-mosaic 15最后 15 个 epoch 关闭 mosaic 数据增强。mosaic 会把 4 张图拼在一起最后阶段关闭它能让模型在接近真实分布的图片上微调AP 会涨一点。训练开始后注意观察两个信号。第一个是 loss 刚开始会急剧下降然后缓慢下降这是正常的。第二个是如果 loss 在前 20 个 epoch 纹丝不动基本可以断定数据有问题赶紧 CtrlC 去查标注文件。不用等 100 个 epoch 跑完再骂自己数据集没配好前 20 个 epoch 足够暴露问题。4.2 断点续训与训练结果产物训练中断是常态别慌。YOLOv9 会每个 epoch 保存last.pt默认放在runs/train/helmet_run/下。续训命令只需要把--weights改成runs/train/helmet_run/last.pt即可python train_dual.py \ --weights runs/train/helmet_run/last.pt \ --data data/helmet.yaml \ --epochs 100 \ --batch-size 8 \ --device 0 \ --close-mosaic 15训练完成的runs/train/helmet_run/目录里你会看到best.pt、last.pt、results.png和results.csv。其中best.pt是验证集上 mAP 最高的权重推理就用它。results.csv用 Excel 直接打开能看到每个 epoch 的 Precision、Recall、mAP50、mAP50-95 变化曲线。这里有个血泪经验如果你看到val_mAP50很高但val_mAP50-95很低说明模型对遮挡目标的框定位不稳需要增加标注质量而不是盲目加 epoch。4.3 超参数别乱动的原因不少新手喜欢把hyp.scratch-high.yaml里的 learning rate 调成 0.01 或 0.001理由是「网上说这么调更好」。这个文件是 YOLOv9 官方在 COCO 上反复调过的里面的lr0: 0.01、mosaic: 1.0、hsv_h: 0.015都对应特定的收敛行为瞎改只能让模型更难收敛。真正值得你动的只有close-mosaic这一个参数。5. 推理验证与实测避坑三个确认信号加上六个真实踩坑记录5.1 推理脚本怎么改才能测出自己的头盔模型训练完成后修改detect_dual.py或直接命令行推理python detect_dual.py \ --weights runs/train/helmet_run/best.pt \ --source test_imgs/ \ --conf-thres 0.25 \ --iou-thres 0.45 \ --imgsz 640--source可以传图片文件夹、单张图片、视频文件路径也能传0表示调用摄像头实时检测。测试图片我建议选 20 张以上、包含不同角度、不同光线、正负样本各半的图片。--conf-thres 0.25表示置信度低于 0.25 的检测框会被过滤--iou-thres 0.45是 NMS 的 IoU 阈值。如果你的场景误检多把conf提到 0.4如果漏检多降到 0.15。推理结果保存在runs/detect/下。三个确认模型真的能用的信号第一测试集上的 mAP50 在 0.85 以上第二单张图上能稳定框出不戴头盔的骑行者并且不会把行人框成 no_helmet第三对未见过的场景比如雨天夜景有一定泛化能力。如果第三条不满足说明数据多样性不够不是模型的锅。5.2 避坑记录十个问题九个出在数据和路径上我把实操中遇到过的典型问题整理成六个踩坑记录每条都是血泪换来的。踩坑一FileNotFoundError: No such file or directory: data/helmet.yaml现象训练命令执行后立刻报错说 yaml 文件不存在。原因当前工作目录不在项目根目录PyCharm 默认工作目录可能指向别处。解决在 PyCharm 的 Run Configurations 里把 Working directory 改成项目根目录或者直接cd到项目根目录再执行命令。踩坑二训练不报错但 loss 完全不降现象loss 一直稳定在某个值附近没有任何下降趋势。原因数据路径配了 train 和 val 指向同一批图片模型在「背答案」验证集 loss 自然不会降。解决确认val目录和train目录图片不重叠且标注文件路径与图片路径一一对应。踩坑三训练正常但检测时一张图都没框出来现象detect_dual.py跑完输出图片上没有检测框。原因置信度阈值设得太高或者输入图片分辨率过大导致小目标漏检。解决先降到--conf-thres 0.05看是否有框如果有再逐步调到合理值。同时把imgsz调整到 640过大的原始图片会压缩后丢失细节。踩坑四类别输出和预期完全不一致现象模型明明训练的是头盔输出结果却是 banana/very-ripe。原因--weights指到了官方预训练权重或原项目自带的 banana 模型而不是你训练生成的best.pt。解决检查--weights路径确认是runs/train/helmet_run/best.pt。这个错误看起来很蠢但相当多的人在高强度改参时会犯。踩坑五显存不足 CUDA out of memory现象训练跑了几十个 step 后直接报 OOM。原因batch-size 太大或者训练中期的 mosaic 拼接把图像尺寸放大了 4 倍显存占用。解决先降--batch-size如果还报就把--close-mosaic从 15 改成 5让 mosaic 提前关闭再不行就降--img 512。踩坑六检测结果框的位置时准时不准现象同一个视频里有些帧检测框贴得很准有些帧偏了很多。原因没有做重参数化模型还带着辅助分支在推理速度慢且不稳定。解决跑reparameterization.ipynb把权重合并后再推理见下一章。6. 重参数化与落地把训练权重收拢成推理模型的一个关键习惯YOLOv9 的网络结构包含主干网络和辅助可逆分支。训练时这两个分支协同优化梯度的传播但推理时辅助分支只是负担。官方方案是训练完成后做重参数化把辅助分支合并到主干网络里得到更精简的推理模型。这个操作在工程上不是可选项如果省掉一是推理速度会慢 20%~40%二是模型行为在连续帧上容易出现轻微抖动。项目根目录的reparameterization.ipynb就是干这件事的。打开 Notebook逐格运行核心逻辑是加载最好的权重文件调用融合方法导出一个合并后的模型import torch from models.yolo import Model # 加载训练时的模型结构 model Model(cfgmodels/detect/yolov9-c.yaml, ch3, nc2) # 训练好的权重里有额外的 aux 分支推理时需要切换模式 ckpt torch.load(runs/train/helmet_run/best.pt, map_locationcpu) model.load_state_dict(ckpt[model].float().state_dict()) # 关键融合辅助分支这一步就是重参数化 model.eval() fake_input torch.randn(1, 3, 640, 640) traced torch.jit.trace(model, fake_input) torch.jit.save(traced, runs/train/helmet_run/best_traced.pt)不是所有模型都能用torch.jit.trace直接搞定如果报算子兼容错误更通用的做法是直接保存融合后的权重# 进入检测模式会触发模块内部的 reparameterize model.eval() torch.save({model: model}, runs/train/helmet_run/best_rep.pt)融合完成后用detect_dual.py指定新权重跑一遍同样的测试图片对比前后两版推理的 fps 和检测框坐标输出你会发现框更稳、延迟更低。从那以后我每次训完模型都强制自己先做重参数化再谈测试——不是多此一举而是权重合并后才会暴露真正的泛化能力。希望这点经验能帮你在做头盔检测落地时少走一次弯路。本文还有配套的精品资源点击获取