加载中
加载中
表情图片
评为精选
鼓励
加载中...
分享
加载中...
文件下载
加载中...
修改排序
加载中...
所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
STM32MCU边缘AI部署之再探NanoEdge AI Studio(二)
李艺良
全桥整流2024/09/12原创 秋名山最速传说 IP:广东

前言:

原文再续,书接上一回。上一回讲到如何使用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 - 科创网 (kechuang.org)


正文:

1、把.h .a文件移动到要部署的工程中,我的路径如下

image.png

2、我们打开NanoEdgeAI.h看看里面有什么好东西,注释看着挺详细的。

image.png

3、NanoEdgeAI.h的接近末尾下面有写道“Here some sample declaration added in your main program for the use of the NanoEdge AI library.”开启CV大法,如下图二。

image.png

image.png

4、在main里面初始化neai

C++
enum neai_state error_code = neai_classification_init(knowledge);

image.png

5、如果你这时候编译,你就会发现,沃日?怎么undefined reference to neai_classification_init',点击转跳是能转跳进NanoEdgeAI.h的......还记得我们前面添加进来的libneai.a吗,要把静态编译库添加进GCC链路。如下图,注意要去掉lib和后缀.a,实际上键入的是neai,路径也要添加。

image.png

如果你使用的是Keil5,先添加文件到工程目录,如下图

image.png

右键.a文件,点击OptionXXXX如下图一,然后更改文件类型如下图二

image.png

image.png

编译如果如下图一报错--wchar32什么的,在C/C++中添加如下图二的

Other
--wchar32

静态库分 wc16 和 wc32 库,keil5默认的是 wc16 的,这里我们需要修改为wc32库,此时编译应该是没问题了(处理这个问题的时候,真的感慨AI知识污染的严重性,搜索出来的第一页完全是乱来的,一眼AI生成,还霸占CSDN之类的简中网站,这类回答对解决问题毫无用处,还误导学习者)。

image.png

image.png


6、我们来看看main里面的代码,主要逻辑是初始化neai,然后进入neai_classification函数(这个函数无法查看内部实现,我们只能使用接口)

C++
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附近。

C++
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,运行结果符合预期,输出每个类的置信度和最终结果。

image.png

9、余下的交由各位自行发挥了......电流、加速度、电压、温度、湿度等数据都可以进行训练,甚至你不在乎延迟,低算力MCU跑个图像也可以。


PS:写到这里,想起长者的“闷声发大财,这是坠吼滴。”不经感慨,本系列的文章没有意外应该是简中互联网关于这个工具的首篇详细教程,从训练部署到应用,希望有受益的朋友水个回复也好吧^_^

PPS:浅浅数了一下,本文似乎是我发的第25篇文章。

[修改于 3个月21天前 - 2024/11/22 17:09:10]

来自:电子信息 / 电子技术计算机科学 / 软件综合严肃内容:教程/课程
1
 
4
新版本公告
~~空空如也
zRed洲虹
6个月1天前 IP:中国
937443

感谢分享,感觉电炮自动调整时序要来哩 sticker

引用
评论(1)
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的