泰山派 RK3566 部署 NCNN 极速推理实践

在立创·泰山派(RK3566, 4 核 Cortex-A55, 2GB 内存)这类嵌入式 Linux 开发板上部署深度学习模型时,CPU 算力与内存带宽往往是最大掣肘。

很多开发者默认使用 ONNX Runtime CPU 模式作为落地兜底方案,但在泰山派上运行标准的 PP-OCRv4 ONNX 模型时,耗时通常长达 1.5 ~ 2 秒,且模型总体量达 90MB,内存占用极高。

而在开源项目 tspi-ocr-web 的研发过程中,我引入了腾讯开源的高性能移动端推理框架 Tencent/ncnn 与百度 PaddlePaddle/PaddleOCR 移动端轻量模型。通过开启 ARM NEON FP16 汇编加速 与 pnnx 动态尺寸图优化,将整图 OCR 识别速度直接提升至 120~180ms(性能提升近 10 倍),同时模型体量骤降至 11MB!

本文提取项目中关于 NCNN 的核心实践,系统整理“架构优势 $\to$ 模型来源与下载 $\to$ 模型转换 (pnnx) $\to$ Python 板端推理 $\to$ 性能对比与避坑”的完整落地方法论。


一、为什么选择 NCNN?

在边缘 ARM 嵌入式设备上,主流推理框架主要有 ONNX Runtime、TFLite 以及硬件专用的 NPU 工具链(如 RKNN)。但在实际工程落地中,NCNN 展现出了极其独特的不可替代性:

评估维度ONNX Runtime (CPU)瑞芯微 RKNN (NPU)腾讯 NCNN (ARM CPU)
依赖与体积依赖较重(动态库庞大)强依赖专有 NPU 内核驱动**极简纯 C++**,零第三方库依赖
模型算子兼容兼容性极强需模型量化,部分动态算子受限算子全面,图优化成熟
模型体积 (PP-OCR)约 90MB约 15MB仅 11MB(FP16 压缩)
RK3566 整图耗时~1500ms(慢)~80ms(极快)~150ms(极快,接近 NPU)
跨硬件通用性通用仅限瑞芯微指定芯片通用(任意 ARMv8/A55/A72/A76)

核心加速秘密:ARMv8.2-A NEON FP16 指令

RK3566 的 CPU 核心基于 ARM Cortex-A55 架构,支持 ARMv8.2-A 指令集中的 FP16 矢量运算。

  • 传统 ONNX Runtime 大多运行在 FP32 单精度下,不仅计算量翻倍,更严重的是在 2GB 开发板上受限于 L2/L3 缓存与 DDR4 带宽瓶颈;
  • NCNN 针对 ARM NEON 手写了深度的汇编优化内核(Kernel),计算时以 16 位半精度浮点(FP16)直接加载并执行 FMA(乘加)向量指令;
  • 双重红利:内存带宽占用直接减半,浮点吞吐率翻倍,彻底释放 4 核 A55 的硬件潜能。

二、模型来源与资源清单(Hugging Face 开源仓库)

项目中所采用的 NCNN 与 ONNX 模型均来自于开源社区并经过轻量化验证,详细开源出处与文件清单如下:

2.1 PP-OCRv5 NCNN 优化版(开箱即用推荐,总计约 11MB)

  • 开源仓库出处:Hugging Face 开源项目 qefro/pp-OCRv5-6-ncnn
  • 国内镜像加速:https://hf-mirror.com/qefro/pp-OCRv5-6-ncnn
  • 包含文件与映射:
    • 文本检测:PP_OCRv5_mobile_det.param / PP_OCRv5_mobile_det.bin(约 3.0 MB)
    • 文本识别:PP_OCRv5_mobile_rec.param / PP_OCRv5_mobile_rec.bin(约 7.9 MB)
    • 方向分类器:PP_LCNet_x0_25_textline_ori.param / PP_LCNet_x0_25_textline_ori.bin(约 0.3 MB)
    • 字典文件:ppocr_keys_v5.txt(映射为 dict.txt)

