第1章:开发环境搭建——交叉编译工具链安装、SSH远程开发、Python虚拟环境配置、ONNX Runtime与TFLite Runtime的交叉编译
大家好,我是你们的嵌入式AI部署讲师。今天咱们正式开始第一课——把开发环境搭起来。
说实话,我见过太多同学卡在环境搭建这一步。明明模型训练得好好的,一到嵌入式设备上就各种报错。嗯,这很正常。嵌入式开发跟服务器开发完全是两码事。你想想看,一个ARM Cortex-A系列的小芯片,要跑一个几十MB的深度学习模型,中间要跨过多少坑?
所以这一章,我带你一步步把交叉编译工具链、SSH远程开发、Python虚拟环境,还有ONNX Runtime和TFLite Runtime的交叉编译全部搞定。别急,咱们慢慢来。
本章核心目标:在x86主机上搭建一套完整的嵌入式AI推理开发环境,能够编译出在ARM Linux设备上运行的推理引擎。
1.1 交叉编译工具链安装
先说说交叉编译。说白了,就是在你的x86电脑上编译出能在ARM芯片上跑的程序。为什么不能直接在ARM设备上编译?因为嵌入式设备资源有限,编译一个ONNX Runtime可能要半小时,而在x86上只要几分钟。
我个人习惯用Linaro提供的工具链,稳定且社区活跃。以aarch64(ARMv8 64位)为例:
# 下载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 CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
小提示:我建议把环境变量写到 ~/.bashrc 或 ~/.zshrc 里,这样每次打开终端就不用重新设置了。记得 source 一下。
验证是否安装成功:
aarch64-linux-gnu-gcc --version
# 输出类似:aarch64-linux-gnu-gcc (Linaro GCC 7.5-2019.12) 7.5.0
嗯,看到版本号就说明工具链装好了。这里要注意,工具链版本要和目标设备的glibc版本匹配。我曾经遇到过用高版本工具链编译的程序,在低版本glibc的设备上跑不起来,报错说找不到符号。后来老老实实降级了工具链版本。
1.2 SSH远程开发
嵌入式设备通常没有显示器,我们通过SSH连接上去操作。我个人强烈推荐VS Code的Remote-SSH插件,体验非常好。
基本配置步骤:
- 在嵌入式设备上开启SSH服务:
sudo systemctl start sshd - 在主机上生成SSH密钥:
ssh-keygen -t rsa -b 4096 - 将公钥复制到设备:
ssh-copy-id user@device_ip - 配置VS Code Remote-SSH,连接设备
避坑指南:我曾经在配置SSH时忽略了设备的IP地址变化问题。嵌入式设备如果使用DHCP,重启后IP会变,导致VS Code连不上。建议在路由器上绑定MAC地址和IP,或者给设备设置静态IP。
文件传输方面,我常用rsync,比scp快很多,特别是传输大文件时:
# 将本地目录同步到设备
rsync -avz --progress ./build/ user@192.168.1.100:/home/user/project/
# 从设备拉取文件
rsync -avz --progress user@192.168.1.100:/home/user/logs/ ./logs/
1.3 Python虚拟环境配置
做AI部署,Python环境是绕不开的。我建议在主机上使用conda管理虚拟环境,在设备上使用venv(因为conda在ARM上支持不太好)。
主机上的配置:
# 创建conda环境
conda create -n embedded_ai python=3.8
conda activate embedded_ai
# 安装常用依赖
pip install numpy opencv-python pillow onnx onnxruntime
设备上的配置(ARM Linux):
# 设备上安装Python3和venv
sudo apt-get install python3 python3-venv python3-pip
# 创建虚拟环境
python3 -m venv /home/user/venv/embedded_ai
source /home/user/venv/embedded_ai/bin/activate
# 安装纯Python依赖(不需要交叉编译的)
pip install numpy
注意:设备上的numpy等包需要从源码编译,或者使用预编译的ARM wheel包。我建议在设备上直接 pip install numpy,它会自动下载ARM版本的wheel。如果下载慢,可以配置国内镜像源。
1.4 ONNX Runtime交叉编译
这是本章的重头戏。ONNX Runtime的交叉编译,说白了就是让它在ARM Linux上能跑起来。我踩过不少坑,这里给你一个经过验证的流程。
首先,下载ONNX Runtime源码:
git clone --recursive https://github.com/microsoft/onnxruntime.git
cd onnxruntime
git checkout v1.15.1 # 选择一个稳定版本
然后,配置交叉编译参数。这里有个关键点——要指定CMAKE_TOOLCHAIN_FILE:
# 创建交叉编译工具链文件
cat > /opt/aarch64-toolchain.cmake << EOF
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
EOF
开始编译:
./build.sh --config Release \
--build_shared_lib \
--parallel \
--cmake_extra_defines CMAKE_TOOLCHAIN_FILE=/opt/aarch64-toolchain.cmake \
--arm64 \
--enable_onnx_tests=OFF
小提示:编译时间取决于你的CPU核心数。我一般用 --parallel 参数,让编译并行进行。第一次编译大概需要20-30分钟,耐心等待。
编译完成后,产物在 build/Linux/Release/ 目录下。主要文件:
libonnxruntime.so— 动态链接库onnxruntime— 可执行文件include/— 头文件
把这些文件传输到设备上,就可以在ARM Linux上运行ONNX模型了。
1.5 TFLite Runtime交叉编译
TFLite的交叉编译相对简单一些。Google官方提供了预编译的ARM库,但我建议自己编译,这样可以定制优化选项。
# 下载TensorFlow源码
git clone https://github.com/tensorflow/tensorflow.git
cd tensorflow
git checkout v2.13.0
# 配置交叉编译
./configure # 按提示选择ARM交叉编译选项
# 编译TFLite静态库
bazel build --config=elinux_aarch64 \
//tensorflow/lite:libtensorflowlite.so
如果你不想用bazel(说实话,bazel确实有点重),可以用CMake方式:
cd tensorflow/lite
mkdir build && cd build
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=/opt/aarch64-toolchain.cmake \
-DTFLITE_ENABLE_XNNPACK=ON \
-DTFLITE_ENABLE_GPU=OFF
make -j$(nproc)
经验之谈:我建议开启XNNPACK优化,它在ARM CPU上有显著的加速效果。GPU支持在嵌入式设备上通常不成熟,我一般关掉。另外,如果设备支持NEON指令集,可以加上 -DTFLITE_ENABLE_NEON=ON。
编译产物:
libtensorflowlite.so— TFLite动态库benchmark_model— 性能测试工具label_image— 图像分类示例
1.6 环境验证
所有东西都编译好了,怎么验证?我一般写一个简单的测试程序:
// test_ort.c
#include <onnxruntime/core/session/onnxruntime_c_api.h>
#include <stdio.h>
int main() {
const OrtApi* api = OrtGetApiBase()->GetApi(ORT_API_VERSION);
OrtSessionOptions* session_options;
api->CreateSessionOptions(&session_options);
printf("ONNX Runtime version: %s\n", api->GetVersionString());
printf("Environment setup successfully!\n");
api->ReleaseSessionOptions(session_options);
return 0;
}
交叉编译并传输到设备上运行:
# 主机上交叉编译
aarch64-linux-gnu-gcc test_ort.c -o test_ort \
-I/path/to/onnxruntime/include \
-L/path/to/onnxruntime/lib \
-lonnxruntime
# 传输到设备
scp test_ort libonnxruntime.so user@device_ip:/home/user/
# 设备上运行
export LD_LIBRARY_PATH=/home/user:$LD_LIBRARY_PATH
./test_ort
如果看到版本号输出,恭喜你,环境搭建成功了!
常见问题:如果设备上运行时报错 cannot open shared object file,说明动态库路径没设置对。用 ldd test_ort 查看依赖,确保所有.so文件都能找到。我一般把库文件放在 /usr/local/lib 下,然后运行 ldconfig 更新缓存。
好了,这一章的内容就到这里。环境搭好了,后面的事情就顺了。下一章我们开始讲模型转换和量化,那才是真正有意思的部分。