第二章:开发环境搭建——交叉编译工具链安装、OpenCV库编译、ONNX Runtime部署、Python与C++混合编程环境配置
好,咱们直接进入正题。这一章说白了就是「把家伙事儿备齐」。做分拣机物料识别,你不可能在PC上跑完模型就往ARM板上一丢——那肯定跑不起来。交叉编译、库的移植、混合编程,每一步踩坑我都替你踩过了。今天我把这些经验掰开揉碎讲给你听。
2.1 交叉编译工具链安装
先说说交叉编译。你想想看,你的开发电脑是x86架构,但分拣机上的主控芯片往往是ARM架构,比如瑞芯微RK3588、全志T507或者树莓派的BCM系列。在x86上编译出来的程序,ARM根本认不了。怎么办?用交叉编译工具链。
我个人习惯用Linaro提供的工具链,稳定且社区活跃。以ARM64(aarch64)为例,安装步骤其实就三步:
# 下载工具链
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 -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt/
# 配置环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
export CROSS_COMPILE=aarch64-linux-gnu-
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++
验证是否安装成功,跑一句:
aarch64-linux-gnu-gcc --version
能看到版本号,就说明工具链活了。
2.2 OpenCV库的交叉编译
OpenCV是视觉识别的核心库。但直接在ARM板上编译OpenCV?那速度慢得你想砸电脑。我试过在树莓派4上编译OpenCV 4.5.5,整整跑了6个小时。所以必须交叉编译。
这里我给出一个经过验证的CMake配置。嗯,要注意的是,分拣机场景下我们通常只需要imgproc、highgui、core这几个模块,别一股脑全编译,不然库体积会很大。
# 创建构建目录
mkdir build_arm64 && cd build_arm64
# 配置CMake
cmake -DCMAKE_TOOLCHAIN_FILE=../platforms/linux/aarch64-gnu.toolchain.cmake \
-DCMAKE_INSTALL_PREFIX=./install \
-DBUILD_opencv_world=ON \
-DBUILD_opencv_imgproc=ON \
-DBUILD_opencv_highgui=ON \
-DBUILD_opencv_core=ON \
-DWITH_GTK=OFF \
-DWITH_QT=OFF \
-DWITH_V4L=ON \
-DWITH_FFMPEG=OFF \
-DBUILD_TESTS=OFF \
-DBUILD_PERF_TESTS=OFF \
..
# 编译(用-j4加速)
make -j4
# 安装到指定目录
make install
BUILD_opencv_world设为ON,这样会生成一个单独的libopencv_world.so,部署时只需要拷一个库文件,省心很多。
编译完成后,在install目录下你会得到:
lib/—— 动态库文件(.so)include/—— 头文件bin/—— 一些工具(如opencv_version)
把这些拷到ARM板的/usr/local/下,就完成了OpenCV的部署。
2.3 ONNX Runtime部署
模型训练好之后,我们通常导出为ONNX格式。ONNX Runtime就是用来在端侧高效推理的引擎。分拣机场景下,我推荐用ONNX Runtime的C++接口,性能比Python好太多。
交叉编译ONNX Runtime稍微有点麻烦,因为它依赖protobuf、abseil等库。不过官方提供了预编译的ARM64包,省了我们不少事。
# 下载ONNX Runtime for ARM64
wget https://github.com/microsoft/onnxruntime/releases/download/v1.15.1/onnxruntime-linux-aarch64-1.15.1.tgz
# 解压
tar -xzvf onnxruntime-linux-aarch64-1.15.1.tgz
# 目录结构
# onnxruntime-linux-aarch64-1.15.1/
# ├── include/
# ├── lib/
# └── share/
如果你非要自己编译,也可以。但说实话,我试过一次,编译了将近两个小时,而且中间还因为protobuf版本冲突报错。后来我就学乖了,直接用官方预编译包。
libonnxruntime.so和libonnxruntime_providers.so拷到ARM板的/usr/lib/下,头文件拷到/usr/include/onnxruntime/。这样你的C++程序就能链接ONNX Runtime了。
2.4 Python与C++混合编程环境配置
为什么要在分拣机上搞混合编程?说白了,Python写业务逻辑快,C++跑推理快。我们用Python调用C++编译好的动态库,既享受了Python的开发效率,又保留了C++的性能。
这里我推荐用pybind11,它比Python原生的ctypes好用太多。你想想看,ctypes需要你手动声明每个函数的参数类型和返回值,而pybind11只需要写几行绑定代码。
先安装pybind11:
# 在ARM板上安装
pip3 install pybind11
# 或者交叉编译时作为子模块引入
git submodule add https://github.com/pybind/pybind11.git
然后写一个简单的C++函数,比如物料识别中的图像预处理:
// preprocess.cpp
#include <pybind11/pybind11.h>
#include <opencv2/opencv.hpp>
namespace py = pybind11;
cv::Mat preprocess_image(const std::string& image_path) {
cv::Mat img = cv::imread(image_path);
cv::Mat resized;
cv::resize(img, resized, cv::Size(224, 224));
return resized;
}
PYBIND11_MODULE(preprocess, m) {
m.def("preprocess_image", &preprocess_image, "预处理图像");
}
编译命令:
aarch64-linux-gnu-g++ -O3 -Wall -shared -std=c++11 \
-fPIC `python3-config --includes` \
-I/usr/local/include/opencv4 \
-I./pybind11/include \
preprocess.cpp \
-o preprocess.so \
-L/usr/local/lib -lopencv_world
在Python中调用:
import preprocess
img = preprocess.preprocess_image("/path/to/image.jpg")
print(img.shape) # 输出 (224, 224, 3)
-fPIC参数,结果生成的.so文件在Python里import时报「undefined symbol」错误。折腾了一下午才发现是编译选项的问题。所以-fPIC一定要加上。
2.5 环境验证与常见问题
环境搭好之后,怎么验证?我一般写一个简单的测试程序:
// test_env.cpp
#include <opencv2/opencv.hpp>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>
int main() {
// 测试OpenCV
cv::Mat img(100, 100, CV_8UC3, cv::Scalar(0, 255, 0));
std::cout << "OpenCV version: " << CV_VERSION << std::endl;
// 测试ONNX Runtime
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
std::cout << "ONNX Runtime is ready!" << std::endl;
return 0;
}
编译并拷到ARM板上运行,如果都能正常输出,说明环境搭建成功。
常见问题我列个表:
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 编译时报找不到opencv头文件 | 交叉编译时头文件路径没指定 | 检查-I参数是否正确指向OpenCV的include目录 |
| 运行时提示libopencv_world.so找不到 | 动态库路径没配置 | 在ARM板上执行export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH |
| Python import时报段错误 | pybind11版本与Python版本不匹配 | 确保pybind11和Python都是同一架构编译的 |
| ONNX Runtime推理结果不对 | 输入张量的形状或数据类型不匹配 | 检查模型的输入输出,用Ort::Session::GetInputTypeInfo调试 |
好了,这一章的内容就这些。环境搭建是后面所有工作的基础,别嫌麻烦。把这一步做扎实了,后面模型移植和算法优化才能顺风顺水。下一章我们开始讲物料识别模型的选型与导出,到时候见。