在物联网(IoT)和边缘计算迅猛发展的今天,将智能功能嵌入到资源有限的低端单片机(Microcontroller Unit, MCU)上,已经成为许多开发者和工程师追求的目标。然而,这一过程充满挑战,但只要掌握正确的方法,也能在低端MCU上实现高效的机器学习应用。本文将以具体的案例为例,逐步讲解每个步骤的实际操作,包括所需的工具、命令和代码示例,帮助开发者成功地将机器学习算法移植到不同平台的低端MCU上。
本文为原创内容,版权所有。未经许可,不得转载或用于商业用途。
版权所有 © 深圳市为也科技有限公司
目标:在不同的低端ARM Cortex-M系列MCU(如STM32F4)、ESP32和NXP LPC系列上部署一个简单的神经网络模型,实现基于传感器数据的二分类任务(例如,温度异常检测)。
工具与资源:
确保您的计算机上安装了Python 3.6及以上版本。然后,通过pip
安装TensorFlow和其他必要的库。
# 安装pip(如果尚未安装)
sudo apt-get install python3-pip
# 安装TensorFlow
pip install tensorflow
# 安装其他必要库
pip install numpy
根据不同的MCU平台,安装相应的集成开发环境(IDE)。
STM32F4:安装STM32CubeIDE
ESP32:安装ESP-IDF
NXP LPC:安装MCUXpresso IDE
假设我们有一组温度数据,用于训练一个简单的二分类模型(正常/异常)。
import numpy as np
# 生成示例数据
# 正常温度范围:20°C - 30°C
# 异常温度范围:30°C - 40°C
np.random.seed(42)
normal_temps = np.random.uniform(20, 30, 500).astype(np.float32)
anomalous_temps = np.random.uniform(30, 40, 500).astype(np.float32)
# 标签:0 - 正常, 1 - 异常
temps = np.concatenate([normal_temps, anomalous_temps])
labels = np.concatenate([np.zeros(500), np.ones(500)])
# 打乱数据
indices = np.arange(temps.shape[0])
np.random.shuffle(indices)
temps = temps[indices]
labels = labels[indices]
# 特征扩展(例如,将温度转换为多个特征)
# 这里简单地将温度值作为单一特征
X_train = temps.reshape(-1, 1)
y_train = labels
使用TensorFlow构建一个简单的全连接神经网络。
import tensorflow as tf
# 定义模型
model = tf.keras.Sequential([
tf.keras.layers.Dense(8, activation='relu', input_shape=(1,)),
tf.keras.layers.Dense(1, activation='sigmoid')
])
# 编译模型
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
# 训练模型
model.fit(X_train, y_train, epochs=50, batch_size=16, validation_split=0.2)
loss, accuracy = model.evaluate(X_train, y_train)
print(f"训练准确率: {accuracy * 100:.2f}%")
为了在低端MCU上运行,我们需要将模型进行量化,减少模型大小和计算需求。
# 转换为TensorFlow Lite模型并进行动态范围量化
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
# 保存量化后的模型
with open('model.tflite', 'wb') as f:
f.write(tflite_model)
STM32CubeIDE需要将模型文件嵌入到C代码中。我们将使用xxd
工具将.tflite
文件转换为C数组。
xxd -i model.tflite > model_data.cc
解释:model_data.cc
将包含模型的二进制数据,以C数组的形式表示。
内容示例:
unsigned char model_tflite[] = {
0x20, 0x00, 0x00, 0x00, // 模型数据...
// 更多数据...
};
unsigned int model_tflite_len = 1234; // 模型长度
本节将介绍如何在不同的MCU平台上配置项目,并集成TensorFlow Lite Micro库。
本节将介绍如何在不同的MCU平台上配置项目,并集成TensorFlow Lite Micro库。
配置STM32CubeIDE项目
File
-> New
-> STM32 Project
。Next
。TemperatureClassifier
),点击Finish
。Pinout & Configuration
界面,将PA0配置为GPIO_Input
,用于连接温度传感器。Asynchronous
模式。Project
-> Generate Code
,STM32CubeIDE将生成初始化代码。由于STM32CubeIDE不直接支持TensorFlow Lite Micro,我们需要手动集成相关库。
git clone https://github.com/tensorflow/tflite-micro.git
tensorflow/tflite-micro
目录,找到以下文件并复制到您的STM32CubeIDE项目中(例如,创建一个tensorflow
文件夹):
all_ops_resolver.cc
和 all_ops_resolver.h
micro_interpreter.cc
和 micro_interpreter.h
micro_error_reporter.cc
和 micro_error_reporter.h
schema/schema_generated.h
version.h
model_data.cc
和model_data.h
添加到项目中。model_data.h
中声明了模型数组:#ifndef MODEL_DATA_H
#define MODEL_DATA_H
extern unsigned char model_tflite[];
extern unsigned int model_tflite_len;
#endif // MODEL_DATA_H
3.在model_data.cc
中定义模型数组:
unsigned char model_tflite[] = {
// 模型的二进制数据
0x20, 0x00, 0x00, 0x00, // 示例数据
// 更多数据...
};
unsigned int model_tflite_len = 1234; // 模型长度
确保您的项目包含了TensorFlow Lite Micro的源文件和头文件。您可能需要在项目的Include Paths
中添加相关目录。
在main.c
或main.cpp
中编写代码,实现模型加载、推理和结果输出。
#include "main.h"
#include "model_data.h"
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/version.h"
// 内存池大小,根据模型大小调整
constexpr int tensor_arena_size = 2 * 1024;
uint8_t tensor_arena[tensor_arena_size];
// UART缓冲区
char uart_buffer[100];
// 假设的温度读取函数
float Read_Temperature() {
// 实现具体的温度传感器读取逻辑
// 例如,通过ADC读取PA0引脚的模拟信号,并转换为温度值
// 这里返回一个随机值作为示例
return 25.0 + (rand() % 1000) / 100.0; // 25.0°C - 35.0°C
}
int main(void)
{
// 初始化HAL库
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
// 初始化TensorFlow Lite Micro
static tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = µ_error_reporter;
// 获取模型
const tflite::Model* model = tflite::GetModel(model_tflite);
if (model->version() != TFLITE_SCHEMA_VERSION) {
error_reporter->Report("Model provided is schema version %d not equal to supported version %d.",
model->version(), TFLITE_SCHEMA_VERSION);
return -1;
}
// 创建操作解析器
static tflite::MicroMutableOpResolver<10> resolver;
resolver.AddBuiltin(tflite::BuiltinOperator_FULLY_CONNECTED, tflite::ops::micro::Register_FULLY_CONNECTED());
resolver.AddBuiltin(tflite::BuiltinOperator_RELU, tflite::ops::micro::Register_RELU());
resolver.AddBuiltin(tflite::BuiltinOperator_SIGMOID, tflite::ops::micro::Register_SIGMOID());
// 创建解释器
static tflite::MicroInterpreter interpreter(model, resolver, tensor_arena, tensor_arena_size, error_reporter);
TfLiteStatus allocate_status = interpreter.AllocateTensors();
if (allocate_status != kTfLiteOk) {
error_reporter->Report("AllocateTensors() failed");
return -1;
}
// 获取输入和输出张量
TfLiteTensor* input = interpreter.input(0);
TfLiteTensor* output = interpreter.output(0);
while (1)
{
// 读取温度传感器数据(假设通过ADC读取,并转换为浮点数)
float temperature = Read_Temperature();
// 设置输入数据
input->data.f[0] = temperature;
// 运行推理
TfLiteStatus invoke_status = interpreter.Invoke();
if (invoke_status != kTfLiteOk) {
error_reporter->Report("Invoke failed");
return -1;
}
// 获取输出
float prediction = output->data.f[0];
// 根据预测结果执行相应操作
if (prediction > 0.5) {
// 异常温度,发送警报
snprintf(uart_buffer, sizeof(uart_buffer), "Temperature Anomaly Detected: %.2f°C\r\n", temperature);
HAL_UART_Transmit(&huart2, (uint8_t*)uart_buffer, strlen(uart_buffer), HAL_MAX_DELAY);
} else {
// 正常温度
snprintf(uart_buffer, sizeof(uart_buffer), "Temperature Normal: %.2f°C\r\n", temperature);
HAL_UART_Transmit(&huart2, (uint8_t*)uart_buffer, strlen(uart_buffer), HAL_MAX_DELAY);
}
// 延时1秒
HAL_Delay(1000);
}
}
说明:
Read_Temperature()
函数需要根据具体的传感器和硬件接口进行实现。tensor_arena
大小需要根据模型的需求进行调整,避免内存溢出。在STM32CubeIDE中,点击“Build”按钮,编译项目。确保没有编译错误。
使用ST-Link或其他编程工具,将编译生成的固件烧录到STM32F4开发板。
使用串口监视器(如PuTTY或Tera Term),连接到MCU的UART接口,波特率设置为115200(根据配置)。
步骤:
在Python中运行同样的输入数据,确保C++代码的输出与Python模型一致。
Python验证示例:
import tensorflow as tf
import numpy as np
# 加载TFLite模型
interpreter = tf.lite.Interpreter(model_path="model.tflite")
interpreter.allocate_tensors()
# 获取输入输出张量
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
# 读取温度数据
temperature = 25.0 # 示例值
input_data = np.array([[temperature]], dtype=np.float32)
interpreter.set_tensor(input_details[0]['index'], input_data)
# 运行推理
interpreter.invoke()
# 获取输出
output_data = interpreter.get_tensor(output_details[0]['index'])
print(f"Temperature: {temperature}°C, Prediction: {output_data[0][0]}")
比较结果:
tensor_arena_size
,避免内存浪费或溢出。CMake
GNU Arm Embedded Toolchain
OpenOCD
将机器学习算法移植到低端MCU上需要综合考虑硬件限制、模型选择与优化、工具和框架的支持以及开发流程的各个环节。通过上述详细的步骤和实际操作示例,可以逐步实现这一复杂的任务。成功移植的关键在于:
通过系统化的规划和执行,可以在资源受限的低端MCU上实现高效、智能的机器学习应用,推动物联网和边缘计算的发展。如果在实际操作过程中遇到具体问题,欢迎随时提问,我将尽力为您提供帮助!
本文为原创内容,版权所有。未经许可,不得转载或用于商业用途。
版权所有 © 深圳市为也科技有限公司