← 返回文章

PaddleOCR 3.7 在 Jetson AGX Orin 上编译 C++ 推理部署

记录在 Jetson AGX Orin 上编译 PaddleOCR 3.7 C++ 推理运行库,适配 CUDA/Jetson 环境,并完成 PP-OCRv6 GPU 部署的过程。

#PaddleOCR#Jetson#CUDA#C++#OCR

PaddleOCR 3.7 在 Jetson AGX Orin 上编译 C++ 推理部署

本文记录使用 PaddleOCR 3.7 源码,在 Jetson AGX Orin 上编译 Paddle Inference C++ 运行库和 PaddleOCR deploy/cpp_infer,并完成 PP-OCRv6 GPU 推理的过程。

本文省略源码、依赖和模型的下载过程,只记录源码准备完成后的编译、适配、排错和部署。

1. 最终结论

PaddleOCR 3.7 的 C++ 推理程序可以在 Jetson AGX Orin 上编译并使用 CUDA GPU 推理。

最终验证结果:

  • ppocr 生成为 ARM64/aarch64 ELF 可执行文件;
  • Paddle Inference 动态库成功链接;
  • ldd 检查无缺失动态库;
  • PP-OCRv6 medium 检测模型和识别模型均可加载;
  • 在 Orin GPU 上完成检测+识别,返回码为 0;
  • 目标机运行阶段不需要 Python 版 PaddleOCR。

需要注意的是,PP-OCRv6 的 safetensors 模型属于 Transformers 引擎格式,不能直接交给 C++ cpp_infer。C++ 部署应使用对应的 Paddle 静态推理模型,即包含 inference.jsoninference.pdiparams 的模型目录。

2. 源码和环境

2.1 源码版本

使用用户提供的 PaddleOCR 3.7 源码:

/Users/guwei/Downloads/PaddleOCR-main.zip

源码 SHA-256:

309a8283049df77a38cb631ecfc75647788289c7ef6eb219fb7ec76cbceac193

PaddleOCR C++ demo 位于:

PaddleOCR-main/deploy/cpp_infer

PaddleOCR 源码本身不包含完整的 Paddle Inference C++ 运行库,因此还需要准备 Paddle 源码,用来生成 PADDLE_LIB。本次使用 Paddle develop 源码,提交版本为:

4887335c9679

整个过程没有使用 Paddle 3.2.0 或 PaddleOCR 3.2.0。

2.2 Orin 环境

本次实际编译环境如下:

项目 版本
设备 Jetson AGX Orin Developer Kit 64GB
CPU 架构 aarch64
系统 Ubuntu 22.04.5
JetPack 6.2.1
L4T 36.4.7
GCC/G++ 11.4.0
CMake 3.22.1
CUDA 12.6.68
cuDNN 9.3
TensorRT 10.3
OpenCV 4.8.0

Orin 的 GPU 架构为 Ampere,CUDA 计算能力为 8.7。因此 Paddle runtime 编译时使用 CUDA_ARCH_NAME=Ampere,编译输出中可以看到 compute_87/sm_87

3. 编译目录规划

在 Orin 上设置路径变量,避免源码、构建目录和最终部署目录混在一起:

export WORK_ROOT=/home/wooley/paddle_build
export PADDLE_SRC=$WORK_ROOT/Paddle-develop-git
export PADDLE_BUILD=$PADDLE_SRC/build-orin-cxx
export OCR_SRC=$WORK_ROOT/PaddleOCR-main
export OCR_BUILD=$OCR_SRC/deploy/cpp_infer/build-orin-gpu
export PADDLE_LIB=$PADDLE_BUILD/paddle_inference_install_dir

目录关系:

Paddle-develop-git/       Paddle Inference runtime 源码
PaddleOCR-main/           PaddleOCR 3.7 源码
build-orin-cxx/           Paddle runtime 构建目录
build-orin-gpu/           C++ demo 构建目录
paddleV6/                 最终运行部署目录

4. 编译 Paddle Inference C++ runtime

4.1 配置原则

Orin 上首次编译建议先使用 CUDA GPU、关闭 TensorRT,先把 Paddle 原生 GPU 推理链路跑通。TensorRT 可以在基础版本验证完成后再单独开启。