该套模型是针对移动端与嵌入式平台优化后的 PP-OCRv5 结构,相比 v4 识别精度提升的同时参数量进一步压缩,完美适配 NCNN 的 NEON 汇编计算核心。

2.2 PP-OCRv4 ONNX 原版(对比基准与转换源模型,约 90MB)

2.3 自动化下载脚本

在 tspi-ocr-web 项目中,可通过内置脚本 scripts/download_models.py 一键拉取上述模型(支持自动走 hf-mirror.com 国内加速):

1
2
# 自动下载 PP-OCRv5 NCNN 推荐模型到 models/ocr/ppocrv5-ncnn/
python scripts/download_models.py --ncnn

三、模型转换实战:ONNX $\longrightarrow$ NCNN (基于 pnnx)

NCNN 早期使用的是 onnx2ncnn 工具,而目前官方主力推荐的是经过彻底重构的 pnnx(PyTorch/ONNX Neural Network eXchange)。pnnx 能自动完成算子融合、冗余消除以及动态尺寸定义。

在 tspi-ocr-web 项目中,转换逻辑封装在 utils/ncnn_converter.py 模块中。

3.1 安装转换工具链(建议在 PC 端执行)

虽然 pnnx 可以通过 pip 安装,但官方的 pip install pnnx 往往会强行绑定安装 500MB+ 的 PyTorch。为了保持环境纯净,可从 pnnx GitHub Releases 直接下载独立的 C++ 预编译二进制文件。

1
2
# 也可通过 pip 在独立虚拟环境中安装:
pip install pnnx

3.2 文本检测模型(DBNet)转换

文本检测模型输入通常为动态大图,若未指定输入维度,转换工具往往无法正确推导动态算子形状。需使用 inputshape 与 inputshape2 显式预设多级动态分辨率:

1
2
3
4
5
pnnx det.onnx \
inputshape=[1,3,640,640] \
inputshape2=[1,3,960,960] \
optlevel=2 \
fp16=1
  • optlevel=2:启用全局算子融合与图结构裁剪;
  • fp16=1:权重大幅压缩至原大小的 50%,并在运行时开启半精度推理;
  • 产物:生成 det.ncnn.param(网络拓扑)与 det.ncnn.bin(权重参数)。

3.3 文本识别模型(CRNN / SVTR / LCNet)转换

文本识别模型的特点是:图像高度固定(如 48px),而文本行宽度随字符数量动态可变:

1
2
3
4
5
pnnx rec.onnx \
inputshape=[1,3,48,320] \
inputshape2=[1,3,48,640] \
optlevel=2 \
fp16=1

通过指定不同宽度的 inputshape,pnnx 能够推导出任意步长的动态宽度拓扑,使得模型在板端能够直接接收变长文本切片推理。


四、板端 Python 极速推理实战

在 RK3566 开发板端(建议使用此前搭建的 Python 3.12 虚拟环境),安装编译好的 Python 绑定:

1
pip install ncnn

4.1 初始化与网络加载

基于 tspi-ocr-web 中 PPOCRv5NCNNEngine 的核心实现,网络初始化与线程调优如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
import ncnn
import numpy as np

# 1. 实例化 NCNN 网络
net = ncnn.Net()

# 2. 核心性能参数调优(RK3566 为 4 核 A55,设置为 4 线程最大化吞吐)
net.opt.num_threads = 4
net.opt.use_vulkan_compute = False # 嵌入式无头环境禁用 Vulkan
net.opt.use_fp16_packed = True # 启用紧凑 FP16
net.opt.use_fp16_storage = True # 启用 FP16 权重存储
net.opt.use_fp16_arithmetic = True # 启用硬件 NEON FP16 浮点计算

# 3. 加载拓扑结构与权重
net.load_param("models/ocr/ppocrv5-ncnn/rec.param")
net.load_model("models/ocr/ppocrv5-ncnn/rec.bin")

4.2 动态 Blob 名称自适应

经过 pnnx 优化后的网络,输入输出 Blob 节点名有时会由 input/output 重命名为 in0/out0。为了增强兼容性,可在加载后动态自省匹配:

