基于YOLOv8的烟雾检测实战:从数据构建到部署优化

发布时间:2026/9/8 14:34:32
基于YOLOv8的烟雾检测实战:从数据构建到部署优化 简介面向计算机视觉开发者的YOLOv8烟雾检测完整工程包包含已训练好的smoke权重模型、4500余张标注图像及配套XML/TXT标签文件类别统一为smoke。代码基于PyTorch实现并附带Qt可视化界面便于本地加载模型进行图片、视频或实时摄像头烟雾识别适用于化工园区、森林防火等场景的预警系统搭建与算法验证。资源包共2000个文件包含1283个txt标注文件、510个md说明文档、134个Python脚本、57个yaml配置以及少量C、Shell、HTML等辅助文件压缩包大小约241.58MB。目录结构清晰从模型推理、训练配置到界面交互均有覆盖可帮助使用者快速完成环境复现。目前已有333人学习下载适合具备一定YOLOv8基础的开发者直接迁移使用或作为烟雾检测数据扩充与算法对比的参考基准。1. 项目概述与方案选型做烟雾检测这件事说难不难说简单真不简单。早些年大家普遍用的是传统图像处理的路子比如帧差法、背景建模、颜色阈值分割对静态场景、固定摄像头还凑合一旦遇到室外光照变化、雾气干扰、远距离小目标烟雾效果就崩得一塌糊涂。深度学习普及之后检测精度上来了但实时性又成了老大难——毕竟火灾报警这种事晚一秒钟都可能造成完全不同的结果。我这次的项目选型是 YOLOv8 自建烟雾数据集 训练好的 smoke 模型整套流程走下来从数据准备、模型训练到实际部署都有比较完整的实践经验。选 YOLOv8 而不是更早的 YOLOv5 或 YOLOv7核心原因有三个第一YOLOv8 的 C2f 结构在保持轻量化的同时提升了梯度流动效率在烟雾这种边缘模糊、形态不规则的检测目标上特征提取能力比 C2f 的前身 C3 模块更强第二YOLOv8 内置了 Anchor-Free 检测头省去了锚框聚类和调参的麻烦对新手极其友好第三Ultralytics 官方把训练、验证、导出、部署的链路都封装好了一条命令能跑通全流程工程效率高不少。这个项目适合谁参考如果你是做毕业设计、实验室项目或者工业消防预警系统的前期验证这套方案能帮你少走很多弯路。烟雾检测和普通物体检测有一个本质区别烟雾没有固定的形状、颜色和纹理它是一团不断扩散的气体颗粒集合体。这意味着你不能像检测行人、车辆那样依赖强结构特征必须充分挖掘模型的上下文语义能力和多尺度特征融合能力。这篇文章我把自己踩过的坑、调过的参数、验证过的思路全部摊开来讲希望能给你的项目提供实打实的参考。2. 烟雾数据集构建与预处理2.1 数据来源与清洗策略训练烟雾检测模型数据是地基。很多人上来就去网上扒公开数据集比如某开源平台上的火灾烟雾数据集下载下来直接用结果训练出来的模型在测试集上效果尚可一上真实场景就疯狂误报。问题出在哪绝大多数公开烟雾数据集的场景单一背景基本是天空、森林或者室内走廊而且烟雾浓度、光照条件、摄像头角度高度同质化。模型学到的是“这个背景下的半透明区域是烟雾”而不是“烟雾本身是什么”。我的做法是混合多源数据除了公开数据集之外自己爬取并标注了一批不同场景的烟雾图片包括城市街道、工厂车间、室外停车场、夜间灯光下的烟雾等。清洗时遵循几条硬性标准分辨率低于 640×640 的模糊小图直接丢弃烟雾占画面比例过小的图片保留但单独标注为小目标样本有明显水印、严重压缩伪影的图片剔除。最终数据集规模控制在 8000 张左右其中公共数据 5000 张自采标注 3000 张。这个量级对于单类别检测来说完全够用再往上堆数据边际收益就非常有限了。2.2 标注细节与格式转换标注工具我用的是 LabelImg虽然界面老旧但胜在稳定支持 YOLO 格式直接导出。这里有一个关键细节烟雾的标注框该怎么打烟雾是扩散的边界是渐变的不像人、车那样有清晰轮廓。我实验下来标注框应该紧贴烟雾的“核心浓密区”不要把淡淡的薄雾也框进去。原因很简单——薄雾边缘的像素值变化极小模型在这个区域学不到判别性特征反而会把背景特征当作烟雾特征学进去导致误检率飙升。标注完成之后需要把 VOC 格式转成 YOLO 格式。YOLO 格式的标注信息是归一化后的中心点坐标和宽高分别存放在同名 .txt 文件中。转换脚本不复杂核心代码大致如下import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_file, class_names, output_dir): tree ET.parse(xml_file) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): class_name obj.find(name).text if class_name not in class_names: continue class_id class_names.index(class_name) xmin int(obj.find(bndbox/xmin).text) ymin int(obj.find(bndbox/ymin).text) xmax int(obj.find(bndbox/xmax).text) ymax int(obj.find(bndbox/ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(os.path.join(output_dir, os.path.splitext(os.path.basename(xml_file))[0] .txt), w) as f: f.write(\n.join(lines))数据集划分上我采用 8:1:1 的比例训练集 6400 张验证集 800 张测试集 800 张。划分时要注意同一个场景拍摄的连续帧必须放到同一个集合里否则会出现数据泄露验证集指标虚高。3. YOLOv8 训练细节与参数调优3.1 环境配置与硬件门槛先解决一个大家问得最多的问题训练 YOLOv8 到底需不需要 GPU答案是分情况。如果你的数据集只有几百张图片模型选 YOLOv8s那用纯 CPU 训练也能出结果只是慢得让人怀疑人生。但如果像我这样 6000 多张训练图CPU 训练一个 epoch 可能要二十多分钟跑 300 个 epoch 就是一百多个小时完全不现实。我实际训练用的显卡是 GTX 1660Ti6GB 显存这张卡属于入门级但跑 YOLOv8s 完全没问题批次大小设 16图像分辨率 640×640显存占用约 4.2GB。如果你也想用 1660Ti 或者其他 6GB 显存的卡训练建议把 batch size 从默认的 16 降到 8同时开启梯度累积效果等同但显存压力小很多。环境配置按照 Ultralytics 官方文档一步步装即可Python 3.9 PyTorch 2.1 CUDA 11.8 是我实测最稳的组合。3.2 训练超参设置与损失曲线分析训练命令我用的是yolo train datasmoke.yaml modelyolov8s.pt epochs300 imgsz640 batch16 patience20这里重点说一下几个参数的选择逻辑。patience20是早停机制表示连续 20 个 epoch 验证集指标没有提升就自动终止训练能有效防止过拟合同时节省时间。imgsz640是输入分辨率如果你检测的是远距离小面积烟雾建议尝试 768 甚至 896小目标的特征在更大分辨率下更容易被模型捕捉但训练速度和推理速度都会相应下降需要根据实际场景权衡。训练过程中要养成看损失曲线的习惯。YOLOv8 的训练日志里有 box_loss、cls_loss、dfl_loss 三类损失。烟雾检测场景下重点观察 box_loss 和 cls_loss。正常情况下box_loss 应该在训练初期快速下降然后趋于平缓。如果 box_loss 出现剧烈震荡大概率是学习率过大或者数据集里有标注错误如果 cls_loss 降不下去说明模型对“什么是烟雾”和“什么不是烟雾”的区分度不够需要补充更多难负样本。画损失曲线的方式有很多最简单的是直接读取训练保存的 results.csv 文件用 pandas 和 matplotlib 画图。这段代码我每次训练完都会跑一遍方便复盘import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.plot(df[box_loss], labelbox_loss) plt.plot(df[val_box_loss], labelval_box_loss) plt.legend() plt.subplot(1, 2, 2) plt.plot(df[cls_loss], labelcls_loss) plt.plot(df[val_cls_loss], labelval_cls_loss) plt.legend() plt.savefig(loss_curve.png)3.3 训练结果与模型评估我的 smoke 模型最终训练了约 260 个 epoch触发了早停best.pt 在验证集上的 mAP50 达到 0.942mAP50-95 为 0.718推理速度在 1660Ti 上单张图片约 12ms640×640 输入。这个精度对于烟雾检测来说已经属于优秀水平毕竟烟雾目标的边缘不确定性极大能到 0.94 的 mAP50 说明模型确实学到了烟雾的本质特征。训练完成后会得到两个模型文件best.pt 和 last.pt。best.pt 是验证集上表现最好的权重last.pt 是最后一个 epoch 的权重。实际部署一定用 best.pt这个不需要犹豫。4. 模型部署与推理优化4.1 本地摄像头实时推理训练好的 smoke.pt 模型可以直接用于实时检测Ultralytics 封装得很友好加载模型跑摄像头视频流只需几行代码from ultralytics import YOLO model YOLO(best.pt) results model.predict(source0, showTrue, conf0.35, imgsz640, line_width2)这段代码里source0表示调用第一个摄像头如果你用的是 RTSP 网络摄像头把source改成rtsp://用户名:密码IP:端口/stream1即可。conf0.35是置信度阈值烟雾检测建议设置在 0.3~0.4 之间。太低容易误报太高漏报严重。烟雾本身是半透明的置信度天然不如“人”“车”这类实体目标高我用 0.35 这个阈值在多个测试视频上表现最均衡。4.2 模型导出与边缘设备部署如果你的项目最终要部署到 RK3588、Jetson Orin Nano 这类边缘设备上需要把 PyTorch 模型转换成 ONNX 或 TensorRT 格式核心命令是yolo export modelbest.pt formatonnx imgsz640 opset12导出成 ONNX 之后在 RK3588 上可以利用 RKNN-Toolkit2 工具链转成 RKNN 格式在 Jetson 设备上则用 TensorRT 的 trtexec 工具转成 engine 文件。这里有一个重要提醒ONNX 导出时不要勾选 dynamic动态输入尺寸边缘设备的推理引擎对动态 Shape 支持较差固定 640×640 的输入尺寸最稳妥。部署方面还有一个容易被忽略的性能瓶颈——视频解码。很多人在推理优化上花了大功夫结果瓶颈在 OpenCV 的 VideoCapture 解码速度上。处理 RTSP 流时建议用 FFmpeg 硬解码或者英伟达的 PyNvVideoCodec 库别让解码拖了推理的后腿。我实测 1080p 30fps 的 RTSP 流用软件解码要占用约 15% 的 CPU换成硬解码后几乎可以忽略不计这 15% 的 CPU 资源留给预处理和后处理整个 pipeline 的延迟能降低不少。5. 常见问题与排查技巧5.1 漏检与误报的平衡烟雾检测项目做多了你会发现最棘手的问题不是模型精度不够而是漏检和误报之间的平衡。传统机器学习时代大家喜欢用一个固定的阈值一刀切但深度时代完全可以把“疑似烟雾”和“确定烟雾”区分出来分别设置不同的报警策略。我实际项目中采用了二级确认机制第一级用低阈值conf0.25做初步筛选第二级用时序滤波对连续 5 帧中至少 3 帧检测到烟雾的区域才触发报警。这个机制大幅降低了误报率而且只增加了极少的计算开销。如果你的场景里存在蒸汽、灰尘、雾气等容易混淆的目标建议在训练数据里刻意加入这些干扰物作为负样本。负样本不需要标注放在训练集目录下但没有对应的 txt 文件即可。YOLO 训练时会自动将它们视为背景这样模型被迫学习区分“真正的烟雾”和“看上去像烟雾的东西”。5.2 单目标重复识别问题处理热搜词里有一个很典型的问题运动物体经过摄像头被识别了多次。这其实是目标跟踪中的 ID Switch 问题如果只做单帧检测不做跟踪每一帧都会独立输出检测结果物体在画面中停留多久就会被识别多少次。解决方案是接入 ByteTrack 或 BoT-SORT 这类轻量级跟踪器给每个目标分配唯一 ID同一目标只触发一次识别事件。Ultralytics 已内置跟踪功能只需把model.predict改成model.track并加上persistTrue参数from ultralytics import YOLO model YOLO(best.pt) results model.track(source0, persistTrue, trackerbotsort.yaml)5.3 数据增强的坑与经验数据增强是提升模型泛化能力的利器但用在烟雾检测上要格外谨慎。YOLOv8 默认开启马赛克增强它把四张图拼接成一张。对于烟雾检测马赛克增强有一个副作用拼接边界处可能会出现两个不同场景的半透明烟雾叠在一起模型容易学到错误的上下文特征。我的做法是训练前期前 50 个 epoch保持默认增强训练后期手动关闭马赛克增强让模型在正常分布上精调。具体做法是在训练命令中加mosaic0.5参数调低马赛克的启用概率或者干脆到训练后期把增强强度降下来。颜色抖动增强同样需要克制。烟雾检测严重依赖颜色和亮度信息如果把色相饱和度随机扰动拉得太高模型会把某些极端颜色下的背景误判为烟雾。我建议 HSV 扰动参数设置为hsv_h0.01, hsv_s0.3, hsv_v0.2比默认值更低实测对复杂光照场景的稳定性有明显改善。5.4 显存不足与训练崩溃最后说一个很多新手会遇到的问题训练到一半显存爆炸进程被杀。除了降低 batch size 之外还有一个屡试不爽的技巧——开启梯度累积。假设 batch size 设为 8 会显存溢出可以设置 batch size 为 4同时配合accumulate2等效于用 4×28 的批次更新梯度模型收敛效果几乎不受影响。另外把训练参数中的workers调高比如 8能让数据加载速度追上 GPU 的计算速度避免 GPU 空转。我在这个项目里最大的体会有两点一是数据集的质量上限决定了模型精度的上限与其花大量时间研究网络结构改进不如先把数据整理干净二是烟雾检测没有一劳永逸的模型换个场景、换个摄像头角度模型的精度可能就大打折扣需要针对实际场景做持续的数据补充和模型迭代。我在部署到具体项目时一般会预留一个“现场数据回流”的机制把漏检和误检的图片定期收集起来重新微调模型跑一次全流程的增量训练。这个习惯帮我解决了不少实际问题也推荐你在做类似项目时尝试一下。本文还有配套的精品资源点击获取