在 Purple Pi OH(RK3566 + OpenHarmony 5.0)上部署 RKNN 模型完整指南
本文档记录在 Purple Pi OH 鸿蒙开发板(芯片:瑞芯微 RK3566,系统:OpenHarmony 5.0 32位标准系统)上,从零交叉编译并部署 RKNN NPU 推理程序的完整流程,以
rknn_mobilenet_demo为例。完成本文档后,你将拥有:
- 可复用的 OHOS arm32 交叉编译环境
- 适配 OHOS musl libc 的 RKNN runtime + OpenCV
- 标准化的 RKNN demo 部署模板
- 在 RK3566 NPU 上跑通 MobileNet v1 图像分类(波斯猫识别)
目录
- 一、背景知识(必读)
- 二、硬件和软件清单
- 三、环境准备
- 四、关键认知:为什么这事难做
- 五、从零开始部署
- 六、运行验证
- 七、常见错误速查表
- 八、复用本模板部署其他模型
- 九、进阶方向
- 附录 A:目录结构参考
- 附录 B:整个调试历程回顾
一、背景知识(必读)
在动手之前,确认下面这些事实,理解了它们才能顺利避坑:
1.1 Purple Pi OH 板子的特殊性
| 项目 | 实际情况 |
|---|---|
| 芯片 | RK3566(ARMv8 / 64 位 CPU, 单核 NPU 0.8 TOPS) |
| 系统 | OpenHarmony 5.0 Release(标准系统) |
| 内核 | aarch64(64 位) |
| 用户态 | 32 位 ARM(/bin/sh、init 都是 32-bit) |
| C 库 | musl libc(不是 glibc),链接器 /lib/ld-musl-arm.so.1 |
| C++ 库 | LLVM libc++(libc++_shared.so,系统目录没有,需要自带) |
| NPU 设备节点 | /dev/rknpu |
关键含义:不能用普通 Linux 的预编译二进制(它们用 glibc + libstdc++),必须用 OHOS clang 重新编译,target 必须是 arm-linux-ohos(32 位)。
1.2 厂商资源说明
Purple Pi OH 厂商提供了 RKNN 资源套件(下文称"厂商套件"),目录大致是:
arm64-v8a/ ← 套件根目录(名字叫 arm64,但其实同时包含 arm32 资源)
├── libnnrt/
│ ├── include/ ← rknn_api.h 等头文件
│ ├── lib/librknnrt.so.1.4.3b0 ← ★ 32 位库(我们用这个)
│ └── lib64/librknnrt.so.1.4.3b0 ← 64 位库(本板子用不上)
├── opencv_3.4.1/
│ ├── arm64-v8a/ ← 64 位 OpenCV
│ └── armeabi-v7a/ ← ★ 32 位 OpenCV(我们用这个)
├── npu_demo/
│ ├── rknn_benchmark/ ← 不依赖 OpenCV 的性能测试 demo
│ ├── rknn_common_test/ ← 依赖 OpenCV 的图像分类 demo
│ └── rknn_mobilenet_demo/ ← 依赖 OpenCV 的 MobileNet demo(本文示例)
├── librga_1.9.0/ ← 图像加速库(可选)
└── arm64-v8a样例编译.md ← 厂商编译说明(arm64,本文档把它转为 arm32)
⚠️ 注意:librknnrt.so.1.4.3b0 用的是 musl libc + LLVM libc++,不是 glibc 版本。所以不要用 Rockchip 公开 GitHub 仓库下载的 librknnrt.so(那是 glibc 版本,ABI 完全不兼容),必须用厂商套件里的这一份。
1.3 工具链选择
正确做法:用 OHOS SDK 里的 LLVM/clang,指定 --target=arm-linux-ohos。
错误做法(都会失败):
- ❌ 用 Linaro gcc(
arm-linux-gnueabihf-gcc):产出 glibc 二进制,在 OHOS musl 上跑不了 - ❌ 用 OHOS clang 但
--target=aarch64-linux-ohos:产出 64 位二进制,interpreter/lib/ld-musl-aarch64.so.1不存在 - ❌ 用 Android NDK:产出 bionic 链接的二进制,OHOS 不兼容
二、硬件和软件清单
2.1 硬件
- Purple Pi OH 鸿蒙开发板(RK3566, 烧录了 OpenHarmony 5.0 镜像)
- USB Type-C 数据线(连接电脑做 hdc 传输)
- 一台 Ubuntu 18.04 / 20.04 主机(交叉编译用,Ubuntu 22 也行)
2.2 需要下载的软件
| 软件 | 用途 | 下载位置 |
|---|---|---|
| OpenHarmony 5.0 Native SDK (Linux 版) | 交叉编译工具链 | https://repo.huaweicloud.com/openharmony/os/5.0-Release/ |
| hdc 工具 | PC↔板子 文件传输 / shell | OHOS SDK 自带,或独立下载 |
| 厂商 RKNN 资源套件 | librknnrt + 预编译 OpenCV + demo 源码 | 找开发板厂商(润和/扬创等)索取 |
具体到 OHOS SDK,下载文件名:
ohos-sdk-windows_linux-public.tar.gz (约 2.5 GiB)
2.3 Ubuntu 主机依赖
sudo apt update
sudo apt install -y cmake make git wget unzip build-essential
cmake --version # 至少 3.4.1,建议 3.16+
三、环境准备
3.1 解压 OHOS SDK
cd ~
mkdir ohos_sdk && cd ohos_sdk
# 把下载的 ohos-sdk-windows_linux-public.tar.gz 放进来
tar -xzf ohos-sdk-windows_linux-public.tar.gz
cd ohos-sdk/linux
# 解压 native SDK(其中包含 clang 工具链)
unzip native-linux-x64-*.zip
ls native/
# 应看到:llvm/ sysroot/ build/ build-tools/ ...
ls native/llvm/bin/clang # 验证 clang 存在
3.2 设置环境变量
# 临时设置(每个新终端都要 export)
export OHOS_SDK=~/ohos_sdk/ohos-sdk/linux/native
# 或者永久写入 ~/.bashrc
echo 'export OHOS_SDK=~/ohos_sdk/ohos-sdk/linux/native' >> ~/.bashrc
echo 'export PATH=$OHOS_SDK/llvm/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# 验证
echo $OHOS_SDK
$OHOS_SDK/llvm/bin/clang --version
# 应输出 OpenHarmony clang ...
3.3 确认板子状态(必做)
# 连板子,看 NPU 节点
hdc shell
# 在板子上执行
ls -l /dev/rknpu # 必须存在
cat /proc/rknpu/version # 记下驱动版本号
uname -m # 应该是 aarch64
file /bin/sh # 应该是 32-bit ARM,interpreter /lib/ld-musl-arm.so.1
exit # 退出 hdc shell
通过条件:/dev/rknpu 存在,file /bin/sh 显示 32-bit ARM。如果 /dev/rknpu 不存在,说明 OHOS 镜像里没编 NPU 内核驱动,需要联系厂商换镜像或重编内核。
3.4 放置厂商资源
mkdir -p ~/rknn_workspace
cd ~/rknn_workspace
# 解压厂商套件(假设套件名为 npu_arm64-v8a.zip)
unzip ~/Downloads/npu_arm64-v8a.zip
ls
# 看到 arm64-v8a/ 目录
ls arm64-v8a/
# 看到 libnnrt/ opencv_3.4.1/ npu_demo/ ...
四、关键认知:为什么这事难做
理解下面三个核心矛盾,可以避免反复踩坑:
4.1 矛盾一:架构错位
板子是 32 位 OHOS,但主流文档和资源默认 arm64。你看到任何"arm64-v8a"路径,都要在脑子里改成 armeabi-v7a / arm-linux-ohos / lib(不是 lib64)。
4.2 矛盾二:libc 不兼容
OHOS 是 musl libc,普通 Linux 是 glibc。
- ❌ Linaro gcc 编出来的程序、Debian armhf 的 .deb 包里的 .so,全都跑不了
- ❌ Rockchip GitHub 公开发布的
librknnrt.so是 glibc 版本,跑不了 - ✅ 必须用 OHOS clang 编 + 厂商提供的 OHOS musl 版
librknnrt.so
4.3 矛盾三:运行时库不全
OHOS 系统 /system/lib/ 下有 libc.so 但没有 libc++_shared.so。所以任何 C++ 程序都得自己带 libc++_shared.so。
具体到 RKNN demo,通常需要自带这些 .so:
librknnrt.so.X.X.XbX(厂商提供)libc++_shared.so(OHOS SDK 提供)libz.so/libz.so.1(OHOS SDK 提供,如果用了 OpenCV imgcodecs)
五、从零开始部署
以 rknn_mobilenet_demo 为例,完整流程。
5.1 拷贝 demo 源码到工作目录
cd ~/rknn_workspace
cp -r arm64-v8a/npu_demo/rknn_mobilenet_demo .
cd rknn_mobilenet_demo
# 看看源码用了哪些 API
ls model/
# 期望:RK3566_RK3568/ RK3588/ cat_224x224.jpg dog_224x224.jpg
# 如果是老命名 RK356X/,把它重命名:
[ -d model/RK356X ] && mv model/RK356X model/RK3566_RK3568
grep -n "rknn_\|cv::" src/main.cc | head -20
5.2 修复厂商 OpenCV 配置(关键步骤)
厂商在 arm64 编译机器上编 OpenCV 时,在 OpenCVModules.cmake 里硬编码了他们的 libz.so 绝对路径。我们需要替换成自己 SDK 里的 arm32 libz.so 路径。
cd ~/rknn_workspace/arm64-v8a/opencv_3.4.1/armeabi-v7a/share/OpenCV/
# 第一步:看原始硬编码路径是什么
grep -nE "libz|libdl|libpthread|\.so" OpenCVModules.cmake OpenCVModules-release.cmake 2>/dev/null | head -30
输出会有类似的行:
/root/OpenHarmony/3588_npu/npu_4.0r/ohos-sdk/linux/native/sysroot/usr/lib/aarch64-linux-ohos/libz.so
记下这个 OLD_LIBZ 路径。
# 第二步:找你自己 SDK 里的 arm 版 libz.so
find $OHOS_SDK/sysroot -name "libz.so*"
# 期望看到:
# $OHOS_SDK/sysroot/usr/lib/arm-linux-ohos/libz.so
# $OHOS_SDK/sysroot/usr/lib/arm-linux-ohos/libz.so.1
# $OHOS_SDK/sysroot/usr/lib/arm-linux-ohos/libz.so.1.2.13
记下 arm-linux-ohos/libz.so 的完整路径,设为 NEW_LIBZ。
# 第三步:备份并替换
cp OpenCVModules.cmake OpenCVModules.cmake.bak
cp OpenCVModules-release.cmake OpenCVModules-release.cmake.bak
OLD_LIBZ="/root/OpenHarmony/3588_npu/npu_4.0r/ohos-sdk/linux/native/sysroot/usr/lib/aarch64-linux-ohos/libz.so"
NEW_LIBZ="$OHOS_SDK/sysroot/usr/lib/arm-linux-ohos/libz.so"
# 验证新路径文件存在
ls -l $NEW_LIBZ
# 替换
sed -i "s|${OLD_LIBZ}|${NEW_LIBZ}|g" OpenCVModules.cmake
sed -i "s|${OLD_LIBZ}|${NEW_LIBZ}|g" OpenCVModules-release.cmake
# 验证替换成功
grep -n "libz" OpenCVModules.cmake | head
# 应该看到新路径
💡 如果还有其他
.so硬编码路径(libdl、libpthread等),用同样方法批量替换。
5.3 改写 demo 的 CMakeLists.txt
原版 CMakeLists 已经接受 -DRKNN_API_PATH 等参数,只需要小幅强化(加 install 规则、改输出目录名、补充链接库):
cd ~/rknn_workspace/rknn_mobilenet_demo
cp CMakeLists.txt CMakeLists.txt.bak
cat > CMakeLists.txt << 'CMAKE_EOF'
cmake_minimum_required(VERSION 3.4.1)
project(rknn_mobilenet_demo)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# 必须由命令行传入(参考厂商文档风格)
if(NOT RKNN_API_PATH)
message(FATAL_ERROR "Please pass -DRKNN_API_PATH")
endif()
if(NOT RKNN_RT_LIB)
message(FATAL_ERROR "Please pass -DRKNN_RT_LIB")
endif()
if(NOT TARGET_SOC)
set(TARGET_SOC "RK3566_RK3568")
endif()
include_directories(${RKNN_API_PATH})
# OpenCV 由 -DCMAKE_PREFIX_PATH 提供
find_package(OpenCV REQUIRED)
message(STATUS "OpenCV_VERSION = ${OpenCV_VERSION}")
message(STATUS "OpenCV_LIBS = ${OpenCV_LIBS}")
set(CMAKE_INSTALL_RPATH "lib")
add_executable(rknn_mobilenet_demo
src/main.cc
)
target_link_libraries(rknn_mobilenet_demo
${RKNN_RT_LIB}
${OpenCV_LIBS}
dl
m
pthread
)
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_mobilenet_demo_OHOS)
install(TARGETS rknn_mobilenet_demo DESTINATION ./)
install(DIRECTORY model/${TARGET_SOC} DESTINATION ./model)
file(GLOB IMAGE_FILES "model/*.jpg")
install(FILES ${IMAGE_FILES} DESTINATION ./model/)
install(PROGRAMS ${RKNN_RT_LIB} DESTINATION lib)
CMAKE_EOF
5.4 创建交叉编译脚本
这是核心可复用模板,后续任何 demo 都能套用:
cat > build-ohos-arm32.sh << 'BUILD_EOF'
#!/bin/bash
set -e
if [ -z "$OHOS_SDK" ]; then
echo "请先 export OHOS_SDK=/path/to/ohos-sdk/linux/native"
exit 1
fi
# 厂商资源路径(按实际情况改)
LIBNNRT_PATH=$(realpath ../arm64-v8a/libnnrt)
OPENCV_PATH=$(realpath ../arm64-v8a/opencv_3.4.1/armeabi-v7a)
if [ ! -d "$LIBNNRT_PATH" ]; then
echo "未找到 libnnrt: $LIBNNRT_PATH"; exit 1
fi
if [ ! -d "$OPENCV_PATH" ]; then
echo "未找到 OpenCV armeabi-v7a: $OPENCV_PATH"; exit 1
fi
echo "LIBNNRT_PATH = $LIBNNRT_PATH"
echo "OPENCV_PATH = $OPENCV_PATH"
echo "OHOS_SDK = $OHOS_SDK"
ROOT_PWD=$(dirname $(readlink -f "$0"))
BUILD_DIR=${ROOT_PWD}/build/build_ohos_arm32
rm -rf ${BUILD_DIR}
mkdir -p ${BUILD_DIR}
cd ${BUILD_DIR}
# OHOS clang + 32 位 arm target(完全照搬厂商文档的环境变量风格)
export AS=${OHOS_SDK}/llvm/bin/llvm-as
export CC="${OHOS_SDK}/llvm/bin/clang --target=arm-linux-ohos"
export CXX="${OHOS_SDK}/llvm/bin/clang++ --target=arm-linux-ohos"
export LD=${OHOS_SDK}/llvm/bin/ld.lld
export STRIP=${OHOS_SDK}/llvm/bin/llvm-strip
export RANLIB=${OHOS_SDK}/llvm/bin/llvm-ranlib
export OBJDUMP=${OHOS_SDK}/llvm/bin/llvm-objdump
export OBJCOPY=${OHOS_SDK}/llvm/bin/llvm-objcopy
export NM=${OHOS_SDK}/llvm/bin/llvm-nm
export AR=${OHOS_SDK}/llvm/bin/llvm-ar
export CFLAGS="-fPIC -D__MUSL__=1 -march=armv7-a -mfloat-abi=softfp -mfpu=neon"
export CXXFLAGS="-fPIC -D__MUSL__=1 -march=armv7-a -mfloat-abi=softfp -mfpu=neon"
cmake ../.. \
-DRKNN_API_PATH=${LIBNNRT_PATH}/include \
-DRKNN_RT_LIB=${LIBNNRT_PATH}/lib/librknnrt.so.1.4.3b0 \
-DCMAKE_PREFIX_PATH=${OPENCV_PATH} \
-DTARGET_SOC=RK3566_RK3568 \
-DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
make install
echo "================================="
echo "Build done."
file ${ROOT_PWD}/install/rknn_mobilenet_demo_OHOS/rknn_mobilenet_demo
ls -la ${ROOT_PWD}/install/rknn_mobilenet_demo_OHOS/
BUILD_EOF
chmod +x build-ohos-arm32.sh
5.5 编译
./build-ohos-arm32.sh
期望最终输出:
rknn_mobilenet_demo: ELF 32-bit LSB pie executable, ARM, EABI5 ...,
interpreter /lib/ld-musl-arm.so.1, ...
如果不是 32-bit / ARM / ld-musl-arm.so.1,某个环节配错了,回到 5.4 检查 --target=arm-linux-ohos 是否生效。
5.6 检查可执行文件依赖
cd install/rknn_mobilenet_demo_OHOS/
readelf -d rknn_mobilenet_demo | grep NEEDED
典型输出:
Shared library: [librknnrt.so.1.4.3b0] ← 已带,在 lib/
Shared library: [libz.so] ← 需要自带!
Shared library: [libc++_shared.so] ← 需要自带!
Shared library: [libc.so] ← 板子系统自带 /system/lib/libc.so
5.7 补全运行时库
# 仍在 install/rknn_mobilenet_demo_OHOS/ 目录
# 复制 libc++_shared.so
cp $OHOS_SDK/sysroot/usr/lib/arm-linux-ohos/libc++_shared.so lib/ 2>/dev/null || \
cp $OHOS_SDK/llvm/lib/arm-linux-ohos/libc++_shared.so lib/
# 复制 libz.so 及其软链接
cp -P $OHOS_SDK/sysroot/usr/lib/arm-linux-ohos/libz.so* lib/
# 验证所有 .so 都是 32-bit ARM
echo "--- 架构最终检查 ---"
file rknn_mobilenet_demo
file lib/*
ls -la lib/
最终 lib/ 应包含:
librknnrt.so.1.4.3b0 (32-bit ARM)
libc++_shared.so (32-bit ARM)
libz.so → libz.so.1 (软链接)
libz.so.1 (32-bit ARM)
5.8 创建启动脚本
cat > run.sh << 'RUN_EOF'
#!/system/bin/sh
DIR="$(cd "$(dirname "$0")" && pwd)"
export LD_LIBRARY_PATH="$DIR/lib:$LD_LIBRARY_PATH"
MODEL="${1:-$DIR/model/RK3566_RK3568/mobilenet_v1.rknn}"
IMAGE="${2:-$DIR/model/cat_224x224.jpg}"
echo "Model: $MODEL"
echo "Image: $IMAGE"
exec "$DIR/rknn_mobilenet_demo" "$MODEL" "$IMAGE"
RUN_EOF
chmod +x run.sh
5.9 打包并推送到板子
cd ..
tar czf rknn_mobilenet_deploy.tar.gz rknn_mobilenet_demo_OHOS/
ls -lh rknn_mobilenet_deploy.tar.gz
# 推送
hdc file send rknn_mobilenet_deploy.tar.gz /data/local/tmp/
六、运行验证
6.1 板子上解压和配置
hdc shell
cd /data/local/tmp/
tar xzf rknn_mobilenet_deploy.tar.gz
cd rknn_mobilenet_demo_OHOS/
chmod +x rknn_mobilenet_demo run.sh
# 检查 NPU
ls -l /dev/rknpu
# 如果权限不足:chmod 666 /dev/rknpu
6.2 运行
# 默认猫图
./run.sh
# 狗图
./run.sh ./model/RK3566_RK3568/mobilenet_v1.rknn ./model/dog_224x224.jpg
6.3 预期输出
Model: /data/local/tmp/rknn_mobilenet_demo_OHOS/model/RK3566_RK3568/mobilenet_v1.rknn
Image: /data/local/tmp/rknn_mobilenet_demo_OHOS/model/cat_224x224.jpg
model input num: 1, output num: 1
input tensors:
index=0, name=input, n_dims=4, dims=[1, 224, 224, 3],
n_elems=150528, size=150528, fmt=NHWC, type=INT8,
qnt_type=AFFINE, zp=0, scale=0.007812
output tensors:
index=0, name=MobilenetV1/Predictions/Reshape_1,
n_dims=2, dims=[1, 1001, 0, 0],
n_elems=1001, size=1001, fmt=UNDEFINED, type=INT8,
qnt_type=AFFINE, zp=-128, scale=0.003906
rknn_run
--- Top5 ---
283: 0.468750 ← Persian cat (波斯猫)
282: 0.242188 ← tabby cat (虎斑猫)
286: 0.105469 ← Egyptian cat (埃及猫)
464: 0.089844
264: 0.019531
ImageNet 类别 283 (输出 ID 偏移 +1) = Persian cat,识别成功 ✅
💡 MobileNet v1 输出 1001 类(标准 1000 类 + 1 个 background),所以输出 ID 比 ImageNet 标准类别 ID 大 1。
七、常见错误速查表
| 错误信息 | 原因 | 解决 |
|---|---|---|
./xxx: No such file or directory(但文件明明存在) |
interpreter 不存在(架构不对) | 确认是 32 位 ARM,interpreter /lib/ld-musl-arm.so.1 |
Error loading shared library librknnrt.so |
NEEDED 的库找不到 | 1) 建软链接 ln -sf librknnrt.so.X.X.XbX librknnrt.so2) 检查 LD_LIBRARY_PATH |
Error loading shared library libc++_shared.so |
系统没自带 | 从 OHOS SDK 复制到 lib/ |
Error loading shared library libz.so |
系统没自带 | 从 OHOS SDK 复制 libz.so* 到 lib/ |
Error loading shared library libstdc++.so.6 |
错用了 glibc 版库 | 不要用 Rockchip 公开仓库的 librknnrt.so,用厂商套件的 |
大量 symbol not found: _ZNSt7__cxx11... |
C++ ABI 不兼容(glibc/musl 错配) | 同上 |
symbol not found: rknn_matmul_set_quant_params 等 |
librknnrt 版本太老,缺新 API | 改源码注释掉调用,或换新版库 |
failed to open rknpu module / errno: 13 |
NPU 设备权限不足 | chmod 666 /dev/rknpu |
RKNN_ERR_DEVICE_UNAVAILABLE |
库版本与内核 NPU 驱动不匹配 | 对照 cat /proc/rknpu/version 和 strings librknnrt.so | grep version,换匹配的库 |
RKNN_ERR_MODEL_INVALID |
模型由新版 RKNN-Toolkit2 转,老库跑不了 | 用对应版本工具链重新转换模型 |
cv::imread fail |
图片路径错 | 用绝对路径或确认当前工作目录 |
find_package(OpenCV) 失败 |
-DCMAKE_PREFIX_PATH 不对 |
确认指向含 share/OpenCV/OpenCVConfig.cmake 的目录 |
链接时 undefined reference to png_* 等 |
OpenCV 3rdparty 静态库没链接 | 手动加 ${OpenCV_PATH}/share/OpenCV/3rdparty/lib/liblibpng.a 等 |
| 链接时 libz 路径报错 | OpenCVModules.cmake 里硬编码路径错 |
回到 5.2 步骤重新 sed 替换 |
八、复用本模板部署其他模型
恭喜!你已经具备完整工具链。部署任何新模型的步骤大致如下:
8.1 类型 A:替换模型,使用 demo 现有的 main.cc
适用场景:模型同样是分类网络,输入 224×224 RGB,输出 1001 类。
# 1. 备份原模型,放入新模型(命名一致)
cp my_new_model.rknn model/RK3566_RK3568/mobilenet_v1.rknn
# 2. 准备测试图,放到 model/
cp my_test_image.jpg model/test.jpg
# 3. 重新打包推送
cd ~/rknn_workspace/rknn_mobilenet_demo
./build-ohos-arm32.sh # 重新构建,因为 install 会重新拷贝 model/
# ... 后续同 5.7-5.9
# 4. 板子上跑
./run.sh ./model/RK3566_RK3568/mobilenet_v1.rknn ./model/test.jpg
8.2 类型 B:输入分辨率/通道数不同
修改 main.cc 头部常量:
const int MODEL_IN_WIDTH = 640; // 改成新模型宽
const int MODEL_IN_HEIGHT = 640; // 改成新模型高
const int MODEL_IN_CHANNELS = 3;
重新编译,后续流程一样。
8.3 类型 C:完全不同任务(检测、分割等)
需要写新的 main.cc:
- 输入预处理可能要归一化、改通道顺序
- 输出后处理:检测需要 NMS,分割需要 mask 解码
RKNN API 调用流程是通用的,直接抄 mobilenet_demo 的 rknn_init → query → inputs_set → run → outputs_get 框架,只改前/后处理。
build-ohos-arm32.sh 和 CMakeLists.txt 完全可复用,不用改。
8.4 类型 D:新模型用到了老版库不支持的 API
症状:链接通过,但运行时报 symbol not found: rknn_xxx,或编译时直接报错。
解法 1:把不支持的 API 调用注释掉(适用于 fp16 mode 用不上 int8 量化参数等情况)
grep -n "rknn_matmul_set_quant_params" src/main.cc
# 在那一行前加 //
解法 2:找厂商升级 librknnrt.so 到匹配版本
8.5 同样套路部署其他厂商 demo
厂商套件里 rknn_benchmark 和 rknn_common_test 部署方式几乎一样:
# rknn_benchmark(不依赖 OpenCV,更简单)
cp -r arm64-v8a/npu_demo/rknn_benchmark .
cd rknn_benchmark
# 改 CMakeLists,去掉 OpenCV 相关
# 编译脚本去掉 -DCMAKE_PREFIX_PATH=${OPENCV_PATH}
# rknn_common_test(同 mobilenet_demo)
cp -r arm64-v8a/npu_demo/rknn_common_test .
cd rknn_common_test
# 改 CMakeLists 同样风格
# 编译脚本完全照抄 build-ohos-arm32.sh
九、进阶方向
9.1 用 RGA 硬件加速预处理
厂商套件里 librga_1.9.0/armeabi-v7a/lib/librga.so 可以做硬件加速的 resize / 颜色转换。视频流推理场景下,预处理用 RGA 替代 OpenCV 软件实现,能显著降低 CPU 占用、提升 FPS。
9.2 RKNN-Toolkit2 模型转换
用 PyTorch / TensorFlow / ONNX 训练好模型后,在 PC 上用 RKNN-Toolkit2 量化转换为 .rknn 格式。注意:
- 工具链版本要和板子上
librknnrt.so版本对齐。本文用的是 1.4.3b0,就要用 RKNN-Toolkit2 1.4.x 转 - 转换时指定
target_platform='rk3566' - 量化为 int8 可大幅提速,但要准备校准数据集
9.3 性能分析
在 main.cc 里加循环计时,得到 FPS:
struct timeval tv0, tv1;
gettimeofday(&tv0, NULL);
for (int i = 0; i < 100; i++) {
rknn_run(ctx, NULL);
}
gettimeofday(&tv1, NULL);
double ms = (tv1.tv_sec - tv0.tv_sec) * 1000.0 + (tv1.tv_usec - tv0.tv_usec) / 1000.0;
printf("Avg: %.2f ms, FPS: %.1f\n", ms / 100, 100000.0 / ms);
RK3566 NPU 跑 MobileNet v1 大约 5~15 ms / 帧,即 60~200 FPS。
9.4 后续 YOLO 等检测模型
Rockchip 官方仓库 rknn_model_zoo 有 YOLOv5/v8 完整代码,但多数依赖 librknnrt 1.5+ 或 2.x。在你这块板子(1.4.3b0)上需要:
- 改源码兼容老 API
- 或者联系厂商升级 NPU 驱动 + librknnrt
附录 A:目录结构参考
完成本流程后,工作目录的最终结构:
~/rknn_workspace/
├── arm64-v8a/ ← 厂商套件(只读)
│ ├── libnnrt/
│ │ ├── include/ ← rknn_api.h 等
│ │ ├── lib/librknnrt.so.1.4.3b0 ← ★ 32 位库
│ │ └── lib64/librknnrt.so.1.4.3b0
│ ├── opencv_3.4.1/
│ │ ├── arm64-v8a/ ← 不用
│ │ └── armeabi-v7a/ ← ★ 32 位 OpenCV
│ │ ├── include/
│ │ ├── lib/ ← .a 静态库
│ │ └── share/OpenCV/
│ │ ├── OpenCVConfig.cmake
│ │ ├── OpenCVModules.cmake ← ★ 改过路径的
│ │ └── 3rdparty/lib/
│ └── npu_demo/
│ └── rknn_mobilenet_demo/ ← 原始 demo
│
└── rknn_mobilenet_demo/ ← 工作副本
├── CMakeLists.txt ← ★ 已改写
├── build-ohos-arm32.sh ← ★ 编译脚本
├── src/main.cc
├── model/
│ ├── RK3566_RK3568/mobilenet_v1.rknn
│ ├── cat_224x224.jpg
│ └── dog_224x224.jpg
├── build/build_ohos_arm32/ ← cmake 中间产物
└── install/rknn_mobilenet_demo_OHOS/ ← ★ 最终部署目录
├── rknn_mobilenet_demo (32-bit ARM)
├── run.sh
├── lib/
│ ├── librknnrt.so.1.4.3b0
│ ├── libc++_shared.so
│ ├── libz.so → libz.so.1
│ └── libz.so.1
└── model/
├── RK3566_RK3568/mobilenet_v1.rknn
├── cat_224x224.jpg
└── dog_224x224.jpg
板子上部署后:
/data/local/tmp/rknn_mobilenet_demo_OHOS/ ← 推送解压后的目录
└── (同上面 install/ 的结构)
附录 B:整个调试历程回顾
把走过的坑整理出来,理解原理可以避免再次踩:
| 阶段 | 现象 | 误判 | 实际原因 | 解决 |
|---|---|---|---|---|
| 1 | 推送 aarch64 二进制运行报 “No such file or directory”,但文件存在 | 以为权限或路径问题 | interpreter /lib/ld-musl-aarch64.so.1 不存在 |
切到 32 位 target |
| 2 | 板子上 ls /bin/sh 是 32-bit |
以为内核也是 32 位 | 内核 aarch64 但用户态 arm32(RK3566 上 OHOS 5.0 是 arm32 配置) | 确认架构,统一用 arm32 |
| 3 | 用 Rockchip GitHub 公开版 librknnrt.so 全是 _ZNSt7__cxx11... 符号缺失 |
以为版本不对 | glibc/libstdc++ ABI 完全不兼容 OHOS musl/libc++ | 必须用厂商套件的 OHOS musl 版库 |
| 4 | 厂商套件叫 arm64-v8a,以为只有 64 位 |
忽略了子目录 | 套件实际包含 lib/(32) 和 lib64/(64) 双版本,以及 opencv/armeabi-v7a/ |
用 32 位资源 |
| 5 | 编译时 OpenCV 找 libz.so 失败 | 以为缺包 | 厂商 OpenCVModules.cmake 硬编码到他们编译机的绝对路径 |
sed 替换为本机 SDK 路径 |
| 6 | 运行时缺 libc++_shared.so / libz.so |
以为 OHOS 都自带 | OHOS 标准系统 /system/lib/ 没这两个 |
从 OHOS SDK 复制到 deploy/lib |
| 7 | matmul demo 缺 rknn_matmul_set_quant_params 符号 |
以为编译没链接成功 | 老版库 1.4.3b0 不支持新 API | sed 注释掉调用 |
核心经验:OHOS 部署本质就是要解决"三个不一致":
- 架构不一致(aarch64 内核 vs arm32 用户态)
- libc 不一致(普通 Linux glibc vs OHOS musl)
- 运行时库不全(C++ 库、辅助库需要自带)
理解这三点后,所有报错都有迹可循。
许可与致谢
- 本文档涉及的 RKNN 部分基于 Rockchip 公开技术资料和厂商提供的预编译资源
- OpenCV 部分基于 OpenCV 3.4.1 开源版本
- MobileNet v1 模型来自 TensorFlow 官方 slim 仓库,经 RKNN-Toolkit2 转换
- 文档调试过程历经 N 次试错,完整记录于对话日志
作者注:本指南面向 Purple Pi OH(RK3566)+ OpenHarmony 5.0 的特定场景。不同厂商的开发板、不同 OHOS 版本可能略有差异,核心方法论(arm32 target + 厂商 musl 库 + 运行时打包)是通用的。
文档版本:v1.0
最后更新:2026-05
适用范围:Purple Pi OH 开发板,OpenHarmony 5.0 Release
RKNN runtime 版本:1.4.3b0
OHOS SDK 版本:5.0 Release
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)