摘要

针对传统火警识别系统存在的误报率高、实时性不足、资源占用大等问题,本文提出一种YOLO 实时检测 + Qwen3-VL-4B 二次确认的双模型联动架构,并基于瑞芯微 RK3576 芯片实现落地。该架构充分利用 RK3576 的 NPU 算力与多模型调度能力,通过轻量级目标检测模型实现火情的实时筛查,仅在检测到疑似目标时调用多模态大模型进行语义级验证,既保证了系统的实时性,又大幅降低了误报率。经实测验证,该方案在 RK3576 上可稳定运行,YOLO 模型维持 25~40FPS 的实时检测帧率,Qwen3-VL-4B 单次推理耗时控制在 1~2 秒,满足火警识别的实时性与高精度要求,可广泛应用于工业安防、家用监测、冷链监控等场景。

一、项目背景与需求分析

1.1 传统火警识别的痛点

传统火警识别主要依赖单一算法,存在两大核心问题:

  • 误报率高:仅通过颜色、亮度等简单特征判断,易将红色灯光、晚霞、红色纸箱、反光物体等误判为火情,导致大量无效报警。
  • 实时性与精度矛盾:若采用复杂的语义分析模型提升精度,会导致推理耗时过长,无法满足实时监控需求;若采用轻量级模型保证实时性,又难以过滤复杂场景下的误报。

1.2 系统核心需求

结合实际应用场景,本项目需满足以下需求:

  1. 实时性:摄像头视频流处理需维持 25FPS 以上,确保火情出现后能快速响应。
  2. 低误报:有效过滤灯光、反光、自然景物等非火情干扰,误报率接近 0。
  3. 资源适配:基于 RK3576 芯片部署,充分利用其 NPU 算力,避免资源过载。
  4. 低成本:无需对大模型进行微调,降低开发与部署成本。
  5. 稳定性:支持 7×24 小时连续运行,满足安防设备长期稳定需求。

二、整体架构设计

2.1 架构核心思路

本项目采用 **“轻量级检测 + 大模型验证”** 的分层架构,核心逻辑为:YOLO 模型负责 “筛查”,Qwen3-VL-4B 负责 “终审”,通过两级判断实现 “不漏报、少误报” 的目标。

  • 第一级(YOLO 实时检测):部署轻量级 YOLOv8n/YOLO11n 火情检测模型,快速筛选视频流中的疑似火焰、烟雾目标,不追求零误报,核心目标是不漏报
  • 第二级(Qwen3-VL-4B 二次确认):仅当 YOLO 连续 2~3 帧检测到疑似目标时,截取当前帧送入 Qwen3-VL-4B,通过语义分析判断是否为真实火情,核心目标是去误报

2.2 硬件平台与软件栈

2.2.1 硬件平台:RK3576

RK3576 是瑞芯微推出的高性能边缘计算芯片,核心参数为:

  • NPU 算力:6TOPS(INT8 量化),可同时支持多模型分时调度。
  • CPU 架构:4×A72+4×A53,主频最高 2.2GHz,满足模型调度与业务逻辑处理需求。
  • 内存支持:LPDDR4X 2GB~4GB,足够容纳 YOLO 与 Qwen3-VL-4B 模型及视频流缓存。
  • 外设接口:支持摄像头 MIPI 接口、BLE 无线传输,适配安防监控硬件场景。
2.2.2 软件栈
  • 系统层:Linux 5.10+,提供完善的硬件驱动与进程调度能力。
  • 模型层:YOLO 模型采用 RKNN 量化部署,Qwen3-VL-4B 采用 INT4/INT8 量化部署,适配 RK3576 NPU。
  • 业务层:C/C++ 开发,实现多模型联动、多帧触发、提示词调度、结果解析等核心逻辑。

2.3 系统工作流程

  1. 视频采集:摄像头采集实时视频流,通过 MIPI 接口传输至 RK3576,进行帧解码与预处理(缩放、归一化)。
  2. YOLO 实时检测:NPU 调用 YOLO 模型对每一帧进行推理,输出疑似火焰 / 烟雾的目标框与置信度。
  3. 多帧触发判断:若连续 2~3 帧检测到目标,触发 “二次确认” 机制,截取当前关键帧。
  4. Qwen3-VL-4B 推理:将截取的帧与专用提示词拼接,送入 Qwen3-VL-4B 进行语义分析。
  5. 结果解析与报警:解析大模型输出结果,若判定为 “火警” 则触发报警(声光提示、BLE 上传手机),否则继续正常检测。

三、核心模块设计与实现

3.1 YOLO 火情检测模块

3.1.1 模型选择与量化
  • 模型选型:选择 YOLOv8n/YOLO11n 轻量级模型,该模型参数少、推理速度快,在 RK3576 上可实现 25~40FPS 的帧率,完全满足实时性需求。
  • 数据集准备:收集火焰、烟雾、干扰场景(红灯、晚霞、红色物体)的图片 / 视频数据,共约 5000 + 样本,进行标注(火焰 / 烟雾 / 背景)。
  • 量化部署:将训练好的模型转换为 RKNN 格式(INT8 量化),通过 RK3576 NPU 进行推理,大幅提升推理速度并降低内存占用。
