safesight-edge/docs/bugfix/001-scrfd-rknn-crash.md

149 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 中存在兼容性问题,建议使用**静态形状**版本:
**推荐模型**: `face_det_scrfd_500m_640_rk3588.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 <rknn_api.h>
// 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)