【ArduinoNiclaVision】火焰检测系统——TinyML实践
本项目聚焦于打造一套可在Arduino Nicla Vision开发板上实时运行的火焰检测系统,核心优势在于全程脱离网络依赖,所有视觉感知、AI推理运算均在设备端独立完成,完美兼顾轻量化设计与实际应用需求。
该系统以合成数据突破真实火焰数据采集的瓶颈,结合TinyML模型实现边缘端快速推理,完整覆盖“数据生成-模型训练-设备部署”全流程。无论是作为嵌入式AI技术的入门实践案例,还是直接适配实际场景进行落地应用,都具备极高的实用价值。

² 硬件极简适配:依托Nicla Vision板载200万像素摄像头,无需额外外接传感器,便携性优势突出;
² 模型轻量化设计:采用TinyML领域的FOMO模型,专为资源受限的MCU架构量身打造,在速度与检测精度之间实现最优平衡;
² 数据采集革新:基于开源合成火焰数据,彻底规避真实火焰采集过程中的安全风险与场景局限;
² 实时性保障:模型体积控制在100KB以内,推理延迟低,完全满足边缘设备实时检测的核心需求。

本系统采用模块化闭环设计,流程清晰且具备高度可复现性,架构如下:

核心逻辑:通过合成数据高效解决“数据短缺+标注繁琐”的行业痛点,借助轻量化模型突破边缘设备的资源限制,全程无需依赖云端算力,确保系统在离线环境下稳定可用。

名称 | 说明 | 备注 |
Arduino Nicla Vision | 搭载200万像素摄像头、ARM Cortex-M7内核及AI加速模块 | 核心控制单元,原生支持OpenMV固件 |
USB-C数据线 | 用于开发板供电、程序下载及文件传输 | 推荐使用原装或支持数据传输功能的线材,避免仅支持充电的线材 |
2. 软件与平台
以下软件均提供免费版本,可完整覆盖项目开发全流程,按步骤安装即可顺利使用:
• Edge Impulse Studio:在线TinyML训练平台,无需本地配置复杂训练环境,支持FOMO模型一键训练与量化优化;
• OpenMV IDE:用于Nicla Vision开发板的固件烧录、推理脚本编写,支持实时预览摄像头画面与检测结果。


合成火焰数据的目录结构如下,通过多视角模拟生成丰富样本,为模型训练提供充足数据支撑:
开源合成火焰数据集:
https://github.com/shahizat/Fire-detection-system
Plain Text |
五、在Edge Impulse训练TinyML模型
Edge Impulse作为易用性极强的在线TinyML平台,无需本地配置训练环境,可直接上传标注数据集,一键完成FOMO模型训练与边缘设备适配优化。
本项目开源地址:https://studio.edgeimpulse.com/public/878999/latest ,感兴趣的同学可以去克隆到自己的edge-impluse,查看项目的具体设置和训练性能。

5.1 创建项目并上传数据集
1. 访问Edge Impulse Studio官方平台,完成账号注册与登录;
2. 新建项目:点击「Create new project」,项目命名为“Nicla Vision Fire Detection”,选择Project Type为「Image」,Task为「Object Detection」,点击「Create」确认创建;
3. 数据集上传:
○ 点击左侧导航栏「Data acquisition」→「Upload data」;
○ 选择“dataset_with_labels”目录下的所有图像文件与标签文件,按「Train」和「Test」分类上传(推荐数据划分比例为8:2,例如400张训练集、100张测试集);
○ 上传完成后,在「Data explorer」中校验数据,确认标签类别为“fire”(ID 0),无标注错误或数据异常。

