☰
无人机航拍烟雾识别与森林防火:数据集构建到YOLO部署实战
2026/10/16 2:35:39 网站建设 项目流程

写这个项目,是因为在实际林业巡检和乡村安防场景里,大家已经默认了一件事:火情发现越早,损失越小。而“早”,靠人盯着监控墙是不够的,靠卫星遥感也不够及时,真正能快速响应的,是贴近现场的无人机航拍巡检,以及部署在关键点位的安防摄像头。但图像数据一多,算法就得跟上。这个项目的核心,就是围绕无人机航拍和监控图像,构建一套能识别森林火灾烟雾、农田及农村区域烟雾的数据集,并用它训练出能用于预警系统的模型。这篇文章会完整拆解数据集从采集、标注到训练、部署的整个链路,把我在实操中踩过、填平过的坑都写出来,适合正在做森林防火、秸秆焚烧监控、智慧安防这类项目的工程师参考。

1. 项目概述与核心需求拆解

1.1 这不是一个简单的目标检测项目

很多第一次接触这个需求的朋友,会觉得烟雾识别不就是找个目标框出来吗?实际完全不是。烟雾和普通物体最大的区别在于它的形态是半透明、非刚性、边界模糊的,而且颜色会随光照、背景、燃烧物变化。我在做无人机航拍巡检图像数据集时,特意把烟雾、火焰、干扰物三类样本做了独立建库,就是为了强迫模型去学“烟雾到底是什么”,而不是仅仅去学“烟雾大概长什么样”。

这个项目的全称我写在标签里:无人机航拍巡检图像数据集 + 森林火灾烟雾图像识别 + 田间及农村烟雾图像识别 + 森林防火数据集 + 安防监控与火灾预警图像识别,第10358期。这个“期”没有任何特殊含义,就是我们内部按批次归档数据的编号。每期原始图像大概在2000到7000张之间,经过抽帧、清洗、标注之后,真正能进入训练集的约八成。第10358期这批数据的特点是场景跨度大,既有高海拔林区的航拍俯视角,也有平地农田的地面监控视角,还有农村房前屋后的低角度画面。这对模型的泛化能力要求一下子就上来了。

1.2 烟雾识别要解决的三个实际问题

现实项目里,用户不会关心你用的是YOLO还是什么,他们只关心三件事:误报多不多、漏报多不多、报警快不快。

第一是误报。清晨的山谷雾气、村庄里的炊烟、水面的水汽反光,这些在图像上都非常容易跟火灾烟雾混淆。我项目里最初版本的模型,在雾天误报率一度高到20%,值班员都懒得看报警提示了。第二是漏报。无人机在高空巡检时,烟雾在画面里可能只占几十个像素,人眼都要仔细看才找得到,模型很容易直接忽略。第三是响应速度。森林防火讲究“打早打小打了”,从识别到告警推送,管道全链路延迟不能超过3秒,否则无人机已经在空中转过去了,报警信息才到,意义就大打折扣了。

所以这个项目表面上是做一个图像识别数据集,本质上是解决一个复杂场景下的高召回、低误报实时检测问题。下面我按数据构建、模型训练、工程部署、问题排查四个层面,展开讲整个实操过程。

2. 核心数据集的构建逻辑与实操

2.1 数据从哪里来,怎么组织

在项目启动阶段,我最先做的不是找模型,而是花了两周时间审定数据采集清单。因为模型效果的天花板由数据决定,这句话在烟雾识别领域格外真实。我按照无人机航拍、杆塔监控、布控球三路来源,分别录制约8小时、4小时、2小时的视频素材,再统一做抽帧处理,统一按每秒1帧到5帧抽取,保证同一烟团在连续帧里有形态变化,而不是静止图片堆砌。

为了数据集的专业性,我把图像整理成了三类目录:

raw/ # 原始抽帧图像 forest_air/ # 森林无人机航拍图像 field_ground/ # 田间地头地面摄像头图像 rural_monitor/ # 农村安防监控图像 annotations/ # 标注文件(VOC格式XML) forest_air/ # 对应森林航拍的所有标注 field_ground/ # 对应田间地面的所有标注 rural_monitor/ # 对应农村监控的所有标注 labels/ # 训练标签(YOLO格式TXT) train/ # 训练集标签 val/ # 验证集标签 test/ # 测试集标签 images/ # 切分好并重命名后的图像 train/ # 训练图像 val/ # 验证图像 test/ # 测试图像

数据存储规范是后面一切工作的基础。我建议在项目一开始就固定这套目录,不要后面改来改去。我有个朋友做的项目就是目录结构来回调整,最后脚本全部重写,白白浪费两周时间。

2.2 标注规范比标注数量更重要

