☰
YOLOv8火灾检测实战:结构、训练与GUI部署
2026/10/12 18:43:53 网站建设 项目流程

简介:基于YOLOv8的火灾烟雾检测系统完整资源包,面向深度学习开发者、安全监控项目人员以及火灾检测相关专业学生。内含训练好的best.pt和last.pt权重,支持图片、视频和摄像头三种输入方式的实时检测,配套PyQt5编写的图形界面,可一键导出检测结果到指定目录,方便快速搭建演示或实际部署。整个压缩包共33个文件,大小16.18MB,组成包括两个pt权重文件、两个Python推理脚本、yaml环境配置、训练曲线与混淆矩阵等可视化图表、结果统计csv以及readme说明文档。权重与代码结构清晰,适合作为YOLOv8火灾检测项目的基线参考。目前已有523人学习。借助这份资源能省去数据标注与训练调参时间,直接运行图形界面体验完整检测流程,也能基于现有代码进行二次开发,是入门烟雾火灾检测和YOLOv8实践的实用工具。

1. 火灾检测不是换个数据集那么简单:YOLOv8 的边界要先想清楚

把 YOLOv8 用在火灾检测上,常见做法是下载一套带权重的工程源码,然后跑一下图片、视频、摄像头三种输入,总觉得「能出框」就完事了。但实际交付过的人都知道,火灾检测真正麻烦的是两类问题:一是火焰边缘在热浪扰动下形态不稳定,标注本身就有噪声;二是烟雾是半透明目标,置信度天然偏低,直接套用 COCO 预训练权重的默认阈值,很容易出现漏报或者框在乱跳。这篇内容就围绕一套可落地的 YOLOv8 火灾检测系统来展开:训练好的权重要怎么组织、推理代码怎么写才能同时吃图片/视频/摄像头、GUI 界面怎么在不卡界面的前提下显示检测结果,以及结果导出时要注意的坐标和编码问题。适合要亲手交付这套系统的人,也适合刚入行、想照着一个完整结构做目标检测项目的开发者。

2. YOLOv8 模型结构拆解:C2F、Anchor-Free 检测头和火灾小目标的关系

2.1 C2F 模块如何改善火灾目标的梯度流

YOLOv8 的 backbone 里,C2F 是替换 YOLOv5 C3 的核心模块。它的结构是:输入先过一个 1x1 卷积将通道数降下来,然后分成两条路径,一条直接向后传播,另一条依次经过多个 Bottleneck,关键区别在于每个 Bottleneck 的输出都会保留下来,最后在通道维度上拼接,再通过 1x1 卷积恢复输出通道。相比 C3 只保留整个模块最后一个输出,C2F 相当于把分支里每一层学到的特征都拿了出来,梯度回传时可以选择更短的路径绕过中间层。

这个设计对火灾检测的意义在于:烟雾区域本身纹理弱、对比度低,经过多层卷积后小目标的响应容易被背景淹没。C2F 更丰富的梯度路径能让网络在前几层就保留住烟雾边缘的低阶特征,避免信息在深层传播里被稀释。在 ultralytics 的实现里,C2F 的实例化参数包括n(Bottleneck 数量)和hidden_ratio(隐藏层通道比例),默认配置下 YOLOv8s 的 C2F 使用 3 个 Bottleneck,这个数量在精度和速度之间已经比较平衡,不需要为火灾场景特意加深。

2.2 SPPF 与 PAN-FPN:多尺度特征怎么传给检测头

YOLOv8 在 backbone 末端接了 SPPF 模块,把同一份特征图分别做 1/5/9/13 像素的池化,再拼接起来。这个设计的价值在于,固定监控视野里火灾目标的尺寸跨度非常大:远处刚冒出的烟可能只有十几个像素,近处的大火可以占满整帧画面。SPPF 用不同池化核把不同感受野的信息拼在一起,让后面检测头能同时注意到这两类极端尺寸。

之后是 PAN-FPN 路径聚合网络,它除了自顶向下的特征传递,还加了一条自底向上的路径,把浅层的位置信息和高层的语义信息做双向融合。针对烟雾这类边缘模糊的目标,位置信息尤其重要,因为烟雾框的定位误差主要来自边界不确定性,而不是语义判断错误。这也是为什么 yaml 结构里 YOLOv8 沿用 PAN-FPN 而不是简单 FPN 的原因。

2.3 Anchor-Free 解耦头:为什么它对火灾更友好

