news 2026/9/12 4:33:22

YOLOv5安全帽检测系统:从模型训练到RTSP实时视频流部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5安全帽检测系统:从模型训练到RTSP实时视频流部署

简介:基于Python与YOLOv5算法打造的安全帽佩戴及危险区域实时检测毕业设计项目,面向计算机、通信、人工智能、自动化等专业学生与从业者,可服务于毕业设计、课程设计或工程入门。项目已接通海康摄像头实现实时视频流检测,覆盖模型训练、数据配置、界面可视化与区域危险判断等完整流程,代码经过调试测试,具备较高复用与二次开发价值。压缩包共63个文件,总大小28.61MB,包含21个Python源码文件、9个YAML配置与模型结构文件、若干JPG/PNG/GIF演示素材,以及UI界面文件、Markdown文档说明和requirements依赖清单,方便快速搭建环境并对照学习。配套有训练好的模型权重、可视化工具教程、测试输出图和视频示例,可直观查看检测效果。当前已有193人学习下载,适合零基础入门或需要在现有框架上做功能扩展的开发者。

1. 一套能毕业设计答辩的实时检测系统,难点不在模型

用 YOLOv5 做安全帽检测,早已不是新鲜事,真正让毕设卡壳的是把训练好的模型和硬件环境串成一套“实时系统”。多数人拿到开源权重,跑几张测试图片没问题,一旦接上海康摄像头的 RTSP 视频流,要么画面卡死,要么检测框延迟两三秒,要么 GPU 显存直接被撑爆。标题里“源码 + 文档 + 训练好的模型”这套组合,本质上是要求你交付一个开箱即用的工程,而不是一个 notebook 演示。这意味着三件事必须同时成立:模型能在你的机器上快速跑通推理、海康摄像头的取流地址和参数设置正确、危险区域判定逻辑能稳定地叠加在检测结果上。适合拿这套方案做毕设的人,是已经装好 Python 但没碰过 YOLOv5 的、手里有海康摄像头但不知道 RTSP 怎么取的、以及训练完模型却不知道如何部署到实时视频流上的。

2. YOLOv5 的检测流程与安全帽识别的最小闭环

2.1 YOLOv5 网络结构里的三个关键部件,决定了你的训练效果

YOLOv5 的检测流程分三段:Backbone 负责提取特征,Neck 负责融合不同尺度的特征图,Head 负责输出预测框。对于安全帽检测这种目标尺寸不大、场景相对固定的任务,真正值得关注的是 Neck 部分。

C3 模块是 YOLOv5 的基本构件,它借鉴了 CSPNet 的思路,将输入特征图分成两个分支,一条分支经过一系列 Bottleneck 操作,另一条分支直接连接,最后再拼接起来。这个设计减少了计算量,同时保持了梯度信息的丰富程度。训练自己数据集的时候,C3 模块里的 Bottleneck 数量由depth_multiple这个超参数控制。

SPPF 是空间金字塔池化的快速版本,它通过串联多个最大池化层来扩大感受野,让模型能够同时看到局部细节和全局上下文。检测安全帽这种小尺寸目标时,SPPF 的输出会直接送入 Head 的浅层特征图,所以它的作用不可替代。

Head 部分使用锚框机制,在 COCO 数据集上默认的锚框尺寸对于安全帽这种目标可能偏大。你在训练自定义数据集时,需要让 YOLOv5 自动计算适合你数据集的锚框尺寸,后面会具体讲。

2.2 从零准备安全帽数据集到模型训练的最小命令集

先明确一个概念:YOLOv5 的数据集格式与 COCO 不同,它使用的是 YOLO 格式的标注文件。每张图片对应一个同名的.txt文件,文本行的格式为class x_center y_center width height,其中坐标值都是归一化后的浮点数,取值范围 0 到 1。

假设你的数据集目录结构如下:

helmet_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml

data.yaml的定义方式如下,以我常用的/home/user/helmet_dataset路径为例:

train: /home/user/helmet_dataset/images/train val: /home/user/helmet_dataset/images/val nc: 2 names: ['helmet', 'head']

