3. 开发环境搭建:交叉编译工具链、ONNX Runtime、TensorRT、OpenCV 的嵌入式部署
说实话,嵌入式开发的第一个坎,往往不是算法本身,而是环境搭建。我见过不少同事,模型在 PC 上跑得飞起,一到板子上就各种报错,最后发现是交叉编译工具链没配好。这一章,我就带你一步步把地基打牢。
3.1 交叉编译工具链:打通 PC 与板子的桥梁
你想想看,你的开发机是 x86 架构,而嵌入式板子通常是 ARM 或 RISC-V。直接在 PC 上编译出来的程序,板子根本认不了。这时候就需要交叉编译——在 PC 上生成目标平台的可执行文件。
3.1.1 工具链的选择
我个人习惯用 Linaro 提供的 GCC 工具链,稳定且社区活跃。以 ARM64 为例,我通常这样配置:
# 下载并解压
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
tar -xf gcc-linaro-*.tar.xz -C /opt/
# 设置环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
3.1.2 验证工具链
配好之后,写个简单的 Hello World 测试一下:
# test.c
#include <stdio.h>
int main() {
printf("Hello from ARM64!\n");
return 0;
}
# 编译
aarch64-linux-gnu-gcc test.c -o test_arm
# 查看文件信息
file test_arm
# 输出: test_arm: ELF 64-bit LSB executable, ARM aarch64, ...
看到 ARM aarch64 就对了。把这个文件传到板子上,./test_arm 能打印出信息,说明工具链没问题。
3.2 ONNX Runtime:跨平台推理引擎的部署
ONNX Runtime 的好处是,你训练时用 PyTorch 还是 TensorFlow 都无所谓,导出 ONNX 模型后,它都能跑。在嵌入式端,我主要用它来做 CPU 推理,或者作为 TensorRT 的备选方案。
3.2.1 交叉编译 ONNX Runtime
官方提供了预编译包,但嵌入式平台往往需要自己编译。我建议用 CMake 交叉编译,步骤大致如下:
# 设置交叉编译工具链
cmake -DCMAKE_TOOLCHAIN_FILE=<path_to_toolchain.cmake> \
-DCMAKE_INSTALL_PREFIX=<install_path> \
-Donnxruntime_BUILD_SHARED_LIB=ON \
-Donnxruntime_USE_CUDA=OFF \
-Donnxruntime_USE_OPENMP=ON \
..
make -j4
make install
-Donnxruntime_USE_MKLDNN=OFF,能减少不少体积。
3.2.2 部署验证
编译完成后,把 libonnxruntime.so 和头文件拷贝到板子上。写个简单的推理程序:
#include <onnxruntime_cxx_api.h>
int main() {
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(4);
const char* model_path = "yolov8n.onnx";
Ort::Session session(env, model_path, session_options);
printf("ONNX Runtime loaded successfully!\n");
return 0;
}
编译时链接 -lonnxruntime,运行前记得 export LD_LIBRARY_PATH=./。嗯,这里要注意,ONNX Runtime 的版本要和模型导出时的 opset 版本匹配,否则会报算子不支持的错误。
3.3 TensorRT:NVIDIA 平台的性能利器
如果你用的是 Jetson 系列板子,那 TensorRT 几乎是必选项。它能将 ONNX 模型优化成 TensorRT 引擎,推理速度能提升 2-5 倍。
3.3.1 环境准备
Jetson 板子出厂一般自带 JetPack,里面包含了 TensorRT。但版本可能比较旧,我建议手动更新:
# 查看当前版本
dpkg -l | grep tensorrt
# 安装最新版(以 JetPack 5.1 为例)
sudo apt-get update
sudo apt-get install nvidia-tensorrt
3.3.2 ONNX 转 TensorRT 引擎
我最常用的方式是使用 trtexec 工具,简单直接:
# 将 ONNX 转为 FP16 引擎
trtexec --onnx=yolov8n.onnx \
--saveEngine=yolov8n_fp16.engine \
--fp16 \
--workspace=1024
# 测试推理性能
trtexec --loadEngine=yolov8n_fp16.engine --duration=10
3.3.3 动态形状处理
YOLO 的输入尺寸有时会变化。TensorRT 支持动态形状,但需要在转换时指定范围:
trtexec --onnx=yolov8n.onnx \
--saveEngine=yolov8n_dynamic.engine \
--minShapes=input:1x3x320x320 \
--optShapes=input:1x3x640x640 \
--maxShapes=input:1x3x1280x1280 \
--fp16
我个人建议,如果应用场景固定,尽量用静态形状。动态形状会引入额外的优化开销,推理速度会慢 10%-20%。
3.4 OpenCV:图像预处理与后处理的基础库
YOLO 的推理流程中,图像读取、缩放、归一化、NMS 等操作都离不开 OpenCV。嵌入式端的 OpenCV 需要裁剪,去掉不必要的模块。
3.4.1 交叉编译 OpenCV
我一般只保留 core、imgproc、imgcodecs 这几个模块:
cmake -DCMAKE_TOOLCHAIN_FILE=<toolchain.cmake> \
-DCMAKE_INSTALL_PREFIX=<install_path> \
-DBUILD_opencv_core=ON \
-DBUILD_opencv_imgproc=ON \
-DBUILD_opencv_imgcodecs=ON \
-DBUILD_opencv_highgui=OFF \
-DBUILD_opencv_videoio=OFF \
-DWITH_JPEG=ON \
-DWITH_PNG=ON \
-DWITH_TIFF=OFF \
..
make -j4
make install
-DENABLE_NEON=ON,图像缩放和颜色转换能快 30% 以上。
3.4.2 常用操作示例
在板子上,我经常这样处理图像:
cv::Mat img = cv::imread("input.jpg");
cv::Mat resized, blob;
// 缩放并保持宽高比
cv::resize(img, resized, cv::Size(640, 640));
// 归一化并转为浮点
resized.convertTo(blob, CV_32FC3, 1.0 / 255.0);
// 后处理:NMS
std::vector<cv::Rect> boxes;
std::vector<float> scores;
cv::dnn::NMSBoxes(boxes, scores, 0.5, 0.45);
注意,OpenCV 的 dnn::NMSBoxes 在 ARM 上性能一般,我后来改用手写的 NMS,速度提升了 2 倍。
3.5 整体环境结构图
下面这张图,帮你理清各个组件之间的关系:
3.6 环境验证清单
配完环境后,我建议按这个清单逐项验证,避免遗漏:
| 组件 | 验证方法 | 预期结果 |
|---|---|---|
| 交叉编译工具链 | aarch64-linux-gnu-gcc --version |
显示版本信息 |
| ONNX Runtime | 运行测试程序加载 ONNX 模型 | 加载成功,无报错 |
| TensorRT | trtexec --version |
显示版本号 |
| OpenCV | 运行图像读取 + 缩放程序 | 输出正确尺寸的图像 |