一、核心背景:为什么选 RK3576 做 “YOLO + 大模型” 并发?

在火焰识别、水库危险识别等安防场景中,轻量检测 + 大模型二次决策是降低误报的核心方案,但传统芯片(如 RV1126B)受限于硬件架构,根本无法同时支撑实时检测与大模型推理 —— 要么 YOLO 占满资源导致大模型卡死,要么大模型占用算力导致检测掉帧。

而 RK3576 的硬件设计完美解决了这个问题:

  • NPU 双核独立:内置 2 个物理 NPU 核心(Core0/Core1),各 3TOPS INT8 算力,硬件层面完全隔离,互不抢占资源;
  • CPU 大小核架构:4×A72 大核(2.2GHz)+ 4×A53 小核(2.0GHz),可通过 CPU 亲和性严格绑定任务,实现 “大核跑大模型、小核跑 YOLO / 视频流” 的分工;
  • 算力与内存适配:6TOPS 总 NPU 算力 + 最高 8GB 内存,既能支撑 YOLO 实时高帧率,又能满足 Qwen2-VL-0.5B 的推理需求。

本文将详细讲解RK3576 双核 NPU 并发部署的完整流程:从模型转换(绑定 NPU 核心)、CPU 大小核绑定,到完整工程代码实现,最终实现 “YOLO 实时检测不卡顿 + 大模型触发决策不延迟” 的工业级效果。

二、核心调度逻辑:硬件与任务的完美匹配

在开始代码实现前,必须明确任务 - 硬件绑定规则,这是并发稳定的基础,具体分配如下:

表格

硬件单元 绑定任务 核心作用 性能预期
NPU Core0 YOLOv8 火焰 / 人体检测 实时视频流目标检测,高帧率、低延迟 25~35fps 稳定,无掉帧
NPU Core1 Qwen2-VL-0.5B-Instruct 推理 触发后二次决策,过滤误报(光影 / 动物等) 0.6~1.2 秒 / 次,异步不阻塞
CPU A53 小核 视频采集、YOLO 调度、数据上报 轻量任务,保证视频流流畅与系统基础运行 负载 40%~60%,稳定不卡顿
CPU A72 大核 大模型前后处理、图像裁剪 / 编码 大模型推理的前置 / 后置处理,释放小核压力 负载 30%~50%,高效并行

三、前置准备:模型转换(核心:绑定 NPU 核心)

要实现双核并发,模型转换阶段必须明确指定 NPU 核心——YOLO 绑定 Core0,Qwen2-VL 绑定 Core1,否则运行时会出现核心争抢。以下分两部分讲解 YOLO 和 Qwen2-VL 的转换流程,均基于 RK3576 官方工具链。

3.1 环境准备

  • 主机:Ubuntu 20.04/22.04(64 位)
  • 工具链:RKNN Toolkit2(YOLO 转换)、RKLLM Toolkit(Qwen2-VL 转换)
  • 模型文件:
    • YOLO:训练好的火焰 / 人体检测 ONNX 模型(推荐 YOLOv8-nano,轻量高帧率)
    • Qwen2-VL:Qwen2-VL-0.5B-Instruct(HuggingFace 下载,需量化)

3.2 YOLOv8 RKNN 模型转换(绑定 NPU Core0)

使用 RKNN Toolkit2 将 ONNX 模型转换为 RK3576 专属的 RKNN 模型,核心参数core_mask=1(对应 NPU Core0,二进制0b01)。

完整转换代码(Python)

python

运行

# -*- coding: utf-8 -*-
"""
RK3576 YOLOv8 转换脚本(绑定NPU Core0)
适配火焰/人体检测场景
"""
from rknn.api import RKNN
import os

# 配置参数
ONNX_MODEL_PATH = "yolov8n_fire_person.onnx"  # 你的YOLOv8-nano ONNX模型路径
OUTPUT_RKNN_PATH = "yolov8n_core0.rknn"        # 输出绑定Core0的RKNN模型
DATASET_PATH = "dataset.txt"                  # 量化数据集(少量样本即可,如100张图片)
TARGET_PLATFORM = "rk3576"                    # 目标芯片平台

# 初始化RKNN
rknn = RKNN(verbose=True)

# 1. 加载ONNX模型
print("=== 加载ONNX模型 ===")
ret = rknn.load_onnx(model=ONNX_MODEL_PATH)
if ret != 0:
    raise Exception(f"加载ONNX模型失败,错误码:{ret}")

# 2. 配置RKNN参数(核心:绑定NPU Core0)
print("=== 配置RKNN参数(绑定NPU Core0) ===")
rknn.config(
    # 图像预处理参数(与训练时一致,YOLOv8-nano默认RGB归一化)
    mean_values=[[0, 0, 0]],
    std_values=[[255, 255, 255]],
    # 目标平台与核心绑定(core_mask=1 对应NPU Core0)
    target_platform=TARGET_PLATFORM,
    core_mask=1,  # 0b01 = NPU Core0,0b10 = NPU Core1,0b11 = 双核共用
    # 量化配置
    quantized_dtype="int8",  # INT8量化,平衡速度与精度
    force_quantize_channel_axis=0  # 通道量化
)

