多模型并行推理
本章讲在 RK182X NPU 上同时跑多个 AI 模型的并行调度策略与多上下文 API。
整体框图
摄像头帧(1080p@30fps)
↓
┌─────────────────────────────────┐
│ Model A:人形检测(YOLOv8n) │ → 全帧推理
│ 输出:bbox 列表 │
└────────┬────────────────────────┘
│ crop 人体区域
↓
┌─────────────────────────────────┐
│ Model B:属性识别(ResNet50) │ → 每人 1 次推理
│ 输出:性别 / 年龄 / 衣着 │
└────────┬────────────────────────┘
│ 属性 + 轨迹
↓
┌─────────────────────────────────┐
│ Model C:行为分析(SlowFast) │ → 时序推理
│ 输出:站 / 走 / 跑 / 跌倒 │
└─────────────────────────────────┘实际产品中往往需要同时跑多个模型:人脸检测 + 属性识别 + 行为分析 + OCR。
1. 典型应用场景
实际产品常需三级流水线:Model A(YOLOv8n 全帧人形检测)→ Model B(ResNet50 裁剪后属性识别)→ Model C(SlowFast 时序行为分析),各模型输出级联传递。
2. NPU 多实例支持
RK182X NPU 支持多上下文并行:每个 rknn3_init 创建独立上下文,硬件调度器自动分时复用 NPU 核心。
#include <rknn3_api.h>
rknn3_context ctx_det = 0;
rknn3_context ctx_attr = 0;
rknn3_context ctx_act = 0;
rknn3_init_extend ext = {0};
rknn3_init(&ctx_det, &ext);
rknn3_init(&ctx_attr, &ext);
rknn3_init(&ctx_act, &ext);
/* 三个上下文可交替调用 rknn3_run() */
rknn3_destroy(ctx_det);
rknn3_destroy(ctx_attr);
rknn3_destroy(ctx_act);3. 编译(RK3588 上直接编译)
aarch64-linux-gnu-gcc multi_model.c -o multi_model \
-lrknn3_api \
-I/userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include \
-lpthreadRK3588 上编译成功:多上下文 API 用到 pthread,需要加
-lpthread。
3.1 关键 API 要点
| API | 说明 |
|---|---|
rknn3_init(ctx, ext) | 2 个参数版 |
rknn3_destroy(ctx) | 释放上下文 |
rknn3_run(ctx, inputs, n_in, outputs, n_out) | 5 个参数版推理 |
rknn3_init_extend | 只包含 device_id + reserved |
4. RKNN3 多上下文 API 验证
grep -n "rknn3_init\|rknn3_destroy\|rknn3_run\|rknn3_init_extend" /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h | head -10输出:
569: * @struct _rknn3_init_extend
571:typedef struct _rknn3_init_extend
576:} rknn3_init_extend;
1046: * @note The context must be released using rknn3_destroy when no longer needed
1048:int rknn3_init(rknn3_context* context, rknn3_init_extend* init_extend);
1104:int rknn3_destroy(rknn3_context context);
1124: * @param context The RKNN3 context handle obtained from rknn3_init
1135:int rknn3_run(rknn3_context context, const rknn3_tensor inputs[], uint32_t n_inputs, rknn3_tensor outputs[], uint32_t n_outputs);
1140: * @param context The RKNN3 context handle obtained from rknn3_init
1151:int rknn3_run_async(rknn3_context context, const rknn3_tensor inputs[], uint32_t n_inputs, rknn3_tensor outputs[], uint32_t n_outputs);rknn3_init_extend 结构体定义:
sed -n '571,576p' /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h输出:
typedef struct _rknn3_init_extend
{
char* device_id; /** < input parameter, indicate which device selected. if only one
device connected, can set nullptr. */
uint8_t reserved[128]; /** < reserved */
} rknn3_init_extend;API 签名验证:
rknn3_init():2 个参数 (rknn3_context*,rknn3_init_extend*)rknn3_destroy():1 个参数 (rknn3_context)rknn3_run():5 个参数 (rknn3_context,inputs[],n_inputs,outputs[],n_outputs)rknn3_run_async():5 个参数(异步版本)
rknn3_init 完整声明(含返回值注释):
sed -n '1042,1050p' /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h输出:
* @param[out] context Pointer to the RKNN3 context handle that will be initialized
* @param[in] init_extend Pointer to the device-specific initialization information
* @return int Return status code:
* - 0: Success
* - <0: Error code
*
* @note The context must be released using rknn3_destroy when no longer needed
*/
int rknn3_init(rknn3_context* context, rknn3_init_extend* init_extend);rknn3_destroy 完整声明:
sed -n '1098,1104p' /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h输出:
* @param context The RKNN3 context handle to be destroyed.
*
* @return int Return 0 if the operation is successful, otherwise return error code.
*/
int rknn3_destroy(rknn3_context context);rknn3_run 完整声明:
sed -n '1125,1135p' /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h输出:
* @brief Execute the RKNN3 model inference.
*
* @param context The RKNN3 context handle obtained from rknn3_init
* @param inputs Array of input tensors containing the input data
* @param n_inputs Number of input tensors
* @param outputs Array of output tensors to store the inference results
* @param n_outputs Number of output tensors
* @return int Return 0 if successful, otherwise return error code5. 推荐的三级流水线架构
用 Python threading + Queue 实现:detector_thread(全帧检测)→ attribute_thread(裁剪识别)→ action_thread(时序分析),各线程间通过 Queue 传递帧与检测结果。
import threading
import queue
from rknn3lite.api import RKNN3Lite
# 三个队列:原始帧 / 检测结果 / 属性结果
frame_q = queue.Queue(maxsize=10)
det_q = queue.Queue(maxsize=10)
attr_q = queue.Queue(maxsize=10)
det_model = RKNN3Lite(); det_model.load_rknn('yolov8n.rknn', 'yolov8n.weight'); det_model.init_runtime()
attr_model = RKNN3Lite(); attr_model.load_rknn('resnet50.rknn', 'resnet50.weight'); attr_model.init_runtime()
act_model = RKNN3Lite(); act_model.load_rknn('slowfast.rknn', 'slowfast.weight'); act_model.init_runtime()
def detector_thread():
while True:
frame = frame_q.get()
bbox = det_model.inference(inputs=[frame])[0]
det_q.put((frame, bbox))
def attribute_thread():
while True:
frame, bbox = det_q.get()
# crop 人体区域
attr = attr_model.inference(inputs=[crop])[0]
attr_q.put((frame, bbox, attr))
def action_thread():
while True:
frame, bbox, attr = attr_q.get()
act = act_model.inference(inputs=[frame])[0]
# 输出 (frame, bbox, attr, act)6. 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
编译报 undefined reference to rknn3_* | 缺 -lrknn3_api | 加 -lrknn3_api |
| 编译报缺 pthread | 多线程 | 加 -lpthread |
rknn3_init 返回负值 | 设备忙 / 权限 | lsof /dev/pcie-rkep-* |
| 多 ctx 推理互相抢占 NPU | 调度默认分时 | 接受,或改 -c 给单模型 |
device_id 传错 | 多卡场景 | 留 nullptr(单卡)或查 lspci |
