1. 先搞清楚“嵌入式AI”到底指的是什么:它不是一个芯片,而是一套新的干活方式
先聊个现实问题。很多搞了几年单片机的朋友,包括曾经的我,第一次听到“嵌入式AI”这个词时,脑子里冒出来的画面是:一个很贵的开发板,上面插着摄像头,然后板子能识别出猫和狗,屏幕上还画着框。但真要问“它跟我平时用Keil写个流水灯、用STM32CubeMX配个串口到底有什么区别”,十有八九会卡壳。
先给个结论:嵌入式AI不是某颗特定的芯片,也不是某个IDE,它是一套完整的、从数据处理到模型部署的工程方法论。它的核心是把在PC上训练好的神经网络模型,压缩、量化、转换之后,部署到资源受限的嵌入式设备上,让设备能“感知”数据(图像、声音、振动、传感器序列),然后直接做出判断。
而我们平时写的STM32程序,无论是用寄存器开发还是用HAL库,本质上是在做另一件事:用明确的逻辑规则去控制硬件。如果电压高于阈值,就打开继电器;如果定时器计数到了,就翻转IO口。这些逻辑是人一行一行写出来的,每个分支都是确定的、可预期的。
这两种开发方式差的不是“谁更高级”,而是解决问题的思路完全不同。传统嵌入式是“人告诉机器怎么做”,嵌入式AI是“人告诉机器怎么学,然后机器自己总结出怎么做”。用大白话讲:你写一个红绿灯控制程序,你得一条一条地写“红灯亮30秒→绿灯亮20秒→黄灯闪3秒”,而如果你做一个拍照识别红绿灯的程序,你不是去写“怎么判断一张图里是红灯”的规则,而是拿几千张红绿灯照片去“喂”一个模型,让它自己去学“红灯长什么样”。
这个差异看起来只是思路不同,实际上牵扯到开发流程、工具链、芯片选型、调试方式和性能优化手法的全面变化。后面我会把每一块都拆开讲。
2. 传统STM32开发和嵌入式AI开发的本质差别:写规则 vs 调参数
2.1 传统单片机程序:一切尽在掌控中的确定逻辑
先看我们最熟悉的东西。STM32开发的核心是“寄存器操作+外设驱动+中断服务”。你翻开参考手册,找到某个外设的寄存器地址,然后把相应的位写0或写1,硬件就会按照你设置的模式工作。这个过程非常“硬核”,但好处是逻辑完全确定。
举个例子,我要做一个简单的温控风扇:
// 伪代码,示意传统单片机编程思路 uint16_t adc_value = read_ADC(); // 读取温度传感器电压 if(adc_value > TEMP_THRESHOLD_HIGH) { FAN_ON(); // 风扇全速 } else if(adc_value > TEMP_THRESHOLD_LOW) { FAN_SET_SPEED(50); // 风扇半速 } else { FAN_OFF(); }这段代码的工作方式就是:读一个值,跟预设的阈值比较,执行一个动作。整个程序的状态空间是有限的、可穷举的。你甚至可以用穷举法把所有输入组合全测一遍,程序运行的对不对,黑盒测试就能判断。
传统嵌入式开发的调试手段也很成熟:断点、单步、串口打印、逻辑分析仪抓波形。你看到某个变量值不对,顺藤摸瓜就能找到问题在哪一行代码。整条链路是透明的。
2.2 嵌入式AI程序:模型内部是一个“说不清”的黑盒
再来看嵌入式AI的典型工作方式。你用STM32的DCMI接口接一个摄像头,把图像数据采集进来,然后送进一个跑在MCU上的神经网络模型里,模型吐出一个分类结果:Cat(猫),置信度0.92。
这个“Cat”是怎么来的?模型内部有几百层卷积、池化、激活函数的运算,每一层的权重参数是训练阶段自动学出来的,不是人写的规则。你说不清“为什么这张图是猫”到底是因为模型的哪个参数,你只知道模型整体表现很好,在测试集上准确率有95%。
这里就有一个很多传统单片机工程师一开始特别不舒服的点:代码的运行结果不再100%可控。同样一张图片,换一个角度,识别可能就从Cat变成了Dog。模型在某些输入下可能给出错误输出,而且这个错误可能很难复现、很难定位。传统代码的世界里没有这个概念。
为了更直观地对比,我列个表格:
| 对比维度 | 传统STM32开发 | 嵌入式AI开发 |
|---|---|---|
| 核心工作 | 编写明确规则、控制外设 | 准备数据、训练模型、部署推理 |
| 程序行为 | 完全确定,可穷举验证 | 统计确定,存在误判率 |
| 主要工具 | Keil/IAR、CubeMX、调试器 | Python、TensorFlow/PyTorch、模型转换工具 |
| 调优方式 | 改代码、改阈值、改时序 | 调超参数、增加数据、改网络结构 |
| 硬件要求 | MCU即可,靠外设能力 | 需要有足够算力和内存跑神经网络的芯片 |
| 调试维度 | 看变量、抓波形、断点 | 看loss曲线、看混淆矩阵、分析推理耗时 |
| 出错时的排查思路 | “哪段代码写错了” | “数据够不够”“模型容量够不够”“量化是否损失太大” |
这个表格基本把两种开发模式的骨架给框出来了。后面每一行我都能展开讲一大堆,这里先有个整体概念。
3. 算力的鸿沟:为什么不是随便拿个STM32就能跑AI
3.1 存储容量的现实约束
聊完思路层面的区别,马上要面对的就是“老祖宗”物理层面的问题。很多刚接触的朋友第一个疑问是:我的STM32F103C8T6有64KB Flash、20KB RAM,能不能跑个图像识别?
答案很残酷:基本跑不动稍微像样一点的模型。原因有两层:一是模型本身的体积,二是推理过程中的计算量和临时内存需求。
拿一个很小的图像分类模型举例,MobileNetV1的输入规格是224x224x3的RGB图,模型体积大约16MB,这还不包括运行时的激活值存储和中间张量缓存。即便是MobileNetV2,全精度模型也有13MB左右。而一颗STM32F103的Flash只有64KB,连模型参数的零头都不够。
那有人会问:那我把模型压缩到几十KB不就行了?这就要说到嵌入式AI的一个核心概念——量化。把一个 float32 的权重参数缩成 int8,体积就压缩到原来的四分之一。再加上剪枝(把接近0的权重删掉)、蒸馏等技术,确实能压出几KB到几十KB的小模型。但问题是:模型体积越小、越量化,精度损失就越大。宠物识别这种分类任务,压到极限后准确率可能从95%掉到70%,这在很多场景下是不可接受的。
我用一个真实数据来说明:我用TensorFlow Lite Micro在Cortex-M4内核的芯片上做过一个关键词唤醒(识别“OK Google”和“Hey Siri”这类语音词),模型参数量大约5万,量化后整体体积是18KB,读一次音频帧的推理耗时大约350ms,RAM峰值占用约20KB。这已经是MCU级别能跑的极限分类任务了。如果换成检测任务(识别图像里猫和狗的位置,需要输出框坐标),复杂度又上一个数量级,在普通MCU上基本跑不动。
3.2 CPU算力的差距:一次推理要“算”多少次
存储只是一方面,算力才是更大的瓶颈。做一次图像推理,要做是几百万次乘加运算(MAC)。一颗主频72MHz的STM32F103,理论上每秒最多能做7200万次乘加运算,听起来不少?但你算一笔账:
假设一个极简模型每帧推理需要500万次乘加运算,STM32F103算下来每帧处理量大约是:
$$t = \frac{5 \times 10^6 \text{ MAC}}{72 \times 10^6 \text{ Hz} \times 0.5 \text{ IPC(实际效率)}} \approx 0.14 \text{ 秒}$$
这只是理想值,实际模型还不止500万次MAC。这意味着你要在这个芯片上做到实时视频识别(30帧/秒)是完全不可能的。所以现实中的嵌入式AI落地,大多跑在三种硬件上:
- 带NPU(神经网络处理单元)的芯片:比如瑞萨RA8系列、ST新款N6系列、地平线旭日系列,NPU能并行做大量乘加运算。
- 带DSP的MCU:DSP指令集能加速乘加运算,但还得做大量手工优化。
- 高性能MPU(跑Linux):比如树莓派、瑞芯微RK3588、Jetson Nano这类,运算能力接近电脑。
这也解释了一个现象:为什么市面上的AI宠物喂食器、智能猫眼,几乎都用的是海思、瑞芯微、全志这类跑Linux的方案,很少见到有人在STM32F103上跑图像识别。不是没人尝试,而是硬件天花板摆在那里。
3.3 一个很重要的中间地带:MCU+NPU的混合架构
有一个趋势值得提一下:现在不少厂商在传统MCU的基础上加了一个独立的NPU模块。比如ST在2024年发布的STM32N6系列,里面带了一个算力约600GOPS的NPU,可以跑比较像样的视觉模型。这种架构的意义在于:MCU部分继续干它擅长的实时控制、外设交互,NPU部分负责处理AI推理。
这其实就是嵌入式AI领域里很常见的一个设计理念:异构计算。让合适的硬件干合适的活。控制任务用确定性很强的MCU逻辑跑,感知任务用统计性很强的神经网络跑。两者之间通过内存共享或者消息队列通信。
我在实际项目中用过的做法是:MCU通过摄像头DMA采集图像,把图像数据放在一块共享SRAM里,然后给NPU发一个启动推理的指令,NPU算完后在同样位置放上检测结果并触发一个中断,MCU这边去读取分类标签和坐标。这套流程跟传统“传感器中断→读取数据→处理→输出”的裸机编程范式非常像,只是中间多了一个“不可控的推理过程”。
4. 从训练到部署:一套完全陌生的数据流
4.1 开发流程的巨大变化:PC上训练,嵌入式设备上推理
“嵌入式”这个词本身就意味着资源受限。AI模型在PC上训练,在嵌入设备上运行,这个跨平台的流程,可能是传统嵌入式工程师最需要重新适应的地方。
传统STM32开发流程大概是这样:建工程 → 写代码 → 编译 → 下载 → 调试 → 完成。整个过程中,你开发用的工具链和最终运行的硬件是同一条“语言体系”——你写的是C,编译成ARM机器码,烧到Flash里跑。
嵌入式AI开发流程变成了:
- 数据准备:采集大量样本(例如猫狗图片),做标注、清洗、增强。
- 模型设计:在PC上用Python定义网络结构,选好损失函数和优化器。
- 训练:用GPU训练几十个epoch,观察loss收敛情况,调整超参数直到准确率达标。
- 模型转换:把PyTorch或Keras的模型转成ONNX,再做量化(int8/float16),然后转成TensorFlow Lite Micro的C数组格式,或者厂商NPU框架的自定义格式。
- 部署:把转换后的模型数组和推理库代码一起编译进嵌入式工程里。
- 评估与迭代:在目标硬件上跑真实数据,测量推理耗时、RAM占用、识别准确率,如果不满意,回到第2步或第3步再调。
4.2 模型转换这一步,坑比想象中多得多
模型转换是新人最容易翻车的地方。我在刚接触TFLite Micro时,拿一个训练好的Keras模型想部署到MCU上,按照网上的教程一步步转,结果烧进板子里一运行就HardFault。
排查了很久,最后发现是两个问题:
- 原模型里用了一个嵌入式端不支持的算子(好像是
LeakyReLU的某个参数配置),TFLite转换器虽然给过了,但MCU端的推理引擎算到这一步就崩。 - 输入数据的排布方式没搞清楚,模型期望的输入是CHW格式,而摄像头输出的图像是HWC,直接把数组喂进去,模型“看”到的是一堆乱序的像素。
这种问题就很有意思:它跟传统的“指针越界”有点像,但是表现形式完全不同。传统越界是访问了不该访问的地址,而模型输入数据排布错误是“数据在,但是位置不对”,导致模型输出结果完全是垃圾,但它不崩溃。这种bug你拿仿真器单步根本看不出问题,因为每个变量都“合理”地存在,除非你去复现模型在PC上的前向传播结果,然后跟嵌入式端逐层对比中间张量。
这里建议一个非常实用的排查方法:分阶段验证。先把模型在PC上用Python跑一遍,对同一张输入图片记录每一层的输出;然后在嵌入式设备上,把同样的输入喂给模型,逐层打印中间张量,跟PC端的结果做对比。第一层输出对不上,说明输入数据在嵌入式端就有问题;中间某一层有NaN或Inf,说明量化精度或者算子实现有bug;最后一层只有最终结果不对,大概率是后处理代码写错了。这套方法我试过很多次,比瞎猜有用一万倍。
4.3 现在主流的部署框架和厂商工具链各有什么脾气
市面上的嵌入式AI部署方案五花八门,按“出身”可以分成三类:
第一类是开源通用框架,代表是TensorFlow Lite Micro(TFLM)和CMSIS-NN。TFLM是专门为了在MCU上跑推理而裁剪和优化过的C++库,支持Cortex-M系列,配合CMSIS-NN的优化算子能发挥不小的性能。它的好处是免费、社区活跃、不绑定硬件厂商,坏处是能用的算子种类有限,且每个新模型都要重新做算子兼容性检查。
第二类是MCU厂商的自有AI工具链。ST有STM32Cube.AI和NanoEdge AI Studio,恩智浦有eIQ,瑞萨也有自己的AI工具链。这类工具链的好处是跟自家芯片绑定很深,算子优化到位,还有配套的模型压缩工具和性能评估工具。STM32Cube.AI可以直接把Keras或ONNX模型转成C代码,还能在CubeMX里生成性能估算报告(推理耗时、Flash占用、RAM占用),这个对于工程师做选型评估非常方便。
第三类是专门做NPU/IP授权的IP厂商工具链,比如Arm的Ethos-U系列NPU、Synopsys的NPX系列。这类通常配合语音唤醒芯片、视觉SoC一起出现,工具链一般是由SoC厂商二次封装。
我个人的建议是:如果是学生或者刚入门,先玩TFLM + 任意一块Cortex-M4以上的开发板,把流程跑通。如果公司项目要做落地,直接看目标芯片厂商的自有工具链,因为性能和显存优化都做得更踏实。
5. 系统开发的差异:从“裸机状态机”到“数据管道”
说完了模型层面的差异,再回到代码层面来看。传统嵌入式程序的结构是“中断+状态机”:外部事件来了触发中断,中断里设置标志位,主循环根据标志位跳转到对应的状态去处理。程序的执行路径是离散的、有明确时序的。
而嵌入式AI应用更像一条“数据管道”。以宠物检测AI模型——嵌入式设备上的猫狗实时识别这个项目为例:
// 典型的AI视觉管道伪代码 while(1) { camera_dma_start(); // 启动DMA采集一帧图像 wait_dma_done(); // 等待采集完成 image_preprocess(image_raw, image_input); // 缩放、格式转换、归一化 ai_engine_run(image_input, &result); // NPU/CPU推理 post_process(&result, &boxes, &labels); // 解析输出,做阈值过滤 draw_and_display(boxes, labels); // 叠加上画框 check_network_cmd(); // 检查上位机指令,随时可中断改参数 }这里就引出了几个传统开发里不常见的设计要点:
首先是预处理环节。你得把摄像头的原始输出格式(通常是RGB565或YUV422)转成模型期望的RGB888,还得做缩放(把高清图缩小到模型输入尺寸,比如320x320)和归一化(把0~255的像素值除255变成0~1的浮点数,或者转成int8的-128~127)。很多初学的朋友在这里犯迷糊:为什么我的模型一上板就识别不准,但在PC上测得好好的?大概率是预处理和后处理的某个环节跟训练时不一致。训练时用的是PIL库“resize+归一化”的特定方式,部署时你却用了另一个库的实现,两边的像素值分布对不上,模型的表现自然一落千丈。数据预处理的一致性,很多时候比模型本身更影响最终效果。
其次是内存管理问题。传统MCU程序里,一个全局数组几百字节就不得了了,但AI推理需要的是几MB级别的缓冲区:一帧图像原图1MB,预处理后的输入张量几百KB,中间激活值几百KB,后处理还要画框的显存。在STM32N6这样的芯片上,通常要把这些缓冲区放到外部SDRAM或者应用专用的SRAM中。这意味着你要手动管理DMA缓冲区的对齐、内存分配和生命周期,一个疏忽就可能造成缓存一致性问题(Cache Coherency),显示出现花屏或推理结果随机出错。
第三是CPU负载的转移。传统程序的中断服务函数讲究“越快越好”,最好几百纳秒内就跑完,不然影响主循环的实时性。在AI管道中,重活都在NPU或CPU的推理阶段。因此代码结构上经常要设计成“生产者-消费者模式”:DMA中断只负责“我采完一帧了”,翻译成AI的说法就是“我把图像推进了管道”,主循环或者另一个DMA通道才去执行推理。如果AI推理占用了MCU太多时间,实时控制任务(比如电机PID)就会抖,此时必须靠调整任务优先级、把AI推理放到独立核(M4+M7双核芯片)或者独立NPU上来解决。
6. 实战对比:用宠物识别这个例子把两种开发方式放在一起看
既然热搜词里有“宠物检测ai模型——嵌入式设备上的猫狗实时识别”,我就拿这个经典目标当成案例,做一个完整的两条路线对比。这事我正好做过,有参考价值。
6.1 传统方案:用“笨办法”做宠物识别会是什么样
假设你非要用STM32F407,不跑任何神经网络,去实现“识别猫和狗”。你会怎么做?
一个典型的传统嵌入式工程师思路是这样的:用传感器代替摄像头。在宠物进出通道的两侧装红外对射,左边先触发算进、右边先触发算出;用体重传感器判断胖瘦(猫平均4kg,狗平均15kg);用加速度计判断整体体型特征。然后写一堆阈值判断:
if (weight > 8.0f) { detected_species = DOG; } else if (weight < 6.0f) { detected_species = CAT; } else { detected_species = UNKNOWN; // 8kg附近太模糊,需要更多传感器 }这个方案能跑,而且实时性非常好,逻辑也完全可控。但它有个致命问题:它没有真正“识别”猫和狗,它只是在用间接物理量去猜。如果遇到一只10kg的大橘猫(现实中比比皆是),你会把它判断成狗;遇到一只5kg的小型泰迪,你又把它判成猫。传感器的特征维度太少了,根本没有“毛发纹理”“耳朵形状”“面部比例”这种真正区分物种的信息。
这恰恰是传统规则式程序的天花板:特征工程的能力有限。你只能靠人眼和经验选几个便于测量的物理量,再用阈值切分。一旦现实世界的分布超出了你预设的阈值区间,系统就失灵。而神经网络干的事情,本质上就是自动提取成千上万个特征,然后非线性地组合它们,最终得到比人工特征判断稳定得多的分类结果。
6.2 AI方案:同样的任务,开发重心完全不同
如果走嵌入式AI路线,开发流程变成了:
- 数据收集:网上爬猫狗图片,加上自己拍摄的素材,整理成几千张带标签的数据集。
- 模型选择:因为要在嵌入式设备上跑,选一个轻量级检测模型,比如EfficientDet-Lite或YOLO-Nano。
- 训练:在PC上用预训练权重做迁移学习,微调个几十个epoch,最终在验证集上达到95%以上的mAP。
- 量化与部署:转换成int8量化格式,目标平台如果是带NPU的芯片,用量化工具校准;如果目标平台是纯MCU,考虑裁剪模型或者换更小的backbone。
- 整合系统:把图像采集、预处理、推理、后处理、显示、WebSocket上报整合成一个完整项目。
这套流程下,你要写的C代码反而不多,核心逻辑就那么几十行,但Python的训练脚本、数据预处理脚本、量化校准脚本加起来可能有上千行。整个项目的复杂度从“写代码”转移到了“准备数据、调模型、优化部署”上。
6.3 我在实际宠物识别项目里踩过的坑和反思
这个项目试跑过程中有几个真实细节,分享出来给大家避坑。
第一个坑是过拟合。我一开始用的数据集只有800张图,训练时准确率99%,一上板子对着真实环境的各种光线、遮挡、角度就崩了,准确率掉到70%左右。后来加了数据增强(随机裁剪、旋转、色彩抖动),又补充了500张真实室内环境照片,才把最终准确率拉回90%以上。这让我意识到嵌入式AI里的“软件”其实很大程度是“数据工程”,数据质量直接决定模型上限,模型结构只是逼近这个上限的手段。
第二个坑是量化掉点。同样的模型,float32精度在PC上跑mAP有95%,转成int8量化后掉到了88%。后来尝试了量化感知训练(QAT),就是训练时故意模拟量化误差,才把量化后的mAP拉回到93%。很多芯片厂商的资料里都提到QAT,但真正会用的人不多,这是嵌入式AI项目能不能商业落地的关键技能之一。
第三个坑是端侧性能分析和调优的巨大耗时。模型部署上去后,我发现N PU的利用率只有35%,大量时间浪费在图像预处理和后处理上。后来用双缓冲DMA把图像DMA传输和NPU推理流水线化,推理帧率从6fps提升到了18fps。性能优化不是在模型本身挖潜,而是在整个数据管道的统筹协调上挖潜——这个话题跟传统嵌入式系统的优化思路还是有共通之处的。
7. 如果想转型做嵌入式AI,建议的学习路线和工具链准备
最后聊点实际的。如果你是一名传统STM32开发工程师,或者是在校学生,想往嵌入式AI方向发展,我认为最合适的路径不是一上来就买一块几千块的开发板,然后照着例程点灯,而是按下面这个顺序逐步渗透。
先补数学和Python基础,但不用学太深。需要掌握的数学知识主要是线性代数(矩阵乘法、张量概念)和一点点概率统计(准确率、召回率的含义)。不需要你会推反向传播公式,但你要能看懂一个神经网络的几个基本模块各自在做什么:卷积层(提取局部特征)、池化层(降采样)、全连接层(汇总分类)、激活函数(引入非线性)。
接着用Python把一次完整的训练流程跑通。在PC上装好TensorFlow或PyTorch(推荐PyTorch,生态更活跃,网上教程也多),用官方教程里的MNIST手写数字识别或者猫狗二分类练手。重点不是调出一个多好的模型,而是体会三件事:数据集怎么组织、训练过程怎么看(loss曲线)、模型怎么保存和导出。
然后进入嵌入式部署环节。这一步建议直接用你手头已有的、主频不低于100MHz的开发板(比如STM32F407、F767、H743),下载好STM32Cube.AI或TFLite Micro的示例,把一个“图像分类”的小模型烧进去,用串口打印出识别结果。只要你把“PC上训练→嵌入式推理”这条链路完全跑通,你就已经比别人领先一大半了。
再就是模型选型和系统架构设计能力。进阶阶段,你要学会根据“具体任务需要什么效果、功耗和成本允许到多少、可用算力和内存是多少”来反推模型选型。比如“宠物识别”这种检测任务跟“关键词唤醒”这种语音任务,对模型结构的要求完全不同。多看点行业应用案例,心里对“什么芯片能跑什么模型”形成一个估算能力,比会写代码更重要。
工具链方面,目前阶段我建议这几个组合:
- PC端模型训练:PyTorch + Google Colab(免费GPU),如果公司有预算就本地搭训练服务器。
- 模型通用交换格式:ONNX(几乎所有框架都能转)。
- 嵌入式部署:根据目标芯片选,ST芯片用STM32Cube.AI,其他MCU类芯片用TFLite Micro + CMSIS-NN,带NPU的SoC芯片用厂商SDK(一般是改过的TFLite)。
- 端侧调试工具:STM32CubeMonitor(看变量和推理输出)、串口调试助手、J-Link RTT(低开销日志输出,强烈推荐,AI模型部署阶段的调试全靠它)。
8. 写在最后的话:这是一次开发范式的切换,不只是学新工具
我做完宠物识别项目之后有个很深的感触:嵌入式AI跟传统STM32开发之间最大的门槛,不是技术细节,而是“接受不确定性”的心态转换。传统开发里,程序错了就是代码有bug,修完就对;但AI世界里,模型错了你不知道是哪错了,可能是数据、可能是超参数、可能是模型容量、可能是量化策略,排查链路完全不一样。
如果你之前只有纯MCU经验,想转型,千万别慌。你对硬件、对中断、对内存、对实时性的理解,是很多纯AI工程师不具备的宝贵技能。嵌入式AI这个领域真正缺的是“既懂模型又懂硬件”的复合型人才。你缺的只是模型训练和部署那一环的经验,这部分有大量现成的开源资料和工具可以用,补起来其实没有你想象的那么慢。
我自己也是从“只会写裸机状态机”一步步摸索过来的。踩过上面的坑,才更理解为什么有人说嵌入式AI不是一个新技术,而是一套全新的思维框架。希望这篇东西能帮刚开始接触这块的朋友少走点弯路。
如果你正准备入坑,我的建议还是那句话:先找一个具体的小任务,比如“猫狗识别”“手势识别”或者“关键词唤醒”,把从数据到部署的完整流程跑通,再回来读理论,你会发现自己看什么资料都顺畅了。实践永远是这行最好的老师。