1. 嵌入式AI到底在解决什么问题
1.1 从“传感器只会出数”到“传感器自己会判断”
过去十几年,传感器在设备里的角色非常单纯:采集物理量,输出一个电压、电流或数字量,剩下的判断全部交给上位机或者云端。温度传感器只管把热敏电阻的分压值送出来,霍尔传感器只管在磁场变化时翻转电平,颜色传感器只管把RGB原始值通过I2C吐出去。这种模式在系统规模小、实时性要求低的时候完全够用,但一旦设备数量上去、响应时间压下来,问题就暴露了。
我最早接触这类痛点是在一个电机状态监测的项目里。当时用了十几个振动传感器,全部通过RS485汇总到一块主控板上,主控再打包上传。结果采样率一高,总线就堵,主控的CPU有将近四成时间花在搬运数据上,真正做分析的算力反而被挤掉了。更麻烦的是,一旦网络抖动,数据就断档,事后想补都补不回来。
嵌入式人工智能(Embedded AI)改变的就是这个分工。它把推理能力直接下沉到传感器节点或者紧邻传感器的MCU上,让设备在本地完成“感知—判断—动作”的闭环。传感器不再只是“出数”,而是“出结论”。振动传感器输出的不再是原始波形,而是“轴承外圈故障概率0.87”;声音传感器输出的不再是PCM流,而是“玻璃破碎事件已确认”。这个转变带来的直接好处有三个:带宽降下来了、响应快上去了、隐私数据不出本地了。
1.2 TinyML为什么偏偏盯上了MCU
提到嵌入式AI,就绕不开TinyML这个词。它的核心思路很朴素:把本来跑在服务器上的神经网络,压缩到KB级别的内存里,让一颗几块钱的MCU也能做推理。为什么是MCU而不是应用处理器?因为绝大多数传感器节点的预算就摆在那里——功耗要微安级、成本要压到最低、封装要小到能塞进探头里。你不可能在每个温度探头里塞一颗Cortex-A核。
MCU的典型资源是什么样?拿常见的Cortex-M4来说,主频几十到一百多兆,SRAM通常几十到几百KB,Flash从256KB到2MB不等。TinyML要做的就是在这个笼子里跳舞。一个用于关键词唤醒的卷积网络,参数量可以压到10KB以内;一个用于异常检测的自编码器,权重加激活值可以控制在50KB以下。这些数字不是理论值,是我在实际项目里反复验证过的量级。
这里有个容易被忽略的点:MCU内部的Flash访问接口和RAM的访问速度差异很大。Flash通常走的是类似SPI的间接访问通道,有等待周期,而RAM是直接挂在总线上的。所以模型权重的存放位置会直接影响推理延迟。我一般会把最频繁访问的权重层放在RAM里,把不常动的层留在Flash,用XIP(就地执行)的方式跑。这个取舍后面会详细展开。
1.3 哪些场景真的需要“传感器+AI”而不是“传感器+云”
不是所有场景都值得上嵌入式AI。我的判断标准很简单:如果数据传上去再传下来,延迟能接受、带宽不心疼、隐私不敏感,那就老老实实上云。反过来,下面这几类场景,本地推理几乎是唯一解。
第一类是实时闭环控制。比如无人机的姿态稳定,IMU数据必须在毫秒级完成融合和修正,你不可能等云端返回。第二类是带宽受限或成本敏感。一个农业大棚里布几百个土壤传感器,每个都传原始数据,流量费和网关成本会吃掉利润。第三类是隐私敏感。可穿戴设备采集的PPG信号、居家养老场景里的活动传感器,原始数据涉及个人生理和行为信息,本地处理完只上传事件标签是更稳妥的做法。第四类是离线可用。工业现场很多地方网络不稳定,设备必须能在断网时继续工作。
注意:判断是否上嵌入式AI,先问自己“如果网络永远断开,这个设备还能不能完成核心功能”。如果答案是能,那本地推理就是加分项;如果不能,那本地推理就是必选项。
2. 核心技术点拆解:从模型到MCU的完整链路
2.1 模型选型:不是越小越好,而是越匹配越好
很多人一上来就想把ResNet塞进MCU,这是方向性错误。嵌入式AI的模型选型要同时看三个维度:输入数据的物理特性、任务的复杂度、MCU的资源上限。
对于传感器数据,输入通常是时序信号,不是图像。这意味着全连接网络和一维卷积往往比二维卷积更合适。我做过一个声音事件检测的项目,输入是1秒的音频,采样率16kHz,如果直接做FFT再送进二维卷积,参数量轻松超过200KB。后来改成先做MFCC特征提取,再用一维卷积处理,参数量压到30KB,准确率只掉了不到两个百分点。
另一个关键决策是用分类还是用异常检测。分类需要每个类别都有标注数据,异常检测只需要正常数据。工业场景里故障样本往往很少,异常检测的自编码器结构就更实用。它的逻辑是:用正常数据训练一个网络,让它学会重构正常信号,推理时如果重构误差超过阈值,就判定为异常。这个思路在轴承故障、电机异响、管道泄漏等场景里我都验证过,效果稳定。
2.2 量化:把浮点模型变成整数模型的关键一步
训练出来的模型通常是float32的,直接放到MCU上跑,速度和内存都吃不消。量化就是把权重和激活值从浮点映射到int8甚至int4的过程。这一步做得好不好,直接决定模型能不能落地。
量化的核心是确定缩放因子和零点。简单说,就是把浮点范围线性映射到整数范围。比如权重范围是[-1.2, 1.2],映射到int8的[-128, 127],缩放因子就是1.2/127≈0.00945。推理时用整数乘加,最后再反量化回浮点。听起来简单,但坑很多。
最大的坑是激活值的动态范围。权重是固定的,激活值却随输入变化。如果校准数据选得不好,某些层的激活值会溢出,导致精度断崖式下跌。我的做法是:用一批有代表性的真实传感器数据做校准,覆盖正常和边缘情况,然后逐层检查激活值的分布。如果某一层的动态范围特别大,就考虑把它单独保留为float16,或者调整网络结构。
| 量化方案 | 权重精度 | 激活精度 | 内存占用 | 适用场景 |
|---|---|---|---|---|
| 全浮点 | float32 | float32 | 100% | 资源充足的MCU |
| 混合量化 | int8 | float16 | 约40% | 精度敏感任务 |
| 全整型 | int8 | int8 | 约25% | 大多数传感器任务 |
| 极低比特 | int4 | int8 | 约15% | 极简分类任务 |
2.3 内存布局:Flash和RAM的取舍艺术
MCU的存储结构和PC完全不同。Flash大但慢,RAM快但小。模型部署时,权重、激活值、中间缓冲区怎么放,直接影响推理速度和能否跑起来。
我的经验法则是:权重放Flash,激活值和频繁访问的层放RAM。具体来说,第一层和最后一层的权重访问最频繁,可以复制到RAM;中间层权重留在Flash,用XIP方式读取。激活值缓冲区必须放RAM,而且要尽量复用。比如卷积层的输出缓冲和下一层的输入缓冲可以是同一块内存,只要计算顺序安排得当。
还有一个细节是DMA和CPU的配合。传感器数据通过I2C或SPI进来,如果用CPU轮询读取,会浪费大量算力。正确做法是用DMA把数据搬到RAM缓冲区,CPU只在缓冲区满时被中断唤醒,然后开始推理。这样CPU可以在数据采集期间休眠,功耗能降一个数量级。
2.4 推理引擎选型:TFLite Micro、CMSIS-NN还是自研
市面上主流的MCU推理框架有几个:TensorFlow Lite for Microcontrollers、CMSIS-NN、MicroTVM,还有一些厂商自带的NPU加速库。选哪个取决于你的MCU架构和团队熟悉度。
TFLite Micro的优点是生态好、算子全、文档多,缺点是代码体积偏大,对极小内存的MCU不太友好。CMSIS-NN是ARM官方针对Cortex-M系列优化的库,算子经过手写汇编优化,速度很快,但只支持ARM架构。如果你的MCU是RISC-V或者别的架构,就得找对应的方案。
我个人的选择逻辑是:Cortex-M且追求极致性能,用CMSIS-NN;需要快速原型验证,用TFLite Micro;有特殊算子需求,考虑自研或MicroTVM。不管选哪个,都要做一件事:实测推理时间和内存峰值。厂商标称的数据往往是在理想条件下测的,实际项目里传感器数据搬运、中断响应、其他任务调度都会影响。
3. 实操过程:从零搭建一个传感器AI节点
3.1 硬件选型与传感器接口确认
假设我们要做一个基于振动传感器的电机异常检测节点。硬件选型的第一步是确定传感器输出类型。振动传感器常见的有模拟电压输出、IEPE输出、数字SPI输出。模拟输出需要MCU的ADC采样,IEPE需要额外的恒流源电路,数字输出最省事但成本高。
我一般优先选数字输出的传感器,比如带SPI接口的MEMS加速度计。原因很简单:模拟链路引入的噪声、温漂、ADC量化误差都会影响模型效果,数字输出把这些不确定性都封装在传感器内部了。选定传感器后,要仔细读手册,确认三件事:输出数据速率、接口时序、供电要求。
以某款常见SPI加速度计为例,它支持最高3.2kHz的输出速率,SPI时钟最高10MHz。MCU这边要确认SPI外设能不能跑到这个速率,DMA通道够不够用。如果MCU的SPI最高只有8MHz,那就得降速,或者换传感器。这些细节在选型阶段就要确认,不然画完板子才发现跑不起来,返工成本很高。
3.2 数据采集与标注的实操细节
数据质量决定模型上限。传感器AI项目里,数据采集往往比模型训练更耗时。我的做法是分三步走:先采正常数据,再采异常数据,最后采边界数据。
正常数据要覆盖设备的各种正常工况。比如电机,要包括空载、半载、满载,不同转速,不同环境温度。异常数据如果拿不到真实故障,可以用故障模拟台或者人为注入故障。边界数据是最容易被忽略的:设备刚启动时的瞬态、负载突变时的过渡过程、传感器受到冲击时的饱和输出。这些数据不参与训练,但用来测试模型的鲁棒性。
标注环节,如果是分类任务,每个样本要打标签;如果是异常检测,只需要标记正常和异常。我习惯在采集时就做好时间戳和工况记录,后期标注时能省很多事。数据量方面,每个类别至少要有几百个样本,异常检测的正常样本要上千。太少的话,模型学不到足够的模式。
3.3 模型训练与转换的完整流程
训练通常在PC或服务器上完成。框架选TensorFlow或PyTorch都行,我偏向TensorFlow,因为后续转TFLite比较顺。网络结构从简到繁:先试全连接,不行再上一维卷积,再不行考虑加残差连接。
训练时要注意数据增强。传感器数据不能像图像那样随便旋转裁剪,但可以加噪声、做时间偏移、调整幅度。这些增强手段能显著提升模型在真实环境里的泛化能力。另一个关键是验证集划分。不能随机划分,要按工况划分。比如用空载数据训练,用满载数据验证,这样测出来的准确率才反映真实泛化能力。
训练完成后,转成TFLite格式,再做量化。量化时用一批真实数据做校准,然后转成C数组或者二进制文件,嵌入到MCU工程里。这一步的常见问题是算子不支持。TFLite Micro支持的算子有限,如果用了自定义算子,转换会失败。解决办法是训练时就用支持的算子,或者自己实现缺失的算子。
# 量化转换示例 import tensorflow as tf converter = tf.lite.TFLiteConverter.from_saved_model('model') converter.optimizations = [tf.lite.Optimize.DEFAULT] converter.representative_dataset = representative_data_gen converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8] converter.inference_input_type = tf.int8 converter.inference_output_type = tf.int8 tflite_quant_model = converter.convert() with open('model_int8.tflite', 'wb') as f: f.write(tflite_quant_model)3.4 部署到MCU与实时性调优
部署阶段,先把模型文件加入工程,然后初始化推理引擎,分配内存缓冲区。TFLite Micro需要一块arena内存,所有中间张量都从这里分配。arena大小要实测,太小会初始化失败,太大浪费RAM。我的做法是从小往大试,找到能跑通的最小值,再留20%余量。
实时性调优有几个抓手。第一是降低推理频率。振动信号不需要每毫秒推理一次,可以每100ms推理一次,中间的数据用滑动窗口累积。第二是优化算子。如果用了CMSIS-NN,确认所有算子都走了加速路径。第三是任务优先级。推理任务不能阻塞传感器采集,要用RTOS的任务优先级和信号量做好协调。
实测时我会用GPIO翻转来测推理时间。在推理开始和结束各翻转一次IO,用示波器看脉宽。这个方法比软件计时准,因为不受中断影响。一个典型的int8一维卷积网络,在100MHz的Cortex-M4上,推理时间在几毫秒到几十毫秒之间,具体取决于网络深度和输入长度。
4. 常见问题与排查技巧实录
4.1 模型精度在PC上很好,到MCU上就崩了
这是最典型的问题,原因通常有三个。第一是量化误差,PC上是float32,MCU上是int8,精度损失是必然的。解决办法是检查每一层的量化误差,对敏感层做混合量化。第二是数据预处理不一致。PC上训练时用的归一化参数,部署时要原样搬过去,不能重新算。第三是传感器数据在MCU上的读取顺序和PC上不一致,比如字节序、通道顺序。
排查方法:在MCU上把推理前的输入数据通过串口打印出来,和PC上的输入做逐字节对比。如果输入一致但输出不一致,那就是量化或算子问题;如果输入就不一致,那就是采集或预处理问题。
4.2 推理时间忽长忽短,偶尔超时
这种抖动通常来自中断干扰或内存竞争。MCU上如果有其他高优先级中断,比如通信中断,会打断推理过程。解决办法是把推理任务放在足够高的优先级,或者把推理拆成多个小步骤,在步骤之间响应中断。
另一个原因是Cache和Flash等待周期。如果MCU有指令Cache,推理代码的布局会影响命中率。把热点函数放到RAM里执行,或者用编译器属性把关键函数对齐,能减少抖动。我遇到过因为Flash等待周期导致的推理时间翻倍,后来把模型权重搬到RAM就稳定了。
4.3 传感器数据漂移导致模型误判
传感器会随温度、时间漂移,模型训练时的数据分布和实际运行时的分布不一致,误判就来了。解决办法有两个:一是定期用正常数据做在线校准,调整归一化参数;二是训练时加入漂移数据,让模型学会忽略缓慢变化。
我在一个气体检测项目里遇到过MQ3酒精传感器基线漂移的问题。后来在预处理阶段加了一个滑动平均基线跟踪,把慢变分量减掉,只保留快变分量送进模型,误报率降了八成。
4.4 内存不够,模型放不下
这是资源受限MCU的常态。解决思路按优先级排:先做量化,int8不行就int4;再剪枝,去掉不重要的连接;再换更小的网络结构;最后才考虑换MCU。剪枝时要注意,非结构化剪枝虽然压缩率高,但需要稀疏计算支持,很多MCU推理库不支持。结构化剪枝更实用,直接去掉整个通道或层。
| 问题现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 推理结果全为同一类 | 输入未归一化 | 打印输入数据 | 检查预处理代码 |
| 推理时间超预期 | 算子未加速 | 查看推理库日志 | 启用CMSIS-NN |
| 初始化失败 | arena内存不足 | 逐步增大arena | 优化模型或换MCU |
| 精度骤降 | 量化校准差 | 逐层对比输出 | 混合量化或换校准集 |
| 运行一段时间后死机 | 内存泄漏 | 监控堆栈使用 | 检查动态分配 |
4.5 实操心得:几个让我少走弯路的习惯
第一个习惯是先跑通再优化。不要一上来就追求极致压缩,先用float32在PC上验证模型可行性,再逐步量化、剪枝、部署。每一步都保留可回退的版本。
第二个习惯是保留原始数据。传感器数据采集成本高,原始数据一定要存档。模型迭代时,同一批数据可以反复用,重新标注也比重采便宜。
第三个习惯是在目标硬件上测,不在开发板上测。开发板的电源、时钟、外围电路和最终产品往往不一样,推理功耗和稳定性可能有差异。我吃过这个亏,开发板上跑得好好的,到产品上因为电源纹波导致ADC噪声增大,模型误判率上升。
第四个习惯是给模型留余量。准确率不要追求99%,留几个点的余量给实际环境的噪声和漂移。一个在测试集上95%的模型,实际部署后可能只有90%,这很正常。关键是要有在线监控和回退机制,模型置信度低时切换到规则判断。
5. 嵌入式AI的边界与延伸
5.1 什么任务不该交给MCU
嵌入式AI不是万能的。有些任务MCU做不了,硬做就是给自己找麻烦。比如大词汇量语音识别、高分辨率图像分类、复杂自然语言处理,这些任务的模型参数量动辄几十MB,MCU的Flash和RAM根本装不下。强行量化到int4,精度会崩到不可用。
我的判断标准是:如果模型参数量超过MCU Flash的50%,或者推理时间超过任务周期的50%,就应该考虑换方案。换方案不一定是换更贵的MCU,也可以是换架构——把重任务放网关或边缘服务器,MCU只做轻量预处理和事件触发。
5.2 从单传感器到多传感器融合
单个传感器的信息量有限,多传感器融合能显著提升判断准确率。比如电机监测,振动加温度加电流,三个模态融合后,故障识别率比单振动高十几个百分点。但融合也带来新问题:数据对齐、时间同步、融合模型的资源开销。
我的做法是分层融合。底层每个传感器各自做轻量推理,输出特征或初步结论;中间层做特征拼接或投票;顶层做最终决策。这样每层的计算量都可控,也方便单独调试。
5.3 在线学习与自适应
固定模型部署后,环境变化会导致性能下降。在线学习让模型在运行中持续更新,但MCU上做反向传播不现实。折中方案是只更新最后一层,或者用无梯度的自适应方法,比如调整归一化参数、更新异常检测阈值。
我在一个可穿戴项目里用过阈值自适应:模型输出异常分数,系统根据最近一段时间的分数分布动态调整判定阈值。这样即使传感器灵敏度随佩戴位置变化,误报率也能保持稳定。
5.4 工具链与生态的现状
嵌入式AI的工具链还在快速演进。训练侧有TensorFlow、PyTorch,转换侧有TFLite Converter、ONNX,部署侧有TFLite Micro、CMSIS-NN、Edge Impulse。每个环节都有坑,但整体趋势是越来越顺。
我的建议是跟着主流走。不要为了追求极致性能去用冷门框架,除非你有专门的团队维护。主流框架的社区活跃,遇到问题能搜到答案,算子支持也全。等产品稳定了,再考虑针对性优化。
最后分享一个我在多个项目里验证过的经验:嵌入式AI项目的成败,七成在数据和需求定义,两成在模型,一成在部署。很多人把精力花在调模型上,其实先把“要解决什么问题、数据怎么采、怎么判断成功”想清楚,后面会顺很多。传感器不会说话,但它的数据会。嵌入式AI要做的,就是让设备听懂这些话。