标注环节是整个数据集构建里最枯燥但最关键的步骤。我们用LabelImg做标注,内置包含四类标签:smoke(烟雾)、fire(明火)、mist(雾或水汽干扰)、chimney(炊烟)。很多人会觉得奇怪,为什么要专门把mist和chimney标出来呢?这两类本身就是模型误报的主要来源,把它们单独标成负样本类别,让模型在训练时看到它们的形态特征,比纯粹把所有非烟雾的区域都当背景要有效得多。

标注时我定了一个硬规矩:半透明烟雾用主要烟团轮廓框,不把整片淡薄区域全框进去。举个例子,一阵山风吹来,烟团被拉成细长的丝带状,视觉上很清楚,但AI其实很难学。如果标注框把整条烟带都囊括进去,正方形框附近几乎全是背景,反而会让模型决策紊乱。所以我要求标注员只框住烟团密度较高、肉眼能明确辨别的核心区域。明火一定是可见火焰的,至于炊烟,识别关键看烟柱是否集中、颜色偏青灰、且形态是小范围聚集。

这套标注规则执行下来,平均一张图需要45秒到3分钟不等。12000多张有效图像,前后投入5个人力连续忙了接近两周。期间我每天抽检50张,发现有框偏了就退回返工。数据质量检查必须有人来做,不能完全依赖标注器输出。我自己的经验是,标注规范里提前定好“边界怎么框、遮挡怎么处理、极小目标怎么定义”,否则后期清洗时的返工量会大到怀疑人生。

2.3 数据清洗与切分技巧

原始抽帧图像里有大量连拍重复帧,无人机悬停时画面几乎不动,这种情况必须去重,否则训练集里相似度太高的样本会让模型过拟合到特定背景上。我用的策略是感知哈希去重加人工二次抽检。程序算一遍图像相似度,丢掉超过阈值的相似帧,再让标注员快速过一眼,把仍然相似的去掉。

清洗完的有效数据,按照6:2:2的比例切分为训练集、验证集、测试集。切分有个硬约束:同一个场景的连续帧不能跨集。比如无人机围绕同一片林区飞行时连续抽帧的画面,要么全部在训练集,要么全部在验证集。如果不这么做,模型会在验证集上偷看到高度相似的图像,指标虚高,到真实场景就露馅。

我拿第10358期的实际数据做个说明:原始图像约18000张,去重清洗后得到13200张有效图像。切分后训练集7920张,验证集2640张,测试集2640张。其中森林场景约占55%,田间场景约30%,农村监控场景约15%。在类别数量上,烟雾目标的出现频次超出了我的预期,占到了全部标注框的约七成,可见项目中烟雾的形态比明火多得多。火焰往往在烟雾出现之后的某几帧才出现,所以训练集里天然存在大量“有烟无火”的样本,这正是现实应用里最常见的早期火情状态。

3. 模型选型与训练实战

3.1 为什么选YOLO,以及分支选择

在烟雾识别项目里,我首选YOLOv8和YOLOv8,因为它有几点和这个场景非常匹配:推理速度快、显存占用可控、易于部署到边缘设备。我也试过更早的YOLOv5,它在检测速度上很不错,但小目标召回率不及v8;而R-CNN系列检测精度虽然高,但是实时性差,单帧推理动辄几百毫秒,完全不适合无人机巡检。在无人机边缘端,模型推理最好控制在30ms以内,否则飞机飞行的轨迹和云台视角配合不上,烟雾区域可能已经飘出画面。

具体到YOLO的几个版本里,我实际使用的是YOLOv8m(中规模网络),在精度和速度之间比较平衡。如果让我给新手一个建议,第一次跑通用n尺寸,项目落地用m尺寸,追求最高精度再考虑x尺寸。l和x先不用想,因为在森林防火这种需要长时间续航的场景里,功耗带来的续航焦虑比那两三个点的mAP更让人头疼。

3.2 训练前需要准备的配置文件

项目训练选用Ultralytics框架,这是一套完整的训练推理工具链,不需要自己再手写前向反向逻辑。首先是安装依赖和准备数据集:

pip install ultralytics

然后看数据配置YAML文件。这份文件治理了整个数据集的读取路径和类别名称:

path: /data/smoke_detection/smoke_10358/ train: images/train val: images/val test: images/test nc: 4 names: 0: smoke 1: fire 2: mist 3: chimney

注意path这里是绝对路径。我踩过坑,相对路径在服务器上跑没问题,但一换机器就全乱,后面全靠猜,所以统一用绝对路径最省心。

3.3 训练超参数的调整心得

训练命令我用的是:

yolo detect train \ data=/data/smoke_detection/smoke_10358/dataset.yaml \ model=yolov8m.pt \ epochs=300 \ imgsz=1280 \ batch=16 \ device=0 \ patience=40 \ augment=true \ cache=true