1
2
3
blobs = {b.name for b in net.blobs()}
in_name = "in0" if "in0" in blobs else "input"
out_name = "out0" if "out0" in blobs else "output"

4.3 执行前向推理(Extractor)

NCNN 的设计思想是无状态的网络拓扑(Net)搭配有状态的提取器(Extractor),Extractor 负责单次前向的数据绑定与计算执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def run_inference(net: ncnn.Net, img_blob: np.ndarray, in_name: str, out_name: str) -> np.ndarray:
"""
img_blob: 经过预处理归一化后的浮点数组,形状 [C, H, W]
"""
# 1. 确保内存连续 (Contiguous)
data = np.ascontiguousarray(img_blob, dtype=np.float32)

# 2. 转换为 ncnn.Mat
in_mat = ncnn.Mat(data)

# 3. 创建提取器并绑定输入
ex = net.create_extractor()
ex.input(in_name, in_mat)

# 4. 执行前向推理并提取输出
ret, out_mat = ex.extract(out_name)
if ret != 0:
raise RuntimeError(f"NCNN 推理提取失败, ret={ret}")

# 5. 转回 NumPy 供后处理解码 (添加 Batch 维度)
output_np = np.array(out_mat)[np.newaxis, ...]
return output_np

4.4 资源释放

在长周期驻留的服务中,当网络不再使用或需要重载时,显式释放底层 C++ 显存与权重:

1
net.clear()

五、RK3566 真实性能对比基准

在立创·泰山派(RK3566 4核 1.8GHz, 2GB LPDDR4, Ubuntu 26.04)上,对一张标准发票图片(包含 18 行文字)进行端到端识别测试:

推理引擎计算单元模型体量 (Det + Rec)进程常驻内存整图端到端延迟
ONNX Runtime (PP-OCRv4)CPU 4-Thread (FP32)90.4 MB~380 MB1520 ms
NCNN (PP-OCRv5)CPU 4-Thread (NEON FP16)11.2 MB~85 MB158 ms ⭐
RKNN (PP-OCRv4)NPU (0.8 TOPS INT8)14.8 MB~110 MB78 ms

从测试数据可以看出:

  1. 对比 ONNX Runtime:NCNN 速度直接提升近 10 倍,内存节省超过 **75%**,使得在 2GB 内存板卡上跑复杂视觉服务完全不会发生 OOM;
  2. 对比 RKNN NPU:NCNN 仅通过 CPU 纯算力就跑出了 150ms 的优异成绩,且完全避开了 RKNN 的 NPU 量化精度损失以及专有内核驱动门槛,非常适合快速落地交付。

六、关键避坑指南

  1. NumPy 数组连续性问题(C-Contiguous):
    在向 ncnn.Mat 喂入切片数据(如从检测框裁剪文本小图)时,NumPy 切片通常是非连续内存。**必须调用 np.ascontiguousarray(...)**,否则可能导致底层 C++ 内存指针越界直接 Core Dump。
  2. 多线程参数设置(num_threads):
    嵌入式多核 CPU 并非线程越多越好。在泰山派 4 核 A55 上,设置为 num_threads = 4 是吞吐与延迟的最佳平衡点;若设置过高,频繁的线程上下文切换反而会导致性能劣化。
  3. 输入输出尺寸必须符合网络步长:
    检测网络通常包含多级下采样(如 32 倍),输入的宽和高必须预处理为 32 的整数倍,否则在经过 pnnx 算子时会触发维度不匹配错误。

结语与开源资源导航

NCNN 凭借纯粹的底层工程优化与对 ARM 架构的极致压榨,为边缘嵌入式设备提供了一条在 CPU 上实现准实时 AI 推理的高效通路。

本文涉及的所有项目源码、模型与依赖均已开源,欢迎查阅与体验:

1. 项目与代码仓库

2. 开源框架与模型出处


泰山派 RK3566 部署 NCNN 极速推理实践
https://bubao.github.io/posts/80a659d.html
作者
一念
发布于
2026年11月1日
许可协议