YOLOv8 的检测头有两个特点:一是分类和回归分支解耦,各自用独立的卷积;二是回归分支使用 Anchor-Free 方式,直接预测每个位置到目标框四条边的距离,配合 DFL 损失把距离分布建模成概率分布再取期望。

对火灾检测来说,最大的好处是不再依赖聚类生成的 anchor 表。火焰和烟雾的形状极不规则,传统 anchor 机制如果长宽比设置不合理,训练时正样本数量会很不均匀,导致召回率偏低。Anchor-Free 让模型在每个特征图位置上自适应学习边框,对长宽比奇怪的火灾目标容忍度更高。DFL 的作用也值得一提:火焰边界在热浪中不稳定,标注时即使是同一个框,不同标注员画出的边缘差异也很大,DFL 通过分布建模弱化了对单一边框数值的强依赖,训练时不会因为少数标注偏差产生过大梯度。

2.4 模型尺寸怎么选:参数量、帧率与部署目标

火灾检测场景的算力差异很大,从 Jetson Orin Nano 到服务器 GPU 都有。下表是几个常见型号的参考对比,帧率为 640 输入在单卡环境下的大致表现,实际数值会受 batch、线程数和 TensorRT 影响。如果需要精确实测,建议用系统自带的yolo benchmark model=weights/best.pt命令在目标设备上跑一遍。

模型参数量级640输入参考帧率适用场景
YOLOv8n约 3.2M150~180 FPSRK3588 等边缘盒子
YOLOv8s约 11.2M80~110 FPSGTX 1660 Ti / Orin Nano
YOLOv8m约 25.9M50~60 FPS单路服务器 GPU
YOLOv8l约 43.7M30~35 FPS多路视频流分析
YOLOv8x约 68.2M20~25 FPS追求最高精度

火灾检测属于单类或双类目标检测任务,类别数少,YOLOv8s 在精度和速度上通常已经够用。如果摄像头数量多且没有 GPU,考虑 YOLOv8n 并配合后续蒸馏;如果画面里小目标烟雾占比高,可以直接上 m 或 l 级模型,靠输入尺寸 768 或 1024 提升小目标召回。

3. 环境搭建与推理代码:从权重文件到图片、视频、摄像头三种输入

3.1 用 conda 装出干净的 ultralytics 推理环境

权重文件一般放在weights/目录下,和推理代码、GUI 代码分离,这样换模型时不需要改业务代码。环境搭建推荐用 conda 隔离,避免把系统 Python 搞乱:

conda create -n fire python=3.10 -y conda activate fire pip install ultralytics opencv-python pandas

ultralytics 包会自动安装 torch 依赖,但默认装的是 CPU 版本。如果本机有 NVIDIA GPU,需要把 torch 换成 CUDA 版,以 CUDA 12.1 为例:

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121

安装完成后用python -c "import torch;print(torch.cuda.is_available())"验证,输出True才说明 GPU 可用。这里有个经常踩的坑:训练机器的 CUDA 版本和推理机器的 CUDA 版本不一致时,模型在加载时有概率直接报 CUDA error,所以交付前最好在目标机器上做一次权重加载测试,不要假设本地跑通就一定能部署。

提示:OpenCV 在无显示器 Linux 服务器上调用cv2.imshow会报错,需要屏蔽 GUI 显示或用 Qt 窗口替代,下文 GUI 部分会专门说明。

3.2 三合一推理脚本:一个入口处理图片、视频和摄像头

推理代码的核心是拿到输入来源后,判断类型再走对应分支。摄像头传0(或其他设备编号),图片传路径,视频传视频文件路径。下面是一个可以直接用的三合一函数:

import cv2 from pathlib import Path from ultralytics import YOLO def run_inference(source, weights="weights/best.pt", conf=0.25, imgsz=640): model = YOLO(weights) # 摄像头:source 是设备编号,如 0 if isinstance(source, int) or str(source).isdigit(): cap = cv2.VideoCapture(int(source)) while True: ret, frame = cap.read() if not ret: break results = model.predict(source=frame, conf=conf, imgsz=imgsz, verbose=False) annotated = results[0].plot() cv2.imshow("fire-detect", annotated) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows() return # 图片 if isinstance(source, (str, Path)) and Path(source).suffix.lower() in [".jpg", ".jpeg", ".png", ".bmp"]: results = model.predict(source=source, conf=conf, imgsz=imgsz, verbose=False) annotated = results[0].plot() cv2.imwrite("output_annotated.jpg", annotated) print(f"检测到 {len(results[0].boxes)} 个目标") return # 视频文件 cap = cv2.VideoCapture(str(source)) fps = int(cap.get(cv2.CAP_PROP_FPS)) writer = None while True: ret, frame = cap.read() if not ret: break results = model.predict(source=frame, conf=conf, imgsz=imgsz, verbose=False) annotated = results[0].plot() if writer is None: h, w = annotated.shape[:2] writer = cv2.VideoWriter("output_video.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h)) writer.write(annotated) cap.release() writer.release()