主要配置:

  • WITH_GPU=ON:启用 CUDA GPU;
  • WITH_ARM=ON:启用 ARM/aarch64;
  • WITH_NV_JETSON=ON:启用 Jetson 适配;
  • WITH_TENSORRT=OFF:减少 TensorRT 版本耦合;
  • WITH_PYTHON=OFF:只构建 C++ runtime;
  • ON_INFER=ON:生成推理库;
  • WITH_MKL=OFF:Orin 不使用 Intel MKL;
  • WITH_TESTING=OFFWITH_INFERENCE_API_TEST=OFF:减少编译目标;
  • WITH_NCCL=OFFWITH_DISTRIBUTE=OFF:单机单卡不需要分布式组件;
  • WITH_FLASHATTN=OFFWITH_CUTLASS=OFF:先降低 CUDA 适配复杂度。

4.2 CMake 配置

cmake -S "$PADDLE_SRC" \
  -B "$PADDLE_BUILD" \
  -G "Unix Makefiles" \
  -DCMAKE_BUILD_TYPE=Release \
  -DWITH_GPU=ON \
  -DWITH_ARM=ON \
  -DWITH_NV_JETSON=ON \
  -DWITH_TENSORRT=OFF \
  -DWITH_FLASHATTN=OFF \
  -DWITH_CUTLASS=OFF \
  -DWITH_PYTHON=OFF \
  -DON_INFER=ON \
  -DWITH_SETUP_INSTALL=ON \
  -DWITH_TESTING=OFF \
  -DWITH_INFERENCE_API_TEST=OFF \
  -DWITH_MKL=OFF \
  -DWITH_AVX=OFF \
  -DWITH_XBYAK=OFF \
  -DWITH_SHARED_PHI=OFF \
  -DWITH_SYSTEM_BLAS=OFF \
  -DWITH_ONEDNN=OFF \
  -DWITH_OPENVINO=OFF \
  -DWITH_ONNXRUNTIME=OFF \
  -DWITH_DISTRIBUTE=OFF \
  -DWITH_NCCL=OFF \
  -DWITH_RCCL=OFF \
  -DWITH_CINN=OFF \
  -DWITH_CRYPTO=OFF \
  -DWITH_POCKETFFT=OFF \
  -DWITH_FLAGCX=OFF \
  -DWITH_CUSPARSELT=OFF \
  -DWITH_PROFILER=OFF \
  -DCUDA_ARCH_NAME=Ampere

WITH_ARM=ONWITH_NV_JETSON=ON 建议同时指定。只指定 Jetson 选项时,部分 CMake 逻辑会较晚才设置 ARM 选项,容易造成配置和编译阶段不一致。

4.3 AArch64 大型动态库链接问题

Paddle runtime GPU 组件较多,生成大型 libpaddle_inference.so 时,默认 GNU ld.bfd 可能出现:

R_AARCH64_CALL26 relocation truncated

这是 AArch64 分支跳转距离超出默认范围导致的链接错误,不是 CUDA kernel 编译错误。

ld.bfd 增加 -Wl,--long-plt 会出现:

ld.bfd: unrecognized option '--long-plt'

Orin 系统中存在支持该选项的 GNU gold,因此重新配置 Paddle runtime,显式指定 gold:

cmake -S "$PADDLE_SRC" \
  -B "$PADDLE_BUILD" \
  -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=gold -Wl,--long-plt" \
  -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=gold -Wl,--long-plt"

然后重新构建:

cmake --build "$PADDLE_BUILD" -j 8 --target paddle_inference_shared
cmake --build "$PADDLE_BUILD" -j 8 --target inference_lib_dist

最终使用 inference_lib_dist 生成的目录作为 PADDLE_LIB

$PADDLE_BUILD/paddle_inference_install_dir

该目录应包含:

paddle/include/
paddle/lib/libpaddle_inference.so
paddle/lib/libpaddle_inference.a
paddle/lib/libcommon.so
third_party/install/

5. Paddle runtime 的 CUDA/Jetson 兼容修复

5.1 ROIAlign 线程变量类型不匹配

编译 Jetson CUDA 路径时,paddle/phi/kernels/gpu/roi_align_kernel.cu 出现:

argument of type "uint32_t *" is incompatible with parameter of type "int *"

将 Jetson 分支中的:

uint32_t threads = kNumCUDAThreads;

改为:

int threads = kNumCUDAThreads;

原因是 Jetson 分支调用的 ChangeThreadNum 接口接收 int*,原代码使用了 uint32_t*。该修改只解决类型匹配问题,不改变 kernel 算法。

5.2 CUDA 12.6 下 Thrust 内部宏不存在

编译 paddle/phi/kernels/funcs/shuffle_batch.cu.h 时出现 __thrust_exec_check_disable__ 未声明或找不到的错误。

