4.4 KiB
4.4 KiB
SCRFD RKNN 推理崩溃问题
问题现象
SCRFD 640x640 人脸检测模型在推理时立即崩溃:
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 18446744073709551615) >= this->size() (which is 1)
崩溃发生在 rknn_inputs_set() 调用时。
环境信息
- 平台: Orange Pi 5 Plus (RK3588)
- RKNN 运行时: 最初 1.5.2,后更新到 2.3.2
- RKNN-Toolkit2: 2.3.2
- 模型: SCRFD 2.5G 640 (版本 2.3.2)
根因分析
1. RKNN 库版本不匹配
系统最初安装的 librknnrt.so 是 1.5.2 版本:
$ strings /usr/local/lib/librknnrt.so | grep "librknnrt version"
librknnrt version: 1.5.2 (c6b7b351a@2023-08-23T15:28:22)
但 SCRFD 模型是用 2.3.2 版本的 toolkit 编译的:
$ strings scrfd_2.5g_640.rknn | grep "compiler version"
2.3.2(compiler version: 2.3.2 (@2025-04-03T08:26:16))
2. 动态形状模型兼容性问题
SCRFD 2.3.2 模型使用动态形状 (dynamic_shape):
- 输入/输出维度在运行时才确定
- C API
rknn_inputs_set()无法正确处理 - Python API (rknnlite) 可以正常工作
对比不同版本的模型输出维度:
| 模型 | 版本 | 输出维度示例 | 是否崩溃 |
|---|---|---|---|
| SCRFD 2.5G 640 | 2.3.2 | [12800, 1] (2D, 动态) |
✅ 崩溃 |
| SCRFD 500M 640 | 1.4.1b16 | [1, 12800, 1, 1] (4D, 静态) |
✅ 正常 |
| RetinaFace 320 | 2.3.2 | [1, 4200, 4] (静态) |
✅ 正常 |
| YOLOv8n 640 | 2.3.2 | [1, 84, 8400] (静态) |
✅ 正常 |
解决方案
步骤 1: 更新 RKNN 运行时库
将系统库更新到与模型编译版本一致:
# 备份旧版本
sudo cp /usr/local/lib/librknnrt.so /usr/local/lib/librknnrt.so.1.5.2.backup
# 删除旧版本
sudo rm /usr/local/lib/librknnrt.so
# 链接新版本 (系统已安装的 2.3.2)
sudo ln -s /usr/lib/librknnrt.so /usr/local/lib/librknnrt.so
# 验证
strings /usr/local/lib/librknnrt.so | grep "librknnrt version"
# 应输出: librknnrt version: 2.3.2 (429f97ae6b@2025-04-09T09:09:27)
步骤 2: 使用兼容的模型版本
由于动态形状模型在 C API 中存在兼容性问题,建议使用静态形状版本:
推荐模型: scrfd_500m_640.rknn (版本 1.4.1b16)
- 版本: 1.4.1b16-dad86923
- 编译时间: 2022-11-26
- 输入: 640x640x3, NHWC
- 输出: 9个张量 (scores_8/16/32, bbox_8/16/32, kps_8/16/32)
不兼容模型: scrfd_2.5g_640.rknn (版本 2.3.2)
- 动态形状导致 C API 崩溃
- 仅能通过 Python rknnlite 使用
步骤 3: 正确的 RKNN 输入配置
对于量化 INT8 模型,正确的输入配置:
InferInput input;
input.type = RKNN_TENSOR_UINT8; // 传递 UINT8,让 RKNN 自动量化
input.is_nhwc = true;
input.data = input_buf;
input.size = 640 * 640 * 3;
// rknn_input 结构体
rknn_input inputs[1];
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].size = input.size;
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].buf = input_buf;
inputs[0].pass_through = 0; // 关键:0 表示需要 RKNN 进行类型转换
关键点:
pass_through = 0: 让 RKNN 自动将 UINT8 转换为模型需要的 INT8pass_through = 1: 直接传递数据,需要数据已经是 INT8 格式且维度匹配
验证方法
验证库版本
strings /usr/local/lib/librknnrt.so | grep "librknnrt version"
strings /usr/lib/librknnrt.so | grep "librknnrt version"
验证模型版本
strings model.rknn | grep "compiler version" | head -1
最小化 C++ 测试
#include <rknn_api.h>
// 1. rknn_init()
// 2. rknn_query() 获取输入属性
// 3. rknn_inputs_set() 设置输入
// 4. rknn_run() 运行推理
// 5. rknn_outputs_get() 获取输出
经验总结
- 版本一致性: RKNN 运行时库版本必须与模型编译版本一致
- 动态形状: C API 对动态形状模型支持不完善,优先使用静态形状模型
- 输入配置: 量化模型的
pass_through和type必须正确配置 - 调试技巧: 先用 Python (rknnlite) 验证模型可用,再移植到 C++