当前位置: 首页 > news >正文

CUBEAI详细使用教程(STM32运行神经网络)---以手写识别为例

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、CUBEMX配置步骤
  • 二、模型结构及模型存储方式
  • 三、常用API函数
    • 1.ai_(name)_create()
    • 2.ai_(name)_init
    • 3.ai_(name)_create_and_init()
    • 3.ai_(name)_run()
    • 官方提供的示例代码
  • 四、如何获取官方开发文档
  • 五、手写识别案例


前言

实验效果,通过上位机上传图像到单片机识别后返回识别结果
在这里插入图片描述


CUBEAI(Cube Artificial Intelligence)是一种人工智能(AI)中间件,旨在为嵌入式系统提供高效、灵活的神经网络推理能力。该中间件的设计目标是在资源有限的嵌入式设备上实现深度学习推理,从而为物联网(IoT)设备、嵌入式系统和边缘计算场景提供强大的人工智能支持。

一、CUBEMX配置步骤

下载X-CUBE-AI工具包
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

在这里插入图片描述

创建项目选择能运行神经网络的MCU,STM32F4以上系列的都支持部署神经网络。

在这里插入图片描述

在这里插入图片描述
设置串口,注意一定要把串口中断打开要不然接收不到数据。
在这里插入图片描述
选择AI工具包,选择最近的CUBEAI工具包。最新的是8.1版本的,不同版本的生成的代码有些会不一样,有的模型老版本可以部署新版本就不行了,挺让人头疼的。
在这里插入图片描述
在这里插入图片描述

利用CUBEAI工具分析模型,这里并没有选择去优化压缩,因为模型本来就很小。CUBEAI提供的是一种无数据压缩,压缩效率并不是很高,也会损失精度。
在这里插入图片描述
进行分析得到,模型分析后的结果。会有模型的Flash和RAM
在这里插入图片描述

二、模型结构及模型存储方式

网络结构利用Netron这个网址可以将模型进行可视化。模型结构如下图所示。
在这里插入图片描述
在这里插入图片描述
模型如何在单片机上进行存储

  1. 权重参数:存放在Flash中
  2. 激活值:存在MCU自带的SRAM中,也可以使用外部的SD卡或者外接SRAM进行存储。需要用户自己定义
  3. 输入输出数据:存放在SRAM中需要用户自己定义。如下图所示

在这里插入图片描述
以下是官方文档中翻译过来的

“激活”缓冲区是一个简单的连续内存映射缓冲区,放置在一个读写内存段中。它由AI客户端拥有和分配。它被传递给网络实例(参见ai_init()函数),并在执行推理期间用作私有堆(或工作缓冲区)来存储中间结果。在两次运行之间,应用程序可以使用相关的内存段。其大小AIDATA_ACTIVATIONS_SIZE是在代码生成期间定义的,对应于报告的内存度量。
Weights”缓冲区是一个简单的连续内存映射缓冲区(或多个内存映射缓冲区与——split-weights选项)。它通常放置在一个非易失和只读的内存设备中。总长度AI
DATA_WEIGHTS_SIZE是在代码生成期间定义的,对应于报告的ROM度量。
“输出”和“输入”缓冲区也必须放在读写内存映射缓冲区中。默认情况下,它们由AI客户端拥有和提供。它们的大小依赖于模型,称为生成时间(AI
_IN/OUT_SIZE_BYTES)。它们也可以位于“激活”缓冲区中。

三、常用API函数

CUBEAI模型推理库不开源只开放了一些API接口,因此我们必须了解一些常用的API使用方法。
在这里插入图片描述
在这里插入图片描述

1.ai_(name)_create()

ai_error ai__create(ai_handle* network, const ai_buffer* network_config);
参数:
1.network:神经网络句柄
2.Network_config参数是一个特定的网络配置缓冲区(不透明结构),编码为ai_buffer。它是由代码生成器生成的,应用程序不应该修改它。当前,该对象总是空的,可以传递NULL,但最好传递AI_NETWORK_DATA_CONFIG(见_data.h文件)。以上是官方文档的解释,我们自己设置为NULL就可以
功能: 这个强制函数是应用程序创建c模型实例时必须调用的早期函数。如果ai_handle对象被更新,并且它引用了一个上下文(不透明对象),该上下文应该被传递给其他函数。

2.ai_(name)_init

ai_bool ai_(name)_init(ai_handle network, const ai_network_params* params);
参数:
1.network:神经网络句柄
2.Params参数是一个结构体(ai_network_params类型),它允许传递权重和激活缓冲区的引用(数组格式)。也就是激活缓冲区的地址官网上面的案例直接传递激活缓冲区的地址就可以,权重是自动生成的不用管。
返回值: 初始化正确还是错误
功能: 这个强制函数用于应用程序初始化内部运行时数据结构,并设置激活缓冲区和权重缓冲区。

