本文档记录在 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 图像分类(波斯猫识别)

目录


一、背景知识(必读)

在动手之前,确认下面这些事实,理解了它们才能顺利避坑:

1.1 Purple Pi OH 板子的特殊性

项目 实际情况
芯片 RK3566(ARMv8 / 64 位 CPU, 单核 NPU 0.8 TOPS)
系统 OpenHarmony 5.0 Release(标准系统)
内核 aarch64(64 位)
用户态 32 位 ARM(/bin/shinit 都是 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 硬编码路径(libdllibpthread 等),用同样方法批量替换。

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.so
2) 检查 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/versionstrings 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.shCMakeLists.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_benchmarkrknn_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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