微控制器(MCU)与人工智能(AI)的深度融合,正在开启一个令人振奋的技术新纪元。过去,AI应用几乎离不开大型服务器或云端算力的支撑,但如今情况已截然不同——我们可以将AI直接嵌入微控制器之中。这意味着嵌入式系统、物联网设备、机器人乃至更多场景,都将拥有前所未有的智能化能力。
MCU AI的崛起
所谓MCU AI,本质上就是将机器学习和深度学习模型部署到资源有限但功能日益强大的微控制器上,使其能够自主思考、感知并做出决策。说起来简单,但要实现这一目标,必须跨越几个关键门槛:
- 低功耗 —— 微控制器大多依靠电池供电,节电是首要任务。AI模型必须经过特殊优化,才能在保证推理效果的同时,将能耗压至最低。
- 实时性 —— 这些芯片常出现在实时控制系统中,模型必须在毫秒甚至微秒级内完成计算,才能应对现场的突发需求。
- 感知与决策 —— MCU AI赋予了设备“感知环境、思考问题、执行动作”的能力,这对自主机器人、智能传感器和自动控制系统而言,几乎是刚性需求。
MCU AI的应用场景
这项技术可以应用于哪些领域?以下列举几个典型实例:
- 智能物联网设备 —— 智能家居、智能灯具、智能门锁,在加载MCU AI后,可学习用户习惯,自动适应环境变化,不再只是被动执行的“工具”。
- 自主机器人 —— 扫地机器人、无人机,依靠微控制器上的AI即可实现避障、路径规划与任务执行,无需每次都把数据上传至云端。
- 医疗设备 —— 实时监测患者生命体征,遇到异常能提前发出警报,为护理争取宝贵时间。
- 工业自动化 —— 工业机器人、产线质检、自动化控制系统,MCU AI让效率与质量双双提升。
本篇文章将详细介绍如何将TinyMaix这个轻量级推理框架移植到RT-Thread上,并使其真正运行起来。
TinyMaix
TinyMaix是矽速科技(Sipeed)利用两个周末的业余时间完成的项目——没错,它诞生于一个疯狂又有趣的周末。这是一款专为微控制器设计的轻量级开源机器学习库,或者说,是面向单片机的超轻量级神经网络推理库。其目标十分明确:让你在任意单片机上都能运行轻量级深度学习模型。
开源代码链接:https://github.com/sipeed/tinymaix
TinyMaix关键特性
- 核心代码少于400行(tm_layers.c + tm_model.c + arch_cpu.h),代码段(.text)小于3KB
- 内存消耗极低——甚至Arduino ATmega328(32KB Flash,2KB RAM)都能运行mnist手写数字识别
- 支持INT8/FP32/FP16模型,实验性支持FP8模型,可转换keras h5或tflite模型
- 针对多种芯片架构提供专用指令优化:ARM SIMD/NEON/MVEI、RV32P、RV64V
- 用户接口简洁友好,只需加载(load)和运行(run)模型即可
- 支持全静态内存配置(无需malloc)
- 即将支持MaixHub在线模型训练
TinyMaix底层依赖
可以将TinyMaix简单理解为一个矩阵和向量计算库。目前支持以下计算硬件:
#define TM_ARCH_CPU (0) //default, pu re cpu compute
#define TM_ARCH_ARM_SIMD (1) //ARM Cortex M4/M7, etc.
#define TM_ARCH_ARM_NEON (2) //ARM Cortex A7, etc.
#define TM_ARCH_ARM_MVEI (3) //ARMv8.1: M55, etc.
#define TM_ARCH_RV32P (4) //T-head E907, etc.
#define TM_ARCH_RV64V (5) //T-head C906,C910, etc.
#define TM_ARCH_CSKYV2 (6) //cskyv2 with dsp core
#define TM_ARCH_X86_SSE2 (7) //x86 sse2
对于ARM-Cortex系列MCU,可选用纯CPU计算或SIMD计算。纯CPU部分没有特殊依赖,全部用标准C实现;SIMD部分则采用了C语言内嵌汇编,需要CPU支持对应指令集才能正常编译和运行。
TinyMaix等级选择
TinyMaix目前提供两种等级供选择:
- TM_OPT0(默认):最小化代码量和缓冲区
- TM_OPT1:追求速度,需要更多代码和缓冲区
#define TM_OPT0 (0) //default, least code and buf
#define TM_OPT1 (1) //opt for speed, need more code and buf
#define TM_OPT2 (2) //TODO
TinyMaix量化
支持不同位宽的量化方式:
#define TM_MDL_INT8 0
#define TM_MDL_INT16 1
#define TM_MDL_FP32 2
#define TM_MDL_FP16 3
#define TM_MDL_FP8_143 4 //experimental
#define TM_MDL_FP8_152 5 //experimental
TinyMaix核心API
框架对外提供的主要API集中在tinymaix.h中,包括模型加载、卸载、预处理和推理四个核心函数:
/******************************* MODEL FUNCTION ************************************/
tm_err_t tm_load (tm_mdl_t* mdl, const uint8_t* bin, uint8_t*buf, tm_cb_t cb, tm_mat_t* in); //load model
void tm_unload(tm_mdl_t* mdl); //remove model
tm_err_t tm_preprocess(tm_mdl_t* mdl, tm_pp_t pp_type, tm_mat_t* in, tm_mat_t* out); //preprocess input data
tm_err_t tm_run (tm_mdl_t* mdl, tm_mat_t* in, tm_mat_t* out); //run model
此外还有统计函数,用于输出模型中间层信息:
/******************************* STAT FUNCTION ************************************/
#if TM_ENABLE_STAT
tm_err_t tm_stat(tm_mdlbin_t* mdl); //stat model
#endif
工具函数则包含FP32与uint8的相互转换:
/******************************* UTILS FUNCTION ************************************/
uint8_t TM_WEAK tm_fp32to8(float fp32);
float TM_WEAK tm_fp8to32(uint8_t fp8);
这里的模型,通常是指预训练模型经过脚本转换后生成的TinyMaix格式文件。
TinyMaix移植到RT-Thread
移植的工作量其实不大,主要就是适配tm_port.h这个文件。RT-Thread的配置使用的是Kconfig,所以我们把硬件类型、等级选择、量化类型都改成了Kconfig可配置项:
#define TM_ARCH R_TINYMAIX_USING_ARCK_TYPE
#define TM_OPT_LEVEL R_TINYMAIX_USING_OPTION_LEVEL
#define TM_MDL_TYPE R_TINYMAIX_USING_MODULE_TYPE
TinyMaix需要对接平台的内联、内存、打印等接口,我们把对应的宏定义适配到RT-Thread平台的接口上:
#define TM_INLINE rt_inline
#define TM_WEAK rt_weak
#define tm_malloc(x) rt_malloc(x)
#define tm_free(x) rt_free(x)
#define TM_PRINTF(...) rt_kprintf(__VA_ARGS__)
调试功能依赖精准计时,TinyMaix用了几组宏来测量时间。由于RT-Thread系统没有提供微秒级接口,只有毫秒级,我们做了一点简单的适配:
#define TM_GET_US() rt_tick_get_millisecond() / 1000;
#define TM_DBGT_INIT() uint32_t _start,_finish; float _time; _start = TM_GET_US();
#define TM_DBGT_START() _start = TM_GET_US();
#define TM_DBGT(x) { _finish=TM_GET_US(); _time = (float)(_finish-_start) / 1.0; TM_PRINTF("===%s use %.3f ms", (x), _time); _start=TM_GET_US(); }
TinyMaix提供了多个示例,比如cifar10、mnist、vww。RT-Thread支持命令行输入,为了让这些示例能通过命令运行,需要改一下文件名和接口名字:把examples目录下三个示例的main.c分别重命名为cifar10.c、mnist.c、vww.c,同时将里面的main函数改成对应示例名字:
int cifar10(int argc, char** argv)
int mnist(int argc, char** argv)
int vww(int argc, char** argv)
最后将这些接口导出到命令行中:
MSH_CMD_EXPORT(cifar10, TinyMaix cifar10 example);
MSH_CMD_EXPORT(mnist, TinyMaix mnist example);
MSH_CMD_EXPORT(vww, TinyMaix vww example);
TinyMaix运行效果
测试环境:STM32F401RE,M4内核,时钟频率84MHz,RAM 96KB,Flash 512KB。
- cifar10示例:分类检测,识别图片为一只鸟。

- mnist示例:数字识别,图片上的数字是2。


- vww示例:检测画面中是否有人,结果判定有人。

总结
值得一提的是,TinyMaix的作者已经开发了一个RT-Thread的软件包:r-tinymaix。在RT-Thread工程中直接加入这个软件包,就能快速验证。TinyMaix确实非常出色——它让一个普普通通的单片机拥有了AI能力,将嵌入式AI的成本门槛大幅降低。
