1. 环境搭建与基础验证

任务目标

本案例完成STM32CubeAI环境搭建与基础验证闭环:使用CubeMX生成带AI库的工程,将Keras正弦波预测模型转换为STM32可用的C代码,并在开发板上运行推理,通过串口输出预测结果。

环境与依赖

  • STM32CubeMX 6.x(含X-CUBE-AI扩展包,建议v7.3+)
  • STM32CubeIDE 1.13+(或任意支持所生成工程的IDE)
  • Python 3.8+,安装 tensorflow==2.10、numpy、keras
  • 开发板:NUCLEO-F401RE(本文以该板为例,其他F4系列可类推)
  • USB转串口线(板载ST-Link已集成虚拟串口)

实现步骤

1. 生成并训练正弦波预测模型

创建Python脚本train_sine.py,训练一个输入为过去10个采样点、输出为下一点的LSTM模型,并保存为Keras格式。

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

# 生成正弦波数据
t = np.linspace(0, 20*np.pi, 2000)
data = np.sin(t)

# 构造序列样本:用前10点预测下一点
seq_len = 10
X, y = [], []
for i in range(len(data)-seq_len-1):
    X.append(data[i:i+seq_len])
    y.append(data[i+seq_len])
X = np.array(X).reshape(-1, seq_len, 1)
y = np.array(y)

# 划分训练/验证
split = int(len(X)*0.8)
X_train, X_val = X[:split], X[split:]
y_train, y_val = y[:split], y[split:]

# 构建模型
model = keras.Sequential([
    layers.LSTM(16, input_shape=(seq_len, 1)),
    layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=30, validation_data=(X_val, y_val), verbose=0)

# 保存模型
model.save('sine_model.h5')
print('模型已保存为 sine_model.h5')

运行python train_sine.py生成模型文件。

2. CubeMX工程配置

  1. 打开CubeMX,新建基于NUCLEO-F401RE的工程。
  2. 配置USART2(PA2/PA3)为异步模式,波特率115200,用于输出推理结果。
  3. 在Software Packs中选择X-CUBE-AI,点击Add。
  4. 进入Middleware and Software Packs → X-CUBE-AI → Network,点击Add Network,选择sine_model.h5。
  5. 将网络名称设为sine_net,其余保持默认(分析模式为ONNX/TFLite自动转换)。
  6. 时钟配置:确保主频84MHz(HSE+锁相环),CubeMX默认即可。
  7. 生成代码(Toolchain选择STM32CubeIDE)。

3. 编写推理主程序

在生成的工程中,打开Core/Src/main.c,在main()函数的用户代码区添加推理逻辑。核心代码如下:

/* 在main.c中添加头文件 */
#include "ai_platform.h"
#include "sine_net.h"
#include "sine_net_data.h"

/* 用户代码区(USER CODE BEGIN PV) */
static float input_data[10] = {0};
static float output_data[1] = {0};
static ai_handle network = AI_HANDLE_NULL;
static ai_handle weights[AI_SINE_NET_NUM_OF_WEIGHTS_ARRAYS] = {0};
static ai_buffer input_buffers[1];
static ai_buffer output_buffers[1];
static ai_i8 activations[AI_SINE_NET_DATA_ACTIVATIONS_SIZE];
/* USER CODE END PV */

/* main()函数内,在while(1)之前添加初始化与推理代码 */
int main(void)
{
  /* ... CubeMX生成的初始化代码 ... */

  /* USER CODE BEGIN 2 */
  // 构造输入序列:模拟最近10个采样点(示例数据)
  for (int i = 0; i < 10; i++) {
    input_data[i] = sinf((float)(i) * 0.5f);
  }

  // 初始化AI网络
  ai_error err;
  err = ai_sine_net_create(&network, weights);
  if (err.type != AI_ERROR_NONE) {
    printf("网络创建失败\n");
    Error_Handler();
  }

  // 绑定激活缓冲区
  ai_sine_net_data_params_get(&network, AI_SINE_NET_DATA_ACTIVATIONS,
                              &activations[0], AI_SINE_NET_DATA_ACTIVATIONS_SIZE);

  // 设置输入输出缓冲区
  input_buffers[0].data = input_data;
  input_buffers[0].format = AI_BUFFER_FORMAT_FLOAT;
  input_buffers[0].size = 10 * sizeof(float);
  output_buffers[0].data = output_data;
  output_buffers[0].format = AI_BUFFER_FORMAT_FLOAT;
  output_buffers[0].size = 1 * sizeof(float);

  // 执行推理
  ai_i32 ret = ai_sine_net_run(&network, input_buffers, output_buffers);
  if (ret != 1) {
    printf("推理失败\n");
    Error_Handler();
  }

  // 输出结果
  printf("预测值: %f\n", output_data[0]);
  /* USER CODE END 2 */

  while (1)
  {
  }
}

注意:printf重定向到USART2需在fputc中实现,CubeMX生成的工程默认支持(若未启用,在main.c中添加以下代码):

/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f) {
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 100);
  return ch;
}
/* USER CODE END 0 */

4. 编译与烧录

  1. 在STM32CubeIDE中编译工程,确认无错误。
  2. 连接开发板,点击Run烧录并启动调试。

5. 验证方法

打开串口终端(115200-8-N-1),观察输出。若程序正常运行,应看到类似以下输出(数值取决于输入序列):

预测值: 0.479426

通过条件:串口输出一行以预测值:开头的浮点数,且程序不进入Error_Handler。该数值与Python端对相同输入序列的推理结果一致(误差<1e-3),说明模型转换与部署链路正确。

常见问题

  • 网络创建失败:检查CubeMX中X-CUBE-AI的Network是否已正确添加,且sine_net.h头文件路径已包含。
  • 编译报错找不到ai_platform.h:确认CubeMX生成的Middlewares/ST/AI目录已加入include路径。
  • 输出乱码:检查USART2波特率是否与终端一致,以及fputc重定向是否正确。