这里的nc是类别数量,我用的训练配置是两类:戴了安全帽的helmet和未佩戴的head。如果你还想检测危险区域闯入,可以在names里追加类别,比如['helmet', 'head', 'person', 'area']

标注工具方面,常见的做法是用 LabelImg 或 LabelStudio,导出格式直接选 YOLO 即可。如果你拿到的数据集是 VOC 格式的 XML 标注,可以用 YOLOv5 仓库里的转换脚本处理。

训练命令的最小可运行版本如下:

cd yolov5 python train.py --data /home/user/helmet_dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --device 0

参数说明:

  • --img 640表示训练时输入图片的尺寸,推理时也建议保持相同的尺寸,否则检测框位置会偏移。
  • --batch-size取决于 GPU 显存大小,我常在 8GB 显存的卡上使用 16,如果在 CPU 上训练,建议改成 4 或直接不写让脚本自动尝试。
  • --weights yolov5s.pt是 COCO 预训练权重,在自定义数据集上做迁移学习能大幅缩短训练时间。如果数据集很小(少于 1000 张),建议把--freeze-layers 10加到命令里,冻结 Backbone 层只训练 Head 部分。

训练完成后会在runs/train/exp/weights/目录下生成best.ptlast.pt。验证模型效果时使用:

python val.py --data /home/user/helmet_dataset/data.yaml \ --weights runs/train/exp/weights/best.pt \ --task val

检测结果的 mAP 通常在验证集上能到 0.95 以上,这个数值在安全帽这种目标类别少、外观差异不大的任务里算是正常水平。

3. 海康摄像头实时取流的 RTSP 配置与 YOLOv5 推理对接

3.1 每一步都在解决“实时”的瓶颈,而你距离实线只差一段代码

海康摄像头的视频流通过 RTSP 协议传输,默认推流地址遵循固定格式,但你第一次拿到的地址往往打不开,原因是摄像头的用户名、密码、IP 或子码流参数不一致。一个最基础的海康 RTSP 地址格式如下:

rtsp://username:password@192.168.1.64:554/Streaming/Channels/101

URL 结尾的101代表主码流通道 1,102代表子码流通道 1。对于实时检测场景,我用的是子码流,因为主码流分辨率通常是 2560x1440,直接喂给 YOLOv5 做逐帧推理会占用大量 CPU 解码资源和显存。子码流分辨率一般为 640x480 或 704x576,对安全帽检测来说完全够用。

如果地址报错,优先检查以下几点:

  • 密码中是否含有特殊字符。RTSP URL 里的密码如果有@:等字符,需要做 URL 编码,比如@要写成%40,否则地址解析会截断出错。
  • 端口是否为 554。有些摄像头默认 HTTP 端口是 80,但 RTSP 端口固定是 554,不要混淆。
  • 在 VLC 播放器里先试一下这个地址能打开,排查摄像头本身的异常。
参数主码流子码流我的选择
分辨率2560x1440640x480子码流
帧率25fps12fps保持默认
带宽占用~4Mbps~0.5Mbps子码流
适用任务录像存储实时检测推荐检测

3.2 用 OpenCV 的 VideoCapture 解码 RTSP 流并逐帧推理

OpenCV 处理 RTSP 流的方式很简单,但有两个坑:一是默认的缓冲区会导致画面延迟越来越高,二是read()方法阻塞时间不稳定。我习惯在捕获到帧后先清空缓冲区,再通过判断关键帧条件来决定是否执行推理:

import cv2 import torch # 加载训练完成的模型 model = torch.hub.load('./yolov5', 'custom', path='runs/train/exp/weights/best.pt', source='local') model.conf = 0.5 # 置信度阈值 model.iou = 0.45 # NMS 的 IoU 阈值 # RTSP 地址:使用子码流 rtsp_url = 'rtsp://admin:password@192.168.1.64:554/Streaming/Channels/102' cap = cv2.VideoCapture(rtsp_url) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) while True: ret, frame = cap.read() if not ret: continue # 关键帧推理:每 3 帧处理一次,减轻 GPU 负载 if cv2.waitKey(1) & 0xFF == ord(' '): # 推理并画框 results = model(frame, size=640) rendered = results.render()[0] cv2.imshow('helmet_detection', rendered) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

