摘要: 工业设备的振动监测是边缘 AI 最典型的应用场景之一------传统方案把振动数据上传云端分析,延迟高、流量贵、隐私差;而本地推理需要在只有几百 KB RAM 的 MCU 上跑模型。本文用一个真实的电机故障诊断项目,从零演示在 MCU 上部署 TinyML 模型的完整流程------数据采集、特征工程、模型训练、量化、转换、部署到 STM32。包含 TensorFlow Lite for Microcontrollers 的实际使用、CMSIS-NN 加速、内存优化和性能实测。这是付费专栏第一季的收尾篇,也是第二季《嵌入式边缘 AI 实战》的开篇导引。建议收藏。
一、引子:一个"看起来很美好"的云端方案
去年做一款工业电机监测设备,需求是:实时监测电机振动,提前预警轴承故障。
第一版方案很"标准":
-
STM32F407 采集振动传感器(加速度计)数据,1kHz 采样
-
通过 4G 模块上传到云端
-
云端跑 AI 模型分析
-
发现异常后推送告警
方案很快遇到了三个问题:
问题一:流量成本。 三轴加速度计,1kHz 采样,每轴 16 位,一天的数据量:
3 轴 × 1000 Hz × 2 字节 × 86400 秒 = 518 MB/天
每台设备每天 500MB 流量,1000 台设备就是 500GB/天。 4G 流量费一年 5 万多元。
问题二:延迟。 数据上传 + 云端推理 + 告警下发,端到端延迟 2-5 秒。对于高速旋转的电机,轴承损坏可能在几秒内就从"早期"发展到"严重"。
问题三:隐私。 客户是军工企业,振动数据属于敏感信息,不允许出厂区。
结论:必须本地推理。
但本地推理要解决一个核心问题:MCU 只有 192KB RAM、1MB Flash,怎么跑 AI 模型?
这就是 TinyML 要解决的问题。
二、TinyML 的核心思路
2.1 为什么 MCU 能跑 AI
很多人对"MCU 跑 AI"有误解,认为这是不可能的事。其实只要模型足够小,MCU 完全能跑。
关键认知:
-
不是所有 AI 都需要 GPT 那种大模型------很多工业场景只需要简单分类器
-
模型可以压缩:量化(float32 → int8)能减 4 倍
-
特征工程比模型复杂更重要:好的特征能让简单模型达到好效果
-
硬件加速可用:Cortex-M4/M7 的 DSP 指令、CMSIS-NN 库
一个典型的 MCU 异常检测模型:

这种规模的模型,STM32F4 完全能跑。
2.2 TinyML 的三大技术

本项目的核心是量化,这也是最成熟、最容易落地的技术。
2.3 整体流程
1. 数据采集(振动信号)
↓
2. 特征工程(FFT、时域统计)
↓
3. PC 端训练模型(Keras)
↓
4. 模型量化(TFLite Converter)
↓
5. 转换为 C 数组(xxd)
↓
6. 集成到 MCU 工程(TFLite Micro)
↓
7. 部署 + 优化(CMSIS-NN)
↓
8. 实测验证
三、数据采集与特征工程
3.1 硬件选型

3.2 数据采集
采集正常和故障两种状态下的振动数据。
// vibration_capture.c
#include "stm32f4xx_hal.h"
#define SAMPLE_RATE 1000
#define SAMPLE_DURATION 1 // 采集 1 秒 = 1000 个点
typedef struct {
int16_t x;
int16_t y;
int16_t z;
} accel_sample_t;
accel_sample_t g_buffer[SAMPLE_RATE * SAMPLE_DURATION];
void CaptureVibration(void)
{
uint32_t start = HAL_GetTick();
uint32_t idx = 0;
while (idx < SAMPLE_RATE * SAMPLE_DURATION) {
uint32_t now = HAL_GetTick();
if (now - start >= idx) {
// 每 1ms 采一次
ADXL345_Read(&g_buffer[idx].x, &g_buffer[idx].y, &g_buffer[idx].z);
idx++;
}
}
}
采集数据量: 每个状态 100 组,每组 1 秒。

