ChocolateBlack
做一个人体动作识别的微信小程序

做一个人体动作识别的微信小程序

人体动作识别(Human Activity recognition)的应用领域有很多,比如健身、医疗等。最近学校期末作业要求做一个人体动作识别的小程序,正好把相关项目的经历与经验分享给大家~

HAR小程序原理

微信小程序识别人体动作,可以通过手机内置的六轴传感器采集加速度以及角速度,之后利用机器学习模型进行判别。为了实现这一功能,我们需要以下几步:

  • 确定要识别的动作;
  • 设计开发一个能够采集人体动作数据的微信小程序;
  • 数据处理;
  • 构建模型;
  • 把训练好的模型部署到微信小程序上。

确定要识别的动作&采集数据

首先我们第一步要做的就是先确定要识别的动作——这个根据实际情况选择,一般来说动作差别越大越好区分识别。这里我选择的是站立、行走、仰卧起走、深蹲、扩胸这五个动作。

那么如何利用微信小程序采集到人体动作的数据(即六轴的数据)呢?其实很简单,我们可以通过微信提供的加速度计和陀螺仪API实现。wx.startAccelerometer和wx.startGyroscope这两个函数可以开启对手机六轴数据的监听,wx.onAccelerometerChange和wx.onGyroscopeChange这两个函数可以记录下六轴的数据。具体用法可以参考微信文档:https://developers.weixin.qq.com/miniprogram/dev/api/device/gyroscope/wx.onGyroscopeChange.html

需要强调的一点是,加速度计和陀螺仪的start函数都有一个interval的属性,代表着回调函数的执行频率。这里我们需要把interval设置为game,也就是20ms/次,以保证后续识别的准确率。

数据处理

采集完人体动作数据后,我们首先需要对原始数据进行滤波处理。这里我用到的是中值滤波和巴特沃斯低通滤波,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from scipy.signal import butter, lfilter, medfilt

def butter_lowpass(cutoff, fs, order=5): #cutoff为截止频率,fs为采样频率,order为阶数
nyq = 0.5 * fs
normal_cutoff = cutoff / nyq
b, a = butter(order, normal_cutoff, btype='low', analog=False)
return b, a


def butter_lowpass_filter(data, cutoff, fs, order=5): #data为原始数据
b, a = butter_lowpass(cutoff, fs, order=order)
y = lfilter(b, a, data)
return y


def median_filter(data):
output = np.copy(data)
for i in range(6):
output[i] = medfilt(data[i], 3)
return output

这里我选用的截止频率为20Hz,阶数为5,可以根据实际情况进行调整。

之后,我们还需要对滤波后的数据进行切片。切片长度为2.56s(128条记录),同时要有50%的overlap。什么意思呢?其实就是在以2.56s的长度切割完一个数据后,起点向后平移1.28s(一半长度),然后从起点开始切割2.56s的数据,而非整段整段的切。

为什么要做overlap呢?这是因为人的动作往往是连续的,如果整段整段切,恐怕会丢失掉很多有用的信息,不利于后续模型的训练。overlap可以让我们充分利用我们采集到的数据,减少了我们采集数据的成本。至于overlap的比例,则可根据实际情况进行调整,以达到最好的效果。

构建模型

在对数据进行滤波和切片处理后,我们就可以构建模型进行训练了。通过六轴数据识别人体动作,其实本质上是一个时间序列多分类问题。因此,我们可以选用LSTM进行训练,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers

model = keras.Sequential()
model.add(layers.LSTM(18,activation='tanh', recurrent_activation='sigmoid',dropout=0.2,return_sequences=True))
model.add(layers.LSTM(18,activation='tanh', recurrent_activation='sigmoid',dropout=0.2,return_sequences=True))
model.add(layers.LSTM(18,activation='tanh', recurrent_activation='sigmoid',dropout=0.2))
model.add(layers.Dense(5, activation='softmax')) #最后一层的节点数量取决于要训练的动作类别数量


model.compile(optimizer=keras.optimizers.RMSprop(lr=0.001),
loss=keras.losses.CategoricalCrossentropy(),
metrics=['acc'])


history = model.fit(train_x, train_y, batch_size=32,epochs=100)
model.summary()

result = model.evaluate(test_x, test_y)
predict = model.predict(test_x)
print(model.metrics_names)
print(result)
print("saving the model...")
model.save('HAR_model_LSTM.h5')

把模型部署到微信小程序上

微信支持并拥有tensorflow.js插件,然而个人并不是很推荐——因为微信小程序的大小是有限制的,把包安装完很容易超大小限制。因此最好的解决办法是把tensorflow.js安装在后端。这里我选择用云开发,并用npm在云函数所在的文件夹里把tensorflow.js安装好,之后上传到云端。

1
npm install @tensorflow/tfjs@1.7.4

如果想要引用的这个包的话,需要在js文件夹里加一行:

1
const tf = require('@tensorflow/tfjs')

然后就可以使用tf包里的函数了!具体如何使用还请查看官方文档:https://tensorflow.google.cn/js/guide/nodejs?hl=zh-cn

那么怎么去识别呢?首先,我们还是调用加速度计和角速度计的API,获取手机的六轴数据;然后,要把收集到的数据进行滤波处理(并且要和之前数据清洗时使用的滤波方法一样)——这会影响你识别的准确率;之后,调用你写好的云函数,把数据传到云函数上;最后,在云函数上输出预测值,返回到小程序上。

以上就是我的HAR小程序的制作经验了。我把自己的代码放在了GitHub上,欢迎查看~https://github.com/Chocolate-Black/HAR_tutor

本文作者:ChocolateBlack
本文链接:http://chocolateblack.club/2020/07/23/做一个人体动作识别的微信小程序/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可