想要自行体验使用STM32进行超级轻量级模型的部署,需要具备以下的知识:
- 编程语言:熟悉C和python
- 基础知识:熟悉基础的机器学习的框架,例如TensorFlow等
0.什么是神经网络
神经网络的基础知识可以看这一篇CSDN博客:https://blog.csdn.net/illikang/article/details/82019945
作为使用方,你可以把神经网络理解为一个黑箱,给定输入,然后给你输出~
1.搭建一个神经网络
1.1.模型搭建
首先我们需要从最最简单的例子说起,这里是我们的目标是预测正弦函数。给定一个输入,范围(0, 180),输出其对应的正弦值范围(0, 1)。我们搭建的简单的网络结构如下图所示:

当然你可以自行试着随便搭建尝试。
1.2.生成sin(x)的数据,进行模型的训练
在仓库中的2.software/2.Advanced/1.CubeAI./python_code文件夹中已经给出所有的文件,请自行查看。其中test为模型的效果测试,train为模型训练, sine_gen为sin的数据生成,生成的csv内容可以自行查看。

在这里生成sin(x)的一系列数据后,使用keras框架搭建模型进行训练,激活函数使用tanh,没有使用sigmod是因为其不能表达负数的部分。学习率设置为0.001,epoch设置为2000。最后生成.h5和.tflite文件。
#导入工具包
import tensorflow as tf
import pandas as pd
import numpy as np
#读取数据
data = pd.read_csv('./Embedded_things/sin_values.csv', sep=',', header=None)
raw_x = data.iloc[:,0].astype(float)
sinex = data.iloc[:,1].astype(float)
print(sinex.shape)
#建立模型
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(units=10, activation='tanh', input_shape=(1,)))
model.add(tf.keras.layers.Dense(units=5, activation='tanh'))
model.add(tf.keras.layers.Dense(units=1))
model.summary()
model.compile(optimizer=tf.keras.optimizers.AdamW(0.001),
loss=tf.keras.losses.mse, #loss使用均方差,刚才的分类用的交叉熵
metrics=[tf.keras.metrics.mse])
history = model.fit(x=raw_x, y=sinex, epochs=2000)
print(model.evaluate(raw_x, sinex))
#保存模型
model.save('./Embedded_things/sine_calcu.h5')
#转换模型为tf lite格式 不量化
load_model = tf.keras.models.load_model('./Embedded_things/sine_calcu.h5')
converter = tf.lite.TFLiteConverter.from_keras_model(load_model)
tflite_model = converter.convert()
open("./Embedded_things/sine_calcu.tflite", "wb").write(tflite_model)2.STM32工程创建
2.1.打开CubeMX,把常规的设置都设置好,打开串口等
2.2.打开select components,选上X-CUBE-AI


在上面这个device applicantion选项中,分别意义如下:
- Not selected:不生成设备侧应用示例。只保留网络相关文件,你需要自己写初始化、喂数据、推理、打印。
- ApplicationTemplate:最精简模板。初始化 + 一次推理流程框架,方便你自己改业务逻辑。优点是干净、好改;缺点是默认不带完整性能统计。
- SystemPerformance:专门做性能评测。会生成用于测推理耗时/周期/吞吐等的测试流程,并打印报告。
- Validation:专门做结果校验。用测试样本跑网络,和参考输出对比,打印误差指标(如 max error、均方误差等),用于验证部署正确性。
我们选最方便简单的 ApplicationTemplate 就行,其他的基本是性能测试的。
2.3.添加网络,选择你的模型(.h5或.tflite),再进行验证


2.4.生成工程,代码功能

- app_x-cube-ai.c: 负责业务侧调用流程。初始化模型、准备输入、调用推理、后处理输出。
- sin_calcu.c: 模型执行引擎的“网络定义文件”。里面定义了张量、层、权重映射、激活内存映射,以及公开 API(create/init/run 等)
- sin_calcu_data.c: 模型数据映射层。把“权重区/激活区”组织成 ai_buffer,并提供 ai_sin_calcu_data_params_get 供初始化阶段绑定。
- sin_calcu_data_params.c: 模型常量参数文件。核心是权重常量数组 s_sin_calcu_weights_array_u64,以及权重/激活表句柄。
2.5.修改代码
在main中,找到MX_X_CUBE_AI_Process()函数,里面有前处理,run推理,还有后处理。具体详见仓库代码,我们这节课要用模型预测sine的值。
void MX_X_CUBE_AI_Process(void)
{
/* USER CODE BEGIN 6 */
int res = -1;
if (sin_calcu) {
/* 1 - acquire and pre-process input data */
res = acquire_and_process_data(data_ins);
/* 2 - process the data - call inference engine */
if (res == 0)
res = ai_run();
/* 3 - post-process the predictions */
if (res == 0)
res = post_process(data_outs);
if (res == 0)
HAL_Delay(100);
}
if (res) {
ai_error err = {AI_ERROR_INVALID_STATE, AI_ERROR_CODE_NETWORK};
ai_log_err(err, "Process has FAILED");
}
/* USER CODE END 6 */
}前处理中,主要我们做数据的生产,我们吧sin的角度值生成到input数组。大致是设定输入为0~180。
int acquire_and_process_data(ai_i8* data[])
{
float* input = (float*)data[0];
s_input_deg = s_deg;
s_y_true = sinf(s_input_deg * APP_PI / 180.0f);
input[0] = s_input_deg;
s_deg += APP_INPUT_STEP_DEG;
if (s_deg > APP_INPUT_MAX_DEG) {
s_deg = 0.0f;
}
return 0;
}run推理,通过我们的网络推理后,得到输出y到数组中
static int ai_run(void)
{
ai_i32 batch;
batch = ai_sin_calcu_run(sin_calcu, ai_input, ai_output);
if (batch != 1) {
ai_log_err(ai_sin_calcu_get_error(sin_calcu),
"ai_sin_calcu_run");
return -1;
}
return 0;
}后处理,我们将数组的结果输出出来,然后去vofa看看输出的y与真实的sin(x)是否拟合比较好。
int post_process(ai_i8* data[])
{
const float* output = (const float*)data[0];
/* FireWater text frame for VOFA+: input(deg), prediction, ground truth */
printf("input, y_pre, y_ture: %.2f, %.2f, %.2f\r\n", s_input_deg, output[0], s_y_true);
return 0;
}各个函数其具体含义读者可以阅读手册或看API深入了解,这里不过多阐述。
2.5.验证
通过串口传输出的数据到上位机VAFA,其中蓝色的线为准确的sin(x)的值,绿色的为模型输出的值,可以看到拟合的比较好,但是在接近sin(x)=0的附近,拟合的比较不好。
