
这两年安防消防场景里“火焰烟雾检测”相关需求越来越多无论是森林防火、化工厂区安监还是楼宇消防预警大家最后基本都会收敛到同一个诉求能不能用摄像头自动识别早期火情别再依赖传统烟感探头。这类项目最直接的解法就是用目标检测模型而YOLO系列一直是首选。正赶上YOLO26发布我基于Ultralytics框架从零搭了一套火焰烟雾检测系统覆盖了数据集构建、模型训练、调优到落地部署的全部环节。这篇就完整记录整个流程踩过的坑、调参经验都会写到方便你直接照着做。这篇内容适合什么人群如果你刚接触目标检测想拿一个真实场景练手或者你已经在用YOLO做项目但火焰烟雾这类特殊目标总是误检漏检再或者你需要快速交付一个可演示的消防检测Demo这篇都能帮你省下大量时间。特别适合安防集成商、AI算法工程师、高校做毕设的学生。1. 项目全貌与方案选型思路1.1 火焰烟雾检测到底难在哪火焰烟雾检测和常规行人、车辆检测最大的区别在于目标形态极不规律。行人、车辆都有相对固定的结构边界清晰、特征稳定模型学起来容易。但火焰是不断跳动、外形时刻变化的半透明物体烟雾更是没有固定形状边缘模糊还有浓淡变化。加上光照影响同样的火焰在不同背景下看起来完全不同。这就导致传统目标检测模型直接用在火焰烟雾上往往在训练集上表现不错一到真实环境就各种漏检。另一个难点是场景尺度差异。森林防火需要检测几公里外的小火点可能只有几个像素化工厂区需要识别大面积的火焰和滚滚浓烟目标可能占满整张图。模型要同时应对这两种极端尺度对数据集的丰富度和特征提取能力要求都很高。这也是为什么这次项目我没有选择早期版本的YOLO结构而是直接上YOLO26。1.2 为什么选择YOLO26而不是其他版本YOLO26作为YOLO系列新一代版本在模型结构上做了不少针对小目标和遮挡场景的优化。它的CSP结构与注意力机制结合得更好在保持实时性的前提下对小目标的特征保留能力比前代强很多。火焰烟雾恰恰需要这种能力因为早期火情往往就是画面里很小的一个亮点。从工程角度看Ultralytics框架支持YOLO26的一键加载API和之前YOLO11、YOLOv8一致我们这些老用户基本上零学习成本切换。还有一个实际原因是模型文件体积和推理速度比较均衡部署到普通IPC或者Jetson这类边缘设备上也能跑实时。火焰烟雾检测场景往往需要做成监测系统长期运行在服务器或者嵌入式设备上模型效率直接影响硬件成本和电费开销。1.3 系统整体技术架构整个系统我拆成三个模块数据层、训练层、部署层。数据层负责收集原始图片、标注、增强和划分数据集训练层基于Ultralytics完成YOLO26模型的训练与评估部署层导出ONNX或TensorRT模型接摄像头RTSP流做实时推理再通过HTTP回调把告警信息推给业务方。这种分层设计的好处是每一层都能独立替换。比如你后续想换一个更大的训练数据集或者要部署到性能完全不同的设备上都只需要动对应模块不需要推翻整个系统。实际项目里这个灵活度非常关键因为你永远不知道客户现场会是什么硬件条件。2. 数据集构建从零搞定标注与增强2.1 数据来源与初步清洗火焰烟雾公开数据集渠道不算多常见的做法是混合多个公开来源再结合自己采集补充。我这次项目用了公开渠道下载的约5000张包含火焰、烟雾的图片另外自己组织了大概800张实地拍摄素材场景包括森林、建筑工地、室内走廊、厂房车间、夜晚和雨雾天气等。数据拿回来先做清洗这一步千万别省。公开数据集里经常有空图、标签错位、重复样本、以及图片中火焰太小根本无法确认的样本。我把明显质量问题全部剔除最终保留了4200张可用图片。清洗逻辑很简单用工具批量看一遍凡是肉眼都无法确认目标在哪的图片模型肯定也学不好直接丢掉。清洗完以后还有一个关键操作检查类别分布。火焰和烟雾两类目标数量要尽量均衡否则模型会偏向样本多的类别。我当时统计下来火焰框约7800个烟雾框约6200个比例在1.2比1左右算基本健康。如果你的数据某一类严重偏少优先补数据而不是靠调权重硬扛。2.2 标注规范火焰和烟雾的边界怎么画标注质量直接决定模型上限我这里详细说一下火焰烟雾的标注规范这是我们项目组反复试验后定下来的你可以直接参考。火焰的标注框对于火焰本体如果用矩形框注意要包含火焰最亮的核心区域和最外层的火焰边缘宁可稍微外扩一点也不要切掉火焰主体。因为火焰边缘是模型识别为“火焰”的重要特征框太紧反而丢失特征。对于树枝遮挡的火焰框要覆盖可见火焰的整体范围实在看不清的部分不要强行扩大。烟雾的标注框烟雾是最难标的。它扩散范围大、边缘极度不规则矩形框往往包含大量背景干扰。我的建议是烟雾框要覆盖浓烟的核心区域不需要把整片淡薄烟雾都框进去否则会让模型学到“天空也是烟”。多个烟雾团块比较分散时分开标注多个框不要用一个大框全包这样检测更精准。文件格式上我用Ultralytics标准的YOLO格式也就是每个图片对应一个同名txt文件每行是“类别ID cx cy width height”坐标是相对值。标注工具选的LabelImg和Roboflow前者免费单机后者在线协作方便但数据上传要注意保密。涉及敏感区域数据的话建议本地标注不要传云端。2.3 数据增强策略与数据集划分数据增强是火焰烟雾检测提升泛化能力的最有效手段之一。YOLO26框架自带增强策略训练时默认开启Mosaic、随机翻转等操作可以让模型见过更多样的场景。在框架自带增强的基础上我还额外做了三类针对性的离线增强。第一是光照扰动模拟白天不同时段的阳光角度和强度变化以及夜晚的灯光干扰。火焰在强光下对比度降低在黑暗中则格外显眼模型需要适应这种对比度差异。第二是模糊模拟烟雾场景下摄像头镜头容易结雾、沾灰画面本身就会模糊。我在一部分训练图上叠加高斯模糊和运动模糊让模型学会从模糊图像中提取火焰特征。第三是尺度模拟把原图缩小后嵌入大图模拟远距离小火点。这个操作对小目标检测非常有效能让模型更关注小火焰。增强完以后最终训练集约12000张验证集1500张测试集800张。划分时要保证同一场景的不同帧不会同时出现在训练集和验证集里否则评估结果会虚高。这个细节很多新手会忽略结果模型线上表现远不如测试指标其实就是数据泄漏了。3. 模型训练配置YOLO26训练火焰烟雾模型3.1 环境搭建与工程准备环境搭建我直接说了避免你走弯路。训练机用的是Linux系统显卡是标称24GB显存的GPUCUDA和cuDNN装好就不用再折腾了。Python环境建议用3.10版本太高或太低都可能和一些依赖库冲突。安装Ultralytics框架非常简单一条命令pip install ultralytics装完顺手验证一下版本确保是支持YOLO26的版本号。然后准备数据集目录结构按照YOLO规范datasets/fire_smoke/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml内容如下names: 0: fire 1: smoke path: datasets/fire_smoke train: images/train val: images/val注意names里0和1的顺序要和你标注文件里的类别ID一致这个错了后面白训。3.2 训练参数配置与启动YOLO26训练支持命令行也支持Python脚本。我推荐用Python脚本方便统一管理超参数。下面是我最终使用的关键参数以及对应的解释from ultralytics import YOLO model YOLO(yolo26n.pt) # 加载模型结构及预训练权重 results model.train( datadatasets/fire_smoke/data.yaml, epochs300, imgsz640, batch16, lr00.01, lrf0.01, optimizerAdamW, patience50, device0, workers8, cacheTrue, )这里的逻辑说一下。epochs设300对于中等规模数据集是足够的。选择预训练权重的方式是从COCO预训练模型开始训练这能省下大量时间因为火焰和烟雾的底层特征纹理、颜色、边缘在COCO数据上已经有了基础。batch设为16这是根据24GB显存推算出来的。显存小就调低batch比如8或者4同时可以降低imgsz到480。lr0是初始学习率0.01这个数值在YOLO系列中是稳妥的默认值。optimizer用的是AdamWYOLO11之后在自定义数据集上普遍表现优于SGD收敛更快泛化也更好。训练过程中最重要的事情是盯着loss曲线。启动后留意train/box_loss、train/cls_loss等指标是否稳定下降如果前几十个epoch loss出现剧烈震荡或者升高不降说明学习率偏高或者数据有问题需要及时停掉调整。框架自带训练可视化功能浏览器里打开端口就能看到曲线。3.3 训练过程监控与断点续训一个完整训练流程跑下来即使有GPU也要好几个小时。中途断电、重启怎么办放心Ultralytics支持的自动断点续训机制是默认开启的训练过程的进度保存在项目目录下。如果意外中断用同样的命令重新启动会自动从最近的checkpoint恢复。我自己习惯在每个epoch结束后看一眼验证集上的mAP50指标变化。YOLO26有验证集指标实时回调mAP50如果连续多个epoch不涨并且loss已经收敛就没必要继续跑了可以提前手动停止。我这次项目实际训练到230个epoch就收敛了后面70个epoch基本无提升属于白白消耗算力。通过早停策略能省不少时间。训练结束后输出两个关键文件best.pt和last.pt。best.pt是验证集mAP最高的权重最后部署用的就是这个。last.pt是最后一个epoch的权重一般用于断点续训。两者用途要分清别搞混。4. 模型优化精度提升与误检抑制4.1 评估指标解读模型训练完框架会输出一系列指标但我建议不要只盯着一个mAP值看。火焰烟雾场景下你要重点关心的是三类错误漏检真实火焰没被检测出来。这是最危险的错误意味着火灾没有被发现直接影响系统价值。误检把阳光、红色灯光、汽车尾灯、蒸汽等误判为火焰。这类错误会大量触发警报让运维人员“狼来了”。重复框抖动同一个火焰被多个不同置信度的框同时框住或者在相邻几帧中框的位置跳来跳去。这种问题在视频流里特别明显影响用户体验。我训练完第一版模型的mAP50在0.87左右看起来不低。但一跑真实视频立刻发现问题集中在误检上——傍晚阳光斜照下的红色墙面、工地上的红色塔吊都被模型检测为火焰置信度还不低。这说明只看mAP指标远远不够必须针对场景继续调优。4.2 误检和漏检的常见原因误检的根源通常是数据中“假正样本”太少。模型没见过红色墙面、红色车灯这些容易混淆的目标没法学会区分。解决办法有两个方向一是收集更多容易混淆的“硬负样本”也就是看起来像火焰但不是火焰的图片单独建一类或者让模型在这些图上不产生任何检测框二是通过数据增强把红色调物体的图片增多帮助模型理解火焰的动态纹理特征和静态红色物体的差异。漏检的根源常见于小目标和遮挡。前面提到的尺度模拟增强可以缓解小目标问题但对于火焰被树叶遮住大半的情况还是要靠增加遮挡样本。我补了大约200张“火焰被部分遮挡”的标注图漏检率立刻下降明显。4.3 实际采用的优化手段第一轮优化我把mAP50刷到了0.91误检也减少了约40%。具体做了这么几件事。做了类别权重的微调。如果烟雾样本少可以在loss函数中给smoke类别更大的权重。更多实际案例表明在数据均衡的前提下YOLO26的类别加权机制能有效修正类别不平衡。加入硬负样本策略。我收集了一批完全不含火焰烟雾、但颜色特征很像的高风险场景图片比如晚霞下的红色建筑、红色霓虹灯、工地红色脚手架、汽车尾灯特写。这些图片不标注任何目标作为背景样本参与训练。结果是模型学习到这些场景不该输出火焰框误检率明显下降。这一步非常有效建议你务必尝试。做了置信度阈值和后处理策略调整。在部署推理时把默认的置信度阈值从0.25提高到0.35大幅减少低置信度的误报框。同时开启NMS的类别间抑制避免火焰和烟雾框互相重叠造成重复告警。对于需要长时间无人值守的场景我还加入了连续帧确认逻辑即同一个检测框关联目标连续出现在3帧以上才触发告警这个机制几乎能消除掉所有偶发误检。另外如果你有算力预算可以尝试把模型从nano升级到small或medium。YOLO26s相比nano在火焰烟雾这种细节特征上提升明显mAP能有2到3个点的增幅。我最终部署版本使用的就是YOLO26s。5. 部署落地从导出到实时检测5.1 模型导出与推理加速训练好模型之后第一步就是导出成部署格式。我这里首选导出ONNX因为ONNX是通用中间格式市面上推理框架基本都支持切换成本最低。导出命令from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) model.export(formatonnx, imgsz640, simplifyTrue, dynamicFalse)simplifyTrue会用ONNX Simplifier对计算图做精简去掉冗余算子。dynamicFalse固定输入尺寸推理速度更快。导出后可以用ONNX Runtime跑一下验证输出是否和PyTorch一致。如果部署目标是英伟达Jetson或者特定服务器建议进一步导出TensorRT引擎。TensorRT的加速效果非常明显在低算力设备上能把推理时间压缩到原来的1/3甚至更低。需要说明的是TensorRT引擎和显卡型号强绑定换设备就要重新导出所以我在项目里把它作为可选项不强制。5.2 摄像头实时检测与告警流程实时检测我用的方案是OpenCV读取RTSP流逐帧送入模型推理然后叠加检测框把结果推送出去。核心逻辑如下import cv2 from ultralytics import YOLO model YOLO(fire_smoke_engine.onnx) # 支持ONNX格式加载 cap cv2.VideoCapture(rtsp://your_camera_stream) fire_threshold 0.35 smoke_threshold 0.30 while cap.isOpened(): ret, frame cap.read() if not ret: break results model.predict(frame, conf0.30, iou0.5) # 对检测结果做平滑和告警判定 for result in results: for box in result.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) if cls_id 0 and conf fire_threshold: send_fire_alert() elif cls_id 1 and conf smoke_threshold: send_smoke_alert()这里几个细节要注意。RTSP流经常断线代码里要加自动重连机制否则程序静默挂掉。我写了一个简单的重连逻辑断流后每隔3秒重新尝试连接最多重试5次。对于多个摄像头场景建议用多线程或异步处理每个摄像头的拉流和推理分开避免一路画面卡死导致全线瘫痪。也可以把推理放到一个独立进程通过队列传递画面帧更稳定。告警推送可以用HTTP回调、MQTT或者企业微信机器人。消防项目我建议至少做两级一级推给现场值班中心二级短信推送给负责人。告警信息要附带截图和视频片段这需要额外写一个视频片段截取模块保留告警前10秒的画面方便事后追溯。5.3 边缘设备与低算力场景适配如果你现场只能放一台小盒子不想上服务器那么部署策略要调整。第一模型用YOLO26n而不是s。第二输入尺寸从640降到480或者416速度提升显著。第三开启TensorRT FP16精度。第四把帧率控制在10到15帧就够用了火焰烟雾变化不像车辆那么快10帧完全够。这里给一组实测参考数据同一套模型在标称8G算力的设备上640输入尺寸FP16推理耗时约35毫秒416输入约20毫秒。对于火焰烟雾检测来说416输入在近距离场景下效果可以接受远距离小火点识别会变差。所以我的建议是近处监控用416提速园区制高点这类远距离场景必须用640。边缘设备的散热也是容易被忽视的问题。7x24小时运行的话设备持续发热长时间高负载可能降频甚至死机。我在部署时额外加了温度监控脚本超过85度自动重启推理服务实测下来稳定性提升不少。6. 踩坑实录与排查技巧6.1 训练阶段容易踩的坑训练时第一个坑是数据标签格式出错。数据集里用了Pascal VOC格式框坐标写的是绝对值而YOLO要的是归一化相对值。结果模型一度什么都学不到loss完全不降。排查方法是随机挑几张图把标签画到图上看看是否对齐。这个检查一定要做而且最好写个脚本批量验证。第二个坑是类别ID混淆。低级错误类别的顺序在标注脚本和data.yaml里不一致导致模型把火焰学成了烟雾。这类问题肉眼很难发现只能通过验证集输出比对识别。第三个坑是Mosaic增强在目标较小的时候反而有负面影响。Mosaic把四张图拼一起火焰目标会被缩得更小小目标更难学。我试过在最后50个epoch关闭Mosaic只保留基础的翻转和色彩增强mAP平滑上升小目标检测也有改善。Ultralytics提供了在训练末尾自动关闭Mosaic的参数推荐开启。第四个坑是显存不足时不要直接降batch更推荐先降imgsz。比如原计划640输入、batch16显存不够改成 imgsz512、batch16效果比imgsz640、batch8要好因为大batch的BN统计更稳定。6.2 部署阶段容易踩的坑部署踩过最严重的坑是ONNX模型和PyTorch模型输出不一致。排查后明确原因是输入图像归一化方式没有对齐。训练时用的是Ultralytics标准归一化但我的推理脚本用了OpenCV常见的除以255或者别的方式导致数值范围不同结果置信度整体漂移。解决方法是严格按照框架的预处理流程写推理代码或者直接用Ultralytics封装好的推理API。第二个坑是RTSP流解码延迟越来越严重。运营几天后发现视频延迟从1秒涨到多秒。原因定位到OpenCV的缓冲队列积压。解决办法是推流拉流之后丢弃积压的旧帧只处理最新帧。这个操作让延迟稳定在600毫秒以内。第三个坑是告警重复推送刷屏。同一堆火焰持续存在时系统每秒推一条告警直接把值班人员炸崩溃。我的处理是加入告警冷却机制同一目标在5分钟内只推送一次状态变更时比如从“火情”变为“已扑灭”才再次推送。6.3 快速排查速查表现象可能原因排查方法loss不降标签格式错误、学习率过高可视化标签、调低lr训练正常但验证集mAP很低数据泄漏、验证集和训练集重叠检查图片hash去重全屏误检、全都报火焰硬负样本太少、置信度阈值过低增加红景负样本、提高阈值小火焰完全漏掉输入尺寸过小、数据集缺少小目标用640输入、补小目标样本部署后置信度和训练时不一致预处理不一致对齐归一化参数视频流卡顿解码缓冲堆积、CPU转码丢弃旧帧、开启硬件解码告警刷屏缺少冷却机制增加告警去重和冷却7. 从实验室到生产环境需要补什么7.1 生产级系统要考虑的附加模块如果这套系统要真的交付给客户而不是停留在演示Demo有几个模块是必须补上的。第一是设备管理模块一个项目中可能同时接入几十上百路摄像头需要统一管理每个摄像头的状态、在线率、拉流地址断线了要能自动告警提示运维。第二是告警中心模块集中展示所有告警记录、处理状态、现场照片支持导出报表。第三是权限管理不同角色看到的内容不同接警员可以看实时画面但改不了模型参数。我这套系统目前已经接入了某园区项目中模拟部署使用的是完全虚构的部署环境。生产部署时我发现模型本身的性能反而是最容易搞定的一部分真正的复杂度全在周边配套系统。这也是为什么项目初期就要规划好架构不要只做一个模型完事不然后面接业务系统的时候会很痛苦。7.2 后续优化方向火焰烟雾检测这个方向没有终点后续可以做的优化还很多。一是引入时序信息靠单帧检测毕竟是截断信息把连续帧的目标运动特征结合起来比如火焰的闪烁频率、烟雾的扩散速度能大幅降低误检。二是加入温感数据融合普通可见光摄像头加红外测温双光谱验证火情准确率几乎能做到零误报。三是针对特定场景做专用模型比如森林防火、仓储、楼宇分别训练专用模型精度比通用模型更高。这些方向我自己也还在探索目前已经基于现有系统积累了一批现场反馈数据后续打算逐步补充到训练集中做增量训练。8. 一些实操心得与最终建议整个项目做下来我的体感是火眼金睛式的模型精度是由“数据质量合理调参场景适配”共同决定的任何一个环节掉链子都会影响最终效果。数据标注规范尤其重要火焰和烟雾的边界画法直接影响模型学习方向这个功夫花得最值。第二点体会是不要过分追求SOTA指标。在实际部署中稳定性和低误报率比mAP高零点几个点重要得多。一个容忍偶发漏检、但绝不误报的系统比一个指标好看但动不动误报的系统在用户那里口碑好得多。你现在训练出的模型如果误报率过高不妨试试我前面说的硬负样本策略和连续帧确认机制大概率立竿见影。最后分享一个小技巧训练完不要急着删掉中间结果。把每个epoch的验证集输出保存成视频或动图看一看你往往能从中间状态发现模型是“先学会了火焰再学会烟雾”还是“把红色误判成火焰”。这种认知能帮你更精准地定位问题。目前这套YOLO26火焰烟雾系统的预训练权重文件已经整理好了放在网盘里包含本文中训练的best.pt权重以及配套的标注样例文件。需要的读者可以按文章中的流程直接加载权重进行推理或继续调优。我后续还计划写一篇针对YOLO26自定义数据集部署到边缘设备的具体踩坑记录到时候再来填坑。