毕业设计选目标检测方向,绕不开“安全帽佩戴检测”这个经典题目。一套带源码和论文的开源项目,往往用YOLO系模型做检测主体,配合标注好的安全帽数据集,最终跑出一个能实时检测佩戴状态的演示系统。这个项目我前前后后帮人梳理过很多次,自己也动手复现过,整体感受是:技术链路完整、踩坑点明确、答辩展示效果好,非常适合作为目标检测入门或本科毕设的底座。这篇文章就把这类项目的里里外外讲清楚,从数据标注、模型训练、部署联调到论文写作,不绕弯子,直接给你可复用的思路和参数。
1. 项目选题与技术选型:安全帽佩戴检测为什么是毕设常青树
1.1 应用场景与题目价值
安全帽佩戴检测解决的是施工现场、工厂车间、电力巡检等场景的安全监管问题。传统方式是安排安全员盯监控大屏或者现场巡查,但人眼长时间盯着十几路视频,漏看是必然的。基于深度学习的检测系统可以做到七乘二十四小时自动分析,一旦发现有人没戴安全帽,立刻截图、标记、告警,还能联动语音播报,让管理人员第一时间处理。
这个场景对毕设来说有三个天然优势。第一,数据不难搞:公开的安全帽数据集不少,自己用手机去工地外围拍一批、或者从监控视频里抽帧,都能凑出可用训练集。第二,技术路线成熟:目标检测领域有大量开源基础代码,不需要从零写网络结构。第三,展示效果好:答辩现场打开摄像头,实时框出有没有戴帽,视觉冲击力很强,老师一看就明白你做了什么。
从能力培养角度看,安全帽佩戴检测项目覆盖了深度学习目标检测的完整闭环:数据采集、标注、训练、评估、部署、告警。哪怕是完全没有做过相关项目的同学,只要完整跟一遍,对目标检测的理解会比只看书深刻得多。这就是它能够持续成为热门毕设题目的底层原因。
1.2 YOLO、SSD、Faster R-CNN到底怎么选
选模型是这个项目第一个要做的决策。目前开源项目里最常见的是YOLO系,其次是SSD,少数会拿Faster R-CNN做对比实验。三者的差异不只是速度,还直接影响你在论文里“实验对比”章节怎么写。
| 模型 | 推理速度 | 精度表现 | 显存占用 | 工程化难度 | 适合定位 |
|---|---|---|---|---|---|
| YOLOv5/YOLOv8 | 快 | 高 | 低 | 低 | 主模型、部署落地 |
| SSD | 快 | 中等 | 低 | 中等 | 边缘设备部署对比 |
| Faster R-CNN | 慢 | 较高 | 高 | 中等 | 论文中的精度对照组 |
我的建议很直接:主线用YOLOv5或YOLOv8,论文里再拿Faster R-CNN做一组对比,说明“单阶段模型在精度损失可接受的前提下显著提升了帧率,更符合实时监控需求”。理由有三个。第一,YOLO社区生态成熟,遇到任何报错都能搜到解决方案。第二,YOLO的代码模块化程度高,后面你想加注意力机制、换backbone、改损失函数,都是在固定的位置改,工作量可预期。第三,它自带完整的训练、验证、导出、推理工具链,不需要自己拼装。
如果答辩老师追问“现在DETR、ViT这些新方法也很火,为什么不用”,你要能答上来:Transformer类检测模型训练收敛慢、对数据量要求高,部署时对边缘设备不友好,本科毕设追求的是稳定落地,不是算法前沿探索。把“为什么不用”和“为什么用”都讲清楚,比只会跑一个默认代码强很多。
2. 数据准备与标注:决定模型上限的隐形分水岭
2.1 安全帽数据集的目录结构与标签设计
拿到一个开源项目,第一步不是急着训练,而是看数据。很多同学第一次复现失败,原因不是模型代码错了,而是数据目录结构和配置文件对不上。一个标准的数据集应该长这样:
safety_hat/ ├─ images/ │ ├─ train/ (训练集图片) │ └─ val/ (验证集图片) ├─ labels/ │ ├─ train/ (每张图片对应的txt标签) │ └─ val/ └─ safety_hat.yamlYOLO的标签格式是纯文本:每行代表一个目标,包含类别ID和归一化后的目标中心点坐标、宽高。归一化是指坐标除以图片宽高,值域在0到1之间。举例来说,0 0.5 0.45 0.2 0.3表示类别0(helmet)的目标位于图片中心偏上一点,目标宽度占图片宽度的20%,高度占30%。
类别设计上,推荐设定两类:helmet(佩戴安全帽)和head(未佩戴,能看到头部)。为什么不额外定义一个“身体”类?因为检测未佩戴的核心是“头上有没有帽子”,聚焦头部区域比检测整个人更准确,也能避免把路过的、没进作业区的人员误报进来。还要注意,修改数据集类别数之后,必须同步修改模型配置文件里的nc参数。这是新人最容易犯的低级错误,光我看到的报错案例就不下十次。
2.2 标注实操规范:该框哪里、怎么处理特殊样本
标注质量直接决定模型效果,这一环节不能图快。工具方面,LabelImg是主流选择,支持BBox框选,可以保存成PascalVOC格式再统一转成YOLO格式,也可以直接用YOLO模式输出txt。关键问题是:框选范围怎么定。
我的经验是框“头肩部区域”,而不是框整个人。原因有两个。一是聚焦:模型只需要判断头部附近有没有帽子,既然不关心人的手臂和腿,就别把无关特征塞进去。二是解决小目标:远景中整个人很小,框全人容易丢失头部细节,框头肩区域能让模型更关注有效区域。实际操作中,头肩框的坐标大约是人脸的上下各放宽一点,向下覆盖到肩膀位置即可。
特殊样本的处理也很讲究。最常见的是“手拿安全帽”的情况,有人手里提着帽子走进画面,如果你把这顶帽子标成helmet,模型会学到“帽子和人分离也是佩戴”——这是非常典型的错误标签污染。我在这类项目里看到很多团队直接把这类样本从训练集剔除,或者单独增加一个carrying类别,两种做法都比硬标成helmet好。
还有两种情况必须补充:一是帽子和背景颜色相近,比如黄色安全帽配上黄色墙面,容易出现漏检,需要特意收集这种样本;二是背光、逆光环境下拍摄的图,如果训练集里全是光线充足的图片,实际部署时一到傍晚就会大量漏检。简单说,数据集里缺什么,模型就会在什么场景翻车。
注意:标注完成后,抽20%的图用一个“画框工具”把标签可视化出来人工复查。看文字标签没问题不代表框的位置没问题,框偏了、框太小、类别标错,都会在训练结果里变成莫名其妙的误检。
2.3 数据增强与类别不平衡处理
数据增强的正确使用方式是把有限的样本用出无限的变化。常用的增强操作包括Mosaic(把四张图拼成一张训练)、随机水平翻转、HSV颜色抖动、随机模糊、模拟雨雾噪声等。Mosaic对提升小目标检测能力尤其有效,因为拼接后相当于缩小了每个目标的面积,让模型被迫学习更小尺度的特征。
增强不是越多越好。我见过一个项目把旋转增强开到30度,结果模型把“上下颠倒的人”也当成了有效目标去学习,完全不现实——安全帽检测场景中摄像头是固定的,人不会倒立走路。旋转角度控制在5到10度即可,随机翻转虽然常用,但如果你的训练数据中人们都是靠右行走,翻转后模型对方向性的理解反而会更好,这一点倒是可以大胆开。
类别不平衡是这个项目最隐蔽的问题。很多数据集里佩戴样本是未佩戴样本的好几倍,模型训练出来mAP看起来不错,一部署就发现“没戴帽的人”大量漏检。原因是模型学到的先验是“大多数人戴帽”,没戴帽的反而是少数派,它的默认倾向就是判成佩戴。解决办法主要有三个:一是对head类别的样本做过采样,让两类的数量接近1比1;二是增加更多未佩戴样本的采集;三是训练时适当调高类别权重,让模型更关注少数类别。我在实操中体会最深的还是第一条,数据平衡比调参管用得多。
3. 训练过程拆解:从YOLOv5s基线到稳定收敛
3.1 第一次训练前的环境与参数准备
训练环境的搭建看似简单,但版本问题能折腾一整天。推荐组合是Python 3.8以上、PyTorch 1.13或2.x、CUDA 11.8以上。显卡建议至少8GB显存,没有显卡的用CPU也能跑,只是速度慢很多,一个百来轮的小数据集可能要跑十几个小时。
数据集配置文件里要写清楚路径和类别信息,以YOLOv8为例:
train: /your_path/images/train val: /your_path/images/val nc: 2 names: ['helmet', 'head']训练命令很简单,YOLOv8直接用Python接口:
from ultralytics import YOLO model = YOLO('yolov8n.pt') model.train(data='safety_hat.yaml', epochs=150, imgsz=640, batch=16, lr0=0.01)YOLOv5的命令行版本也很常见:
python train.py --weights yolov5s.pt --data safety_hat.yaml --epochs 150 --batch-size 16 --img 640参数选择上,新手常犯的错误是一上来就用大模型。第一次跑通整个流程,我建议从YOLOv5s或YOLOv8n这种轻量级模型开始,确认数据没问题、训练能收敛之后,再考虑要不要换更大的模型。imgsz=640是速度和精度的平衡点;如果显存不够,可以降到448,精度会轻微下降但能跑起来;如果小目标很多,可以提高到960或1280,代价是显存和训练时间都上去了。batch尽量大,但以不爆显存为限,同时配合开启AMP混合精度,能在显存不变的情况下把batch提上去一截。
最关键的一点:一定要加载预训练权重,也就是上面命令里的yolov8n.pt或yolov5s.pt。从零开始训练COCO那些初始化权重收敛难度大,而且很容易不收敛。预训练权重让模型起步就具备通用特征提取能力,你只需在安全帽场景上做迁移调整。
3.2 观察训练动态:Loss、mAP与过拟合信号
训练开始后,不是等着跑完看结果,而要实时看指标曲线。我习惯同时观察train loss和val loss,以及验证集上的mAP50、Precision、Recall。下面这些训练信号,直接对应不同的处理动作:
| 观察现象 | 可能原因 | 处理方式 |
|---|---|---|
| train loss完全不降 | 标签格式错误、类别数不匹配、学习率过大 | 可视化标签;检查yaml;降低lr |
| train loss降但val loss升 | 过拟合 | 减小epoch、增强数据增强、开启早停 |
| mAP高但实际漏检多 | 类别不平衡、小目标占比高 | 补充少数类样本、提高imgsz、调低conf |
| Precision和Recall差距过大 | 阈值设置不合理 | 调整conf和iou找到平衡点 |
训练轮数设置,我建议100到150轮起步,同时开启早停机制(patience设为20轮左右)。不要觉得跑满epoch就一定更好——我见过很多模型在第80轮效果最好,后面30轮反而因为过拟合导致验证指标下降。训练完成后,真正要用的权重文件是runs/detect/train/weights/best.pt,不是last.pt。如果你发现best.pt的效果反而不如last.pt,那大概率是验证集划分有问题或者过拟合信号很严重了。
还有一个容易被忽略的点:查看混淆矩阵。验证会输出confusion matrix图片,你要重点关注“head被预测成helmet”的数量。这个数字越大,说明模型对未佩戴的判别能力越差,也意味着实际部署时漏报风险越高。如果样本不均衡,光看mAP你会觉得一切正常,混淆矩阵能帮你把问题暴露出来。
3.3 针对小目标与漏检场景的优化策略
安全帽检测和通用目标检测的最大差异在于:小目标非常多。工地监控通常是广角,一个站在画面远处的工人,头部可能只有三四十个像素,几乎就是一团模糊色块。模型在这种尺寸下很难提取到“帽子和头”的判别特征。
常见的优化路径有三条。第一条,提高输入分辨率。把imgsz从640提到960或1280,相当于让小目标的像素面积成倍增加,效果立竿见影,代价是显存占用和推理时间都会上涨。第二条,增加包含小目标的训练样本。从视频里抽帧时,专门保留那些人员距离较远、画面占比很小的帧。第三条,如果用了高分辨率还是漏检,可以考虑SAHI切片推理:把原图切成多个小块分别检测,再把结果合并。这个方法很实用,尤其在无人机视角和广角监控场景中表现突出。
推理阶段的阈值也要认真调。conf_thres默认0.25,如果你的场景更看重“不漏报”,可以降到0.15甚至0.1,代价是误检增多;如果更看重准确率,可以调到0.4以上。iou_thres默认0.45,当画面中人员密集、互相遮挡时,可以调到0.5到0.6,减少相邻目标的框被合并掉的情况。这些阈值没有绝对标准,要在实际测试集上反复试,记录“漏检数”和“误检数”的变化曲线,选一个两者相对平衡的位置。
4. 部署与接口封装:让检测模型真正跑起来
4.1 模型导出:ONNX导出与推理加速
训练完的best.pt是PyTorch格式,直接部署能用,但推理时开销略大。如果要做Web服务或者跑在边缘设备上,建议导出成ONNX格式。YOLO官方提供了非常简洁的导出接口:
from ultralytics import YOLO model = YOLO('best.pt') model.export(format='onnx', imgsz=640, half=True, simplify=True)half=True表示半精度FP16存储,模型体积减半,推理速度提升明显;simplify=True会简化计算图,去除冗余OP。导出后用onnxruntime加载推理,比直接跑PyTorch在CPU上普遍快2到3倍,在GPU上提升更明显。如果部署到Jetson这类设备,还可以进一步转成TensorRT引擎,帧率能再上一个台阶。
导出前后要对比一下检测结果。ONNX经过计算图优化,正常情况结果应该一致,但偶尔会有微小差异,特别是NMS部分在不同框架实现上可能有区别。我的习惯是拿同一张测试图片分别在PyTorch和ONNX上跑一次,对比输出框的坐标和置信度,误差在1%以内才算正常。
4.2 用FastAPI封装检测服务并接入摄像头流
部署的第一步是提供HTTP接口。FastAPI是首选框架,代码简洁、性能好、自带Swagger文档方便调试。一个最简的检测服务核心就十几行:
from fastapi import FastAPI, File, UploadFile import cv2 import numpy as np from ultralytics import YOLO model = YOLO('best.pt') app = FastAPI() @app.post('/detect') async def detect(file: UploadFile = File(...)): data = await file.read() img = cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR) results = model(img, conf=0.25, iou=0.45, verbose=False) dets = [] for box in results[0].boxes: b = box.xyxy[0].tolist() dets.append({ 'class': int(box.cls[0]), 'confidence': round(float(box.conf[0]), 4), 'bbox': [round(x, 2) for x in b] }) return {'detections': dets}这样一个POST请求传图片,就能拿到JSON格式的检测结果。公司里做Web前端的同学拿到这个接口就能直接接一个管理页面,展示每路摄像头的实时画面和告警记录。
接入摄像头的思路也差不多。用cv2.VideoCapture读取本地视频、IP摄像头或RTSP流,循环拿到每一帧,但不需要每帧都检测。我一般设置成每3到5帧检测一次,检测到未佩戴时立刻保存当前帧并记录时间戳。跳帧处理的原因是模型推理需要时间,如果摄像头25帧每秒,模型只能跑5帧每秒,那还是按模型的节奏走更合理,硬要逐帧检测只会导致视频越积越卡。
4.3 输出与告警:把检测结果变成可用信息
检测到“未佩戴”只是第一步,系统要能真正帮助管理,还得有告警闭环。最简单的告警是保存截图+文字日志,后续人工审核;进阶一点的可以对接企业微信或短信通知;再完整一点的,可以联动现场语音播报喇叭,检测到未戴帽就自动喊话提醒。
告警去重是很多初做系统的人忽略的细节。一个没戴帽的人站在摄像头前,模型每一帧都会检测到,如果不做处理,一分钟可能触发上百条告警,全是重复信息。低成本的解决方法是维护一个“最近告警列表”:记录目标所在区域的中心坐标和告警时间,新检测结果先用IoU和上一帧的目标做关联,如果同一个目标在短时间内已经告警过,就跳过这次推送,等它离开画面后再重新告警。
在边缘设备上的部署是另一个优化方向。Jetson Nano、树莓派这类低算力设备,推理引擎用TensorRT或者NCNN,模型换成YOLOv8n,输入尺寸降到320或416,实测下来也能跑到10到20帧每秒。把检测模型塞进边缘盒子,再把告警结果通过MQTT推上来,整个系统的架构就完整了,也特别适合在论文里画一张部署架构图。
5. 拿到开源源码后必做的检查与二开思路
5.1 开源项目复现前的5项关键检查
网上下载的“毕设开源+源码+论文”项目,质量参差不齐;有不少项目代码能跑,但数据集缺失或者论文内容有截断。拿到手不要急着训练,按下面这个顺序检查一圈:
| 检查项 | 怎么查 | 遇到问题怎么办 |
|---|---|---|
| 依赖版本 | 看requirements.txt和README | 与本地环境不一致时逐项装,不要一次全装 |
| 数据集是否完整 | 检查data.yaml里的路径和图片数量 | 缺失就重新下载或换用公开数据集 |
| 类别数一致性 | 看模型配置文件里的nc参数 | 必须以数据集的类别数为准,常见错误 |
| 权重文件 | 确认best.pt存在且能正常加载 | 没有就自己重新训练 |
| 标签格式 | 抽查labels里的txt坐标值范围 | 数值小于0或大于1说明未归一化,写脚本修正 |
这五项里,最折磨人的往往是版本兼容问题。PyTorch 1.x和2.x在某些API上有差异,YOLOv5和YOLOv8的配置文件写法也不一样,别拿到新项目就用老代码的习惯去猜。先把requirements.txt里的版本号和你本机的环境对比一下,再决定是升级环境还是降级版本。
5.2 从复现到创新:论文思路与二开方向
复现开源项目只是起点,如果原封不动交上去,老师大概率见过同款,论文查重也容易出问题。想在毕设里体现工作量,建议至少做一处小的“二开”。我这里给出几个成本不高、效果明显的方向:
- 换backbone:把YOLOv5的CSPDarknet换成轻量化的MobileNet系列,或者反过来换成更强的主干网络,做一组“模型尺寸与精度”的对比实验。
- 加注意力机制:在检测头的特征层前加入SE模块或CBAM,代码量不大,但能作为论文的一个创新点,答辩时也好解释。
- 改进小目标检测:在Neck部分增加一个针对小尺度目标的检测分支,这个改动稍微复杂,但安全帽检测恰好是典型的小目标场景,实验效果好讲就好。
- 调整损失函数:换成Focal Loss或者EIoU、SIoU等,对比不同的框回归损失对检测精度的影响。
论文写作上,我建议把重点放在“实验分析”章节,而不要大篇幅贴代码。一个完整的安全帽检测项目论文结构可以这样安排:第一章绪论写背景和现状,第二章相关工作讲目标检测方法发展,第三章系统设计画出整体流程和模型结构,第四章实验部分放数据集说明、训练参数、与Faster R-CNN和SSD的对比表格、不同场景下的效果图,第五章总结。老师最想看到的是你怎么发现数据问题、怎么调参、怎么分析漏检原因。这些是开源项目里没有、需要你自己实践总结的部分,也是论文真正的价值所在。
最后再给准备拿开源项目做毕设的同学一句实在话。把demo跑起来不难,难的是你把每个环节的“为什么”想清楚:为什么标注框要框头部而不是整个人,为什么加了预训练权重更好收敛,为什么样本不均衡会导致漏检率高。答辩的时候,老师往深处问三个问题,你如果能结合自己的数据集和训练曲线答上来,就说明这个项目你是真做进去了。把这个开源项目当成深入理解目标检测的一个入口,而不是应付交差的素材,它带给你的收获会远超你拿到的那张成绩单。