RT-Thread上部署TinyMaix推理框架,使MCU赋予AI能力

2023-11-06 17:10:18 浏览数 (1)

概要

当谈到微控制器(MCU)和人工智能(AI)的结合,我们进入了一个激动人心的领域。传统上,AI应用程序需要大型计算机或云服务器的处理能力,但随着技术的发展,现在可以将AI嵌入到微控制器中。这为嵌入式系统、物联网设备、机器人和各种其他应用开启了新的可能性。

MCU AI的崛起

MCU AI代表着微控制器上的人工智能。它是将机器学习和深度学习模型部署到资源有限但功能强大的微控制器中,以实现智能决策和感知。以下是MCU AI的一些关键方面:

  • 低功耗: 微控制器通常以电池供电,因此低功耗是至关重要的。AI模型需要经过优化,以在微控制器上运行,同时尽量减小能耗。
  • 实时性: 微控制器常常用于实时控制系统,因此AI模型需要在极短的时间内执行,以应对即时需求。
  • 感知和决策: MCU AI可以使设备具备感知环境、分析数据并作出决策的能力。这对于自主机器人、智能传感器和自动控制系统尤为有用。
MCU AI的应用

MCU AI可以应用于各种领域,下面是一些示例:

  • 智能物联网设备: 微控制器上的AI可以使物联网设备更加智能,例如智能家居设备、智能灯具和智能门锁。它们可以学习用户的偏好,并自动适应不同环境。
  • 自主机器人: 微控制器上的AI使自主机器人能够避障、规划路径和执行任务,例如清扫机器人和无人机。
  • 医疗设备: 在医疗设备中,MCU AI可以用于监测患者的生命体征,提供早期警报和更好的病人护理。
  • 工业自动化: 微控制器上的AI可用于工业机器人、自动化生产线和质量控制系统,提高效率和质量。

作者开始深入进嵌入式AI这个领域,不过学习之前先了解如何用起来,跑起来。本篇文章聊一下如何移植TinyMaix推理框架到RT-THREAD并运行起来。

TinyMaix

TinyMaix:是矽速科技(Sipeed)利用两个周末的业余时间完成的项目,它是一款专为微控制器设计的轻量级开源机器学习库,面向单片机的超轻量级的神经网络推理库,即TinyML推理库,可以让你在任意单片机上运行轻量级深度学习模型。TinyMaix开源代码链接:https://github.com/sipeed/tinymaix。

TinyMaix关键特性
  • 核心代码少于 400行(tm_layers.c tm_model.c arch_cpu.h), 代码段(.text)少于3KB
  • 低内存消耗,甚至 Arduino ATmega328 (32KB Flash, 2KB Ram) 都能基于 TinyMaix 跑 mnist(手写数字识别)
  • 支持 INT8/FP32/FP16 模型,实验性地支持 FP8 模型,支持 keras h5 或 tflite 模型转换
  • 支持多种芯片架构的专用指令优化: ARM SIMD/NEON/MVEI,RV32P, RV64V
  • 友好的用户接口,只需要 load/run 模型~
  • 支持全静态的内存配置(无需 malloc )
  • 即将支持 MaixHub 在线模型训练
TinyMaix底层依赖

TinyMaix可以简单理解为一个矩阵和向量计算库,目前已支持如下几种计算硬件:

代码语言:javascript复制
#define TM_ARCH_CPU                 (0) //default, pure 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目前支持两种等级:1. 选择最少代码和buf 2. 选择速度,需要更多代码和buf

代码语言:javascript复制
#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量化

TinyMaix支持不同位宽的量化:

代码语言:javascript复制
#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

TinyMaix框架对上层应用程序提供的核心API主要位于代码仓的tinymaix.h文件中,其中:

  1. 模型API包含四个:模型加载,模型卸载,预处理,推理。
代码语言:javascript复制
/******************************* 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
  1. 统计函数:用于输出模型中间层信息
代码语言:javascript复制
/******************************* STAT FUNCTION ************************************/
#if TM_ENABLE_STAT
tm_err_t tm_stat(tm_mdlbin_t* mdl);                    //stat model
#endif
  1. 工具函数,包含FP32和uint8的互转
代码语言:javascript复制
/******************************* UTILS FUNCTION ************************************/
uint8_t TM_WEAK tm_fp32to8(float fp32);
float TM_WEAK tm_fp8to32(uint8_t fp8);

这里的模型,通常是预训练模型经过脚本转换生成的TinyMaix格式的模型;

TinyMaix移植到RT-Thread

  1. TinyMaix移植到RT-Thread工作量其实不到,主要适配tm_port.h文件即可。
  2. RT-Thread的配置是通过Kconfig设置一些参数的,所以我把硬件类型,选择等级,量化类型都修改为Kconfig进行配置。
代码语言:javascript复制
#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
  1. TinyMaix需要对接平台内联,内存,打印等接口,所以我们修改对应宏定义,将其适配到RT-Thread平台的接口上。
代码语言:javascript复制
#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__)
  1. TinyMaix调试依赖于精准的计时,我们需要适配其对应的几个宏定义,因为RT-Thread系统没有提供微秒级的接口,只有毫秒级的接口,所以我做了简单的适配。
代码语言:javascript复制
#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 msn", (x), _time);
                                        _start=TM_GET_US();
                                    }
  1. TinyMaix提供了多个实例,如:cifar10,mnist,vww等,RT-Thread支持命令行输入,为了实例可以在通过命令函运行,我们需要修改一下文件名和接口名字。
  • 我们将examples下的cifar10,mnist,vww三个实例下的main.c修改为对应实例的名字:cifar10.c,mnist.c,vww.c。
  • 将cifar10.c,mnist.c,vww.c中的main函数修改为对应实例名字。
代码语言:javascript复制
int cifar10(int argc, char** argv)

int mnist(int argc, char** argv)

int vww(int argc, char** argv)
  • 将实例接口导出到命令行中。
代码语言:javascript复制
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:96 KB,Flash:512 KB

  1. cifar10实例,分类检测,识别图片是一只鸟:
  1. mnist实例,数字识别,图片是一个数字2:
  1. vww实例,检测有没有人,图片有人:

总结

  1. TinyMaix作者已经做了一个RT-Thread的软件包:r-tinymaix。可以在RT-Thread中工程中加入软件包即可以验证。
  2. r-tinymaix的开源链接:https://github.com/RiceChen0/r-tinymaix
  3. TinyMaix非常赞,可以让一个普普通通的单片机拥有AI能力,让嵌入式AI成本减低。

0 人点赞