# 3. 构建RKNN模型(量化+导出)
print("=== 构建RKNN模型(量化+导出) ===")
# 检查数据集文件
if not os.path.exists(DATASET_PATH):
    print(f"警告:数据集文件{DATASET_PATH}不存在,将使用随机数据量化")
    dataset = None
else:
    dataset = DATASET_PATH

# 构建模型
ret = rknn.build(
    do_quantization=True,  # 必须开启量化
    dataset=dataset,
    pre_compress=True      # 预压缩,提升推理速度
)
if ret != 0:
    raise Exception(f"构建RKNN模型失败,错误码:{ret})

# 4. 导出RKNN模型
print("=== 导出RKNN模型 ===")
ret = rknn.export_rknn(OUTPUT_RKNN_PATH)
if ret != 0:
    raise Exception(f"导出RKNN模型失败,错误码:{ret}")

# 5. 释放资源
rknn.release()
print(f"✅ 模型转换完成!生成文件:{OUTPUT_RKNN_PATH}(已绑定NPU Core0)")

3.3 Qwen2-VL-0.5B RKLLM 模型转换(绑定 NPU Core1)

Qwen2-VL 是多模态视觉大模型,需使用 RKLLM Toolkit 转换,核心参数npu_core=1(对应 NPU Core1),同时开启 INT8 量化保证推理速度。

完整转换代码(Python)

python

运行

# -*- coding: utf-8 -*-
"""
RK3576 Qwen2-VL-0.5B 转换脚本(绑定NPU Core1)
适配火焰/危险识别二次决策场景
"""
from rkllm.api import RKLLM
import os

# 配置参数
MODEL_DIR = "./Qwen2-VL-0.5B-Instruct"  # 下载的Qwen2-VL-0.5B原始模型目录
OUTPUT_RKLLM_PATH = "qwen2vl_0.5b_core1.rkllm"  # 输出绑定Core1的RKLLM模型
TARGET_PLATFORM = "rk3576"                # 目标芯片平台
MAX_CONTEXT_LEN = 512                     # 上下文长度(短上下文提升速度)
QUANTIZED_DTYPE = "int8"                  # 量化类型(INT8速度最快,精度足够)
NPU_CORE = 1                              # 绑定NPU Core1

# 检查原始模型目录
if not os.path.exists(MODEL_DIR):
    raise Exception(f"原始模型目录{MODEL_DIR}不存在,请先下载Qwen2-VL-0.5B-Instruct")

# 初始化RKLLM
rkllm = RKLLM()

# 1. 加载原始模型
print("=== 加载Qwen2-VL-0.5B原始模型 ===")
ret = rkllm.load(
    model_path=MODEL_DIR,
    model_type="qwen2_vl",  # 模型类型(Qwen2-VL系列固定为qwen2_vl)
    max_context_len=MAX_CONTEXT_LEN,
    num_npu_core=1          # 单核心运行,避免资源争抢
)
if ret != 0:
    raise Exception(f"加载原始模型失败,错误码:{ret}")

# 2. 构建RKLLM模型(核心:绑定NPU Core1)
print("=== 构建RKLLM模型(绑定NPU Core1+INT8量化) ===")
ret = rkllm.build(
    target_platform=TARGET_PLATFORM,
    quantized_dtype=QUANTIZED_DTYPE,
    npu_core=NPU_CORE,      # 强制绑定NPU Core1
    export_model_path=OUTPUT_RKLLM_PATH,
    # 优化参数
    optimize_for="speed"    # 优先优化推理速度
)
if ret != 0:
    raise Exception(f"构建RKLLM模型失败,错误码:{ret}")

# 3. 释放资源
rkllm.finish()
print(f"✅ 模型转换完成!生成文件:{OUTPUT_RKLLM_PATH}(已绑定NPU Core1)")

四、工程实现:完整 C++ 代码(双核 NPU + 大小核 CPU 隔离)

以下是量产级完整 C++ 代码,实现了 “小核 + NPU Core0 跑 YOLO”、“大核 + NPU Core1 跑 Qwen2-VL” 的并发逻辑,包含视频采集、YOLO 检测、大模型触发、决策上报全流程。

4.1 代码整体结构

plaintext

├── main.cpp          # 主程序(核心逻辑)
├── CMakeLists.txt    # 编译配置文件
├── yolov8n_core0.rknn # 转换好的YOLO模型
├── qwen2vl_0.5b_core1.rkllm # 转换好的Qwen2-VL模型
├── test.jpg          # 测试图片(可选)
└── dataset.txt       # YOLO量化数据集(可选)

4.2 编译配置文件(CMakeLists.txt)

cmake

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(rk3576_yolo_vlm_concurrent)

# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 开启编译优化
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall")

# 查找依赖库
find_package(OpenCV REQUIRED COMPONENTS core imgproc highgui)
find_library(RKNN_LIB rknn_api HINTS /usr/lib)
find_library(RKLLM_LIB rkllm HINTS /usr/lib)

# 包含头文件
include_directories(
    ${OpenCV_INCLUDE_DIRS}
    /usr/include/rknn
    /usr/include/rkllm
)

# 可执行文件
add_executable(rk3576_concurrent main.cpp)

# 链接库
target_link_libraries(
    rk3576_concurrent
    ${OpenCV_LIBS}
    ${RKNN_LIB}
    ${RKLLM_LIB}
    pthread  # 多线程支持
)

4.3 完整主程序代码(main.cpp)

cpp

运行

// main.cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <pthread.h>
#include <unistd.h>
#include <cstring>
#include <opencv2/opencv.hpp>
#include "rknn_api.h"
#include "rkllm.h"

// ========================= 全局配置与常量 =========================
// 模型路径(替换为你的实际路径)
const char* YOLO_MODEL_PATH = "/userdata/yolov8n_core0.rknn";
const char* QWEN_MODEL_PATH = "/userdata/qwen2vl_0.5b_core1.rkllm";

// 视频流配置(替换为你的摄像头路径,如/dev/video0)
const char* VIDEO_PATH = "/dev/video0";
const int FRAME_WIDTH = 640;
const int FRAME_HEIGHT = 480;
const int FPS = 30;

// YOLO配置
const float YOLO_CONF_THRESH = 0.5f;  // 置信度阈值
const float YOLO_NMS_THRESH = 0.45f;  // NMS阈值
// 目标类别(0=火焰,1=人体,根据你的训练集调整)
const int CLASS_FIRE = 0;
const int CLASS_PERSON = 1;

// 大模型配置
const char* QWEN_PROMPT_FIRE = "图中有真实火焰吗?只回答:有/没有";
const char* QWEN_PROMPT_DANGER = "图中的人物是否在水库危险区域?只回答:危险/安全";
const int MAX_VLM_TOKEN = 10;  // 大模型输出最大token(短输出提升速度)

// 并发控制变量
std::mutex g_frame_mutex;          // 帧数据互斥锁
std::condition_variable g_cv;       // 条件变量(触发大模型)
cv::Mat g_crop_frame;               // 裁剪后的目标帧(大模型输入)
bool g_trigger_vlm = false;         // 大模型触发标志
bool g_yolo_has_target = false;     // YOLO检测到目标标志
std::string g_vlm_result;           // 大模型推理结果

// CPU核心ID定义
const int CPU_A53_CORE0 = 0;  // A53小核0(主进程)
const int CPU_A53_CORE1 = 1;  // A53小核1(辅助)
const int CPU_A72_CORE0 = 4;  // A72大核0(大模型线程)

// ========================= 工具函数:CPU亲和性绑定 =========================
/**
 * @brief 绑定线程到指定CPU核心
 * @param thread_id 线程ID
 * @param core_id 核心ID
 * @return int 0成功,非0失败
 */
int bind_thread_to_core(pthread_t thread_id, int core_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);
    return pthread_setaffinity_np(thread_id, sizeof(cpu_set_t), &cpuset);
}

