最近在做一个云台项目,突发奇想:能不能用手势来控制云台的俯仰?
通常的做法是:
- 买个 OpenMV 或者 K210(太贵,且不想加硬件)。
- 用蓝牙传给手机,手机跑 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)
同时,云台缓缓抬起了头。
那一刻,我觉得那几百次挥手和熬过的夜都值了。
📝 经验总结
- 内存管理是核心:在单片机上玩 AI,每一字节 RAM 都要精打细算。
- 不要迷信大模型:对于简单的信号处理,几层全连接网络往往比复杂的 CNN/RNN 更有效且更省电。
- 工具链很重要:Edge Impulse 这种图形化平台虽然好用,但手撸代码能让你更懂底层原理(也能让你掉更多头发)。
下一步,我打算试试在 ESP32-S3 上跑视觉模型,听说那玩意儿自带 AI 加速指令,应该能玩出更多花样。