几个关键超参数展开说:

  • imgsz=1280。这可能是整个训练里最重要的一步。无人机航拍画面是高空视角,远处烟雾往往很小,用640输入识别小目标会有明显漏检。加大到1280后,小目标召回率提升非常明显,但训练显存和耗时也随之上升。如果你的设备只能跑640,那也没关系,但不要指望小目标效果多好。
  • patience=40。这是早停机制,如果连续40个epoch验证集指标没有提升,就自动停止。森林烟雾数据变化幅度大,训练后期loss曲线可能来回震荡,给40个epoch的容忍空间比较合理。
  • augment=true。使用Ultralytics自带的增强策略,包括随机翻转、色彩空间抖动、针对烟雾场景很有用的MixUp和Mosaic等。烟雾在真实环境里本身就受到光照影响,增强能提高模型在不同时间段的适应性。

训练过程中,我最关注的验证指标是mAP50-95,因为这类数据集的自建测试集和实际应用场景最接近。训到后期,mAP50-95稳定在0.51到0.58之间,mAP50在0.72到0.78之间。如果你是头一次接触这类项目,不用被数值迷惑,关键要看亲测视频里烟雾到底检出没有。指标是参考,实战是标准。

3.4 训练不收敛的排查方向

如果你的模型训练曲线一直上不去,我建议按以下顺序排查:

  1. 先确认数据集路径对不对,再去检查标签文件是否有空文件。
  2. 然后看类别数nc是否和names一一对应,names顺序错乱会让模型学疯。
  3. 接着试降低imgsz到640或416,排除目标和显存问题。
  4. 最后检查数据集中负样本比例是否过大,如果标注的烟雾目标数量实在太少,模型只能学到“背景”,输出自然差。

我遇到过最离奇的问题是训练loss已经很低,但验证集对一帧有明显烟雾的图像始终不出框。后来反复看数据才发现,这一帧图像里烟雾被树枝半遮挡,标注人员把框画得很小,模型确实没有见过类似的正样本,所以学不出来。解决办法是在数据增强里增加随机遮挡和局部裁剪的配置,因为真实火灾场景里,烟雾经常会被树冠遮挡,被建筑遮蔽,不是每次都是完整的烟团。

4. 从模型到预警落地的工程化部署

4.1 端侧部署与推理性能优化

训练好的模型不能只活在一台训练机上。这个项目里的无人机巡检,用的是边缘计算盒子。我部署推理时最关注的是延迟:检测模型从摄像头输入画面到输出结果,必须控制在100ms以内,最好在30ms上下。

我用TensorRT,把模型导出为engine格式:

yolo export model=/data/smoke_detection/weights/best.pt \ format=engine \ device=0 \ imgsz=1280 \ half=True

导出为engine格式后,在Orin NX级别的边缘设备上,单帧1280推理约30ms,GPU利用率控制在百分之六七十左右,同时可以并发处理4路摄像头画面。如果算力紧张,可以把推理图缩到960或768输入,速度会更快,但小目标召回率有一定下降。核心原则是算力不够,不要硬上大分辨率,先用降低帧率或分段巡检来兜底,保证告警链路不断。

4.2 预警联动逻辑设计

检测到烟雾之后,系统要做的不是“画个框”,而是形成一条完整的信息流。我设计的预警联动流程是这样的:

  • 边缘端识别到烟雾,置信度高于0.55;
  • 触发时间窗口确认机制,在连续5秒内至少2次检测到烟雾目标,才判定为有效告警;
  • 将告警截图、GPS定位(针对无人机)、检测框信息、置信度值推送到后端服务;
  • 后端根据位置信息查询三维地图,标注所属林班、小班号或田块信息,推送到值班人员的移动端和管理后台。
  • 值班员复核之后,系统转发到应急响应群,同时附上路线建议。

这套流程的关键前提是不要把置信度调得太高。我见过为了压低误报,把阈值提到0.85的,结果真发生火情时烟雾特征被云层干扰,置信度只有0.7,直接漏过去。阈值定在0.5到0.6之间,同时用连续帧判断过滤偶然误报,是最稳妥的做法。

4.3 误报抑制的工程技巧

工程层面抑制误报有几个低级但有效的做法,我在项目里全用上了:

  • 不可见光时段强制切换:夜间监测无意义,烟雾在夜视红外图像里的对比度很低,我干脆在凌晨1点到4点这个时段把检测告警关闭,而是在能见度较好的清晨和上午、午后站点重点巡检。
  • 动态ROI去干扰:水上雾气通常出现在特定区域,农田烧荒则通常在秋冬季节。我在配置后台增加一个可绘制的屏蔽区功能,任何出现在屏蔽区内的检测结果直接丢弃,这从根本上解决了烟气和无关雾气混淆的问题。比如某些河道上空水汽反射特别频繁,我在后台直接把河道区域划成忽略区,误报率直接下降一半。
  • 模型“双通道”参考:对同一帧图像,同时启用烟检测和为另一个轻量级目标检测。第二次检测只输出smoke和fire,两次都确认才告警,因为纯背景干扰两次同时误报的概率要小得多。