代码里的几个关键参数按场景调:conf=0.25是置信度阈值,低于这个值的框会被滤掉,监控场景想要减少漏报适量调低到 0.2,但随后误报会增加;imgsz=640是输入分辨率,检测小目标烟雾时可以提高到 768,显存占用同时上涨;verbose=False关闭每帧日志输出,否则视频推理时终端会被刷屏。results[0].plot()返回的是画好框的 BGR 图像,直接用 OpenCV 显示或写盘即可。

这里有个容易忽略的问题:model.predict每次调用都会做预处理和后处理,视频循环中如果verbose=True,CPU 占用会比推理本身还高。另外,不要在循环里重复调用YOLO(weights),模型加载一次就够了,放在循环外。

3.3 验证权重是否健康:先看这三项再交付

拿到一套「训练好的权重」,先不要急着接 GUI,按下面三步验证:

第一,跑yolo val model=weights/best.pt data=fire.yaml查看 mAP50 和混淆矩阵。如果 jar 包自带数据集,这步能直接得到精度指标;没有数据集时至少用三张没参与训练的图片做人工 sanity check,分别挑白天大火、夜间火光、远距离小烟雾三种典型场景。

第二,检查类别 ID 映射。火灾检测一般有两种:单类fire或双类fire, smoke。权重文件里的names是训练时固化下来的,推理端必须和它一致,否则会出现「烟雾框但打印成火焰」的低级错误。在代码里可以通过model.names打印确认:

model = YOLO("weights/best.pt") print(model.names) # 例如 {0: 'fire', 1: 'smoke'}

第三,验证推理设备的显存占用。best.pt 在 CPU 上跑一张 640 图片可能需要 200ms 以上,如果交付给了只有 CPU 的用户,可能需要换成 ONNX 格式并启用half=True。

4. 用自己的火灾数据微调模型:数据集组织、训练参数与损失曲线判读

4.1 数据集怎么组织:目录、标注格式和常见标注错误

如果系统里不包含训练脚本,通常的做法是从公开火灾数据集起步,再用自己监控场景的截图做增量微调。公开数据集一般已经转成 YOLO txt 格式,但类别命名不统一,拿到手第一件事是统一类别。建议只保留fire和smoke两个类别,最多加一个background或直接忽略,类别越多误报率越高。

目录结构按 YOLO 标准组织:

datasets/fire/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── fire.yaml

fire.yaml内容如下:

train: ./datasets/fire/images/train val: ./datasets/fire/images/val nc: 2 names: ["fire", "smoke"]

标注时最容易犯的错误是「框大不框准」。火焰的边缘在热浪里不断抖动,标注时看到红色就尽量框住整个火苗,但火苗核心区才是有判别力的区域,框得太大反而让模型学到背景。烟雾同理,标注时把半透明区域完整框进去,不要只框浓烟部分。另一个常见问题是数据增强没有针对性:火灾训练时fliplr通常可以开,因为水平翻转不改变火的语义,但hsv_h色相扰动建议保持默认 0.015,不要大幅度调整,否则会出现绿色火焰这种不真实训练样本。

4.2 训练命令与超参数含义:从 yolov8s.pt 起步

从官方预训练权重yolov8s.pt上继续训练,比从零训练收敛快很多。训练命令如下:

yolo train model=yolov8s.pt \ data=datasets/fire/fire.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ patience=20

关键参数的推荐值与原因:

参数推荐值说明
modelyolov8s.pt火灾检测类目少,s 级性价比最高;边缘设备用 n
epochs100~150火灾数据集一般几千张,100 轮足够收敛,配 patience 早停
imgsz640~768小目标烟点占比高时调大,显存不够则优先降 batch
batch16~32与显存挂钩,默认自适应;OOM 时减半
optimizerauto 或 AdamWauto 会自动选 SGD,小数据集上 AdamW 收敛更稳
patience20验证损失连续 20 轮不降则停止,防止过拟合浪费时间

