M5StickCで歩数計を作る

# はじめに

[LINE Thingsで歩数計Botを作ろう](https://qiita.com/ufoo68/items/5009a2d87843430ed545)の補足記事です。前回はM5StackのPedometerライブラリを使って歩数計を作成しましたが、ここでは[M5StickC](https://www.switch-science.com/catalog/5517/)で歩数計を作成します。

M5StackCには歩数計のライブラリがなかったので自分で実装する必要がありました。ちなみに書いたコードは[こちら](https://github.com/ufoo68/linethings-pedometer-hands-on/blob/master/m5stick-c-linethings/m5stick-c-linethings.ino)で公開しています。
また、M5StickCに搭載されているモーションセンサーは旧デバイスでは**SH200Q**で新デバイスでは**MPU6886**になります。ライブラリの記述にも違いが少しあるので注意が必要です。

# 参考

- [3軸デジタル加速度センサーを使用したフル機能の歩数計の設計](https://www.analog.com/jp/analog-dialogue/articles/pedometer-design-3-axis-digital-acceler.html)
- [センサの入力などに使うディジタルフィルタ](https://ehbtj.com/electronics/sensor-digital-filter/)
- [L1距離（マンハッタン距離）の意味と性質](https://mathtrain.jp/manhattan)

# とりあえず歩数計を作ってみる

まずは下の図のような方針を考えてみました。
![歩数計.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/6c8d8895-f997-5633-e35c-c3bde19c26b0.jpeg)
各ブロックの具体的な実装に上記のサイトを参考にしました。センサーデータは単純にセンサー値を取ってきただけなので省略して、そこから先の

1. ノルム（ベクトルの長さ）の算出
2. フィルタリングによるセンサーノイズの除去
3. 信号波形からの歩数算出

について書いていきます。

# ノルムの算出

デバイスからはx,y,z軸の加速度がとれるわけですが、歩いているときに変化するのはどこか一方の軸になることが想像できますが、デバイスを固定しない限りどこの軸が変化するのかわからないので今回は**3軸のベクトルから長さを算出してそれをセンサー値としました。**
ノルムを算出するにあたり通常なら以下の**ユークリッド距離**を使うのが通常ですが、

```math
L=\sqrt{x^2+y^2+z^2} \\
```

別に正確なノルムを計算する必要がないことと、マイコンで平方根の計算をするのもいかがなものかと思ったので今回は下の式のような**マンハッタン距離**を使いました。

```math
L=|x|+|y|+|z|
```

これだと単純に絶対値を足すだけでいいので計算が軽くなるのと、センサー値を大きくとることができるので良いかなと思いました。

# フィルタリングによるノイズ除去

これに関しては定石どおりに**デジタルローパスフィルタ**をかけることにしました。

```math
y_i = a \times y_{i-1} + (1-a) \times x_i
```

今回は`a`の値を`0.8`としました。

# 波形を見てみる

歩数算出のために一旦ここでセンサー値を見てみることにしました。計測のためにポケットにM5StickCを入れてそこら辺をグルグルと歩きながら、デバイスからセンサー値をwifiでローカルサーバ送って、ローカルサーバからそれをcsvファイルに変換してグラフを求めました。

![data0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/14eb4afd-f048-e764-8fed-15c8ccc73c2f.png)

ローパスフィルタをかけたのにあまりノイズが除去できてなかったのが悲しかったですが、とりあえず**閾値からの立ち上がりor立ち下がり**を求めたら歩数が算出できそうな気がしました。しかしこの閾値は常に一定にはならなさそうな波形でもあったので算出方法に工夫がいりそうです。
ここで先述した参考サイトを見ながら**動的に閾値を求める方法**を実装してみました。

# 動的に閾値を求める

ざっくりしたイメージが下図です。

![閾値の求め方.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/c5df3d97-c88a-f22c-82b8-916e44bb2dc7.jpeg)

n回分センサー値を求めてその**最大値と最小値を足したものを2分して平均値を求める**というシンプルなやり方です。とりあえず今回はnの値を10,20,30,40,50に変えながら閾値の算出のようすを計測してみてグラフを出してみました。

## n=10

![data1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/18845b9e-e745-2e37-4416-681ba1a8fb41.png)

青い線がセンサー値で、赤い線が動的に求めた閾値です。ちゃんと追従はしてくれますが、よけいなノイズとかも拾ってしまっている気もします。

## n=20

![data2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/84cac3c2-54d7-f350-03cd-ae5b3c5c75b1.png)

さきほどより落ち着きは感じますが、いまいち。

## n=30

![data3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/9d3e82c7-bb60-06ff-0ddb-9dda17781d21.png)

あまり違いがない。

## n=40

![data4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/4fb9da64-24ed-9a60-d4cf-77a67b163934.png)

落ち着きがでてきた。もう少し！！

## n=50

![data5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/888b39fd-c60c-48f6-474d-8f2f16d23116.png)

安定して閾値を算出しているように感じる。欠点としては最初に多くのサンプルを取る必要があるので立ち上がりが遅いことですね。参考サイトでは50がいいと言っていたのでこの数値あたりが妥当だと思います。これ以上上げると立ち上がりが遅くなってしまうので。。。

# さいごに
この後は前回の記事と同じなので省略します。とりあえず今回の考察は以下です。

- 加速度の取得の間隔が短すぎるとMPU6886からちゃんと値が取れなかったので今回は**100ms**の間隔を置いていたが、歩数計では人間の歩行のペース感覚に合わせれば良いのでもう少し遅延を置いても良かったのかもしれない
- 歩行の波形の周波数はある程度の帯域みたいなものが存在する気もするので**バンドパスフィルタ**をかけることも効果がありそう
- 今回は併設されてる**ジャイロセンサー**は不使用だったが、それを用いてセンサー値の補正などを行えるかもしれない
- センサー値を計測するときに今回は前後の小さな値の変化のときは前の値をそのまま渡すことで、デバイスの僅かな動きでも歩数が加算されることを避けたが、ここでは**50**という値のみで固定していたので、適切な値を設定すれば歩行以外の動作を切り捨てられるかもしれない

作ったはいいものの、精度があまりよろしくないので今後の改良の余地がありそうです。ちなみに[スライド版](https://docs.google.com/presentation/d/181HMJolk0J1o1ohZyY2R0hrh9AHYPY-1dgzbL7LDXJU/edit?usp=sharing)も公開しています。どちらかというと記事の方が結構後に書かれたものです。
こういった数パターンのグラフを貼る作業は学生の時以来だったので懐かしさを感じました。