代码里的torch.hub.load会在第一次执行时加载模型结构,source='local'表示从本地目录读取 YOLOv5 代码,不要每次都从远程拉取。model.confmodel.iou是推理时的两个核心阈值参数,直接影响误报和漏报比例:

  • conf降至 0.3 左右可以召回更多小目标,但代价是出现更多误检框。
  • iou越大,重叠的检测框越容易被合并,一般保持默认即可。

键盘等待函数waitKey(1)同时起到了控制帧率的作用,它可以让你看到推理画面的实时效果,但无法解决网络延迟本身的问题。

注意:如果画面出现明显卡顿,先观察摄像头子码流的实际帧率,不必强求在屏幕上达到 25fps,实时检测的“实时”通常指单帧推理耗时小于 200 毫秒。

4. 危险区域检测的坐标判定与规则叠加

4.1 从像素坐标到实际空间位置的映射,危险区域的两种定义方式

危险区域可以有两种理解:一是画面中像素级别的固定矩形或多边形区域,二是基于测距或坐标转换后的物理区域。对毕设系统来说,像素级别的区域判定实现简单、效果直观,是常见的做法。

定义危险区域最直接的方式是让用户在画面中用鼠标框选一个多边形,将顶点坐标保存下来。OpenCV 的cv2.selectROI只能选矩形,若需要任意多边形,通常用cv2.setMouseCallback配合鼠标事件实现。

另一种方式是读取配置文件里的区域顶点,比如使用 JSON 文件保存坐标。判定逻辑用 OpenCV 内置的cv2.pointPolygonTest函数,它能判断某个点是否在一个多边形内,也支持返回点到边界的距离。

4.2 把检测框与危险区域结合,用一段代码让安全帽和区域同时生效

安全帽检测输出的是每个目标的边界框(x1, y1, x2, y2, conf, class)。判断是否闯入危险区域的位置参考点,我一般取边界框底部中心点,原因是人在站立时脚部所在的平面更接近真实的地面投影位置。头部被检测到时,底部中心点其实对应的是肩膀位置,对安全帽检测场景来说仍然可接受。

import cv2 import numpy as np # 危险区域顶点坐标(像素),按顺序连接成多边形 danger_zone = np.array([[200, 400], [600, 500], [450, 120], [100, 300]], dtype=np.int32) danger_zone_pts = danger_zone.reshape(-1, 2) def is_point_in_polygon(point, polygon): # point: (x, y),polygon: Nx2 的数组 result = cv2.pointPolygonTest(polygon, point, False) return result >= 0 # 对每一帧的检测结果做区域判断 for *xyxy, conf, cls in results.xyxy[0].cpu().numpy(): x1, y1, x2, y2 = map(int, xyxy) bottom_center = ((x1 + x2) // 2, y2) # 未戴安全帽且进入危险区域,触发报警 if int(cls) == 1: # 'head' 类 if is_point_in_polygon(bottom_center, danger_zone_pts): cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, 'DANGER!', (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

pointPolygonTest的第三个参数传False时返回三种值:正数表示点在多边形内部,负数表示在外部,0 表示在边界上。所以判断条件result >= 0能保证边界上的情况也被算作闯入。

危险区域报警往往要做连续帧确认。我通常的做法是:维护一个字典,键是目标 ID,值是连续判定为闯入的帧数计数,当计数超过 5 帧时才确认报警。这样可以避免人员站在区域边缘时因为检测框轻微抖动带来的误报。代码实现只是简单的计数逻辑,关键在于这个思路在实际场景中对误报率的压制效果非常明显。

4.3 三条能直接抄走的区域判定优化经验,避开坐标错位问题