3.ai_(name)_create_and_init()

ai_error ai__create_and_init(ai_handle* network,
const ai_handle activations[], const ai_handle weights[]);
参数:
1.network:神经网络句柄
2.const ai_handle activations[]:激活图存储地址
返回值: 初始化正确还是错误
功能: 结合了上面两个函数的功能,一般直接使用这个函数就可以

应用案例:进行创建和初始化网络模型

#include "network.h"
#include "network_data.h"AI_ALIGNED(32)
static ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE];
...
const ai_handle acts[] = { activations };ai_network_create_and_init(&network, acts, NULL);

3.ai_(name)_run()

ai_i32 ai__run(ai_handle network, const ai_buffer* input, ai_buffer* output);
> 参数:
1.network:神经网络句柄
2.ai_buffer* input:输入数据地址
3.ai_buffer* output:输出数据地址
返回值: 返回值是n_batches >= 1时处理的输入张量的数量。如果<=0,应该使用ai_network_get_error()函数来知道错误
功能: 根据输入数据,运行神经网络

官方提供的示例代码

根据示例代码可知,自己需要修改的地方主要有三点。
1、定义网络句柄、输入、输出和激活缓冲区数据buf 和管理输入和输出数据的指针。
2、获取和处理数据并进行推理。如果是图像分类任务,图像数据可能是来自摄像头模块获取。其他任务例如运动检测任务,数据可能会来自六轴加速度传感器。
3、对输出数据进行后处理。对于分类任务,模型输出结果是每个类别的概率,有时我们要输出准确率最高的类别就要写一个求最大值的函数

#include <stdio.h>#include "network.h"
#include "network_data.h"/* Global handle to reference the instantiated C-model *//*引用实例化的C-model的全局句柄*/
static ai_handle network = AI_HANDLE_NULL;/* Global c-array to handle the activations buffer *//*用于处理激活缓冲区的全局c数组*/
AI_ALIGNED(32)
static ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE];/* Array to store the data of the input tensor *//*用于存储输入张量数据的数组*/
AI_ALIGNED(32)
static ai_float in_data[AI_NETWORK_IN_1_SIZE];
/* or static ai_u8 in_data[AI_NETWORK_IN_1_SIZE_BYTES]; *//*用于存储输出张量数据的c数组*//* c-array to store the data of the output tensor */
AI_ALIGNED(32)
static ai_float out_data[AI_NETWORK_OUT_1_SIZE];
/* static ai_u8 out_data[AI_NETWORK_OUT_1_SIZE_BYTES]; *//* Array of pointer to manage the model's input/output tensors */
/*用于管理模型输入/输出张量的指针数组*/
static ai_buffer *ai_input;
static ai_buffer *ai_output;/* * Bootstrap*/
int aiInit(void) {ai_error err;/* Create and initialize the c-model *//*创建并初始化c-model */const ai_handle acts[] = { activations };err = ai_network_create_and_init(&network, acts, NULL);if (err.type != AI_ERROR_NONE) { ... };/* Reteive pointers to the model's input/output tensors *//*获取指向模型输入/输出张量的指针*/ai_input = ai_network_inputs_get(network, NULL);ai_output = ai_network_outputs_get(network, NULL);return 0;
}/* * Run inference*/
int aiRun(const void *in_data, void *out_data) {ai_i32 n_batch;ai_error err;/* 1 - Update IO handlers with the data payload *//* 1 -用数据有效载荷更新IO处理程序*/ai_input[0].data = AI_HANDLE_PTR(in_data);ai_output[0].data = AI_HANDLE_PTR(out_data);/* 2 - Perform the inference *//* 2 -执行推理*/n_batch = ai_network_run(network, &ai_input[0], &ai_output[0]);if (n_batch != 1) {err = ai_network_get_error(network);...};return 0;
}/* * Example of main loop function*/
void main_loop() {/* The STM32 CRC IP clock should be enabled to use the network runtime library *//*应启用STM32 CRC IP时钟,以使用网络运行时库*/__HAL_RCC_CRC_CLK_ENABLE();aiInit();while (1) {/* 1 - Acquire, pre-process and fill the input buffers *//* 1 -获取、预处理和填充输入缓冲区*/acquire_and_process_data(in_data);/* 2 - Call inference engine *//* 2 -调用推理引擎*/aiRun(in_data, out_data);/* 3 - Post-process the predictions *//* 3——对预测进行后处理*/post_process(out_data);}
}

四、如何获取官方开发文档