3.1.2 核心代码(C++,RKNN 部署)

cpp

运行

#include <opencv2/opencv.hpp>
#include "rknn_api.h"
#include <vector>
#include <string>

// 配置参数
#define YOLO_MODEL_PATH "/user/model/yolo_fire.rknn"
#define CONF_THRESH 0.5    // 置信度阈值
#define NMS_THRESH 0.45    // NMS阈值
#define TARGET_CLASS 0     // 0=火焰/烟雾(根据标注类别修改)

// YOLO检测结果结构体
struct Object {
    int x1, y1, x2, y2;   // 目标框坐标
    float conf;           // 置信度
};

class YoloFireDetector {
public:
    YoloFireDetector() {
        // 初始化RKNN模型
        rknn_context ctx;
        rknn_init(&ctx, YOLO_MODEL_PATH, 0);
        m_ctx = ctx;

        // 获取模型输入输出信息(省略,实际需根据模型配置获取)
        m_input_attr = rknn_get_input_attr(m_ctx);
        m_output_attr = rknn_get_output_attr(m_ctx);
    }

    ~YoloFireDetector() {
        rknn_destroy(m_ctx);
    }

    // 推理函数:输入图像,输出检测结果
    std::vector<Object> detect(cv::Mat& frame) {
        // 1. 图像预处理:缩放至模型输入尺寸(640×640)、归一化
        cv::Mat resized;
        cv::resize(frame, resized, cv::Size(640, 640));
        float* input_data = (float*)malloc(m_input_attr.size);
        // 归一化逻辑(根据模型要求修改,如除以255)
        for (int i=0; i<resized.total(); i++) {
            input_data[i] = resized.data[i] / 255.0f;
        }

        // 2. RKNN推理
        rknn_input inputs[1];
        rknn_output outputs[1];
        inputs[0].buf = input_data;
        inputs[0].size = m_input_attr.size;
        inputs[0].type = RKNN_TENSOR_FLOAT32;

        rknn_inputs_set(m_ctx, 1, inputs);
        rknn_outputs_get(m_ctx, 1, outputs, nullptr);

        // 3. 后处理:解析输出,置信度过滤+NMS
        std::vector<Object> results = post_process(outputs[0].buf, frame.size());

        // 释放资源
        free(input_data);
        rknn_outputs_release(m_ctx, 1, outputs);
        return results;
    }

private:
    rknn_context m_ctx;
    rknn_tensor_attr m_input_attr, m_output_attr;

    // 后处理函数(简化版,实际需根据YOLO输出结构修改)
    std::vector<Object> post_process(float* output, cv::Size frame_size) {
        std::vector<Object> objects;
        // 解析输出特征图,获取目标框、置信度
        // (省略具体解析逻辑,核心是遍历输出、过滤低置信度、NMS)
        for (int i=0; i<m_output_attr.size/256; i++) {
            float conf = output[i*5 + 4];
            if (conf < CONF_THRESH) continue;

            Object obj;
            obj.conf = conf;
            // 转换坐标至原始图像尺寸(省略)
            objects.push_back(obj);
        }
        // NMS处理(省略)
        return objects;
    }
};

3.2 Qwen3-VL-4B 二次确认模块

3.2.1 模型选择与量化
  • 模型选型:Qwen3-VL-4B 是通义千问推出的 4B 参数多模态大模型,支持图像 + 文本输入,无需微调即可完成火警语义判断,完美适配端侧 “二次确认” 需求。
  • 量化策略:采用INT4 量化(平衡速度与精度),量化后模型体积从 7GB 降至 2GB 左右,适配 RK3576 2GB + 内存配置。
  • 提示词工程:为火警识别场景定制专用提示词,控制模型输出 “火警 / 正常” 二选一结果,避免自由回答导致的解析误差。
3.2.2 核心提示词(产品级)

text

你是专业火灾检测系统。请判断图片中是否存在真实火焰、明火或烟雾。
注意:红色灯光、夕阳、红色物体、反光、暖色光源均不属于火警。
输出规则:仅允许输出“火警”或“正常”,不添加任何额外内容。
3.2.3 核心代码(C++,调用大模型推理)

cpp

运行

#include <string>
#include <fstream>
#include "qwen_vl_api.h"  // 假设为Qwen3-VL端侧推理API

// 配置参数
#define QWEN_MODEL_PATH "/user/model/qwen3_vl_4b_int4.rknn"
#define PROMPT "你是专业火灾检测系统。请判断图片中是否存在真实火焰、明火或烟雾。注意:红色灯光、夕阳、红色物体、反光、暖色光源均不属于火警。输出规则:仅允许输出“火警”或“正常”,不添加任何额外内容。"

class QwenFireVerifier {
public:
    QwenFireVerifier() {
        // 初始化Qwen3-VL模型(加载模型、初始化推理上下文)
        qwen_vl_init(&m_qwen_ctx, QWEN_MODEL_PATH);
    }

    ~QwenFireVerifier() {
        qwen_vl_destroy(m_qwen_ctx);
    }

