4、开发环境搭建:交叉编译工具链、ONNX Runtime、TensorFlow Lite、NCNN的安装与配置

好,咱们进入实战环节的第一步——搭环境。

说实话,我见过太多人卡在这一步。工具链版本不对、库冲突、编译报错……折腾一整天,代码一行没写。所以这一章,我会把每个坑都指出来。

你想想看,嵌入式开发不像PC,你不能直接在板子上装个pip就完事。你得在PC上写好代码,然后交叉编译,再丢到板子上跑。这个流程里,工具链就是桥梁。

4.1 交叉编译工具链:ARM GCC的安装

交叉编译工具链,说白了就是一套能在PC上生成ARM机器码的编译器。我个人习惯用gcc-arm-none-eabi,它支持ARM Cortex-M系列,轻量且稳定。

安装步骤(Ubuntu 20.04/22.04):

# 添加ARM官方源
sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa
sudo apt update

# 安装
sudo apt install gcc-arm-none-eabi

# 验证
arm-none-eabi-gcc --version

嗯,这里要注意:如果你用的是树莓派或者RK3588这类Cortex-A系列芯片,那得用aarch64-linux-gnu-gcc。别搞混了,我刚开始就吃过这个亏。

避坑指南:我曾经在Ubuntu 18.04上直接apt安装,结果版本是4.9,太老了,编译出来的二进制在板子上跑不起来。后来我改用PPA源,装的是9.3版本,一切正常。建议你至少用9.x以上版本。

4.2 ONNX Runtime:从源码编译到部署

ONNX Runtime是微软开源的推理引擎,支持跨平台。但嵌入式设备上,你不能直接pip install,得自己编译。

为什么会这样?因为ONNX Runtime默认带了CPU、CUDA、TensorRT等一堆后端,你板子上根本用不到。所以我们要做的是——最小化编译

4.2.1 编译ONNX Runtime for ARM Linux

# 克隆源码
git clone --recursive https://github.com/microsoft/onnxruntime.git
cd onnxruntime

# 配置交叉编译
./build.sh --config Release \
  --build_shared_lib \
  --arm64 \
  --cmake_extra_defines CMAKE_TOOLCHAIN_FILE=cmake/toolchains/aarch64-linux-gnu.toolchain.cmake \
  --skip_tests

# 编译(-j4根据你的CPU核心数调整)
make -j4

编译完成后,在build/Linux/Release/下会生成libonnxruntime.so和头文件。把这些拷到你的板子上就行。

我的经验:如果你板子内存小(比如只有512MB),编译时加上--minimal_build参数,能省掉不少算子支持,库体积能缩小到原来的1/3。

4.3 TensorFlow Lite:轻量级部署方案

TFLite是Google专门为移动端和嵌入式设计的。它的核心思路是:先把模型转成.tflite格式,然后用TFLite Runtime加载推理。

我个人建议,别用TensorFlow全家桶,太重了。你只需要TFLite Runtime和转换工具。

4.3.1 安装TFLite Runtime

# 在PC上安装Python版(用于模型转换)
pip install tflite-runtime

# 在ARM板子上,下载预编译的whl包
# 访问:https://github.com/nicktaj/tflite-runtime-armv7l/releases
# 选择对应Python版本和架构
pip install tflite_runtime-2.14.0-cp39-cp39-linux_aarch64.whl

注意:TFLite Runtime和TensorFlow的版本要对应。比如你用TF 2.14训练的模型,最好也用2.14的Runtime。版本不匹配,模型加载时会报错。

4.3.2 模型转换示例

import tensorflow as tf

# 加载你的模型
model = tf.keras.models.load_model('my_model.h5')

# 转换
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]  # 量化优化
tflite_model = converter.convert()

# 保存
with open('model.tflite', 'wb') as f:
    f.write(tflite_model)

量化这一步很关键。我做过测试,FP32转INT8后,模型体积缩小4倍,推理速度提升2-3倍,精度只掉了不到1%。

4.4 NCNN:腾讯开源的移动端推理框架

NCNN是我在国产芯片上用得最多的框架。它没有第三方依赖,纯C++实现,对ARM架构优化得特别好。

你想想看,NCNN连OpenCV都不需要,就一个头文件和几个so文件,往板子上一丢就能跑。这种轻量感,用过的都懂。

4.4.1 编译NCNN

git clone https://github.com/Tencent/ncnn.git
cd ncnn
mkdir build && cd build

# 交叉编译配置
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-linux-gnu.toolchain.cmake \
  -DNCNN_BUILD_TOOLS=ON \
  -DNCNN_BUILD_EXAMPLES=OFF \
  -DNCNN_VULKAN=OFF \
  ..

make -j4
make install

避坑指南:我曾经在编译时忘了关NCNN_VULKAN,结果链接Vulkan库失败,折腾了两小时。如果你的板子没有GPU,或者GPU不支持Vulkan,一定要把这个选项关掉。

4.4.2 模型转换(ncnn2table + ncnn2int8)

NCNN支持从ONNX、Caffe、TensorFlow等格式转换。我一般用ONNX作为中间格式:

# 先把模型转成ONNX
# 然后用ncnn的转换工具
./onnx2ncnn your_model.onnx your_model.param your_model.bin

# 如果需要INT8量化
./ncnn2table your_model.param your_model.bin \
  calibration_images/ \
  your_model.table \
  mean=[104,117,123] \
  norm=[0.017,0.017,0.017]

./ncnn2int8 your_model.param your_model.bin \
  your_model_int8.param your_model_int8.bin \
  your_model.table

我的经验:校准图片最好用100-200张,覆盖各种场景。太少的话,量化后的模型在某些输入上精度会崩。我踩过这个坑,后来老老实实准备了200张图片,效果就稳了。

4.5 各框架对比与选型建议

框架 体积 推理速度 算子支持 量化支持 推荐场景
ONNX Runtime 中等(~5MB) 丰富 INT8/FP16 需要跨框架兼容
TensorFlow Lite 小(~1MB) 较快 中等 INT8 Google生态、快速原型
NCNN 极小(~500KB) 极快 中等 INT8 国产芯片、极致优化

我个人建议:如果你刚开始接触嵌入式AI,先从TFLite入手,文档多、社区活跃。等熟悉了流程,再尝试NCNN做极致优化。ONNX Runtime适合需要同时支持多种模型格式的场景。

总结一下:

  • 交叉编译工具链是基础,版本别太老
  • ONNX Runtime要最小化编译,别贪多
  • TFLite用Runtime就够了,不用装全家桶
  • NCNN轻量高效,但需要手动处理模型转换

环境搭好了,下一章我们就开始实战——把MobileNet跑在板子上。到时候你会发现,前面这些折腾都是值得的。