ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

YOLOv11安全帽检测与高空作业预警:训练调参到Jetson部署实战

2026/9/29 2:44:21 拓冰建站 浏览量
YOLOv11安全帽检测与高空作业预警:训练调参到Jetson部署实战 简介面向建筑工程安全管理与计算机视觉应用人群这份《建筑工程中的YOLOv11-安全帽检测与高空作业风险预警实战》PDF文档系统讲解了如何利用YOLOv11单阶段检测算法完成施工现场安全帽佩戴识别与高空作业风险预警。文档从YOLO系列发展历程、网络结构和工作原理切入完整覆盖安全帽数据集收集、标注、预处理到模型训练、优化与评估再到风险预警系统的需求分析、架构设计、实现部署与实战案例内容贴近工程落地场景适合有初步深度学习基础、希望将目标检测技术应用到工地安全监控的开发者或研究人员参考。资源包共1个PDF文件大小2.24MB文档共37页支持目录章节跳转及阅读器左侧大纲快速定位排版清晰、图文表完整。目前已有46人学习下载。读者可借此掌握从数据标注如LabelImg、RectLabel、LabelBox到模型调优剪枝、量化的完整实施链路并理解预警阈值设定、界面设计等系统集成细节是一份可快速上手的实战型参考资料。1. 安全帽检测与高空作业预警为什么这个项目大家都要用YOLOv11我最早接触 YOLOv11-安全帽检测与高空作业风险预警这个方向是在一个工地智能化改造的需求单上。摄像头已经架到塔吊和爬架上拍到的人小、帽子更小旧模型要么把黄色油漆桶当帽子要么戴着安全帽的人也漏掉。换到 YOLOv11 之后小目标检测能力和推理速度才真正压得住工地现场的场景。这篇笔记就是把我从准备数据、训练调参到 Jetson 上部署、联动高空作业预警这一整条链路拆开讲给准备上这个项目的团队省掉我踩过的那些坑。适合的对象很明确有摄像头和标注数据想在本地跑通验证再决定要不要买算力卡的人。你不需要先懂论文公式但你要会跑命令行、会看损失曲线。2. 准备YOLOv11环境与训练数据从Anaconda到数据集划分2.1 YOLOv11的网络结构三个模块决定了你训练上限做工程的人常问“我要不要改网络结构”回答这个问题前得先知道 YOLOv11 由哪几块组成。它仍然是 backbone neck head 的框架backbone 负责提特征neck 负责把不同尺度的特征融合head 负责输出分类和回归框。真正和“安全帽检测”相关的改动集中在 head 部分的解耦结构分类分支和回归分支分开这让小尺寸的帽子不会因为分类压力太大而被回归分支带偏。另一个值得关注的是 C3k2 模块它替代了早期的 C3在保持特征复用的同时计算更省这让 YOLOv11 在 Jetson 这类低算力设备上还有机会跑实时。实际调优时不需要重写这些模块你要做的是理解两个选择。第一模型尺寸选 n、s、m 还是 l工地边缘盒子一般选 n 或 s第二是否引入注意力机制比如热词里的 HCANet它的思路是对通道和空间两个维度做并行注意力放到 neck 输出端能让“远处的小安全帽”在特征图上有更强响应。不要一上来就魔改 backbone先在基线模型上把数据质量做对再决定动哪里。2.2 环境配置一个能跑起来的minimal命令YOLOv11 环境配置最怕的是把 PyTorch、CUDA、cuDNN 版本弄得各管各最后训练时 OSError 崩掉。我一般用 Anaconda 建独立环境Python 版本锁在 3.10。CUDA 先看显卡驱动支持的最高版本再决定装哪一版 PyTorch而不是反过来装最新版。下面这套是在 Linux 服务器上的最小命令Windows 上把activate换成conda activateCUDA 换成对应的 cu121 轮子conda create -n yolo11 python3.10 -y conda activate yolo11 pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics第一行创建独立环境隔离系统自带的 Python第二行激活第三行装 PyTorch 时要特别注意--index-url指定 CUDA 12.1 的轮子--index-url参数会把 torch 和 torchvision 同时固定到 cu121最后一行装 Ultralytics 官方库它内部会带上训练、验证、导出的 CLI 工具。装完后别急着训练先跑一个yolo predict modelyolo11n.pt sourcebus.jpg验证推理链路通不通。这一步花五分钟能排除八成后续报错。2.3 标注与数据划分不要被安全帽数据集的坑带着走安全帽检测的数据集公开的不少但工地上用会有两个问题。一是“帽子戴在头上”的样本远多于“帽子拿在手里”的样本模型会学成“头上有帽”而不是“帽在头上”二是远景安全帽只有几个像素标注框稍微画大一点框中心偏移就够模型学乱。因此我强烈建议你如果手头有现场摄像头至少自己补标 30% 的镜头画面不要全部依赖公开数据集。标注格式我选择 YOLO 的 txt一行代表一个目标class x_center y_center width height坐标都是归一化到 01 的小数。LabelMe 导出的 JSON 或 VOC 的 XML 都要先转成这个格式再喂给 YOLOv11。数据划分上我的习惯是训练集 70%、验证集 20%、测试集 10%而且划分时按“同一个摄像头视角”分组避免同一个场景的视频帧同时出现在训练和验证里导致验证分数虚高。import os, random, shutil dataset datasets/helmet_project images [f for f in os.listdir(f{dataset}/images) if f.endswith(.jpg)] random.seed(42) random.shuffle(images) train, val, test images[:int(len(images)*0.7)], images[int(len(images)*0.7):int(len(images)*0.9)], images[int(len(images)*0.9):] for name, split in [(train, train), (val, val), (test, test)]: os.makedirs(f{dataset}/{split}/images, exist_okTrue) os.makedirs(f{dataset}/{split}/labels, exist_okTrue) for img in split: shutil.copy(f{dataset}/images/{img}, f{dataset}/{split}/images/{img}) label img.replace(.jpg, .txt) if os.path.exists(f{dataset}/labels/{label}): shutil.copy(f{dataset}/labels/{label}, f{dataset}/{split}/labels/{label})这段脚本先把所有图片列表打乱再按 7:2:1 切出三个文件夹同时把同名 label 拷过去。random.seed(42)很关键否则每次运行分组不同复现实验就变猜大小。实际项目中你还要检查切分后每个类别的样本数比例安全帽类别样本太少时先做简单复制增强否则训练时类别损失会把少数类压没。3. 训练安全帽检测模型参数设置、小目标优化与改进3.1 基线训练一条完整的命令行数据准备完成之后先用 YOLOv11 官方配置跑一个基线不做任何魔改。这一步的意义是建立“分母”后面所有改进都要和它对比。我的建议是从yolo11s开始因为n虽然快但小目标检测上限低m在边缘设备又跑不动。训练命令如下yolo detect train \ modelyolo11s.pt \ datahelmet.yaml \ imgsz640 \ epochs120 \ batch16 \ device0 \ patience20 \ projectruns/train \ namehelmet_baseline参数逐一说清楚modelyolo11s.pt是从官方预训练权重继续训练不要从随机权重开始datahelmet.yaml指向你数据集配置里面写训练/验证路径和类别名imgsz640是输入分辨率安全帽小目标场景我后面会调大batch16取决于显存12G 卡这个数比较稳patience20是连续 20 个 epoch 验证损失不下降就停止避免无效烧卡。跑起来之后你要盯两个东西train/box_loss和train/cls_loss曲线。如果你看到 box_loss 快速下降后停滞而 cls_loss 还在波动说明模型在“找得到位置但认不清类别”这往往是小目标类别语义不足不是训练轮数不够。这时候不要盲目加 epoch先看下一节的小目标优化。3.2 必调参数imgsz、epochs、batch、patience 的含义与建议值这四个参数每个都直接影响结果但它们的作用点完全不同。imgsz影响输入图像被缩放到多大安全帽在一张 1080p 画面里可能只有 20×20 像素缩到 640 就只剩 12×12 左右特征图上的响应会被池化抹掉所以小目标场景我至少开到 960代价是训练和推理变慢epochs不是越大越好训练后期模型会过拟合现场光影我见过有人跑到 300 个 epoch验证集 mAP 反而掉了batch影响 BN 统计量的稳定性batch 太小比如 4安全帽这种样本数不平衡的数据集很容易在最后几十个 epoch 震荡。patience很多新手直接不设结果模型在验证集上已经过拟合两周还在跑。我的建议是设成epochs的 15%20%比如 120 个 epoch 就设 20。有时你重启训练会发现损失曲线比之前掉得更快那是因为 warmup 又跑了一遍不是模型变好了不用慌。模型在验证集上的 mAP50-95 连续 20 个 epoch 不涨时就该停再去分析数据而不是加轮次。3.3 小目标优化为什么安全帽总是漏检我做了三件事安全帽检测最大的痛是漏检不是误检。漏检集中在两个位置远处出入口的小帽檐和俯视视角下只露出头顶的小圆点。针对这两个情况我按顺序做了三件事每一步都重新测验证集。第一步调高输入分辨率到 960同时把mosaic增强概率从默认 1.0 降到 0.5。Mosaic 把四张图拼在一起训练能丰富背景但安全帽框本身太小拼图后帽子被裁掉的概率变大反而教坏模型。第二步修改数据增强参数让hsv_h降低到 0.01工地安全帽的黄色和红色是一类强语义特征颜色抖动太狠会让模型靠纹理而不是颜色来认帽子。第三步是给 loss 里的小目标分配更高权重这一步在配置文件里改# helmet.yaml 中的关键增强与损失配置 task: detect imgsz: 960 augment: mosaic: 0.5 hsv_h: 0.01 hsv_s: 0.5 hsv_v: 0.5 loss: box: 7.5 cls: 0.5 dfl: 1.5box: 7.5表示框回归损失权重高于默认小目标位置稍有偏差 IoU 就掉很多需要拉高让模型更在意框准cls: 0.5相对低是因为安全帽类别数少且差异明显不需要给分类太大压力。跑通后对比基线你会发现 mAP50 可能只涨 1~2 个点但漏检帧数减少三分之一这个指标比 mAP 更贴近工地验收。读者要注意小目标优化没有银弹。你还需要检查 label 的质量很多“漏检”其实是标注框比目标大一圈帽檐的语义完全落在框内灰边里模型学不到帽檐边界。把这类坏标注清洗一轮效果比改任何参数都明显。3.4 从YOLOv11到改进模型HCANet 注意力的嵌入思路当数据质量和分辨率都到头了再往下挖就需要改结构。热词里的 HCANet 是一种混合注意力网络核心思想是通道注意力与空间注意力并行再把两路特征相加后与原始主干特征相乘。嵌入到 YOLOv11 时我选择的插入点是 neck 的上采样之后也就是 P3、P4 特征即将进入检测头之前。下面这段示意代码展示了在 YOLOv11 的 head 前插入一个简化的 HCANet 模块工程上你可以把这段写成自定义 nn.Module 再挂到模型上import torch import torch.nn as nn class HCANet_Block(nn.Module): def __init__(self, channels): super().__init__() self.ch_att nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(channels, channels // 4, kernel_size1), nn.ReLU(inplaceTrue), nn.Conv2d(channels // 4, channels, kernel_size1), nn.Sigmoid() ) self.sp_att nn.Sequential( nn.Conv2d(channels, 1, kernel_size7, padding3), nn.Sigmoid() ) def forward(self, x): ch self.ch_att(x) sp self.sp_att(x) return x * (ch sp)通道注意力用全局平均池化计算每个通道的重要性空间注意力用 7×7 卷积计算每个像素位置的重要性二者相加后与原始特征相乘。这样做的好处是远处安全帽的微弱响应会乘上一个大于 1 的空间权重但要注意channels // 4后的通道数不能太小我试过在 Jetson 上通道压缩到 8推理时间反而变长因为低算力设备对逐元素乘法更敏感。嵌入方式不唯一常见做法是加在Detect层的前面然后用ultralytics的model.load接口加载改动后的权重。插入模块之后必须重新小学习率训练我从lr00.0005开始比基线小一半否则预训练权重会被注意力模块的随机初始化破坏掉。如果你发现加入注意力后 mAP 反而降了先检查是不是训练轮数不够这个模块收敛慢我通常比基线多训练 40 个 epoch 才能看到收益。4. 推理结果保存与高空作业风险预警从单张到视频流4.1 保存检测结果两种常用方式很多人在 Ultralytics 里跑完检测不知道结果存哪。YOLOv11 默认会保存在runs/detect/predict下面带标注框的原图和标注后的 label 文本都有。如果你要在自己的业务系统里用我一般用下面两种方式之一。第一种是直接调用model.predict并指定saveTrue适合批量离线分析历史监控片段from ultralytics import YOLO model YOLO(runs/train/helmet_baseline/weights/best.pt) results model.predict( sourcedemo_clip.mp4, imgsz960, conf0.35, saveTrue, save_txtTrue, projectruns/infer_helmet )这里的conf0.35是置信度阈值工地现场光线杂我一般放 0.35室内场景可以放在 0.45save_txtTrue会把每个目标的类别和坐标写成labels下的 txt 文件这个文件就是后面风险预警的输入。结果保存在project指定的目录里不会覆盖原视频。第二种是实时逐帧处理时不保存图片而是把结果对象直接传给业务函数。这样省掉磁盘 IO还能在每帧里做判断。4.2 预测后保存如何把坐标和置信度落到结构体里预测之后真正有用的是boxes对象。它包含xywhn归一化坐标、conf和cls。我会把一帧的检测结果整理成列表再交给后续的风险预警模块这样代码边界清晰调试也方便def parse_results(result, shape): parsed [] boxes result.boxes if boxes is None: return parsed xywhn boxes.xywhn.cpu().numpy() confs boxes.conf.cpu().numpy() clss boxes.cls.cpu().numpy().astype(int) for i in range(len(clss)): x_c, y_c, w, h xywhn[i] if confs[i] 0.35: continue parsed.append({ class: int(clss[i]), conf: float(confs[i]), x: float(x_c), y: float(y_c), w: float(w), h: float(h) }) return parsedxywhn是归一化中心点坐标不管原图是 1080p 还是 4K都换算成 0~1 的小数。这样后面做“画面区域划分”时不会受分辨率影响。shape参数是你自己传入的原图尺寸如果想转成像素坐标就在这步乘回去。常见错误是直接拿归一化坐标去画像素框结果框漂移。另外注意每个循环别忘置信度过滤否则后面会把一堆 0.1 置信度的噪点框当成真实目标。4.3 高空作业风险预警逻辑安全帽状态与区域规则的组合很多团队做到检测就停了但标题里的“风险预警”才是验收重点。高空作业预警不是“有人没戴安全帽就报警”这么简单否则工地上会全天误报。我实现的规则分两层。第一层是“人-帽子”配对。检测模型输出的类别我设为person和helmet两类然后用同一个人的检测框来找对应安全帽框。判据是安全帽框的中心点是否落在人框的头部区域人框上 20% 高度范围内并且帽框面积与人框面积之比要在 8% 到 30% 之间。这个比值区间很关键太大说明帽子框包含整个头太小说明是误检远处的杂物。第二层是区域规则。通过画多边形区域判定作业区只有人框中心点进入危险区域才触发预警。下面这段是核心判断逻辑def judge_risk(person, helmet_list, danger_polygon): # person 和 helmet_list 都是 parse_results 输出的对象 person_x, person_y, person_h person[x], person[y], person[h] head_top_y person_y - person_h / 2 safe_zone_y head_top_y 0.1 * person_h has_helmet False for h in helmet_list: # 帽框中心是否在头部区域 if abs(h[x] - person_x) person_h * 0.35: if head_top_y h[y] safe_zone_y: has_helmet True break # 点是否落入多边形危险区 inside cv2.pointPolygonTest(danger_polygon, (person_x * frame_w, person_y * frame_h), False) 0 return inside and not has_helmet危险区域多边形的顶点用标注工具画一次固化到配置文件里。注意pointPolygonTest接收的是像素坐标所以前面parse_results拿到的中心点要乘回画面宽高。实际工地上爬架区域往往被防护网遮挡人的检测框会时断时续这时我会加一个“连续 3 帧内超过 2 帧触发”的缓冲避免单帧丢框导致误报。cv2的光照变化会让预测不稳定必要时在进检测模型前先做 CLAHE 增强。4.4 摄像头场景下的帧率控制YOLOv11s 在 GPU 上能跑到 60fps 以上但到 IPC 摄像头流里就未必因为拉流解码和推流上报都可能卡住。我在部署中固定一个方案用 OpenCVVideoCapture读检测线程和显示线程分开。检测线程只对抽帧后的图像处理每 3 帧取 1 帧作为检测帧中间帧沿用上一帧的目标位置利用视频连续性减少压力。重点关注队列积压问题如果检测线程处理帧的速度赶不上抽帧queue会堆积延迟越来越高。我常用的做法是检测线程开始前grab一帧直接丢掉相当于主动丢帧保证处理的是最新画面。对于 Jetson 这类设备宁愿降低到 8~10fps 保持稳定延迟也不要卡顿 5 秒后才看到上一秒的图像。部署监控画面上看到延迟超过 500ms 时优先检查拉流缓冲而不是去看模型耗时。5. 部署到Jetson Nano与常见问题排查血泪经验5.1 Jetson Nano部署YOLOv11的完整步骤工地边缘盒子里 Jetson Nano 很常见但它只有 4G 内存部署 YOLOv11 需要一套完全不同于服务器的流程。我的第一步是刷 JetPack 4.6它自带 CUDA 10.2与最新版 PyTorch 冲突很大。Ultralytics 官方没有直接给 Nano 的安装包所以用下面的步骤从源码装 torchapt-get update apt-get install -y python3-pip libopenblas-dev pip3 install numpy1.19.5 wget https://github.com/ultralytics/yolov5/releases/download/v1.0/pytorch-v1.9.0.whl pip3 install pytorch-v1.9.0.whl pip3 install ultralytics第一行装 OpenBLAS 是 torch 依赖的线性代数库第二行锁 numpy 版本太高版本的 numpy 在 JetPack 的 aarch64 Python 上会编译失败第三行这里是示意实际要找到对应 JetPack 版本的 torch 轮子名。装完 Ultralytics 后先导出成 TensorRT 引擎再推理。不要直接在 Nano 上用.pt文件做预测PyTorch 推理吃内存跑几帧就 OOM。导出命令一般是这样yolo export modelruns/train/helmet_baseline/weights/best.pt formatengine device0formatengine要求你已装好 TensorRT 版本JetPack 自带trtexec。导出前建议先把imgsz640固定因为 engine 文件是绑定输入尺寸的导出后就不能随便改。我在 Nano 上实际场景只跑 640 分辨率因为 960 会让 engine 占用内存超过 1.4G和系统共用 4G 时就容易崩。5.2 编译问题与内存限制最典型的问题是在 Nano 上pip install ultralytics时编译torchvision报No matching distribution found。原因是 PyPI 默认仓库没有 aarch64 的 torch 轮子。解决方法是换用 NVIDIA 的官方索引或者直接下载 JetPack 对应的轮子文件离线安装。另一个问题是 4G 内存无法同时跑推理和 GUI我在部署时用systemd把检测进程设为开机自启只保留 SSH不启动桌面。5.3 模型加载失败现象、原因、解决记录我在项目现场遇到过三次“加载引擎文件失败”。第一次现象是Engine deserialization failed。原因是我在服务器上导出 engine然后直接拷到 Nano 上两边的 TensorRT 版本不一致。解决方法是必须在 Nano 本机重新导出或者保证两端 TensorRT 大版本完全一致。第二次现象是加载成功但推理几秒后CUDA out of memory。原因是我没有检查其他进程占显存nvtop一看还有个残留的 Python 进程。解决方法是先kill掉旧进程再设置torch.cuda.set_per_process_memory_fraction(0.5)把显存占用限制在系统可接受范围。第三次现象是import torch直接报Illegal instruction (core dumped)。原因是 Nano 的 ARM CPU 不支持编译时的某项指令集。解决方法是换用 JetPack 官方预编译的 torch 版本不要在自己交叉编译时开启过高的-march优化。5.4 检测不稳定现象、原因、解决记录模型在 Nano 上跑得很稳但现场框的位置会一跳一跳。排查后发现两个原因一是画面里有压缩噪声老旧的模拟摄像头转网络流后噪声直接变成高频特征导致同一帧检测框左右漂移。解决方法是进模型前加一层轻量滤波import cv2 cap cv2.VideoCapture(rtsp://camera/stream) while True: ret, frame cap.read() frame cv2.medianBlur(frame, 3) results model.predict(frame, conf0.4, verboseFalse)medianBlur比GaussianBlur更适合保留边缘同时去除椒盐噪声让框的抖动明显下降。二是季节光线的变化冬天下午 3 点的低角度阳光会让帽子反光安全帽框被削弱。我不是去改模型而是在系统里增加一个“时段阈值切换”中午用高阈值 0.45早晚低光照用 0.3。阈值切换不需要重新训练但需要你把一天不同时段的数据各抽 200 帧做一次小验证确认误报率可接受。6. 最后的落地技巧验证指标、蒸馏与离我最近的一次教训模型训练到最后验证指标不要只看 mAP50安全帽检测的真实验收我推荐三个数字漏报率、误报率和平均延迟。mAP50 只衡量框定位能力而漏报率才是工地上真正会闹出伤情的事故指标。我通常会从一整天监控里均匀抽 5000 帧标出来算漏报率这个动作虽然累但能发现白天和夜间不同光线下的真实差异。夜间画面如果黑乎乎YOLOv11 会大量漏报这是正常现象不要指望模型万能给摄像头加补光比换模型更有效。另一个能明显提高部署效能的技巧是蒸馏。把前面训练出的 YOLOv11m 作为教师模型蒸馏到 Nano 上能跑的 yolo11n。做法很简单用教师模型推理输出的 logits 作为软标签让学生模型同时拟合硬标签和软标签。我试过在安全帽数据集上蒸馏后的 n 模型 mAP 比直接训练高 2 个百分点左右而且推理速度完全没变。这部分实现不要自己手写 lossUltralytics 官方就支持蒸馏训练的结构你只需提供教师权重和蒸馏温度参数。最后说个真实教训有次我把训练集增广时不小心开了rotate90 度工地画面是固定的俯视角度歪着的安全帽根本不会出现在真实场景结果模型训练 mAP 很高到了现场把侧躺的帽子当漏检。自那以后我给自己立了个规矩每个增强参数都要问一句“现场会出现这种情况吗”。增强不能为了刷分牺牲真实分布否则再贵的模型都是纸面指标。希望这些记录能帮你在 YOLOv11 安全帽检测这个方向上少走几趟弯路。本文还有配套的精品资源点击获取