ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOv8的智慧工地未戴安全绳预警系统实现

基于YOLOv8的智慧工地未戴安全绳预警系统实现 简介基于YOLOv8的智慧工地未戴安全绳预警系统是一套可直接落地的目标检测实践项目面向计算机视觉、人工智能类专业学生和开发者尤其适合用于毕业设计或课程设计。系统集成完整源码、已标注数据集、可视化操作页面和部署说明可生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果与标签分布图覆盖模型训练、评估与推理全流程。整个资源包共97个文件以70个Python源码文件为主体另含4个模型权重文件、5个XML配置文件、2个TXT说明文档以及1个MP4演示视频压缩后大小约24.21MB目录结构清晰。目前已有58人参与学习代码经过测试且功能稳定可支撑毕业设计答辩和课程设计展示同时便于在现有基础之上扩展其他功能或作为目标检测学习进阶的入门参考。1. 为什么“未戴安全绳预警”会让通用检测模型集体翻车先判断这个项目值不值得做在智慧工地的高处作业场景里YOLOv8 做人形检测已经不算新鲜真正难的是“未戴安全绳预警系统”安全绳在监控画面里经常只剩十几个像素又被脚手架、反光衣挡来挡去同一个工人正面看系了、背面看又像没系。这个项目解决的正是这件事。它不直接检测“画面里有没有绳子”而是用 YOLOv8 把“佩戴安全绳”和“未佩戴安全绳”两类状态框出来再用连续帧状态机过滤单帧抖动最后通过可视界面显示实时画面、位置并触发告警形成数据采集、模型训练、界面部署的完整闭环。它特别适合三类人拿来做毕业设计或课程设计的学生想在安监系统里快速出一个可用 demo 的工程师以及刚接触视觉落地、想完整走一遍全流程的入门者。源码包里通常已经带好数据集和权重但真正投入使用时数据怎么标注、模型参数怎么调、界面线程怎么不卡死才是决定系统可不可用的关键。下面按标签设计、训练、界面、部署这条线把它讲透每一段都能直接照着改。2. 把“未戴安全绳”拆成可训练的任务标签体系、数据集清洗与 YOLOv8 选型2.1 先定标签体系检测“有没有系”而不是“找绳子”很多人拿到这个标题第一反应是训练一个检测器去找“安全绳”这个物体。实际做下来会发现这条路坑很深绳子本身是细长形变目标远看只有几个像素被身体挡住一半之后人和绳子共享同一个边界框后处理根本分不清这条绳子属于谁。常见做法是把任务改成“对人的佩戴状态分类”也就是检测人同时把人分成两类——with_harness和without_harness。我一般会把两个类别都做进模型里而不是只检测without_harness一个负类。只做负类的问题在于工地画面里“未系绳”的人占比很高正负样本严重失衡不说模型会把周围环境里的绳子、吊索也当成干扰训练很难收敛。两个类别一起做相当于让模型同时学习“系上的样子”和“没系的样子”决策边界更稳。推理时只挑出without_harness的高置信度框作为告警对象。数据集规模上别被网上动辄几万张的架子吓到。这个任务不是通用检测场景高度固定每个类别有 800 到 1200 个标注实例就能做出可演示的效果。前提是覆盖足够全正面、背面、侧面晴天、阴天、夜间红外近景和远景。尤其是“背面未系”和“绳子垂在腰间但没挂到挂点”这两个形态是最容易漏掉的。2.2 数据从哪来公开数据集收集、自采与 Labelme 标注数据来源一般分三路。第一路是公开数据集平台找safety harness、safety helmet、reflective vest相关的智慧工地数据集注意看标注格式和授权很多是 COCO 或 Pascal VOC 格式需要转换。第二路是自采监控截图从工地现场录一段 20 到 30 分钟的视频按帧抽图只保留有行人的帧。第三路是就地取材做数据增强把现有的图片做光照扰动和拼接弥补雨天、灰尘、遮挡样本的不足。Labelme 是习惯用的标注工具矩形框快多边形框更贴人物轮廓。不管用哪种最终训练 YOLOv8 都要落到class_id x_center y_center width height的归一化文本格式。下面这段脚本把 Labelme 的 JSON 转成 YOLO 格式同时兼容矩形和多边形标注import json import os import glob from PIL import Image def convert_labelme_to_yolo(json_path, out_dir, class_map): with open(json_path, r, encodingutf-8) as f: data json.load(f) # 以实际图片尺寸为准Labelme 的 imageWidth 有的版本会丢 img_path os.path.join(os.path.dirname(json_path), data[imagePath]) img Image.open(img_path) img_w, img_h img.size base_name os.path.splitext(os.path.basename(json_path))[0] lines [] for shape in data[shapes]: label shape[label] if label not in class_map: continue class_id class_map[label] pts shape[points] if shape[shape_type] rectangle and len(pts) 2: x1, y1 pts[0] x2, y2 pts[1] elif shape[shape_type] polygon and len(pts) 3: xs [p[0] for p in pts] ys [p[1] for p in pts] x1, y1, x2, y2 min(xs), min(ys), max(xs), max(ys) else: continue # 过滤掉标注失误造成的退化框 if x2 - x1 3 or y2 - y1 3: continue x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h box_w (x2 - x1) / img_w box_h (y2 - y1) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) if lines: out_path os.path.join(out_dir, base_name .txt) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: class_map {with_harness: 0, without_harness: 1} for jp in glob.glob(labelme/*.json): convert_labelme_to_yolo(jp, yolo_labels, class_map)这段脚本的核心动作是读 JSON拿实际的图片宽高做归一化再按shape_type分开处理矩形和多边形。矩形直接用对角点多边形取所有点的最小包围盒。过滤掉短边小于 3 像素的退化框是因为 YOLOv8 在数据加载时会过滤极端小目标这种标注留着只会让 loss 曲线抖得厉害。转换完后记得检查.txt文件和图片是否一一对应以及类别 ID 是否从 0 开始连续编号。把图片放进images/train、images/val对应的 txt 放进labels/train、labels/val目录结构保持一致即可。这一步是整个项目里最机械又最影响结果的步骤转换脚本建议全项目统一跑不要手工改坐标。2.3 为什么选 YOLOv8结构、性价比与工程生态YOLOv8 能成为这个项目的主流选择不是因为它比所有模型都准而是因为它在“工地监控视频流实时检测”这个约束下性价比最高。网络结构上主干用 C2f 模块替换了 C3颈部保持 SPPF 和 PAN-FPN 的结构检测头改成 Anchor-Free省掉了锚框聚类这层手动设计。虽然 YOLOv8 在 COCO 上的精度提升相比 YOLOv5 不是断档式的但对安全绳这种强语义、弱纹理的目标Anchor-Free 的回归方式更不容易被细长形变带偏。针对 6GB 显存的 GTX 1660 Ti 或 RTX 3060我一般用yolov8s起步。yolov8n虽然更快但小目标召回率明显下降工地监控通常又是 1080p 到 4K 画面里找小人和细绳n级很容易翻车。yolov8m可以放到训练阶段做对比但推理机上如果不想换显卡s更稳。Faster R-CNN 这类两阶段模型在小目标上有优势可 1080p 视频流单帧推理速度跟不上做成毕设演示可以做成实时预警就会卡顿明显。把 YOLOv8 选型和后面的部署一起看还有个现实理由Ultralytics 的predict接口天然支持摄像头、RTSP 流、视频文件和文件夹批量推理画框、导出 ONNX/TensorRT 都有现成命令省掉大量胶水代码。这也意味着拿到这个项目源码后你想换自己的数据重训或者把模型导出到边缘设备迁移成本都在可控范围内。3. 用 YOLOv8 训练自己的数据集Ubuntu 20.04 环境搭建、参数含义与训练命令3.1 Ubuntu 20.04 把最小环境跑起来CPU 版与 GPU 版先把最小可用环境跑通再谈调参。Ubuntu 20.04 上用 conda 管理环境是最省心的做法。CPU 机器同样能训练只是速度慢适合先验证数据和代码链路有 N 卡就装 CUDA 版 PyTorch训练时间能差一个数量级。conda create -n yolo python3.9 -y conda activate yolo # CPU 版适合先跑通流程小数据集也能训练 pip install ultralytics opencv-python pillow matplotlib # GPU 版先把 PyTorch 换成 CUDA 12.1 版再装 ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics opencv-python pillow matplotlib装完后用一行命令验证环境yolo predict modelyolov8n.pt sourcetest.jpg。如果它能自动下载模型并输出一张带框的图片说明依赖链没问题。这里有个容易忽略的点ultralytics会自动安装它依赖的opencv-python有些机器上会和系统自带的 OpenCV 冲突后面避坑章节专门讲。CPU 版训练不是不能跑只是要把数据集规模控制住。一个 1000 张图的训练集用yolov8s在纯 CPU 上可能要跑十几个小时但如果你只是想验证“自己的数据转换有没有问题”可以先把 epochs 设成 5跑完看 loss 有没有下降趋势再上全量。3.2 数据集配置文件train/val 路径与类别名YOLOv8 用 YAML 文件描述数据集。关键是path必须写绝对路径很多数据集路径报错都是因为用了相对路径后/拼接出错。类别名要和标注脚本里的class_map一致顺序不能乱。# harness.yaml path: /home/user/harness_dataset train: images/train val: images/val names: 0: with_harness 1: without_harness这里不做 test 划分也行训练时会从 train 里自动分一部分做验证。如果你的 val 图很少比如不到 50 张建议把 val 指向 train 所在目录用val: images/train先顶上但这时候看到的 mAP 会偏乐观只能作为过程指标。创建完 YAML 后我通常会先跑一步数据校验yolo detect train dataharness.yaml modelyolov8n.pt epochs1。这一步只跑一个 epoch能快速暴露三类问题图片路径找不到、标签类别 ID 越界、标签和图片数量不匹配。等这一步通过再换成正式模型和完整 epochs。3.3 训练命令与 7 个关键参数epochs、imgsz、batch 怎么定正式训练命令如下yolo detect train \ dataharness.yaml \ modelyolov8s.pt \ epochs120 \ imgsz640 \ batch16 \ patience30 \ workers4 \ device0 \ projectruns/harness \ nameexp1modelyolov8s.pt表示加载 COCO 预训练权重做迁移学习而不是从头训练。这对小数据集特别重要主干已经会提取边缘、纹理、人体部件这类通用特征只需要微调检测头去适配“安全绳状态”。参数里最影响效果的几个参数常见取值作用与注意点epochs80150看 loss 不再下降就停不是越大越好imgsz640 / 960小目标多时调 960显存不够就保持 640batch8326G 显存跑 s 模型建议 816patience2050连续 patience 轮 mAP 不涨就早停省时间device0 / cpu多卡用 0,1CPU 用 cpuworkers28过高会在数据加载时报 SharedMemory 错误cacheFalse / TrueTrue 会把数据缓存进内存提速但吃 RAMampTrue半精度训练显存紧张就别关如果训练时显存报 OOM优先把batch降到 8、imgsz降到 640。如果 loss 震荡得很厉害把batch调大或把学习率调低lr00.001往往是这个任务比较稳的起点。1660 Ti 这类 6G 卡跑yolov8s加imgsz640加batch16是能跑的再往上加就会触发显存溢出。3.4 训练过程怎么盯损失曲线、mAP、PR 曲线训练开始后runs/harness/exp1/目录下会持续产出results.csv和results.png。results.png是官方给你画好的全套曲线但很多人觉得它信息太密更习惯自己画损失和 mAP。用 pandas 读 CSV 自己画反而能更清楚看到过拟合点在哪里。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/harness/exp1/results.csv) df.columns [c.strip() for c in df.columns] fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].plot(df[epoch], df[train/box_loss], labeltrain box_loss) ax[0].set_title(Box Loss) ax[0].legend() ax[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) ax[1].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) ax[1].set_title(mAP) ax[1].legend() plt.savefig(loss_curve.png, dpi150)这段代码的逻辑是读取训练日志里的逐 epoch 指标画出边界框损失和 mAP 两条主线。mAP50指的是 IoU 阈值在 0.5 时的平均精度mAP50-95是 0.5 到 0.95 按步长 0.05 的平均后者更严格小目标涨得慢是正常的。我判断训练是否结束的标准很简单mAP50连续 20 个 epoch 不涨同时train/box_loss不再下降就早停取best.pt不用硬等 120 轮跑满。训练中断了也不用慌yolo detect train resume modelruns/harness/exp1/weights/last.pt能接着跑。这是很多人不知道的后悔药。4. 把模型变成“会预警的系统”可视界面、推理链路与报警状态机4.1 界面选型PyQt5 桌面端还是 Flask Web 端模型训好之后项目包的“可视化界面”一般有两种形态PyQt5 桌面客户端和 Flask/Streamlit 网页端。毕设和课设答辩场景我强烈建议 PyQt5 桌面端。原因很实际答辩时能本地连摄像头演示画面流畅还能现场展示报警截图不会因为现场网络问题翻车打包成 exe 后单独一台电脑就能跑演示成本最低。如果目标是“多路摄像头远程查看”Flask 更合适后端调模型前端网页实时刷新帧。但 Web 端要处理的并发、流媒体转发细节更多作为课程设计容易把精力耗在不必要的工程问题上。Streamlit 适合快速做原型展示写报警记录和统计图表非常快但做实时视频流体验一般。方案适合场景开发成本实时视频体验PyQt5单机演示、答辩、本地监控中等好Flask局域网多路查看中高一般需配合 MJPEG 或 WebSocketStreamlit快速原型、告警记录展示低弱4.2 摄像头实时推理链路读帧、跳帧与检测实时推理的核心不是“每帧都检测”而是“每帧都来得及处理”。工地监控如果是 RTSP 流直接把每一帧送进模型大概率会越来越卡因为检测速度跟不上视频帧率积压帧全在排队。常见做法是跳帧比如每 2 帧取一帧检测或者把分辨率先缩到 640 再送模型。import cv2 import time from ultralytics import YOLO model YOLO(runs/harness/exp1/weights/best.pt) cap cv2.VideoCapture(rtsp://192.168.1.64:554/stream1) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) frame_interval 2 frame_idx 0 while True: ret, frame cap.read() if not ret: time.sleep(0.5) cap.open(rtsp://192.168.1.64:554/stream1) continue if frame_idx % frame_interval ! 0: frame_idx 1 continue frame_idx 1 results model(frame, conf0.45, imgsz640, verboseFalse)[0] boxes results.boxes.xyxy.cpu().numpy() cls_ids results.boxes.cls.cpu().numpy().astype(int) confs results.boxes.conf.cpu().numpy() for box, cls_id, conf in zip(boxes, cls_ids, confs): if cls_id 1: x1, y1, x2, y2 map(int, box) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, fno harness {conf:.2f}, (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imshow(harness monitor, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码把推理链路分成读帧、跳帧、检测、画框四步。CAP_PROP_BUFFERSIZE设为 1 是避免 OpenCV 内部缓存堆叠旧帧否则画面延迟会越来越大。conf0.45是置信度阈值调低会提高召回率但误报也会变多。RTSP 掉线时cap.read()会一直返回 False这里用time.sleep(0.5)加重新open的方式做断线重连比直接退出循环实用得多。4.3 报警状态机连续帧确认、冷却与截图落库单帧检测结果直接触发报警是这类系统最常见的翻车点。检测框是抖动的可能这一帧没系、下一帧检测器又认为系了一秒内报警三次现场值班人员直接把系统关掉。报警必须加状态机连续多帧都检测到未佩戴才报警报警后进入冷却时间。UNSAFE_THRESHOLD 5 COOLDOWN_FRAMES 150 no_harness_count 0 cooldown 0 while True: # ... 上面的推理代码拿到 unsafe_detected 布尔值 ... if cooldown 0: cooldown - 1 continue if unsafe_detected: no_harness_count 1 else: no_harness_count max(0, no_harness_count - 1) if no_harness_count UNSAFE_THRESHOLD: alarm_time time.strftime(%Y-%m-%d_%H-%M-%S) cv2.imwrite(falarm_{alarm_time}.jpg, frame) print(f{alarm_time}: unsafe detected, saving snapshot) no_harness_count 0 cooldown COOLDOWN_FRAMES这里的UNSAFE_THRESHOLD连续 5 帧确认配合 150 帧的冷却能过滤掉检测器单帧抖动和工人短暂弯腰造成的误判。触发报警后把当前帧存成截图这是毕设答辩时最有说服力的证据报警时间、报警图片、检测框都在。如果想要更完整的记录可以在报警时把时间、置信度、类别写进 SQLite后续做成“当日告警统计”展示。4.4 界面线程布局别让模型推理卡死 PyQt5 界面PyQt5 界面最忌讳把模型推理直接塞进主线程。model(frame)单帧就要几十毫秒主线程被占住后窗口拖不动、按钮点不了看起来像死机。正确做法是开一个后台线程跑推理通过信号把带框画面和报警事件发回主线程。from PyQt5.QtCore import QThread, pyqtSignal class InferenceThread(QThread): frame_ready pyqtSignal(object) alarm_triggered pyqtSignal(str) def run(self): # 在这里执行 4.2 的循环 # 每得到一帧带框图像self.frame_ready.emit(frame) # 每次触发报警self.alarm_triggered.emit(alarm_time) pass主线程只做两件事接收frame_ready信号更新 QLabel 的 pixmap接收alarm_triggered信号弹出提示或播放声音。这样即使现场网络波动导致某帧耗时飙到 1 秒界面也只是画面刷新变慢不会整个窗口无响应。5. 部署与运行避坑从“能跑”到“敢用”之间的五道坎5.1 依赖冲突libGL.so.1 与 OpenCV 玄学现象装完ultralytics后代码一import cv2就报错提示libGL.so.1: cannot open shared object file或者训练到一半提示numpy.ndarray size changed。原因opencv-python依赖系统的 libGL 图形库纯服务器环境没装桌面组件就会缺这个库numpy版本和opencv、torch的预编译库版本错位时会出现二进制接口不匹配的报错。解决先统一卸载重装一个组合pip uninstall opencv-python opencv-python-headless然后只装一个opencv-python4.8.1.78同时固定numpy1.26.4或numpy2.0。如果服务器确实没有 GUI 环境就改成装opencv-python-headless代码里不要调用cv2.imshow界面用 PyQt5 的 QLabel 显示即可。这类问题一定要在装好环境后先跑一次import cv2和from ultralytics import YOLO再开始训练别把问题留到部署阶段。5.2 显存不足与 CPU 推理太慢降级、半精度与边缘部署现象训练时torch.cuda.OutOfMemoryError推理时 CPU 占用拉满但只有 2 帧每秒根本没达到“实时预警”的最低标准。原因训练imgsz960batch32对显存是灾难推理端用 CPU 跑yolov8s在 640 分辨率下也不过 5 到 10 FPS。如果现场是 4K 摄像头把整帧直接送进模型速度还会再掉一半。解决训练侧把batch降到 8imgsz保持 640必要时把cacheFalse关掉数据缓存。推理侧先缩放到 640 再检测输出框坐标映射回原图开model(frame, halfTrue)用半精度推理1660 Ti 这类卡能有明显提速。真正要上工地现场常见方向是边缘部署把best.pt导出 ONNX 再转 TensorRT或者跑到 RK3588、海思 HI3516 这类带 NPU 的板子上做硬解码和模型加速这个方向可以后续在做“边缘部署”时单独展开。5.3 小目标漏检远距离安全绳为什么看不见现象训练时 mAP50 能到 0.9但拿到工地实拍视频里一测远端小尺寸的工人漏检严重安全绳的检测率几乎为零。原因这是典型的小目标问题。安全绳在 1080p 画面里可能只占 10×10 像素标注框短边小于 3 像素时还会被数据加载器直接丢弃。训练图里这类小目标占比少模型自然没学会。解决数据层面标注时保留这类小框但单独统计一下短边像素分布如果绝大多数小于 10 像素建议训练时把imgsz调到 960 再试。推理层面可以用切图检测把原图分成 2×2 的块分别送模型再按坐标拼回结果。改动更大一些的做法是改进网络在 YOLOv8 的颈部增加 P2 小目标检测头或者在主干后用可变形卷积增强细长目标特征。这类“yolov8改进”在论文里常见但要记住改进带来的显存和速度代价会让实时性更难保证建议先用切图这种不改模型的手段。5.4 报警风暴与误报是谁把值班人员逼到关系统现象系统上线后一分钟报警三十次误报率极高现场人员最后直接把声音关掉系统的预警能力形同虚设。原因报警逻辑只做了“当前帧有未佩戴的人”这一个判断没有做持续性和区域约束。工人从摄像头前经过、反光衣的拉链反光、远处脚手架上的安全绳被风吹起都可能被模型当成目标。单帧阈值设低了之后误报会成倍放大。解决至少做两层过滤。第一层是状态机用连续 5 帧确认代替单帧判断具体逻辑见 4.3。第二层是 ROI 区域用cv2.fillPoly把高空作业区以外的区域涂黑只检测 ROI 内的目标让脚手架、通道、地面上的行人全部排除。报警时再做一个最小间隔时间限制比如同一目标 10 秒内只报一次。这样处理后误报通常能压到一小时一两次以内。5.5 长时间运行的卡死与断流线程、队列与重连现象系统跑一两个小时就开始卡顿画面停住内存占用缓慢上涨最后进程被杀或者 RTSP 视频流断掉后程序直接崩溃。原因推理线程和界面线程之间用了无限增长的队列帧积压越来越多VideoCapture断流后没有重连机制每次报警截图都存入列表不清理内存只进不出。解决队列用queue.Queue(maxsize1)或maxsize3丢旧保新保证推理速度跟不上时不会积压。RTSP 层做断线重连cap.read()返回 False 连续超过 10 次就cap.release()后重新cap.open()。报警截图写文件的同时把“最近 100 条报警记录”的索引列表限制长度不要无脑累积。排查这种问题时先看三处任务管理器里内存是不是线性上涨队列长度是否越堆越高日志里cap.read()是否周期性返回 False。一条一条对完稳定性会好非常多。6. 最后一道工序盲测验证与一个容易被忽略的标注技巧模型训练和界面都跑通不意味着项目可以交付。最后一道工序是用“从没参与训练的视频”做盲测而不是用训练时会泄露结果的测试集截图反复演示。我的做法是单独留出一段连续工地监控录像包含白天、黄昏、雨雾和夜间红外四种时段让系统完整跑一遍统计三件事未系绳目标的检出率、误报截图数量、单帧平均耗时。检出率用“人工数出未系人员出现的帧数”除以“系统报警帧数”来估算误报则靠报警截图人工复核。只有把这两个数字记下来才能在答辩或验收时回答“你的系统到底准不准”这类问题。其次是标注问题也是我踩过最大的坑很多人只标了“系了”和“没系”两种干净状态结果模型把“手里拿着绳子但没挂到挂点”的人识别成已系或者工人弯腰系绳的过渡姿态触发报警风暴。现在我要求数据集里必须包含两类中间态样本“绳子在手上/肩上但挂点未连接”归入未佩戴“正在系绳、即将完成”归入已佩戴。标注前把这些形态定义写成一页规范标出来的数据质量会明显不一样。这也是我接手这类项目时养成的习惯先不看代码先要一段现场视频把“没系”的所有形态列清楚再标数据。系统上线前再拿一个工作日的录像连续跑满 12 小时确认没有内存泄漏和断流问题。这套流程走完这个“基于 YOLOv8 的智慧工地未戴安全绳预警系统”才算真正从演示变成可用工具希望帮到你。本文还有配套的精品资源点击获取
返回列表