本帖最后由 甲基红橙黄绿蓝 于 2017-7-13 20:12 编辑
概述: 采集和处理加速度计的数据,使用 Curie 的硬件神经元进行训练和识别
特点:
1、自动捕捉手势:这个程序能分辨静止和运动状态,不需要用户使用按钮等方式告诉程序什么时候开始手势,什么时候结束手势。
2、消除重力影响:如果你训练了一个手势:画圈,那么CurieNano的倾角将不会对识别造成影响。
3、识别率因人而异,我测试时达到90%
4、允许你存储训练结果到板载SPI flash,这样你就能在下次上电时直接用上次训练的结果进行识别。
5、识别结果通过蓝牙和UART两种方式传输,你可以用 NRF connect ,或者我编写的UWP程序查看蓝牙发送的识别结果。
代码:
话不多说,先贴代码:github.com/WangXuan95/CurieGesture
思路概述:
一、预处理算法
因为 Curie 自带神经网络,我要写的只有预处理算法。因为直接获得的加速度计数据是海量的,需要判断哪些是手势片段,哪些是静止片段。另外,还有消除重力偏差,归一化。因此,预处理算法我写了大概100行。
预处理算法包括:手势片段截取、消除重力偏差、时间轴规范化、幅度轴规范化、三轴数据拼接。
1、手势片段截取:
为了实现自动手势片段截取,首先需要判断Curie是处于近似静止的状态,还是运动状态。我们规定了两个参数:WSIZE、THRESHODE。WSIZE次采样内三轴加速度的极差的和称为A。在数据结构层面,算法采用了一个WSIZE大小的循环数组,每采一次样就把采样数据放入循环数组,再计算循环数组内三轴数据的极差值只和作为A值。若A>THRESHODE,则进入运动状态,若A<=THRESHODE,则进入静止状态。手势片段必须是夹在两个静止状态间的运动状态。当一个手势片段被提取出来后,判断他的长度,若太长或太短都被丢弃,只有时间在30~200个采样周期之间的手势片段,才被认为是一个合法的手势。为了确定参数WSIZE和THRESHODE的值,我们先实现手势截取算法,再不断调整WSIZE和THRESHODE,当算法能正确截取用户的手势时,确定了WSIZE=30,THRESHODE=2000
2、消除重力偏差
当芯片竖直向上时,获得的加速度数据是(ax,ay,az) = (0,0,1g)实际上,我们做手势是无法保证芯片完全向上,比如可能有一个30度的倾角。无论如何,重力会在3轴加速度数据上产生一个常数的偏差。为了消除重力产生的偏差,需要记录截取手势片段前平稳状态的加速度平均值(xAvg1, yAvg1, zAvg1)、以及截取手势片段后的平稳状态的加速度平均值(xAvg2, yAvg2, zAvg2)然后计算前后平均值:
xAvg = (xAvg1+xAvg2) / 2
yAvg = (yAvg1+yAvg2) / 2
zAvg = (zAvg1+zAvg2) / 2
代表手势前后的静止状态的重力产生的加速度偏差。
最后,将动态手势片段的三轴加速度片段一一减去以上的重力加速度偏差。
3、时间轴归一化
因为每个手势时长不同,获得的数据的维数也不同。因此需要时间轴规范化。我们获得的手势片段的长度在30~200维之间,我们使用相邻数据求平均值的方式,把它压缩到30维。
4、幅度轴归一化
由于每个手势的幅度和猛烈程度不同,因此需要幅度规范化。方法是:计算三轴数据的极差中最大的一个,该极差为delta,然后使用以下公式进行幅值规范化:
规范化数据 = 原始数据 × 128 / delta
这样,所有的手势片段的最大最小值差距都为128
幅值规范化有效地减小了手势的幅度带来的影响,比如,同样的画圈手势,缓慢地画一个圈和猛烈地画一个圈,得到的原始数据不同,但经过幅值规范化后,这个差距会减小。
5、三轴数据拼接
经过以上步骤,我们得到了x,y,z三个方向的三个向量,每个各30维。因为神经网络只接受一维向量,我们将它拼接为一个90维的向量。
二、训练和识别
将上一步获得的90维数据片段向量交付硬件神经网络进行训练和识别。每个手势训练30次左右,可以获得较好的识别结果。为了用户方便,建议训练的手势都是能回到原地的手势。
三、其他的一些工作
1、 编写了一个专门用于训练的程序,使用串口传输提示信息,用户在提示下做手势,完成一组手势的训练。
2、支持使用板载Flash(非易失性存储器)保存神经网络权重数据,训练的数据掉电不丢失。
3、编写了一个专门用于识别的程序,它从Flash中读取曾经训练好的数据后,对手势进行识别,并用蓝牙把识别结果传出去。
4、编写了一个Windows10上的UWP程序,调用蓝牙API,获取识别结果,显示在屏幕上。
|