# 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** 版本: ```bash $ 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 编译的: ```bash $ 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 运行时库 将系统库更新到与模型编译版本一致: ```bash # 备份旧版本 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 模型,正确的输入配置: ```cpp 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 转换为模型需要的 INT8 - `pass_through = 1`: 直接传递数据,需要数据已经是 INT8 格式且维度匹配 ## 验证方法 ### 验证库版本 ```bash strings /usr/local/lib/librknnrt.so | grep "librknnrt version" strings /usr/lib/librknnrt.so | grep "librknnrt version" ``` ### 验证模型版本 ```bash strings model.rknn | grep "compiler version" | head -1 ``` ### 最小化 C++ 测试 ```cpp #include // 1. rknn_init() // 2. rknn_query() 获取输入属性 // 3. rknn_inputs_set() 设置输入 // 4. rknn_run() 运行推理 // 5. rknn_outputs_get() 获取输出 ``` ## 经验总结 1. **版本一致性**: RKNN 运行时库版本必须与模型编译版本一致 2. **动态形状**: C API 对动态形状模型支持不完善,优先使用静态形状模型 3. **输入配置**: 量化模型的 `pass_through` 和 `type` 必须正确配置 4. **调试技巧**: 先用 Python (rknnlite) 验证模型可用,再移植到 C++ ## 参考链接 - [RKNN API 文档](https://github.com/rockchip-linux/rknpu2/blob/master/doc/Rockchip_RKNPU_User_Guide_RKNN_API_V1.4.0_EN.pdf) - [SCRFD 原始仓库](https://github.com/deepinsight/insightface/tree/master/detection/scrfd) - [RKNN-Toolkit2 版本兼容性](https://github.com/airockchip/rknn-toolkit2)