资讯动态

基于YOLO的森林火灾火焰烟雾检测系统实现与部署

发布时间:2026/9/15 23:38:53 来源:尧图企业网站定制
去年年底接手了一个森林野外火灾监测相关的项目要求用可见光摄像头对火焰和烟雾做自动识别。说实话火焰烟雾检测跟普通目标检测完全是两个世界目标轮廓不确定、颜色会骗人、远小目标多得离谱模型很容易被晚霞、灯光、雾气带偏。我选了YOLO系列作为检测主力后端用 Spring Boot 搭业务平台前端用 Vue 做监控大屏AI 推理单独拆成 Flask 服务最后还接入了 DeepSeek 和千问大模型让系统不仅能报警还能自动生成火情研判报告。这篇文章把整套实现思路、关键坑点和 YOLOv8/v10/v11/v12/26 的模型对比结果完整梳理一遍给准备做同类项目的朋友一个可以直接参考的落地样本。1. 项目背景与方案选型森林野外火灾最大的特点是“早发现一分钟损失差一个量级”。但山区监控点位多、摄像头分布散、值班人员盯不过来靠人眼一直盯着几十路视频不现实。所以项目目标很明确用可见光摄像头画面做实时火焰烟雾检测检测结果推送给值班人员同时自动生成一份包含位置、置信度、蔓延趋势建议的文字报告辅助决策。1.1 火焰烟雾检测到底难在哪火焰不是一个固定形状的物体它在每一帧都在变形颜色从亮白到橙红到暗红都有。烟雾更麻烦它是半透明的边缘模糊扩散以后往往和云、雾、工地扬尘混在一起。再加上森林场景光照变化剧烈太阳落山前的那半个小时整个画面都是暖色调误报率会明显上升。我最早试过传统图像处理方案比如HSV颜色空间分割火焰、帧差法检测烟雾实验室场景下效果还行一放到真实山区监控画面里就崩了。原因很简单传统特征是人手工设计的无法覆盖火焰烟雾的多样化形态。后面换成深度学习目标检测本质是让模型从大量样本中自动学习火焰和烟雾的特征组合鲁棒性完全不一样。1.2 为什么选择YOLO系列做检测模型项目第一版考虑过Faster R-CNN、Swin Transformer这些高精度模型但最后全部放弃了。原因很直接森林防火要接几十路甚至上百路视频单路画面也不能等太久推理速度必须控制在几十毫秒到一百多毫秒级别。Faster R-CNN在高分辨率图上跑一次要一秒钟左右Swin Transformer更慢完全撑不住实时监控场景。YOLO系列的工程优势太明显了。模型推理快部署生态成熟ultralytics框架一条命令就能训练和导出对于需要频繁迭代的落地项目非常友好。项目里我选择了YOLOv8作为基线然后把v10、v11、v12和更新的v26都训练了一遍目的就是搞清楚这几代模型在火焰烟雾这个特定任务上到底有多大差异。结论后面会详细展开这里先给个总体感受新版本在长尾小目标上确实有提升但并不是无脑选最新就最优。1.3 为什么是Spring Boot Vue Flask三段式架构项目一开始也纠结过要不要全部用Python写后端用FastAPI或者Django直接一套搞定。后来评估下来业务平台部分涉及用户权限、告警工单、历史记录、多人在线协同Java生态在这一块确实更稳。Spring Boot 负责核心业务MyBatis-Plus 操作数据库Spring Security 做权限控制都是成熟方案。AI推理单独拆出来做成Flask微服务是因为模型推理和业务开发的生命周期不一样。模型可能要频繁换版本、调参数、加预处理如果和Java业务代码耦合在一起每次模型更新都要重新编译部署整个后端太折腾。Flask只暴露几个接口Spring Boot通过HTTP调用它两边各自独立部署、独立扩展这个解耦思路在多人协作的项目里非常关键。Vue负责前端大屏和后台管理。选Vue3 Vite Element Plus的组合组件生态丰富监控大屏里的地图、图表、视频播放器都能找到成熟插件。前端和Spring Boot之间走HTTP和WebSocketSpring Boot再和Flask通信整条链路是清晰的。1.4 为什么接入DeepSeek和千问大模型检测模型输出的是“类别、坐标、置信度”这对开发人员来说很直观但值班人员看到一堆坐标框是没概念的。他们想知道的是现在哪个摄像头附近疑似起火火势大概多大周围有没有高风险区域这些判断需要把检测结果转成自然语言。DeepSeek和千问大模型正好补上这一环。把检测结果、摄像头位置、时间等信息拼成Prompt大模型可以生成一段结构化的火情研判报告甚至能给出“建议持续观察”“建议派人现场确认”“建议启动应急响应”这类的分级提醒。我在项目里同时接了两家主要原因是不同接口在不同时间的稳定性和响应速度有差异双通道可以互备。2. 系统整体设计与功能拆解整个系统不是一个单点Demo而是从摄像头接入、AI检测、业务处理、前端展示到智能分析都打通了的闭环。这一节把功能和数据流拆开讲清楚。2.1 核心功能模块梳理大致可以分成六个模块视频源管理维护摄像头RTSP地址、点位名称、经纬度、所属区域支持新增和停用。实时检测与跟踪定时或按帧抽取视频画面调用Flask推理服务输出火焰/烟雾类别、坐标、置信度。告警事件管理连续多帧检测到目标才触发告警避免单帧误检告警支持人工确认、误报标记、转工单。火情报告生成把告警信息发送给大模型生成自然语言报告并存入数据库。监控可视化Vue大屏展示实时视频、标注框、地图点位、告警列表、统计图表。模型对比分析离线评测不同YOLO版本的性能指标用图表展示差异方便决定后续用哪个模型上线。2.2 数据流转与核心链路以一路摄像头为例完整链路是这样的摄像头通过RTSP协议将视频流推送到流媒体服务。流媒体服务转成HLS/M3U8格式给前端播放同时Spring Boot定时任务按固定间隔从视频流中抽取一帧画面。Spring Boot把图片通过HTTP POST发送给Flask推理服务。Flask加载YOLO模型对图片做推理返回检测框列表。Spring Boot对结果做业务过滤比如置信度阈值、告警冷却时间、连续帧确认。命中告警后系统把检测结果写入数据库并通过WebSocket推送给前端。前端大屏实时弹出告警卡片在地图上标红同时回放视频流。告警信息同步调用大模型接口生成火情报告并归档。2.3 数据库与消息设计数据库里比较关键的是这几张表video_source摄像头信息字段包括camera_id、name、rtsp_url、hls_url、longitude、latitude、area_code。detect_result每次检测的明细记录包括id、camera_id、class_name、confidence、bbox_json、model_version、create_time。alert_event告警事件表每个事件关联多帧检测结果字段包括alert_level、status、confirm_user、create_time。model_report大模型生成的报告字段包括event_id、report_text、model_name、create_time。检测结果表和告警事件表分开设计是为了支持模型对比分析。比如我们想统计v8和v26在同样一批历史视频上的检出情况直接按model_version字段查询就行数据不会互相干扰。告警事件则聚合了业务信息避免开发人员每次都要从一堆原始检测框里自己算事件。3. 核心细节解析与实操要点这一部分是项目里花时间最多的地方也是网上教程通常不会细讲的东西。火焰烟雾检测能不能落地关键不在框架选型而在于数据、训练细节和部署优化。3.1 数据集准备与任务拆解模型效果的上限由数据决定。火焰和烟雾检测的数据集不能简单从网上下一个通用目标检测数据集就完事一定要贴合森林野外场景。我这边整理了三部分公开的火灾检测图像集、无人机视角和监控视角的山火视频截帧、以及自己标注的白天/傍晚/夜间样本。标注类别建议只设两类fire和smoke。不要细分“明火”“暗火”“浓烟”“薄烟”因为类别过多会让模型在边界模糊的时候更难学习后期根据置信度再做细分判断反而更灵活。标注工具用LabelImg或者X-AnyLabeling导出YOLO格式即可。样本量方面火焰目标至少要3000张以上烟雾目标建议5000张以上如果真实样本不够加大对薄雾、远小目标样本的采集比例。数据增强要特别注意两点。第一Mosaic增强对火焰这种形态多变的非常有帮助能在一个训练批次里混合四张图增加目标尺度和背景多样性。第二常规的HSV颜色增强要适度火焰颜色本身就是重要的判别特征过度随机改色调可能让模型学歪。对远小目标可以单独做Copy-Paste增强把小块火焰反复粘贴到不同背景里提高小目标召回率。3.2 YOLO版本怎么选、怎么训练在ultralytics框架下训练命令非常统一但参数背后的逻辑得想清楚。建议输入尺寸imgsz640起步火焰烟雾属于中小目标如果显卡显存足够可以尝试960甚至1280对远小目标有明显改善。batch size根据显存调整先跑通再逐步加大。epochs一般100到200同时开启早停防止过拟合。优化器我习惯用AdamW初始学习率lr0设置在0.001到0.01之间。学习率太大容易出现loss为nan太小则收敛慢。预热步数warmup设置为3个epoch让模型先用小学习率稳定起步。类别权重需要根据样本数量调整烟雾类别数量占优时给火焰类别更高的loss权重避免模型偏向数量多的类。这里有一个很多人容易忽略的点训练前一定要检查标注框是否越界。图像缩放后YOLO格式坐标是归一化的如果标注工具导出时没处理好可能出现x2小于x1或者坐标超过1的情况训练时模型会莫名其妙不收敛。我习惯写一个小脚本遍历每个txt标签文件把不合法框过滤掉或者修正好。3.3 YOLO系列损失函数和网络结构的差异做模型对比时不能只换模型文件还得理解它们到底改了什么。YOLOv8是最常用的基线采用anchor-free检测头损失函数由分类损失和回归损失组成回归部分使用CIoU和DFL整体训练稳定。YOLOv10的核心变化是去掉了NMS后处理用双标签分配策略让模型在训练和推理阶段行为一致理论上推理流程更简洁但在密集小目标场景下需要调阈值。YOLOv11在v8基础上改善了backbone和neck的C3k2模块参数量略有下降实测在中等目标上精度有小幅提升。YOLOv12重点引入了注意力机制的工程化改进让全局信息建模更容易对大面积烟雾这种上下文依赖强的目标有一定帮助。v26作为较新迭代在特征提取上加入了更多动态卷积和自适应注意力对远小目标更友好但显存和延迟也随之上升。从损失函数角度看DFL让回归更平滑CIoU则对重叠框的收敛更稳定。训练火焰烟雾这类形状不规则的物体我感受最深的是回归损失不需要调得太激进因为火焰边界本来就是模糊的过度追求和标注框完全吻合反而容易过拟合。3.4 Flask推理服务封装与性能优化Flask是一个轻量Web框架单进程跑模型推理时要注意模型加载方式。模型文件应该在服务启动时加载一次存成全局变量不要在每次请求时重新加载。推理请求用单独的线程池执行避免阻塞其他接口。一个经典的坑是Flask默认的开发服务器是单进程单线程的模型推理本身又很耗时并发一高接口就卡死。我这边用Gunicorn部署配置多个worker同时把模型放在共享内存中。GPU推理可以配置device0确保多个worker共用同一块显存。注意PyTorch的模型在fork方式下会被多个进程各自拷贝一份显存会成倍增长解决方案是用spawn方式启动worker或者干脆只用单worker配合线程并发。预处理阶段我做了letterbox填充把图像缩放到640x640同时记录缩放比例和填充偏移后处理要把检测框坐标映射回原图尺寸。这一套逻辑虽然简单但特别容易写错坐标错位在界面上表现为标注框和火焰位置明显不贴合。为了提升单路视频的检测频率我后来又加了模型预热和TensorRT导出。TensorRT在NVIDIA显卡上能把推理时间再压到原来的一半左右但导出过程中要注意固定输入尺寸动态尺寸会增加很多麻烦。3.5 Spring Boot后端集成与任务调度Spring Boot这边按常规四层架构拆分成Controller、Service、Mapper、Entity。Controller只做参数校验和返回Service写业务逻辑Mapper负责数据库操作Entity对应数据表。分层的意义是职责清晰检测结果处理、告警过滤、大模型报告生成这些逻辑都可以各自独立单元测试。调用Flask我用的是Spring的RestTemplate增强版WebClient。因为推理接口可能耗时几百毫秒异步调用更合适。配置连接超时为3秒读取超时为10秒重试次数为1次。这里要特别小心如果摄像头抽帧频率很高比如每秒一次而Flask处理不过来Spring Boot侧不能无脑继续发请求否则消息队列会堆积。我加了信号量和拒绝策略超过并发限制时直接丢弃当前帧等下一帧再检测。定时抽帧任务用的是Spring自带的Scheduled。每个摄像头配置一个调度任务按固定间隔执行间隔时间根据摄像头路数和GPU性能调整。如果GPU性能不足宁可把检测间隔拉长也不要让任务堆积造成延迟越来越大。3.6 Vue前端展示与M3U8视频播放前端大屏的核心是视频墙、告警弹窗、地图点位和统计面板。视频播放用的是HLS流因为RTSP协议浏览器原生不支持需要先转成M3U8。转流我用的是流媒体服务器把RTSP拉流后切片成HLS。前端用video.js加hls插件播放实测下来延迟在三到五秒左右对监控场景来说可以接受。实现M3U8播放时最常遇到的问题不是播放不了而是切片大小和缓存策略。保鲜期太短会导致频繁请求太长则延迟更大。一般切片时长设置为2到4秒播放器加上自动清理旧缓存。如果画面卡顿优先检查流媒体服务器的带宽和转码负载而不是前端代码。前端和Spring Boot之间的跨域问题我是在Vite配置里加了代理开发环境直接指向后端地址生产环境则通过Nginx统一转发。WebSocket用来推送检测结果连接建立后后端每次命中告警就推一条消息前端收到后更新告警列表和地图点位。3.7 DeepSeek和千问大模型的接入细节大模型接入要解决的是“结构化检测结果转自然语言”的问题。我用的是HTTP接口调用方式和官方文档保持一致。DeepSeek兼容OpenAI格式传模型名称、messages数组即可。千问走DashScope服务也有兼容模式把base_url指到对应的网关地址就能用同一套SDK。Prompt设计很关键。我先给一段系统提示词告诉模型它是在森林防火监控系统里工作输入是检测结果JSON输出是一段火情研判报告。然后把摄像头名称、烟雾置信度、火焰置信度、检测时间拼进去。为了让输出稳定我要求模型输出固定结构包括“事件概述”“风险等级”“处置建议”三个字段。温度参数设置为0.2过高会输出很多冗余内容。调用大模型时必须处理超时和失败。我设置了15秒超时失败后自动切换另一个模型。报告生成是异步的用户先看到告警再等几秒看到报告体验上比较自然。4. 完整实现过程与关键配置这一节按照从零搭建的顺序走一遍属于可以直接照做的部分。4.1 环境准备与依赖安装Python端建议Python 3.10版本PyTorch 2.x对应CUDA版本。核心依赖是ultralytics、flask、gunicorn、opencv-python、requests。Java端需要JDK 17、Maven 3.8以上。前端需要Node.js 18以上Vue3项目用Vite初始化。一个非常常见的坑是CUDA和PyTorch版本不匹配。安装PyTorch时一定要先确认显卡驱动支持的CUDA版本再用官方命令安装对应版本。检测不到GPU的时候训练速度会慢到让人怀疑人生。4.2 模型训练与导出准备好数据和配置文件后训练命令非常简单。以YOLOv8s为例yolo train modelyolov8s.pt datafire_smoke.yaml epochs120 imgsz640 batch16 device0训练完成后模型会生成在runs/detect目录下。评估指标在results.csv里包括每轮的mAP50、mAP50-95、precision、recall。我习惯把每一轮的best.pt和last.pt都保留下来best.pt用于部署last.pt用于继续训练。导出ONNX格式yolo export modelbest.pt formatonnx opset12导出TensorRT格式yolo export modelbest.pt formatengine halfTrue device0TensorRT导出会花一段时间期间显存占用很高耐心等就行。成功后会生成engine文件Flask推理时加载这个文件速度比onnx快很多。4.3 Flask推理接口实现推理服务我写成一个极简Flask应用核心代码如下from flask import Flask, request, jsonify from ultralytics import YOLO import cv2 import numpy as np app Flask(__name__) model YOLO(best_v26.engine) app.route(/detect, methods[POST]) def detect(): file request.files[image] img cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR) results model.predict(img, conf0.25, iou0.5, verboseFalse) boxes [] for r in results: for box in r.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() cls int(box.cls[0]) conf float(box.conf[0]) boxes.append({ class: model.names[cls], confidence: round(conf, 4), bbox: [x1, y1, x2, y2] }) return jsonify({boxes: boxes}) if __name__ __main__: app.run(host0.0.0.0, port8081)这只是最简版本。生产环境还要加请求体大小限制、鉴权token、日志记录和健康检查。/health接口返回GPU使用率和模型加载状态方便Spring Boot做服务健康监测。4.4 Spring Boot业务接口实现Spring Boot通过WebClient调用Flask接口的代码大致这样public DetectResult detectImage(MultipartFile image) { return webClient.post() .uri(http://ai-service:8081/detect) .contentType(MediaType.MULTIPART_FORM_DATA) .body(BodyInserters.fromMultipartData(image, image.getResource())) .retrieve() .bodyToMono(DetectResult.class) .timeout(Duration.ofSeconds(10)) .block(); }拿到检测结果后我会先过滤掉置信度低于阈值的框判断连续N帧是否都命中再决定是否生成告警。告警事件写库后调用WebSocket推送接口将消息广播给在线前端。大模型报告的生成则放到异步线程池里不阻塞主流程。4.5 Vue前端联调与跨域设置前端Vite的代理配置如下{ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true }, /ws: { target: ws://localhost:8080, ws: true } } } }视频播放部分安装video.js和hls相关插件后创建一个视频组件动态切换M3U8地址。页面上的告警列表用WebSocket事件驱动更新出现新告警时播放提示音同时在ECharts统计图上刷新今天的告警趋势。4.6 大模型API接入代码DeepSeek和千问接入我用的是OpenAI兼容接口统一封装。示例请求如下import requests def generate_report(detection_summary): messages [ {role: system, content: 你是森林防火监控系统的火情研判助手输出结构化报告。}, {role: user, content: f检测到以下目标{detection_summary}请生成火情研判报告。} ] resp requests.post( https://api.deepseek.com/chat/completions, headers{Authorization: Bearer YOUR_API_KEY}, json{model: deepseek-chat, messages: messages, temperature: 0.2} ) return resp.json()[choices][0][message][content]千问的接入只是在base_url和模型名上做区别整体代码结构可以复用。由于API密钥属于敏感信息我放在环境变量里不写进代码库。5. 模型对比分析结果项目花了大量时间做对比因为森林防火场景对误报和漏报都很敏感模型选型直接影响落地效果。这一节把评测结果和选型建议写清楚。5.1 精度与速度指标对比我在同一个测试集上对比了YOLOv8s、YOLOv10s、YOLOv11s、YOLOv12s和YOLOv26s。测试集包括白天、傍晚、远小目标、薄雾干扰等场景共1200张图片。硬件环境是单张RTX 4090推理延迟只算单图前向时间没有包含预处理。模型输入尺寸mAP50 (%)mAP50-95 (%)平均延迟 (ms)显存占用 (GB)YOLOv8s64082.456.84.22.1YOLOv10s64080.154.23.82.0YOLOv11s64083.658.34.01.9YOLOv12s64084.259.15.12.4YOLOv26s64086.562.76.83.2需要说明的是这些数据是针对火焰烟雾数据集的相对表现不代表在COCO等通用数据集上有相同结论。5.2 不同场景下的差异化表现从细分项来看各版本差距主要体现在远小目标和烟雾目标上。远小目标测试集上YOLOv26的mAP50比YOLOv8高出约7个百分点这个提升非常明显。因为v26的backbone对全局上下文建模更强可以把小片火焰和背景中的亮点区分开。但在近距离火焰场景v8和v11反而误报更少因为深层次特征提取越强越容易把叶子间的橙红色缝隙也当成火焰。烟雾检测方面YOLOv10表现弱一些对半透明目标的召回率偏低需要把置信度阈值降到0.15才能追平其他版本但那样会引入大量误报。YOLOv12和v26表现接近可能是因为注意力机制能捕捉更大范围的烟雾扩散趋势。5.3 部署选型建议如果摄像头路数多、GPU资源有限我的建议是优先用YOLOv11s甚至YOLOv8n。它们推理快显存占用低配合TensorRT能在单张消费级显卡上跑几十路视频抽帧检测。如果项目对漏报容忍度低比如重点林区建议用YOLOv26s尤其要保证远小目标场景的召回。代价是延迟增加但可以降低抽帧频率来平衡。比如原来一秒检测两帧改为两秒检测一帧模型处理能力反而可能更充裕。如果做边缘设备部署比如Jetson盒子我会选择YOLOv8n或者YOLOv11n导出TensorRT后延迟能控制在10毫秒级别。v26在边缘设备上压力偏大性价比不高。6. 常见问题与排查技巧实录这类系统踩坑点很多我把项目里遇到的典型问题整理成一份排查清单希望能帮你少走弯路。6.1 训练阶段训练时loss出现nan最常见原因是学习率过大。解决办法是把lr0从0.01降到0.001同时检查数据里有没有全黑图或全白图这类极端样本会导致梯度异常。模型只检烟雾不检火焰大概率是火焰样本太少。除了增加数据可以在配置文件中给火焰类别设置更高的loss权重。具体做法是修改数据配置里的class_weights参数或在训练参数中设置weight_path。标注框越界导致训练报错这个问题在自制数据集里特别常见。我会写一个脚本读取每个标签文件判断框坐标是否在0到1之间不合法就直接舍弃或者缩放到合法范围。处理后重新统计数据分布再开始训练。6.2 推理阶段Flask服务第一次请求非常慢因为模型需要加载和预热。解决方法是启动时加载模型并用一张空白图先跑一次推理让CUDA kernel完成预热。之后请求延迟才会稳定下来。GPU显存持续增长不释放通常是因为PyTorch的缓存机制。可以设置torch.cuda.empty_cache()但更根本的是避免在请求循环里反复创建模型或tensor对象。模型初始化放到全局作用域推理时固定用同一输入维度。TensorRT导出失败常见于版本不匹配。建议ultralytics、CUDA、TensorRT三者的版本要配套或者直接尝试增大onnx opset版本。如果仍然失败可以先导出fp16格式等验证通过后再做int8量化。6.3 Spring Boot与Vue联调前端请求后端出现CORS跨域开发环境用Vite代理解决生产环境配置Nginx反向代理统一域名基本不会再遇到。WebSocket连接频繁断开需要检查心跳机制。服务端每隔30秒发送ping客户端收到后回pong如果长时间没有心跳Nginx或浏览器会自动断开连接。M3U8视频播放卡顿优先观察流媒体服务器切片时的码率。对于森林监控画面画面静止时间很长可以用更低的码率切片减少带宽压力。另外播放器要开启自动降低清晰度弱网环境下至少能保证不花屏。6.4 大模型调用大模型接口超时可以在业务上做降级处理告警照常推送报告生成失败就先标记为“待生成”后续通过重试机制补齐不要让一个报告任务阻塞整个告警链路。输出内容不稳定表现为报告语气、字段格式经常变。处理办法是把输出格式要求写进系统提示词并要求模型输出JSON解析成功后展示。如果仍然不稳定在请求参数里把temperature调低到0.1基本能固定下来。7. 项目扩展方向与个人经验整套系统跑通之后我最大的体会是技术选型不能只看算法排名。YOLOv26精度确实最高但实际部署时要考虑推理成本、硬件资源、误报率平衡最后还是需要按业务指标来决定。很多项目失败不是因为模型不够强而是数据和工程细节没有跟上。这个项目后续还可以从几个方向扩展。比如接入更多数据源把无人机巡航画面纳入检测也可以融合红外热像仪的弱目标信息提高夜间火灾发现能力。大模型部分可以进一步做多轮对话让值班人员直接问“第三号摄像头附近什么情况”系统自动调取上下文和最新检测结果进行回答。最后再分享一个部署小技巧Flask推理服务开启批处理模式把短时间内多个摄像头传来的帧凑成一个batch一次性推理吞吐量能提升不少。一些新版本模型支持动态batch用起来也很方便。这些细节做不做直接决定了系统在真实环境中能不能稳定跑起来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价