将机器学习算法移植到低端MCU上的实用指南
在物联网(IoT)和边缘计算迅猛发展的今天,将智能功能嵌入到资源有限的低端单片机(Microcontroller Unit, MCU)上,已经成为许多开发者和工程师追求的目标。然而,这一过程充满挑战,但只要掌握正确的方法,也能在低端MCU上实现高效的机器学习应用。本文将以具体的案例为例,逐步讲解每个步骤的实际操作,包括所需的工具、命令和代码示例,帮助开发者成功地将机器学习算法移植到不同平台的低端MCU上。
版权声明
本文为原创内容,版权所有。未经许可,不得转载或用于商业用途。
版权所有 © 深圳市为也科技有限公司
目录
案例概述
目标:在不同的低端ARM Cortex-M系列MCU(如STM32F4)、ESP32和NXP LPC系列上部署一个简单的神经网络模型,实现基于传感器数据的二分类任务(例如,温度异常检测)。
工具与资源:
- 硬件:
- STM32F4:STM32F4 Discovery Kit
- ESP32:ESP32 DevKitC
- NXP LPC:NXP LPC845 Development Board
- 传感器:温度传感器(如DS18B20)
- 软件:
- 开发环境:
- STM32CubeIDE(用于STM32F4)
- ESP-IDF(用于ESP32)
- MCUXpresso IDE(用于NXP LPC)
- 机器学习框架:TensorFlow
- 模型转换工具:TensorFlow Lite Converter
- 其他工具:xxd(用于将模型转换为C数组)
- 开发环境:
准备开发环境
1. 安装必要的软件
a. 安装Python和相关库
确保您的计算机上安装了Python 3.6及以上版本。然后,通过pip
安装TensorFlow和其他必要的库。
# 安装pip(如果尚未安装)
sudo apt-get install python3-pip
# 安装TensorFlow
pip install tensorflow
# 安装其他必要库
pip install numpy
b. 安装开发环境
根据不同的MCU平台,安装相应的集成开发环境(IDE)。
-
STM32F4:安装STM32CubeIDE
- 下载地址:STM32CubeIDE 下载
- 按照官网指引完成安装。
-
ESP32:安装ESP-IDF
- 下载地址:ESP-IDF 下载
- 按照官方文档进行安装和配置。
-
NXP LPC:安装MCUXpresso IDE
- 下载地址:MCUXpresso IDE 下载
- 按照官网指引完成安装。
2. 准备硬件
- 连接开发板:将各自的开发板(STM32F4 Discovery Kit、ESP32 DevKitC、NXP LPC845 Development Board)通过USB连接到计算机。
- 连接温度传感器:将DS18B20温度传感器连接到开发板的GPIO引脚(例如,STM32F4的PA0,ESP32的GPIO4,NXP LPC的P0_4)。
数据准备与模型训练
1. 收集和准备数据
假设我们有一组温度数据,用于训练一个简单的二分类模型(正常/异常)。
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
2. 定义和训练模型
使用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)
3.评估模型
loss, accuracy = model.evaluate(X_train, y_train)
print(f"训练准确率: {accuracy * 100:.2f}%")
4. 模型量化
为了在低端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)
将模型转换为C数组
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平台的项目
本节将介绍如何在不同的MCU平台上配置项目,并集成TensorFlow Lite Micro库。
配置不同MCU平台的项目
本节将介绍如何在不同的MCU平台上配置项目,并集成TensorFlow Lite Micro库。
STM32F4平台
配置STM32CubeIDE项目
1. 创建新的STM32项目
- 打开STM32CubeIDE,选择
File
->New
->STM32 Project
。 - 在弹出的窗口中,选择您的开发板型号(如STM32F407VG),点击
Next
。 - 设置项目名称(如
TemperatureClassifier
),点击Finish
。
2. 配置GPIO和UART
- GPIO配置:
- 在
Pinout & Configuration
界面,将PA0配置为GPIO_Input
,用于连接温度传感器。
- 在
- UART配置:
- 配置USART2或其他可用的UART接口为
Asynchronous
模式。 - 启用相关引脚(如PA2和PA3)用于串口通信。
- 配置USART2或其他可用的UART接口为
- 生成初始化代码:
- 点击
Project
->Generate Code
,STM32CubeIDE将生成初始化代码。
- 点击
3. 添加TensorFlow Lite Micro库
由于STM32CubeIDE不直接支持TensorFlow Lite Micro,我们需要手动集成相关库。
a. 下载TensorFlow Lite Micro
git clone https://github.com/tensorflow/tflite-micro.git
b. 添加必要的源文件到项目
- 打开克隆下来的
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
c. 添加模型数据
- 将之前生成的
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; // 模型长度
4. 配置项目的编译路径
确保您的项目包含了TensorFlow Lite Micro的源文件和头文件。您可能需要在项目的Include Paths
中添加相关目录。
编写推理代码
在main.c
或main.cpp
中编写代码,实现模型加载、推理和结果输出。
示例代码(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()
函数需要根据具体的传感器和硬件接口进行实现。- UART用于输出推理结果,可以通过串口监视器查看输出。
tensor_arena
大小需要根据模型的需求进行调整,避免内存溢出。
编译与烧录
1. 编译项目
在STM32CubeIDE中,点击“Build”按钮,编译项目。确保没有编译错误。
2. 烧录固件
使用ST-Link或其他编程工具,将编译生成的固件烧录到STM32F4开发板。
- 步骤:
- 连接STM32F4开发板到计算机。
- 点击STM32CubeIDE中的“Run”按钮,选择合适的编程接口(如ST-Link)。
- 等待烧录完成,确保固件正确加载。
测试与验证
1. 连接串口监视器
使用串口监视器(如PuTTY或Tera Term),连接到MCU的UART接口,波特率设置为115200(根据配置)。
步骤:
- 打开串口监视器,选择正确的串口号和波特率。
- 观察输出结果,验证模型的推理是否正常。
2. 验证一致性
在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]}")
比较结果:
- 确保MCU输出与Python推理结果一致,验证移植的正确性。
优化与提升
1. 优化内存使用
- 调整内存池大小:根据模型的需求,适当增大或减小
tensor_arena_size
,避免内存浪费或溢出。 - 静态内存分配:尽量避免动态内存分配,减少内存碎片。
- 内存复用:在不同模块之间复用内存空间,最大化内存利用率。
2. 提升推理速度
- 使用硬件加速器:如果STM32F4具备DSP指令集,可以优化代码以利用这些指令,加速计算。
- 优化代码结构:减少不必要的计算和内存访问,提高代码效率。
- 简化模型架构:减少模型的层数和参数量,降低计算需求。
3. 增强模型性能
- 进一步剪枝和量化:在保证准确率的前提下,继续优化模型,减少资源消耗。
- 模型蒸馏:通过知识蒸馏,提升小模型的性能,使其更适合低端MCU。
工具与库
-
CMake
- CMake
- 用于跨平台构建项目的工具,适用于管理嵌入式项目的编译过程。
-
GNU Arm Embedded Toolchain
- GNU Arm Embedded Toolchain
- 免费的编译工具链,适用于ARM架构的MCU开发。
-
OpenOCD
- OpenOCD
- 开源的调试器和编程工具,支持多种调试接口和MCU型号。
总结
将机器学习算法移植到低端MCU上需要综合考虑硬件限制、模型选择与优化、工具和框架的支持以及开发流程的各个环节。通过上述详细的步骤和实际操作示例,可以逐步实现这一复杂的任务。成功移植的关键在于:
- 深入理解硬件限制:合理选择和优化模型,确保其在资源有限的MCU上高效运行。
- 合理利用工具与框架:如TensorFlow Lite Micro、CMSIS-NN等,简化开发流程。
- 严格的测试与验证:确保模型在目标硬件上的可靠性和性能。
- 持续优化与迭代:根据实际应用需求,不断提升系统的智能化水平。
通过系统化的规划和执行,可以在资源受限的低端MCU上实现高效、智能的机器学习应用,推动物联网和边缘计算的发展。如果在实际操作过程中遇到具体问题,欢迎随时提问,我将尽力为您提供帮助!
版权声明
本文为原创内容,版权所有。未经许可,不得转载或用于商业用途。
版权所有 © 深圳市为也科技有限公司
标签:低端,tflite,模型,算法,np,interpreter,model,MCU From: https://blog.csdn.net/WYKJ_001/article/details/145180515