原代码:

__thrust_exec_check_disable__ __host__ __device__

改为:

__host__ __device__

CUDA 12.6 当前 Thrust 版本中,该内部宏已经不可用。删除它不会改变函数的 host/device 属性。

5.3 非阻塞编译警告

src/utils/ilogger.cc 可能出现 sprintf 格式溢出相关 warning。该 warning 不影响生成 ppocr,本次没有作为阻塞错误处理。若生产环境要求告警清零,可以再单独修改日志格式化代码。

6. 编译 PaddleOCR C++ demo

6.1 Jetson OpenCV CMake 路径差异

PaddleOCR C++ demo 的 CMake 配置会按照以下形式查找 OpenCV:

${OPENCV_DIR}/lib64/cmake/opencv4

但 Jetson 系统中的 OpenCV CMake 配置实际位于:

/usr/lib/cmake/opencv4

建立一个只用于 CMake 查找的前缀目录:

export OPENCV_PREFIX=$WORK_ROOT/opencv-prefix
mkdir -p "$OPENCV_PREFIX/lib64/cmake"
ln -s /usr/lib/cmake/opencv4 "$OPENCV_PREFIX/lib64/cmake/opencv4"
ln -s /usr/lib "$OPENCV_PREFIX/lib"

这不会修改系统 OpenCV,只是让 demo 的固定目录拼接逻辑找到 Jetson 的 OpenCV 配置和库文件。

6.2 CMake 配置

cmake -S "$OCR_SRC/deploy/cpp_infer" \
  -B "$OCR_BUILD" \
  -G "Unix Makefiles" \
  -DCMAKE_BUILD_TYPE=Release \
  -DPADDLE_LIB="$PADDLE_LIB" \
  -DWITH_GPU=ON \
  -DWITH_MKL=OFF \
  -DWITH_STATIC_LIB=OFF \
  -DUSE_FREETYPE=OFF \
  -DOPENCV_DIR="$OPENCV_PREFIX" \
  -DCUDA_LIB=/usr/local/cuda/lib64 \
  -DCUDNN_LIB=/usr/lib/aarch64-linux-gnu \
  -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=gold -Wl,--long-plt"

这里使用 WITH_STATIC_LIB=OFF,让 ppocr 动态链接 libpaddle_inference.so,便于最终部署时单独管理 runtime。

USE_FREETYPE=OFF 是为了减少字体库和 OpenCV freetype 模块的额外依赖。关闭后,核心 OCR 推理不受影响;部分单模块可视化可能提示 OpenCV 没有 freetype 支持。

6.3 编译

cmake --build "$OCR_BUILD" -j 8

成功后可执行文件为:

$OCR_BUILD/ppocr

验证文件类型:

file "$OCR_BUILD/ppocr"

应看到类似:

ELF 64-bit LSB pie executable, ARM aarch64

7. PP-OCRv6 模型兼容处理

7.1 safetensors 不能直接用于 C++

PP-OCRv6_medium_rec_safetensors 是 Transformers 引擎模型,官方使用方式类似:

from paddleocr import TextRecognition

model = TextRecognition(
    model_name="PP-OCRv6_medium_rec",
    engine="transformers",
)

它的 model.safetensors 不能直接被 deploy/cpp_infer 的静态模型加载器读取。

C++ demo 要求模型目录至少包含:

inference.json
inference.pdiparams
inference.yml

因此本次部署使用官方静态模型:

PP-OCRv6_medium_det
PP-OCRv6_medium_rec

这两个模型已经包含 Paddle Inference 格式文件,不需要在 Orin 上安装 Python 做转换。自定义 safetensors 权重如果要进入 C++,应优先获取对应的 .pdparams checkpoint,再使用 PaddleOCR 的 tools/export_model.py 导出静态模型;只有 safetensors 时,需要自行完成模型结构和参数名映射,不能简单改文件后缀。

7.2 V6 检测模型导致 std::out_of_range

使用官方 V6 静态检测模型首次运行时,程序异常退出:

terminate called after throwing an instance of 'std::out_of_range'
what():  _Map_base::at

原因不是动态库,也不是模型权重损坏,而是 V6 的 inference.yml 中:

PreProcess:
  transform_ops:
  - DetResizeForTest: null

当前 C++ 检测模块却无条件读取:

pre_tfs.at("DetResizeForTest.resize_long")

涉及文件:

deploy/cpp_infer/src/modules/text_detection/predictor.cc

将无条件读取改为可选读取:

