1. 一块能跑AI模型的TinyML开发板,硬件选型到底在选什么
TinyML这个词这两年热度上来了,但很多人第一次接触时都会有个误区:觉得只要MCU主频够高、内存够大,就能跑AI模型。实际做过几个项目之后你会发现,事情远没有这么简单。一块能跑AI模型的TinyML开发板,硬件选型本质上是在做一场“在针尖上跳舞”的平衡术——算力、内存、功耗、外设、成本,五个维度互相拉扯,任何一个短板都会让模型跑不起来或者跑得很难受。
我最早接触TinyML是在一个语音唤醒关键词识别的项目上,当时选了一颗主频不低的Cortex-M4芯片,满心欢喜地把训练好的模型转成C数组烧进去,结果一跑就HardFault。排查了半天才发现,模型推理过程中的中间张量把RAM撑爆了。从那以后我就明白了一个道理:TinyML开发板的硬件选型,不能只看主频,得从模型的计算图和内存布局反推硬件需求。
这篇文章主要面向两类人:一类是嵌入式工程师想转型做TinyML,手里有开发板但不知道怎么评估能不能跑AI;另一类是算法工程师想把模型部署到端侧,但对硬件参数没概念。我会从实际项目出发,把TinyML开发板需要的核心硬件拆开来讲,包括MCU选型、内存架构、外设接口、功耗设计、调试工具链这几个维度,每个维度都会给出具体的参数参考和选型逻辑。看完之后你至少能判断一块开发板能不能跑你的模型,以及如果要自己画板子,哪些地方不能省。
2. TinyML开发板的核心硬件架构拆解
2.1 MCU选型:为什么STM32H743成了热门参考
聊TinyML开发板的MCU选型,STM32H743是一个绕不开的型号。这颗芯片在TinyML社区里被大量用作参考平台,原因很直接:Cortex-M7内核,主频480MHz,带双精度浮点单元(FPU),还有指令和数据缓存。这几个特性凑在一起,刚好卡在TinyML的甜点位上。
先看主频。TinyML模型推理本质上是大量的乘加运算(MAC),主频直接决定了每秒能完成多少次MAC。以关键词识别常用的DS-CNN模型为例,参数量大概在2万到5万之间,每次推理需要的MAC数量在百万级别。480MHz的M7内核,配合FPU和CMSIS-NN库优化,跑一次推理大概在10到30毫秒之间,这个延迟对于语音唤醒场景是可以接受的。如果换成主频80MHz的Cortex-M0+,同样的模型可能要跑几百毫秒,实时性就没了。
再看FPU。很多人觉得TinyML模型都是量化成int8的,不需要浮点。这个想法对了一半。推理阶段确实可以用int8,但模型的前处理和后处理往往涉及浮点运算,比如音频的MFCC特征提取、图像的归一化。没有硬件FPU的话,这些操作会吃掉大量CPU周期。STM32H743带的是双精度FPU,虽然TinyML主要用单精度,但有总比没有强。
缓存这个点容易被忽略。Cortex-M7有I-Cache和D-Cache,对于模型权重存放在外部Flash、运行时搬运到内部RAM的场景,缓存能显著减少访问延迟。我实测过,开启D-Cache之后,一个图像分类模型的推理速度提升了将近40%。当然缓存也带来了一致性问题,DMA传输和缓存刷新要特别小心,这个后面会细说。
除了STM32H743,市面上常见的TinyML开发板MCU还有几个梯队。第一梯队是Cortex-M7和Cortex-M55,M55带Helium向量扩展,对int8和int16的SIMD支持更好,但开发板价格偏高。第二梯队是Cortex-M4和M33,主频在80到200MHz之间,适合中小型模型。第三梯队是Cortex-M0+和M3,基本只能跑极小的模型,比如简单的异常检测。
| MCU内核 | 典型主频 | FPU | 适合模型规模 | 典型开发板 |
|---|---|---|---|---|
| Cortex-M0+ | 48-80MHz | 无 | 极小(<10KB参数) | 低功耗传感器节点 |
| Cortex-M4 | 80-200MHz | 单精度 | 中小(10-100KB) | 通用TinyML入门板 |
| Cortex-M7 | 400-600MHz | 双精度 | 中大型(100KB-1MB) | STM32H743/H750 |
| Cortex-M55 | 200-400MHz | 带Helium | 中大型+向量加速 | Alif Ensemble |
| Cortex-M33 | 100-200MHz | 单精度 | 中小+安全 | 带TrustZone的板子 |
选MCU的时候还有一个隐藏参数:Flash和RAM的容量以及总线架构。STM32H743有2MB Flash和1MB RAM,而且RAM分成了多个块,有DTCM、AXI SRAM、SRAM1-4等。DTCM紧耦合内存访问延迟最低,适合放模型推理时的中间张量。AXI SRAM容量大,适合放模型权重。这种多块内存的架构给了你很大的优化空间,但也意味着你需要手动规划内存布局,不能像PC上那样随便malloc。
2.2 内存架构:模型能不能跑起来的关键
TinyML开发板上跑AI模型,内存是比算力更常见的瓶颈。一个模型占用的内存分三部分:权重(weights)、激活值(activations)和中间缓冲区(scratch buffer)。权重是静态的,可以放在Flash里,运行时按需加载到RAM。激活值是每层计算的输入输出,必须放在RAM里。中间缓冲区是某些算子(比如卷积的im2col)需要的临时空间。
以STM32H743为例,1MB的RAM听起来不少,但实际能用的没那么多。DTCM有128KB,AXI SRAM有512KB,SRAM1-4加起来有384KB。如果你用TensorFlow Lite Micro作为推理引擎,它本身会占用一部分内存作为tensor arena。这个arena的大小需要你根据模型的最大中间张量来估算。
我一般会这样估算:先看模型每一层的输出张量大小,取最大值,再乘以2到3倍的余量。比如一个模型的最大中间张量是50KB,那tensor arena至少设150KB。如果模型权重也放在RAM里,还要加上权重的大小。STM32H743的1MB RAM,跑一个100KB权重、150KB arena的模型是绰绰有余的,但如果你想把整个模型都塞进DTCM,那就不够了。
外部内存扩展是另一个思路。有些TinyML开发板会带HyperRAM或者SDRAM,容量从几MB到几十MB。但外部内存的访问延迟比内部RAM高一个数量级,对于需要频繁访问的激活值来说,放在外部内存会严重拖慢推理速度。我的经验是:权重可以放外部Flash,运行时按层加载;激活值必须放内部RAM;中间缓冲区如果内部RAM不够,可以放外部RAM,但要接受性能损失。
还有一个容易踩的坑是内存对齐。Cortex-M7的D-Cache是按cache line(32字节)操作的,如果DMA传输的目标地址没有32字节对齐,缓存刷新的时候会把相邻数据也刷掉,导致数据损坏。我在一个摄像头项目里就遇到过这个问题,图像数据通过DMA搬到RAM,结果每隔几行就出现花屏。后来把缓冲区地址强制32字节对齐,问题就消失了。所以画板子的时候,外部内存的地址映射要留好对齐余量。
2.3 外设接口:传感器和调试的通道
TinyML开发板的外设接口决定了它能接什么传感器,以及调试起来方不方便。常见的外设包括I2C、SPI、UART、I2S、PDM、ADC、DCMI等。不同的AI应用场景需要的外设不一样。
语音类应用需要I2S或者PDM接口接数字麦克风。PDM麦克风便宜且布线简单,但需要MCU做抽取滤波,会消耗CPU。I2S麦克风输出的是PCM数据,MCU直接读就行,但价格贵一些。STM32H743的SAI接口支持I2S和PDM,配合DMA可以做到零CPU开销采集音频。我一般推荐用I2S麦克风,省下来的CPU周期可以留给模型推理。
图像类应用需要DCMI或者SPI接口接摄像头。DCMI是并口,速度快,适合高分辨率摄像头。SPI接口的摄像头布线简单,但带宽有限,适合低分辨率场景。TinyML的图像模型输入通常是96x96或者128x128的灰度图,SPI摄像头的带宽勉强够用。如果要做160x160以上的彩色图,最好用DCMI。
调试接口方面,SWD是标配,但TinyML开发板最好带一个高速UART或者USB CDC,用来输出推理结果和性能数据。我习惯在代码里加一个性能计数器,每次推理完通过UART打印耗时和内存占用,这样调优的时候心里有数。另外,如果开发板带ETM或者SWO引脚,可以用指令追踪功能分析热点函数,对优化推理速度很有帮助。
| 应用场景 | 必需外设 | 推荐外设 | 注意事项 |
|---|---|---|---|
| 语音唤醒 | I2S/PDM麦克风 | SAI+DMA | PDM需CPU抽取,I2S更省资源 |
| 图像分类 | DCMI/SPI摄像头 | DCMI+DMA | SPI带宽有限,高分辨率用DCMI |
| 振动检测 | ADC/I2C加速度计 | SPI加速度计 | 采样率要满足Nyquist |
| 手势识别 | I2C/SPI IMU | SPI IMU | 注意中断引脚和FIFO |
| 异常检测 | ADC | 硬件触发ADC | 用定时器触发保证采样率 |
2.4 功耗设计:电池供电场景的硬约束
TinyML的一大卖点是端侧低功耗推理,所以开发板的功耗设计很关键。功耗分动态功耗和静态功耗。动态功耗跟主频和电压有关,静态功耗主要是漏电流和外围器件的待机功耗。
STM32H743在480MHz全速运行的时候,电流大概在200mA左右,这个功耗对于电池供电的设备来说太高了。实际部署的时候一般会用动态调频,推理的时候升到高频,推理完降回低频或者进入Stop模式。Stop模式下电流可以降到几十微安,配合RTC定时唤醒,平均功耗可以做到毫安级别。
但这里有个坑:从Stop模式唤醒的时间。STM32H743从Stop模式唤醒大概需要几十微秒,如果推理本身只要10毫秒,唤醒开销占比不大。但如果你的应用是每秒推理一次,那大部分时间都在Stop模式,平均功耗就很低。我做过一个振动异常检测的项目,用加速度计的中断唤醒MCU,推理完立刻回Stop模式,平均电流只有1.2mA,用CR2032纽扣电池能撑好几个月。
开发板上的外围器件也会吃电。LDO的静态电流、电平转换芯片的漏电流、LED指示灯,这些加起来可能比MCU待机电流还大。自己画板子的时候,不用的外设要彻底断电,LED要能通过跳线断开。我见过一个开发板,MCU待机电流只有5微安,但板载的USB转串口芯片待机电流有500微安,直接把低功耗优势抵消了。
3. 从模型到硬件:实操选型流程
3.1 第一步:确定模型的资源需求
在选硬件之前,你得先知道模型要吃什么。如果你用的是TensorFlow Lite Micro,可以用它的分析工具输出每层的张量大小和算子类型。我一般会关注三个数字:模型权重大小、最大中间张量大小、算子类型分布。
模型权重大小决定了Flash需求。int8量化的模型,权重大小约等于参数量乘以1字节。比如一个5万参数的模型,权重大概50KB。加上推理引擎的代码,Flash至少留256KB比较稳妥。
最大中间张量大小决定了RAM需求。这个数字在TFLite Micro的模型分析里能看到,叫“arena size”。但实际分配的时候要留余量,因为不同版本的推理引擎可能有差异。我的经验是乘以1.5倍。
算子类型分布决定了MCU的指令集需求。如果模型里全是卷积和全连接,那带FPU和DSP扩展的M4/M7就很合适。如果模型里有大量element-wise操作,那内存带宽可能成为瓶颈,需要关注总线的位宽和频率。
3.2 第二步:匹配MCU的算力和内存
拿到模型的资源需求后,就可以对照MCU的参数来匹配了。算力方面,我一般用“MAC数量除以主频”来估算推理时间。比如模型需要100万次MAC,MCU主频480MHz,理论上每次MAC一个周期,那推理时间约2毫秒。但实际上因为内存访问、循环开销、算子实现效率,实际时间可能是理论值的3到5倍。所以100万MAC的模型,在STM32H743上大概跑6到10毫秒。
内存方面,把权重大小、arena大小、推理引擎开销、栈空间加起来,跟MCU的可用RAM对比。注意MCU的RAM不是全部可用的,启动文件、堆栈、外设缓冲区都要占一部分。STM32H743的1MB RAM,实际能留给模型的大概800KB左右。
如果算下来发现MCU跑不动,有几个优化方向:一是进一步量化模型,比如从int8降到int4;二是用剪枝或者知识蒸馏缩小模型;三是换更高性能的MCU,比如带Helium的M55;四是把模型拆成多段,用外部Flash存储权重,运行时按层加载。
3.3 第三步:验证外设和调试通道
模型能跑起来之后,还要确认外设能不能满足数据采集的需求。比如语音唤醒需要麦克风以16kHz采样,每次推理需要1秒的音频数据,那就是16000个采样点。如果用的是PDM麦克风,MCU需要做抽取滤波,这个计算量要算进CPU预算里。
调试通道也很重要。我习惯在开发板上留一个UART接口,专门用来输出推理日志。日志内容包括:推理耗时、内存占用、输入数据的统计特征、输出置信度。这些数据对于调优模型和排查问题非常有用。如果开发板带USB,可以用USB CDC虚拟串口,速度比UART快很多,适合传输大量数据。
3.4 第四步:功耗预算和电源设计
如果是电池供电的场景,功耗预算要提前算。假设设备用1000mAh的锂电池,期望续航一年,那平均电流要控制在0.11mA以下。这个要求很苛刻,意味着MCU大部分时间必须在Stop模式,推理占空比要非常低。
我一般会这样设计:用传感器的事件中断唤醒MCU,MCU采集数据、推理、输出结果,然后立刻回Stop模式。整个活跃时间控制在几十毫秒以内,占空比低于1%。这样平均电流可以做到毫安级别,续航几个月到一年。
电源设计方面,如果输入电压和MCU电压接近,用LDO就行。如果压差大,用DC-DC效率更高。但DC-DC的开关噪声可能影响模拟外设,比如ADC和麦克风。我一般会在DC-DC输出后面加一级LDO,给模拟部分供电,数字部分直接用DC-DC。
4. 常见问题与排查技巧实录
4.1 模型跑不起来:HardFault和内存溢出
HardFault是TinyML部署中最常见的问题,十有八九是内存相关。可能的原因包括:tensor arena太小、栈溢出、内存对齐问题、DMA和缓存冲突。
排查HardFault的第一步是看LR和PC寄存器的值,定位出错指令的地址。如果地址在memcpy或者memset里,基本可以确定是内存越界。这时候把tensor arena调大试试,如果问题消失,那就是arena不够。
栈溢出也很常见。TFLite Micro的推理函数调用层次比较深,局部变量多,默认的栈大小可能不够。我一般会把栈设到8KB以上,如果模型复杂,设到16KB。在STM32CubeMX里可以调整栈大小,但要注意栈和堆的总和不能超过RAM容量。
内存对齐问题在Cortex-M7上特别突出。D-Cache的cache line是32字节,如果DMA的目标缓冲区没有32字节对齐,缓存刷新会破坏相邻数据。解决方法是用__attribute__((aligned(32)))修饰缓冲区,或者在DMA传输前后做cache clean/invalidate。
4.2 推理速度慢:性能瓶颈定位
推理速度慢的原因可能有很多:主频没跑满、缓存没开、算子实现效率低、内存带宽不够。我一般会按这个顺序排查。
先确认主频。STM32H743默认的时钟配置可能不是480MHz,需要手动配置PLL。用SystemCoreClock变量确认实际主频。如果主频不对,检查时钟树配置和电源电压。STM32H743跑480MHz需要把电压调到Scale 1模式,否则会不稳定。
再确认缓存。I-Cache和D-Cache默认可能是关闭的,需要在启动代码里开启。开启缓存后,记得配置MPU,把外部内存区域设为Non-cacheable,避免缓存一致性问题。
然后看算子实现。TFLite Micro的参考算子是没有优化的,跑得慢很正常。CMSIS-NN提供了针对Cortex-M优化的算子,速度能快好几倍。把TFLite Micro的算子替换成CMSIS-NN的,是提升推理速度最直接的方法。
最后看内存带宽。如果模型权重放在外部Flash,每次推理都要从Flash读权重,SPI Flash的带宽可能成为瓶颈。解决方法是用内存映射模式(XIP)读Flash,或者把权重预加载到RAM。
4.3 功耗降不下来:待机电流排查
低功耗项目最头疼的就是待机电流比预期高。排查方法是一步步断开外设,看电流变化。
先把所有GPIO设为模拟输入或者输出低,避免浮空输入导致的漏电流。然后关闭不用的外设时钟,比如ADC、SPI、I2C。如果板子上有LDO或者DC-DC,确认它们的静态电流。有些LDO的静态电流有几百微安,换成低静态电流的型号能省不少。
调试接口也会吃电。SWD接口在待机时如果保持连接,调试器可能会给MCU供电或者拉高某些引脚。量产的时候可以把SWD引脚复用成GPIO,或者加跳线断开。
还有一个隐蔽的坑是Flash的待机电流。有些外部Flash芯片在待机时电流有几十微安,如果不需要存储数据,可以把Flash的片选拉高,让它进入深度睡眠。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| HardFault | arena太小 | 增大arena看是否消失 | 调整arena大小 |
| HardFault | 栈溢出 | 查看SP是否越界 | 增大栈空间 |
| 推理结果错误 | 内存对齐 | 检查缓冲区地址 | 32字节对齐 |
| 推理结果错误 | 缓存一致性 | 关闭D-Cache测试 | 配置MPU或手动刷新 |
| 推理速度慢 | 主频未跑满 | 查看SystemCoreClock | 配置PLL和电压 |
| 推理速度慢 | 算子未优化 | 对比CMSIS-NN | 替换算子实现 |
| 待机电流高 | 外设未关 | 逐个关闭外设 | 关闭时钟和电源 |
| 待机电流高 | GPIO浮空 | 设为模拟输入 | 配置GPIO模式 |
| 麦克风无数据 | PDM时钟不对 | 示波器看时钟 | 调整SAI配置 |
| 摄像头花屏 | DMA带宽不够 | 降低分辨率测试 | 优化DMA优先级 |
4.5 几个容易忽略的实操心得
第一个心得是关于模型转换的。TensorFlow Lite的转换器有很多参数,比如representative_dataset、inference_type、supported_ops。如果转换的时候没有指定representative dataset,量化后的模型精度可能掉得很厉害。我一般会准备几百张代表性样本,覆盖各种场景,这样量化后的精度损失能控制在1%以内。
第二个心得是关于内存布局的。STM32H743的DTCM和AXI SRAM在地址空间上是连续的,但访问速度不一样。把频繁访问的数据放在DTCM,把大块的权重放在AXI SRAM,能明显提升性能。在链接脚本里可以指定变量放到哪个内存段,比如__attribute__((section(".DTCM")))。
第三个心得是关于调试的。TinyML的调试比普通嵌入式开发麻烦,因为你看不到中间张量的值。我的做法是在模型里插入几个输出节点,把中间层的统计特征(均值、方差、最大值)打印出来,跟PC上跑的结果对比。如果差异大,说明量化或者算子实现有问题。
第四个心得是关于开发板选择的。如果只是学习和验证,买现成的开发板就行,STM32H743I-EVAL或者Nucleo-H743ZI都不错。如果要量产,建议自己画板子,把不需要的外设去掉,优化功耗和成本。自己画板子的时候,MCU的电源引脚一定要加足够的去耦电容,STM32H743的电源引脚有十几对,每对都要加100nF和10uF的电容,否则高速运行时可能不稳定。
第五个心得是关于工具链的。STM32CubeMX和CubeIDE是标配,但TFLite Micro的集成需要手动配置。我一般会用Makefile或者CMake管理项目,把TFLite Micro的源码和CMSIS-NN的库加进去。编译的时候注意优化等级,-O3比-Os快不少,但代码体积会大一些。如果Flash够用,优先用-O3。
5. 自己画一块TinyML开发板的硬件清单参考
如果你看完上面的内容,想自己画一块专门跑TinyML的开发板,这里给一个硬件清单参考。这个清单是基于STM32H743的,适合中大型模型,兼顾性能和功耗。
核心器件方面,MCU选STM32H743VIT6,LQFP100封装,2MB Flash,1MB RAM。电源用TPS62840或者类似的低静态电流DC-DC,输出3.3V和1.8V。如果对噪声敏感,1.8V可以用LDO。外部Flash选W25Q128或者IS25LP128,128Mbit,SPI接口,支持XIP。外部RAM可选,如果模型大,加一颗APS6404 HyperRAM,8MB,用来放权重。
传感器接口方面,留一个I2S接口接数字麦克风,比如INMP441。留一个DCMI接口接摄像头,比如OV7670或者OV2640。留一个SPI接口接IMU,比如LSM6DSO。留一个I2C接口接环境传感器,比如BME280。调试接口用SWD,加一个USB CDC虚拟串口。
功耗管理方面,MCU的电源域要分开,VBAT、VDD、VDDA都要有去耦。不用的外设电源要能通过跳线断开。LED指示灯要能通过跳线断开。SWD引脚要能通过跳线断开。
PCB布局方面,MCU的电源引脚去耦电容要尽量靠近引脚。晶振要靠近MCU,负载电容要匹配。高速信号线(比如DCMI、I2S)要等长,避免反射。模拟部分和数字部分要分开,地平面要完整。
这个清单只是一个参考,具体选型要根据你的模型和应用场景调整。比如如果你的模型很小,用Cortex-M4就够了,成本和功耗都更低。如果你的模型需要向量加速,可以考虑带Helium的M55。如果你的应用需要无线连接,可以加一颗Wi-Fi或者蓝牙芯片,但要注意射频部分的布局和认证。
我在实际画板子的过程中踩过不少坑,最大的教训是不要为了省成本而砍掉调试接口。TinyML的调试本来就麻烦,如果没有方便的调试通道,排查问题会非常痛苦。另外,电源的去耦电容不要省,STM32H743在480MHz运行时对电源噪声很敏感,去耦不够会导致随机死机。最后,如果要做低功耗,一定要在画板子之前把功耗预算算清楚,每个器件的静态电流都要查数据手册确认,不要想当然。