拓冰建站拓冰建站
首页 / 资讯中心 / 正文

YOLOv5+OpenCV:工地危险区域入侵检测告警系统实战

简介基于深度学习的工地危险区域入侵检测监控告警系统面向智慧工地场景下的安全管理人员、计算机相关专业学生及开发者。项目融合YOLOv5目标检测框架能够识别搅拌车、吊车等机械与作业工人支持自定义危险区域绘制一旦检测到人员闯入即刻触发画面告警与语音提醒适用于毕设、课设、实训及项目预研演示。压缩包共92个文件主要包括Python源码、pt训练权重、yaml模型与配置、jpg/png测试图像及mp4演示视频整体约84.3MB部署简单可处理本地图片、视频及网络视频流运行稳定可靠。当前已有134人学习使用代码经验证可完整运行除主程序外还附带了数据集配置、测试图表与推理结果示例便于二次开发与算法效果评估是快速落地工地安全监控方案的高性价比参考资料。1. 工地监控画面里最缺的不是摄像头是识别动作的人你在工地安全管理系统里接入二十路摄像头值班员盯不了两小时就会走神。这是智慧工地落地时最现实的问题——采集数据的设备早就齐全了缺的是能把危险事件自动挑出来的那一层软件。这个基于深度学习的工地危险区域入侵检测监控告警系统把 YOLOv5s 作为感知核心用训练好的模型识别搅拌车、吊车、作业工人再用一套自定义的入侵判定逻辑判断工人是否进入预先绘制的危险区域。随着检测结果实时叠加告警框系统同时通过语音播报提醒现场人员撤离。源码由完整的 YOLOv5 3.0 项目结构和二次开发的检测脚本构成带权重、带检测示例视频解压后首先跑detect.py就能验证模型效果随后把危险区域标定逻辑接进去就能形成一套可演示的告警闭环。下面按模型检测、区域判定、告警联动、模型替换四层依次展开。2. YOLOv5s 检测管线用一条命令把工人和机械从画面里抠出来2.1 模型选型为什么是 YOLOv5s 而不是更大的 v5l/v5x压缩包里的weights/yolov5s.pt是 YOLOv5 系列里体积和速度最均衡的一个版本。工地监控场景有一个容易被忽略的要求并不是每帧都要做高精度检测而是要在低成本设备上长时间连续运行。yolov5s 的模型体积小CPU 上也能跑到可用级别换到 NVIDIA 显卡后帧率可以再翻几倍这是 yolov5l 和 yolov5x 做不到的硬件适应性。models目录下的yolov5s.yaml定义了网络结构它与其他版本的主要差异在 depth_multiple 和 width_multiple 两个缩放系数。官方开源时保留了一整套可切换的配置但实际部署时我倾向于固定用 s 版本理由是工地场景的检测目标大小跨度很大远处吊车可能占据上百像素安全帽区域可能只有二十个像素yolov5s 的 PANet 颈部结构已经能处理这种尺度变化继续加网络深度对召回率提升有限却会直接拉高推理延迟。2.2 detect.py 参数与图片、视频、RTSP 三种输入项目根目录的detect.py是推理入口参数设计与原生 YOLOv5 保持一致。先用项目自带的测试图片跑通流程python detect.py --source inference/images/00058.jpg --weights weights/yolov5s.pt --conf-thres 0.4 --img-size 640--source支持图片路径、视频文件路径、目录路径也支持rtsp://开头的网络视频流地址--conf-thres是置信度阈值用于过滤置信度低的检测框--img-size指定推理时统一缩放的边长默认 640。执行完成后结果输出到runs/detect/目录检测到的目标会以矩形框和类别标签形式叠加在输出图上。对于视频检测直接用压缩包里的sp1.mp4验证python detect.py --source sp1.mp4 --weights weights/yolov5s.pt --conf-thres 0.4 --img-size 640 --view-img--view-img会在桌面窗口实时弹窗显示检测结果适合快速验证模型效果。若需要把结果保存成视频文件原生逻辑会自动基于输入文件名生成带标注的输出视频。接入现场网络摄像头时把--source替换成rtsp://用户名:密码IP地址:端口/Streaming/Channels/101即可常见做法是先在海康、大华的 SDK 里确认 RTSP 主码流地址格式主码流分辨率高但带宽占用大工地场景建议同时准备好子码流地址用于巡检有了目标后再切换主码流做精细识别。下面是本项目detect.py常用的参数说明表参数示例值作用--sourceinference/images/00058.jpg输入源图片、视频、目录、RTSP--weightsweights/yolov5s.pt模型权重路径--conf-thres0.4置信度阈值调低可检出更多目标但误报增加--iou-thres0.45NMS 交并比阈值--img-size640推理尺寸大尺寸提升小目标召回--classes0 7只保留指定类别索引--save-txt无同时导出检测框坐标到 txt 文件2.3 类别过滤只保留 person 与工程机械COCO 预训练权重能检测 80 类目标但工地场景只需要其中一部分。直接对所有 80 类做告警判定会产生大量无关框比如画面里出现一辆普通轿车也会被标记。项目在二次开发时按业务需要做类别白名单过滤--classes 0 7表示只保留 person索引 0和 truck索引 7吊车、搅拌车这类工程机械在 COCO 预训练模型下大概率会被识别为 truck 或 bus因此先用这两个类别把流程跑通再基于自有工地数据做微调。若更换项目中runs/exp240/weights/best.pt这类自训练权重类别索引与data目录下的 yaml 配置对应先用--save-txt跑一遍查看 txt 文件中类别编号避免硬编码索引导致的漏检。3. 危险区域自定义从背景提取到多边形命中判定3.1 提取背景.py把移动的人和机械从场景里拿掉危险区域的标定不能在地面真实拉线或摆放围栏否则就失去了视频监控的意义。项目里的提取背景.py解决了区域标定时的干扰问题工地现场人员、机械始终在运动直接截取某一帧画区域人和车的轮廓会遮挡地面参考物而标定区域需要的是干净的道路、脚手架、基坑边缘线。这个脚本的常见实现逻辑是固定摄像头不动采集连续 N 帧图像在相同像素位置取中值作为该点像素值。运动目标在大部分帧里都不占据同一位置取中值后运动目标自然被抹掉剩下的就是纯背景。关键参数是采样帧数我一般取 50 到 100 帧帧数太少背景中会残留运动目标的半透明拖影帧数太多则标定前等待时间长。背景图生成后用 OpenCV 的鼠标回调事件在背景图上逐个点击多边形顶点生成危险区域坐标文件import cv2 import json points [] def on_mouse(event, x, y, flags, param): if event cv2.EVENT_LBUTTONDOWN: points.append((x, y)) print(f已添加顶点: ({x}, {y}), 当前顶点数: {len(points)}) bg cv2.imread(background.png) cv2.namedWindow(mark_zone) cv2.setMouseCallback(mark_zone, on_mouse) while True: img bg.copy() if len(points) 3: cv2.polylines(img, [np.array(points)], True, (0, 0, 255), 2) cv2.imshow(mark_zone, img) key cv2.waitKey(1) 0xFF if key ord(s): # 按 s 保存 with open(danger_zone.json, w) as f: json.dump({polygon: points}, f) print(危险区域已保存到 danger_zone.json) break elif key ord(r): # 按 r 撤销上一个点 if points: points.pop()这段代码将多边形顶点以像素坐标形式保存到danger_zone.json。逻辑说明鼠标点击收集顶点坐标cv2.polylines实时预览闭合多边形按s落盘按r回退误点的顶点。实际使用时要注意一个细节——背景图尺寸必须与后续视频推理的原始帧尺寸一致如果摄像头分辨率是 1920x1080就应在该尺寸的背景图上标定否则区域坐标与检测帧坐标对不上。3.2 用射线法判定工人是否踩进危险区域获得多边形区域后下一层核心是判断行人检测框是否落在区域内。直接拿整个检测框做「框与多边形是否相交」会导致大量误报工人站在区域边上检测框很大只有手臂伸入也会被判定入侵。正确的做法是取检测框底部的中点作为人的立足点。检测框的左上角和右下角坐标(x1, y1, x2, y2)来自 YOLO 输出底边中点坐标为((x1 x2) / 2, y2)它对应人的脚部位置。用射线法判断这个点是否在多边形内部def point_in_polygon(check_point, polygon): x, y check_point n len(polygon) inside False p1x, p1y polygon[0] for i in range(1, n 1): p2x, p2y polygon[i % n] if (y min(p1y, p2y)) and (y max(p1y, p2y)): if p1y ! p2y: xinters (y - p1y) * (p2x - p1x) / (p2y - p1y) p1x if p1x p2x or x xinters: inside not inside p1x, p1y p2x, p2y return inside射线法逻辑说明从待判定点向右水平作射线统计射线与多边形边的交点个数交点数为奇数则该点在多边形内偶数则在多边形外。这个过程对任意凸多边形和凹多边形都有效。注意代码中有两个边界处理一是y min(p1y, p2y)与y max(p1y, p2y)保证顶点只被计数一次二是p1x p2x处理垂直边段的除零问题。check_point传人的立足点坐标polygon传从danger_zone.json读出的顶点列表。3.3 坐标对齐为什么判定要在原始帧做而不是在 640 推理图上做原生detect.py会把输入帧缩放到 640x640 再送入模型检测框坐标在 640 的分辨率坐标系下。如果直接把危险区域多边形也按比例缩到 640表面看可行实际会引入一个隐蔽问题YOLOv5 的letterbox缩放会保持宽高比并在两侧填充灰边不是简单的等比拉伸坐标换算稍不注意就会整体偏移。项目开发时的经验是危险区域坐标始终固定在原始视频分辨率坐标系下检测框从模型输出后先换算回原始坐标再判定。换算公式为假设模型输出框为(nx1, ny1, nx2, ny2)原图宽W高H则x1 nx1 * W / 640这个换算在letterbox填充场景下会有一小段偏移严谨做法是先读取detect.py中letterbox算子生成的仿射参数再做逆映射。如果只是快速验证直接按比例换算在大多数画面中也能用但精度要求高的姿态下应改造 Detect.py 在inference阶段就完成坐标反算。决策逻辑采用如下规则表检测目标位置状态告警动作工人person立足点在危险区域内立即告警工人person立足点在区域外不告警机械truck/bus进入危险区域不告警无目标任意不告警机械不告警是业务规则上的取舍——搅拌车、吊车本身就是作业设备它们的正常工作范围往往就在红线区域内部真正的风险是工人误入机械作业区因此只对 person 类目标做入侵判定。4. 入侵告警与语音提醒把检测结果变成现场能听见的声音4.1 告警状态机连续 N 帧命中才拉响避免单帧误报模型检测天然存在抖动的可能某一帧把远处戴安全帽的工人误识别成人员且其立足点刚好落在区域边缘如果单帧命中就告警值班室一天会响几十次假警。所以在检测判定之上需要加一层状态机项目实践中的设计是三态安全态、确认态、告警态。安全态下每帧执行判定命中一次就进入确认态确认态持续统计后续帧是否连续命中若连续命中超过设定值CONFIRM_FRAMES才转入告警态告警态触发语音和截图之后进入冷却时间冷却期间即使区域内有工人也不再重复告警防止语音循环轰炸。class InvasionDetector: def __init__(self, confirm_frames5, cooldown_seconds10): self.confirm_frames confirm_frames self.cooldown_seconds cooldown_seconds self.hit_count 0 self.state SAFE self.last_alarm_time 0 def update(self, invaded: bool, current_time: float): if self.state ALARM: if current_time - self.last_alarm_time self.cooldown_seconds: self.state SAFE return self.state if invaded: self.hit_count 1 if self.hit_count self.confirm_frames: self.state ALARM self.last_alarm_time current_time self.hit_count 0 else: self.hit_count 0 self.state SAFE return self.state代码逻辑说明invaded是上一节中point_in_polygon的判定结果current_time用time.time()传入。连续命中confirm_frames帧后才拉响告警告警后进入ALARM状态并开启冷却计时冷却结束自动回到安全态。参数权衡confirm_frames设为 3 到 5 较为合适——相机帧率 25fps 时约 0.2 秒确认延迟人耳基本无感设为 15 或 20 虽然更稳但快速跑过区域边缘的行人可能还没触发确认就离开了。4.2 语音播报的实现与线程隔离语音提醒是监控告警系统的关键动作之一。Python 环境下离线语音合成最常见的选择是pyttsx3它调用系统自带的语音引擎不需要网络请求工地内网环境下也能工作。import threading import pyttsx3 def speak_alert(message有工人进入危险区域请立即撤离): engine pyttsx3.init() engine.say(message) engine.runAndWait() def on_alarm_detected(): thread threading.Thread(targetspeak_alert, daemonTrue) thread.start()pyttsx3.init()会初始化系统语音引擎runAndWait()是阻塞操作单次播报可能耗时 1 到 3 秒。如果直接在视频检测主循环里调用帧率会瞬间降为零。用threading将语音播报放到独立线程中执行检测主循环不受影响。注意每个线程内都要重新init()避免跨线程复用引擎实例带来的崩溃。如果机器上没有可用的中文语音包engine.setProperty(voice, zh)可以切换语言但需要系统先安装对应语音库say()的文本内容也可以与告警状态联动比如播报「检测到工人进入 1 号危险区域」这类信息从配置文件中读取。pyttsx3依赖系统语音引擎在精简版 Windows Server 或某些 Linux 容器里可能没有预装。常见的替代方案是调用espeak或edge-tts。edge-tts需要联网访问微软语音服务工地网络环境受限时不推荐espeak音质一般但稳适合作为备选。还有一个更简单的思路是用.wav提示音文件在告警触发时通过playsound播放音质可控且零依赖。4.3 告警截图留档与 RTSP 巡检循环告警信息除了语音提醒还需要留下一份可追溯的证据。在检测循环中当状态机输出ALARM时将当前帧连同检测框、危险区域多边形一起写入本地磁盘import cv2 from datetime import datetime def save_alert_frame(frame, detections, polygon, save_diralerts): timestamp datetime.now().strftime(%Y%m%d_%H%M%S_%f) for *box, conf, cls in detections: x1, y1, x2, y2 map(int, box) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.polylines(frame, [polygon], True, (0, 255, 255), 2) cv2.imwrite(f{save_dir}/{timestamp}.jpg, frame)这段代码将检测框绘制为红色、危险区域边界绘制为黄色文件名精确到微秒避免同一秒多次告警时文件名覆盖。写入磁盘的 I/O 会阻塞主循环因此文件写入也应该放到子线程或者使用cv2.imencode先编码成内存缓冲再异步写盘。RTSP 流接入的场景下OpenCV 的VideoCapture读流失败是个高频问题。网络抖动会导致read()返回空帧某些摄像头在断开后VideoCapture.isOpened()仍返回 True造成虚假连接。常见做法是每次read()失败计数累加连续 30 帧失败就释放VideoCapture并重新初始化初始化时在CAP_PROP_OPEN_TIMEOUT_MSEC属性上传入超时值。巡检多路摄像头时每一路独立进程或独立线程避免一路断流拖垮整个告警系统。5. 换权重跑自己的工地类别数修改与模型导出加速初始部署用 COCO 预训练权重可以识别 person 和 truck但要精准识别搅拌车、挖掘机、吊车甚至细分不同颜色的安全帽必须用工地自有数据微调。项目根目录的train.py和data/voc.yaml提供了完整训练入口以 3.0 版本的 YOLOv5 写法为例把 VOC 配置改成自己的数据集train: dataset/images/train val: dataset/images/val nc: 4 names: [worker, mixer_truck, crane, excavator]nc是类别总数names按索引排列顺序必须与标注工具导出的标签索引一致。然后执行微调训练python train.py --data data/工地.yaml --cfg models/yolov5s.yaml --weights weights/yolov5s.pt --epochs 100 --batch-size 16 --img-size 640 --hyp data/hyp.scratch.yaml--weights yolov5s.pt表示在预训练权重基础上续训收敛速度远快于从零开始--hyp指向数据增强策略文件hyp.scratch.yaml是官方调好的默认增强参数集。训练日志和results.png会输出到runs/train目录。观察训练结果时重点看precision-recall_curve.png——曲线与坐标轴围成的面积大说明模型在置信度阈值变化时表现稳定labels_correlogram.png反映标注框的分布是否均匀如果类别严重不平衡需要补充少数类样本而不是加长训练时间。训练完成后将best.pt放入weights目录替换掉原来的yolov5s.pt在detect.py里把类别过滤参数改成自己的类别编号即可。此时推理速度如果不够可以用项目内models/export.py将 PyTorch 权重导出为 ONNXpython models/export.py --weights weights/best.pt --img-size 640 --batch 1 --onnxONNX 格式可直接用 ONNX Runtime 或 TensorRT 加速推理在 NVIDIA 显卡上通常能获得比 PyTorch 原生推理高一倍的吞吐。导出后验证输出维度是否与export.py中model.model[-1].nl的推断一致类别数修改后如果出现维度不匹配的报错多数是models/yolov5s.yaml的nc没同步更新。部署到边缘设备时Jetson 系列优先跑 TensorRT普通 PC 用 ONNX Runtime 足够CPU 上还可以开启 OpenMP 多线程这些优化改的是部署框架不需要动检测核心代码。值得额外检查的是runs/exp240/中opt.yaml保存的训练超参数组合这个文件能看到别人的调参按什么策略走的。如果你在自己的数据集上训练结果始终不收敛优先排查data/工地.yaml里train路径是否写成了绝对路径以及样本图片是否统一为三通道 JPEG这两处出错不会报异常模型会在几十轮后输出一堆低置信度框。利用该文件检查标注类别的频次也很有参考价值用results.png的val/obj_loss曲线能判断模型是否在学到目标本身的特征而不是在学习训练集图片的噪声。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门