前言:
原文再续,书接上一回。上一回讲到如何使用NanoEdge AI Studio训练出用于部署的模型,下面我将继续讲解如何部署模型到STM32的MCU。这里我使用的MCU是STM32F103,软件是CubeIDE。训练使用的数据集是上文正态分布生成的数据集。分别为3-1-200-16.csv,4-1-200-16.csv,5-1-200-16.csv,命名格式为:期望-标准差-矩阵的行数-矩阵的列数。
本系列第一篇文章:STM32MCU边缘AI部署之初探NanoEdge AI Studio - 科创网 (XXXXXXXXXXXX)
正文:
1、把.h .a文件移动到要部署的工程中,我的路径如下
2、我们打开NanoEdgeAI.h看看里面有什么好东西,注释看着挺详细的。
3、NanoEdgeAI.h的接近末尾下面有写道“Here some sample declaration added in your main program for the use of the NanoEdge AI library.”开启CV大法,如下图二。
4、在main里面初始化neai
enum neai_state error_code = neai_classification_init(knowledge);
5、如果你这时候编译,你就会发现,沃日?怎么undefined reference to neai_classification_init',点击转跳是能转跳进NanoEdgeAI.h的......还记得我们前面添加进来的libneai.a吗,要把静态编译库添加进GCC链路。如下图,注意要去掉lib和后缀.a,实际上键入的是neai,路径也要添加。
如果你使用的是Keil5,先添加文件到工程目录,如下图
右键.a文件,点击OptionXXXX如下图一,然后更改文件类型如下图二
编译如果如下图一报错--wchar32什么的,在C/C++中添加如下图二的
--wchar32
静态库分 wc16 和 wc32 库,keil5默认的是 wc16 的,这里我们需要修改为wc32库,此时编译应该是没问题了(处理这个问题的时候,真的感慨AI知识污染的严重性,搜索出来的第一页完全是乱来的,一眼AI生成,还霸占CSDN之类的简中网站,这类回答对解决问题毫无用处,还误导学习者)。
6、我们来看看main里面的代码,主要逻辑是初始化neai,然后进入neai_classification函数(这个函数无法查看内部实现,我们只能使用接口)
int main(void) { /* USER CODE BEGIN 1 */ uint16_t id_class = 0; uint16_t newClass = 0; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ enum neai_state error_code = neai_classification_init(knowledge);//初始化 if (error_code != NEAI_OK) { /* This happens if the knowledge does not correspond to the library or if the library works into a not supported board. */ } /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { char uart_buffer[100];//存放要发送的数据,不使用printf偷个懒 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);//查看代码有没有运行 HAL_Delay(100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); HAL_Delay(100); neai_classification(input_user_buffer, output_class_buffer, &id_class);//跑数据 // 逐个类发送置信度 for (uint16_t class_cpt = 0; class_cpt < CLASS_NUMBER; class_cpt++) { // 使用 sprintf 格式化字符串,存储到 uart_buffer 中 int len = sprintf(uart_buffer, "%d: %.3f ", class_cpt, output_class_buffer[class_cpt]); // 通过 UART 发送字符串,等待发送完成 HAL_UART_Transmit(&huart1, (uint8_t*)uart_buffer, len, HAL_MAX_DELAY); } // 发送换行符 sprintf(uart_buffer, "\n"); HAL_UART_Transmit(&huart1, (uint8_t*)uart_buffer, 1, HAL_MAX_DELAY); // 发送 "Class found" 信息 sprintf(uart_buffer, "Class found: %s.\n\n", id2class[id_class]); HAL_UART_Transmit(&huart1, (uint8_t*)uart_buffer, strlen(uart_buffer), HAL_MAX_DELAY); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
7、使用虚拟数据进行预测,使用的数据如下,期望是5附近。
float input_user_buffer[DATA_INPUT_USER * AXIS_NUMBER]={5.1,4.8,4.8,4.6,4.5,5,4.6,4.6,5.5,4.9,4.4,4.6,4.9,4.9,5.6,4.9}; // Buffer of input values
8、烧录然后RUN查看串口结果,实测单次运算时间小于0.01ms,BINGO,运行结果符合预期,输出每个类的置信度和最终结果。
9、余下的交由各位自行发挥了......电流、加速度、电压、温度、湿度等数据都可以进行训练,甚至你不在乎延迟,低算力MCU跑个图像也可以。
PS:写到这里,想起长者的“闷声发大财,这是坠吼滴。”不经感慨,本系列的文章没有意外应该是简中互联网关于这个工具的首篇详细教程,从训练部署到应用,希望有受益的朋友水个回复也好吧^_^
PPS:浅浅数了一下,本文似乎是我发的第25篇文章。
[修改于 27天6时前 - 2024/11/22 17:09:10]
时段 | 个数 |
---|---|
{{f.startingTime}}点 - {{f.endTime}}点 | {{f.fileCount}} |