简介:本资源为基于YOLOv9的火焰识别检测系统完整项目包,面向计算机、人工智能、自动化等专业在校学生、教师及企业开发人员,可用于毕业设计、课程实践或目标检测入门进阶。包内共192个文件,以83个Python源码、30个YAML配置、33张JPG与9张PNG图像、3个PT权重文件为主,另含训练日志、评估指标曲线与数据集说明,压缩包约62.53MB。项目提供训练好的模型与详细运行教程,覆盖环境配置、数据集准备、参数修改、模型训练与推理测试全流程,读者可据此复现火焰识别效果,并参考评估曲线判断模型性能。已有324人学习,适合希望快速上手YOLOv9目标检测、掌握从数据到部署完整链路的开发者。
1. 火焰识别为什么值得用 YOLOv9 重做一遍
厂区监控里最先报警的往往不是温度传感器,而是摄像头画面里那一小簇橘红色像素。等烟感探头触发时,火势通常已经过了最佳处置窗口。这就是火焰识别检测系统存在的意义:在可见光或红外画面里,用目标检测模型把火焰从背景中框出来,抢在传感器之前给出预警。而 YOLOv9 之所以被拿来重做这件事,核心在于它的可编程梯度信息机制缓解了深层网络的信息丢失问题,小目标火焰的召回率比前几代有明显提升。这套方案适合两类人:一是手里已有监控视频流、想快速搭一套火焰检测原型的安防从业者;二是正在做课程设计或技术验证、需要一份能跑通的 Python 源码加训练好模型加评估曲线的开发者。标题里提到的源码、运行教程、预训练模型和评估指标曲线,本质上是一套完整的落地包,接下来我会按「先跑通再调优」的顺序把它拆开讲清楚。
2. 把环境跑通:从零到第一帧火焰框
2.1 依赖选型与版本对齐
火焰检测这套东西,环境装不对,后面全是玄学。YOLOv9 的官方实现依赖 PyTorch,而 PyTorch 又和 CUDA 驱动版本强绑定。我一般建议先用nvidia-smi看驱动支持的 CUDA 上限,再去 PyTorch 官网找对应的安装命令,不要直接pip install torch了事。常见做法是建一个独立虚拟环境,把 Python 锁在 3.9 或 3.10,这两个版本和大多数检测库的兼容性最稳。
# 创建虚拟环境,Python 版本建议 3.9/3.10 conda create -n fire_yolov9 python=3.10 -y conda activate fire_yolov9 # 先装 PyTorch,CUDA 版本按 nvidia-smi 结果选,这里以 cu118 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 再装检测常用依赖 pip install opencv-python numpy pyyaml tqdm matplotlib pandas seaborn这段命令的逻辑是:先隔离环境避免污染全局包,再装 PyTorch 这个最大的依赖,最后补上推理和评估要用的库。参数上唯一需要你改的是cu118,如果你的驱动只支持到 CUDA 11.6,就换成cu116;如果是纯 CPU 推理,把--index-url那行去掉,直接pip install torch torchvision即可,但训练会慢到让你怀疑人生。
2.2 目录结构与权重放置
拿到源码包后,别急着运行。先看清楚目录约定,YOLOv9 类项目通常长这样:data/放数据集配置,models/放网络结构定义,weights/放训练好的.pt文件,runs/是训练和推理的输出目录。预训练模型一般是一个几十到上百 MB 的.pt文件,把它放进weights/下,命名保持和配置文件里引用的一致。
# check_env.py 快速自检脚本 import torch import cv2 import os print("PyTorch:", torch.__version__) print("CUDA available:", torch.cuda.is_available()) print("OpenCV:", cv2.__version__) # 检查权重文件是否存在 weight_path = "weights/fire_best.pt" if os.path.exists(weight_path): size_mb = os.path.getsize(weight_path) / 1024 / 1024 print(f"权重文件存在,大小 {size_mb:.1f} MB") else: print("权重文件缺失,请确认路径")这个自检脚本解决的是「我到底装没装对」的问题。torch.cuda.is_available()返回 False 就说明 CUDA 没配好,后面训练会退化成 CPU 模式。权重文件大小也要看一眼,如果只有几 KB,多半是下载中断的残包,这种坑我踩过不止一次。
2.3 用预训练模型跑通单张图片推理
环境确认无误后,先用一张火焰图片验证整条链路。推理脚本的核心是加载模型、读图、前处理、前向传播、后处理画框。
# infer_image.py 单图推理 import cv2 import torch from models.experimental import attempt_load from utils.general import non_max_suppression, scale_boxes from utils.augmentations import letterbox # 加载模型 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = attempt_load("weights/fire_best.pt", device=device) model.eval() # 读图并做 letterbox 缩放,保持长宽比 img0 = cv2.imread("test_fire.jpg") img = letterbox(img0, new_shape=640, auto=True)[0] img = img[:, :, ::-1].transpose(2, 0, 1) # BGR 转 RGB,HWC 转 CHW img = torch.from_numpy(img).float().to(device) / 255.0 img = img.unsqueeze(0) # 推理 with torch.no_grad(): pred = model(img)[0] pred = non_max_suppression(pred, conf_thres=0.25, iou_thres=0.45) # 画框 for det in pred: if det is not None and len(det): det[:, :4] = scale_boxes(img.shape[2:], det[:, :4], img0.shape).round() for *xyxy, conf, cls in det: x1, y1, x2, y2 = map(int, xyxy) cv2.rectangle(img0, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img0, f"fire {conf:.2f}", (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite("result.jpg", img0) print("推理完成,结果已保存")逻辑说明:letterbox是 YOLO 系列的标准前处理,把图片缩放到 640 并填充灰边,保证不拉伸变形。non_max_suppression做非极大值抑制,conf_thres=0.25是置信度阈值,iou_thres=0.45是重叠框合并阈值。这两个参数是火焰检测里最需要调的,后面会专门讲。scale_boxes把检测框坐标映射回原图尺寸,否则你画出来的框位置全是错的。
3. 训练自己的火焰数据:标注、配置与启动
3.1 火焰数据集标注的四个要点
预训练模型能识别通用火焰,但你的场景如果是特定工业环境,比如高炉口、焊接火花、红色警示灯,误报率会很高。这时候必须用自己的数据微调。标注用 LabelImg 或 CVAT 都行,输出 YOLO 格式的.txt,每行是类别 x_center y_center width height,全部归一化到 0 到 1。
标注火焰有四个要点:第一,火焰边缘模糊,框要稍微放宽一点,把火苗外焰也包进去;第二,反光、夕阳、橙色衣物这些负样本一定要标出来或者单独作为背景图加入,否则模型会把它们当火焰;第三,小火焰目标要单独检查,如果标注框小于 8 像素,训练时容易被忽略;第四,训练集和验证集按 8:2 分,且要保证同一段视频的帧不要同时出现在两边,否则验证指标会虚高。
3.2 数据配置文件与模型配置
YOLOv9 需要一个数据 YAML 文件告诉它去哪里找图片和标签。
# data/fire_data.yaml path: ./datasets/fire # 数据集根目录 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 nc: 1 # 类别数,火焰检测通常只有 fire 一类 names: ['fire'] # 类别名称nc是类别数,如果你还要区分烟雾,就改成 2,names里加smoke。path建议用相对路径,换机器时不容易翻车。模型配置文件一般选yolov9-c.yaml或yolov9-e.yaml,前者速度快适合实时监控,后者精度高适合离线分析。我一般先用 c 版本跑通,再根据指标决定要不要换 e。
3.3 启动训练与关键参数
# 单卡训练命令 python train_dual.py \ --workers 8 \ --device 0 \ --batch-size 16 \ --data data/fire_data.yaml \ --img 640 \ --cfg models/detect/yolov9-c.yaml \ --weights weights/fire_best.pt \ --name fire_train \ --epochs 100 \ --patience 20 \ --lr0 0.01 \ --lrf 0.01参数说明:--batch-size 16是显存够的情况下的常用值,显存不够就降到 8 或 4;--img 640是输入分辨率,火焰小目标多可以提到 1280,但显存和速度会成倍变化;--epochs 100配合--patience 20表示 20 轮指标不提升就早停,避免过拟合;--lr0是初始学习率,微调预训练模型时建议降到 0.001 到 0.01 之间,从零训练才用 0.01 以上。训练日志和权重会保存在runs/train/fire_train/下,results.csv就是评估指标曲线的原始数据。
4. 评估指标曲线怎么看:mAP、召回与误报的三角关系
4.1 训练产出的曲线文件说明
训练结束后,runs/train/fire_train/目录下会有一组曲线图,常见的有results.png、confusion_matrix.png、PR_curve.png、F1_curve.png。results.png里包含 box_loss、cls_loss、mAP@0.5、mAP@0.5:0.95、precision、recall 随 epoch 的变化。火焰检测最该盯的是 recall 和 mAP@0.5,因为漏报一场火比误报几次代价大得多。
| 指标 | 含义 | 火焰场景建议值 |
|---|---|---|
| mAP@0.5 | IoU 0.5 时的平均精度 | 0.85 以上可用 |
| Recall | 召回率,漏报的反面 | 0.90 以上优先 |
| Precision | 精确率,误报的反面 | 0.70 以上可接受 |
| F1 | 精确与召回的调和 | 0.80 以上算均衡 |
4.2 用 Python 画出自己的评估曲线
如果源码包里的曲线不够用,可以自己从results.csv重画,方便对比不同实验。
# plot_metrics.py 绘制训练指标曲线 import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/train/fire_train/results.csv") df.columns = df.columns.str.strip() # 列名常有空格,先清理 fig, axes = plt.subplots(1, 3, figsize=(15, 4)) axes[0].plot(df["epoch"], df["metrics/mAP_0.5"], label="mAP@0.5") axes[0].plot(df["epoch"], df["metrics/mAP_0.5:0.95"], label="mAP@0.5:0.95") axes[0].set_title("mAP") axes[0].legend() axes[1].plot(df["epoch"], df["metrics/precision"], label="precision") axes[1].plot(df["epoch"], df["metrics/recall"], label="recall") axes[1].set_title("Precision & Recall") axes[1].legend() axes[2].plot(df["epoch"], df["train/box_loss"], label="box_loss") axes[2].plot(df["epoch"], df["train/cls_loss"], label="cls_loss") axes[2].set_title("Loss") axes[2].legend() plt.tight_layout() plt.savefig("my_metrics.png", dpi=150) print("曲线已保存")这段代码的关键是df.columns.str.strip(),YOLO 输出的 CSV 列名经常带前后空格,不清理会直接 KeyError。画出来的三张图分别看精度、召回和损失收敛情况。如果 box_loss 一直不降,多半是标注框有问题;如果 recall 高但 precision 低,说明误报多,需要加负样本。
4.3 阈值调整对火焰检测的实际影响
置信度阈值不是固定的。安防场景我一般把conf_thres从 0.25 降到 0.15,牺牲一点精确率换更高的召回率,宁可多报也不能漏报。但降到 0.1 以下,夕阳和灯光就会开始频繁触发。IoU 阈值iou_thres在火焰密集时调到 0.5 到 0.6,避免相邻火苗被合并成一个框。这两个参数没有标准答案,拿一段包含火焰和干扰物的测试视频,跑几组不同阈值,看误报和漏报的平衡点在哪里。
5. 避坑与排查:火焰检测落地时最容易翻车的五件事
5.1 现象:模型在验证集上 mAP 很高,实际视频里几乎不报警
原因通常是数据泄漏。同一段视频的相邻帧被分到了训练集和验证集,模型记住了背景而不是火焰特征。解决方法是按视频片段划分数据集,而不是按帧随机划分。另外检查验证集里是不是火焰占比过高,真实监控画面里火焰可能只占几帧,验证集分布要和实际一致。
5.2 现象:推理速度只有几帧每秒,完全达不到实时
原因可能是用了yolov9-e这种大模型,或者输入分辨率设成了 1280,又或者没开半精度。解决方法是换yolov9-c或更小的模型,把--img降到 640,推理时加model.half()用 FP16。如果还不行,检查是不是在 CPU 上跑,torch.cuda.is_available()必须是 True。
5.3 现象:白天正常,晚上或红外画面下检测全乱
原因是训练数据缺少夜间和红外样本。可见光模型直接用在红外画面上,颜色分布完全不同,特征提取层会失效。解决办法是分别采集白天、夜间、红外三类数据,训练时做混合,或者干脆训练两个模型按时间段切换。不要指望一个模型通吃所有光照条件。
5.4 现象:训练到一半 loss 变成 NaN
原因通常是学习率太大或者标注文件里有非法值。先检查--lr0是不是设成了 0.1 这种大值,微调时用 0.001 到 0.01。再检查标注.txt里有没有坐标超过 1 或者负数,写个脚本遍历一遍所有标签文件,把异常行打印出来。
5.5 现象:评估曲线里 mAP 波动剧烈,像心电图
原因可能是 batch size 太小导致梯度噪声大,或者数据集本身太小。解决办法是把--batch-size提到 16 或 32,同时加数据增强,比如 mosaic、mixup、随机翻转。如果数据量实在少,考虑用预训练模型冻结主干先训几轮,再解冻微调。
6. 把火焰检测推到可用的最后一公里
模型训完、曲线好看,不等于系统能用。我一般会做三件事把最后一公里补上。第一件是滑动窗口投票:单帧检测容易抖动,连续 5 帧里有 3 帧以上检出火焰才触发报警,能压掉大部分瞬时误报。第二件是区域掩膜:监控画面里有些区域永远不可能有火焰,比如天空、水面,把这些区域涂黑再送进模型,直接消除对应误报。第三件是报警分级:置信度 0.8 以上直接推紧急告警,0.4 到 0.8 之间只记录日志人工复核,避免半夜被夕阳叫醒。
# vote_filter.py 滑动窗口投票示例 from collections import deque class FireVoter: def __init__(self, window=5, threshold=3): self.window = window # 窗口帧数 self.threshold = threshold # 触发所需检出帧数 self.history = deque(maxlen=window) def update(self, detected): # detected: 当前帧是否检出火焰 self.history.append(1 if detected else 0) if len(self.history) < self.window: return False return sum(self.history) >= self.threshold voter = FireVoter(window=5, threshold=3) # 每帧推理后调用 voter.update(len(det) > 0) 即可这个投票器的参数window和threshold要根据帧率调。25 帧每秒的视频,window 取 5 大约对应 0.2 秒,响应够快;如果摄像头只有 5 帧每秒,window 要拉到 10 以上才有意义。threshold 一般取 window 的六成左右,太高会漏报,太低等于没过滤。
验证方法上,我习惯用一段包含火焰、夕阳、红色车辆、焊接火花的混合视频跑一遍,统计误报次数和漏报次数,画成混淆矩阵。只有误报和漏报都降到可接受范围,这套火焰识别检测系统才算真正落地。最后一个习惯:每次改完参数,把results.csv和推理视频的统计结果一起存档,不然过两周你根本想不起来哪组参数最好。希望帮到你。
本文还有配套的精品资源,点击获取