
简介面向智能安防与火灾预警场景的YOLOv8火灾检测系统部署包适合计算机视觉学习者、课程设计者以及需要快速搭建检测原型的开发者。压缩包共9个文件总大小约19.82MB核心结构包含3个Python脚本分别负责主程序、参数配置与工具函数、1个训练好的模型权重、1份依赖清单、1份说明文档以及2个编译缓存文件模块划分清晰便于直接运行和二次改造。项目覆盖数据预处理、模型加载、实时推理、后处理与结果输出等关键环节配合说明文档中的部署步骤可帮助使用者理解YOLOv8从训练到落地的完整流程并快速接入视频流实现火源识别与报警联动。资源中还整理了模型微调、数据增强与超参数调整等常见优化思路方便进一步改进识别效果。已有247人学习下载适用于毕业设计、课程项目或安全监控演示是一份轻量且可上手的实战参考。1. 基于YOLOv8的火灾检测部署这份代码包拿到手该怎么跑说实话第一次看到这个yolov8_firedetection-main.zip的时候我以为是又一套从零训练的教学代码解压之后发现里面躺着app.py、utils.py、config.py和weights目录我意识到这其实是一份部署包而不是训练脚本。它把 YOLOv8 的推理能力包成了一个 HTTP 服务你传一张图或者一帧视频进去它返回火焰的边界框坐标和置信度本质上就是一个开箱即用的火灾检测后端。对正在做安防毕设、消防课设或者想在本地快速搭一个火灾预警 Demo 的人来说这份代码的价值在于省掉了你从训练到调接口的中间环节。我下面会从项目结构、环境搭建、训练自研权重、部署踩坑到模型替换完整拆一遍这套基于 YOLOv8 的火灾检测系统到底能不能直接用、怎么用、坑在哪。2. 先读懂项目骨架app.py、utils.py 与 weights 之间的调用链2.1 app.py 是入口HTTP 服务如何组织检测任务这个项目的核心入口就是app.py它做的事情用一句话概括接收外部请求把图片数据交给 YOLOv8 模型推理再把结果以 JSON 形式返回。我见过不少同学拿到代码先去看utils.py其实顺序反了你应该先看app.py里定义了哪些路由因为这决定了你该怎么调用它。# 常见做法是使用 Flask 起一个轻量服务 from flask import Flask, request, jsonify from utils import load_model, detect_image import config app Flask(__name__) model load_model(config.WEIGHTS_PATH, config.DEVICE) app.route(/detect, methods[POST]) def detect(): file request.files.get(image) if file is None: return jsonify({code: 1, msg: no image}) result detect_image(model, file.read(), conf_thresconfig.CONF_THRES) return jsonify(result) if __name__ __main__: app.run(hostconfig.HOST, portconfig.PORT, debugFalse)这段代码的逻辑很简单load_model负责加载weights目录下的权重文件到指定设备detect_image做预处理、推理、后处理三个动作最后把边界框列表序列化成 JSON。注意debugFalse我这个习惯是从一次线上事故里长出来的教训Flask 的 debug 模式会启动 reloader导致模型被重复加载两次内存直接翻倍。参数方面重点在config.py里的HOST和PORT。默认监听0.0.0.0:5000是把服务暴露在局域网里方便你用另一台机器或者手机调接口测试但如果是在公网服务器上部署一定要改端口并加访问鉴权否则任何人都能往你的模型服务里塞图片轻则被刷爆内存重则被塞恶意文件。2.2 weights 目录与 config.py模型权重加载的那点事weights目录里存放的是训练好的权重文件格式通常是best.pt或者last.pt。config.py里会有WEIGHTS_PATH、DEVICE、CONF_THRES、IOU_THRES这几个关键变量。我拆过很多类似的项目最常见的问题是权重路径写死解压后没有放在预期位置导致启动直接报FileNotFoundError。# config.py 中常见的配置项建议按自己的实际路径改 WEIGHTS_PATH ./weights/best.pt # 权重文件相对路径 DEVICE cuda:0 # 有 GPU 用 cuda:0没有就改成 cpu CONF_THRES 0.25 # 置信度阈值低于此值的目标被过滤 IOU_THRES 0.45 # NMS 的 IoU 阈值用于去除重叠框 INPUT_SIZE 640 # 模型输入尺寸YOLOv8 默认 640 CLASS_NAMES [fire] # 类别名影响最终输出标注 HOST 0.0.0.0 PORT 5000这里DEVICE是个值得展开的参数。如果你机器上有 NVIDIA 显卡并且装好了 CUDA 版的 PyTorch用cuda:0推理一张 640×640 的图片大约 20 到 40 毫秒如果只是 CPU 环境同一张图可能要 200 到 500 毫秒差距非常明显。我建议你启动前先检查一下torch.cuda.is_available()不要想当然地认为装了torch就能用 GPU很多同学在这上面翻车装的是 CPU 版的 PyTorchDEVICE写cuda:0直接报错。CLASS_NAMES这个参数很容易被忽略但它直接影响后处理输出。如果训练时类别是[fire]部署时写成了[smoke, fire]虽然模型推理本身不会崩但输出结果里对不上索引的类别名会让人摸不着头脑。2.3 utils.py 的职责预处理、推理与后处理的细节utils.py承担的是脏活累活它决定了检测结果准不准、会不会出现大量重复框。YOLOv8 输出的原始张量需要经过解码、置信度过滤、非极大值抑制NMS三个步骤才能变成人能看懂的坐标。# utils.py 中典型的后处理流程省略了解码部分的具体矩阵变换 def detect_image(model, image_bytes, conf_thres0.25, iou_thres0.45): img preprocess(image_bytes) # 读图、缩放、归一化 results model.predict(img, confconf_thres, iouiou_thres) boxes [] for r in results: for box in r.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) cls int(box.cls[0]) boxes.append({bbox: [x1, y1, x2, y2], conf: conf, class: cls}) return {code: 0, count: len(boxes), boxes: boxes}逻辑上需要注意两点第一preprocess里做了letterbox缩放也就是保持宽高比把图片填充到 640×640而不是直接拉伸变形否则检测框坐标会偏第二返回的x1, y1, x2, y2是缩放后坐标系里的值如果前端要在原图上画框还需要做一次坐标映射。有的项目在这里偷懒直接返回缩放后的坐标前端画框就会对不上原图这是部署集成时最容易出现的看起来没报错、结果全歪了的典型问题。对conf值的解读也要有概念。火灾检测场景里火焰特征比较明显置信度一般偏高如果阈值设成 0.5小火焰或者远距离火焰很容易被过滤掉漏报风险大设成 0.15 又会出现大量误检。我一般做法是先跑一批真实场景截图看模型输出的置信度分布再倒推合适的阈值。3. 本地环境搭建与 CPU 推理从 requirements 到输出第一张检测图3.1 安装依赖requirements.txt 的正确打开方式项目根目录下的requirements.txt是部署的第一步里面一般声明了ultralytics、flask、opencv-python、numpy这些核心依赖。需要注意ultralytics这个包会自动拉取对应版本的 PyTorch如果你机器上没有 GPU建议先手动安装 CPU 版的 PyTorch再装ultralytics不然你会白白下载 2 个多 GB 的 CUDA 依赖包。# 建议在干净的虚拟环境里操作python 3.8-3.10 兼容性最好 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # CPU 环境先装 CPU 版 torch再装项目依赖 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install -r requirements.txt这里有个细节requirements.txt里的版本号如果写的是ultralytics8.0.0实际安装时会拉到当时的最新版而 YOLOv8 的 API 在不同小版本之间有细微变化比如model.predict()的入参、返回结果的属性名都变过。我踩过一次坑项目里用的是旧版 APIrequirements.txt装的却是新版本启动后r.boxes.xyxy调用报错后来不得不手动锁定版本ultralytics8.0.221才跑通。建议你在安装后执行pip freeze | grep ultralytics确认版本如果项目里没有锁版本号装了报错就先降级试试。3.2 启动服务并验证接口用本地图片做冒烟测试依赖装好、权重文件确认在weights目录下之后就可以启动服务了。我会先做一个最小验证不调用 HTTP 接口直接用 Python 加载模型跑一张测试图确认权重文件本身没问题再启动 Flask 服务这样可以把权重坏了和服务代码有问题两类故障隔离开。# 第一步启动 Flask 服务前台运行看日志 python app.py正常情况下日志会显示Running on http://0.0.0.0:5000这时候打开另一个终端窗口用curl发一张图片过去测试。curl -X POST http://127.0.0.1:5000/detect \ -F image./test_fire.jpg \ -o result.json # 看到返回内容里 count 不为 0说明检测链路通了 cat result.json如果test_fire.jpg是一张明显的火焰图片count应该大于 0并且boxes里有置信度较高的框。如果count为 0问题往往出在图片内容上火焰占比太小或者图片分辨率过高导致缩放后特征丢失。有一个老熟人在这里折腾了半小时最后发现测试图是一张 4000×3000 的室内照片火焰区域只有几十个像素缩放后几乎不可见换一张火焰占比大的图就好了。3.3 用 Python 写一个调用客户端接入你自己的业务流程curl验证通过后下一步就是写一个可复用的调用客户端毕竟实际项目里不可能每次都用命令行发请求。下面这个脚本做了三件事读图、POST 给检测服务、在原图上画框并保存。import requests import cv2 def detect_fire(image_path, server_urlhttp://127.0.0.1:5000/detect): img cv2.imread(image_path) with open(image_path, rb) as f: resp requests.post(server_url, files{image: f}, timeout10) data resp.json() for box in data.get(boxes, []): x1, y1, x2, y2 map(int, box[bbox]) conf box[conf] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, ffire {conf:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(result_annotated.jpg, img) return data if __name__ __main__: result detect_fire(test_fire.jpg) print(result)这个客户端最大的价值在于暴露了坐标系统的转换问题如果你按返回的坐标画框发现框的位置偏了说明服务端返回的是缩放后的坐标需要乘以缩放比例才能映射回原图坐标。timeout10这个参数也值得养成习惯CPU 推理速度慢并发高时服务可能排队不设置超时会导致线程卡死我之前一个监控脚本就是没加超时服务挂了一次后所有请求全部挂起最后不得不重启进程。4. 训练自己的火灾检测模型从数据集组织到训练参数设置4.1 数据集准备目录结构与标注格式是第一个大坑项目里自带了一个训练好的weights权重但实际部署中你几乎一定会面临重训模型的需求比如要识别烟雾、要区分明火和余烬、要适配特定的摄像头角度。YOLOv8 训练的数据集格式有其固定要求一个根目录下分images/train、images/val、labels/train、labels/val四个文件夹标注文件是与图片同名的.txt每行代表一个目标格式是类别id 中心点x 中心点y 宽度 高度坐标值都归一化到 0 到 1 之间。# 推荐用以下目录结构组织火灾检测数据集 dataset/ ├── images/ │ ├── train/ │ │ ├── fire_001.jpg │ │ └── ... │ └── val/ │ ├── fire_050.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── fire_001.txt │ │ └── ... │ └── val/ │ ├── fire_050.txt │ └── ... └── fire.yaml标注工具我推荐用 LabelImg 或 Labelme导出时选 YOLO 格式。这里有个血泪经验LabelImg 导出的格式本身是归一化的但如果你中途用某些工具做过数据增强或者裁剪坐标就会错位。我处理过一个数据集训练时 loss 降不下来最后排查发现是有几张图用了旋转增强工具标注没有跟着旋转。另一个坑是类别 id 必须从 0 开始连续编号如果你只有火灾一个类别fire.yaml里就写names: [fire]id 就是 0千万不能写成names: [fire, smoke]但只标注了 fire这样训练时模型会认为有两个类别预测时类别索引会错乱。4.2 训练命令与参数batch size、imgsz 和 epoch 的选择逻辑fire.yaml文件是训练配置的核心它告诉 YOLOv8 数据在哪里、有几类、类别名是什么。写好后直接在项目根目录执行训练命令这里以yolov8n.pt为基础模型做迁移学习比从头训练收敛快得多需要的数据量也少。# fire.yaml path: ./dataset train: images/train val: images/val nc: 1 names: [fire]# GPU 环境下训练batch size 根据显存调整 yolo detect train \ datafire.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20训练参数里imgsz640是精度和速度的平衡点调到 1280 会提升对小火焰的检测能力但显存占用和推理耗时都会翻倍。batch16在 8GB 显存的卡上比较安全显存不够就降到 8 或者 4。patience20表示验证集指标连续 20 个 epoch 没有提升就提前终止训练这是个省时间的好参数但对火灾检测这种目标相对单一的任务模型通常在 50 个 epoch 左右就收敛了后面全是过拟合你需要主动观察训练日志里的val/box_loss和val/cls_loss而不是机械地把 100 个 epoch 跑完。训练完成后runs/detect/train/weights/目录下会有best.pt和last.pt部署时永远用best.pt它在验证集上表现最好last.pt只是最后一个 epoch 的快照。4.3 模型评估与导出 ONNX衡量指标和转换陷阱训练结束后项目根目录下的runs/detect/train/里会自动生成results.png、confusion_matrix.png、PR_curve.png等评估图表。重点关注mAP50和mAP50-95火灾检测场景里mAP50比mAP50-95更有参考价值因为火灾检测不需要极其精确的边界框大致框住火焰区域即可。# 导出 ONNX 格式方便后续在边缘设备上用 OpenCV 或 ONNXRuntime 推理 yolo export model./runs/detect/train/weights/best.pt formatonnx \ imgsz640 opset12导出 ONNX 有我自己踩过的坑默认的opset版本在较新的ultralytics里可能是 17如果后续用到 OpenCV 的dnn模块做推理它只支持到 opset 11 或 12会导致加载失败。所以导出时我会显式指定opset12。另外ONNX 导出后建议先用onnxruntime跑一遍对比 PyTorch 的输出结果因为ultralytics的预处理细节归一化方式、颜色通道顺序在导出后不会被自动继承前端推理代码必须自己实现完全一致的预处理。5. 部署避坑指南五个高频问题与排查思路5.1 环境与依赖类问题修完一个还有下一个现象一执行python app.py直接报 ModuleNotFoundError提示找不到ultralytics或者cv2。原因几乎都是没有激活虚拟环境或者requirements.txt没装全。解决方法是先pip list看已装包列表再逐个比对requirements.txt不要盲目重装。我见过一个最恶劣的情况是机器上同时存在 Python 3.6 和 3.10 两套环境用户pip install装到了 3.6但python app.py用的是 3.10排查了很久才反应过来。现象二安装了 CPU 版 PyTorch 后执行时警告torch.cuda.is_available() is False但config.py里DEVICE写的是cuda:0。原因就是硬件不支持或驱动没配好。解决方法是把DEVICE改成cpu或者卸载重装 CUDA 版 PyTorch。判断方法是执行python -c import torch; print(torch.__version__, torch.cuda.is_available())输出里能看到cu118之类字样说明是 CUDA 版。现象三启动时接口正常但一访问就 500 错误日志显示RuntimeError: Found no NVIDIA driver on your system。这个最坑的是启动阶段不报错因为load_model只是加载权重到指定设备访问时才真正执行前向推理。解决方法和上一条相同统一改成cpu或者装好驱动。有一个技巧把加载模型的代码单独跑一次能立刻定位是设备问题还是服务代码问题。5.2 检测效果与推理链路问题模型没崩但结果不对劲现象四图片上传后返回的count永远是 0换什么图都检测不到火焰。原因分两种测试图片里火焰占比太小缩放后丢失或者CONF_THRES设置太高。解决方法先用一张火焰占据画面 50% 以上的测试图把CONF_THRES临时改成0.01如果还是 0说明模型本身有问题或权重文件损坏重新下载权重。如果降到 0.01 后能检测出来说明模型没问题是阈值不合理。现象五自己训练了新的best.pt替换到weights目录后检测结果错乱比如在完全没有火焰的图片上画了一堆框。原因多半是训练时类别数和config.py里的CLASS_NAMES不一致。比如你训练时nc1、类别名是fire部署端CLASS_NAMES [fire]是对的但如果你重新训练了包含[smoke, fire]两个类别的模型而没有同步修改CLASS_NAMES模型的类别 1 会被错标为类别 0 对应的名字。解决方法是训练好后记录下来fire.yaml里的names部署时严格保持一致。现象六CPU 上推理一张 1920×1080 的图耗时超过 2 秒画面卡顿。原因一是输入尺寸固定为 640处理器在缩放大图时耗时原因二是utils.py里的预处理器没有做帧缓存复用。解决方法有二批量检测时把图片先缩放到 1280 以内再做 letterbox连续帧检测时相同尺寸的预处理只做一次OpenCV 的resize和letterbox函数是热点尽量复用分配好的内存。还有一个小技巧是设置model.predict(..., halfTrue)在 CPU 上不一定快但 GPU 上开启半精度能显著提速。6. 进阶用法替换自训权重并在视频流中做实时检测6.1 替换权重与类别对齐从静态图到摄像头视频流部署场景走到最后一定会从单张图片过渡到视频流无论是 USB 摄像头、RTSP 网络摄像头还是本地视频文件。关键改动在utils.py的循环读取部分而不是模型本身。# 用 OpenCV 读取视频帧逐帧送入检测模型 import cv2 cap cv2.VideoCapture(0) # 0 表示默认摄像头RTSP 地址可以填 rtmp://xxx while True: ret, frame cap.read() if not ret: break result detect_frame(frame) # 注意这里传的是 ndarray不是 bytes for box in result[boxes]: x1, y1, x2, y2 map(int, box[bbox]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow(fire detection, frame) if cv2.waitKey(1) 0xFF ord(q): break这里要特别提醒HTTP 接口的detect接受的是图片字节流但视频流场景下你应该绕过 Flask直接调用utils.py里的检测函数减少一次图片编解码的开销。cap.read()在部分低端摄像头或网络流媒体上可能出现阻塞我的做法是给read()加超时机制或者用多线程缓存最近一帧否则一旦网络抖动整个检测线程就会卡死。真正做实时检测时帧率才是绕不开的坎。GPU 环境下 YOLOv8n 跑 640×640 输入能到 30 到 60 FPSCPU 环境下只能到 5 到 10 FPS。如果你必须在 CPU 上实时跑降低imgsz到 480、关闭检测框的置信度输出、每 3 帧检测一次是三个最有效的降载手段虽然损失一些精度但画面流畅度能接受。6.2 检测到火灾后的报警联动与性能验证检测出火焰只是第一步实际部署往往要接报警逻辑。常见做法是维护一个连续帧计数器连续 N 帧检测到火焰才触发报警避免单帧误检导致频繁误报。N 设成 3 到 5 帧比较合理太低容易误报太高会延迟报警。# 连续 5 帧检测到火焰才触发报警是减少误报的实用策略 FIRE_CONSECUTIVE_FRAMES 5 fire_frame_count 0 while True: ret, frame cap.read() result detect_frame(frame) if result[count] 0: fire_frame_count 1 else: fire_frame_count 0 if fire_frame_count FIRE_CONSECUTIVE_FRAMES: trigger_alarm() # 可以是声音、短信或 HTTP 回调 fire_frame_count 0验证这套系统能不能扛住真实场景我习惯准备三类视频白天室外火焰、夜间室内火光、以及包含红色物体如红色汽车、红色广告牌的视频后一类的误报率是衡量模型质量的关键指标。如果红色物体频繁触发报警说明训练数据里缺少难负样本需要在训练集中加入大量红色非火焰图片重训。模型替换到边缘设备是另一个常用场景。之前我把这套系统往瑞芯微 RK3588 平台迁移时发现 ONNX 直接跑在 CPU 上效率不够需要先用瑞芯微的 RKNN 工具把 ONNX 模型转成 RKNN 格式。这个转换过程最折磨人的是算子的兼容性问题我的经验是训练时尽量用标准模块少用自定义算子导出时opset12的兼容性比opset17好很多。从那以后我每次做迁移都会先导出 ONNX 跑一遍onnxruntime对比原始 PyTorch 的推理结果确认输出一致再进入平台转换不再跳过校验直接上板子这个习惯救过我很多次希望也能帮到你。本文还有配套的精品资源点击获取