最近在做一个云台项目,突发奇想:能不能用手势来控制云台的俯仰?

通常的做法是:

  1. 买个 OpenMV 或者 K210(太贵,且不想加硬件)。
  2. 用蓝牙传给手机,手机跑 AI 再传回来(太慢,且依赖手机)。

作为一个“极简主义”嵌入式工程师,我看着板子上那颗 STM32F407,冒出一个大胆的想法: 能不能直接在单片机上跑神经网络?

这就是 TinyML

🎯 目标:挥挥手,云台走

我想实现的功能很简单:

  • 手持装有 IMU (MPU6050) 的板子。
  • 向上挥 -> 云台抬头。
  • 向下挥 -> 云台低头。
  • 画圈圈 -> 云台复位。

🛠️ 数据采集:像个疯子一样挥手

第一步是搞数据。神经网络是吃数据的怪兽,哪怕是 TinyML 也不例外。

我写了个简单的 Python 脚本,通过串口读取 MPU6050 的 6 轴数据(加速度+角速度)。 然后,我就在实验室里像个跳大神的巫师一样,对着空气疯狂挥手。

  • “上挥” x 100 次
  • “下挥” x 100 次
  • “画圈” x 100 次

采集完,手臂酸得连筷子都拿不稳。

🧠 模型训练:Colab 真香

模型结构不能太复杂,毕竟 F407 只有 192KB RAM。我设计了一个极简的全连接网络 (DNN):

model = tf.keras.Sequential([
  tf.keras.layers.Dense(16, activation='relu', input_shape=(128,)), # 输入是采集的波形点
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(8, activation='relu'),
  tf.keras.layers.Dense(3, activation='softmax') # 3种手势
])

在 Google Colab 上训练只用了几秒钟,准确率就飙到了 98%。 然后是关键的一步:量化 (Quantization)。 为了省空间,我把模型参数从 float32 压缩到了 int8。模型体积瞬间从 40KB 变成了 10KB。

💥 部署:噩梦开始

把模型转成 .tflite 文件,再转成 C 数组,这都很顺利。 真正的坑在 TensorFlow Lite for Microcontrollers (TFLM) 的移植。

坑一:Tensor Arena 的玄学

TFLM 需要你预先分配一块内存叫 Tensor Arena,用来存放模型的输入、输出和中间变量。 分配多了,编译报错 RAM 不够;分配少了,运行报错。

我一开始给了 10KB: Arena size is too small for all tensors. Needed: 12048, but got: 10240

行,我改到 15KB。 编译通过,烧录,运行… HardFault

查了一晚上,发现是堆栈溢出。原来 TFLM 的解释器在初始化时会吃掉大量的栈空间。我不得不去修改 .ld 链接文件,把栈空间强行拉大。

坑二:CMSIS-NN 的诱惑

为了追求速度,我开启了 ARM 官方的 CMSIS-NN 加速库。 理论上,它能利用 Cortex-M4 的 DSP 指令集加速卷积运算。

实际上,它让我调了两天 Bug。 原因是我的输入数据没有对齐。CMSIS-NN 为了性能,要求数据地址必须是 4 字节对齐的。而我随手定义的 float input_data[128] 并没有强制对齐。

加上 __attribute__((aligned(4))) 后,世界终于清静了。

🎉 最终效果

当我不抱希望地再次烧录代码,拿起板子随手一挥。 串口助手跳出一行字: Detected: UP (Score: 0.92) 同时,云台缓缓抬起了头。

那一刻,我觉得那几百次挥手和熬过的夜都值了。

📝 经验总结

  1. 内存管理是核心:在单片机上玩 AI,每一字节 RAM 都要精打细算。
  2. 不要迷信大模型:对于简单的信号处理,几层全连接网络往往比复杂的 CNN/RNN 更有效且更省电。
  3. 工具链很重要:Edge Impulse 这种图形化平台虽然好用,但手撸代码能让你更懂底层原理(也能让你掉更多头发)。

下一步,我打算试试在 ESP32-S3 上跑视觉模型,听说那玩意儿自带 AI 加速指令,应该能玩出更多花样。