FOMO(Faster Objects, More Objects)是专为边缘设备研发的轻量级目标检测模型,相较于传统YOLO模型,具备以下核心优势:
优势点 | 具体说明 |
资源占用低 | 模型体积可控制在100KB以内,无需GPU加速,完美适配Nicla Vision的MCU架构 |
推理速度快 | 省去复杂的框回归计算步骤,推理帧率可达10+ FPS,充分满足实时检测需求 |
部署便捷性高 | Edge Impulse可直接导出适配OpenMV的固件,无需手动进行模型格式适配 |
1. 点击左侧导航栏「Create impulse」,按以下配置设置:
○ Input block:选择「Image」,设置Input size为96×96(平衡检测精度与运行速度,适配Nicla Vision硬件性能);
○ Processing block:选择「Image data」,色彩空间设置为「Grayscale」(灰度图可有效减少计算量,提升模型推理速度);
○ Learning block:选择「Object Detection (FOMO)」,点击「Save impulse」保存配置。
2. 模型训练参数配置:
○ 点击左侧导航栏「Object Detection (FOMO)」→「Configure」;
○ Learning rate:设置为0.005(平衡训练收敛速度与模型稳定性);
○ Epochs:设置为100(确保模型充分收敛,若训练后期精度无明显提升,可手动提前停止训练);
○ Quantization:选择「Int8」(采用整数量化技术,减少模型体积与推理延迟,适配边缘设备运行环境);
○ 其他参数保持默认配置,点击「Save parameters」确认。

1. 点击「Start training」启动模型训练,等待训练完成(训练时长约10-20分钟,具体取决于数据集大小与网络传输速度);
2. 训练结果评估:训练完成后,在「Model performance」面板查看核心指标,达标标准如下:
○ F1 Score:建议达到85%以上(本项目实测值约85-90%),该指标直接反映模型的综合检测能力;
○ 模型大小:实测约56 KB,远低于Nicla Vision的存储上限;
○ 推理时间:在Nicla Vision开发板上实测约80-100 ms/帧,完全满足实时检测需求。
3. 优化建议:若F1 Score低于80%,可通过以下方式优化:扩充合成数据量(如增加至800张)、调整标注阈值、添加数据增强手段(如随机翻转、亮度扰动等),之后重新启动训练。

本阶段将训练完成的模型导出为适配OpenMV的格式,烧录至Nicla Vision开发板,实现火焰实时检测功能落地。
1. 在Edge Impulse项目页面,点击左侧导航栏「Deployment」;
2. 选择部署目标为「OpenMV」,点击「Build」生成固件压缩包;

生成 OpenMV Firmware 会把对应目标器件的固件和运行代码一起打包了,会更加方便使用。
1. OpenMV固件安装:
打开OpenMV IDE,点击顶部菜单栏「Tools」→「加载自定义固件」→ 选择edge_impluse生成的「Arduino Nicla Vision固件」;

