3. 正弦波预测模型部署
任务目标
训练一个基于LSTM的正弦波时间序列预测模型,转换为TensorFlow Lite格式,并部署到STM32F407开发板上,实现基于历史采样点的实时正弦波预测。MCU通过串口接收当前时刻的正弦波采样值,模型输出未来时刻的预测值,并通过DAC输出还原为模拟波形。
环境与依赖
- 训练端:Python 3.8+,TensorFlow 2.10+,numpy,matplotlib(可选)
- 转换工具:tensorflow.lite.TFLiteConverter(随TensorFlow安装)
- 部署端:STM32F407VET6开发板,STM32CubeIDE 1.10+,STM32CubeF4固件包
- TFLite运行时:TFLite Micro(下载并添加到工程)
实现步骤
1. 训练正弦波预测模型(Python)
生成正弦波序列数据,构造滑动窗口样本,训练一个单层LSTM网络用于预测下一个时间点的值。
import numpy as np
import tensorflow as tf
# 生成正弦波数据
fs = 100 # 采样率100Hz
t = np.arange(0, 10, 1/fs)
freq = 1.0 # 1Hz正弦波
data = np.sin(2 * np.pi * freq * t).astype(np.float32)
# 构造滑动窗口样本:用前20个点预测下一点
seq_len = 20
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(0.8 * len(X))
X_train, X_val = X[:split], X[split:]
y_train, y_val = y[:split], y[split:]
# 构建LSTM模型
model = tf.keras.Sequential([
tf.keras.layers.LSTM(16, input_shape=(seq_len, 1), return_sequences=False),
tf.keras.layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.summary()
# 训练
history = model.fit(X_train, y_train, epochs=30, batch_size=32,
validation_data=(X_val, y_val), verbose=1)
# 保存Keras模型
model.save('sine_lstm.h5')
2. 转换为TFLite模型
import tensorflow as tf
# 加载训练好的模型
model = tf.keras.models.load_model('sine_lstm.h5')
# 转换为TFLite
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
# 保存为C数组格式(方便直接嵌入STM32工程)
with open('sine_model.tflite', 'wb') as f:
f.write(tflite_model)
# 生成C头文件
c_array = ', '.join(f'0x{b:02x}' for b in tflite_model)
with open('sine_model_data.h', 'w') as f:
f.write(f'const unsigned char sine_model_tflite[] = {{{c_array}}};\n')
f.write(f'const unsigned int sine_model_tflite_len = {len(tflite_model)};\n')
print(f'模型大小: {len(tflite_model)} 字节')
3. STM32部署代码(C语言)
将生成的sine_model_data.h加入工程,使用TFLite Micro运行时进行推理。ADC1采样正弦波输入,DAC1输出预测结果。
#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"
// 模型输入输出缓冲区
constexpr int kTensorArenaSize = 8 * 1024;
uint8_t tensor_arena[kTensorArenaSize];
// 历史输入缓冲区(20个点)
float input_buffer[20] = {0};
int buffer_index = 0;
// ADC/DAC句柄(由CubeMX生成)
extern ADC_HandleTypeDef hadc1;
extern DAC_HandleTypeDef hdac;
void setup_tflite() {
static tflite::AllOpsResolver resolver;
const tflite::Model* model = tflite::GetModel(sine_model_tflite);
static tflite::MicroInterpreter interpreter(
model, resolver, tensor_arena, kTensorArenaSize);
// 获取输入输出张量
TfLiteTensor* input = interpreter.input(0);
TfLiteTensor* output = interpreter.output(0);
while (1) {
// 读取ADC采样值(0~3.3V映射到-1~1)
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10);
float sample = (HAL_ADC_GetValue(&hadc1) / 4095.0f) * 2.0f - 1.0f;
// 更新输入缓冲区
input_buffer[buffer_index] = sample;
buffer_index = (buffer_index + 1) % 20;
// 按时间顺序填入模型输入
for (int i = 0; i < 20; i++) {
int idx = (buffer_index + i) % 20;
input->data.f[i] = input_buffer[idx];
}
// 执行推理
interpreter.Invoke();
// 获取预测值并输出到DAC(-1~1映射到0~3.3V)
float prediction = output->data.f[0];
uint16_t dac_value = (uint16_t)((prediction + 1.0f) / 2.0f * 4095.0f);
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value);
HAL_Delay(10); // 100Hz采样率
}
}
运行与验证
- 训练验证:训练完成后,在Python中加载模型对测试集进行预测,计算MSE应小于0.001,表明模型已收敛。
- 转换验证:使用
tflite_runtime.interpreter在PC上加载sine_model.tflite,输入相同测试样本,输出应与Keras模型误差小于1e-4。 - 硬件验证:将信号发生器输出1Hz、1V偏置、2Vpp的正弦波接入STM32的ADC引脚。用示波器观察DAC引脚输出,应得到同频率、同幅度的正弦波,且相位超前输入约20个采样点(200ms)。
- 通过标准:DAC输出波形频率误差小于1%,THD小于5%,且连续运行10分钟无异常复位。
常见问题
- 模型无法加载:确认
sine_model_data.h中的数组与模型文件一致,且TFLite Micro版本与转换工具版本兼容(推荐均使用2.10+)。 - 推理结果恒为0:检查输入张量数据排列是否按时间顺序,LSTM对输入顺序敏感。
- DAC输出噪声大:在DAC输出端增加RC低通滤波器(截止频率约10Hz),滤除高频量化噪声。