☰
TinyML 初探:在 MCU 上部署异常检测模型,实现本地振动故障诊断
2026/10/27 10:12:14 网站建设 项目流程

摘要:工业设备的振动监测是边缘 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 个特征

  • 特征有物理意义,可解释性强

本项目提取的特征:

# 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 数据集准备

# 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 层全连接就够

# 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()

典型训练结果:

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。

确定方法:

  1. 从 4KB 开始尝试

  2. 失败就增加一倍

  3. 成功后再减 1KB,找到临界值

  4. 最终值加 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 倍、流量成本为零、数据不出设备。

三个核心要点:

  1. MCU 上的 AI 不是"缩小版 GPT",而是特征工程 + 小模型 + 量化

  2. 特征工程比模型设计更重要——好的特征能让简单模型达到好效果

  3. 量化是必经之路——float32 模型在 MCU 上又大又慢

这是付费专栏第一季《嵌入式系统调优高手课》的收尾篇。20 篇文章涵盖了 RTOS 内核、内存池、链接脚本、优先级反转、编译器优化、中断风暴、Cache 一致性、低功耗、日志系统、栈保护、固件瘦身、OOP、状态机、安全启动、防逆向、敏感数据存储、RISC-V 移植、TinyML。从底层汇编到系统架构,从性能优化到安全加固,构成一套完整的嵌入式系统调优方法论。

第二季《嵌入式边缘 AI 实战:从 STM32 到 TinyML 的模型部署与优化》即将推出,将深入以下主题:

  1. TFLite Micro 架构剖析(interpreter、arena、op resolver)

  2. 模型量化进阶(per-channel、混合量化、量化感知训练)

  3. CMSIS-NN 与 XNNPACK 深度对比

  4. 关键词唤醒(KWS)实战——音频流 + MFCC + CNN

  5. 手势识别——ToF 传感器 + 轻量级分类

  6. 模型剪枝与知识蒸馏

  7. 边缘 AI 的数据闭环与 OTA 更新

  8. NPU 初探——Ethos-U55 与 CMSIS-NN 性能对比

  9. 多模型协同——在单 MCU 上同时跑检测 + 分类 + 控制

  10. 从 TinyML 到 Edge Impulse——低代码平台的利与弊

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询