- 2024-11-15
-
回复了主题帖:
[STM32H7R/S]测评 ⑨nano edge ai studio 训练一个模型--下
电子烂人 发表于 2024-11-14 17:00
好文章,等我回去试试这个方法。打个胶先(
好呀,有问题我们再交流
-
回复了主题帖:
[STM32H7R/S]测评 ⑨nano edge ai studio 训练一个模型--下
Jacktang 发表于 2024-11-13 07:50
STM32H7R/S-DK开发板的测评做的不错,赞
谢谢夸奖
- 2024-11-12
-
发表了主题帖:
[STM32H7R/S]测评 ⑨nano edge ai studio 训练一个模型--下
本帖最后由 不爱胡萝卜的仓鼠 于 2024-11-11 21:37 编辑
/***********************************************************************************************************/
本文强烈建议和上一篇文章以及datalogger的那篇一起交叉看,因为今天又学到了一点新东西。并且还发现了之前datalogger的一个重大BUG
/***********************************************************************************************************/
在上一篇中最后一步移植.a文件到工程中没能顺利完成,我个人感觉还是工程的问题,可惜我能力有限,没法解决。
正经能力我可能有限,但是我歪办法多。我的思路是创建一个空白工程,用Keil,然后只要boot工程,不要跳转APP工程,直接把Boot工程当做主工程来用,我看了主芯片Flash和Ram其实还是可以的,有64K Flash,620K RAM,简单跑跑这个项目应该问题不大。只要我能想尽一切办法让这个boot工程的代码能顺利编译,那就万事大吉啦
经过不懈努力,我终于搞定了我想要的东西,现在这个工程就是实现了之前dataLogger功能的,可以用keil编译的,相比之前的我还增加了ready信号的get,这样应该会更好一点,每次都可以保证获取到新的值。还发现之前datalogger代码的一个BUG,他打印出来的结果是错误的,偏移写的不正确。
之前的工程红框位置的偏移写错了
本次修正后
工程如下:(后面我把datalogger的功能整合进最终的工程了,所以这个大家有兴趣的话就打开看看,可以忽略不看,并且后续有一些代码的小修改,他和最终工程中的也不一定一致,主要是数据格式从int改成了float)
一.再次训练模型
因为上一个模型有问题,我就重新训练了一个模型,这个模型比之前的那个好,现在这个可以到100%,上一篇就92.5(不过上一篇用了错误的datalogger代码,原始样本数据就是错的,那个模型就是报废的。也没啥参考意义)。具体操作步骤我就不说了,详见上一篇
并且在验证环节,我尝试了正确位置放正确数据,还尝试交换放数据,结果都是100%正确识别。这个模型肯定嘎嘎好用
/***********************************************************************************************************/
这里再补充一点,最后一步生成库文件时,Compilation Flags默认会勾选第1个和第3个。我的编译器可能不匹配,如果按默认的来,我这儿能编译,能运行,但是检测出来的结果一直是同一个,很奇怪,编译器这玩意也是触摸到了我的知识盲区,搞不明白。反正全部去掉勾选,就可以了
/***********************************************************************************************************/
二.在线验证模型
并且本次我还发现一个很有用的东西,就是在线模拟,通过串口直接把datalogger实时采集的数据传给电脑,电脑直接运行模型,推算结果。这样就不用把模型下载到开发板上再验证结果了,并且这样在线验证后,我们就可以知道模型是不是真的好用,如果回头在开发板上运行时有问题,那至少可以排除模型的问题
开发板上运行datalogger的代码,然后界面上选择串口和波特率,再点开始即可
这个玩意儿图文不够生动,我就录了一段视频给大家
[localvideo]69151aec60f97e6d2658b72ad5fb01e5[/localvideo]
三.移植模型到开发版上运行
最终得到的模型压缩包如下:
接下来就是把本次得到的新模型弄到工程中去运行一下,在如图路径创建一个文件夹,用于存放.a和.h
然后把压缩包里的.a、.h拷贝到这个路径下
接下来我们打开keil,然后添加nanoedgeai的文件夹和文件
接下来是添加头文件路径
然后要对.a文件做一下修改,右键点击.a文件
接下来就是对main.c进行修改了,参照nanoEdgeAi给的示例代码,和上一篇的修改差不多,只是我这边用了printf(这玩意儿终于好用了)
以下是整个main.c,我这边是整合了datalogger工程和AI模型的,通过“MODE”这个宏定义控制
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @File : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "iks4a1_motion_sensors.h"
#include "iks4a1_motion_sensors_ex.h"
#include "lsm6dso16is_reg.h"
#include "NanoEdgeAI.h"
#include "knowledge.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct displayFloatToInt_s {
int8_t sign; /* 0 means positive, 1 means negative*/
uint32_t out_int;
uint32_t out_dec;
} displayFloatToInt_t;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define MAX_BUF_SIZE 256
#define ACC_SAMPLE_MAX 64
#define AXIS 3
#define MODE (1) /* 0:datalogger模式, 1: 运行nanoedgeai模型模式 */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart4;
/* USER CODE BEGIN PV */
//static uint8_t verbose = 0; /* Verbose output to UART terminal ON/OFF. */
static IKS4A1_MOTION_SENSOR_Capabilities_t MotionCapabilities[IKS4A1_MOTION_INSTANCES_NBR];
static char dataOut[MAX_BUF_SIZE];
// static int acc_sample_buffer[AXIS * ACC_SAMPLE_MAX] = {0};
float input_user_buffer[DATA_INPUT_USER * AXIS_NUMBER]; // Buffer of input values
float output_class_buffer[CLASS_NUMBER]; // Buffer of class probabilities
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
static void MX_GPIO_Init(void);
static void MX_UART4_Init(void);
static void MX_FLASH_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart4, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
static void floatToInt(float in, displayFloatToInt_t *out_value, int32_t dec_prec)
{
if(in >= 0.0f)
{
out_value->sign = 0;
}else
{
out_value->sign = 1;
in = -in;
}
in = in + (0.5f / pow(10, dec_prec));
out_value->out_int = (int32_t)in;
in = in - (float)(out_value->out_int);
out_value->out_dec = (int32_t)trunc(in * pow(10, dec_prec));
}
static void Accelero_Sensor_Handler(uint32_t Instance)
{
// float odr;
// int32_t fullScale;
IKS4A1_MOTION_SENSOR_Axes_t acceleration;
// displayFloatToInt_t out_value;
// uint8_t whoami;
uint16_t i = 0;
lsm6dso16is_status_reg_t Status;
for (i = 0; i < ACC_SAMPLE_MAX;)
{
while(1)
{
if (BSP_ERROR_NONE == IKS4A1_MOTION_SENSOR_Get_DRDY_Status(Instance, MOTION_ACCELERO, (uint8_t *)&Status))
{
if (Status.xlda == 1)
{
break;
}
}
}
if (0 == IKS4A1_MOTION_SENSOR_GetAxes(Instance, MOTION_ACCELERO, &acceleration))
{
input_user_buffer[AXIS * i] = (float)acceleration.x;
input_user_buffer[(AXIS * i) + 1] = (float)acceleration.y;
input_user_buffer[(AXIS * i) + 2] = (float)acceleration.z;
i++;
}
}
for(i = 0; i < ACC_SAMPLE_MAX - 1; i++)
{
sprintf(dataOut, "%.2f,%.2f,%.2f,", input_user_buffer[(AXIS * i)], input_user_buffer[(AXIS * i)+1], input_user_buffer[(AXIS * i)+2]);
printf("%s", dataOut);
}
i = ACC_SAMPLE_MAX - 1;
sprintf(dataOut, "%.2f,%.2f,%.2f", input_user_buffer[(AXIS * i)], input_user_buffer[(AXIS * i)+1], input_user_buffer[(AXIS * i)+2]);
printf("%s\r\n", dataOut);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
#if MODE == 1
uint16_t id_class = 0;
#endif
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* 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_UART4_Init();
MX_FLASH_Init();
/* USER CODE BEGIN 2 */
printf("test");
/* ACC传感器初始化 */
displayFloatToInt_t out_value_odr;
int i;
IKS4A1_MOTION_SENSOR_Init(IKS4A1_LSM6DSO16IS_0, MOTION_ACCELERO | MOTION_GYRO);
printf("IKS4A1_MOTION_SENSOR_Init ok\r\n");
for(i = 0; i < IKS4A1_MOTION_INSTANCES_NBR; i++)
{
IKS4A1_MOTION_SENSOR_GetCapabilities(i, &MotionCapabilities[i]);
snprintf(dataOut, MAX_BUF_SIZE,
"\r\nMotion Sensor Instance %d capabilities: \r\n ACCELEROMETER: %d\r\n GYROSCOPE: %d\r\n MAGNETOMETER: %d\r\n LOW POWER: %d\r\n",
i, MotionCapabilities[i].Acc, MotionCapabilities[i].Gyro, MotionCapabilities[i].Magneto, MotionCapabilities[i].LowPower);
printf("%s", dataOut);
floatToInt(MotionCapabilities[i].AccMaxOdr, &out_value_odr, 3);
snprintf(dataOut, MAX_BUF_SIZE, " MAX ACC ODR: %d.%03d Hz, MAX ACC FS: %d\r\n", (int)out_value_odr.out_int,
(int)out_value_odr.out_dec, (int)MotionCapabilities[i].AccMaxFS);
printf("%s", dataOut);
floatToInt(MotionCapabilities[i].GyroMaxOdr, &out_value_odr, 3);
snprintf(dataOut, MAX_BUF_SIZE, " MAX GYRO ODR: %d.%03d Hz, MAX GYRO FS: %d\r\n", (int)out_value_odr.out_int,
(int)out_value_odr.out_dec, (int)MotionCapabilities[i].GyroMaxFS);
printf("%s", dataOut);
floatToInt(MotionCapabilities[i].MagMaxOdr, &out_value_odr, 3);
snprintf(dataOut, MAX_BUF_SIZE, " MAX MAG ODR: %d.%03d Hz, MAX MAG FS: %d\r\n", (int)out_value_odr.out_int,
(int)out_value_odr.out_dec, (int)MotionCapabilities[i].MagMaxFS);
printf("%s", dataOut);
}
#if MODE == 1
enum neai_state error_code = neai_classification_init(knowledge);
if (error_code != NEAI_OK)
{
while(1)
{
/* This happens if the knowledge does not correspond to the library or if the library works into a not supported board. */
printf("neai_classification_init fail");
HAL_Delay(1000);
}
}
printf("neai_classification_init ok");
#endif
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// HAL_GPIO_WritePin(GPIOO, GPIO_PIN_1, GPIO_PIN_RESET);
// HAL_Delay(1000);
// printf("led on");
// HAL_GPIO_WritePin(GPIOO, GPIO_PIN_1, GPIO_PIN_SET);
// HAL_Delay(1000);
// printf("led off");
Accelero_Sensor_Handler(0);
#if MODE == 1
neai_classification(input_user_buffer, output_class_buffer, &id_class);
printf("stop:%.2f, line:%.2f, circle:%.2f\r\n", output_class_buffer[0], output_class_buffer[1], output_class_buffer[2]);
printf("result: %d\r\n", id_class);
#endif
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL1.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_PCLK4|RCC_CLOCKTYPE_PCLK5;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV1;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;
RCC_ClkInitStruct.APB5CLKDivider = RCC_APB5_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief FLASH Initialization Function
* @param None
* @retval None
*/
static void MX_FLASH_Init(void)
{
/* USER CODE BEGIN FLASH_Init 0 */
/* USER CODE END FLASH_Init 0 */
FLASH_OBProgramInitTypeDef pOBInit = {0};
/* USER CODE BEGIN FLASH_Init 1 */
/* USER CODE END FLASH_Init 1 */
HAL_FLASHEx_OBGetConfig(&pOBInit);
if ((pOBInit.USERConfig2 & OB_I2C_NI3C_I2C) != OB_I2C_NI3C_I2C)
{
if (HAL_FLASH_Unlock() != HAL_OK)
{
Error_Handler();
}
if (HAL_FLASH_OB_Unlock() != HAL_OK)
{
Error_Handler();
}
pOBInit.OptionType = OPTIONBYTE_USER;
pOBInit.USERType = OB_USER_I2C_NI3C;
pOBInit.USERConfig2 = OB_I2C_NI3C_I2C;
if (HAL_FLASHEx_OBProgram(&pOBInit) != HAL_OK)
{
Error_Handler();
}
if (HAL_FLASH_OB_Lock() != HAL_OK)
{
Error_Handler();
}
if (HAL_FLASH_Lock() != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN FLASH_Init 2 */
/* USER CODE END FLASH_Init 2 */
}
/**
* @brief UART4 Initialization Function
* @param None
* @retval None
*/
static void MX_UART4_Init(void)
{
/* USER CODE BEGIN UART4_Init 0 */
/* USER CODE END UART4_Init 0 */
/* USER CODE BEGIN UART4_Init 1 */
/* USER CODE END UART4_Init 1 */
huart4.Instance = UART4;
huart4.Init.BaudRate = 921600;
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart4.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart4) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart4, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart4, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart4) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN UART4_Init 2 */
/* USER CODE END UART4_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOO_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOO, GPIO_PIN_1, GPIO_PIN_RESET);
/*Configure GPIO pin : PO1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOO, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/* MPU Configuration */
static void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x0;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
编译后有如图报错
TEST_Boot\TEST_Boot.axf: Error: L6242E: Cannot link object NanoEdgeAI.o as its attributes are incompatible with the image attributes.
... wchart-16 clashes with wchart-32.
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
需要做如下修改:
--wchar32
还要切换回AC5,否则这个改了以后还会有更多的报错
这样就万事大吉了,虽然有点警告,但问题不大
接下来就是激动人心的时刻了,运行代码,看看能否正确识别
[localvideo]7e48eb830a8c166617694c7f478f2e49[/localvideo]
本次最终的工程:
四.心得
本次STM32H7R/S-DK开发板的测评终于圆满完成。本次真的是收获满满,我在今年上半年就了解到ST在Ai方向的各种方案及工具,但是当时nanoedgeai还是要收费的,并且我的本职工作不是做AI方向的开发的,对于Ai相关的知识以及如何训练模型、使用模型完全不懂,是属于小白的档位,所以一直就处于观望阶段,最近nanoedgeai免费了,正好遇上H7S7开发板的试用,就趁着这个契机逼自己一把,学习一下相关的知识和技能。
不得不说,ST在文档、demo、视频教程相关的技术支持、工具的设计等真的没的说,在业界绝对算的上数一数二。我通过看官方提供的学习资料,一个门外汉,通过几十小时的学习、实践。可以快速掌握模型训练、在单片上运行模型的开发技能,可以看得出来ST的工程师绝对是付出了巨大的努力,并且训练出来的模型实际效果也是非常棒,100条数据,训练一次,就得到了一个非常完美的模型,体积很小,响应很快,准确度很高。这样的结果真的是令我非常震撼,在得到结果前我一直认为会需要有多轮的训练、调参数、调整样本等
·最后再次感谢ST和EEWORLD给我这次试用的机会,谢谢
- 2024-11-11
-
回复了主题帖:
[STM32H7R/S]测评 ⑧nano edge ai studio 训练一个模型--上
电子烂人 发表于 2024-11-10 11:23
大佬有检查过MAKEFILE没
我只会一些简单的makefile,这种工程的makefile太复杂了,看不明白不过我已经通过一种曲线救国的方法达到目的了,模型成功在开发板上跑起来了,效果超级好,今晚会更新一篇新的文章
- 2024-11-09
-
发表了主题帖:
[STM32H7R/S]测评 ⑧nano edge ai studio 训练一个模型--上
本帖最后由 不爱胡萝卜的仓鼠 于 2024-11-12 11:53 编辑
一.nano edge ai studio简介
nano edge ai studio是ST AI套件中的一个,他的作用就是帮助我们快速训练一个AI模型,并且这个软件现在是免费的,几个月前他还是收费的。但是这个软件有点局限性,他训练的模型只能是传感器的数据,并且是时间序列的(如果你要实现一些例如图像识别的AI模型,那这个软件就不合适了,图像ST有对应的图像套件)
下图为nano edge ai studio工作流程的见图,一张图就说明了这个工具是如何使用的,我们只需要创建一个工程,然后收集数据,给到软件,软件就会自动分析并帮我们选择一个最佳的模型,对于我们这些没有AI基础的嵌入式工程师简直就是帮助巨大
nano edge ai studio官网如下:https://stm32ai.st.com/nanoedge-ai/
具体怎么下载安装我这儿就不赘述了,额外说一下的就是他需要你注册一个ST账号,然后licence会通过邮件发送给你,软件打开后需要用licence注册后才能使用
nano edge ai studio软件主界面如下,软件左上角时这个软件可以支持的4种算法,右边是一些应用的展示,可以为大家提供一些思路
简单给大家讲一下4中算法(可以看一下这个视频在大约23:23左右开始介绍,https://www.bilibili.com/video/BV1EG4y1a7Ji?spm_id_from=333.788.videopod.sections&vd_source=b5472a9f9c8c4560fd993637d3d9cb0f)
AD(异常检测):需要输入正常工作的信号和异常工作的信号给到软件去生成算法,算法就能分辨出机器是否在正常工作。并且这个算法是唯一一个支持在使用过程中自学习的,不同机器不同安装环境下检测到的信号可能会和训练时的样本不一致,这个自学习就可以做到很好的适应性。但是需要我们列举出异常信号给软件去学习
nC(多分类):给软件多种信号,生成算法,算法就能分辨出当前检测到的信号是之前训练时的哪一种
1C(单分类):给软件一种信号,生成算法,生成的算法就能分辨出当前时候和之前训练时提供的信号是否一致。对于检测设备是否异常工作的场景,可以只提供正常信号用于学习,不需要提供异常信号,因为异常信号可能有很多种,我们不一定可以一一列举
E(外推法):这是一种回归算法,可以基于当前检测到的信号预测未来。例如某个设备生命周期内各个时间点的振动信号,然后实际工作是,软件就可以预测,该机器寿命大概还有多久,用于提前检测异常并提示保养或维修。
二.训练模型
我这边就训练一个多分类模型,回头我会使用加速度传感器,让模型识别出我现在的动作,例如静止、直线运动、环形运动
先选择nC
2.1 工程设置
接下来就是第一步,工程的一些设置
我们主要关注的就是taget和sensor
其中target我们可以选择ST的开发板、或者MCU信号、或者arduino开发板。我这边因为以前选过H7S的DK板,所以他还有favorite的展示
然后传感器的话有很多,加速度计、电流、磁力、麦克风等等
我这边使用的ACC,并且选择3轴,最终设置如下
2.2 信号采集
信号采集相关内容已经在上一篇中说过了,这边我就不具体展开了。[STM32H7R/S]测评 ⑦制作一个NANO EDGE AI STUDIO采集数据使用的Data Logger
我采集了3中信号,停止、画线、画圈。每种都采样100轮
2.3 Benchmark
这里名字叫benchmark,事实上他还干了另外一件事,那就是从众多的算法中,拿着我们给的样本数据,一个一个去试,看那个算法最匹配我们提供的样本
我们这边创建一个新的benchmark,然后默认的会把3种数据样本都选上,CPU处理器的核也拉满,这样可以算的快一点
然后等待启动计算
计算中,左边有个小的进度指示。右边可以看到总耗时,以及匹配度、RAM占用、Flash占用情况。并得到一个分数
经过大约半个多小时的计算(这个速度还是挺可以的,我这电脑CPU是好多年前的Ryzen 5 1600X),结果如下
2.4 校验
这里我们可以看到刚才软件尝试的所有算法,一共尝试了60种,并找出了里面效果最好的一种
如果你想在电脑上模拟测试这个模型的效果,就可以在这个界面完成,当然不想的话就可以跳过这个界面。这个步骤是可选的
首先要准备要用于验证的样本数据,我这儿就偷懒不再拿新的数据了,把之前第二步采样到的数据用一下。回到第二步,点击如图,就可以把整个采样数据下载下来(里面有100条)
然后用notepad++打开,随便选择一条,其他的都删掉
回到第四步的界面,选择一种算法,我就选择效果最好的那个
接下来会让我们放入用于测试的样本
把刚才修改过的文件放到circle下面,点击开始
测试结果如下,模型认为这个数据100%是圈,效果很不错
2.5 导出模型
最后一步就是导出模型
下一步ST会做个小小的问卷调查,我就直接跳过了
保存,我们会得到一个压缩包,这个我们待会儿看,因为这个界面还有好东西,一段示例代码
/* =============
Copyright (c) 2024, STMicroelectronics
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that
the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written permission.
*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER / OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*
*/
/**
**************************************************************************
* Demo: NanoEdge AI process to include in main program body
*
* @note This program must be completed and customized by the user
**************************************************************************
*/
/* Includes --------------------------------------------------------------------*/
#include "NanoEdgeAI.h"
#include "knowledge.h"
/* Private define --------------------------------------------------------------*/
/* Private variables defined by user -------------------------------------------*/
float input_user_buffer[DATA_INPUT_USER * AXIS_NUMBER]; // Buffer of input values
float output_class_buffer[CLASS_NUMBER]; // Buffer of class probabilities
/* Private function prototypes defined by user ---------------------------------*/
/*
* @brief Collect data process
*
* This function is defined by user, depends on applications and sensors
*
* @param sample_buffer: [in, out] buffer of sample values
* @retval None
* @note If AXIS_NUMBER = 3 (cf NanoEdgeAI.h), the buffer must be
* ordered as follow:
* [x0 y0 z0 x1 y1 z1 ... xn yn zn], where xi, yi and zi
* are the values for x, y and z axes, n is equal to
* DATA_INPUT_USER (cf NanoEdgeAI.h)
*/
void fill_buffer(float sample_buffer[])
{
/* USER BEGIN */
/* USER END */
}
/* -----------------------------------------------------------------------------*/
int main(void)
{
/* Initialization ------------------------------------------------------------*/
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. */
}
/* Classification ------------------------------------------------------------*/
uint16_t id_class = 0;
while (1) {
fill_buffer(input_user_buffer);
neai_classification(input_user_buffer, output_class_buffer, &id_class);
/* USER BEGIN */
/*
* e.g.: Trigger functions depending on id_class
* (print output class probabilities using output_class_buffer[],
* print the name of the identified class using id2class[id_class],
* blink LED, ring alarm, etc.).
*/
/* USER END */
}
}
通过这段示例代码我们就可以知道,待会儿这个模型我们如何使用,只需要3步,初始化,采样数据,调用检测函数。即可得到检测结果,非常简洁明了,对于完全不了解AI底层算法或者调参的工程师来说,这个真的是太方便好用了,可以帮助我们快速完成项目
接下来看看压缩包里有点什么
docs中是一个pdf文档,介绍nanoedge ai还给了一些网址用于学习
emulators中的文件是模拟用的,可以通过命令行做测试,我这儿用不到,就不展示了
.a、.h文件 就是我们回头移植到单片机所需的。
json文件目前没搞明白干嘛的,可能是
三.移植算法到开发板上运行
很可惜,本次移植最终是失败率,搞了很久一直无法编译通过。其实移植是很简单的,把.a、.h放到工程中,参照刚才的示例代码调用函数就可以了,但是很可惜学艺不精,cubeide是现学的,有可能是那边没配置到位。也有可能是这个工程有问题,之前就是遇到ld的问题,现在也是卡在ld.exe上。我就把我的操作步骤放一下,工程也放后面,看看有没有懂的大佬给看看
工程我就直接用上一篇的datalogger的
main.c修改如下
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @File GPIO/GPIO_IOToggle/Src/main.c
* @author MCD Application Team
* @brief This example describes how to configure and use GPIOs through
* the STM32H7RSxx HAL API.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "flash.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "iks4a1_motion_sensors.h"
#include "NanoEdgeAI.h"
#include "knowledge.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct displayFloatToInt_s {
int8_t sign; /* 0 means positive, 1 means negative*/
uint32_t out_int;
uint32_t out_dec;
} displayFloatToInt_t;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define MAX_BUF_SIZE 256
#define ACC_SAMPLE_MAX 64
#define AXIS 3
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
static uint8_t verbose = 0; /* Verbose output to UART terminal ON/OFF. */
static IKS4A1_MOTION_SENSOR_Capabilities_t MotionCapabilities[IKS4A1_MOTION_INSTANCES_NBR];
static char dataOut[MAX_BUF_SIZE];
// static int acc_sample_buffer[AXIS * ACC_SAMPLE_MAX] = {0};
float input_user_buffer[DATA_INPUT_USER * AXIS_NUMBER]; // Buffer of input values
float output_class_buffer[CLASS_NUMBER]; // Buffer of class probabilities
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
static void MPU_AdjustRegionAddressSize(uint32_t Address, uint32_t Size, MPU_Region_InitTypeDef* pInit);
static void MPU_Config(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart4 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
static void floatToInt(float in, displayFloatToInt_t *out_value, int32_t dec_prec)
{
if(in >= 0.0f)
{
out_value->sign = 0;
}else
{
out_value->sign = 1;
in = -in;
}
in = in + (0.5f / pow(10, dec_prec));
out_value->out_int = (int32_t)in;
in = in - (float)(out_value->out_int);
out_value->out_dec = (int32_t)trunc(in * pow(10, dec_prec));
}
static void Accelero_Sensor_Handler(uint32_t Instance)
{
// float odr;
// int32_t fullScale;
IKS4A1_MOTION_SENSOR_Axes_t acceleration;
// displayFloatToInt_t out_value;
// uint8_t whoami;
uint16_t i = 0;
for (i = 0; i < ACC_SAMPLE_MAX;)
{
if (0 == IKS4A1_MOTION_SENSOR_GetAxes(Instance, MOTION_ACCELERO, &acceleration))
{
input_user_buffer[AXIS * i] = (float)acceleration.x;
input_user_buffer[(AXIS * i) + 1] = (float)acceleration.y;
input_user_buffer[(AXIS * i) + 2] = (float)acceleration.z;
i++;
}
}
// unsigned char data[50] = {0};
// uint8_t len = 0;
// for(i = 0; i < ACC_SAMPLE_MAX - 1; i++)
// {
// sprintf(data, "%d,%d,%d,", acc_sample_buffer[i], acc_sample_buffer[i+1], acc_sample_buffer[i+2]);
// len = strlen(data);
// HAL_UART_Transmit(&huart4, data, len, 0xFFFF);
// }
// sprintf(data, "%d,%d,%d", acc_sample_buffer[i], acc_sample_buffer[i+1], acc_sample_buffer[i+2]);
// len = strlen(data);
// HAL_UART_Transmit(&huart4, data, len, 0xFFFF);
// unsigned char data2[] = {"\r\n"};
// len = strlen(data2);
// HAL_UART_Transmit(&huart4, data2, len, 0xFFFF);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
MPU_Config();
uint16_t id_class = 0;
/* USER CODE END 1 */
/* Enable the CPU Cache */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Update SystemCoreClock variable according to RCC registers values. */
SystemCoreClockUpdate();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_UART4_Init();
MX_FLASH_Init();
/* USER CODE BEGIN 2 */
/* ACC传感器初始化 */
displayFloatToInt_t out_value_odr;
int i;
IKS4A1_MOTION_SENSOR_Init(IKS4A1_LSM6DSO16IS_0, MOTION_ACCELERO | MOTION_GYRO);
unsigned char data[] = "IKS4A1_MOTION_SENSOR_Init ok\r\n";
HAL_UART_Transmit(&huart4, data, sizeof(data), 0xFFFF);
// for(i = 0; i < IKS4A1_MOTION_INSTANCES_NBR; i++)
// {
// IKS4A1_MOTION_SENSOR_GetCapabilities(i, &MotionCapabilities[i]);
// snprintf(dataOut, MAX_BUF_SIZE,
// "\r\nMotion Sensor Instance %d capabilities: \r\n ACCELEROMETER: %d\r\n GYROSCOPE: %d\r\n MAGNETOMETER: %d\r\n LOW POWER: %d\r\n",
// i, MotionCapabilities[i].Acc, MotionCapabilities[i].Gyro, MotionCapabilities[i].Magneto, MotionCapabilities[i].LowPower);
// printf("%s", dataOut);
// floatToInt(MotionCapabilities[i].AccMaxOdr, &out_value_odr, 3);
// snprintf(dataOut, MAX_BUF_SIZE, " MAX ACC ODR: %d.%03d Hz, MAX ACC FS: %d\r\n", (int)out_value_odr.out_int,
// (int)out_value_odr.out_dec, (int)MotionCapabilities[i].AccMaxFS);
// printf("%s", dataOut);
// floatToInt(MotionCapabilities[i].GyroMaxOdr, &out_value_odr, 3);
// snprintf(dataOut, MAX_BUF_SIZE, " MAX GYRO ODR: %d.%03d Hz, MAX GYRO FS: %d\r\n", (int)out_value_odr.out_int,
// (int)out_value_odr.out_dec, (int)MotionCapabilities[i].GyroMaxFS);
// printf("%s", dataOut);
// floatToInt(MotionCapabilities[i].MagMaxOdr, &out_value_odr, 3);
// snprintf(dataOut, MAX_BUF_SIZE, " MAX MAG ODR: %d.%03d Hz, MAX MAG FS: %d\r\n", (int)out_value_odr.out_int,
// (int)out_value_odr.out_dec, (int)MotionCapabilities[i].MagMaxFS);
// printf("%s", dataOut);
// }
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. */
}
unsigned char data2[] = "neai_classification_init ok\r\n";
HAL_UART_Transmit(&huart4, data2, sizeof(data2), 0xFFFF);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
#if 0
HAL_GPIO_TogglePin(LD1_GPIO_PORT, LD1_Pin);
/* Insert delay 100 ms */
HAL_Delay(100);
HAL_GPIO_TogglePin(LD2_GPIO_PORT, LD2_PIN);
/* Insert delay 100 ms */
HAL_Delay(100);
HAL_GPIO_TogglePin(LD3_GPIO_PORT, LD3_PIN);
/* Insert delay 100 ms */
HAL_Delay(100);
HAL_GPIO_TogglePin(LD4_GPIO_PORT, LD4_PIN);
/* Insert delay 100 ms */
HAL_Delay(100);
#endif
#if 0
unsigned char data[] = "123";
HAL_UART_Transmit(&huart4, data, sizeof(data), 0xFFFF);
// printf("666\r\n");
// float a = 3.14;
// printf("a = %.2f\r\n", a);
#endif
#if 1
Accelero_Sensor_Handler(0);
// HAL_GPIO_TogglePin(LD1_GPIO_PORT, LD1_Pin);
// HAL_Delay(100);
neai_classification(input_user_buffer, output_class_buffer, &id_class);
unsigned char result[50] = {0};
uint8_t len = 0;
sprintf(result, "id_class = %d", id_class);
len = strlen(result);
HAL_UART_Transmit(&huart4, result, len, 0xFFFF);
#endif
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
/**
* @brief This function configures the MPU context of the application.
* @retval None
*/
static void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
uint32_t index = MPU_REGION_NUMBER0;
uint32_t address;
uint32_t size;
/* Disable the MPU */
HAL_MPU_Disable();
/* Initialize the background region */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.BaseAddress = 0x0;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
/* Initialize the region corresponding to external RAM */
#if defined ( __ICCARM__ )
extern uint32_t __region_EXTRAM_start__;
extern uint32_t __region_EXTRAM_end__;
address = (uint32_t)&__region_EXTRAM_start__;
size = (uint32_t)&__region_EXTRAM_end__ - (uint32_t)&__region_EXTRAM_start__ + 1;
#elif defined (__CC_ARM) || defined(__ARMCC_VERSION)
extern uint32_t Image$$RW_EXTRAM$$Base;
extern uint32_t Image$$RW_EXTRAM$$ZI$$Length;
extern uint32_t Image$$RW_EXTRAM$$Length;
address = (uint32_t)&Image$$RW_EXTRAM$$Base;
size = (uint32_t)&Image$$RW_EXTRAM$$Length + (uint32_t)&Image$$RW_EXTRAM$$ZI$$Length;
#elif defined ( __GNUC__ )
extern uint32_t __EXTRAM_BEGIN;
extern uint32_t __EXTRAM_SIZE;
address = (uint32_t)&__EXTRAM_BEGIN;
size = (uint32_t)&__EXTRAM_SIZE;
#else
#error "Compiler toolchain is unsupported"
#endif
if (size != 0)
{
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.SubRegionDisable = 0u;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_AdjustRegionAddressSize(address, size, &MPU_InitStruct);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
}
/* Initialize the non cacheable region */
#if defined ( __ICCARM__ )
/* get the region attribute form the icf file */
extern uint32_t NONCACHEABLEBUFFER_start;
extern uint32_t NONCACHEABLEBUFFER_size;
address = (uint32_t)&NONCACHEABLEBUFFER_start;
size = (uint32_t)&NONCACHEABLEBUFFER_size;
#elif defined (__CC_ARM) || defined(__ARMCC_VERSION)
extern uint32_t Image$$RW_NONCACHEABLEBUFFER$$Base;
extern uint32_t Image$$RW_NONCACHEABLEBUFFER$$Length;
extern uint32_t Image$$RW_NONCACHEABLEBUFFER$$ZI$$Length;
address = (uint32_t)&Image$$RW_NONCACHEABLEBUFFER$$Base;
size = (uint32_t)&Image$$RW_NONCACHEABLEBUFFER$$Length + (uint32_t)&Image$$RW_NONCACHEABLEBUFFER$$ZI$$Length;
#elif defined ( __GNUC__ )
extern int __NONCACHEABLEBUFFER_BEGIN;
extern int __NONCACHEABLEBUFFER_END;
address = (uint32_t)&__NONCACHEABLEBUFFER_BEGIN;
size = (uint32_t)&__NONCACHEABLEBUFFER_END - (uint32_t)&__NONCACHEABLEBUFFER_BEGIN;
#else
#error "Compiler toolchain is unsupported"
#endif
if (size != 0)
{
/* Configure the MPU attributes as Normal Non Cacheable */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_AdjustRegionAddressSize(address, size, &MPU_InitStruct);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
}
/* Initialize the region corresponding to the execution area
(external or internal flash or external or internal RAM
depending on scatter file definition) */
#if defined ( __ICCARM__ )
extern uint32_t __ICFEDIT_region_ROM_start__;
extern uint32_t __ICFEDIT_region_ROM_end__;
address = (uint32_t)&__ICFEDIT_region_ROM_start__;
size = (uint32_t)&__ICFEDIT_region_ROM_end__ - (uint32_t)&__ICFEDIT_region_ROM_start__ + 1;
#elif defined (__CC_ARM) || defined(__ARMCC_VERSION)
extern uint32_t Image$$ER_ROM$$Base;
extern uint32_t Image$$ER_ROM$$Limit;
address = (uint32_t)&Image$$ER_ROM$$Base;
size = (uint32_t)&Image$$ER_ROM$$Limit-(uint32_t)&Image$$ER_ROM$$Base;
#elif defined ( __GNUC__ )
extern uint32_t __FLASH_BEGIN;
extern uint32_t __FLASH_SIZE;
address = (uint32_t)&__FLASH_BEGIN;
size = (uint32_t)&__FLASH_SIZE;
#else
#error "Compiler toolchain is unsupported"
#endif
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.SubRegionDisable = 0u;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_AdjustRegionAddressSize(address, size, &MPU_InitStruct);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
/* Reset unused MPU regions */
for(; index < __MPU_REGIONCOUNT ; index++)
{
/* All unused regions disabled */
MPU_InitStruct.Enable = MPU_REGION_DISABLE;
MPU_InitStruct.Number = index;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
}
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/**
* @brief This function adjusts the MPU region Address and Size within an MPU configuration.
* @param Address memory address
* @param Size memory size
* @param pInit pointer to an MPU initialization structure
* @retval None
*/
static void MPU_AdjustRegionAddressSize(uint32_t Address, uint32_t Size, MPU_Region_InitTypeDef* pInit)
{
/* Compute the MPU region size */
pInit->Size = ((31 - __CLZ(Size)) - 1);
if (Size > (1 << (pInit->Size + 1)))
{
pInit->Size++;
}
uint32_t Modulo = Address % (1 << (pInit->Size - 1));
if (0 != Modulo)
{
/* Align address with MPU region size considering there is no need to increase the size */
pInit->BaseAddress = Address - Modulo;
}
else
{
pInit->BaseAddress = Address;
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
在工程的文件夹下建一个名字就叫nanoEdgeAi的文件夹(你取别的名字也可以的),把.a、.h放进来
先在工程中添加文件夹
添加文件
添加.h路径
添加库文件路径
添加库文件
rebuild index
编译,得到如下错误
C:/ST/STM32CubeIDE_1.15.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.12.3.rel1.win32_1.0.100.202403111256/tools/bin/../lib/gcc/arm-none-eabi/12.3.1/../../../../arm-none-eabi/bin/ld.exe: cannot find -l-mcpu=cortex-m7: No such file or directory
collect2.exe: error: ld returned 1 exit status
make: *** [makefile:68: GPIO_IOToggle_Appli.elf] Error 1
-l-mcpu=cortex-m7这玩意儿找不到,这玩意儿我百度找了半天都没搞明白是个什么意思,已经触摸到我的知识盲区了,无奈只能止步于此了
/*****************************************************************************/
2024.11.12补充
移植到开发板失败的问题,已经通过曲线救国的方式解决了,模型成功运行,效果很好,请看“[STM32H7R/S]测评 ⑨nano edge ai studio 训练一个模型--下 https://bbs.eeworld.com.cn/thread-1298763-1-1.html”这篇文章
/****************************************************************************/
本文的一些文件如下
最终生成的压缩包:
第四步的测试数据:
第二步的三种采样数据:
工程:
- 2024-11-07
-
回复了主题帖:
[STM32H7R/S]测评 ⑦制作一个NANO EDGE AI STUDIO采集数据使用的Data Logger
电子烂人 发表于 2024-11-7 13:06
第一步配置的是一轴,我做的电机监测,当震动传感器用,故也不需要太多
当时的截图:
感觉像是数据收 ...
这个解析结果肯定有问题,他这里都打感叹号了。
首先得确定一下你一共采样了几轮,还有每轮采样几次?
他这边解析出来采样138轮,每轮采样3次,每轮每个周采样3次。说明一个轴这个配置应该是没啥问题了。
我认为每轮采样3次太少了(这个要具体看代码你是不是写的一轮只采样3次)。还有一种可能就是数据格式有问题,导致他解析不正确
-
回复了主题帖:
[STM32H7R/S]测评 ⑦制作一个NANO EDGE AI STUDIO采集数据使用的Data Logger
电子烂人 发表于 2024-11-7 09:22
请问下,这个用的是三轴的数据吗,我用的一轴加速度但是软件采集数据不正确
我这个是3轴的,然后nano edge ai第一步我也是选择的加速度计,三轴。
你看看是不是你第一步配置的时候不是配置的1轴?还有SSCOM收到的数据、nano edge ai显示数据不正确的截图方便放一下吗?我们交流交流
- 2024-11-04
-
发表了主题帖:
[STM32H7R/S]测评 ⑦制作一个NANO EDGE AI STUDIO采集数据使用的Data Logger
本帖最后由 不爱胡萝卜的仓鼠 于 2024-11-6 21:09 编辑
我们使用nano edge ai这个软件训练模型时,需要提供样本数据用于训练。在nano edge ai中可以有2中方式上传样本数据:提前准备好的CSV文件、串口实时采集。这两种方案可以根据大家自己的实际情况来选择
CSV:适合已经有样本数据,或者不方便实时采集的场景使用。然后需要把数据按照制定文件格式填写再上传。
串口:串口实时采集的好处是可以现场采集,但是需要单片机可以通过串口实时传输制定格式的
针对串口这种方式,我们可以选择使用ST提供的现成的datalogger,也可以选择自己做的
一.ST 提供的dataLogger
在nano edge ai界面的左上角有个dataLogeer的按钮
点击后就会展示ST为你提供了哪些开发板
数量还是比较可观的,有接近20种
我们随便点一个开发板,就可以选择采集数据的传感器和配置参数
当生成后你会得到一个压缩包
这个bin直接烧录就可以使用了。因为我手上没有对应的开发板,因此没法给大家试用
如果你想得到源码,ST也是提供了的,在下面这个地方就可以得到源码
二.自己做dataLogger
因为上面说了,我没有对应的开发板,并且我们本次是H7R/S开发板的测评,所以我就用H7R/S开发板做一个dataLogger。
在写代码前,我们需要了解一下dataLogger到底实现了一个怎样的功能,或者说他需要发出什么格式的数据。软件数据说明界面如下
在界面中ST其实已经简单描述了他所需的数据内容,这个东西已经写得很生动形象了。我们需要采集很多轮的数据,对应界面中的line。然后每一轮数据采集时,需要以一定的频率采样数据,每轮采样最多采集256次。
例如我这边是采集的加速度计的数据,并且3轴数据都采集,采集M轮,每轮采样N+1次数据,那么最终得到的结果就是
line1(第一轮采样):ACC_X_0 ACC_Y_0 ACC_Z_0 ACC_X_1 ACC_Y_1 ACC_Z_1 ...... ACC_X_N ACC_Y_N ACC_Z_N
。。。。。。
lineM(第M轮采样):。。。。。。
如果你是其他数据,假设每次采样只有一个数据,假设为一个轴的加速度,那么每一轮数据如下
lineX:ACC_X_0 ACC_X_1 ...... ACC_X_N
这是最终呈现在软件中的,那么我们开发板上传数据格式是怎么样的呢?是有什么协议?而且如何区分那几个字符是这个数据,那几个字符是那个数据?每一轮的数据是如何区分的呢?这部分并没有在这个页面中详细展示。
以下是我摸索的结果,数据传输没有很正式的协议,只有一些简单的数据格式,打印都是以文本的形式发送的:【数据】【分隔符】【数据】【分隔符】。。。。。。【数据】【/r/n】【数据】【分隔符】。。。。。。
每次采样有几个数据在nano dege ai工程配置时就已经设置好了,这个软件是知道的。然后每个数据之间是通过分隔符来进行区分的,分隔符可以有很多种选择,例如逗号、空格等等,具体后面有说明。每轮数据之间用换行回车进行区分(/r/n都要,不然会识别异常)
思路都理清楚了,下面就开始修改代码,我的代码沿用上次[STM32H7R/S]测评 ⑤X-NUCLEO-IKS4A1 MEMS传感器移植的工程
首先要定义一些变量
#define ACC_SAMPLE_MAX 64
#define AXIS 3
static int acc_sample_buffer[AXIS * ACC_SAMPLE_MAX] = {0};
我们会把采样得到的数据存放到这个buffer中,然后再统一打印出来
接下来改造while1,因为我们只使用了开发板上的一个传感器,因此之前的for循环就可以不要了,直接改成如下
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
#if 1
Accelero_Sensor_Handler(0);
HAL_Delay(100);
#endif
}
/* USER CODE END 3 */
直接调用ACC传感器处理函数,入参为0,因为只有一个传感器,第一个传感器的序号在数组中就是0。delay函数是为了给打印留足时间(不知道为什么,我切回这个分支后,原先调通的printf突然不能用了,很奇怪,我就只能用HAL_UART_Transmit函数,但是他需要delay一下,不然他数据无法打印出来)
然后我们需要对Accelero_Sensor_Handler函数进行修改,原先的demo是会把很多乱七八糟的数据都打印出来,现在不需要了,我们要在里面for循环完成数据采集,然后再for循环按之前说的格式把数据打印出来。改造后的函数如下
static void Accelero_Sensor_Handler(uint32_t Instance)
{
IKS4A1_MOTION_SENSOR_Axes_t acceleration;
uint16_t i = 0;
for (i = 0; i < ACC_SAMPLE_MAX;)
{
if (0 == IKS4A1_MOTION_SENSOR_GetAxes(Instance, MOTION_ACCELERO, &acceleration))
{
acc_sample_buffer[AXIS * i] = (int)acceleration.x;
acc_sample_buffer[(AXIS * i) + 1] = (int)acceleration.y;
acc_sample_buffer[(AXIS * i) + 2] = (int)acceleration.z;
i++;
}
}
unsigned char data[50] = {0};
uint8_t len = 0;
for(i = 0; i < ACC_SAMPLE_MAX - 1; i++)
{
sprintf(data, "%d,%d,%d,", acc_sample_buffer[i], acc_sample_buffer[i+1], acc_sample_buffer[i+2]);
len = strlen(data);
HAL_UART_Transmit(&huart4, data, len, 0xFFFF);
}
sprintf(data, "%d,%d,%d", acc_sample_buffer[i], acc_sample_buffer[i+1], acc_sample_buffer[i+2]);
len = strlen(data);
HAL_UART_Transmit(&huart4, data, len, 0xFFFF);
unsigned char data2[] = {"\r\n"};
len = strlen(data2);
HAL_UART_Transmit(&huart4, data2, len, 0xFFFF);
}
然后为了提高串口打印速率,我还把波特率从默认的115200改成了921600(我直接改代码了,就不再cubemx中修改了,一来这个分支的代码以后不会再重新生成工程了,而来重新生成又要改ld,太麻烦了)
这样基本上就完工了,代码写的有点乱,调试了太久时间了,大家见谅,但是基本功能是OK的。
运行后SSCOM日志如下:
三.测试功能
接下来我们就去nano edge ai中测试一下功能。nano edge ai怎么创工程等步骤和说明会在后续的文章中说,我们今天就直接跳到数据采集的步骤。
首先需要选择上传数据,并使用串口上传数据
当我们选择串口的方式后,会弹出如下界面
需要选择开发板的串口和波特率
然后我们可以配置采样数据的最大轮数,如果不选的话,他会一直采集,知道你按下stop。反之到了最大值他就自动停止了
最后点击start就可以开始采样了,我们可以实时看到采样的轮数,还有每次采样得到的数据
采样结束后点击continue就会对数据进行解析,然后就可以得到数据解析的结果,如下界面所示
在这个界面的左边,我们就可以选择使用哪种分隔符,第一个就是逗号,也是我使用的,这块就不用改。右边就是解析得到的结果。
line_index就是采样的轮数。
nb_columns就是数据个数,例如我代码中写的3轴数据*采样64次=192个数据
如果数据正常就是白色的,如果解析有明显异常会是红色的(如果只有一两条数据有错误,那可能是串口传输误码,如果有很多建议你好好看看原始数据是否符合规定,我一开始每轮之间用了/n,他就异常了,前面5条数据全部标红,后面白,但是白的数据明显也是错误的)。
我们可以选择delete打钩,删除掉异常数据。白色数据其实也要看看,因为里面可能会存在一些采样到的数据明显是不正常的,这个就需要人工过滤了
数据检查无误后,就可以点击import,加载数据了
这些数据如何进一步使用,我们就在后续文章中说了。今天这个dataloger的制作与使用算是圆满完成
补充内容 (2024-11-12 11:49):
本文datalogger的代码中打印输出结果部分代码有重大BUG,已在链接文章中修复:[STM32H7R/S]测评 ⑨nano edge ai studio 训练一个模型--下 https://bbs.eeworld.com.cn/thread-1298763-1-1.html
- 2024-10-31
-
发表了主题帖:
[嘉楠 CanMV K230]测评 ⑩自己训练一个分类识别模型
本帖最后由 不爱胡萝卜的仓鼠 于 2024-11-1 09:43 编辑
之前我们跑的功能,基本上都是一些基础的图像识别功能,没有太多AI的加持,面对一些复杂的场景,之前的那些内容就显得不足了,需要有AI的加入
我们今天就来自己训练一个AI模型,并运行在K230开发板上,实现分类识别
一.模型训练
嘉楠官网就有模型训练的服务,免费的,对于我们这些初学者来说非常友好。地址如下:https://developer.canaan-creative.com/training/
打开网页后需要注册账号并登录
接下来我们开始添加数据集
添加成功后如图,点击名称,即可打开该数据集,我们就可以开始添加图片了
d
点击上传图片,把准备好的图片上传,我这边准备了几十张鼠标的照片,全部上传
等待上传完成
上传完成后,会弹出如图内容,输入标签名称,我这边是鼠标,那我就叫mouse,然后再点击确定
刚才上传的图片就全部被打上了鼠标的标签
接下来我还准备了笔的照片,流程和鼠标一致,我就不再赘述了(这边的照片数量越多越好,以前官方教程中是推荐100张以上,但是现在这个貌似删掉了。并且官方提供的训练集也是50张,因此50张应该是基本够用)
最后点击“训练”,开始模型训练
训练参数填写如下
其中nnacse的版本要根据实际下载到开发板上的镜像说决定,我现在运行的是1.0版本,根据官网下载固件的名称可以得到对应版本为2.8.3
迭代次数默认700,我为了快速完成训练,就调整为100。这个数字越大训练时长越长,效果我认为也会越好
接下来就是坐等训练结束
经过漫长的等待,训练完成后界面如图。
同时你注册账号的邮箱会收到一封邮件,通知你训练完成,并给结果
下载得到的压缩包内容如下
cls_results:里面是一些测试用的图片
cpp_deployment_source.zip:C++版本的部署包
mp_deployment_source.zip:MicroPython版本部署包
README.md:使用指导手册
我这边使用的micropython的固件,所以我实际需要的是 “mp_deployment_source.zip”这个文件,其他的都不重要
(以上截图是我第一次训练时的截图,中途因为一些小问题,导致我又调整参数重新训练了模型。下面的模型是每个分类160张图片,700次迭代,其他参数不变)
二.使用模型
把mp_deployment_source.zip解压,内容如下
将这个文件夹拷贝到开发板的SD卡中
2.1 视频识别
打开IDE,连接开发板后,打开储存在SD卡中的“cls_video.py”文件
运行,即可在日志串口看到识别的结果,效果如下,基本上识别还是准确的,可信度基本上都是0.8多0.9多
[localvideo]8530d9915a5c07353098966c42cb923b[/localvideo]
cls_video.py的代码如下
import os
import ujson
from media.sensor import *
from media.display import *
from media.media import *
from time import *
import nncase_runtime as nn
import ulab.numpy as np
import time
import utime
import image
import random
import gc
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
OUT_RGB888P_WIDTH = ALIGN_UP(1280, 16)
OUT_RGB888P_HEIGH = 720
root_path="/sdcard/mp_deployment_source/"
config_path=root_path+"deploy_config.json"
deploy_conf={}
debug_mode=1
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
def read_deploy_config(config_path):
# 打开JSON文件以进行读取deploy_config
with open(config_path, 'r') as json_file:
try:
# 从文件中加载JSON数据
config = ujson.load(json_file)
# 打印数据(可根据需要执行其他操作)
#print(config)
except ValueError as e:
print("JSON 解析错误:", e)
return config
# 任务后处理
def softmax(x):
exp_x = np.exp(x - np.max(x))
return exp_x / np.sum(exp_x)
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def classification():
print("start")
# 使用json读取内容初始化部署变量
deploy_conf=read_deploy_config(config_path)
kmodel_name=deploy_conf["kmodel_path"]
labels=deploy_conf["categories"]
confidence_threshold=deploy_conf["confidence_threshold"]
img_size=deploy_conf["img_size"]
num_classes=deploy_conf["num_classes"]
cls_idx=-1
score=0.0
# init kpu and load kmodel
kpu = nn.kpu()
ai2d = nn.ai2d()
kpu.load_kmodel(root_path+kmodel_name)
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,img_size[0],img_size[1]])
# 初始化并配置sensor
sensor = Sensor()
sensor.reset()
# 设置镜像
sensor.set_hmirror(False)
# 设置翻转
sensor.set_vflip(False)
# 通道0直接给到显示VO,格式为YUV420
sensor.set_framesize(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
sensor.set_pixformat(PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# 通道2给到AI做算法处理,格式为RGB888
sensor.set_framesize(width = OUT_RGB888P_WIDTH , height = OUT_RGB888P_HEIGH, chn=CAM_CHN_ID_2)
sensor.set_pixformat(PIXEL_FORMAT_RGB_888_PLANAR, chn=CAM_CHN_ID_2)
# 绑定通道0的输出到vo
sensor_bind_info = sensor.bind_info(x = 0, y = 0, chn = CAM_CHN_ID_0)
Display.bind_layer(**sensor_bind_info, layer = Display.LAYER_VIDEO1)
# 设置为LT9611显示,默认1920x1080
Display.init(Display.LT9611, to_ide = True)
#创建OSD图像
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888)
try:
# media初始化
MediaManager.init()
# 启动sensor
sensor.run()
# init
rgb888p_img = None
ai2d_input_tensor = None
data = np.ones((1,3,img_size[0],img_size[1]),dtype=np.uint8)
ai2d_output_tensor = nn.from_numpy(data)
while True:
with ScopedTiming("total",debug_mode > 0):
rgb888p_img = sensor.snapshot(chn=CAM_CHN_ID_2)
if rgb888p_img == -1:
print("capture_image failed")
camera.release_image(CAM_DEV_ID_0, CAM_CHN_ID_2, rgb888p_img)
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_builder.run(ai2d_input_tensor, ai2d_output_tensor)
# set input
kpu.set_input_tensor(0, ai2d_output_tensor)
# run kmodel
kpu.run()
# get output
results = []
for i in range(kpu.outputs_size()):
output_data = kpu.get_output_tensor(i)
result = output_data.to_numpy()
del output_data
results.append(result)
if num_classes>2:
softmax_res=softmax(results[0][0])
cls_idx=np.argmax(softmax_res)
if softmax_res[cls_idx]>confidence_threshold:
score=softmax_res[cls_idx]
print("classification result:")
print(labels[cls_idx])
print("score",score)
else:
cls_idx=-1
score=0.0
else:
sigmoid_res=sigmoid(results[0][0][0])
if sigmoid_res>confidence_threshold:
cls_idx=1
score=sigmoid_res
print("classification result:")
print(labels[1])
print("score",score)
else:
cls_idx=0
score=1-sigmoid_res
print("classification result:")
print(labels[0])
print("score",score)
osd_img.clear()
if cls_idx>=0:
osd_img.draw_string_advanced(5,5,32,"result:"+labels[cls_idx]+" score:"+str(score),color=(0,255,0))
Display.show_image(osd_img, 0, 0, Display.LAYER_OSD3)
rgb888p_img = None
gc.collect() #用于需要调用gc.mem_alloc()的内存
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
del ai2d_input_tensor
del ai2d_output_tensor
#停止摄像头输出
sensor.stop()
#去初始化显示设备
Display.deinit()
#释放媒体缓冲区
MediaManager.deinit()
gc.collect()
time.sleep(1)
nn.shrink_memory_pool()
print("end")
return 0
if __name__=="__main__":
classification()
2.2 图片识别
结果中还有一个图片的示例程序
需要拷贝一张图片到sd卡中,我们可以从训练结果中的cls_results文件夹下得到一些样本的照片,如果你有其他照片也可以(但是需要.jpg格式)。修改照片的名称为test,拷贝到SD卡中
然后ide中运行的代码为SD卡中的cls_image.py,具体的操作与上面的一样,我就不赘述了
如果运行提示以下内容,可能的原因是放到SD卡中的图片太大了,可以看到,我刚才拷贝进去的有1.63M,太大了,缓冲区不够用了。需要用软件压缩一下,我选择的是降低分辨率,我选择320*240.压缩完了就只有32KB了
运行结果如下,
cls_image.py的代码如下
import os
import ujson
from time import *
import nncase_runtime as nn
import ulab.numpy as np
import time
import image
import gc
import utime
root_path="/sdcard/mp_deployment_source/" # root_path要以/结尾
config_path=root_path+"deploy_config.json"
image_path=root_path+"test.jpg"
deploy_conf={}
debug_mode=1
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
def read_img(img_path):
img_data = image.Image(img_path)
img_data_rgb888=img_data.to_rgb888()
img_hwc=img_data_rgb888.to_numpy_ref()
shape=img_hwc.shape
img_tmp = img_hwc.reshape((shape[0] * shape[1], shape[2]))
img_tmp_trans = img_tmp.transpose()
img_res=img_tmp_trans.copy()
img_return=img_res.reshape((shape[2],shape[0],shape[1]))
return img_return
# 读取deploy_config.json文件
def read_deploy_config(config_path):
# 打开JSON文件以进行读取deploy_config
with open(config_path, 'r') as json_file:
try:
# 从文件中加载JSON数据
config = ujson.load(json_file)
# 打印数据(可根据需要执行其他操作)
#print(config)
except ValueError as e:
print("JSON 解析错误:", e)
return config
# 任务后处理
def softmax(x):
exp_x = np.exp(x - np.max(x))
return exp_x / np.sum(exp_x)
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def classification():
print("--------------start-----------------")
# 使用json读取内容初始化部署变量
deploy_conf=read_deploy_config(config_path)
kmodel_name=deploy_conf["kmodel_path"]
labels=deploy_conf["categories"]
confidence_threshold=deploy_conf["confidence_threshold"]
img_size=deploy_conf["img_size"]
num_classes=deploy_conf["num_classes"]
cls_idx=-1
# ai2d输入输出初始化
ai2d_input = read_img(image_path)
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_input_shape=ai2d_input.shape
data = np.ones((1,3,img_size[0],img_size[1]),dtype=np.uint8)
ai2d_out = nn.from_numpy(data)
# init kpu and load kmodel
kpu = nn.kpu()
ai2d = nn.ai2d()
kpu.load_kmodel(root_path+kmodel_name)
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
ai2d_builder = ai2d.build([1,3,ai2d_input_shape[1],ai2d_input_shape[2]], [1,3,img_size[0],img_size[1]])
with ScopedTiming("total",debug_mode > 0):
ai2d_builder.run(ai2d_input_tensor, ai2d_out)
kpu.set_input_tensor(0, ai2d_out)
kpu.run()
del ai2d_input_tensor
del ai2d_out
# 获取分类结果
results = []
for i in range(kpu.outputs_size()):
data = kpu.get_output_tensor(i)
result = data.to_numpy()
results.append(result)
# 后处理
if num_classes>2:
softmax_res=softmax(results[0][0])
res_idx=np.argmax(softmax_res)
if softmax_res[res_idx]>confidence_threshold:
cls_idx=res_idx
print("classification result:")
print(labels[res_idx])
print("score",softmax_res[cls_idx])
image_draw=image.Image(image_path).to_rgb565()
image_draw.draw_string(10, 10,labels[cls_idx] , scale=2,color=(255,0,255,0))
image_draw.compress_for_ide()
image_draw.save(root_path+"cls_result.jpg")
else:
cls_idx=-1
else:
sigmoid_res=sigmoid(results[0][0][0])
if sigmoid_res>confidence_threshold:
cls_idx=1
print("classification result:")
print(labels[cls_idx])
print("score",sigmoid_res)
image_draw=image.Image(image_path).to_rgb565()
image_draw.draw_string(10, 10,labels[cls_idx] , scale=2,color=(255,0,255,0))
image_draw.compress_for_ide()
image_draw.save(root_path+"cls_result.jpg")
else:
cls_idx=0
print("classification result:")
print(labels[cls_idx])
print("score",1-sigmoid_res)
image_draw=image.Image(image_path).to_rgb565()
image_draw.draw_string(10, 10,labels[cls_idx] , scale=2,color=(255,0,255,0))
image_draw.compress_for_ide()
image_draw.save(root_path+"cls_result.jpg")
del data
del ai2d
del ai2d_builder
del kpu
print("---------------end------------------")
gc.collect()
nn.shrink_memory_pool()
if __name__=="__main__":
nn.shrink_memory_pool()
classification()
三.模型训练经验分享
1.单个标签的图片不要太少,以前官方教程中是推荐100张以上,但是现在这个貌似删掉了。并且官方提供的训练集也是50张,因此50张应该是基本够用。但是小于50张,训练效果可能会变差
2.图片不要太多,当然不是说多了会对模型训练造成影响,对于训练来说是越多越好的,但是训练平台资源有限,做了1G的限制(应该是所有图片总大小不能超过1G),超过1G不能开始训练。不然一次性上传的太多,回头一张一张删除累死人(建议官方改进一下,可以做一个上传的限制,或者允许批量删除)
3.不要一次性上传很多图片,我一次性上传150张,上传时会报错,具体报错信息我也没太看明白,后面换成50张传一次,分批传就可以了
4.如果遇到问题,自己多次尝试实在解决不了问题的,可以考虑去嘉楠官网提问。我做模型测试时,遇到问题,捣鼓了很久,没解决,在晚上提问后,官方技术人员第二天就回复了,并解决了问题。给官方的技术人员大大的点赞
本文最终训练得到的结果见附件,大家可以下载后复现
- 2024-10-25
-
回复了主题帖:
[BearPi-Pico H2821]测评 ⑥丢包及连接稳定性测试
晚风吹散 发表于 2024-10-23 17:09
另外想咨询您几个问题,不胜感激:
1. 海思Hispark IDE中,当我在系统配置 - application 中,切换了Ser ...
需要重新编译,他本质上就是设置编译脚本中的define,为了避免这个值在某个.h中有使用,最好是清除全编一次。
第二个问题,也是由上面的事情导致的,他这个define是没有明确的值的,只有在编译时才会从编译脚本中得到,当vscode读.c.h文件时他是无法得到define的具体值,那他就按默认值来了,所以他显示的和你配置的会不一致
- 2024-10-22
-
发表了主题帖:
[STM32H7R/S]测评 ⑥使用CUBE AI移植模型至开发板运行
本帖最后由 不爱胡萝卜的仓鼠 于 2024-11-3 20:59 编辑
在开始自己训练模型并运行到开发板上前,我得先尝试一下把一个现成的模型移植到STM32开发板上,先积累经验。
训练好的模型是无法直接在STM32上运行的,我浅薄的理解就是STM32只是一个单片机,他的硬件能力和软件架构无法直接运行AI模型,那么就需要一个工具,把这些复杂的东西转换成C语言的代码,这样STM32单片机就可以运行AI模型里,CUBE AI就是ST为各位开发者准备好的工具。
一.安装CUBE AI
CUBE AI是CUBEMX中的一个软件包,安装很简单,只需要打开cubemx,点击“Help”->“Manage embedded software packages”。
然后在弹出的页面中找到“STMicroelectronics”->“X-CUBE-AI”,再选择你需要的版本,再点击“install”即可。我这边以前期安装了9.0.0。今天写文章时最新已经到了9.1.0,我这边就偷懒不更新了
二.使用CUBE AI移植模型
2.1 激活CUBE AI
打开我们的串口工程,在左侧找到“X-CUBE-AI”这个选项,点击
然后弹出的界面如下
选择CUBE AI的版本,我这边安装的是9.0.0,那就选择9.0.0。然后勾选core,然后APP根据自己的需求选择,我这边 就选择Validation了。APP的这三个选项含义如下
开启成功界面如下,其中的勾都是自动勾选的,不需要我们再手动点
2.2 添加网络
接下来就需要添加一个网络了(其实就是添加一个AI模型),我因为没有自己训练的模型,那我就直接使用上次云AI下载下来的工程中的模型(这个模型其实也是从ST的Model Zoo下载的,大家可以直接访问如下网址:https://stm32ai.st.com/model-zoo/,即可获取ST为大家准备好的各种模型,也支持大家自己再次增加样本,重新训练)
选择模型的类型,我这边是Keras,它还支持TFLite、0NNX。大家根据自己的实际情况选择即可。然后选择网络的文件
串口输出我们就不用管了,因为之前的工程串口已经初始化好了,这边他也是自动匹配上了
添加模型到此就OK了
接下来分析一下,不进行分析的话,待会儿不允许我们生成工程
我这儿有个报错,需要修改一下注册表(如何修改注册表我就不赘述了,直接百度就好)
修改后再次分析,就可以看到进度条在动了
分析完成如下图
Analyzing model
C:/Users/Administrator/STM32Cube/Repository/Packs/STMicroelectronics/X-CUBE-AI/9.0.0/Utilities/windows/stedgeai.exe analyze --target stm32h7 --name network -m C:/Users/Administrator/Downloads/CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset.h5 --compression none --verbosity 1 --allocate-inputs --allocate-outputs --workspace C:/Users/ADMINI~1/AppData/Local/Temp/mxAI_workspace171808875528810010910823047711904506 --output C:/Users/Administrator/.stm32cubemx/network_output
STEdgeAI Core v9.0.0-19802
Creating c (debug) info json file C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171808875528810010910823047711904506\network_c_info.json
Exec/report summary (analyze)
----------------------------------------------------------------------------------------------------------------------------------------
model file : C:\Users\Administrator\Downloads\CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset.h5
type : keras
c_name : network
compression : none
options : allocate-inputs, allocate-outputs
optimization : balanced
target/series : stm32h7
workspace dir : C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171808875528810010910823047711904506
output dir : C:\Users\Administrator\.stm32cubemx\network_output
model_fmt : float
model_name : CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset
model_hash : 0x1e108c42827f4c62598744246d259703
params # : 2,752 items (10.75 KiB)
----------------------------------------------------------------------------------------------------------------------------------------
input 1/1 : 'input_1', f32(1x8x8x2), 512 Bytes, activations
output 1/1 : 'dense_1', f32(1x8), 32 Bytes, activations
macc : 8,520
weights (ro) : 11,008 B (10.75 KiB) (1 segment)
activations (rw) : 1,024 B (1024 B) (1 segment) *
ram (total) : 1,024 B (1024 B) = 1,024 + 0 + 0
----------------------------------------------------------------------------------------------------------------------------------------
(*) 'input'/'output' buffers can be used from the activations buffer
Model name - CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset
------ ------------------------------ ------------------- ------------- ------- ---------------
m_id layer (original) oshape param/size macc connected to
------ ------------------------------ ------------------- ------------- ------- ---------------
0 input_1 (InputLayer) [b:1,h:8,w:8,c:2]
------ ------------------------------ ------------------- ------------- ------- ---------------
1 conv2d (Conv2D) [b:1,h:6,w:6,c:8] 152/608 5,192 input_1
------ ------------------------------ ------------------- ------------- ------- ---------------
2 activation (Activation) [b:1,h:6,w:6,c:8] 288 conv2d
------ ------------------------------ ------------------- ------------- ------- ---------------
3 max_pooling2d (MaxPooling2D) [b:1,h:3,w:3,c:8] 288 activation
------ ------------------------------ ------------------- ------------- ------- ---------------
5 flatten (Flatten) [b:1,c:72] max_pooling2d
------ ------------------------------ ------------------- ------------- ------- ---------------
6 dense_dense (Dense) [b:1,c:32] 2,336/9,344 2,336 flatten
dense (Dense) [b:1,c:32] 32 dense_dense
------ ------------------------------ ------------------- ------------- ------- ---------------
7 dense_1_dense (Dense) [b:1,c:8] 264/1,056 264 dense
dense_1 (Dense) [b:1,c:8] 120 dense_1_dense
------ ------------------------------ ------------------- ------------- ------- ---------------
model: macc=8,520 weights=11,008 activations=-- io=--
Number of operations per c-layer
------- ------ ------------------------ ------- --------------
c_id m_id name (type) #op type
------- ------ ------------------------ ------- --------------
0 3 conv2d (Conv2D) 5,768 smul_f32_f32
1 6 dense_dense (Dense) 2,336 smul_f32_f32
2 6 dense (Nonlinearity) 32 op_f32_f32
3 7 dense_1_dense (Dense) 264 smul_f32_f32
4 7 dense_1 (Nonlinearity) 120 op_f32_f32
------- ------ ------------------------ ------- --------------
total 8,520
Number of operation types
---------------- ------- -----------
operation type # %
---------------- ------- -----------
smul_f32_f32 8,368 98.2%
op_f32_f32 152 1.8%
Complexity report (model)
------ --------------- ------------------------- ------------------------- --------
m_id name c_macc c_rom c_id
------ --------------- ------------------------- ------------------------- --------
3 max_pooling2d |||||||||||||||| 67.7% | 5.5% [0]
6 dense_dense ||||||| 27.8% |||||||||||||||| 84.9% [1, 2]
7 dense_1_dense | 4.5% || 9.6% [3, 4]
------ --------------- ------------------------- ------------------------- --------
macc=8,520 weights=11,008 act=1,024 ram_io=0
Requested memory size per segment ("stm32h7" series)
----------------------------- -------- -------- ------- -------
module text rodata data bss
----------------------------- -------- -------- ------- -------
NetworkRuntime900_CM7_GCC.a 10,220 0 0 0
network.o 584 40 1,796 168
network_data.o 52 16 88 0
lib (toolchain)* 318 328 0 0
----------------------------- -------- -------- ------- -------
RT total** 11,174 384 1,884 168
----------------------------- -------- -------- ------- -------
weights 0 11,008 0 0
activations 0 0 0 1,024
io 0 0 0 0
----------------------------- -------- -------- ------- -------
TOTAL 11,174 11,392 1,884 1,192
----------------------------- -------- -------- ------- -------
* toolchain objects (libm/libgcc*)
** RT - AI runtime objects (kernels+infrastructure)
Summary per type of memory device
--------------------------------------------
FLASH % RAM %
--------------------------------------------
RT total 13,442 55.0% 2,052 66.7%
--------------------------------------------
TOTAL 24,450 3,076
--------------------------------------------
Creating txt report file C:\Users\Administrator\.stm32cubemx\network_output\network_analyze_report.txt
elapsed time (analyze): 7.829s
Model file: CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset.h5
Total Flash: 24450 B (23.88 KiB)
Weights: 11008 B (10.75 KiB)
Library: 13442 B (13.13 KiB)
Total Ram: 3076 B (3.00 KiB)
Activations: 1024 B
Library: 2052 B (2.00 KiB)
Input: 512 B (included in Activations)
Output: 32 B (included in Activations)
Done
Analyze complete on AI model
在这里我们还可以在电脑上进行模拟测试
电脑模拟测试完成后,日志如下
Starting AI validation on desktop with random data...
C:/Users/Administrator/STM32Cube/Repository/Packs/STMicroelectronics/X-CUBE-AI/9.0.0/Utilities/windows/stedgeai.exe validate --target stm32h7 --name network -m C:/Users/Administrator/Downloads/CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset.h5 --compression none --verbosity 1 --allocate-inputs --allocate-outputs --workspace C:/Users/ADMINI~1/AppData/Local/Temp/mxAI_workspace171971186255410016731057912494845799 --output C:/Users/Administrator/.stm32cubemx/network_output
STEdgeAI Core v9.0.0-19802
Setting validation data...
generating random data, size=10, seed=42, range=(0, 1)
I[1]: (10, 8, 8, 2)/float32, min/max=[0.005, 1.000], mean/std=[0.498, 0.294], input_1
No output/reference samples are provided
Creating c (debug) info json file C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171971186255410016731057912494845799\network_c_info.json
Copying the AI runtime files to the user workspace: C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171971186255410016731057912494845799\inspector_network\workspace
Exec/report summary (validate)
----------------------------------------------------------------------------------------------------------------------------------------
model file : C:\Users\Administrator\Downloads\CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset.h5
type : keras
c_name : network
compression : none
options : allocate-inputs, allocate-outputs
optimization : balanced
target/series : stm32h7
workspace dir : C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171971186255410016731057912494845799
output dir : C:\Users\Administrator\.stm32cubemx\network_output
model_fmt : float
model_name : CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset
model_hash : 0x1e108c42827f4c62598744246d259703
params # : 2,752 items (10.75 KiB)
----------------------------------------------------------------------------------------------------------------------------------------
input 1/1 : 'input_1', f32(1x8x8x2), 512 Bytes, activations
output 1/1 : 'dense_1', f32(1x8), 32 Bytes, activations
macc : 8,520
weights (ro) : 11,008 B (10.75 KiB) (1 segment)
activations (rw) : 1,024 B (1024 B) (1 segment) *
ram (total) : 1,024 B (1024 B) = 1,024 + 0 + 0
----------------------------------------------------------------------------------------------------------------------------------------
(*) 'input'/'output' buffers can be used from the activations buffer
Running the Keras model...
Running the STM AI c-model (AI RUNNER)...(name=network, mode=HOST)
X86 shared lib (C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171971186255410016731057912494845799\inspector_network\workspace\lib\libai_network.dll) ['network']
Summary 'network' - ['network']
------------------------------------------------------------------------------------------
inputs/ouputs : 1/1
input_1 : f32[1,8,8,2], 512 Bytes, in activations buffer
output_1 : f32[1,1,1,8], 32 Bytes, in activations buffer
n_nodes : 5
compile_datetime : Nov 3 2024 20:50:43
activations : 1024
weights : 11008
macc : 8520
------------------------------------------------------------------------------------------
tools : Legacy ST.AI 9.0.0
capabilities : IO_ONLY, PER_LAYER, PER_LAYER_WITH_DATA
device : AMD64, AMD64 Family 23 Model 1 Stepping 1, AuthenticAMD, Windows
------------------------------------------------------------------------------------------
NOTE: duration and exec time per layer is just an indication. They are dependent of the HOST-machine work-load.
ST.AI Profiling results v1.2 - "network"
------------------------------------------------------------
nb sample(s) : 10
duration : 0.015 ms by sample (0.008/0.069/0.018)
macc : 8520
------------------------------------------------------------
Inference time per node
-------------------------------------------------------------------------------
c_id m_id type dur (ms) % cumul name
-------------------------------------------------------------------------------
0 3 Conv2dPool (0x109) 0.009 58.4% 58.4% ai_node_0
1 6 Dense (0x104) 0.003 17.4% 75.8% ai_node_1
2 6 NL (0x107) 0.001 8.1% 83.9% ai_node_2
3 7 Dense (0x104) 0.000 2.7% 86.6% ai_node_3
4 7 NL (0x107) 0.002 12.8% 99.3% ai_node_4
-------------------------------------------------------------------------------
total 0.015
-------------------------------------------------------------------------------
Statistic per tensor
-------------------------------------------------------------------------------
tensor # type[shape]:size min max mean std name
-------------------------------------------------------------------------------
I.0 10 f32[1,8,8,2]:512 0.005 1.000 0.498 0.294 input_1
O.0 10 f32[1,1,1,8]:32 0.000 1.000 0.125 0.321 output_1
-------------------------------------------------------------------------------
Saving validation data...
output directory: C:\Users\Administrator\.stm32cubemx\network_output
creating C:\Users\Administrator\.stm32cubemx\network_output\network_val_io.npz
m_outputs_1: (10, 1, 1, 8)/float32, min/max=[0.000, 1.000], mean/std=[0.125, 0.321], dense_1
c_outputs_1: (10, 1, 1, 8)/float32, min/max=[0.000, 1.000], mean/std=[0.125, 0.321], dense_1
Computing the metrics...
Cross accuracy report #1 (reference vs C-model)
----------------------------------------------------------------------------------------------------
notes: - the output of the reference model is used as ground truth/reference value
- 10 samples (8 items per sample)
acc=100.00%, rmse=0.000000063, mae=0.000000015, l2r=0.000000183, nse=1.000, cos=1.000
8 classes (10 samples)
------------------------------------------------
C0 10 . . . . . . .
C1 . 0 . . . . . .
C2 . . 0 . . . . .
C3 . . . 0 . . . .
C4 . . . . 0 . . .
C5 . . . . . 0 . .
C6 . . . . . . 0 .
C7 . . . . . . . 0
Evaluation report (summary)
--------------------------------------------------------------------------------------------------------------------------------------
Output acc rmse mae l2r mean std nse cos tensor
--------------------------------------------------------------------------------------------------------------------------------------
X-cross #1 100.00% 0.0000001 0.0000000 0.0000002 -0.0000000 0.0000001 1.0000000 1.0000000 dense_1, (8,), m_id=[7]
--------------------------------------------------------------------------------------------------------------------------------------
acc : Classification accuracy (all classes)
rmse : Root Mean Squared Error
mae : Mean Absolute Error
l2r : L2 relative error
nse : Nash-Sutcliffe efficiency criteria, bigger is better, best=1, range=(-inf, 1]
cos : COsine Similarity, bigger is better, best=1, range=(0, 1]
Creating txt report file C:\Users\Administrator\.stm32cubemx\network_output\network_validate_report.txt
elapsed time (validate): 7.011s
Validation ended
然后我们可以选择是否压缩,还有选择平衡还是要快还是省RAM,这个和我们之前云AI的相似u,就不多说了。这样通过调整和PC模拟,我们就可以在生成工程前得到一起预期的结果,大大节约时间
2.3 其他必须开启的外设
CPU的I CACHE、D CACHE、ART均需要打开(我这边没找到ART,那就先不管,如果你有的话,就给他打开)
然后还要打开CRC
2.4 生成工程
生成工程时要把heap最小值调整到0x2000,然后再生成工程
生成工程时不能有警告,我刚才就是没有分析模型,他给我警告了,如果强行生成工程,可能会出问题
2.5 小修改
我这个工程有点奇奇怪怪的,所以还得额外做点小修改。正常工程是不需要的
首先是ld文件要换回来(具体操作见前面的文章)
第二有个syscall.c的文件会被删除,就导致编译过不了,要把这个文件给弄回来。(弄回来编译会有警告,但是问题不大,代码可以运行,我一时半会儿也没有办法解决这个问题)
三.运行Validation测试工程
因为我们刚才选择Validation测试工程,因此,上电后会打印出各种信息,然后允许用户输入CMD
上电日志如下
[20:41:39.248]收←◆
#
# AI Validation 7.1
#
Compiled with GCC 12.3.1
STM32 device configuration...
Device : DevID:0x0485 (STM32H7[R,]Sxx) RevID:0x1003
Core Arch. : M7 - FPU used
HAL version : 0x01010000
SYSCLK clock : 600 MHz
HCLK clock : 300 MHz
FLASH conf. : ACR=0x00000037 - latency=7
CACHE conf. : $I/$D=(True,True)
[20:41:39.379]收←◆ Timestamp : SysTick + DWT (delay(1)=1.000 ms)
AI platform (API 1.1.0 - RUNTIME 9.0.0)
Discovering the network(s)...
Found network "network"
Creating the network "network"..
Initializing the network
Network informations...
model name : network
model signature : 0x1e108c42827f4c62598744246d259703
model datetime : Sun Nov 3 20:31:53 2024
compile datetime : Nov 3 2024 20:32:53
tools version : 9.0.0
complexity : 8520 MACC
c-nodes : 5
map_activations : 1
[0] @0x24000D60/1024
map_weights : 1
[0] @0x70013060/11008
n_inputs/n_outputs : 1/1
I[0] (1,8,8,2)128/float32 @0x24000DE0/512
O[0] (1,1,1,8)8/float32 @0x24000D60/32
-------------------------------------------
| READY to receive a CMD from the HOST... |
-------------------------------------------
# Note: At this point, default ASCII-base terminal should be closed
# and a serial COM interface should be used
# (i.e. Python ai_runner module). Protocol version = 3.1
看到这个就表示我们的代码已经成功跑起来了。
此时我们要关闭串口工具,然后会到cubemx中,点击在target上验证
选择开发板的串口,然后波特率用默认的115200
等待开发板与上位机交互,完成测试
测试结果如下
Starting AI validation on target with random data...
C:/Users/Administrator/STM32Cube/Repository/Packs/STMicroelectronics/X-CUBE-AI/9.0.0/Utilities/windows/stedgeai.exe validate --target stm32h7 --name network -m C:/Users/Administrator/Downloads/CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset.h5 --compression none --verbosity 1 --allocate-inputs --allocate-outputs --workspace C:/Users/ADMINI~1/AppData/Local/Temp/mxAI_workspace171943553747800013843934305667415686 --output C:/Users/Administrator/.stm32cubemx/network_output --mode target --desc serial:COM49:115200
STEdgeAI Core v9.0.0-19802
Setting validation data...
generating random data, size=10, seed=42, range=(0, 1)
I[1]: (10, 8, 8, 2)/float32, min/max=[0.005, 1.000], mean/std=[0.498, 0.294], input_1
No output/reference samples are provided
Creating c (debug) info json file C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171943553747800013843934305667415686\network_c_info.json
Exec/report summary (validate)
----------------------------------------------------------------------------------------------------------------------------------------
model file : C:\Users\Administrator\Downloads\CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset.h5
type : keras
c_name : network
compression : none
options : allocate-inputs, allocate-outputs
optimization : balanced
target/series : stm32h7
workspace dir : C:\Users\ADMINI~1\AppData\Local\Temp\mxAI_workspace171943553747800013843934305667415686
output dir : C:\Users\Administrator\.stm32cubemx\network_output
model_fmt : float
model_name : CNN2D_ST_HandPosture_8classes_hand_posture_ST_VL53L5CX_handposture_dataset
model_hash : 0x1e108c42827f4c62598744246d259703
params # : 2,752 items (10.75 KiB)
----------------------------------------------------------------------------------------------------------------------------------------
input 1/1 : 'input_1', f32(1x8x8x2), 512 Bytes, activations
output 1/1 : 'dense_1', f32(1x8), 32 Bytes, activations
macc : 8,520
weights (ro) : 11,008 B (10.75 KiB) (1 segment)
activations (rw) : 1,024 B (1024 B) (1 segment) *
ram (total) : 1,024 B (1024 B) = 1,024 + 0 + 0
----------------------------------------------------------------------------------------------------------------------------------------
(*) 'input'/'output' buffers can be used from the activations buffer
Running the Keras model...
Running the STM AI c-model (AI RUNNER)...(name=network, mode=TARGET)
INTERNAL ERROR: E801(HwIOError): Invalid firmware - COM49:115200
Validation ended
-
回复了主题帖:
[BearPi-Pico H2821]测评 ⑥丢包及连接稳定性测试
晚风吹散 发表于 2024-10-22 14:00
仓鼠老师,我下载您文中github链接的代码仓,搜索不到my_sle_send_data、create_send_task…… ...
你下载下来后要把当前分支置到最新的一次提交,默认拉下来应该是在首次提交的那个位置上
- 2024-10-20
-
发表了主题帖:
[STM32H7R/S]测评 ⑤X-NUCLEO-IKS4A1 MEMS传感器移植
本帖最后由 不爱胡萝卜的仓鼠 于 2024-10-20 18:16 编辑
本文是为了后续玩NanoEdge AI Studio做的前期准备。这个软件是一个可以用户自己训练模型的软件,可以实现异常检测、异常值检测、分类和回归库。具体细节我们后续具体文章介绍。
训练模型需要有样本数据输入,我这边打算使用加速度计数据实现某种动作的识别。刚好我手上有一块X-NUCLEO-IKS4A1开发板,上面就有加速度计,所以今天就先把这块开发板的相关内容移植过来
一.安装X-NUCLEO-IKS4A1开发板
X-NUCLEO-IKS4A1开发板使用arduino 的接口,对着开发板直接插上就可以用了,非常方便。开发板上的配置跳帽如图,使用的是默认的跳帽
二.cubemx配置
工程就用之前的,我们继续修改
首先要确定一下arduino接口上使用的IIC接口对应板子上用的那两个GPIO,看了一下文档,使用的的PB6、PB9
首先打开flash(因为这个不打开IIC无法打开,cubemx有对应的提示)
然后我们打开IIC,并且把引脚配置正确
IIC速度改成fast模式
IIC至此就打开完成了
接下来把mems的插件加进来,在“Middleware and Software Packs”中找到X-CUBE-MEMS1,点击后会出现一个弹窗,把X-NUCLEO-IKS4A1给选上(对应的包我已经安装好了)
然后再把X-CUBE-MEMS1打开,并配对实际使用的IIC
这样配置也OK了,可以生成工程了
三.代码修改
参照U575匹配X-NUCLEO-IKS4A1的demo源码,对main.c进行修改,我这边只初始化LSM6DSO16IS,然后读取其ACC数据。main.c代码如下
/* USER CODE BEGIN Header */
/**
******************************************************************************
* [url=home.php?mod=space&uid=1307177]@File[/url] GPIO/GPIO_IOToggle/Src/main.c
* [url=home.php?mod=space&uid=1315547]@author[/url] MCD Application Team
* [url=home.php?mod=space&uid=159083]@brief[/url] This example describes how to configure and use GPIOs through
* the STM32H7RSxx HAL API.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "flash.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "iks4a1_motion_sensors.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct displayFloatToInt_s {
int8_t sign; /* 0 means positive, 1 means negative*/
uint32_t out_int;
uint32_t out_dec;
} displayFloatToInt_t;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define MAX_BUF_SIZE 256
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
static uint8_t verbose = 1; /* Verbose output to UART terminal ON/OFF. */
static IKS4A1_MOTION_SENSOR_Capabilities_t MotionCapabilities[IKS4A1_MOTION_INSTANCES_NBR];
static char dataOut[MAX_BUF_SIZE];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
static void MPU_AdjustRegionAddressSize(uint32_t Address, uint32_t Size, MPU_Region_InitTypeDef* pInit);
static void MPU_Config(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart4 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
static void floatToInt(float in, displayFloatToInt_t *out_value, int32_t dec_prec)
{
if(in >= 0.0f)
{
out_value->sign = 0;
}else
{
out_value->sign = 1;
in = -in;
}
in = in + (0.5f / pow(10, dec_prec));
out_value->out_int = (int32_t)in;
in = in - (float)(out_value->out_int);
out_value->out_dec = (int32_t)trunc(in * pow(10, dec_prec));
}
static void Accelero_Sensor_Handler(uint32_t Instance)
{
float odr;
int32_t fullScale;
IKS4A1_MOTION_SENSOR_Axes_t acceleration;
displayFloatToInt_t out_value;
uint8_t whoami;
snprintf(dataOut, MAX_BUF_SIZE, "\r\nMotion sensor instance %d:", (int)Instance);
printf("%s", dataOut);
if (IKS4A1_MOTION_SENSOR_GetAxes(Instance, MOTION_ACCELERO, &acceleration))
{
snprintf(dataOut, MAX_BUF_SIZE, "\r\nACC[%d]: Error\r\n", (int)Instance);
}
else
{
snprintf(dataOut, MAX_BUF_SIZE, "\r\nACC_X[%d]: %d, ACC_Y[%d]: %d, ACC_Z[%d]: %d\r\n", (int)Instance,
(int)acceleration.x, (int)Instance, (int)acceleration.y, (int)Instance, (int)acceleration.z);
}
printf("%s", dataOut);
if (verbose == 1)
{
if (IKS4A1_MOTION_SENSOR_ReadID(Instance, &whoami))
{
snprintf(dataOut, MAX_BUF_SIZE, "WHOAMI[%d]: Error\r\n", (int)Instance);
}
else
{
snprintf(dataOut, MAX_BUF_SIZE, "WHOAMI[%d]: 0x%x\r\n", (int)Instance, (int)whoami);
}
printf("%s", dataOut);
if (IKS4A1_MOTION_SENSOR_GetOutputDataRate(Instance, MOTION_ACCELERO, &odr))
{
snprintf(dataOut, MAX_BUF_SIZE, "ODR[%d]: ERROR\r\n", (int)Instance);
}
else
{
floatToInt(odr, &out_value, 3);
snprintf(dataOut, MAX_BUF_SIZE, "ODR[%d]: %d.%03d Hz\r\n", (int)Instance, (int)out_value.out_int,
(int)out_value.out_dec);
}
printf("%s", dataOut);
if (IKS4A1_MOTION_SENSOR_GetFullScale(Instance, MOTION_ACCELERO, &fullScale))
{
snprintf(dataOut, MAX_BUF_SIZE, "FS[%d]: ERROR\r\n", (int)Instance);
}
else
{
snprintf(dataOut, MAX_BUF_SIZE, "FS[%d]: %d g\r\n", (int)Instance, (int)fullScale);
}
printf("%s", dataOut);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
MPU_Config();
/* USER CODE END 1 */
/* Enable the CPU Cache */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Update SystemCoreClock variable according to RCC registers values. */
SystemCoreClockUpdate();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_UART4_Init();
MX_FLASH_Init();
/* USER CODE BEGIN 2 */
/* ACC传感器初始化 */
displayFloatToInt_t out_value_odr;
int i;
IKS4A1_MOTION_SENSOR_Init(IKS4A1_LSM6DSO16IS_0, MOTION_ACCELERO | MOTION_GYRO);
printf("IKS4A1_MOTION_SENSOR_Init ok\r\n");
for(i = 0; i < IKS4A1_MOTION_INSTANCES_NBR; i++)
{
IKS4A1_MOTION_SENSOR_GetCapabilities(i, &MotionCapabilities[i]);
snprintf(dataOut, MAX_BUF_SIZE,
"\r\nMotion Sensor Instance %d capabilities: \r\n ACCELEROMETER: %d\r\n GYROSCOPE: %d\r\n MAGNETOMETER: %d\r\n LOW POWER: %d\r\n",
i, MotionCapabilities[i].Acc, MotionCapabilities[i].Gyro, MotionCapabilities[i].Magneto, MotionCapabilities[i].LowPower);
printf("%s", dataOut);
floatToInt(MotionCapabilities[i].AccMaxOdr, &out_value_odr, 3);
snprintf(dataOut, MAX_BUF_SIZE, " MAX ACC ODR: %d.%03d Hz, MAX ACC FS: %d\r\n", (int)out_value_odr.out_int,
(int)out_value_odr.out_dec, (int)MotionCapabilities[i].AccMaxFS);
printf("%s", dataOut);
floatToInt(MotionCapabilities[i].GyroMaxOdr, &out_value_odr, 3);
snprintf(dataOut, MAX_BUF_SIZE, " MAX GYRO ODR: %d.%03d Hz, MAX GYRO FS: %d\r\n", (int)out_value_odr.out_int,
(int)out_value_odr.out_dec, (int)MotionCapabilities[i].GyroMaxFS);
printf("%s", dataOut);
floatToInt(MotionCapabilities[i].MagMaxOdr, &out_value_odr, 3);
snprintf(dataOut, MAX_BUF_SIZE, " MAX MAG ODR: %d.%03d Hz, MAX MAG FS: %d\r\n", (int)out_value_odr.out_int,
(int)out_value_odr.out_dec, (int)MotionCapabilities[i].MagMaxFS);
printf("%s", dataOut);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
#if 0
HAL_GPIO_TogglePin(LD1_GPIO_PORT, LD1_Pin);
/* Insert delay 100 ms */
HAL_Delay(100);
HAL_GPIO_TogglePin(LD2_GPIO_PORT, LD2_PIN);
/* Insert delay 100 ms */
HAL_Delay(100);
HAL_GPIO_TogglePin(LD3_GPIO_PORT, LD3_PIN);
/* Insert delay 100 ms */
HAL_Delay(100);
HAL_GPIO_TogglePin(LD4_GPIO_PORT, LD4_PIN);
/* Insert delay 100 ms */
HAL_Delay(100);
#endif
#if 0
unsigned char data[] = "123";
HAL_UART_Transmit(&huart4, data, sizeof(data), 0xFFFF);
printf("666\r\n");
float a = 3.14;
printf("a = %.2f\r\n", a);
#endif
#if 0
snprintf(dataOut, MAX_BUF_SIZE, "\r\n__________________________________________________________________________\r\n");
printf("%s", dataOut);
for(i = 0; i < IKS4A1_MOTION_INSTANCES_NBR; i++)
{
if(MotionCapabilities[i].Acc)
{
Accelero_Sensor_Handler(i);
}
// if(MotionCapabilities[i].Gyro)
// {
// Gyro_Sensor_Handler(i);
// }
// if(MotionCapabilities[i].Magneto)
// {
// Magneto_Sensor_Handler(i);
// }
}
HAL_Delay( 1000 );
#endif
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
/**
* @brief This function configures the MPU context of the application.
* @retval None
*/
static void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
uint32_t index = MPU_REGION_NUMBER0;
uint32_t address;
uint32_t size;
/* Disable the MPU */
HAL_MPU_Disable();
/* Initialize the background region */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.BaseAddress = 0x0;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
/* Initialize the region corresponding to external RAM */
#if defined ( __ICCARM__ )
extern uint32_t __region_EXTRAM_start__;
extern uint32_t __region_EXTRAM_end__;
address = (uint32_t)&__region_EXTRAM_start__;
size = (uint32_t)&__region_EXTRAM_end__ - (uint32_t)&__region_EXTRAM_start__ + 1;
#elif defined (__CC_ARM) || defined(__ARMCC_VERSION)
extern uint32_t Image$$RW_EXTRAM$$Base;
extern uint32_t Image$$RW_EXTRAM$$ZI$$Length;
extern uint32_t Image$$RW_EXTRAM$$Length;
address = (uint32_t)&Image$$RW_EXTRAM$$Base;
size = (uint32_t)&Image$$RW_EXTRAM$$Length + (uint32_t)&Image$$RW_EXTRAM$$ZI$$Length;
#elif defined ( __GNUC__ )
extern uint32_t __EXTRAM_BEGIN;
extern uint32_t __EXTRAM_SIZE;
address = (uint32_t)&__EXTRAM_BEGIN;
size = (uint32_t)&__EXTRAM_SIZE;
#else
#error "Compiler toolchain is unsupported"
#endif
if (size != 0)
{
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.SubRegionDisable = 0u;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_AdjustRegionAddressSize(address, size, &MPU_InitStruct);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
}
/* Initialize the non cacheable region */
#if defined ( __ICCARM__ )
/* get the region attribute form the icf file */
extern uint32_t NONCACHEABLEBUFFER_start;
extern uint32_t NONCACHEABLEBUFFER_size;
address = (uint32_t)&NONCACHEABLEBUFFER_start;
size = (uint32_t)&NONCACHEABLEBUFFER_size;
#elif defined (__CC_ARM) || defined(__ARMCC_VERSION)
extern uint32_t Image$$RW_NONCACHEABLEBUFFER$$Base;
extern uint32_t Image$$RW_NONCACHEABLEBUFFER$$Length;
extern uint32_t Image$$RW_NONCACHEABLEBUFFER$$ZI$$Length;
address = (uint32_t)&Image$$RW_NONCACHEABLEBUFFER$$Base;
size = (uint32_t)&Image$$RW_NONCACHEABLEBUFFER$$Length + (uint32_t)&Image$$RW_NONCACHEABLEBUFFER$$ZI$$Length;
#elif defined ( __GNUC__ )
extern int __NONCACHEABLEBUFFER_BEGIN;
extern int __NONCACHEABLEBUFFER_END;
address = (uint32_t)&__NONCACHEABLEBUFFER_BEGIN;
size = (uint32_t)&__NONCACHEABLEBUFFER_END - (uint32_t)&__NONCACHEABLEBUFFER_BEGIN;
#else
#error "Compiler toolchain is unsupported"
#endif
if (size != 0)
{
/* Configure the MPU attributes as Normal Non Cacheable */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_AdjustRegionAddressSize(address, size, &MPU_InitStruct);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
}
/* Initialize the region corresponding to the execution area
(external or internal flash or external or internal RAM
depending on scatter file definition) */
#if defined ( __ICCARM__ )
extern uint32_t __ICFEDIT_region_ROM_start__;
extern uint32_t __ICFEDIT_region_ROM_end__;
address = (uint32_t)&__ICFEDIT_region_ROM_start__;
size = (uint32_t)&__ICFEDIT_region_ROM_end__ - (uint32_t)&__ICFEDIT_region_ROM_start__ + 1;
#elif defined (__CC_ARM) || defined(__ARMCC_VERSION)
extern uint32_t Image$$ER_ROM$$Base;
extern uint32_t Image$$ER_ROM$$Limit;
address = (uint32_t)&Image$$ER_ROM$$Base;
size = (uint32_t)&Image$$ER_ROM$$Limit-(uint32_t)&Image$$ER_ROM$$Base;
#elif defined ( __GNUC__ )
extern uint32_t __FLASH_BEGIN;
extern uint32_t __FLASH_SIZE;
address = (uint32_t)&__FLASH_BEGIN;
size = (uint32_t)&__FLASH_SIZE;
#else
#error "Compiler toolchain is unsupported"
#endif
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = index;
MPU_InitStruct.SubRegionDisable = 0u;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_AdjustRegionAddressSize(address, size, &MPU_InitStruct);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
index++;
/* Reset unused MPU regions */
for(; index < __MPU_REGIONCOUNT ; index++)
{
/* All unused regions disabled */
MPU_InitStruct.Enable = MPU_REGION_DISABLE;
MPU_InitStruct.Number = index;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
}
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/**
* @brief This function adjusts the MPU region Address and Size within an MPU configuration.
* @param Address memory address
* @param Size memory size
* @param pInit pointer to an MPU initialization structure
* @retval None
*/
static void MPU_AdjustRegionAddressSize(uint32_t Address, uint32_t Size, MPU_Region_InitTypeDef* pInit)
{
/* Compute the MPU region size */
pInit->Size = ((31 - __CLZ(Size)) - 1);
if (Size > (1 << (pInit->Size + 1)))
{
pInit->Size++;
}
uint32_t Modulo = Address % (1 << (pInit->Size - 1));
if (0 != Modulo)
{
/* Align address with MPU region size considering there is no need to increase the size */
pInit->BaseAddress = Address - Modulo;
}
else
{
pInit->BaseAddress = Address;
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
IKS4A1配置的.h中把其他动作传感器关掉
这样就可以了,成功读取到ACC数据
- 2024-10-13
-
发表了主题帖:
[STM32H7R/S]测评 ④ST Edge AI Developer Cloud
本帖最后由 不爱胡萝卜的仓鼠 于 2024-10-16 00:10 编辑
这几天在学习ST AI方向的知识,发现一个很有意思的网站。链接如下:https://stedgeai-dc.st.com/home。
在日常开发中,我们会需要训练模型,到开发板上实际跑一下,测算其性能,然后再修改模型或调整参数,再跑。比较繁琐并且需要有一个开发板和工程。如果我们什么都没有,只有模型就没办法了,于是这个网站就解决了这个问题,我们可以把模型放上去,并调参数优化,然后可以运行模型测算性能(这个不是软件模拟的,在云端ST是真有对应的实体开发板的,在开发板上运行你的模型),最终你还可以下载源码或者固件等。
接下来我就展示一下这个网站如何使用
一.登录
进入网站首先是一个欢迎页面,下方的图片,三个流程很好的说明了这个网站的功能。我们点击“START NOW”(之后会要求你登录ST账号)
二.选择模型
第一步要上传一个模型,可以选择上传自己的模型,也可以从ST的模型库(ST Model Zoo)中选择一个
我这边因为还没有模型,因此我从ST模型库中选择一个。模型删选条件有3个,分别是目标板卡类型、用途、神经网络
我这边就选择这个使用VL53L5CX这个多点TOF芯片的手势识别模型,点击“import”就选择使用这个模型了
点击“start”即可开始使用模型了
接下来网站会对这个模型进行分析,大概十来秒左右,还是别较快的
三.选择平台
在这里我们要选择CUBE AI的版本和芯片平台,CUBE AI我选择最新的9.0.0(同时我CUBE MX中安装的CUBE AI也是这个版本,虽然我写文章时已经是9.1.0了)。之后可以选择芯片平台了,由于我选了9.0.0,芯片平台就只能选STM32了,不过我们现在就是用STM32,别的平台不能选那也没啥关系了
四.量化模型
这个步骤是可选的,量化模型可以减少神经网络的体积减少内存
通过量化后,模型的体积明显变小了很多(当然精度也会丢失,这个就要后期不停的调参,对体积和性能做平衡了)
如果不使用量化功能,直接点击“Go next”即可下一步。如果量化模型了,并且想使用量化后的模型,要点击“Select”
五、优化模型
这里可以对模型进行优化,选择平衡、节约内存、提高推理速度三选一,非常的简洁明了。右边的输入输出buffer默认是选上的
点击“Optimize”就可以开始优化,同样的,这个速度也是很快的,几秒钟就好了
下面会这展示出优化后的各中性能参数,我选择平衡模式,可以看到flash和RAM都有一定的下降,虽然不多,但也是有用处的。
模型优化后即可开始跑分了,点击“Go to benchmark”
六、跑分
选择一款开发板即可开始跑分,这个过程不是软件模拟的,在云端是真的有一块实体开发板,云端服务器生成代码并编译下载到开发板上,实际在开发板上运行后才得到结果的。所以他花的时间是比较久的,大概要几分钟
刚好我运行时还遇到了排队的情况,一共有2个人,我是第二个。没想到这个平台还是满火热的嘛
可能是网站有点问题,我等了好久一直没动,那这个页面就暂时略过
七、结果
在结果页面会详细展示运算后的结果,历史的结果也会在这里
八、生成工程
这里可以生成C代码、CUBEMX工程、cubeIDE工程、ELF固件。我们下载下来后可以在我们自己手上的开发板上运行刚才云端运行的代码,进行复测
首先选择开发板,然后选择自己需要的就好(我这边选择cubeIDE工程),等待一会儿即可下载
九、本地开发板运行测试代码
下载后代码在一个压缩包内,需要解压缩
双击“.project”即可打开cubeide,编译Boot,下载、编译APP,下载。即可运行测试代码了
结果会从ST-Link的虚拟串口上输出,输出结果如下
[00:04:33.279]收←◆
#
# AI system performance 7.1
#
Compiled with GCC 12.3.1
STM32 device configuration...
Device : DevID:0x0485 (STM32H7[R,]Sxx) RevID:0x1003
Core Arch. : M7 - FPU used
HAL version : 0x01000000
SYSCLK clock : 600 MHz
HCLK clock : 300 MHz
FLASH conf. : ACR=0x00000036 - latency=6
CACHE conf. : $I/$D=(True,True)
[00:04:33.410]收←◆ Timestamp : SysTick + DWT (delay(1)=1.000 ms)
AI platform (API 1.1.0 - RUNTIME 9.0.0)
Discovering the network(s)...
Found network "network"
Creating the network "network"..
Initializing the network
Network informations...
model name : network
model signature : 0x1e108c42827f4c62598744246d259703
model datetime : Tue Oct 15 15:57:19 2024
compile datetime : Oct 16 2024 00:03:47
tools version : 9.0.0
complexity : 8520 MACC
c-nodes : 5
map_activations : 1
[0] @0x20000000/1024
map_weights : 6
[0] @0x70010680/576
[1] @0x70010660/32
[2] @0x7000E260/9216
[3] @0x7000E1E0/128
[4] @0x7000DDE0/1024
[5] @0x7000DDC0/32
n_inputs/n_outputs : 1/1
I[0] (1,8,8,2)128/float32 @0x20000200/512
O[0] (1,1,1,8)8/float32 @0x20000180/32
Running PerfTest on "network" with random inputs (16 iterations)...
................
Results for "network", 16 inferences @600MHz/300MHz (complexity: 8520 MACC)
duration : 0.143 ms (average)
CPU cycles : 86088 (average)
CPU Workload : 0% (duty cycle = 1s)
cycles/MACC : 10.10 (average for all layers)
used stack : 984 bytes
used heap : 0:0 0:0 (req:allocated,req:released) max=0 cur=0 (cfg=3)
observer res : 104 bytes used from the heap (5 c-nodes)
Inference time by c-node
kernel : 0.140ms (time passed in the c-kernel fcts)
user : 0.000ms (time passed in the user cb)
c_id type id time (ms)
---------------------------------------------------
0 OPTIMIZED_CONV2D 3 0.113 81.15 %
1 DENSE 6 0.020 14.35 %
2 NL 6 0.000 0.53 %
3 DENSE 7 0.002 2.00 %
4 NL 7 0.002 1.97 %
-------------------------------------------------
0.140 ms
可以看到这个模型以上述参数在这款开发板上运行一次只需要0.140ms,不愧是H7,主频高就是厉害,嘎嘎快
- 2024-10-12
-
回复了主题帖:
[嘉楠 CanMV K230]测评 ②开发环境搭建及运行一个demo
丸辣,1.1的镜像有很大的问题,大家还是老老实实刷资料里自带的那个1.0版本。
前期跑跑图像识别的代码都正常,直到跑AI相关的代码时,出大问题,
1.代码中读取SD卡中的kmodle文件的路径不对,1.1中的文件路径做了修改,但是1.1镜像自带的代码还是1.0的,没有修改
2.上面那个问题手动修改后,依然无法运行代码,提示MediaManager.init()出错,这个我是怎么整也没整好
折腾了半天,最终刷回1.0,好了
嘉楠官方发布的这个1.1镜像太坑了,你自带的demo好歹全部测试一下啊,搞个自动化脚本跑一跑
- 2024-10-11
-
发表了主题帖:
[STM32H7R/S]测评 ③串口printf输出日志
本帖最后由 不爱胡萝卜的仓鼠 于 2024-10-12 00:40 编辑
在日常调试中日志输出是一个非常重要的功能,今天我们就在上一篇文章工程的基础上增加串口功能。本来这种基础的功能我想跳过,但是调试过程中发现了一个很有意思的事情,想写出来与大家分享
一.配置CubeMX
在配置cubemx前,我们需要知道开发板上ST-Link虚拟串口与H7的那个uart连接,并使用的是哪两个引脚。这个信息可以在《DM01038846_ENV2.0》这个文档中找到,如下图所示
使用的是PD0、PD1
接下来我们双击打开cubemx的ioc文件
在Connectivity中打开UART4,选择“App”的那个框。mode选择“Asynchronous”。接下来还要改一下右边的引脚图,RX默认是PB8,我们要改成PD0(虽然我们待会儿只用于打印日子,暂时用不到RX,不过还是给他配置正确)
然后点击右上角的生成代码即可
因为上一篇中ld的那个问题,每次cubemx生成后,都要记得回去把那个文件给变回去
二.CubeIDE中代码修改
2.1 发送测试
首先我们现在测试一下数据能不能发出去,我们直接调用HAL_UART_Transmit函数即可发送数据。我在while1中放了以下测试代码
unsigned char data[] = "123";
HAL_UART_Transmit(&huart4, data, sizeof(data), 0xFFFF);
串口日志正常输出,说明配置没问题
2.2 printf
想要使用printf函数,需要做一下重定向,首先需要增加以下头文件,否则编译会报错
#include "stdio.h"
然后写一下printf重定向函数
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart4 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
然后针对float类型的数据,还需要额外再做一个操作
把这两个勾上即可
然后我们替换刚才的测试函数,换上printf来试试
printf("666\r\n");
float a = 3.14;
printf("a = %.2f\r\n", a);
结果如下
三.有意思的事情
我今天调试一开始使用的串口工具是正点原子的XCOM,平时我默认打开的是sscom,不知道咋了,今天鬼使神差的点了XCOM,既然都打开了,就用他吧。然后这个软件我之前还打开了时间戳
结果发现他一直不出数据,我以为是我的代码有问题,给我一顿好找。最终发现下方的R:的数字是一直在上涨的,但是界面不显示。那我的程序应该没问题,串口是有东西输出的
接下来我换了SSCOM,就正常了(SSCOM我也勾选了时间戳)
经过我反复的尝试,我发现XCOM的时间戳功能可能有点小问题,当向XCOM定时发送数据时,我的发送频率超过某个值后(这个值的边界是多少我没测试),他就会一直接收,不打印出来。
我猜测他是接收完数据后,要多少时间没有新的数据,他就认为接收完毕,然后把它打印出来,但是这个时间他写的比较大(我代码里400ms的delay发送一次数据,他这个至少是大于400ms的),就导致了这个问题。
解决策略:
1.关闭时间戳功能
2.一定要时间戳的,要么换软件,要么保证不要很快的定时发送
3.XCOM开发人员修改一下代码,当RX buffer到达一定值后,例如50字节,就强制输出一次,不要一直收
-
回复了主题帖:
[STM32H7R/S]测评 ②点灯
慕容雪花 发表于 2024-10-5 08:44
大佬,小弟发现可以在如下位置更改ld文件:
感谢感谢,按照你的这个该确实可以重新选择ld文件修改。但是很可惜我这个demo他只要cubemx再生成一次,他又会给我改回去,估计是cubemx的配置文件的原因,而且这个选项是不再UI界面中让用户配置的
- 2024-10-08
-
发表了主题帖:
[嘉楠 CanMV K230]测评 ⑨二维码读取
我之前参加的竞赛有一次是要求识别条形码,当时还没有接触图像识别,是通过扫码枪模块来实现读取的,这个模块要求距离合适并且不能晃动,在最终比赛时因为机械臂又虚位,不停的晃动,导致有一个条形码识别出来,真是太可惜了。不过现在很多比赛基本上都是用二维码了,并且二维码因为可以比条形码携带更多信息,在我们的生活中应用十分广泛
K230也是支持条形码和二维码的识别的,不过我们今天的重点是二维码。
想要识别出二维码,也是非常的简单,我们不需要知道二维码的解析规则等,只需要调用一个函数即可,非常的简单方便,这个函数就是find_qrcodes。
官方对这个函数的描述的文档:https://developer.canaan-creative.com/k230_canmv/main/zh/api/openmv/image.html#find-qrcodes
他的返回值如下,和上一篇的颜色一样,他会把画面中的所有二维码都找出来,返回值类似C语言的二维数组
具体代码如下:
import time, os, sys
from media.sensor import *
from media.media import *
from media.display import *
red = (31,56,44,76,34,64)
def camera_test():
print("camera_test")
#用默认配置构造一个Sensor对象
sensor = Sensor()
#复位摄像头
sensor.reset()
#设置摄像头水平翻转
sensor.set_hmirror(False)
#设置摄像头垂直翻转
sensor.set_vflip(False)
#设置指定通道的输出图像尺寸, 320x240
sensor.set_framesize(width=320, height=240)
#设置指定sensor设备和通道的输出图像格式
sensor.set_pixformat(Sensor.RGB565)
#通过IDE缓冲区显示图像
Display.init(Display.VIRT, sensor.width(), sensor.height())
#初始化media manager
MediaManager.init()
#摄像头开始工作
sensor.run()
try:
while True:
os.exitpoint()
#摄像头拍一张照片
img = sensor.snapshot()
###################图像识别代码#######################
#找二维码
codes = img.find_qrcodes()
#如果找到
if codes:
#遍历找到的每一个二维码
for code in codes:
#画框,把识别到的二维码框出来
img.draw_rectangle(code.rect(), thickness = 2)
#打印二维码的内容
print(code.payload())
###################图像识别代码#######################
Display.show_image(img)
except KeyboardInterrupt as e:
print("user stop: ", e)
except BaseException as e:
print(f"Exception {e}")
#摄像头停止工作
sensor.stop()
# deinit display
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
#media manager去初始化
MediaManager.deinit()
if __name__ == "__main__":
os.exitpoint(os.EXITPOINT_ENABLE)
camera_test()
我在线生成了2个二维码,让K230一起扫描,成功把2个都扫到,结果如下
-
回复了主题帖:
[BearPi-Pico H2821]测评 ③SLE数据收发测试及日志流程解读
zmlzplzqz 发表于 2024-10-8 11:41
楼主你好,我按照你说的这种方法测试了一下两个GPIO拉高的时间差,这个时间在6ms到20ms之间,最短的是6.8 ...
这个我没有去试过,在config中有一个low latency的开关,你可以打开试一下,可以降低延迟。不过因为没有找到对应的参数配置,也没有第三方抓包工具,没法知道在空口上他到底是多久通讯一次。250us那种是宣传上的极限参数,甚至可能是理论值,看看就好,肯定还是空中的速度,你还要带上收到后的信息处理等,延迟也会变大。就像传输速率,宣传的12Mbps,那玩意儿是理论极限值,实际纯空口都跑不到这个速度的,还带上串口等外设,速率就更低了,有其他家的测速测试,带上串口收发和上位机处理,最大就1.2Mbps
- 2024-10-06
-
发表了主题帖:
[嘉楠 CanMV K230]测评 ⑧颜色识别
K230可以很轻松的实现颜色识别,识别颜色需要调用的函数为find_blobs,官方文档中说明:https://developer.canaan-creative.com/k230_canmv/main/zh/api/openmv/image.html#find-blobs
该函数的返回值如下
需要注意的是返回的不是一个色块的信息,而是多个色块的信息,返回值有点类似C语言中的二维数组
一.如何获取颜色参数
寻找色块函数最核心的参数就是要寻找的颜色是什么,也就是thresholds这个参数。我其实对如何使用一系列参数对一个颜色进行描述是不太懂的,例如我们平时说的RGB、YUV等,它里面具体参数代表什么含义我不是很清楚的。以下是我的简单理解,虽然不是很专业,但是可以帮助我们快速上手。正如上面所描述的,我们使用LAB这种方式描述一个颜色,他需要有6个参数,分别是(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi)
我们可以让摄像头对需要测量的颜色拍照,然后我们就可以在IDE上看到对应的参数。我先在PPT中画一个色块,然后让摄像头对这个色块拍摄,即可在右下角得到对应的参数
我这边拍摄了一个红色的色块,得到的参数为(31,56,44,76,34,64)
二.颜色识别
我们现在已经得到一个颜色的参数了,就可以调用颜色识别函数尝试找一下,代码如下
import time, os, sys
from media.sensor import *
from media.media import *
from media.display import *
red = (31,56,44,76,34,64)
def camera_test():
print("camera_test")
#用默认配置构造一个Sensor对象
sensor = Sensor()
#复位摄像头
sensor.reset()
#设置摄像头水平翻转
sensor.set_hmirror(False)
#设置摄像头垂直翻转
sensor.set_vflip(False)
#设置指定通道的输出图像尺寸, 320x240
sensor.set_framesize(width=320, height=240)
#设置指定sensor设备和通道的输出图像格式
sensor.set_pixformat(Sensor.RGB565)
#通过IDE缓冲区显示图像
Display.init(Display.VIRT, sensor.width(), sensor.height())
#初始化media manager
MediaManager.init()
#摄像头开始工作
sensor.run()
try:
while True:
os.exitpoint()
#摄像头拍一张照片
img = sensor.snapshot()
###################图像识别代码#######################
#找色块
blobs = img.find_blobs([red])
#如果找到
if blobs:
print('find')
###################图像识别代码#######################
Display.show_image(img)
except KeyboardInterrupt as e:
print("user stop: ", e)
except BaseException as e:
print(f"Exception {e}")
#摄像头停止工作
sensor.stop()
# deinit display
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
#media manager去初始化
MediaManager.deinit()
if __name__ == "__main__":
os.exitpoint(os.EXITPOINT_ENABLE)
camera_test()
当找到红色色块后,就会打印一句find,结果如下 ,
可以看到我这边并没有使用其他可选的参数,只填写了必选的,因为我现在这边的场景很简单,当图像复杂时,需要把他们用上,作为筛选过滤条件
接下来我们使用一下返回值,把找到的色块的中心坐标、外框给他画出来,代码如下
import time, os, sys
from media.sensor import *
from media.media import *
from media.display import *
red = (31,56,44,76,34,64)
def camera_test():
print("camera_test")
#用默认配置构造一个Sensor对象
sensor = Sensor()
#复位摄像头
sensor.reset()
#设置摄像头水平翻转
sensor.set_hmirror(False)
#设置摄像头垂直翻转
sensor.set_vflip(False)
#设置指定通道的输出图像尺寸, 320x240
sensor.set_framesize(width=320, height=240)
#设置指定sensor设备和通道的输出图像格式
sensor.set_pixformat(Sensor.RGB565)
#通过IDE缓冲区显示图像
Display.init(Display.VIRT, sensor.width(), sensor.height())
#初始化media manager
MediaManager.init()
#摄像头开始工作
sensor.run()
try:
while True:
os.exitpoint()
#摄像头拍一张照片
img = sensor.snapshot()
###################图像识别代码#######################
#找色块
blobs = img.find_blobs([red])
#如果找到
if blobs:
#遍历找到的每个色块
for blob in blobs:
#把色块的中心坐标用十字标出来
img.draw_cross(blob.cx(), blob.cy(), thickness = 2)
#用矩形框把色块框出来
img.draw_rectangle(blob.x(), blob.y(), blob.w(), blob.h(), thickness = 2)
###################图像识别代码#######################
Display.show_image(img)
except KeyboardInterrupt as e:
print("user stop: ", e)
except BaseException as e:
print(f"Exception {e}")
#摄像头停止工作
sensor.stop()
# deinit display
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
#media manager去初始化
MediaManager.deinit()
if __name__ == "__main__":
os.exitpoint(os.EXITPOINT_ENABLE)
camera_test()