1. 环境搭建与第一个模型部署
任务目标
在STM32CubeIDE中完成Cube.AI插件配置,将一个预训练的2层全连接神经网络(MNIST手写数字识别,输入784,隐藏层32,输出10)转换为STM32可调用的C代码,部署到NUCLEO-F401RE开发板,通过串口发送单张图片像素数据,获取并打印推理结果。
环境依赖
- 硬件:NUCLEO-F401RE开发板(STM32F401RE,84MHz,512KB Flash)
- 软件:STM32CubeIDE 1.13+(含Cube.AI插件,或通过Help→Manage Embedded Software Packages安装X-CUBE-AI)
- Python 3.8+(仅用于生成测试输入数据,不参与嵌入式端推理)
- 模型文件:预训练Keras模型
mnist_fc.h5(本案例提供生成脚本,训练2分钟即可得到)
实现步骤
1. 生成预训练模型
在PC上运行以下Python脚本生成Keras模型文件:
# gen_model.py — 运行一次生成 mnist_fc.h5
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
# 加载MNIST数据
(x_train, y_train), (_, _) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0
# 构建2层全连接网络
model = keras.Sequential([
layers.Dense(32, activation='relu', input_shape=(784,)),
layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=2, batch_size=128, validation_split=0.1, verbose=1)
model.save('mnist_fc.h5')
print('Model saved: mnist_fc.h5')
2. CubeIDE工程配置与Cube.AI集成
- 新建STM32CubeIDE工程,选择MCU为STM32F401RE,初始化时钟(默认84MHz)和USART2(115200-8-N-1,用于打印结果)。
- 在
.ioc文件中,点击左侧Software Packs→Select Components,勾选X-CUBE-AI的Core和Validator组件。 - 回到
.ioc,在Software Packs→X-CUBE-AI→Network选项卡中:
- 点击Add Network,选择mnist_fc.h5
- 设置输出数组长度10,输入类型float
- 点击Analyze确认无错误后,点击Generate Code
3. 主程序调用推理
Cube.AI生成代码位于X-CUBE-AI/App/目录。在main.c中添加推理调用:
/* main.c 中用户代码区 */
#include "ai_platform.h"
#include "network.h"
#include "network_data.h"
#include <stdio.h>
/* 输入缓冲区:784个float,需从串口接收或预置测试数据 */
AI_ALIGNED(4) float input_data[AI_NETWORK_IN_1_SIZE];
AI_ALIGNED(4) float output_data[AI_NETWORK_OUT_1_SIZE];
/* 预置测试数据:手写数字"0"的归一化像素(前64个示例,完整数据见验证说明) */
static const float test_img_0[784] = {
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
/* ... 完整784个值由Python脚本生成后粘贴至此 ... */
};
void run_inference(void) {
ai_error err;
ai_buffer ai_input[1];
ai_buffer ai_output[1];
/* 初始化网络 */
ai_network_params params = {
AI_NETWORK_DATA_WEIGHTS(ai_network_data_weights_get()),
AI_NETWORK_DATA_ACTIVATIONS(ai_network_data_activations_get())
};
ai_network_create_and_init(&network, ¶ms);
/* 准备输入输出缓冲区 */
ai_input[0] = (ai_buffer){AI_NETWORK_IN_1_SIZE, AI_NETWORK_IN_1_BATCH, 0, 0, input_data};
ai_output[0] = (ai_buffer){AI_NETWORK_OUT_1_SIZE, AI_NETWORK_OUT_1_BATCH, 0, 0, output_data};
/* 执行推理 */
ai_network_run(&network, ai_input, ai_output);
/* 打印结果 */
printf("Inference done. Output:\r\n");
for (int i = 0; i < 10; i++) {
printf(" [%d] = %.4f\r\n", i, output_data[i]);
}
/* 获取预测类别 */
int pred = 0;
for (int i = 1; i < 10; i++) {
if (output_data[i] > output_data[pred]) pred = i;
}
printf("Predicted digit: %d\r\n", pred);
ai_network_deinit(&network);
}
4. 生成测试输入数据
在PC上运行以下脚本,输出可直接粘贴到main.c的test_img_0数组:
# gen_test_data.py — 生成数字"0"的测试输入
import numpy as np
from tensorflow import keras
(_, _), (x_test, y_test) = keras.datasets.mnist.load_data()
idx = np.where(y_test == 0)[0][0]
img = x_test[idx].reshape(-1).astype('float32') / 255.0
print(f"// 测试样本索引: {idx}, 标签: {y_test[idx]}")
print("static const float test_img_0[784] = {")
for i in range(0, 784, 8):
row = ", ".join(f"{v:.6f}f" for v in img[i:i+8])
print(f" {row},")
print("};")
5. 编译与烧录
- 在
main.c的main()函数中,在初始化外设后调用run_inference()。 - 编译工程(Project→Build),确认无错误。
- 连接NUCLEO-F401RE的ST-Link USB口,点击
Run→Debug或Run烧录并运行。
6. 验证方法
- 打开串口终端(115200-8-N-1),观察输出。
- 通过条件:程序运行后,串口打印10个类别的概率值,且
Predicted digit: 0(即最大概率对应标签0)。 - 若输出类别错误,检查输入数据是否完整粘贴、模型是否转换成功(Cube.AI分析时无错误)。
预期输出
Inference done. Output:
[0] = 0.9821
[1] = 0.0003
[2] = 0.0012
[3] = 0.0045
[4] = 0.0001
[5] = 0.0023
[6] = 0.0018
[7] = 0.0002
[8] = 0.0067
[9] = 0.0008
Predicted digit: 0
以上数值为基于模型逻辑的示例输出,实际概率值因训练随机性略有差异,但Predicted digit应为0。
常见问题
- 编译错误:找不到network.h:确认Cube.AI代码生成成功,且头文件路径已包含
X-CUBE-AI/App目录。 - 推理结果全为0:检查输入数据是否归一化到[0,1]区间,且数组长度正确(784个float)。
- Flash溢出:F401RE有512KB Flash,本模型权重约25KB,若使用更大模型需确认空间。