3. 正弦波预测模型部署
任务目标
训练一个轻量级RNN模型,用于预测正弦波的下一个采样点数值。模型经TensorFlow Lite量化后部署到STM32F407开发板,通过串口以CSV格式输出预测值与真实值,供上位机对比验证。
环境与依赖
- PC端:Python 3.8+,TensorFlow 2.10+,numpy,pandas(用于验证脚本)
- STM32端:STM32CubeIDE 1.10+,STM32F407VET6开发板,板载LED(PB0)用于指示推理状态
- 通信:USART2(PA2/PA3),波特率115200,8N1
1. 模型训练与量化(PC端)
生成正弦波序列,构造滑动窗口数据集训练一个3层LSTM模型。训练完成后转换为TFLite格式,并应用动态范围量化(权重从float32转为int8,激活保持float32),在保持精度的同时将模型体积压缩约4倍。
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
# 生成正弦波数据
x = np.linspace(0, 100*np.pi, 5000)
data = np.sin(x).astype(np.float32)
# 构造序列样本
seq_len = 16
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).reshape(-1, 1)
# 划分训练/验证集
split = int(len(X)*0.8)
X_train, X_val = X[:split], X[split:]
y_train, y_val = y[:split], y[split:]
# 构建模型
model = Sequential([
LSTM(16, input_shape=(seq_len, 1), return_sequences=True),
LSTM(8),
Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=10, batch_size=64, validation_data=(X_val, y_val), verbose=1)
# 转换为TFLite并量化
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
with open('sine_model.tflite', 'wb') as f:
f.write(tflite_model)
# 验证量化模型精度
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
in_idx = interpreter.get_input_details()[0]['index']
out_idx = interpreter.get_output_details()[0]['index']
preds = []
for i in range(100):
interpreter.set_tensor(in_idx, X_val[i:i+1])
interpreter.invoke()
preds.append(interpreter.get_tensor(out_idx)[0][0])
mse = np.mean((np.array(preds) - y_val[:100, 0])**2)
print(f'量化后MSE: {mse:.6f}')
将生成的sine_model.tflite文件添加到STM32CubeIDE项目的model/目录下。
2. STM32部署代码
使用TFLite Micro推理引擎,模型数据通过xxd工具转为C数组嵌入固件。核心推理流程如下:
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "sine_model_data.h" // 由xxd生成
// 全局变量
constexpr int tensor_arena_size = 8 * 1024;
uint8_t tensor_arena[tensor_arena_size];
static tflite::MicroInterpreter* interpreter = nullptr;
// 初始化TFLite
void tflite_init() {
static tflite::AllOpsResolver resolver;
const tflite::Model* model = tflite::GetModel(sine_model_tflite);
static tflite::MicroInterpreter static_interpreter(
model, resolver, tensor_arena, tensor_arena_size);
interpreter = &static_interpreter;
interpreter->AllocateTensors();
}
// 执行推理:输入16个历史采样点,输出下一个预测值
float predict_next(float* history) {
float* input = interpreter->input(0)->data.f;
for (int i = 0; i < 16; i++) input[i] = history[i];
interpreter->Invoke();
return interpreter->output(0)->data.f[0];
}
主循环逻辑:每100ms采集一次真实正弦值(由DAC生成或查表),将最近16个点送入模型,输出预测值。通过串口发送真实值,预测值,格式为%.4f,%.4f\n。
3. 运行与验证
- PC端验证:训练脚本运行结束后,观察MSE是否<0.001。若MSE过大,增加训练轮次至20或调整LSTM单元数。
- STM32部署:编译烧录后,打开串口助手(115200),应看到持续输出的两列浮点数。
- 上位机对比:将串口数据保存为CSV,用Python脚本计算预测值与真实值的均方根误差(RMSE),RMSE<0.05即判定部署成功。
# 上位机验证脚本
import pandas as pd
import numpy as np
df = pd.read_csv('serial_output.csv', header=None, names=['real', 'pred'])
rmse = np.sqrt(np.mean((df['real'] - df['pred'])**2))
print(f'RMSE: {rmse:.4f}')
assert rmse < 0.05, '预测误差过大'
4. 常见问题
- 编译错误:确认已将
sine_model_data.h和.cc文件加入工程,且包含路径正确。 - 输出恒为0:检查输入张量索引是否正确,LSTM需要连续时间步输入,不能重置状态。
- 精度不足:动态范围量化对RNN精度影响较大,可改用全整型量化(需提供代表性数据集),或增加LSTM单元数。