人机交互方式正不断演进。当键盘、鼠标、数位板已成为日常,你是否想象过,只需在空中挥写一个数字“1”,电脑便能精准输入?
本项目基于 MAX78000微控制器 内置的CNN硬件加速器,结合IMU传感器与ESP32蓝牙模块,打造了一支功能强大的“AI智能遥控笔”。它不仅具备翻页、视频控制等传统遥控笔功能,更能识别空中手势,将“书空”动作直接转化为文本输入。下文将详细解析其设计思路、模型训练、硬件集成与部署的全过程。

项目核心:空中手势输入
当前,遥控笔因其便携与远程操控特性,在演示、教学等场景中广泛应用。我们旨在开发一款功能升级的智能遥控笔,其核心突破在于将空中书写(“书空”)转化为字符输入。例如,在空中书写数字“1”,即可在电脑端输入数字1。
市面常见翻页笔支持的手势有限。为此,我们充分利用MAX78000的CNN加速模块,通过人工智能技术训练笔识别更丰富的手势模式,以满足用户多样化需求。
系统设计思路
遥控笔的交互模块包括实体按钮、开关、惯性测量单元(IMU)及激光头。MAX78000负责读取这些输入(开关电平、按钮状态及IMU数据),经过逻辑处理与神经网络对IMU信号的分类,生成控制指令。指令通过串口发送至ESP32,再由ESP32通过蓝牙HID协议转发给电脑,从而实现手势控制与输入。此外,我们使用 KiCad 设计了专用拓展板,优化握持手感。

最终效果展示
最终成品如下图所示:

手势输入数字的实例如下:


除基础数字输入外,还实现了手势截图、切换应用等功能:


当然,作为一款翻页笔,聚光灯、数字/实体激光、音量调节、PPT翻页、视频快进/快退以及飞鼠功能一应俱全。

实现过程详解
1. 数据采集与数据集制作
1.1 IMU数据读取与采集
项目选用 ICM20602 6轴运动传感器(3轴陀螺仪+3轴加速度计)。我们通过SPI协议与传感器通信。读取过程分为两步:先发送要读取的寄存器地址,再接收数据。
mxc_spi_req_t request;
// 写入要读取的地址
request.txData = tx_data;
request.txLen = 1;
request.rxData = NULL;
request.rxLen = 0;
request.ssDeassert = 0;
MXC_SPI_MasterTransaction(&request);
// 读取返回的数据
request.txData = NULL;
request.txLen = 0;
request.rxData = rx_data;
request.rxLen = 128;
request.ssDeassert = 1;
MXC_SPI_MasterTransaction(&request);
每次读取包含14字节数据(加速度、温度、陀螺仪)。手持遥控笔做出特定动作,单片机通过串口将实时数据发送至电脑保存。采集周期为1秒。
1.2 数据清洗与图像化
为便于网络移植与可视化,我们将IMU时序数据转化为3通道图像。这便于直接利用图像分类网络框架及PyTorch丰富的数据处理工具。
首先,使用正则表达式提取有效传感器数据:
pattern = r'([0-9A-Z]{2} ){14}00 00 00 (?!00 00 )'
随后进行数据标准化。由于原始角速度为16位,而CNN输入要求8位,需进行缩放。为确保单片机与PC端处理一致,并兼顾单片机算力,我们设计了移位替代除法的标准化流程(PC端使用Python,单片机端使用C语言实现逻辑等价操作)。
Python处理示例:
@staticmethod
def process_matrix(matrix):
if not isinstance(matrix, np.ndarray):
matrix = np.array(matrix)
matrix = np.where(matrix > 32767, matrix-65536, matrix)
matrix = matrix/32
matrix = np.where(matrix > 127, 127, matrix)
matrix = np.where(matrix < -128, -128, matrix)
matrix = matrix+128
return matrix
单片机C语言实现:
uint8_t nomilization(uint8_t h, uint8_t l, int16_t mean, int16_t bits) {
int16_t _16bit = (*(int16_t*)&h) << 8 | l;
_16bit = ((_16bit - mean) >> bits) + 128;
if (_16bit >= 255) return 255;
if (_16bit <= 0) return 0;
return (uint8_t)_16bit;
}
// 应用
uint8_t gx = nomilization(rx_data[8], rx_data[9], 0, 5) ^ 0x80;
uint8_t gy = nomilization(rx_data[10], rx_data[11], 0, 5) ^ 0x80;
uint8_t gz = nomilization(rx_data[12], rx_data[13], 0, 5) ^ 0x80;
最后,将标准化后的gx, gy, gz数据作为三个通道,按行填充至6x6的图像中,生成最终的数据集样本。

2. 模型设计与网络训练
输入数据尺寸较小(3x6x6),且数据集规模有限,因此我们设计了一个轻量级卷积神经网络,结构如下:

模型定义文件示例 (基于Maxim AI框架):
from torch import nn
import ai8x
class AI85motion(nn.Module):
def __init__(
self,
num_classes=3,
num_channels=3,
dimensions=(12, 12),
bias=False,
**kwargs
):
super().__init__()
self.conv1 = ai8x.FusedConv2dBNReLU(num_channels, 16, 3, stride=1, padding=1, bias=bias, **kwargs)
self.conv2 = ai8x.FusedMaxPoolConv2dBNReLU(16, 9, 3, pool_size=2, pool_stride=2, stride=1, padding=0, bias=bias, **kwargs)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1)
return x
def ai85motion(pretrained=False, **kwargs):
assert not pretrained
return AI85motion(**kwargs)
models = [
{
'name': 'ai85motion',
'min_input': 1,
'dim': 2,
},
]
训练使用Maxim官方提供的PyTorch训练框架。配置训练策略与量化感知训练(QAT)策略后,启动训练:
python train.py --epochs 40 --optimizer Adam --lr 0.001 --wd 0 \
--compress policies/schedule-motion.yaml --model ai85motion \
--dataset motion --device MAX78000 --batch-size 128 --print-freq 10 \
--validation-split 0 --qat-policy policies/qat_motion.yaml \
--use-bias "$@" --data ./data/motion --enable-tensorboard
最终训练结果优异:
- 训练集: Top1准确率 91.341%, Top5准确率 97.067%
- 测试集: Top1准确率 91.201%, Top5准确率 96.648%
训练集与测试集表现接近,表明模型无明显过拟合或欠拟合问题。
3. 模型量化与部署
MAX78000硬件要求权重为8位,需对训练好的FP32模型进行量化。使用Maxim的synthesis工具,并编写motion.yaml网络描述文件完成量化与C代码生成。
量化命令:
python quantize.py trained/best_without_bn.pth.tar trained/motion-q.pth.tar \
--device MAX78000 -v -c networks/motion.yaml "$@"
量化后模型在测试集上准确率依然保持在91.34%,混淆矩阵显示分类效果良好。
生成部署至MAX78000的C语言工程:
python ai8xize.py --test-dir "sdk/Examples/MAX78000/CNN" --prefix motion \
--checkpoint-file trained/motion-q.pth.tar --config-file networks/motion.yaml \
--overwrite --device MAX78000 --timer 0 --display-checkpoint --verbose \
--compact-data --mexpress --sample-input 'motion.npy' --boost 2.5 "$@"
生成的工程可直接编译并烧录至MAX78000进行推理测试,实现毫秒级识别。
4. 系统逻辑与功能实现
系统上电后,电脑连接ESP32蓝牙(可自动重连)。为使用聚光灯和数字激光功能,需在电脑端运行一个用C#编写的便携式上位机程序。该程序后台运行,通过接收遥控笔模拟的特定快捷键来触发功能。
C#上位机核心代码片段(实现鼠标钩子与光点绘制):
public MouseForm()
{
// ... 窗体初始化代码
const int WH_MOUSE_LL = 14;
using (Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
{
Hook = SetWindowsHookEx(WH_MOUSE_LL, Proc, GetModuleHandle(module.ModuleName), 0);
}
// ... 创建DispatcherQueue
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
const int WM_MOUSEMOVE = 0x0200;
if (nCode >= 0 && (int)wParam == WM_MOUSEMOVE)
{
Point position = PointToClient(Cursor.Position);
MouseVisual.Offset = new Vector3(position.X, position.Y, 0);
}
return CallNextHookEx(Hook, nCode, wParam, lParam);
}
MAX78000检测到按钮或开关动作后,编码为控制指令,通过UART发送给ESP32。ESP32内实现了蓝牙HID协议栈,模拟键盘或鼠标向电脑发送对应指令,从而完成所有可通过键鼠触发的操作。
此外,还实现了飞鼠模式,通过摆动遥控笔控制光标移动。飞鼠模式与手势识别模式通过快速下摆手势切换。当关闭飞鼠模式且IMU检测到较大动作时,系统开始采集0.9秒(36个点)的IMU数据,送入CNN进行手势分类。
MAX78000主控流程图:

遇到的问题与优化方向
目前存在偶尔误触发(将随意动作识别为手势)或漏识别的问题。未来优化方向包括:
- 改进触发条件:考虑增加触摸感应按键,仅在触碰时激活手势识别,避免无意触发。
- 优化数据集与模型:增加“空闲状态”负样本的数据量,提升模型对无效动作的区分能力。
由于开发周期所限,当前仅训练了9种手势(包括数字、字母及空闲状态)。未来若能收集更多字符(字母、汉字)数据,将能充分发挥MAX78000的潜能,实现更丰富的空中输入功能。
项目总结
本项目完整实践了从数据采集、模型训练、量化到边缘端部署的AI应用全流程。Maxim提供的完善开发工具链极大便利了开发。MAX78000毫秒级的神经网络推理速度令人印象深刻,为神经网络在实时控制系统的应用展示了广阔前景。基于此类AI微控制器,未来可探索更多嵌入式智能应用。