以下为核心推理逻辑脚本,可直接在OpenMV IDE中运行,实现摄像头实时图像采集、模型推理及检测结果可视化展示。
1.用OpenMV IDE打开edge_impluse生成的配套「ei_object_detection.py」。
Python # # This work is licensed under the MIT license. # Copyright (c) 2013-2024 OpenMV LLC. All rights reserved. # https://github.com/openmv/openmv/blob/master/LICENSE
import sensor import time import ml from ml.utils import NMS import math import image
sensor.reset() # Reset and initialize the sensor. sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE) sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240) sensor.skip_frames(time=2000) # Let the camera adjust.
min_confidence = 0.85 threshold_list = [(math.ceil(min_confidence * 255), 255)]
# Load built-in model model = ml.Model("trained") # print(model) # Commented out to reduce serial output
# Alternatively, models can be loaded from the filesystem storage. # model = ml.Model('<object_detection_modelwork>.tflite', load_to_fb=True) # labels = [line.rstrip('\n') for line in open("labels.txt")]
colors = [ # Add more colors if you are detecting more than 7 types of classes at once. (255, 0, 0), (0, 255, 0), (255, 255, 0), (0, 0, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255), ]
# FOMO outputs an image per class where each pixel in the image is the centroid of the trained # object. So, we will get those output images and then run find_blobs() on them to extract the # centroids. We will also run get_stats() on the detected blobs to determine their score. # The Non-Max-Supression (NMS) object then filters out overlapping detections and maps their # position in the output image back to the original input image. The function then returns a # list per class which each contain a list of (rect, score) tuples representing the detected # objects. def fomo_post_process(model, inputs, outputs): n, oh, ow, oc = model.output_shape[0] nms = NMS(ow, oh, inputs[0].roi) for i in range(oc): img = image.Image(outputs[0][0, :, :, i] * 255) blobs = img.find_blobs( threshold_list, x_stride=1, area_threshold=1, pixels_threshold=1 ) for b in blobs: rect = b.rect() x, y, w, h = rect score = ( img.get_statistics(thresholds=threshold_list, roi=rect).l_mean() / 255.0 ) nms.add_bounding_box(x, y, x + w, y + h, score, i) return nms.get_bounding_boxes()
clock = time.clock() frame_counter = 0 # 自定义帧计数器
while True: clock.tick() frame_counter += 1
img = sensor.snapshot() total_detections = 0 # 总目标数量计数器
for i, detection_list in enumerate(model.predict([img], callback=fomo_post_process)): if i == 0: continue # background class if len(detection_list) == 0: continue # no detections for this class?
total_detections += len(detection_list) # 累加该类别的检测数量 # print("********** %s **********" % model.labels[i]) # Commented out to reduce serial output for (x, y, w, h), score in detection_list: center_x = math.floor(x + (w / 2)) center_y = math.floor(y + (h / 2)) # print(f"x {center_x} y {center_y} score {score}") # Commented out to reduce serial output img.draw_circle((center_x, center_y, 12), color=colors[i])
# Print total detections only when there are detected objects if total_detections > 0: print(f"Total Detections: {total_detections}", end="\n")
# 利用frame_counter变量,每10次循环输出一次帧率 if frame_counter % 10 == 0: frame_counter = 0 print(f"FPS: {clock.fps():.2f}", end="\n")
|
我在edge-impluse自动生成代码上实现了显著减少不必要的串口输出以提高系统性能,增加了检测目标数量的统计和输出功能,优化了检测数量输出逻辑使其仅在有检测结果时才输出,同时还优化了帧率输出逻辑确保每10次循环准确输出一次帧率。修改后的代码更加简洁高效,适合在资源受限的设备上运行实时目标检测任务。
2.运行脚本:点击OpenMV IDE顶部「Run」按钮,脚本将自动下载至Nicla Vision开发板并启动运行;
3.效果查看:在OpenMV IDE右侧预览窗口中,可实时观察摄像头画面。当检测到火焰时,将以红色框选目标区域,并显示“fire”字样及对应置信度,系统帧率稳定在10+ FPS。 默认的min_confidence 是 0.4 ,为避免误判需要提高到0.8.
该系统可完全脱离网络独立运行,无云端算力依赖,检测准确率与实时性均达到室内火灾早期预警的标准。同时,系统体积小巧、无需外接传感器,无论是家庭实验室、工业车间等场景,都可便捷部署,具备极强的实用性。
从头到尾完成这个火焰检测项目,不仅摸清了 TinyML 在边缘设备上的落地逻辑,更在实际操作中解决了不少之前没预料到的问题,收获远超预期。
深度学习项目最困难最繁琐的步骤是训练材料的预处理,本项目使用了开源的训练素材,暂时放下深度学习最困难的环节,先完成整个项目的流程验证,增强了对完成实际项目的信心和可行性。 这个项目不仅实现了火焰检测的核心功能,更让我对 TinyML 和边缘 AI 有了实操层面的认知,也积累了“从 0 到 1 落地嵌入式 AI 项目”的经验。如果你也想入门 Nicla Vision 和 TinyML,这个项目会是很好的起点——它不依赖复杂的硬件和昂贵的设备,却能完整覆盖核心技术链路,动手实践后收获的远比理论学习更扎实。
希望本教程能帮助你快速上手 Arduino Nicla Vision 和 TinyML 技术,开启边缘 AI 项目开发之旅!
感兴趣的小伙伴可以下载附件里的固件和运行代码进行测试,如果有不明白的地方或者希望跟我交流的可以给我发邮件(251199510@qq.com)
fire_detection-open-mv-fw-v1.zip


我要赚赏金