// ========================= YOLO相关函数 =========================
/**
 * @brief 初始化YOLO模型(绑定NPU Core0)
 * @return rknn_context RKNN上下文
 */
rknn_context init_yolo_core0() {
    rknn_context ctx;
    rknn_init_attr_t attr;
    memset(&attr, 0, sizeof(attr));

    // 核心:绑定NPU Core0
    attr.core_mask = RKNN_NPU_CORE_0;
    attr.model_path = YOLO_MODEL_PATH;

    int ret = rknn_init(&ctx, &attr);
    if (ret != 0) {
        std::cerr << "❌ 初始化YOLO Core0失败,错误码:" << ret << std::endl;
        exit(-1);
    }

    // 设置输入输出张量信息
    rknn_input_output_num io_num;
    rknn_query(ctx, RKNN_QUERY_INPUT_NUM, &io_num, sizeof(io_num));
    if (io_num.n_input != 1) {
        std::cerr << "❌ YOLO输入张量数量异常" << std::endl;
        rknn_destroy(ctx);
        exit(-1);
    }

    std::cout << "✅ 已初始化YOLO模型(绑定NPU Core0)" << std::endl;
    return ctx;
}

/**
 * @brief YOLO推理
 * @param ctx RKNN上下文
 * @param frame 原始帧
 * @return bool 是否检测到目标(火焰/人体)
 */
bool yolo_infer_core0(rknn_context ctx, cv::Mat& frame) {
    // 预处理:缩放为模型输入尺寸(640×640)
    cv::Mat input_mat;
    cv::resize(frame, input_mat, cv::Size(640, 640));
    // 转换为RGB(与RKNN转换配置一致)
    cv::cvt
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