第一,RTSP 流经过解码后frame的分辨率可能和你标注区域时不一致。如果之前用截图工具标注的坐标是在 1920x1080 分辨率的图上做的,而实际推理帧是 640x480,坐标必须做等比缩放,否则区域位置会偏差很大。

第二,把区域顶点和画面缩放做绑定,不要直接写绝对像素值。建议先用cap.get(cv2.CAP_PROP_FRAME_WIDTH)CAP_PROP_FRAME_HEIGHT获取实际画面尺寸,再根据该尺寸按比例设置区域。这样摄像头分辨率调整后,区域会自动适配。

第三,YOLOv5 推理后的坐标始终对应输入帧的分辨率。如果你把size参数设为 640,而输入帧是 1280x720,模型会在内部做缩放并回映射坐标,返回的坐标已经对齐原图,不需要自己换算。注意不要对results.xyxy[0]做额外的尺寸变换,这是新手最容易犯的错误。

5. 发现问题从哪查起:部署后的验证与坑位排查

5.1 一个命令同时验证模型、摄像头和整体流程的可用性

完成整个系统后,先用一段命令同时验证摄像头和模型是否各自正常。我在调试时习惯先跑摄像头裸流:

ffprobe -rtsp_transport tcp -i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/102"

ffprobe会输出视频流的编码格式、分辨率、帧率等信息。如果能正确打印出这些参数,说明网络和摄像头取流正常;如果卡在Opening 'rtsp://...'阶段超过 5 秒,基本可以判定为网络不通或者 RTSP 地址错误。

接着再用单张图片验证模型推理本身没有问题:

python detect.py --weights runs/train/exp/weights/best.pt \ --source /path/to/test_image.jpg \ --img 640 \ --conf 0.5

看到图片上正确画出检测框后,再回到摄像头实时检测流程。这两步拆分能迅速定位问题所在,不会浪费时间去排查一个本来就没问题的环节。

实际运行中你还会遇到一个常见情况:摄像头画面出来了,但检测框没有实时更新,或者画面一直卡在第一帧。这时核心排查点是 OpenCV 的后端解码可能使用了FFMPEG,而在某些系统上CAP_PROP_BUFFERSIZE需要重新设置才会生效。

5.2 三个最容易踩的暗坑与已有解决方案

第一个暗坑是 CPU 推理速度严重不足。如果你的机器没有独立显卡,YOLOv5s 在纯 CPU 模式下推理一张 640x640 的图片约需要 0.5 到 1 秒,无法达到实时效果。解决方案有很多,最有效的是把输入尺寸降到 416,同时改用子码流并将摄像头帧率限制在 12fps。这能以略微降低小目标召回率为代价,换来推理速度提升。如果仍然不够,可以考虑在yolov5/models/yolo.py里把检测层部分通道裁剪,但毕设阶段不建议动模型结构,维护成本太高。

第二个暗坑是torch.hub.load每次都去下载依赖。断网环境下会直接失败,解决方案是提前克隆 YOLOv5 仓库:

git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt

之后加载时用source='local'跳过拉取步骤。

第三个暗坑是多个摄像头同时取流时,线程模式下的延迟累计。OpenCVVideoCapture默认是单线程阻塞读取,两个摄像头就需要两个线程各自维护一个cap实例,不能共用一个对象。跨线程读取时注意把cap的读取循环和推理推理逻辑分离,避免 GIL 锁导致性能进一步下降。

5.3 答辩演示时让系统处于稳定状态的几项准备

答辩现场最怕演示翻车。我建议你把推理脚本封装成一个带--source参数的命令行入口,这样既可以用摄像头实时画面演示,也可以随时切换成视频文件回放。一旦 RTSP 现场网络不稳定,立即切换成本地视频,保证演示流程不断。

为了让画面效果更有说服力,可以提前准备好一段包含安全帽佩戴人员和未佩戴人员行走到危险区域的测试视频。演示时使用本地视频文件一方面规避网络抖动,另一方面你可以控制视频内容,确保出现预期的检测结果。

注意:RTSP 连接断掉不会自动重连。如果你的系统需要长时间运行,务必在read()返回False后增加重连逻辑,比如尝试重新初始化VideoCapture,并加上指数退避重试,避免死循环。

