项目背景
很高兴参加funpack第三季第四期的活动,本次活动的开发板是来自恩智浦的FRDM-MCXN947。
功能介绍
任务描述
- 使用调试器的串口向MCU直接发送图像
- 使用eIQ 实现图像分类(帽子或鞋子)
硬件介绍
开发板具备丰富硬件连接性和拓展性,包括Arduino®、FlexIO/LCD、SmartDMA摄像头、Pmod™、mikroBUS™、Micro SD卡插槽、以太网、HS USB Type-C、SPI/I2C/UART、WIFI及CAN-FD收发器等多种接口。
开发板配备的MCX N947是一款高性能、低功耗微控制器,配备两个Arm Cortex-M33内核和一个神经处理单元(即NPU),运行频率可达150MHz。它提供2MB闪存和可配置的带ECC的RAM,并集成DSP协处理器和eIQ Neutron NPU。与单独的CPU内核相比,NPU能提升高达42倍的机器学习吞吐量,从而减少系统唤醒时间并降低整体功耗。该MCU适用于安全、智能的电机控制和机器学习应用。MCX N947还包括多种智能外设和加速器,如PowerQUAD DSP加速器、SmartDMA协处理器、eIQ Neutron N1-16神经处理单元、以及高达2MB片上闪存和512KB RAM。它支持多种通信接口,包括USB高速/全速接口、Micro SD高速卡接口、多个SPI、I2C、UART接口、以太网接口等。此外,它还具备强大的模拟和定时器功能,适合多样化应用需求。
设计思路
主要困难
从任务描述中可以看出,实现该任务有三个核心功能:
- 建立模型:使用公开数据集训练模型
- 接收图片数据:用串口中断接收二进制图片RGB数据
- 输出识别结果:接收一次图片后,调用模型开始识别,将识别结果通过串口发送出去
解决思路
核心功能一
需要找到一个包含帽子和鞋子图片的公开数据集,并使用该数据集训练一个图像分类模型。数据集是从网上公开数据集中获取的。然后使用eIQ Portal这个软件构建图像分类模型。eIQ Portal集成加载数据集,训练,评估,优化,部署等功能,可极大简化机器学习模型的开发。选择用mobilenet v1架构作为基础模型架构,在此基础上完成鞋帽分类模型的训练,训练完成后对模型进行评估,导出,最后要转换模型架构为tensorflow lite。
训练集:测试集为250:50。
核心功能二
为了让MCU能够接收图像数据,通过调试器的串口发送二进制形式的RGB图像数据。
通过图像取模软件将图片转换为二进制文件,MCU需要设置串口中断来处理接收数据,并将接收到的数据存储到适当的缓存中,确保数据的完整性和正确性。
核心功能三
当MCU接收到完整的图像数据后,会调用之前训练好的模型对图像进行分类,判断图像中是帽子还是鞋子。识别完成后,将分类结果通过串口发送回调试器,以便进一步处理或显示。
软件流程图
功能展示
核心代码片段及说明
加载模型,model-data.s
npu_model_data:
.incbin "../source/model/mobilenet_v1-2024-07-09T03-18-05.169Z_in-int8_out-int8_channel_ptq_converted.tflite"
npu_model_data_end:
调用训练好的模型,识别图片,cifar10_recognize.cpp
extern uint32_t g_label_num;
extern uint32_t g_detedted_flag;
extern uint8_t gImage[49152];
extern volatile uint16_t txIndex; /* Index of the data to send out. */
extern volatile uint16_t rxIndex; /* Index of the memory to save new arrived data. */
extern volatile uint8_t end_flag;
void cifar10_recognize()
{
tensor_dims_t inputDims;
tensor_type_t inputType;
uint8_t* inputData;
tensor_dims_t outputDims;
tensor_type_t outputType;
uint8_t* outputData;
size_t arenaSize;
if (MODEL_Init() != kStatus_Success)
{
PRINTF("Failed initializing model");
for (;;) {}
}
size_t usedSize = MODEL_GetArenaUsedBytes(&arenaSize);
PRINTF("\r\n%d/%d kB (%0.2f%%) tensor arena used\r\n", usedSize / 1024, arenaSize / 1024, 100.0*usedSize/arenaSize);
inputData = MODEL_GetInputTensorData(&inputDims, &inputType);
outputData = MODEL_GetOutputTensorData(&outputDims, &outputType);
while(1)
{
if (rxIndex >= 49100 && rxIndex <= 49152)
{
PRINTF("__end_flag = %d, rxIndex = %d\r\n", end_flag, rxIndex);
}
if (end_flag == 0 || !(kLPUART_TxDataRegEmptyFlag & LPUART_GetStatusFlags(LPUART4)))
continue;
memcpy(model_input_buf, gImage, 49152);
PRINTF("end_flag, rxIndex = %d\r\n", rxIndex);
rxIndex = 0;
end_flag = 0;
uint8_t *buf = 0;
memset(inputData,0,inputDims.data[1]*inputDims.data[2]*inputDims.data[3]);
buf = inputData + (inputData,inputDims.data[1] - MODEL_IN_H) /2 * MODEL_IN_W * MODEL_IN_C;
memcpy(buf, model_input_buf, MODEL_IN_W*MODEL_IN_H*MODEL_IN_C);
auto startTime = TIMER_GetTimeInUS();
MODEL_RunInference();
auto endTime = TIMER_GetTimeInUS();
auto dt = endTime - startTime;
s_infUs = (uint32_t)dt;
MODEL_ProcessOutput(outputData, &outputDims, outputType, dt);
if(g_detedted_flag)
{
char str_buf[96] = {0x0};
sprintf(str_buf,"Label: %s\r\n",labels[g_label_num]);
PRINTF(str_buf);
}
memset(gImage, 0, sizeof(gImage));
}
}
开启串口中断,中断函数中接收图片二进制文件,main.c
#define DEMO_LPUART LPUART4
#define DEMO_LPUART_CLK_FREQ CLOCK_GetLPFlexCommClkFreq(4u)
#define DEMO_LPUART_IRQn LP_FLEXCOMM4_IRQn
#define DEMO_LPUART_IRQHandler LP_FLEXCOMM4_IRQHandler
#define DEMO_RING_BUFFER_SIZE 49152
uint8_t gImage[DEMO_RING_BUFFER_SIZE] = {0};
volatile uint16_t txIndex; /* Index of the data to send out. */
volatile uint16_t rxIndex; /* Index of the memory to save new arrived data. */
/*******************************************************************************
* Code
******************************************************************************/
void DEMO_LPUART_IRQHandler(void)
{
uint8_t data;
uint16_t tmprxIndex = rxIndex;
/* If new data arrived. */
if ((kLPUART_RxDataRegFullFlag)&LPUART_GetStatusFlags(DEMO_LPUART))
{
data = LPUART_ReadByte(DEMO_LPUART);
if (tmprxIndex < DEMO_RING_BUFFER_SIZE)
{
gImage[rxIndex] = data;
rxIndex++;
}
if (rxIndex >= DEMO_RING_BUFFER_SIZE)
{
end_flag = 1;
}
}
SDK_ISR_EXIT_BARRIER;
}
int main(void)
{
BOARD_Init();
TIMER_Init();
lpuart_config_t config;
LPUART_GetDefaultConfig(&config);
config.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE;
config.enableTx = true;
config.enableRx = true;
LPUART_Init(DEMO_LPUART, &config, DEMO_LPUART_CLK_FREQ);
/* Enable RX interrupt. */
LPUART_EnableInterrupts(DEMO_LPUART, kLPUART_RxDataRegFullInterruptEnable);
EnableIRQ(DEMO_LPUART_IRQn);
cifar10_recognize();
while(1)
{
}
}
实现效果
训练模型
评估模型
导出模型
具体演示效果请观看演示视频
总结
本项目依托FRDM-MCX N947平台,完成鞋帽图像分类任务。在此过程中阅读传感器数据手册和平台驱动代码,分别训练分类模型和编写MCU端的驱动代码,完成串口通信、模型调用、分类结果输出,最终顺利完成任务。