训练过程中 ultralytics 默认开启 Mosaic、MixUp、HSV 扰动等数据增强。有一个细节:Mosaic 增强会在最后 10 个 epoch 自动关闭,这是官方设计,目的是让网络在最后阶段适应真实分布,不用手动干预。

训练完成后,runs/detect/目录下会有best.pt和last.pt两个权重,best.pt是验证集上表现最好的,交付时用这个;last.pt是最后一个 epoch 的,调试时看用。

4.3 怎么读 results.png:损失曲线和混淆矩阵的判读方法

训练结束后打开runs/detect/exp/下的results.png,里面有三组关键曲线:train/box_loss、train/cls_loss、train/dfl_loss以及对应的验证集曲线。判断标准很简单:训练损失持续下降,验证损失不再下降甚至回升,就是过拟合信号,直接拿patience停掉即可,不用硬跑满 epoch 数。

更值得看的是confusion_matrix.png。火灾检测的常见问题是smoke类别大量被分到背景,这时回到数据集检查标注:半透明烟雾区域如果漏标,模型会学到「边缘模糊的目标=背景」。反过来,如果fire和smoke互相混淆,说明两类特征太接近,常见解决方法是合并成单类fire,只在后处理逻辑里区分火焰和烟雾。损失函数方面,YOLOv8 默认 CIoU 配合 DFL 已经够用,部分项目尝试把回归损失改成 SIoU 确实在火灾数据上有一点提升,但收益和调参成本不成正比,建议先把数据做干净再考虑。

5. GUI 界面、推理线程与导出细节:把检测能力交给非技术用户

5.1 用 PySide6 搭 GUI,推理必须放 QThread

GUI 界面用 PySide6 实现时,最容易犯的错是把model.predict直接放在按钮回调里。图片推理还能忍,视频和摄像头实时流会让界面彻底卡死。正确做法是检测逻辑放QThread,主线程只负责显示结果。

from PySide6.QtCore import QThread, Signal import cv2 from ultralytics import YOLO class DetectWorker(QThread): frame_ready = Signal(object) def __init__(self, source, weights, conf=0.25): super().__init__() self.source = source self.model = YOLO(weights) self.conf = conf self.running = True def run(self): cap = cv2.VideoCapture(self.source) while self.running: ret, frame = cap.read() if not ret: break results = self.model.predict(frame, conf=self.conf, verbose=False) self.frame_ready.emit(results[0].plot()) cap.release()

主线程里连接frame_ready信号,把图像转成QImage后setPixmap更新界面。视频流场景中,摄像头读取和模型推理在同一个线程,帧率受模型速度限制,但界面不会卡顿。如果视频源是本地文件,建议在 Worker 里用cv2.VideoWriter边读边写,避免内存堆积。

5.2 导出功能:标注图、CSV 坐标、视频三种格式一次做全

结果导出要覆盖使用者的实际需求:可视化检查用标注图,后续数据分析用 CSV 坐标,存档用视频。CSV 导出代码:

import pandas as pd def export_csv(results, output_path): rows = [] for box in results[0].boxes: x1, y1, x2, y2 = box.xyxy[0].tolist() rows.append([ box.cls.item(), box.conf.item(), round(x1), round(y1), round(x2), round(y2) ]) df = pd.DataFrame(rows, columns=["class_id", "conf", "x1", "y1", "x2", "y2"]) df.to_csv(output_path, index=False, encoding="utf-8-sig")

这里用utf-8-sig编码,避免 Windows 下 Excel 打开 CSV 中文乱码。输出文件名统一用时间戳,比如fire_20250610_143022.csv,方便大量告警图归档。

5.3 三个具体技巧:帧率、重复识别和阈值策略

实时检测的三个调优点。第一,摄像头缓冲会导致画面延迟随手势系统越来越大,cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)可以强制读取最新帧,同时把imgsz从 640 降到 480,视觉损失不大但帧率可能提升 20%。第二,运动的物体经过摄像头只识别一次这个问题,本质是置信度抖动导致反复报警,简单的做法是连续三帧都在同一位置检测到目标且置信度上升超过 0.1 才触发告警,不需要引入跟踪器。第三,烟雾和火焰不要共用同一个conf,烟雾是半透明目标,置信度天然低,单独给smoke用 0.2、fire用 0.3,能同时兼顾两类目标。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询