基于 TVM 与 Arm Virtual Hardware 在 Cortex(R)-M55 裸机环境运行 PaddleOCR 文本识别模型实战指南
【免费下载链接】PaddleOCR飞桨多语言OCR工具包(实用超轻量OCR系统,支持80+种语言识别,提供数据标注与合成工具,支持服务器、移动端、嵌入式及IoT设备端的训练与部署) Awesome multilingual OCR toolkits based on PaddlePaddle (practical ultra lightweight OCR system, support 80+ languages recognition, provide data annotation and synthesis tools, support training and deployment among server, mobile, embedded and IoT devices)项目地址: https://gitcode.com/paddlepaddle/PaddleOCR
导读:本文以 PaddleOCR 仓库 deploy/avh 目录下的官方示例为主线,完整讲解如何借助 Apache TVM 将 PaddleOCR 的 PP-OCRv3 英文文本识别模型(裁剪后约 2.7M)编译为面向 Arm(R) Cortex(R)-M55 CPU 与 CMSIS-NN 后端的裸机可执行程序,并分别在 Arm Virtual Hardware(AVH)云端 AMI 实例与本地 FVP 仿真器上运行、输出识别文字与置信度。读完本文,你将掌握完整的嵌入式侧 OCR 模型部署链路:环境准备 → TVM 编译 → 图像数据 C 数组化 → 裸机构建 → 仿真运行,并可直接替换为自己的图片与模型进行验证。
一、方案背景:为什么要在 AVH 上跑 PaddleOCR
Arm Virtual Hardware(AVH)是 Arm 提供的虚拟硬件仿真平台,可以在没有真实物理开发板的情况下,基于 Arm(R) Corstone(TM)-300 软件(Fixed Virtual Platform,FVP)模拟 Cortex(R)-M55 CPU 等嵌入式内核的执行行为。它的价值在于:
- 提前验证嵌入式部署方案:在硬件到位之前即可验证模型编译、算子兼容性与推理流程;
- 可复现的 CI 环境:与 TVM 生态中的 ci_cpu Docker 容器配合,适合做自动化回归验证;
- 降低嵌入式开发门槛:不需要购买开发板、不需要手动搭建交叉编译环境,脚本可一键完成。
本示例的技术栈是「PaddleOCR(模型侧)+ TVM(编译侧)+ CMSIS-NN(算子库)+ AVH/FVP(运行侧)」:先用 TVM 的tvmc把 PaddlePaddle 推理模型编译成面向 Cortex-M55 的 C 代码(AOT 执行器、CRT 运行时),再通过arm-none-eabi-gcc交叉编译出裸机可执行文件,最后在 AVH 平台上运行并打印识别结果。
二、运行环境与前置依赖
原文档给出了三种运行场景,依赖准备方式各不相同,这里逐一说明。
场景一:Arm Virtual Hardware AMI 云实例(推荐,零手工依赖)
如果运行在 AWS / AWS China 市场提供的Arm Virtual Hardware Amazon Machine Image(AMI)实例中,所需的软件会在执行run_demo.sh时通过 configure_avh.sh 脚本自动安装,无需手工干预。
从 configure_avh.sh 的源码可以看到它依次完成四件事:
- 安装 CMSIS:下载指定 SHA 提交(
977abe9849781a2e788b02282986480ff4e25ea6)的 CMSIS_5,解压到/opt/arm/ethosu/cmsis,并用sha512sum -c校验完整性; - 安装 Arm(R) Ethos(TM)-U NPU driver stack:
git cloneethos-u-core-platform 到/opt/arm/ethosu/core_platform并 checkout 到21.11tag; - 安装 Arm(R) GNU Toolchain:下载
gcc-arm-none-eabi-10-2020-q4-major并解压到/opt/arm/gcc-arm-none-eabi; - 安装 TVM:通过
pip install tlcpack-nightly从 TLCPack 安装预编译 TVM。
场景二:TVM 提供的 ci_cpu Docker 容器
TVM 官方仓库的Dockerfile.ci_cpu容器中已预装上述全部软件,直接docker run进入容器后即可使用,无需再装任何依赖。
场景三:本地裸机环境(完全手动)
如果既不用 AMI 也不用 Docker 容器,需要手动准备:
- 构建与运行所需软件(可通过 TVM 仓库的
ubuntu_install_ethosu_driver_stack.sh一键安装):- 基于 Arm(R) Corstone(TM)-300 的 Fixed Virtual Platform(FVP)仿真器;
- cmake 3.19.5;
- Arm(R) GCC 工具链(
gcc-arm-none-eabi-10-2020-q4-major); - Arm(R) Ethos(TM)-U NPU driver stack;
- CMSIS(CMSIS_5)。
- Python 依赖:本目录下的 requirements.txt 定义了四个包,在
deploy/avh目录下执行:
pip install -r ./requirements.txt- TVM:二选一:
- 从 TLCPack 安装预编译包;
- 从源码构建 TVM。若从源码构建,必须在
config.cmake中开启以下三个开关,否则后续编译无法完成:set(USE_CMSISNN ON)set(USE_MICRO ON)set(USE_LLVM ON)
环境变量配置
在场景二、场景三中,需要把 cmake 3.19.5 与 FVP 的路径加入PATH。假设安装在/opt/arm下:
export PATH=/opt/arm/FVP_Corstone_SSE-300/models/Linux64_GCC-6.4:/opt/arm/cmake/bin:$PATH提示:路径中的
FVP_Corstone_SSE-300即 Corstone-300 对应的 FVP 模型目录,Linux64 下实际可执行程序位于其models/Linux64_GCC-6.4子目录。
三、一键运行 Demo:run_demo.sh
在deploy/avh目录下执行:
./run_demo.sh如果无法使用 AWS/AWS China 提供的 AVH AMI 实例,可以追加--enable_FVP 1参数,改用本地 FVP 可执行程序运行:
./run_demo.sh --enable_FVP 1如果 Ethos(TM)-U 平台和/或 CMSIS 没有安装在默认位置/opt/arm/ethosu,可通过参数显式指定:
./run_demo.sh --cmsis_path /home/tvm-user/cmsis \ --ethosu_platform_path /home/tvm-user/ethosu/core_platform从 run_demo.sh 源码的show_usage可以看到它支持的全部参数:
| 参数 | 说明 | 默认行为 |
|---|---|---|
-h, --help | 打印帮助信息 | — |
--cmsis_path CMSIS_PATH | 设置 CMSIS 路径 | /opt/arm/ethosu/cmsis |
--ethosu_platform_path ETHOSU_PLATFORM_PATH | 设置 Ethos(TM)-U core platform 路径 | /opt/arm/ethosu/core_platform |
--fvp_path FVP_PATH | 设置 FVP 路径(脚本会自动拼接models/Linux64_GCC-6.4) | 环境变量PATH |
--cmake_path | 设置 cmake 路径(导出为CMAKE供 Makefile 使用) | cmake |
--enable_FVP | 置 1 时改用本地 FVP 执行(合法取值为 1 或 0) | 0(默认走 AVH) |
脚本本身还做了几件额外的事情:
- 脚本开头执行
sudo pip install -r ./requirements.txt安装 Python 依赖; - 默认将
gcc-arm-none-eabi的 bin 目录加入PATH(export PATH=/opt/arm/gcc-arm-none-eabi/bin:$PATH); - 非 FVP 模式下,若
/opt/arm/不存在,则自动调用sudo ./configure_avh.sh完成环境初始化; - 根据
--enable_FVP选择运行平台名:VHT_Corstone_SSE-300_Ethos-U55(AVH 云端)或FVP_Corstone_SSE-300_Ethos-U55(本地)。
四、run_demo.sh执行的完整链路
原文档列出了脚本的七个步骤,结合 run_demo.sh 源码可以还原每一步的具体命令:
- 设置运行环境:AMI 场景下通过
configure_avh.sh自动安装前置软件(见上文); - 下载 PaddleOCR 文本识别模型:
wget https://paddleocr.bj.bcebos.com/tvm/ocr_en.tar并解压,得到 Paddle 推理模型ocr_en/inference.pdmodel; - 使用 tvmc 将模型编译为 Cortex(R)-M55 CPU + CMSIS-NN 后端:完整编译命令如下:
python3 -m tvm.driver.tvmc compile --target=cmsis-nn,c \ --target-cmsis-nn-mcpu=cortex-m55 \ --target-c-mcpu=cortex-m55 \ --runtime=crt \ --executor=aot \ --executor-aot-interface-api=c \ --executor-aot-unpacked-api=1 \ --pass-config tir.usmp.enable=1 \ --pass-config tir.usmp.algorithm=hill_climb \ --pass-config tir.disable_storage_rewrite=1 \ --pass-config tir.disable_vectorize=1 ocr_en/inference.pdmodel \ --output-format=mlf \ --model-format=paddle \ --module-name=rec \ --input-shapes x:[1,3,32,320] \ --output=rec.tar其中关键选项含义如下:
--target=cmsis-nn,c:生成两级 target,算子优先落到 CMSIS-NN,其余落为纯 C 代码;--runtime=crt:使用 TVM 的 C Runtime(C Runtime / CRT),配合裸机环境;--executor=aot与--executor-aot-interface-api=c、--executor-aot-unpacked-api=1:生成 AOT(Ahead-Of-Time)执行器,C 接口、非打包参数传递,便于嵌入式端直接调用;--pass-config tir.usmp.enable=1与tir.usmp.algorithm=hill_climb:启用 Unified Static Memory Planner,用 hill climb 算法做静态内存规划,减少片上内存占用;--input-shapes x:[1,3,32,320]:指定输入张量x的形状为 NCHW(batch=1, channel=3, height=32, width=320),与 PP-OCRv3 识别模型的预处理尺寸一致;--model-format=paddle:声明输入为 PaddlePaddle 推理模型;--module-name=rec:生成的入口函数命名为tvmgen_rec_run(下文 C 代码中会用到)。
编译产物rec.tar解压后是 TVM 的 Model Library Format(MLF)包,内含codegen/host/src的生成 C 代码与 CRT 运行时源码,供 Makefile 构建使用。
- 生成输入 C 头文件:调用
python3 ./convert_image.py imgs_words_en/word_116.png,把示例图片转换成inputs.h中的 C 数组; - 生成输出 C 头文件:
convert_image.py同时生成outputs.h,预分配长度为 7760 的 float 输出数组; - 构建 Demo 可执行程序:执行
make(详细构建过程见第六节); - 在 AVH 上运行:按平台名执行仿真器,并传入一系列
-C配置参数:
$Platform -C cpu0.CFGDTCMSZ=15 \ -C cpu0.CFGITCMSZ=15 -C mps3_board.uart0.out_file=\"-\" -C mps3_board.uart0.shutdown_tag=\"EXITTHESIM\" \ -C mps3_board.visualisation.disable-visualisation=1 -C mps3_board.telnetterminal0.start_telnet=0 \ -C mps3_board.telnetterminal1.start_telnet=0 -C mps3_board.telnetterminal2.start_telnet=0 -C mps3_board.telnetterminal5.start_telnet=0 \ ./build/demo --stat其中cpu0.CFGDTCMSZ=15/cpu0.CFGITCMSZ=15设置 CPU 的 Data/Instruction TCM(紧耦合内存)大小为 2^15 字节(32KB),UART 输出重定向到标准输出,并约定收到EXITTHESIM字样即结束仿真。程序运行后会在终端输出图片上的文字与对应的置信度(score)。
五、使用自己的图片与模型
更换输入图片
convert_image.py 接收一个命令行参数——图片路径,将其转换为模型可直接消费的字节数组:
python3 ./convert_image.py path/to/image修改run_demo.sh中调用convert_image.py的那一行即可替换为任意图片。参考源码,图像预处理与 C 数组化的完整逻辑为:
- 读取图片后调用
resize_norm_img(img, [3, 32, 320]),将其等比缩放到高度 32,若等比宽度超过 320 则截断为 320,否则在右侧补零(padding); - 像素值除以 255 归一化到
[0,1],再执行(x - 0.5) / 0.5标准化到[-1,1]; - 转为 CHW 布局并
np.expand_dims增加 batch 维,得到[1,3,32,320]的输入; - 通过
create_header_file写入deploy/avh/include/inputs.h,数据以__attribute__((section(".data.tvm"), aligned(16)))的 float 数组形式存放; - 同时生成
outputs.h:output_data = np.zeros([7760], np.float32),即输出缓冲区长度为 7760 = 字符序列长度(80)× 词典大小(97)。
仓库自带的示例输入图(位于 deploy/avh/imgs_words_en 目录)为清晰无干扰的英文单词图,例如默认使用的word_116.png,文字内容为 "QBHOUSE",适合作为 OCR 输入验证:
更换模型
在run_demo.sh中替换下载/解压 Paddle 推理模型的那一处(约 130 行附近,即wget与tar步骤)即可换用其他 PaddleOCR 模型。需要注意:模型必须与 Cortex-M55 的算子能力兼容——例如 RNN 类算子当前无法在 Cortex-M55 上运行(详见第七节),更换模型时需保证其算子集合在 TVMcmsis-nn,ctarget 下可编译通过,且输入形状要与--input-shapes参数一致。
六、裸机程序构建与运行原理(源码级剖析)
Makefile 构建骨架
Makefile 把上一节 tvmc 生成的 MLF 产物、CMSIS 启动代码与示例主程序链接成build/demo:
- 交叉编译器固定为
arm-none-eabi-gcc,编译选项包含-mcpu=cortex-m55 -mthumb -mfloat-abi=hard,即针对 Cortex-M55 的硬浮点 Thumb 代码; - 头文件搜索路径覆盖:TVM CRT 运行时(
build/runtime)、CMSIS Device/Core/NN/DSP 头、Corstone-300 平台头、deploy/avh/include(即inputs.h/outputs.h)以及 tvmc 生成的codegen/host/include; - CMSIS-NN 通过 CMake 交叉编译(使用 arm-none-eabi-gcc.cmake 工具链文件,
TARGET_CPU=cortex-m55,开启BUILD_CMSIS_NN_FUNCTIONS=YES)得到libcmsis-nn.a; - 链接参数包含
-T corstone300.ld(链接脚本)与-specs=nosys.specs,面向无操作系统的裸机目标; - 链接的库还包括 CRT 的
stack_allocator.o、crt_backend_api.o、TVM 生成的libcodegen.a以及 CMSIS 启动代码libcmsis_startup.a。
主程序:推理与后处理
裸机主程序 demo_bare_metal.c 完整演示了嵌入式端调用 AOT 执行器的标准写法:
#include <tvm_runtime.h> #include <tvmgen_rec.h> // tvmc 以 --module-name=rec 生成 #include "inputs.h" // convert_image.py 生成 #include "outputs.h" struct tvmgen_rec_inputs rec_inputs = { .x = input }; struct tvmgen_rec_outputs rec_outputs = { .output = output }; tvmgen_rec_run(&rec_inputs, &rec_outputs);推理完成后,程序在 MCU 内部直接完成 CTC 式解码:
- 词典为 97 个字符(
dict[]),输出数组长度 7760 = 时间步数 × 97,每个时间步对 97 类取 argmax; - 跳过
argmax_idx == 0(空字符/blank),并去除相邻重复字符(argmax_idx == last_index时不输出),这正是 CTC 解码「去重 + 去 blank」的核心逻辑; - 将保留字符的置信度求平均作为整句 score,最终通过 UART 打印
text: <识别文字>, score: <置信度>; - 最后打印
EXITTHESIM触发 FVP 仿真退出,然后进入while(1)死循环挂起。
从这段代码可以直观理解:整个推理与解码流程在裸机端完成,无需操作系统,这正是 TVM AOT + CRT 在嵌入式端部署的典型形态。
七、模型说明:面向 Cortex-M55 裁剪的 PP-OCRv3 英文识别模型
本示例默认使用基于PP-OCRv3英文识别模型裁剪得到的约2.7M模型(下载自paddleocr.bj.bcebos.com/tvm/ocr_en.tar)。之所以需要裁剪,是因为Arm(R) Cortex(R)-M55 CPU 不支持 RNN 算子,而 PP-OCRv3 原始识别模型中含有 RNN(如 BiLSTM)结构;工程上删除不支持的算子后,得到当前这个约 2.7M 的英文识别模型。
PP-OCRv3 是 PaddleOCR 发布的 PP-OCR 系列第三代模型,仓库文档 docs/version2.x/ppocr/blog/PP-OCRv3_introduction.md 详细介绍了其特性,这里摘要三点与本示例强相关的:
- 超轻量级 OCR 系统:检测(3.6M)+ 方向分类器(1.4M)+ 识别(12M)= 17.0M,整体模型体积小,适合边缘/嵌入式部署;
- 多语言支持:支持 80+ 种多语言识别模型,包括英文、中文、法文、德文、阿拉伯文、韩文、日文等;本示例即面向英文识别场景;
- 能力覆盖广:支持竖排文本识别与长文本识别。
可以推断:只要满足算子兼容与输入尺寸约束,PP-OCRv3 系列其他语言/能力的识别模型同样具备移植到本示例链路的潜力。
八、注意事项与常见问题
- 算子兼容性是硬约束:Cortex-M55 上无法直接运行含 RNN 的模型,选型或裁剪模型时必须确认算子集合在
cmsis-nn,ctarget 下可编译(可用tvmc compile提前验证); - 输入形状必须对齐:
--input-shapes x:[1,3,32,320]与convert_image.py中的resize_norm_img(img, [3, 32, 320])必须一致,更换模型时两者需同步修改; - AVH 与 FVP 的差异:默认走 AVH 云端 AMI;本地环境需
--enable_FVP 1并保证 FVP 已安装、路径已加入PATH; - 默认安装路径:CMSIS 与 Ethos-U core platform 默认安装在
/opt/arm/ethosu/下,非此路径时必须用--cmsis_path/--ethosu_platform_path显式指定; - TVM 构建开关:从源码构建 TVM 时,务必开启
USE_CMSISNN、USE_MICRO、USE_LLVM三个选项,否则 tvmc 无法完成cmsis-nn,ctarget 的编译; - 验证步骤建议:先跑通默认的
word_116.png,确认输出与置信度正常后,再替换为自己的图片与模型,逐项排查。
九、总结
本文从 deploy/avh/README.md 出发,结合 run_demo.sh、convert_image.py、demo_bare_metal.c、Makefile 与 configure_avh.sh 等源码,完整还原了「PaddleOCR 模型 → TVM 编译 → 裸机程序 → AVH/FVP 仿真运行」的嵌入式 OCR 部署链路。这套方案的核心价值在于:开发者可以在没有实体开发板的条件下,以最小成本验证 PaddleOCR 模型在 Cortex-M 系列嵌入式平台上的可用性与推理效果,为后续的真实硬件移植(如基于 Ethos-U NPU 的加速部署)打下坚实基础。
【免费下载链接】PaddleOCR飞桨多语言OCR工具包(实用超轻量OCR系统,支持80+种语言识别,提供数据标注与合成工具,支持服务器、移动端、嵌入式及IoT设备端的训练与部署) Awesome multilingual OCR toolkits based on PaddlePaddle (practical ultra lightweight OCR system, support 80+ languages recognition, provide data annotation and synthesis tools, support training and deployment among server, mobile, embedded and IoT devices)项目地址: https://gitcode.com/paddlepaddle/PaddleOCR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考