auto resize_long_it = pre_tfs.find("DetResizeForTest.resize_long");
if (resize_long_it != pre_tfs.end() && !resize_long_it->second.empty()) {
  resize_param.resize_long = std::stoi(resize_long_it->second);
}

这样处理后:

  • 老模型存在 resize_long 时,保持原逻辑;
  • V6 模型为 null 时,使用 pipeline 参数中的 limit_side_lenlimit_typemax_side_limit
  • 不修改 V6 模型结构或推理计算。

重新编译 ppocr 后,V6 检测模块和识别模块均可正常创建。

8. 整理最终部署目录

最终部署目录建议与构建目录分离:

/home/wooley/paddleV6/
├── bin/
│   └── ppocr
├── runtime/
│   └── paddle_inference_install_dir/
│       ├── paddle/include/
│       ├── paddle/lib/
│       └── third_party/
├── models/
│   ├── PP-OCRv6_medium_det/
│   │   ├── inference.json
│   │   ├── inference.pdiparams
│   │   ├── inference.yml
│   │   └── configuration.json
│   └── PP-OCRv6_medium_rec/
│       ├── inference.json
│       ├── inference.pdiparams
│       └── inference.yml
├── examples/
│   └── general_ocr_002.png
├── env.sh
├── ocr_v6.sh
├── README.md
└── BUILD_INFO.md

8.1 运行环境变量

env.sh 的核心内容如下:

#!/usr/bin/env bash
set -euo pipefail

PADDLEV6_HOME="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
export PADDLEV6_HOME

export LD_LIBRARY_PATH="$PADDLEV6_HOME/runtime/paddle_inference_install_dir/paddle/lib:/usr/local/cuda/lib64:/usr/lib/aarch64-linux-gnu:/usr/lib/aarch64-linux-gnu/tegra:/usr/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"

Paddle runtime 和 demo 的动态库由部署目录提供;CUDA、cuDNN、OpenCV、系统 C++ runtime 仍使用 JetPack 系统环境。

8.2 运行脚本

cd /home/wooley/paddleV6
./ocr_v6.sh /path/to/input.png

指定输出目录:

./ocr_v6.sh /path/to/input.png /home/wooley/paddleV6/output

追加其他参数,例如使用 FP16:

./ocr_v6.sh /path/to/input.png /home/wooley/paddleV6/output --precision=fp16

脚本默认关闭文档方向分类、文档矫正和文本行方向分类,只运行文本检测和文本识别,适合先验证主链路。

9. 最终验证

9.1 动态库检查

cd /home/wooley/paddleV6
source ./env.sh
ldd ./bin/ppocr | grep "not found"

没有任何输出表示动态库依赖完整。

9.2 PP-OCRv6 GPU 推理

cd /home/wooley/paddleV6
./ocr_v6.sh ./examples/general_ocr_002.png ./output

运行日志中应出现:

Create model: PP-OCRv6_medium_det.
Create model: PP-OCRv6_medium_rec.

本次实际运行返回码为 0,并生成:

general_ocr_002_res.json
general_ocr_002_ocr_res_img.png

识别结果包含中文和英文,例如:

登机牌
BOARDING PASS
福州
FUZHOU
ZHANGQIWEI

10. 常见问题速查

现象 原因 处理方式
R_AARCH64_CALL26 relocation truncated 大型 ARM64 动态库分支跳转超范围 使用 ld.gold-Wl,--long-plt
ld.bfd: unrecognized option '--long-plt' 当前 linker 是 ld.bfd -fuse-ld=gold
uint32_t*int* 不兼容 Jetson ROIAlign 分支类型不匹配 将线程变量改为 int
__thrust_exec_check_disable__ 未声明 CUDA 12.6 的 Thrust 已移除内部宏 删除该宏,保留 __host__ __device__
OpenCV_DIR 找不到 Jetson OpenCV 配置不在 demo 预期目录 建立 opencv-prefix/lib64/cmake/opencv4 软链接
std::out_of_range _Map_base::at V6 检测 inference.ymlDetResizeForTest 为 null 改为 find() 可选读取 resize_long
libpaddle_inference.so not found 未设置运行时库路径 source /home/wooley/paddleV6/env.sh
C++ 无法加载 model.safetensors 这是 Transformers 模型,不是 Paddle static 模型 使用 inference.json + inference.pdiparams
单模块可运行但可视化图片没有字体 关闭了 freetype 或 OpenCV 未编译 freetype 不影响 OCR 推理;需要可视化字体时单独启用 freetype

11. 参考资料