在实际应用中发现CUBEAI在每个版本会有些差别,我训练好的模型在CUBEAI7.3可以使用但是在CUBEAI8.1就无法使用,每个版本也会新增加一些算子。可以在下载安装包中找到相对应的文档说明,例如在我的电脑中的这个目录获取文档
file:///C:/Users/wg/STM32Cube/Repository/Packs/STMicroelectronics/X-CUBE-AI/8.1.0/Documentation/embedded_client_api.html
在这里插入图片描述

在这里插入图片描述

五、手写识别案例

代码来自https://github.com/colin2135/STM32G070_AI_TEST.git 大家可以去star一下。感谢作者的开源。作者使用的是STM32G0系列的单片机,我是使用的是STM32F7和正点原子的H5mini,F4系列的都可以。目前还没有尝试F1系列的单片机。

一、代码思路:1. 利用上位机将手写数据通过串口发送给单片机。2.单片机进行获取数据利用神经网络进行判断3.将输出结果发送给上位机。上位机链接https://github.com/colin2135/HandWriteApp,下载源码后,找到这个文件路径。在这里插入图片描述

二、代码实现
1、定义网络句柄、输入、输出和激活缓冲区数据buf 和管理输入和输出数据的指针。

ai_handle network;
float aiInData[AI_NETWORK_IN_1_SIZE];
float aiOutData[AI_NETWORK_OUT_1_SIZE];
ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE];ai_buffer * ai_input;
ai_buffer * ai_output;

2、获取和处理数据
通过串口获取数据

