简介:基于YOLOv5的烟雾检测解决方案,面向安全监控、火灾预警与智慧园区等场景的开发者、研究者和学生,解决实时烟雾识别及预警难题。整个资源包共2000个文件,压缩后360.52MB,内含3688张带标注的烟雾图像、对应txt与xml标签文件,以及训练好的pt权重、Python推理脚本和yaml配置模板,覆盖数据准备、模型训练、推理部署的完整链路。已有2355人参与学习下载。模型经多种环境烟雾样本训练,能够区分烟雾与雾、灰尘等干扰物,直接加载即可用于视频流检测;数据集包含不同光照、角度和背景的样本,便于针对具体场景微调,适应现场复杂条件,降低误报漏报风险。资源中还包含YOLOv5源码相关组件、项目目录说明与必要配置文件,适合希望快速集成实时烟雾报警系统,或进一步研究目标检测技术的开发者和研究者使用。
1. 从训练日志到实时检测框:这包YOLOv5权重和数据集开箱即用
在工厂仓库或者小区楼道里,烟雾初起的几十秒往往决定了火灾能不能被掐灭在源头,而大多数常规监控摄像头根本「看不见」烟雾——它半透明、会飘散、颜色还随着环境光变化。这个基于YOLOv5的烟雾检测项目,把深度学习目标检测模型应用到火灾预警场景,压缩包里同时带了训练好的权重和一份标注好的烟雾数据集。解压之后不需要去标一张图、跑一轮训练,直接用权重跑一次推理,画面里的烟雾区域就会被框出来。适合两类人:一类是安防、消防领域的开发者,想把检测能力接进现有监控链路;另一类是刚入门目标检测的新手,想拿一套完整工程把数据集、训练、验证、推理全流程走通。
2. 工程全景与目录结构:权重、标签、训练脚本一次定位
2.1 技术选型:烟雾检测为什么落在YOLOv5上
烟雾在图像里的表现很特殊:边缘模糊、颜色从白到灰黑都有、形状随风变化。传统视觉方案用颜色阈值或者背景建模只能处理固定场景,换个光照角度就翻车。YOLOv5 这类 anchor 检测器靠学习大量样本的纹理和上下文特征,对「半透明、无固定形状」的目标反而比想象中稳。它把整张图切成网格,每个网格预测若干个候选框,再回归出目标的类别和坐标,本质上是把检测问题变成了一次卷积网络的回归任务。
选 YOLOv5 而不是更新的检测框架,有三个现实原因。第一,它的工程生态最全,训练、验证、导 ONNX、转 TensorRT 都有现成脚本,接监控系统的成本低。第二,模型体积和精度之间的档位清楚,s/m/l/x 四种规格可以按机器算力换,边缘设备用 s 级,服务器用 l 级。第三,烟雾检测是单类目标,数据量通常不大,用 COCO 预训练权重做迁移学习,几百张标注图就能收敛出能用的模型。换更复杂框架反而要重新调 embedding 和 anchor,性价比不高。
2.2 目录结构与关键文件清单
解压之后先别急着跑命令,把目录认清楚能省掉很多弯路。整个工程的骨架是这样:
smoke_yolov5/ ├── dataset/ │ ├── images/ │ │ ├── train/ # 训练集图片 │ │ └── val/ # 验证集图片 │ ├── labels/ │ │ ├── train/ # 与图片同名的 .txt 标注文件 │ │ └── val/ │ └── data.yaml # 类别与数据集路径配置 ├── yolov5/ # YOLOv5 官方代码目录 │ ├── train.py # 训练入口 │ ├── detect.py # 推理入口 │ ├── val.py # 验证入口 │ └── models/ ├── runs/ │ └── train/ │ └── smoke_exp/ │ └── weights/ │ ├── best.pt # 验证集上指标最优的权重 │ └── last.pt # 最后一轮训练的权重 └── requirements.txt # Python 依赖清单| 路径 | 作用 | 使用场景 |
|---|---|---|
| dataset/images | 训练和验证图片 | 训练、推理的数据来源 |
| dataset/labels | 同名 txt 标注 | 训练时监督信号,格式见 2.3 |
| dataset/data.yaml | 数据集配置 | 训练、验证都要引用 |
| runs/train/smoke_exp/weights/best.pt | 训练好的模型权重 | 推理、二次开发直接用这个文件 |
| yolov5/detect.py | 官方推理脚本 | 快速验证单张图/视频/摄像头 |
资源里最值钱的两个东西,一是 weights/best.pt,二是 dataset 里那批标注好的图片。前者省掉了几小时的训练时间,后者让你想自己重训时不用从爬图、清洗、标注开始做起。
2.3 数据集细节:YOLO 标注格式与类别配置
烟雾数据集用的是 YOLO 最通用的 txt 标注格式,每一行代表一个目标,五个字段依次是:类别编号、归一化中心点 x、归一化中心点 y、归一化宽度、归一化高度。打开任意一个 labels 文件,长这样:
0 0.462590 0.438077 0.185897 0.129167前面那个 0 表示类别索引,对应 data.yaml 里 names 列表中的第 0 项,也就是 smoke。后面四个数都是 0 到 1 之间的小数,不是像素值。这意味着不管原图是 1920x1080 还是 640x480,标注都不受分辨率影响,训练时模型会统一 letterbox 缩放。理解这一点对排查数据问题很关键——如果你用别的工具标过 VOC 格式的 XML,转成 YOLO 格式时忘记归一化,训练时 loss 会直接起飞。
data.yaml 是整个数据集的配置中心,训练和验证脚本都会先读它:
# dataset/data.yaml path: ../dataset train: images/train val: images/val nc: 1 names: ['smoke']nc: 1表示只有一个类别,names列表的顺序必须和标注文件中的类别编号一一对应。烟这个目标比较特殊,标注时有一个常见原则:宁可框大一圈把周边淡淡的烟羽包进去,也不要只框最浓的核心区,因为模型学到的是「模糊半透明区域 + 周围纹理」的组合特征,框太小会把特征截断。
3. 核心链路拆解:YOLO 标签如何变成画面里的检测框
3.1 数据加载与增强:训练时图片怎么被预处理
训练时,图片并不是直接塞进网络的。YOLOv5 的 dataloader 会先把图片做 letterbox 缩放,保持宽高比不变地补边到 640 或者你指定的尺寸,避免拉伸变形破坏了烟雾的形状特征。送进 GPU 之前还要做归一化,把像素值从 0-255 缩到 0-1,再减去 RGB 均值。这些处理都在数据加载阶段完成,你不需要自己写,但改参数时要明白改的是哪一环。
数据增强对烟雾检测尤其重要。YOLOv5 默认开启了 mosaic 增强,把四张训练图拼成一张,让模型在训练时看到更多不同背景和光照的组合。烟雾经常出现在墙角、窗边、户外天空等复杂背景里,如果不做增强,模型很容易把「白色块」和「烟雾」画等号。我一般还会在超参数里把hsv_h和hsv_s稍微调大,让颜色扰动更明显,逼模型去学纹理而不是只认颜色。
3.2 训练配置:迁移学习与超参数
这份资源重新训练时走的是迁移学习路线:用 COCO 预训练的yolov5s.pt作为起点,只训练最后一层分类头和回归头去适应烟雾数据。数据集不大、类别只有 1 个,从头训练既慢又容易过拟合,迁移学习是最稳的做法。训练命令如下:
cd yolov5 python train.py \ --weights yolov5s.pt \ --data ../dataset/data.yaml \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --name smoke_exp| 参数 | 含义 | 说明 |
|---|---|---|
| --weights | 预训练初始权重 | 换成 best.pt 可在已训好的模型上继续微调 |
| --data | 数据集配置 | 必须是 data.yaml 的路径 |
| --img | 输入分辨率 | 640 是速度与精度的平衡点,追求小目标可以提到 768 或 1280 |
| --batch | 批次大小 | 显存不够就降到 8,但学习率也要跟着降 |
| --epochs | 训练轮数 | 烟雾数据集 100 轮基本收敛,再多容易过拟合 |
| --device | GPU 编号 | 没有 GPU 就写 cpu,但训练会非常慢 |
训练日志里重点盯三个指标:box_loss、obj_loss和cls_loss。对单类检测来说,cls_loss通常很快降下去,真正难降的是box_loss和obj_loss。如果 50 轮之后obj_loss还在高位,先怀疑标注质量,而不是盲目加轮数。
3.3 推理与后处理:置信度过滤、NMS 与输出解析
训练完成后,runs/train/smoke_exp/weights/best.pt就是最终产物。官方 detect.py 的用法很直接,指定权重和图片来源就能跑:
python detect.py \ --weights runs/train/smoke_exp/weights/best.pt \ --source ../dataset/images/val \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt--conf-thres是置信度阈值,低于 0.25 的框会被丢掉,调高减少误检、调低减少漏检;--iou-thres是 NMS 的 IoU 阈值,两个框重叠度超过这个值就合并,调大保留更多重叠框、调小更激进地清除。如果是二次开发,更灵活的方式是直接用 torch.hub 加载权重,在 Python 脚本里拿结果:
import torch # 加载本地训练好的权重,source='local' 表示不联网下载 model = torch.hub.load('./yolov5', 'custom', path='runs/train/smoke_exp/weights/best.pt', source='local') # 运行时阈值 model.conf = 0.25 # 置信度阈值 model.iou = 0.45 # NMS IoU 阈值 model.max_det = 100 # 单张图最多保留的检测框数量 # 推理单张图片 results = model('../dataset/images/val/0001.jpg') results.print() # 打印检测结果 results.show() # 弹出窗口显示画框结果results.pandas().xyxy[0]是后续做告警逻辑最常用的接口,返回一个 DataFrame,每行一个检测框,列包含xmin, ymin, xmax, ymax, confidence, class, name。你可以直接按name过滤出 smoke 类,再取坐标去画框或者算面积。这个数据结构和 OpenCV 的矩形坐标完全兼容,接告警系统时基本不用改格式。
4. 快速跑通:环境配置、单图推理与验证指标解读
4.1 环境准备:Python、PyTorch 与依赖安装
工程依赖写在requirements.txt里,包含 torch、opencv-python、numpy 等基础库。建议用 conda 单独建环境,避免把系统 Python 搞乱。踩坑记录一条:YOLOv5 对 Python 版本有下限要求,3.8 到 3.10 都可以,太老或太新都会在装依赖时报错。
conda create -n smoke python=3.8 -y conda activate smoke cd yolov5 pip install -r requirements.txt装完先确认 PyTorch 能不能用 GPU。CPU 也能跑推理,只是速度差很多;训练的话强烈建议用 GPU。
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"输出里torch.cuda.is_available()为True才说明 CUDA 环境通。这一步不过,后面训练大概率会在第一次 forward 时爆出 device mismatch 或者显存分配错误,到时候再回头查驱动就浪费时间了。
4.2 跑一次推理:图片、视频与摄像头
环境没问题之后,先用验证集图片跑一遍推理,验证权重文件是好的。这一条命令能同时把检测结果图和标注文本输出到runs/detect/下:
python detect.py \ --weights ../runs/train/smoke_exp/weights/best.pt \ --source ../dataset/images/val \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt跑通图片后,换成视频或者摄像头流只需要改--source。视频文件直接写路径,实时流写 RTSP 地址:
python detect.py \ --weights ../runs/train/smoke_exp/weights/best.pt \ --source ../test_video.mp4 \ --view-imgpython detect.py \ --weights ../runs/train/smoke_exp/weights/best.pt \ --source 'rtsp://user:pass@192.168.1.100:554/stream1' \ --view-img注意--view-img会弹窗口逐帧显示,在服务器上跑要加--nosave或者不启用可视化,否则会浪费图形资源。
4.3 验证指标:mAP、Precision、Recall 怎么读
训练结束或者拿到新权重后,用 val.py 在验证集上过一遍,得到模型的真实水平:
python val.py \ --weights ../runs/train/smoke_exp/weights/best.pt \ --data ../dataset/data.yaml \ --task val输出里重点看这几个指标:Precision表示检测出来的框里有多少是真烟雾,越高误检越少;Recall表示真实烟雾里有多少被框出来了,越高漏检越少;mAP@0.5是 IoU 阈值 0.5 下的平均精度,单类项目基本就看这一个数。烟雾检测的现实约束是:宁可 Recall 稍低也要把 Precision 稳住,因为消防告警场景里误报比漏报更伤——连续三天因为水蒸气报警,就没人信这个系统了。
5. 避坑指南:烟雾检测项目最常见的五个坑
5.1 近距离小面积烟雾完全检测不到
现象:画面上明明有一小团烟在冒,模型就是没反应,框都不出。
原因:训练和推理都用了 640 输入分辨率,小目标经过几次下采样后特征图只剩几个像素,卷积核覆盖不到有效信息。标注时把边上的淡烟漏标,也会让模型觉得「小团模糊区域不值得报」。
解决:先把推理分辨率提到 768 或 1280 试一次,框会立刻变多。如果有效,再把这类小目标截图手动补标注,加入训练集重训一轮。训练时开--multi-scale,让模型适应不同尺度的目标。
5.2 白云、雾气、蒸汽被误判成烟雾
现象:晴天白云飘过,画面里刷出一堆置信度 0.6 以上的「烟雾」框。
原因:数据集里基本都是真实的烟,缺少「像烟但不是烟」的负样本,模型没学过白云的纹理差异。加上烟雾数据集拍摄场景单一,模型偷懒学了背景颜色而不是烟的动态特征。
解决:收集一批完全没有烟的监控画面(天空、雾气、蒸汽、灯光光晕都算),放在训练集的背景目录里一起喂进去。推理时把conf从 0.25 提到 0.4,误检会明显减少,代价是小目标漏检增加,需要你权衡。
5.3 训练时 loss 变成 nan 或者剧烈震荡
现象:训练日志里 loss 突然变成 nan,或者前几十轮在 0.05 和 0.8 之间来回蹦。
原因:学习率太大导致梯度爆炸,batch 太小导致梯度估计不稳,也有可能数据集里有损坏的图片或者全黑的无效标注。
解决:先把--lr0从默认的 0.01 降到 0.001 重试;batch 保底开到 16。还不行就遍历一遍数据集,把打不开的图片和不含任何目标的空标注文件清掉。我的习惯是训练前先写几行代码扫一遍图片能否被 OpenCV 正常读取,能省掉大量的玄学调试时间。
5.4 白天效果好、夜间大范围漏检
现象:验证集 mAP 有 0.85,但拿到现场晚上一试,漏检率直接翻倍。
原因:训练集以白天自然光照片为主,夜间监控是红外画面,灰度分布和颜色特征跟白天完全不同,模型没见过这类分布。
解决:补一批夜间、黄昏、背光的烟雾画面重新标注训练。推理侧可以对画面做自适应亮度增强(比如 CLAHE 直方图均衡)再喂给模型。注意别把训练集和推理侧的预处理搞成两套,那会引入新的分布偏移。
5.5 视频推理掉帧严重,CPU 跑不动
现象:跑视频时画面卡成 PPT,GPU 利用率却不到 10%,CPU 风扇狂转。
原因:可能拿了 yolov5x 这种大权重在硬跑,或者没有开启半精度推理。摄像头码流分辨率过高也会让预处理阶段成为瓶颈。
解决:换yolov5s或yolov5m档位的权重,推理代码里加half=True开启 FP16。解码端先用 OpenCV 把帧 resize 到 640 再送进模型,比直接喂 1080p 原图快一倍以上。如果 GPU 都没有,考虑换 TensorRT 或者 ONNX Runtime 做 CPU 加速,硬啃 PyTorch 本身不是好出路。
6. 二次开发:把检测结果接进告警推送系统
6.1 从离线推理改成实时轮询
官方 detect.py 是「读一次、跑一次、保存结果」的一次性流程,真正落地到监控场景需要改成长驻进程。改造的核心是把图片来源从静态文件换成 VideoCapture 视频流,把结果从保存图片换成逐帧处理。实战项目里,我通常会把推理和告警拆成两个循环,推理循环只管出框,告警循环消费检测结果,避免网络请求阻塞画面读取。
6.2 告警去重与冷却时间
直接对每一帧发告警是新手最容易犯的问题——同一团烟会触发几百次请求,把服务打爆。正确做法是加一个冷却时间窗口,比如 30 秒内同一路视频只允许发一次告警。检测到烟雾时同时保存现场截图,方便事后翻查:
import cv2 import time import requests import torch # 加载训练好的权重 model = torch.hub.load('./yolov5', 'custom', path='runs/train/smoke_exp/weights/best.pt', source='local') model.conf = 0.3 # 告警场景稍微收紧阈值 model.iou = 0.5 cap = cv2.VideoCapture('rtsp://user:pass@192.168.1.100:554/stream1') last_alert = 0 ALERT_COOLDOWN = 30 # 冷却时间,单位秒 while True: ret, frame = cap.read() if not ret: time.sleep(0.5) continue results = model(frame) det = results.pandas().xyxy[0] smoke = det[det['name'] == 'smoke'] # 只保留烟雾类别 if len(smoke) > 0 and time.time() - last_alert > ALERT_COOLDOWN: # 触发告警,推送到自己的服务端 requests.post( 'http://127.0.0.1:8000/alert', json={'camera': 'warehouse-1', 'count': len(smoke)} ) # 保存当前帧作为证据图 cv2.imwrite(f'captures/{int(time.time())}.jpg', frame) last_alert = time.time() # 可视化,方便本地调试 annotated = results.render()[0] cv2.imshow('smoke_detection', annotated) if cv2.waitKey(1) & 0xFF == ord('q'): break这个循环里最值得琢磨的是ALERT_COOLDOWN的值。设太短会频繁刷告警,设太长可能错过「烟雾已经消失二次冒烟」的情况。我一般会做成可配置项,而不是写死在代码里,上线后根据现场反馈调节。count字段传的是该帧检测到的烟雾框数量,如果只想关注大面积烟雾,可以过滤掉宽度或高度小于某个像素阈值的框,避免小团水汽触发误报。
接入告警服务只是第一步,更进阶的玩法是把检测结果定时写入时序数据库,统计每天哪个时段烟雾发生最频繁;或者把坐标信息叠加到监控大屏上做可视化。这套权重配合torch.hub的加载方式,二次开发成本非常低——模型推理部分基本不用改,改动都集中在业务侧。自从有一次因为漏掉冷却时间导致告警系统被打爆之后,我每次接新场景都会强制走一遍这个流程:先确认读取视频流没有丢帧,再单独验证告警接口通了,最后才把完整的循环挂到生产环境。从拿到这份权重和数据集到跑通整个告警链路,慢的话一个下午也够了。希望帮到你。
本文还有配套的精品资源,点击获取