    // 二次确认函数:输入图像,返回是否火警
    std::string verify(cv::Mat& frame) {
        // 1. 图像预处理:缩放至Qwen3-VL输入尺寸(448×448)、格式转换
        cv::Mat resized;
        cv::resize(frame, resized, cv::Size(448, 448));
        std::string image_base64 = mat_to_base64(resized);  // 转换为base64(Qwen输入格式)

        // 2. 拼接提示词与图像输入
        std::string input = PROMPT + "|||IMAGE_START|||" + image_base64 + "|||IMAGE_END|||";

        // 3. 调用大模型推理
        std::string result = qwen_vl_infer(m_qwen_ctx, input);

        // 4. 清洗结果(去除多余空格、换行)
        result.erase(remove_if(result.begin(), result.end(), ::isspace), result.end());
        return result;
    }

private:
    qwen_vl_context m_qwen_ctx;

    // OpenCV Mat转base64(简化版,实际需实现base64编码逻辑)
    std::string mat_to_base64(cv::Mat& mat) {
        std::vector<uchar> buf;
        cv::imencode(".jpg", mat, buf);
        std::string base64_str = base64_encode(buf.data(), buf.size());  // 需实现base64_encode函数
        return base64_str;
    }
};

3.3 双模型联动调度模块

该模块是系统的核心,负责多帧触发判断、模型调用时序、资源调度,避免 YOLO 与 Qwen 模型抢占 NPU 资源,保证视频流不卡顿。

3.3.1 核心逻辑
  1. 多帧触发:设置触发阈值为连续 3 帧检测到目标,避免单帧误触发导致的大模型频繁调用。
  2. NPU 调度:采用 “优先级队列” 机制,YOLO 模型优先级高于 Qwen,确保视频流实时处理不卡顿;Qwen 推理时,YOLO 进入等待状态,推理完成后恢复。
  3. 防重复调用:大模型推理完成前,忽略后续 YOLO 检测结果,避免重复触发。
  4. 结果解析:严格匹配大模型输出 “火警 / 正常”,避免格式错误导致的误判。
3.3.2 核心代码(主调度逻辑)

cpp

运行

#include <opencv2/opencv.hpp>
#include <atomic>
#include <thread>
#include <mutex>

// 引入YOLO与Qwen类
#include "yolo_detector.h"
#include "qwen_verifier.h"

// 系统配置
#define FRAME_RATE 25        // 摄像头帧率
#define TRIGGER_FRAMES 3     // 连续触发帧数阈值
#define NPU_TIMEOUT 1000     // NPU推理超时时间(ms)

// 全局变量
std::atomic<bool> g_qwen_running(false);  // 大模型是否正在推理
std::mutex g_frame_mutex;                // 帧数据互斥锁
cv::Mat g_latest_frame;                  // 最新视频帧
YoloFireDetector g_yolo;
QwenFireVerifier g_qwen;

// 视频采集线程
void video_capture_thread() {
    cv::VideoCapture cap(0);  // 打开摄像头(MIPI接口,实际需根据摄像头编号修改)
    cap.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
    cap.set(cv::CAP_PROP_FPS, FRAME_RATE);

    cv::Mat frame;
    while (cap.read(frame)) {
        std::lock_guard<std::mutex> lock(g_frame_mutex);
        g_latest_frame = frame.clone();
    }
    cap.release();
}

// YOLO检测线程
void yolo_detect_thread() {
    int trigger_count = 0;  // 连续检测到目标的帧数
    while (true) {
        std::lock_guard<std::mutex> lock(g_frame_mutex);
        if (g_latest_frame.empty()) continue;

        // 若大模型正在推理,跳过本次检测
        if (g_qwen_running) {
            continue;
        }

        // YOLO推理
        std::vector<Object> results = g_yolo.detect(g_latest_frame);

        // 判断是否检测到目标
        if (!results.empty()) {
            trigger_count++;
            // 连续触发帧数达标,调用大模型
            if (trigger_count >= TRIGGER_FRAMES && !g_qwen_running) {
                g_qwen_running = true;
                // 截取当前帧,异步调用大模型
                cv::Mat frame_copy = g_latest_frame.clone();
                std::thread qwen_thread([&, frame_copy]() {
                    std::string result = g_qwen.verify(frame_copy);
                    // 解析结果,触发报警
                    if (result == "火警") {
                        printf("【火警报警】检测到真实火焰/烟雾!\n");
                        // 此处可添加声光报警、BLE上传逻辑
                    } else {
                        printf("【正常】大模型验证:非火警\n");
                    }
                    g_qwen_running = false;  // 释放大模型推理锁
                });
                qwen_thread.detach();
                trigger_count = 0;  // 重置触发计数
            }
        } else {
            trigger_count = 0;  // 未检测到目标,重置计数
        }
    }
}

int main() {
    // 启动线程
    std::thread capture_thread(video_capture_thread);
    std::thread yolo_thread(yolo_detect_thread);

    // 主线程等待(实际可添加日志输出、系统监控等逻辑)
    capture_thread.join();
    yolo_thread.join();

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