5. 常见问题与排查技巧实录

5.1 本地测试pipeline跑出来的效果和训练指标不一致

这是最常见的问题。训练指标看mAP,但真跑一版视频,感觉什么都框不到。出现这种情况,通常是因为测试视频的分辨率、色彩空间与训练数据差异过大。比如我用的是1280训练,拍视频的相机是1920幅面,软件在推流时做了缩放,导致目标变小。解决办法很简单:在pipeline里加入zero-padding等比缩放,不要直接拉伸填满,不然图像变形会让检测结果乱飘。

5.2 无人机抖动导致目标闪烁,告警时有时无

无人机航拍时从一个角度飞到另一个角度,视角变化会导致同一烟团不同帧之间形态差异大。解决思路是加一个损失函数,损失函数中引入时序一致性约束,或者用跟踪器配合。我实际选了另一种更贴近部署的方式:调整云台策略,在发现疑似烟雾后悬停5秒,让推理侧连续多帧判断。虽然反应时间多了一点,但把闪烁误报彻底压下来了。对于固定点位监控,我直接加了简单的IOU跟踪,同一检测目标在下一帧的位置预测结果与当前帧重叠度高于阈值,才继续跟踪,连续三帧未丢失就确认告警。

5.3 错误框大量框住天空、云层和光线

这类问题多发生在模型训练时过于关注“白色半透明的区域”,因为烟雾很多场景里确实很白。我的复盘是,训练集里负样本数量不足,或者说富含有“无烟但背景复杂”的负样本太少了。我的补救方法是,从实际监控视频里剪一段无烟片段,抽帧后复制为负样本,单独加入训练集。初级负样本可以当背景,但一定要有“看似烟雾却不是烟雾”的近负样本,模型才能学会区分。云端和岩壁的反光和白色物体全部作为近负样本重点标注后,错误框的问题大幅减轻。

5.4 数据增强能弥补数据量不足吗

一定可以补,但有上限。单独用Mosaic增强白嫖小目标样本,效果远不如真实增加一批不同场景下的烟雾图像。第10358期数据正常训练,mAP50大概在0.72左右。后来我把数据增强打开,mAP50提升到0.76。但再往上走,靠增强就不够了。想要在雾天、黄昏、光线很差的环境上提升,最直接的办法就是单独采集特定时段的图像,重新标注后加入训练集。模型没见过黄昏的烟雾,就不可能在黄昏阶段识别出来,这跟增强无关。

5.5 常见问题速查表

我整理了一份在实际项目里最常查的排查表,分享给同行直接用:

现象原因排查方法
漏检远处小烟雾训练分辨率太低或标注框太小imgsz提到1280,检查小目标占比,必要时单独采集小目标样本
误报清晨雾气负样本不足增加真实无火雾气、反光水面、云层的近负样本
检测结果闪烁单帧置信度波动加连续帧确认,或轻量级目标跟踪
无人机移动时识别全乱视角变化过大对相同烟团保持两到三秒连续悬停巡检
夜间误报频繁红外下烟雾特征和模拟场景差异大关闭低可见度时段告警,或单独做红外训练分支
高分辨率图像检测慢模型推理耗时长TensorRT导出engine格式,或降低输入分辨率
一个烟团被分成多个小框标注框重叠过多后处理开启NMS,调低IoU去重阈值到0.5

6. 实操中的一点个人体会

做这类烟雾识别数据集和预警项目,我感觉最大的难点从来不是模型的精度卷到多少,而是把数据组织好、把误报漏报压到能用的水平、把链路跑通。在真实火灾预警场景里,模型不需要每棵树都认得,它只需要在烟刚冒头的那几十秒给值班员打一个电话、弹一个框,这就算成功了。

用第10358期这批数据集训出来的模型,我在多个场景做过实测。森林上空俯视视角,大范围烟气扩散时检测效果很好;农田秸秆焚烧产生的烟柱,因为形态集中且与背景对比明显,检测也很稳;最难处理的还是农村房前屋后的小范围烟雾,经常有炊烟混杂,这时除了靠模型,还得靠连续帧判断和人工复核协同。项目做完后,我最大的建议是:先把数据规范和标注规则做到位,再谈算法优化,这两个环节做好了,后面几乎是一路顺风。

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

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

立即咨询