简介:这是一套面向计算机及相关专业在校学生、教师与企业开发者的智慧工地安全监管实战项目资源,聚焦施工现场安全帽佩戴识别与危险区域入侵告警两大核心需求,融合YOLOv5目标检测与PyQt5桌面应用开发技术,兼具毕设、课程设计与工程原型验证价值。压缩包共2000个文件,含406张标注图像(jpg)、743份PASCAL VOC格式标注(xml)、743个标签文本(txt)、49个Python主程序与配置脚本(py/yaml)、以及部署说明、操作文档等,整体大小236.04MB;图像与标注数据已覆盖多角度、光照与遮挡场景,模型权重与GUI界面代码开箱即用。目前已有4147人学习下载,资源提供完整可运行源码、自定义危险区域绘制功能、实时声光告警逻辑、详细部署说明及测试验证记录,特别适合深度学习入门者理解工业场景落地全流程,亦可作为二次开发基础框架快速适配其他安防检测任务。 做智慧工地的朋友跟我吐槽过一件事:几十路监控屏幕挂在墙上,安全员轮流盯着看,但还是会漏掉没戴安全帽的工人,更别说危险区域闯入这种只要几秒就能出大事的情况。后来他换了一套基于YOLOv5和PyQt5的安全帽佩戴检测及危险区域入侵检测系统,效果立刻不一样了——工人没戴安全帽,镜头前实时弹框报警;有人翻进塔吊下方禁区,系统马上告警并把截图存下来。这篇文章就想完整拆解这个源码包:带GUI界面、带数据集、带训练好的模型、还带部署说明的智慧工地项目,从算法选型、数据标注、模型训练到PyQt5界面实现、危险区入侵判定逻辑,再到环境部署和常见坑,一次性讲透。
这套东西适合谁?如果你正在做安防监控、人员合规检测、工地智慧化改造,或者想用YOLOv5做真实场景落地但不想只停留在跑通demo,那这篇内容应该能帮你省掉大量试错时间。哪怕你只是Python入门、想看看目标检测怎么跟桌面软件结合,里面的思路也值得抄作业。
1. 项目整体设计与功能拆解
1.1 智慧工地安全管理的真实痛点
建筑工地的安全事故里,高处坠落和物体打击占了相当大比例,而没戴安全帽往往就是加重后果的直接原因。传统办法是安全员来回巡逻、盯监控,但工地动辄几十路摄像头,人眼盯久了必然疲劳,漏报是早晚的事。加上塔吊、基坑、配电房这些危险区域不可能时刻派人把守,单靠人力很难做到全天候无死角。
智慧工地想解决的,就是把“人盯屏”变成“算法盯屏”。摄像头采集画面,模型实时判断画面里有没有人没戴安全帽,有没有人进入禁区,一旦触发规则就弹窗、响铃、推送给管理人员。这套项目正是围绕这两个需求展开的:一个是人员防护装备合规检测,一个是区域入侵行为预警。两个功能都跑在一个桌面GUI里,双击就能用,不需要什么高深部署背景。
1.2 两个核心检测任务的技术本质
安全帽佩戴检测,本质上是目标检测任务——在图像中定位出人、以及人的头部区域,同时判断这个头部有没有被安全帽覆盖。数据集的类别设计一般是三类:person(人体)、hat(戴安全帽的头部)、head(未戴安全帽的头部)。这样设计的好处是,模型学会的是“区分带帽与不带帽的头部”这件事,而不是简单二分类,实际场景里泛化能力更强。
危险区域入侵检测,本质上是目标检测加空间判断的组合。首先用同一个YOLOv5模型把画面里的“人”找出来,然后拿这个人的位置信息去和预先划定的危险区域做几何计算,判断落脚点是否落在区域内。这个算法不复杂,真正的难点在于区域怎么划、人的位置怎么取、以及如何控制误报和重复报警。
1.3 技术选型思路:YOLOv5处理视觉,PyQt5处理交互
整个项目的技术栈并不花哨,但都是在真实工程里被验证过、社区资料完备的组合。
YOLOv5选得很稳。它不像YOLOv8/v11那样频繁更新导致大量代码依赖变动,网络结构收敛、训练流程稳定,生态非常成熟。预训练权重、官方训练脚本、各种推理封装都有现成资料,遇到问题基本都能搜到答案。对于做项目交付来说,稳定和可维护性比追新版本重要得多。
PyQt5则是Python桌面应用的事实标准。它不是最炫酷的界面框架,但在Windows工地现场电脑上跑,稳定性、控件丰富度、OpenCV图像显示的兼容性都经得起考验。尤其是信号槽机制,天然适合做视频流实时检测的UI刷新——采集线程把检测结果发信号传回主线程,界面更新不会跟算法计算抢资源。
2. 核心算法选型:YOLOv5在安全帽检测中的应用
2.1 数据集准备与标注规范
拿到源码包后,数据集一般是一个已经整理好的标注目录,但如果你需要自己扩展训练数据,或者后续要换工地场景做微调,得先搞清楚数据集结构。
推荐目录结构是这样:
safety_helmet_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/images里放jpg图片,labels里放同名txt标注文件。每个txt文件的每一行表示一个目标:
类别索引 x_center y_center width height注意这些值是归一化到0~1之间的,格式是YOLO标准格式,不是VOC的xml。类别索引对应顺序很重要,比如0是person,1是hat,2是head,那么labels里的第一列就只能是0、1或2。
公开数据集方面,SHWD(Safety Helmet Wearing Dataset)是很多人用的基础数据,大概七千多张图,覆盖工地常见角度和光线,但里面也有不少标注噪音。如果项目要求高,建议在正式训练前花时间清洗一遍数据,把明显标错或漏标的图片删掉,再补充一部分现场摄像头截图,效果会完全不一样。我在做类似项目时,把现场两周的监控片段抽帧出来,手工补充了两千多张白天和夜间的样本,最终模型在夜间场景的漏检率明显下降。
注意:一定不要让训练集和验证集里有重复图片。很多时候大家从监控视频抽帧生成数据,相邻帧几乎一样,如果不按时间间隔抽样,验证集会出现“假高分”,实际场景泛化却很差。
2.2 模型训练流程与超参数设置
YOLOv5的训练步骤很成熟,关键是几个核心参数要理解透。
首先,数据集描述文件data.yaml要写好:
train: safety_helmet_dataset/images/train val: safety_helmet_dataset/images/val nc: 3 names: ['person', 'hat', 'head']训练命令示例:
python train.py --img 640 --batch 16 --epochs 150 --data safety_helmet.yaml --weights yolov5s.pt --name helmet_exp这里重点说一下参数选择逻辑:
- 输入尺寸img设640。安全帽在监控中通常是小目标,太低会丢失细节,太高训练显存扛不住。640是精度和资源消耗的最佳平衡点。
- batch size看显存。8G显存跑yolov5s,16基本到头;如果显存不够,优先降batch,不要降分辨率。
- 初始权重选yolov5s.pt还是yolov5m.pt,取决于你对精度的要求和部署硬件。工地场景如果摄像头角度好、距离近,yolov5s完全够用,推理速度快很多;如果摄像头装在很高的杆子上,人头很小,那至少要用m起步。
训练过程中的loss曲线需要盯着看。正常情况下train loss和val loss会同步下降,如果出现train loss降了但val loss反弹,大概率是过拟合了,考虑增加数据增强、增加数据量或者提前终止。YOLOv5默认开启了马赛克增强,这对小目标检测帮助很大,但最后一个epoch会自动关闭马赛克做最终评估,所以不用自己手动调。
2.3 从检测结果到业务判定:head与hat的后处理逻辑
模型输出的是三类框,不是直接的“是否戴帽”结论。业务层需要写一个逻辑转换:
- 如果某个person框周围没有对应的head框,也没有hat框,说明这张图没检测到人头,只能标记为“人员未识别到头部”,不能贸然判定为未戴帽。
- 如果person关联到了head框,说明这个人没戴安全帽,触发未佩戴告警。
- 如果关联到了hat框,判定为已佩戴。
这里的难点是“关联”这件事。简单做法是计算每个head/hat框的中心点与person框底部中心的欧氏距离,取最近的进行匹配。更稳的做法是直接用IoU,或者给定一个距离阈值。但不管怎样,单一帧的匹配都可能出错,所以我建议在告警模块里加状态平滑——比如连续3帧都判定为未戴帽才真正发出告警,这样可以过滤掉因为抖动、遮挡导致的孤立误判。
3. 基于PyQt5的检测界面设计与实现
3.1 界面布局与核心交互
这套源码包里的GUI,典型的功能模块包括:视频源选择区、实时画面显示区、检测结果状态区、告警信息列表、以及按钮控制栏。整体不用做得花哨,实用优先。
视频源选择一般用QComboBox下拉框,支持三种输入:
- 本地视频文件(.mp4、.avi等)
- USB摄像头(设备编号0、1等)
- RTSP网络摄像头地址
画面显示区用QLabel或自定义QWidget,通过setPixmap显示OpenCV转换过来的QImage。注意一个坑:OpenCV读出来的图像是BGR格式,必须先转成RGB再显示,否则画面颜色会偏蓝偏红,看着像加了滤镜。
按钮控制栏至少要包含:开始检测、暂停检测、停止检测、截图保存。告警信息列表用QTableWidget或QListWidget,每行记录报警时间、报警类型、截图路径。
3.2 多线程与信号槽:不让界面卡死的核心
PyQt5写实时检测最忌讳的就是把视频读取和模型推理都塞在主线程。视频流是阻塞操作,YOLOv5推理在GPU上还好说,如果CPU推理,单帧可能几百毫秒,主线程一卡住,界面直接假死,用户体验极差。
所以要拆线程。通用的做法是创建一个QThread子类,比如DetectionThread,在run方法里循环做读帧、预处理、推理、后处理,然后把结果通过pyqtSignal发回主线程,主线程只做UI刷新。信号里传一个dict,包含画面帧、检测框列表、是否告警、FPS等字段。
class DetectionThread(QThread): frame_signal = pyqtSignal(dict) def run(self): cap = cv2.VideoCapture(self.video_source) while not self.isInterruptionRequested(): ret, frame = cap.read() if not ret: break results = self.model.detect(frame) self.frame_signal.emit({ 'frame': results.annotated_frame, 'boxes': results.boxes, 'alert': results.alert }) cap.release()主线程里连接信号槽:
self.det_thread.frame_signal.connect(self.update_ui)这样界面刷新永远只有几十毫秒,不会因为模型推理变慢而卡死。
注意:信号传大图片对象也可以,但要避免高频copy,尽量复用同一个缓冲区对象。如果怕传帧太频繁造成UI队列积压,可以在信号里加一个递增的frame_id,槽函数里判断跳过旧帧。
3.3 检测结果可视化:画框、置信度与状态标签
检测结果的绘制逻辑,OpenCV一套搞定。给每个检测框画矩形、类别名、置信度。但业务层最好在画框上做区分:person框用绿色,hat框用蓝色,head框用红色。这样管理员一眼能看出问题在哪。
状态显示区可以放几个大号标签,比如“当前安全人数: 8”,“未佩戴人数: 1”,“最近告警: 10秒前”。这些标签通过QLabel的setStyleSheet实现颜色变化,比如未佩戴超过0,标签背景变红,整个监控画面的紧迫感就出来了。
截图功能建议存成带时间戳的文件名,比如alert_20240115_143205.jpg,同时把当时帧上的检测框也画进截图里,方便事后追责。实测下来,带检测框的截图比原始截图有用得多,管理人员一眼就能看出当时模型的判断依据。
4. 危险区域入侵检测的逻辑与实现
4.1 危险区域如何划定
危险区域入侵检测的第一步,是让用户把“禁区”告诉系统。源码包里通常提供了两种方案:
方案一是固定区域配置文件。在config.json里写多个多边形的顶点坐标:
{ "danger_zones": [ {"name": "塔吊下方", "points": [[100, 200], [350, 200], [350, 480], [100, 480]]}, {"name": "配电房", "points": [[700, 150], [900, 150], [900, 400], [700, 400]]} ] }方案二是GUI里交互画区域。在画面显示控件上监听鼠标事件,点击落点,双击闭合生成多边形,然后保存到配置文件。这种交互对现场人员友好得多,不用打开json手写坐标。
注意:多边形坐标的坐标空间要和视频帧的像素坐标系一致。如果视频源分辨率是1920x1080,画区域时用的也是这个画面下的坐标,那么推理帧也要resize到这个尺寸下做判断。实际项目里最常遇到的“区域漂移”问题,就是resize后忘了换算坐标。
4.2 入侵判定的核心算法
判断一个点是否在某个多边形内部,OpenCV有现成函数cv2.pointPolygonTest:
result = cv2.pointPolygonTest(zone_contour, point, False) # result > 0 表示点在内部 # result == 0 表示点在边界上 # result < 0 表示点在外部这个函数很快,对每个检测到的人判断一次即可。
但这里有个工程细节:用人的哪个点来代表“位置”?如果摄像头是俯拍,用中心点没问题;如果是巷口或门口那种平视或斜视机位,人的中心点会偏向画面中的身体中部,可能导致人明明在区域门口,中心点已经在区域外。更稳妥的做法是用人的脚部中心点——也就是检测框的底部中点——作为落脚点,因为入侵行为的本质是“脚跨进了禁区”。
实际测试中,底部中点明显比中心点更符合人的直觉。唯一的特例是人坐下或蹲下时检测框会变形,脚部中点误差会变大,这种情况可以退回到中心点或者用相邻帧的均值平滑。
4.3 告警联动与记录
触发入侵后的动作,不只是画个框报警。一个完整的告警流程应该是:画红色闪烁框、播放语音提示(可以用pyttsx3或QSound播放wav)、弹出一个提示窗口、把告警截图存到历史记录目录、把告警信息追加到日志表格。
告警去重是管理体验的关键。如果不做去重,一个人站在禁区里1分钟,系统会刷50条报警,管理人员直接崩溃。我实际落地时采用的策略是:对每个目标维护一个“最后告警时间”,同一目标连续出现在同一个区域内,如果距离上次告警不足30秒,就不再重复报警,但画面上的红色高亮保留。如果目标移出区域再进入,或者目标跟踪ID变了,重新计时。
还有一个小技巧,告警截图可以由两张拼在一起:原始画面加上带检测框和区域线画面的对比拼接图。这样既能看清现场真实情况,也能看清算法的判断逻辑,现场人员反馈特别好。
5. 环境配置与整体部署指南
5.1 环境清单与版本匹配
这个项目的部署,最磨人的往往是依赖版本。我建议环境组合如下,实测兼容性很好:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Python | 3.8 或 3.9 | YOLOv5官方支持最稳的版本 |
| PyTorch | 1.10 ~ 1.13 | 2.x也可以,但部分旧代码逻辑要改 |
| CUDA | 11.3 或 11.7 | 需和PyTorch版本严格对应 |
| PyQt5 | 5.15.7+ | 不要太老,QMediaPlayer有坑 |
| opencv-python | 4.5.x ~ 4.8.x | 4.9后个别API调整,要留意 |
| numpy | 1.21 ~ 1.24 | 与opencv的兼容性最稳定 |
安装PyQt5时经常碰到的问题就是pip下载慢或者安装包过大,用国内镜像源会好很多:
pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simpleCUDA和PyTorch版本对不上,是刚入手最容易翻车的地方。先确定自己显卡驱动支持的CUDA版本,然后选择对应版本的PyTorch。NVIDIA控制面板里能直接看到CUDA版本信息,不要凭感觉装。
5.2 快速启动流程
拿到源码包后的正确解包顺序,我按自己的经验整理了一遍:
- 解压到纯英文路径。路径里带中文在PyTorch加载权重和OpenCV读文件时都会出一些莫名其妙的问题,比如权重文件读不出来或者数据加载失败。
- 按上面的版本表创建conda环境:
conda create -n helmet python=3.8。 - 在项目根目录执行
pip install -r requirements.txt。如果源码包没带requirements.txt,自己整理一份,核心依赖就那几个。 - 确认权重文件路径。源码包里的模型文件可能是
best.pt或yolov5s_helmet.pt,需要在代码里找到模型加载那行,把路径改成实际路径,建议写绝对路径。 - 运行主程序:
python main.py或python gui_main.py,看入口文件名。 - 加载模型后,在界面里选择视频源,点开始检测。
如果第一次运行就卡在“模型加载”那一步,八成是权重文件路径不对或者PyTorch版本太新导致的权重兼容问题。前者改路径,后者就老实换回1.13。
5.3 部署成本与性能实测参考
模型推理速度,yolov5s在GTX 1060这类中端卡上,640x640输入,单帧推理大约20~30毫秒,加上前后处理和时间戳,画面能跑到20帧以上。这个帧率在工地监控场景里完全够用——安全帽没戴这个状态不会一秒内就消失,又不是追踪高速运动的物体。
CPU推理就慢多了,yolov5s在i5-10400上单帧大概300~500毫秒,只能做到每3~5帧检测一次。在实际项目里如果现场没有GPU机器,我的做法是:画面显示走实时流,检测线程做跳帧检测,每5帧检测一次,并把检测结果叠加在最近一帧上。视觉体验上会有一点延迟,但告警功能不受影响。
6. 常见问题排查与性能优化心得
6.1 运行报错速查表
拿到源码包后,跑起来的路上基本会遇到这几类问题,整理成速查表方便对照。
| 报错现象 | 可能原因 | 处理方法 |
|---|---|---|
ModuleNotFoundError: PyQt5 | 没装PyQt5 | pip安装,注意用Python3.8/3.9环境 |
AttributeError: 'Upsample' object has no attribute 'recompute_scale_factor' | PyTorch版本太新 | 换PyTorch 1.10~1.13 |
FileNotFoundError: best.pt | 权重路径不对 | 检查权重文件是否存在,改用绝对路径 |
CUDA out of memory | batch太大或分辨率太高 | 降低输入尺寸,或换yolov5s/yolov5n |
cv2.VideoCapture(0) returns False | 摄像头被占用或驱动问题 | 释放摄像头,换设备编号,检查权限 |
QWidget: Must construct a QApplication | 在非主线程创建了QWidget | UI操作只能放主线程,用信号槽传数据 |
| 画面偏蓝或偏红 | OpenCV的BGR转RGB没做 | 在setPixmap前执行cv2.cvtColor |
AttributeError: partially initialized module 'cv2' | opencv与numpy版本冲突 | 按上表统一版本或重装依赖 |
这里面最坑的是PyTorch版本导致的Upsample报错。很多源码包是用旧版PyTorch训练的,新版本框架里底层实现变了,直接加载会报错,而报错提示跟你的业务代码毫无关系,容易让人误判。
6.2 误检漏检调优思路
模型在训练集里表现很好,到了现场工地却各种漏检,这类问题很常见,不外乎几个方向:
第一,训练数据的场景覆盖不够。工地光线变化大,逆光、夜间、粉尘天气都会让检测效果退化。方案是现场采集数据,跟原数据混合后做增量训练。
第二,目标尺度分布和anchor不匹配。如果摄像头安装在很远的杆子上,人非常小,默认anchor对大目标更友好,小目标召回率会很低。可以在YOLOv5的训练配置里增加小目标anchor权重,或者直接用yolov5n这类更适合小目标的模型。
第三,置信度阈值太高或太低。检测头输出的置信度阈值,在GUI里应该做成可调参数。实测中,安全帽检测阈值设0.35~0.4比较合适,太高会把部分被遮挡的帽子漏掉,太低会把安全帽倒影、黄色塑料布误判成帽子。
第四,真正稳定的方案是加追踪。当模型出现单帧误检漏检时,配合DeepSORT跟踪算法,一个目标在多帧间的检测结果做投票,可以显著降低告警抖动。如果觉得集成DeepSORT太重,至少在告警逻辑里加上连续N帧判定,效果提升也非常明显。
6.3 打包成exe的几个坑
给工地现场部署,不能要求对方电脑上装Python环境,所以打包成独立exe几乎是必须的。
PyInstaller是主要工具,打包命令大概这样:
pyinstaller -F -w --add-data "best.pt;." --add-data "config.json;." main.py-w表示不启动命令行窗口,--add-data把权重和配置一起包进去。但这里有几个坑:
一是YOLOv5的动态import问题。YOLOv5的utils模块里大量使用从字符串动态import的方式,PyInstaller的静态分析抓不全依赖,打包后经常报ModuleNotFoundError: utils.*。解决方法是手工在代码入口显式import一遍所有会用的模块,或者在spec文件的hiddenimports里逐个加上。
二是资源文件路径问题。打包成exe后,代码里写相对路径找不到文件,必须用sys._MEIPASS来定位临时解压目录:
def resource_path(relative_path): base_path = getattr(sys, '_MEIPASS', os.path.abspath('.')) return os.path.join(base_path, relative_path)三是体积问题。一个带PyTorch和PyQt5的exe,打包出来至少1.5G起步,启动时间也长。第一次启动时如果没反应,其实是后台在解压资源,耐心等20~30秒。交付给客户时最好提前说明这一点。
6.4 个人建议:从demo到可交付的三点经验
把这类项目从“自己能跑”变成“现场能稳定跑”,我总结出三条经验。
第一,一定要在真实场景采集数据做微调。公开数据集只是起点,现场摄像头的高度、角度、光线、尘土遮挡,每一个变量都会影响模型表现。宁可花一周时间做数据采集和数据清洗,也不要指望模型一次训练就适配所有工地。
第二,告警和记录机制比检测模型本身更影响用户体验。管理人员真正在意的是:有没有告警?告警证据清不清晰?能不能追溯。哪怕检测精度稍微差一点,只要告警截图清晰、日志完整、误报可控,产品口碑就不会差。
第三,GUI和算法解耦设计,后面扩展才轻松。这个项目做扎实后,很容易扩展出抽烟检测、反光衣检测、车辆违规进入检测。只要把YOLOv5的检测类别扩充一下,数据准备好重新训练,GUI和后处理逻辑基本不动,就能很快出一个新功能。
我自己的体会是,这套项目的价值不在于代码本身跑起来有多炫,而在于它把一个完整的智慧工地安防闭环打通了:算法识别、GUI交互、告警记录、部署交付,每个环节都是实际项目里绕不开的硬骨头。如果你打算长期做这类场景,把它跑透、改明白,后面的扩展就是水到渠成的事。
本文还有配套的精品资源,点击获取