第二章:开发环境搭建——交叉编译工具链安装、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++
注意: 我曾经在项目里直接用了Ubuntu源里的gcc-arm-linux-gnueabihf,结果编译出来的OpenCV在板子上跑起来就崩。后来发现是工具链版本太老,不支持某些ARMv8指令集。所以建议你直接用Linaro官方包,别图省事。

验证是否安装成功,跑一句:

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.solibonnxruntime_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)
注意: 我曾经在交叉编译pybind11模块时,忘记加-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调试

好了,这一章的内容就这些。环境搭建是后面所有工作的基础,别嫌麻烦。把这一步做扎实了,后面模型移植和算法优化才能顺风顺水。下一章我们开始讲物料识别模型的选型与导出,到时候见。