5.4 保存检测结果与输出日志

答辩之后通常需要实验数据支撑,可以在推理循环里加入结果保存逻辑。把每帧的检测结果转成 CSV 记录,每行包含时间戳、类别、置信度、区域判定结果。OpenCV 同时可以保存带标注的视频流,方便截取演示片段。

import csv import time csv_file = open('detection_log.csv', 'w', newline='') writer = csv.writer(csv_file) writer.writerow(['timestamp', 'class', 'confidence', 'in_danger_zone']) # 在检测循环内部写入 for *xyxy, conf, cls in results.xyxy[0].cpu().numpy(): in_zone = is_point_in_polygon(((x1 + x2) // 2, y2), danger_zone_pts) writer.writerow([time.strftime('%Y-%m-%d %H:%M:%S'), int(cls), f'{conf:.2f}', in_zone])

日志文件直接落到本地磁盘。保存时注意文件句柄不要每帧开关,否则写入性能会成为瓶颈,正确做法是程序结束时统一 flush 并关闭。

6. 参数调优的后手:用一组detect指令对阈值和区域做边界测试

参数最终要落到model.confmodel.iou上,但这两个值的最优组合需要通过批量测试来确定。我的做法是写一个循环,针对同一段视频测试不同参数组合下的帧率和误检数,形成对比表:

python detect.py --weights runs/train/exp/weights/best.pt \ --source test.mp4 \ --conf 0.5 --iou 0.45 \ --save-txt --save-conf

--save-txt会把每帧检测结果写入labels/目录下,--save-conf会把置信度一并写入。然后用脚本统计同一目标在不同参数组合下的检出次数,通过对比找到最合适的阈值区间。

对于危险区域的边界测试,我习惯准备一组测试用例图片:目标恰好位于区域边界外、边界上、边界内各 5 组。逐一运行区域判定函数并比对输出结果。pointPolygonTest在边界附近误差相对明显,因为检测框本身有抖动,这时可以把判定条件改为result >= 0且同时考虑前 3 帧的平均位置,以帧序列的稳定结果作为最终闯入依据。

帧率调优上,cap.set(cv2.CAP_PROP_FPS, 10)有时候无效,因为它取决于摄像头是否支持修改帧率。更可靠的做法是在推理侧做帧丢弃策略,比如每读 3 帧只推理 1 帧,并保证推理结果渲染回原图时不会出现坐标偏移。YOLOv5 的results.render()返回的是已经绘制好边框的帧,直接输出即可。

最终交付时,确保你的项目目录里包含以下内容:yolov5/模型代码、runs/train/exp/weights/best.pt训练好的模型、detect_rtsp.py实时检测脚本、danger_zone.json危险区域配置文件和README.md。将 RTSP 地址和参数配置统一放在config.yaml里,以便答辩老师查看时能快速理解整个系统的输入输出结构。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 4:33:16

基于Flask与Vue的大学生阅读行为分析系统设计与实现

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

作者头像 李华
网站建设 2026/9/12 4:31:45

2023主流内容分发工具评测与实战指南

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

作者头像 李华
网站建设 2026/9/12 4:29:56

NLP教学实践包:TF-IDF与BiLSTM可复现全流程

简介:本资源是面向高校计算机与人工智能专业学生的Python自然语言处理(NLP)课程设计实践包,聚焦文本分类、情感分析、命名实体识别等核心任务,助力初学者从代码实现到实验报告撰写全流程掌握NLP基础应用。压缩包共288个…

作者头像 李华
网站建设 2026/9/12 4:29:30

bd recall 命令深度指南:用 Beads 按 key 检索持久记忆

bd recall 命令深度指南&#xff1a;用 Beads 按 key 检索持久记忆 【免费下载链接】beads Beads - A memory upgrade for your coding agent 项目地址: https://gitcode.com/GitHub_Trending/beads1/beads 导读 bd recall <key> 是 Beads 持久记忆体系&#xff…

作者头像 李华