3. TensorFlow Lite Micro 环境搭建:从源码编译到开发板跑通
说实话,很多做嵌入式AI的朋友,第一步就卡在环境搭建上。不是下载慢,就是编译报错,折腾一整天连个hello world都跑不起来。我当年第一次搞TFLM,也踩了不少坑。
这一章,咱们就把TFLM的编译环境、CMSIS-NN的集成、以及开发板的烧录验证,一条龙捋清楚。你跟着我的步骤走,应该能省下不少时间。
3.1 为什么非要自己编译TFLM?
你可能想问:直接用官方预编译的库不行吗?
嗯,对于x86 Linux或者Windows,确实可以。但MCU的世界里,每家芯片的指令集、内存布局、外设地址都不一样。预编译的二进制根本没法通用。
自己编译的好处有三个:
- 裁剪体积:你只编译需要的算子,不用把整个框架塞进去。我见过有人把全量TFLM编译完,固件直接超了256KB Flash——其实他只用了一个全连接层。
- 硬件加速:只有源码编译,才能把CMSIS-NN、Helium、甚至自定义NPU驱动嵌进去。
- 调试自由:出bug了你能加打印、改代码、甚至单步调试。预编译库就是个黑盒。
3.2 源码获取与目录结构
先拉代码。我习惯用固定版本,避免后续依赖变动导致问题。
git clone -b v1.15.0 https://github.com/tensorflow/tflite-micro.git
cd tflite-micro
拉下来之后,你看到的目录大概是这样:
| 目录/文件 | 作用 |
|---|---|
tensorflow/lite/micro/ |
TFLM核心代码:解释器、内存分配器、算子注册 |
tensorflow/lite/micro/kernels/ |
所有算子实现,按平台分目录(arm/, xtensa/等) |
tensorflow/lite/micro/tools/ |
编译脚本、Makefile、项目生成器 |
third_party/ |
第三方依赖:CMSIS-5、FlatBuffers、ruy等 |
gen/ |
编译输出目录(运行make后自动生成) |
这里有个坑:third_party/cmsis/ 默认可能只包含CMSIS-Core,没有CMSIS-NN。你需要手动确认一下。
3.3 编译系统选择:Make vs CMake
TFLM官方主推的是Makefile系统。为什么?因为嵌入式工具链五花八门,CMake的跨平台能力在MCU领域反而成了负担——很多老编译器根本不支持CMake生成的Ninja/Makefile。
我个人建议:先用官方Makefile跑通,再考虑迁移到CMake。
编译一个针对Cortex-M4的hello world示例:
make -f tensorflow/lite/micro/tools/make/Makefile \
TARGET=cortex_m_generic \
TARGET_ARCH=cortex-m4 \
OPTIMIZED_KERNEL_DIR=cmsis_nn \
BUILD_TYPE=release \
hello_world_bin
解释一下几个关键参数:
TARGET=cortex_m_generic:目标平台,官方预置了cortex_m_generic、arduino、sparkfun_edge等。TARGET_ARCH=cortex-m4:具体架构,决定编译选项(-mcpu=cortex-m4 -mfpu=fpv4-sp-d16等)。OPTIMIZED_KERNEL_DIR=cmsis_nn:指定使用CMSIS-NN优化算子。不加这个,默认用纯C参考实现。BUILD_TYPE=release:release模式会加-O3优化,debug模式不加。
编译成功后,在 gen/cortex_m_generic_cortex-m4_release/bin/ 下会生成 hello_world_bin 文件。这是个裸机二进制,可以直接烧录。
3.4 CMSIS-NN 集成:不只是加个路径
很多人以为集成CMSIS-NN就是在Makefile里加个 OPTIMIZED_KERNEL_DIR=cmsis_nn 就完事了。其实没那么简单。
CMSIS-NN的集成分三层:
- 头文件路径:确保编译器能找到
arm_nnfunctions.h等头文件。 - 源文件编译:CMSIS-NN的算子实现(如
arm_convolve_s8.c)需要参与编译。 - 算子注册表替换:TFLM默认的卷积实现是
ConvEval,CMSIS-NN版本是ConvEvalCMSIS。你需要确保注册表里用的是后者。
检查是否真的集成了CMSIS-NN,有个简单方法:编译完成后,在生成的 gen/ 目录里搜一下 arm_convolve 符号。
nm gen/cortex_m_generic_cortex-m4_release/bin/hello_world_bin | grep arm_convolve
如果输出了一堆 arm_convolve_s8、arm_convolve_wrapper_s8 之类的符号,说明集成成功。如果只有 tflm_conv 开头的符号,那就是纯C参考实现。
3.5 开发板环境配置:以STM32F746G-Discovery为例
不同开发板的配置差异很大。我这里以STM32F746G-Discovery为例,讲一下通用步骤。
你需要准备:
- 交叉编译器:ARM GCC (gcc-arm-none-eabi),建议版本10.3以上。
- 烧录工具:OpenOCD + GDB,或者STM32CubeProgrammer。
- 串口调试工具:Putty、screen、或者VSCode的Serial Monitor插件。
编译针对STM32F7的TFLM示例:
make -f tensorflow/lite/micro/tools/make/Makefile \
TARGET=stm32f7 \
OPTIMIZED_KERNEL_DIR=cmsis_nn \
BUILD_TYPE=release \
hello_world_bin
注意这里 TARGET=stm32f7,Makefile会自动配置对应的链接脚本、启动文件和内存布局。
烧录命令(使用OpenOCD):
openocd -f board/stm32f7discovery.cfg -c "program gen/stm32f7_release/bin/hello_world_bin 0x08000000 verify reset exit"
烧录完成后,用串口工具连接开发板的USART1(通常是PA9/PA10,波特率115200),你应该能看到:
Hello World! x: 1, y: 0
Hello World! x: 2, y: 1
...
如果没看到输出,先检查串口引脚对不对。我有一回折腾了半小时,结果是杜邦线插反了。
3.6 知识体系总览
下面这张图把整个环境搭建的流程和关键节点串起来了。你可以把它当作一个检查清单。
3.7 常见问题与避坑
最后,我把这些年遇到的高频问题列出来,你碰到了可以直接查:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
编译报错:arm_nn_* 未定义 |
CMSIS-NN源文件未下载或未参与编译 | 检查 third_party/cmsis/ 是否完整,重新执行 make download_dependencies |
链接报错:undefined reference to main |
没有提供入口函数,或者链接脚本不对 | 确认 TARGET 参数是否正确,检查启动文件是否被包含 |
| 烧录后无串口输出 | 串口引脚配置错误,或波特率不匹配 | 核对开发板原理图,确认USART引脚;检查代码中波特率设置 |
| 推理结果全为0或随机值 | CMSIS-NN算子未生效,回退到参考实现;或者输入数据对齐不对 | 加打印确认算子类型;检查输入张量的维度是否满足CMSIS-NN对齐要求 |
好了,环境搭好之后,下一件事就是往里面塞模型了。不过那是后面章节的事。先把这一步跑通,你后面会顺很多。