/* USER CODE BEGIN 4 */利用回调函数接收上位机发来的手写数字的数据
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{if(goRunning ==0){if (uart_rx_length < UART_BUFF_LEN){uart_rx_buffer[uart_rx_length] = uart_rx_byte;uart_rx_length++;if (uart_rx_byte == '\n'){goRunning = 1;}}else{//rt_kprintf("rx len over");uart_rx_length = 0;}}HAL_UART_Receive_IT(&huart1, (uint8_t *)&uart_rx_byte, 1);
}

处理数据,通过上位机发送的数据是8位数据,由于模型参数是32位浮点数因此输入数据要转换成32位浮点数

void PictureCharArrayToFloat(uint8_t *srcBuf,float *dstBuf,int len)
{for(int i=0;i<len;i++){dstBuf[i] = srcBuf[i];//==1?0:1;}
}

神经网络推理

static void AI_Run(float *pIn, float *pOut)
{char logStr[100];int count = 0;float max = 0;ai_i32 batch;ai_error err;/* Update IO handlers with the data payload */ai_input[0].data = AI_HANDLE_PTR(pIn);ai_output[0].data = AI_HANDLE_PTR(pOut);batch = ai_network_run(network, ai_input, ai_output);if (batch != 1) {err = ai_network_get_error(network);printf("AI ai_network_run error - type=%d code=%d\r\n", err.type, err.code);Error_Handler();}for (uint32_t i = 0; i < AI_NETWORK_OUT_1_SIZE; i++) {sprintf(logStr,"%d  %8.6f\r\n",i,aiOutData[i]);Uart_send(logStr);if(max<aiOutData[i]){count = i;max= aiOutData[i];}}sprintf(logStr,"current number is %d\r\n",count);Uart_send(logStr);
}

3、对输出数据进行后处理
将输出结果和最大值通过串口进行发送

 for (uint32_t i = 0; i < AI_NETWORK_OUT_1_SIZE; i++) {sprintf(logStr,"%d  %8.6f\r\n",i,aiOutData[i]);Uart_send(logStr);if(max<aiOutData[i]){count = i;max= aiOutData[i];}}

3.whlie(1)代码

  while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */Uart_send(message);char str[10];if(goRunning>0){if(uart_rx_length == ONE_FRAME_LEN){PictureCharArrayToFloat(uart_rx_buffer+1,aiInData,28*28);AI_Run(aiInData, aiOutData);}memset(uart_rx_buffer,0,784);goRunning = 0;uart_rx_length = 0;}}/* USER CODE END 3 */

完整代码

/* 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 "ai_platform.h"
#include "network.h"
#include "network_data.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/CRC_HandleTypeDef hcrc;UART_HandleTypeDef huart1;/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_CRC_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
ai_handle network;
float aiInData[AI_NETWORK_IN_1_SIZE];
float aiOutData[AI_NETWORK_OUT_1_SIZE];
ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE];ai_buffer * ai_input;
ai_buffer * ai_output;static void AI_Init(void);
static void AI_Run(float *pIn, float *pOut);
void PictureCharArrayToFloat(uint8_t *srcBuf,float *dstBuf,int len);void Uart_send(char * str);
#define UART_BUFF_LEN 1024
#define ONE_FRAME_LEN 1+784+2
uint16_t uart_rx_length = 0;
uint8_t uart_rx_byte = 0;
uint8_t uart_rx_buffer[UART_BUFF_LEN];
volatile uint8_t goRunning = 0;char message[]="hello";/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* Enable I-Cache---------------------------------------------------------*/
//  SCB_EnableICache();
//
//  /* Enable D-Cache---------------------------------------------------------*/
//  SCB_EnableDCache();/* 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_CRC_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */AI_Init();memset(uart_rx_buffer,0,784);HAL_UART_Receive_IT(&huart1, (uint8_t *)&uart_rx_byte, 1);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */Uart_send(message);char str[10];if(goRunning>0){if(uart_rx_length == ONE_FRAME_LEN){PictureCharArrayToFloat(uart_rx_buffer+1,aiInData,28*28);AI_Run(aiInData, aiOutData);}memset(uart_rx_buffer,0,784);goRunning = 0;uart_rx_length = 0;}}/* 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*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** 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.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM = 8;RCC_OscInitStruct.PLL.PLLN = 216;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 2;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Activate the Over-Drive mode*/if (HAL_PWREx_EnableOverDrive() != 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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK){Error_Handler();}
}/*** @brief CRC Initialization Function* @param None* @retval None*/
static void MX_CRC_Init(void)
{/* USER CODE BEGIN CRC_Init 0 *//* USER CODE END CRC_Init 0 *//* USER CODE BEGIN CRC_Init 1 *//* USER CODE END CRC_Init 1 */hcrc.Instance = CRC;hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;if (HAL_CRC_Init(&hcrc) != HAL_OK){Error_Handler();}/* USER CODE BEGIN CRC_Init 2 *//* USER CODE END CRC_Init 2 */}/*** @brief USART1 Initialization Function* @param None* @retval None*/
static void MX_USART1_UART_Init(void)
{/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_Init 2 */}/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 *//* GPIO Ports Clock Enable */__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOH_CLK_ENABLE();/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{if(goRunning ==0){if (uart_rx_length < UART_BUFF_LEN){uart_rx_buffer[uart_rx_length] = uart_rx_byte;uart_rx_length++;if (uart_rx_byte == '\n'){goRunning = 1;}}else{//rt_kprintf("rx len over");uart_rx_length = 0;}}HAL_UART_Receive_IT(&huart1, (uint8_t *)&uart_rx_byte, 1);
}void Uart_send(char * str)
{HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str),0xffff);
}static void AI_Init(void)
{ai_error err;/* Create a local array with the addresses of the activations buffers */const ai_handle act_addr[] = { activations };/* Create an instance of the model */err = ai_network_create_and_init(&network, act_addr, NULL);if (err.type != AI_ERROR_NONE) {printf("ai_network_create error - type=%d code=%d\r\n", err.type, err.code);Error_Handler();}ai_input = ai_network_inputs_get(network, NULL);ai_output = ai_network_outputs_get(network, NULL);
}static void AI_Run(float *pIn, float *pOut)
{char logStr[100];int count = 0;float max = 0;ai_i32 batch;ai_error err;/* Update IO handlers with the data payload */ai_input[0].data = AI_HANDLE_PTR(pIn);ai_output[0].data = AI_HANDLE_PTR(pOut);batch = ai_network_run(network, ai_input, ai_output);if (batch != 1) {err = ai_network_get_error(network);printf("AI ai_network_run error - type=%d code=%d\r\n", err.type, err.code);Error_Handler();}for (uint32_t i = 0; i < AI_NETWORK_OUT_1_SIZE; i++) {sprintf(logStr,"%d  %8.6f\r\n",i,aiOutData[i]);Uart_send(logStr);if(max<aiOutData[i]){count = i;max= aiOutData[i];}}sprintf(logStr,"current number is %d\r\n",count);Uart_send(logStr);
}void PictureCharArrayToFloat(uint8_t *srcBuf,float *dstBuf,int len)
{for(int i=0;i<len;i++){dstBuf[i] = srcBuf[i];//==1?0:1;}
}/* 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 */__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 */

相关文章:

CUBEAI详细使用教程(STM32运行神经网络)---以手写识别为例

系列文章目录 文章目录 系列文章目录前言一、CUBEMX配置步骤二、模型结构及模型存储方式三、常用API函数1.ai_(name)_create()2.ai_(name)_init3.ai_(name)_create_and_init()3.ai_(name)_run()官方提供的示例代码 四、如何获取官方开发文档五、手写识别案例 前言 实验效果&am…...

[NOIP 1998 提高组] 拼数

https://www.luogu.com.cn/problem/P1012 将每个数用字符串的形式读进来&#xff0c;对于任意两个数 x x x, y y y&#xff0c;如果 x y > y x xy>yx xy>yx&#xff0c;对最后的答案来说&#xff0c; x x x一定排在 y y y的前面。 简单证明&#xff1a;假设最后的…...

PHP Web 开发基础

PHP 学习资料 PHP 学习资料 PHP 学习资料 在 PHP Web 开发领域&#xff0c;掌握一些基础概念和技术是构建功能强大、用户体验良好的 Web 应用的基石。接下来&#xff0c;我们将深入探讨 HTTP 协议、表单处理、Cookie 和 Session 的管理、URL 处理等关键内容。 一、HTTP 协议…...

MME-CoT:专为评估大型多模态模型CoT推理能力的基准测试。涵盖了数学、科学、OCR、逻辑、时空和一般场景6个领域。

2025-02-09 &#xff0c;由CUHK MMLab、CUHK MulLab、字节跳动、、东北大学等机构联合发布MME-CoT数据集&#xff0c;该数据集目的评估大型多模态模型&#xff08;LMMs&#xff09;中的思维链&#xff08;CoT&#xff09;推理能力&#xff0c;涵盖数学、科学、OCR、逻辑、时空和…...

HDFS应用-后端存储cephfs-java-API

HDFS(Hadoop Distribute FileSystem)是一个适合运行在通用硬件之上,具备高度容错特性,支持高吞吐量数据访问的分布式文件系统,非常适合大规模数据集应用。 HDFS适用于如下场景: • 处理海量数据(TB或PB级别以上) • 需要很高的吞吐量 • 需要高可靠性 • 需要很好的可扩…...

A与B组件自动对齐与组装,无映射直接补偿。

网上针对组装的从视觉到控制动作,要不就是收费,要不就是简单介绍。这么详细的比较难找~ 手下留情,不喜勿喷! Show time~ 分步解决方案: 标定阶段(Calibration) 9点张氏标定(每个位置A1、A2、B1、B2): 使用机械手在相机视野内沿Z字形路径移动,覆盖9个点(XY方…...

SQL知识体系

SQL复习 MySQL SQL介绍 SQL SQL的全拼是什么&#xff1f; SQL全拼&#xff1a;Structured Query Language&#xff0c;也叫结构化查询语言。 SQL92和SQL99有什么区别呢&#xff1f; SQL92和SQL99分别代表了92年和99年颁布的SQL标准。 在 SQL92 中采用&#xff08;&#xff…...

编译安装php

前置准备 这里的可能不全&#xff0c;每个人安装的模块不一致&#xff0c;依赖也不不相同&#xff0c;按实际情况调整 yum install libxml2 -y yum install libxml2-devel -y yum install openssl-devel -y yum install sqlite-devel -y yum install libcurl-devel -yyum ins…...

【分果果——DP(困难)】

题目 分析 分果果题解参考&#xff0c;下面是补充https://blog.csdn.net/AC__dream/article/details/129431299 关于状态 设f[i][j][k]表示第i个人取到的最后一个糖果编号是j&#xff0c;第i-1个人取到的最后一个糖果编号小于等于k时的最大重量的最小值 关于转移方程 关于 j …...

利用ffplay播放udp组播视频流

ffplay -fs -fflags nobuffer -flags low_delay -analyzeduration 0 -probesize 32 -framedrop -sync ext -strict experimental udp://224.1.1.1:5001 -fs : 全屏显示 -fflags nobuffer &#xff1a; 禁用输入缓冲&#xff08;减少100-200ms缓冲延迟&#xff09; -an…...

C++中变量与容器的默认初始化:0的奥秘

在C编程的世界里&#xff0c;初始化是一个至关重要的概念。它决定了变量或容器在程序开始执行时的初始状态。然而&#xff0c;对于不同的数据类型和容器&#xff0c;C标准对于默认初始化的行为有着不同的规定。本文将深入探讨C中变量与容器的默认初始化规则&#xff0c;特别是关…...

VScode内接入deepseek包过程(本地部署版包会)

目录 1. 首先得有vscode软件 2. 在我们的电脑本地已经部署了ollama&#xff0c;我将以qwen作为实验例子 3. 在vscode上的扩展商店下载continue 4. 下载完成后&#xff0c;依次点击添加模型 5. 在这里可以添加&#xff0c;各种各样的模型&#xff0c;选择我们的ollama 6. 选…...

spark

阶段性 阶段一&#xff1a; 单机时代 阶段二: 大数据时代-分布式处理 阶段三: 实时大数据时代 hadoop慢因为她的计算结果保存在磁盘 处理在spark中可解决属于内存 Hadoop特点&#xff1a; 高可靠性 高拓展性 高效性 高容错性...

蓝桥杯---排序数组(leetcode第912题)

文章目录 1.题目重述2.思路分析3.代码解释 1.题目重述 题目的要求是不使用库函数或者是其他的内置的函数&#xff08;就是已经实现好的函数&#xff09;&#xff0c;也就是这个排序的逻辑需要我们自己进行实现&#xff1b; 2.思路分析 其实这个例子也是很容易理解的&#xff…...

【Javascript Day18】

目录 标签事件绑定的属性参数 阻止默认行为 dialog的实现及组织冒泡&#xff08;捕获&#xff09;传递 基于冒泡的事件委托 键盘事件的事件源对象信息 JS的自动触发操作 标签事件绑定的属性参数 <!-- 标签上的事件绑定&#xff0c;事件源对象通过 关键字event传递 --…...

轻量级C通用库Klib解读 —— kalloc

前言 Klib是一个独立的轻量级c通用库&#xff0c;里面大多数组件除了C标准库外不包含外部库&#xff0c;想用对应组件直接拷贝对应文件即可使用。 该库致力于高效和较小的内存占用&#xff0c;其中部分组件&#xff08;如khash、kbtree、ksort、kvec&#xff09;&#xff0c;无…...

认识HTML的标签结构

一、HTML的基本概念 1.什么是HTML&#xff1f; ①HTML是描述网页的一种标记语言&#xff0c;也被称为超文本标记语言【并不是一种编程语言】 ②HTML包含了HTML标签和文本内容 ③HTML文档也称为web页面 2.HTML的标签 HTML的标签通常成对出现&#xff0c;HTML文档由标签和受…...

C语言 实现一个比较两个整型的函数 / qsort的使用 /qsort排序结构体

一、qsort的一般使用方法 int cmp_int(const void* e1, const void* e2) {return *(int*)e1 - *(int*)e2; } // //使用qsort对数组进行排序&#xff0c;升序 void test1() {int arr[] { 9,8,7,6,5,4,3,2,1,0 };int sz sizeof(arr) / sizeof(arr[0]);//bubble_sort(arr, sz);…...

Arcmap python环境安装sklearn

Arcmap自带的环境为Python2.7&#xff0c;默认安装目录为C:\Python27\ArcGIS10.7 直接安装sklearn会发生兼容性问题&#xff0c;且默认环境不包括pip&#xff0c;因此需要先安装pip 下载 get-pip.py 访问 https://bootstrap.pypa.io/pip/2.7/get-pip.py 并下载 get-pip.py。运…...

FastAdmin后端列表导入表格数据

后台添加数据的时候增加通过表格导入功能 如下图index.html页面增加导入和模板下载按钮代码如下 <div class"panel panel-default panel-intro">{:build_heading()}<div class"panel-body"><div id"myTabContent" class"ta…...

R语言用逻辑回归贝叶斯层次对本垒打数据与心脏移植数据后验预测检验模拟推断及先验影响分析|附数据代码...

全文链接&#xff1a;https://tecdat.cn/?p40152 在统计学领域中&#xff0c;层次建模是一种极为强大且实用的工具。它能够巧妙地处理复杂的数据结构&#xff0c;通过分层的方式对数据进行建模。在贝叶斯统计的框架内&#xff0c;层次建模优势尽显&#xff0c;其可以有效地融合…...

Python基于自然语言处理技术的新闻文本分类系统【附源码、文档说明】

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…...

mybatis存储过程返回list

在MyBatis中调用存储过程并返回列表&#xff08;List&#xff09;通常涉及以下几个步骤&#xff1a; 定义存储过程&#xff1a;首先&#xff0c;在数据库中定义存储过程&#xff0c;并确保它返回结果集。配置MyBatis映射文件&#xff1a;在MyBatis的映射文件中配置调用存储过程…...

Unity项目实战-订阅者发布者模式

实战订阅者发布者模式详解 下面实例是一个射击类游戏&#xff0c;玩家可以切换枪支&#xff0c;最初的设计如下 public class Player:MonoBehaviour {//......省略代码逻辑//我们以及配置好了Scroll的输入&#xff0c;并配置了Scroll的输入事件&#xff0c;当玩家滚动鼠标滚轮…...

长文档处理痛点:GPT-4 Turbo引文提取优化策略与替代方案讨论

引言 随着GPT-4 Turbo的发布&#xff0c;其支持的128K上下文窗口&#xff08;约300页文本&#xff09;被视为处理长文本的突破性升级。然而&#xff0c;实际应用中&#xff0c;用户发现模型在提取长文档中的引文时存在显著缺陷&#xff1a;文档前三分之一的引文数量远多于中间…...

Deepseek 万能提问公式:高效获取精准答案

### **Deepseek 万能提问公式&#xff1a;高效获取精准答案** 在使用 Deepseek 或其他 AI 工具时&#xff0c;提问的质量直接决定了答案的精准度和实用性。以下是一个万能的提问公式回答&#xff1a; --- ### **1. 明确背景&#xff08;Context&#xff09;** - **作用**…...

Ubuntu中离线安装Docker

Ubuntu中离线安装Docker 前言 本教程将详细介绍如何在 Ubuntu 22.04 系统上&#xff0c;通过 .deb 包离线安装 Docker CE、Docker CE CLI 和 Docker Compose。 适用于无法访问互联网的环境。 准备工作 下载 .deb 包 在可以访问互联网的机器上&#xff0c;下载 Docker CE、…...

Linux配置SSH公钥认证与Jenkins远程登录进行自动发布

问题描述&#xff1a;在使用jenkins进行自动化部署时&#xff0c;其中一步是使用jenkins向目标服务器推送文件时&#xff0c;需要先在jenkins的系统配置中进行配置&#xff08;事先安装好对应插件&#xff09;&#xff0c;配置远程服务器时&#xff0c;报错&#xff1a; 检查以…...

【故障处理】- 11g数据泵到19c导致的job不自动执行

【故障处理】- 11g数据泵到19c导致的job不自动执行 一、概述二、报错原因三、解决方法 一、概述 业务正常上线以后&#xff0c;客户反馈大量的job到时间了也不正常运行。 二、报错原因 该报错匹配bug 32249704&#xff0c;导致了迁移之后job的log_user从业务用户变成了sys。JOB…...

WPF8-常用控件

目录 写在前面&#xff1a;1. 按钮控件1.1. Button 按钮1.2. RepeatButton:长按按钮1.3. RadioButton:单选按钮 2. 数据显示控件2.1. TextBlock&#xff1a;只读文本控件2.2. Lable&#xff1a;标签 显示文本控件2.3. ListBox&#xff1a;显示可选择项的列表2.4. DataGrid&…...

电商小程序(源码+文档+部署+讲解)

引言 随着移动互联网的快速发展&#xff0c;电商小程序成为连接消费者与商家的重要桥梁。电商小程序通过数字化手段&#xff0c;为消费者提供了一个便捷、高效的购物平台&#xff0c;从而提升购物体验和满意度。 系统概述 电商小程序采用前后端分离的架构设计&#xff0c;服…...

关于C#的一些基础知识点汇总

1.C#结构体可以继承接口吗&#xff1f;会不会产生GC&#xff1f; 在 C# 中&#xff0c;结构体不能继承类&#xff0c;但可以实现接口。 代码&#xff1a; interface IMyInterface {void MyMethod(); }struct MyStruct : IMyInterface {public void MyMethod(){Console.Write…...

七、敏捷开发工具:持续集成与部署工具

一、敏捷开发工具——持续集成与部署工具 持续集成(CI)与持续部署(CD)是现代敏捷开发中不可或缺的关键实践。通过自动化构建、测试和部署流程,团队可以快速反馈、提高代码质量,并加速产品交付。为此,持续集成与部署工具应运而生,它们能够帮助开发团队在整个开发周期内…...

【工具类】 Hutool 中用于生成随机数的工具类

博主介绍&#xff1a;✌全网粉丝22W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

vue3和vue2的组件开发有什么区别

Vue3和Vue2在组件开发上存在不少差异&#xff0c;下面从多个方面详细介绍&#xff1a; 响应式原理 Vue2&#xff1a;用Object.defineProperty()方法来实现响应式。打个比方&#xff0c;它就像给对象的每个属性都安排了一个“小管家”&#xff0c;属性被访问或修改时&#xff0…...

防御保护选路练习

拓扑 配置 IP的基本配置 r2 [R2]int g0/0/0 [R2-GigabitEthernet0/0/0]ip add 12.0.0.2 255.255.255.0 [R2]int g0/0/2 [R2-GigabitEthernet0/0/2]ip add 210.1.1.254 255.255.255.0 [R2-GigabitEthernet0/0/2]int g0/0/1 [R2-GigabitEthernet0/0/1]ip add 200.1.1.254 255.…...

SQL Server 运算符优先级

在 SQL Server 中&#xff0c;运算符的优先级决定了在没有使用括号明确指定计算顺序时&#xff0c;运算符的执行顺序。 运算符优先级列表 括号 () 一元运算符 &#xff08;正号&#xff09;-&#xff08;负号&#xff09;~&#xff08;按位取反&#xff09; 乘法、除法和取模…...

【RK3588嵌入式图形编程】-SDL2-构建模块化UI

构建模块化UI 文章目录 构建模块化UI1、概述2、创建UI管理器3、嵌套组件4、继承5、多态子组件6、总结在本文中,将介绍如何使用C++和SDL创建一个灵活且可扩展的UI系统,重点关注组件层次结构和多态性。 1、概述 在前面的文章中,我们介绍了应用程序循环和事件循环,这为我们的…...

用STC-ISP写延时函数

若想写出自己可以定义时长的延时函数&#xff0c;需要重新生成一个1ms的延时函数并稍加修改。 STC-ISP生成的1ms的延时函数代码如下&#xff1a; void Delay1ms(void) //12.000MHz {unsigned char data i, j;i 2;j 239;do{while (--j);} while (--i); }将上述代码改为可自定…...

DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地

对于个人开发者或尝鲜者而言&#xff0c;本地想要部署 DeepSeek 有很多种方案&#xff0c;但是一旦涉及到企业级部署&#xff0c;则步骤将会繁琐很多。 比如我们的第一步就需要先根据实际业务场景评估出我们到底需要部署什么规格的模型&#xff0c;以及我们所要部署的模型&…...

使用 Docker 部署 Apache Spark 集群教程

简介 Apache Spark 是一个强大的统一分析引擎&#xff0c;用于大规模数据处理。本文将详细介绍如何使用 Docker 和 Docker Compose 快速部署一个包含一个 Master 节点和两个 Worker 节点的 Spark 集群。这种方法不仅简化了集群的搭建过程&#xff0c;还提供了资源隔离、易于扩…...

基于暗通道先验的图像去雾算法解析与实现

一、算法背景 何凯明团队于2009年提出的暗通道先验去雾算法《single image haze removal using dark channel prior》&#xff0c;通过统计发现&#xff1a;在无雾图像的局部区域中&#xff0c;至少存在一个颜色通道的像素值趋近于零。这一发现为图像去雾提供了重要的理论依据…...

深入内存调试:Valgrind工具的终极指南(转)

在软件开发的世界里&#xff0c;代码质量就是生命线&#xff0c;而内存管理又是这条生命线中最脆弱的一环。内存泄漏&#xff0c;哪怕只是微小的一处&#xff0c;日积月累&#xff0c;都可能对整个系统造成灾难性的打击&#xff0c;无论是大型企业级应用、实时性要求极高的嵌入…...

深入解析MediaPipe:强大的实时计算机视觉框架

深入解析MediaPipe&#xff1a;强大的实时计算机视觉框架 1. 引言 在计算机视觉应用的快速发展中&#xff0c;实时处理和低延迟成为了许多应用的关键需求。Google 开发的 MediaPipe 是一个强大的开源框架&#xff0c;它能够高效处理 手势识别、姿态估计、物体检测、语音处理 …...

DeepSeek 和 ChatGPT 在特定任务中的表现:逻辑推理与创意生成

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ Linux网络编程笔记&#xff1a; https://blog.cs…...

大白话实战Sentinel

Sentinel是SpringCloudAlibaba提供的用来做服务保护的框架,而服务保护的常见手段就是限流和熔断降级。在大型分布式系统里面,由于微服务众多,所以服务之间的稳定性需要做特别关注,Sentinel的核心包就提供了从多个维度去保护服务稳定的策略,而且这些保护策略都可以连接上Se…...

【AI面板识别】

题目描述 AI识别到面板上有N&#xff08;1 ≤ N ≤ 100&#xff09;个指示灯&#xff0c;灯大小一样&#xff0c;任意两个之间无重叠。 由于AI识别误差&#xff0c;每次别到的指示灯位置可能有差异&#xff0c;以4个坐标值描述AI识别的指示灯的大小和位置(左上角x1,y1&#x…...

Docker安装Kafka(不依赖ZooKeeper)

创建docker-compose.yaml version: "3.9" #版本号 services:kafka:image: apache/kafka:3.9.0container_name: kafkahostname: kafkaports:- 9092:9092 # 容器内部之间使用的监听端口- 9094:9094 # 容器外部访问监听端口environment:KAFKA_NODE_ID: 1KAFKA_PROCES…...

大道至简 少字全意 易经的方式看 jvm基础 、 内存模型 、 gc、 内存异常、内存调优实战案例 、类加载机制、双亲委派模型 适用于 懂而久未用回忆 ,不懂而需明正理而用

目录 介绍 内存模型 一、线程私有区域 二、线程共享区域 1.堆Heap 2. 方法区Method Area 3.运行时常量池 Runtime constant Pool 三、直接内存(Direct Memory) 四、内存异常与调优 五、总结对比 类加载机制 一、类加载的三大阶段 二、双亲委派模型 三、类加载的特…...

【Java学习】继承

一、继承 子类继承父类&#xff0c;子类这个类变量的引用在原有的指向子类自己类变量空间的原有访问权限上&#xff0c;增加上了父类类变量空间的访问权限&#xff0c;此时子类类变量指向的空间变为了原来子类类变量空间加上父类类变量空间&#xff0c;此时子类类变量空间就变成…...