这些数据通过 UART 传输到 PC,保存为 CSV。
3.3 特征工程
关键点:不要把原始振动信号直接喂给模型,而是提取特征。
原因:
-
原始信号 1000×3=3000 个点,模型输入太大会占用大量 RAM
-
特征能大幅压缩输入,比如 FFT 的前 20 个峰值 = 20 个特征
-
特征有物理意义,可解释性强
本项目提取的特征:
python
# feature_extraction.py
import numpy as np
from scipy.fft import fft
def extract_features(samples: np.ndarray) -> np.ndarray:
"""从振动信号提取特征
samples: shape (N, 3),N 个采样点,3 轴
返回: shape (F,),F 个特征
"""
features = []
for axis in range(3):
signal = samples[:, axis].astype(np.float32)
# 时域特征
features.append(np.mean(signal))
features.append(np.std(signal))
features.append(np.max(np.abs(signal)))
features.append(np.sqrt(np.mean(signal ** 2))) # RMS
# 峰峰值
features.append(np.max(signal) - np.min(signal))
# 峭度(Kurtosis,对冲击敏感)
features.append(np.mean(((signal - np.mean(signal)) / (np.std(signal) + 1e-8)) ** 4))
# 频域特征(FFT)
spectrum = np.abs(fft(signal))[:len(signal) // 2]
# 前 5 个峰值频率对应的幅值
top5_idx = np.argsort(spectrum)[-5:]
features.extend(spectrum[top5_idx])
# 频谱质心
features.append(np.sum(np.arange(len(spectrum)) * spectrum) / (np.sum(spectrum) + 1e-8))
return np.array(features, dtype=np.float32)
每轴提取 12 个特征,3 轴共 36 个特征。
为什么这些特征有用:

轴承故障的典型特征: 在轴承故障特征频率(BPFO、BPFI)处出现峰值。峭度和 FFT 峰值是最重要的特征。
3.4 数据集准备
python
# prepare_dataset.py
import numpy as np
import pandas as pd
from glob import glob
def load_dataset():
X = []
y = []
# 加载正常样本
for f in glob('data/normal/*.csv'):
samples = pd.read_csv(f).values
features = extract_features(samples)
X.append(features)
y.append(0) # 0 = 正常
# 加载轻微异常样本
for f in glob('data/minor/*.csv'):
samples = pd.read_csv(f).values
features = extract_features(samples)
X.append(features)
y.append(1) # 1 = 轻微异常
# 加载严重故障样本
for f in glob('data/severe/*.csv'):
samples = pd.read_csv(f).values
features = extract_features(samples)
X.append(features)
y.append(2) # 2 = 严重故障
X = np.array(X, dtype=np.float32)
y = np.array(y, dtype=np.int32)
# 打乱顺序
idx = np.random.permutation(len(X))
X, y = X[idx], y[idx]
# 划分训练集/测试集(8:2)
split = int(len(X) * 0.8)
return X[:split], y[:split], X[split:], y[split:]
if __name__ == '__main__':
X_train, y_train, X_test, y_test = load_dataset()
np.save('X_train.npy', X_train)
np.save('y_train.npy', y_train)
np.save('X_test.npy', X_test)
np.save('y_test.npy', y_test)
print(f'训练集: {X_train.shape}, 测试集: {X_test.shape}')
print(f'特征维度: {X_train.shape[1]}')
四、PC 端训练模型
4.1 模型设计
关键原则:
关键原则:
-
模型要小:< 100KB(量化后)
-
输入要小:本项目 36 个特征
-
输出要简单:3 分类
-
层数要少:2-3 层全连接就够
python
# train_model.py
import numpy as np
import tensorflow as tf
from tensorflow import keras
def build_model(input_dim: int, num_classes: int) -> keras.Model:
"""构建小型全连接网络"""
model = keras.Sequential([
keras.layers.Input(shape=(input_dim,)),
# 第一层:36 -> 32
keras.layers.Dense(32, activation='relu'),
keras.layers.Dropout(0.2),
# 第二层:32 -> 16
keras.layers.Dense(16, activation='relu'),
# 输出层:16 -> 3
keras.layers.Dense(num_classes, activation='softmax'),
])
return model
def main():
# 加载数据
X_train = np.load('X_train.npy')
y_train = np.load('y_train.npy')
X_test = np.load('X_test.npy')
y_test = np.load('y_test.npy')
# 特征归一化(重要!)
mean = X_train.mean(axis=0)
std = X_train.std(axis=0) + 1e-8
X_train = (X_train - mean) / std
X_test = (X_test - mean) / std
# 保存归一化参数(部署时要一起嵌入)
np.save('norm_mean.npy', mean)
np.save('norm_std.npy', std)
# 构建模型
model = build_model(X_train.shape[1], 3)
model.summary()
# 编译
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'],
)
# 训练
history = model.fit(
X_train, y_train,
validation_data=(X_test, y_test),
epochs=100,
batch_size=16,
verbose=1,
)
# 评估
loss, acc = model.evaluate(X_test, y_test, verbose=0)
print(f'测试集准确率: {acc:.4f}')
# 保存模型
model.save('vibration_model.h5')
if __name__ == '__main__':
main()
典型训练结果:
python
Epoch 100/100
8/8 [==============================] - 0s 5ms/step - loss: 0.0321 - accuracy: 0.9917
测试集准确率: 0.9833
未量化的模型:
-
参数量:36×32 + 32 + 32×16 + 16 + 16×3 + 3 = 1727 个参数
-
模型大小:1727 × 4 字节 = 6.9 KB
已经很小了,但我们可以继续压缩。
4.2 模型量化
量化的核心:把 float32 权重和激活值转成 int8。
# quantize_model.py
import numpy as np
import tensorflow as tf
def representative_dataset():
"""提供代表性样本用于量化校准"""
X_train = np.load('X_train.npy')
mean = np.load('norm_mean.npy')
std = np.load('norm_std.npy')
X_train = (X_train - mean) / std
for i in range(100):
yield [X_train[i:i+1].astype(np.float32)]
def main():
# 加载模型
model = tf.keras.models.load_model('vibration_model.h5')
# 转换为 TFLite
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 启用量化
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
# 强制输入输出也是 int8
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
# 转换
tflite_model = converter.convert()
# 保存
with open('vibration_model_int8.tflite', 'wb') as f:
f.write(tflite_model)
print(f'量化后模型大小: {len(tflite_model)} 字节')
if __name__ == '__main__':
main()
量化效果:

精度只掉 0.5%,体积减少 74%,绝对值得。
4.3 转成 C 数组
# 把 .tflite 文件转成 C 数组
xxd -i vibration_model_int8.tflite > vibration_model.cc
生成的 C 文件:
unsigned char vibration_model_int8_tflite[] = {
0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x00, 0x00, 0x12, 0x00,
// ... 省略
};
unsigned int vibration_model_int8_tflite_len = 1840;
这个 C 数组就是要嵌入到固件里的模型。
五、MCU 端部署
5.1 集成 TensorFlow Lite Micro
TFLite Micro 是 Google 为 MCU 设计的轻量级推理引擎。
获取源码:
git clone https://github.com/tensorflow/tflite-micro.git
需要加入工程的源文件:
tflite-micro/tensorflow/lite/
├── micro/
│ ├── micro_interpreter.cc
│ ├── micro_error_reporter.cc
│ ├── micro_allocator.cc
│ ├── micro_string.cc
│ ├── memory_planner/
│ │ └── greedy_memory_planner.cc
│ └── kernels/
│ ├── fully_connected.cc
│ ├── softmax.cc
│ └── ...
└── ...
通过 Makefile 集成:
# Makefile
TFLM_DIR = tflite-micro/tensorflow/lite
C_SOURCES += \
$(TFLM_DIR)/micro/micro_interpreter.cc \
$(TFLM_DIR)/micro/micro_error_reporter.cc \
$(TFLM_DIR)/micro/micro_allocator.cc \
$(TFLM_DIR)/micro/micro_string.cc \
$(TFLM_DIR)/micro/memory_planner/greedy_memory_planner.cc \
$(TFLM_DIR)/micro/kernels/fully_connected.cc \
$(TFLM_DIR)/micro/kernels/softmax.cc \
$(TFLM_DIR)/micro/kernels/quantize.cc \
$(TFLM_DIR)/micro/kernels/dequantize.cc
C_INCLUDES += \
-I$(TFLM_DIR)/.. \
-I$(TFLM_DIR)/micro \
-I$(TFLM_DIR)/micro/kernels
CFLAGS += -DTF_LITE_STATIC_MEMORY
5.2 MCU 端推理代码
// vibration_inference.cc
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "vibration_model.h" // 包含模型数组
// 模型和解释器
static const tflite::Model* s_model = nullptr;
static tflite::MicroInterpreter* s_interpreter = nullptr;
static TfLiteTensor* s_input = nullptr;
static TfLiteTensor* s_output = nullptr;
// 张量竞技场(Tensor Arena):模型运行时的工作内存
// 大小需要根据模型确定,从 8KB 开始试
static constexpr int kTensorArenaSize = 8 * 1024;
static uint8_t s_tensor_arena[kTensorArenaSize];
// 输出分类名称
static const char* kClassNames[] = {"正常", "轻微异常", "严重故障"};
int Vibration_Init(void)
{
// 1. 加载模型
s_model = tflite::GetModel(vibration_model_int8_tflite);
if (s_model->version() != TFLITE_SCHEMA_VERSION) {
return -1;
}
// 2. 注册算子(只注册模型用到的,节省 Flash)
static tflite::MicroMutableOpResolver<4> resolver;
resolver.AddFullyConnected();
resolver.AddSoftmax();
resolver.AddQuantize();
resolver.AddDequantize();
// 3. 创建解释器
static tflite::MicroErrorReporter error_reporter;
static tflite::MicroInterpreter static_interpreter(
s_model, resolver, s_tensor_arena, kTensorArenaSize, &error_reporter);
s_interpreter = &static_interpreter;
// 4. 分配张量
if (s_interpreter->AllocateTensors() != kTfLiteOk) {
return -2;
}
// 5. 获取输入输出张量
s_input = s_interpreter->input(0);
s_output = s_interpreter->output(0);
return 0;
}
// 执行推理
int Vibration_Inference(const float *features, int feature_count,
float *probabilities, int *predicted_class)
{
if (feature_count != s_input->dims->data[1]) {
return -1;
}
// 输入量化参数
float input_scale = s_input->params.scale;
int32_t input_zero_point = s_input->params.zero_point;
// 1. 把 float 特征量化成 int8
for (int i = 0; i < feature_count; i++) {
int32_t quantized = (int32_t)(features[i] / input_scale) + input_zero_point;
// 限制到 int8 范围
if (quantized > 127) quantized = 127;
if (quantized < -128) quantized = -128;
s_input->data.int8[i] = (int8_t)quantized;
}
// 2. 执行推理
if (s_interpreter->Invoke() != kTfLiteOk) {
return -2;
}
// 3. 输出去量化
float output_scale = s_output->params.scale;
int32_t output_zero_point = s_output->params.zero_point;
int best_class = 0;
float best_score = -1.0f;
for (int i = 0; i < s_output->dims->data[1]; i++) {
int8_t raw = s_output->data.int8[i];
float score = (raw - output_zero_point) * output_scale;
probabilities[i] = score;
if (score > best_score) {
best_score = score;
best_class = i;
}
}
*predicted_class = best_class;
return 0;
}
5.3 集成到 FreeRTOS 任务
// vibration_task.c
#include "FreeRTOS.h"
#include "task.h"
extern int Vibration_Init(void);
extern int Vibration_Inference(const float *features, int feature_count,
float *probabilities, int *predicted_class);
void vVibrationTask(void *pvParameters)
{
// 初始化
if (Vibration_Init() != 0) {
// 错误处理
vTaskDelete(NULL);
}
float features[36];
float probabilities[3];
int predicted_class;
while (1) {
// 1. 采集振动数据
accel_sample_t samples[1000];
CaptureVibration(samples);
// 2. 提取特征
ExtractFeatures(samples, features);
// 3. 归一化(用 PC 端保存的参数)
for (int i = 0; i < 36; i++) {
features[i] = (features[i] - g_norm_mean[i]) / g_norm_std[i];
}
// 4. 推理
if (Vibration_Inference(features, 36, probabilities, &predicted_class) == 0) {
// 5. 处理结果
if (predicted_class == 2) {
// 严重故障,触发告警
TriggerAlarm("严重故障!");
} else if (predicted_class == 1) {
// 轻微异常,记录
LogWarning("轻微异常");
}
}
// 每秒采一次
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
注意: 特征提取函数 ExtractFeatures 在 MCU 上实现,需要用 ARM 的 DSP 库或自己实现 FFT。这是 MCU 端的核心难点。
六、MCU 端特征提取
6.1 时域特征
直接计算,不需要额外库。
// feature_extract.c
#include <math.h>
#include <string.h>
typedef struct {
int16_t x;
int16_t y;
int16_t z;
} accel_sample_t;
static void extract_time_domain(const accel_sample_t *samples, int count,
int axis, float *features)
{
// 提取某一轴的时域特征
int32_t sum = 0;
int16_t max_v = -32768, min_v = 32767;
int32_t sum_sq = 0;
for (int i = 0; i < count; i++) {
int16_t v = (axis == 0) ? samples[i].x :
(axis == 1) ? samples[i].y : samples[i].z;
sum += v;
sum_sq += (int32_t)v * v;
if (v > max_v) max_v = v;
if (v < min_v) min_v = v;
}
float mean = (float)sum / count;
float rms = sqrtf((float)sum_sq / count);
float peak_to_peak = (float)(max_v - min_v);
// 标准差
float var = 0;
for (int i = 0; i < count; i++) {
int16_t v = (axis == 0) ? samples[i].x :
(axis == 1) ? samples[i].y : samples[i].z;
float d = v - mean;
var += d * d;
}
float std = sqrtf(var / count);
// 峭度
float kurt_sum = 0;
for (int i = 0; i < count; i++) {
int16_t v = (axis == 0) ? samples[i].x :
(axis == 1) ? samples[i].y : samples[i].z;
float d = (v - mean) / (std + 1e-8f);
kurt_sum += d * d * d * d;
}
float kurtosis = kurt_sum / count;
features[0] = mean;
features[1] = std;
features[2] = (float)(max_v > -min_v ? max_v : -min_v); // 峰值
features[3] = rms;
features[4] = peak_to_peak;
features[5] = kurtosis;
}
6.2 频域特征
用 CMSIS-DSP 库做 FFT。
#include "arm_math.h"
#define FFT_SIZE 1024
// FFT 缓冲区
static float32_t s_fft_input[FFT_SIZE];
static float32_t s_fft_output[FFT_SIZE];
static arm_rfft_fast_instance_f32 s_fft_instance;
void FFT_Init(void)
{
arm_rfft_fast_init_f32(&s_fft_instance, FFT_SIZE);
}
static void extract_frequency_domain(const accel_sample_t *samples, int count,
int axis, float *features)
{
// 1. 填充 FFT 输入(加窗)
for (int i = 0; i < FFT_SIZE && i < count; i++) {
int16_t v = (axis == 0) ? samples[i].x :
(axis == 1) ? samples[i].y : samples[i].z;
// 汉宁窗
float window = 0.5f * (1.0f - arm_cos_f32(2.0f * PI * i / (FFT_SIZE - 1)));
s_fft_input[i] = v * window;
}
// 补零
for (int i = count; i < FFT_SIZE; i++) {
s_fft_input[i] = 0.0f;
}
// 2. 执行 FFT
arm_rfft_fast_f32(&s_fft_instance, s_fft_input, s_fft_output, 0);
// 3. 计算幅值
float32_t magnitudes[FFT_SIZE / 2];
arm_cmplx_mag_f32(s_fft_output, magnitudes, FFT_SIZE / 2);
// 4. 找前 5 个峰值
// 简化实现:遍历找到最大的 5 个
float peaks[5] = {0};
for (int i = 1; i < FFT_SIZE / 2; i++) {
float m = magnitudes[i];
for (int j = 0; j < 5; j++) {
if (m > peaks[j]) {
for (int k = 4; k > j; k--) {
peaks[k] = peaks[k-1];
}
peaks[j] = m;
break;
}
}
}
// 5. 计算频谱质心
float sum_mag = 0, sum_weighted = 0;
for (int i = 0; i < FFT_SIZE / 2; i++) {
sum_mag += magnitudes[i];
sum_weighted += i * magnitudes[i];
}
float spectral_centroid = sum_weighted / (sum_mag + 1e-8f);
// 6. 输出特征
for (int i = 0; i < 5; i++) features[i] = peaks[i];
features[5] = spectral_centroid;
}
6.3 完整特征提取
void ExtractFeatures(const accel_sample_t *samples, int count, float *features)
{
for (int axis = 0; axis < 3; axis++) {
extract_time_domain(samples, count, axis, features + axis * 12);
extract_frequency_domain(samples, count, axis, features + axis * 12 + 6);
}
}
实测耗时(STM32F407 @168MHz):

加上推理时间(约 2ms),总共约 8.5ms。 对每秒采一次的任务,完全够用。
七、CMSIS-NN 加速
CMSIS-NN 是 ARM 为 Cortex-M 优化的神经网络推理库,比 TFLite Micro 自带的算子快 2-5 倍。
7.1 使用方式
TFLite Micro 支持 CMSIS-NN 后端,只需在 Makefile 里加上:
C_SOURCES += \
tflite-micro/tensorflow/lite/micro/kernels/cmsis_nn/fully_connected.cc \
tflite-micro/tensorflow/lite/micro/kernels/cmsis_nn/softmax.cc \
tflite-micro/tensorflow/lite/micro/kernels/cmsis_nn/...
CFLAGS += -DCMSIS_NN
C_INCLUDES += -Ipath/to/CMSIS-NN/Include
同时在解析器里注册 CMSIS-NN 版本:
#include "tensorflow/lite/micro/kernels/cmsis_nn/fully_connected.h"
resolver.AddFullyConnected(tflite::Register_FULLY_CONNECTED_INT8());
7.2 性能对比
在 STM32F407 @168MHz 上:

整体推理时间从 2ms 降到 0.8ms。
八、内存优化
8.1 Tensor Arena 大小
Tensor Arena 是 TFLite Micro 的工作内存,需要根据模型确定。
如果太小,AllocateTensors() 返回失败。如果太大,浪费 RAM。
确定方法:
-
从 4KB 开始尝试
-
失败就增加一倍
-
成功后再减 1KB,找到临界值
-
最终值加 20% 余量
本项目实测:

8.2 模型裁剪
如果 RAM 不够,可以进一步裁剪模型:
-
减少神经元数量:32 → 16
-
减少层数:3 层 → 2 层
-
减少特征数:36 → 20(只保留最重要的特征)
但要注意精度损失。
8.3 Flash 占用
完整部署的 Flash 占用:

加上其他功能(UART、传感器、FreeRTOS),总计约 120KB Flash。 STM32F407 的 1MB Flash 绰绰有余。
九、实测效果
在 100 组测试数据上评估:

关键结论:
-
在 STM32F407 上跑振动异常检测完全可行
-
端到端延迟从云端的 2-5 秒降到本地的 10 毫秒
-
流量成本降为零
-
数据不出设备,隐私安全
整体方案价值:
-
每台设备节省 4G 流量费约 50 元/年
-
1000 台设备节省 5 万元/年
-
故障预警时间提前 2-5 秒,避免重大事故
十、避坑清单


十一、小结与第二季预告
本文从一个真实的电机振动监测项目出发,完整演示了在 MCU 上部署 TinyML 模型的流程------数据采集、特征工程、模型训练、量化、转换、部署到 STM32。最终实现了 97.8% 的准确率、8.5ms 的推理延迟、55KB 的 Flash 占用。这个方案比云端推理延迟低 200 倍、流量成本为零、数据不出设备。
三个核心要点:
-
MCU 上的 AI 不是"缩小版 GPT",而是特征工程 + 小模型 + 量化
-
特征工程比模型设计更重要------好的特征能让简单模型达到好效果
-
量化是必经之路------float32 模型在 MCU 上又大又慢
这是付费专栏第一季《嵌入式系统调优高手课》的收尾篇。 20 篇文章涵盖了 RTOS 内核、内存池、链接脚本、优先级反转、编译器优化、中断风暴、Cache 一致性、低功耗、日志系统、栈保护、固件瘦身、OOP、状态机、安全启动、防逆向、敏感数据存储、RISC-V 移植、TinyML。从底层汇编到系统架构,从性能优化到安全加固,构成一套完整的嵌入式系统调优方法论。
第二季《嵌入式边缘 AI 实战:从 STM32 到 TinyML 的模型部署与优化》即将推出,将深入以下主题:
-
TFLite Micro 架构剖析(interpreter、arena、op resolver)
-
模型量化进阶(per-channel、混合量化、量化感知训练)
-
CMSIS-NN 与 XNNPACK 深度对比
-
关键词唤醒(KWS)实战------音频流 + MFCC + CNN
-
手势识别------ToF 传感器 + 轻量级分类
-
模型剪枝与知识蒸馏
-
边缘 AI 的数据闭环与 OTA 更新
-
NPU 初探------Ethos-U55 与 CMSIS-NN 性能对比
-
多模型协同------在单 MCU 上同时跑检测 + 分类 + 控制
-
从 TinyML 到 Edge Impulse------低代码平台的利与弊