- from keras.datasets import mnist
- import matplotlib.pyplot as plt
- from keras.models import Sequential
- from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
- from keras.utils import np_utils
- import tensorflow as tf
- config = tf.compat.v1.ConfigProto()
- config.gpu_options.allow_growth = True
- sess = tf.compat.v1.Session(config=config)
- # 设定随机数种子,使得每个网络层的权重初始化一致
- # np.random.seed(10)
- # x_train_original和y_train_original代表训练集的图像与标签, x_test_original与y_test_original代表测试集的图像与标签
- (x_train_original, y_train_original), (x_test_original, y_test_original) = mnist.load_data()
- """
- 数据可视化
- """
- # 原始数据量可视化
- print('训练集图像的尺寸:', x_train_original.shape)
- print('训练集标签的尺寸:', y_train_original.shape)
- print('测试集图像的尺寸:', x_test_original.shape)
- print('测试集标签的尺寸:', y_test_original.shape)
- """
- 数据预处理
- """
- # 从训练集中分配验证集
- x_val = x_train_original[50000:]
- y_val = y_train_original[50000:]
- x_train = x_train_original[:50000]
- y_train = y_train_original[:50000]
- # 打印验证集数据量
- print('验证集图像的尺寸:', x_val.shape)
- print('验证集标签的尺寸:', y_val.shape)
- print('======================')
- # 将图像转换为四维矩阵(nums,rows,cols,channels), 这里把数据从unint类型转化为float32类型, 提高训练精度。
- x_train = x_train.reshape(x_train.shape[0], 28, 28, 1).astype('float32')
- x_val = x_val.reshape(x_val.shape[0], 28, 28, 1).astype('float32')
- x_test = x_test_original.reshape(x_test_original.shape[0], 28, 28, 1).astype('float32')
- # 原始图像的像素灰度值为0-255,为了提高模型的训练精度,通常将数值归一化映射到0-1。
- x_train = x_train / 255
- x_val = x_val / 255
- x_test = x_test / 255
- print('训练集传入网络的图像尺寸:', x_train.shape)
- print('验证集传入网络的图像尺寸:', x_val.shape)
- print('测试集传入网络的图像尺寸:', x_test.shape)
- # 图像标签一共有10个类别即0-9,这里将其转化为独热编码(One-hot)向量
- y_train = np_utils.to_categorical(y_train)
- y_val = np_utils.to_categorical(y_val)
- y_test = np_utils.to_categorical(y_test_original)
- """
- 定义网络模型
- """
- def CNN_model():
- model = Sequential()
- model.add(Conv2D(filters=16, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
- model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
- model.add(Conv2D(filters=32, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
- model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
- model.add(Flatten())
- model.add(Dense(100, activation='relu'))
- model.add(Dense(10, activation='softmax'))
- print(model.summary())
- return model
- """
- 训练网络
- """
- model = CNN_model()
- # 编译网络(定义损失函数、优化器、评估指标)
- model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
- # 开始网络训练(定义训练数据与验证数据、定义训练代数,定义训练批大小)
- train_history = model.fit(x_train, y_train, validation_data=(x_val, y_val), epochs=10, batch_size=32, verbose=2)
- # 模型保存
- model.save('model.h5')
- # 定义训练过程可视化函数(训练集损失、验证集损失、训练集精度、验证集精度)
- def show_train_history(train_history, train, validation):
- plt.plot(train_history.history[train])
- plt.plot(train_history.history[validation])
- plt.title('Train History')
- plt.ylabel(train)
- plt.xlabel('Epoch')
- plt.legend(['train', 'validation'], loc='best')
- plt.show()
- show_train_history(train_history, 'accuracy', 'val_accuracy')
- show_train_history(train_history, 'loss', 'val_loss')
安装CubeAI
在CubeMX上方Software Packs下拉选择Select Components,选择其中的X-CUBE-AI
在左侧菜单栏选择Middleware and Software Packs,选择其中的X-CUBE-AI,导入模型并分析。如果这个模型过大,超过了flash的大小,可能还需要对模型进行压缩,并配置外部flash。
串口配置
观察开发板原理图可以发现,PA9和PA10可以做虚拟串口使用,对应的是UART1。
开启UART1并设置为异步模式。由于需要串口收发,所以还要使能串口接收中断。
最后使能DEBUG功能
代码编写
首先包含相关头文件- #include "stdio.h"
- #include "string.h"
- #include "ai_platform.h"
- #include "network.h"
- #include "network_data.h"
由于需要串口收发数据,因此需要对printf进行重定向
- #ifdef __GNUC__
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
- #else
- #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
- #endif
- PUTCHAR_PROTOTYPE
- {
- HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
- return ch;
- }
定义AI模型相关参数,并声明后续使用到的一些函数
- 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;
定义串口中断回调函数
- 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);
- }
定义AI模型初始化函数
- 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);
- }
定义AI模型运行函数
- 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,"%ld %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);
- }
定义将串口收到的uint8_t类型数据转换为float类型函数
- void PictureCharArrayToFloat(uint8_t *srcBuf,float *dstBuf,int len)
- {
- for(int i=0;i<len;i++)
- {
- dstBuf[i] = srcBuf[i];//==1?0:1;
- }
- }
主函数部分,需要完成外设初始化以及模型运行逻辑的书写
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- /* USER CODE END 1 */
- /* MCU Configuration--------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* USER CODE BEGIN Init */
- /* USER CODE END Init */
- /* Configure the system clock */
- SystemClock_Config();
- /* USER CODE BEGIN SysInit */
- /* USER CODE END SysInit */
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_ICACHE_Init();
- MX_USART1_UART_Init();
- /* USER CODE BEGIN 2 */
- __HAL_RCC_CRC_CLK_ENABLE();
- 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 */
- 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 */
- }
最终实现效果如下,0-9的数字均能很好识别。