Seeed Studio XIAO nRF52840 Sense とEdge Impulseで姿勢推定する

## 姿勢推定
機械学習に興味があり、Seeed Studioの[Xiao nRF52840 Sense](https://www.switch-science.com/products/8146)を入手したので、今更ながらチュートリアルがてら機械学習（Edge Impulse)を試してみることにしました。

基本、[こちら](https://wiki.seeedstudio.com/XIAOEI/#data-acquisition--training)　のチュートリアルをなぞっています。


## 1. ハードウェアについて
今回はとりあえず触ってみようということで、チュートリアルにあるケースやバッテリー、ディスプレイは接続していません。
正真正銘、Xiao nRF52840だけで行っています。
電源もPCから繋がったUSB-C経由です。


## 使用するソフトウェアのインストール
ライブラリは
- [Seeed_Arduino_LSM6DS3-master](https://files.seeedstudio.com/wiki/XIAO-BLE-Motion-Recognition/Seeed_Arduino_LSM6DS3-master.zip)
- [U8g2](https://files.seeedstudio.com/wiki/XIAO-BLE-Motion-Recognition/U8g2.zip)
をインストールします

またEdge Inpulseが使えるように
- [Node.js v12以上](https://wiki.seeedstudio.com/XIAOEI/#software:~:text=the%20following%20software%3A-,Node.js%20v12,-or%20higher.)
- [Arduino CLI](https://arduino.github.io/arduino-cli/latest/)
-　The Edge Impulse CLI
も必要です。
Edge Impulse CLIは
`npm install -g edge-impulse-cli`
でインストールします。


## Edge Impulseと接続
Edge ImpulseとXiao　nRF52840を接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/f5e9c832-1dc1-b813-ea49-772a91070abb.png)

USB-CでPCとXiao　nRF52840を接続した後、
[Edge Impulse](https://edgeimpulse.com/)にログインして、
プロジェクトを作成します。

![スクリーンショット 2023-12-26 16.04.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/b5f73056-f581-bbe8-6e2e-e1a1bb41048e.png)

（チュートリアルではこの後にどういうタイプのデータを扱うのか聞かれるのですが、そのステップはありませんでした）


## ライブラリとサンプルスケッチを取得
データのトレーニングをおこなうため、
[Download Seeed_Arduino_LSM6DS3 Library](https://github.com/Seeed-Studio/Seeed_Arduino_LSM6DS3)のライブラリをZIP形式でダウンロードし、
その後Arduino IEDで
スケッチ＞ライブラリをインクルード＞.zip形式のライブラリをインストール
でダウンロードしたZIPファイルをインストールします。

その後、下記のコードを入力します。

```
// XIAO BLE Sense LSM6DS3 Accelerometer Raw Data 

#include "LSM6DS3.h"
#include "Wire.h"

//Create a instance of class LSM6DS3
LSM6DS3 myIMU(I2C_MODE, 0x6A);  //I2C device address 0x6A

#define CONVERT_G_TO_MS2 9.80665f
#define FREQUENCY_HZ 50
#define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1))

static unsigned long last_interval_ms = 0;


void setup() {
  Serial.begin(115200);
  while (!Serial)
    ;

  if (myIMU.begin() != 0) {
    Serial.println("Device error");
  } else {
    Serial.println("Device OK!");
  }
}



void loop() {
  if (millis() > last_interval_ms + INTERVAL_MS) {
    last_interval_ms = millis();
    Serial.print(myIMU.readFloatAccelX() * CONVERT_G_TO_MS2, 4);
    Serial.print('\t');
    Serial.print(myIMU.readFloatAccelY() * CONVERT_G_TO_MS2, 4);
    Serial.print('\t');
    Serial.println(myIMU.readFloatAccelZ() * CONVERT_G_TO_MS2, 4);
  }
}
```
このスケッチを実行することで、シリアルモニターで加速度のロウデータを取得できます。

edge impulseに接続するため、ターミナルで下記コードを実行します。
```
edge-impulse-data-forwarder
```
するとターミナル上でログインを促されるため、
Edge Impulseのアカウントでログインを行います。

![スクリーンショット 2023-12-19 17.08.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/6f803a77-0cfa-906f-18ac-27e2e7856784.png)

ちなみにこの際、Arduino側は一度切っておきましょう。
画像でFaied to connectしているのはそのせいです。

またnrf52のライブラリを使おうとしたところ、権限の問題でCompilation errorが出ており、
そちらは `adafruit-nrfutil` の権限を変更すると動くようになりました。

その後、３軸センサーのそれぞれの値の呼び名、デバイス名の入力を求められるのでそちらも適宜入力します。
　![スクリーンショット 2023-12-19 17.09.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/cc6b4531-d351-75c9-da36-187f28497e20.png)

これでEdge ImpulseとXiaoを接続できました。

## 挙動のサンプリング開始
Edge Impulseの`Data acquisition` ページへ行きデバイスとの接続が正常に出来てるか確認します。
ページ右側にデバイス名が出ていれば成功です。
![スクリーンショット 2023-12-19 17.11.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/1cfc9230-7aa9-0c0e-6d1b-b7986795ad1e.png)

ページ右側で取得するセンサーを選択した後、
これからサンプリングする挙動の名前をつけます。
上下運動から設定するため `up and down`と入力します。

Sample lengthはチュートリアルと揃えて20000msとしました。
`Start sapling` ボタンをクリック後、サンプリングが開始されるので、
２０秒間、Xiaoを上下に振り続けます。

２０秒経過すると、取得したデータのグラフが右下に表示されます。
![スクリーンショット 2023-12-19 17.12.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/af83a9ac-85ad-d502-cfc8-7f35dbf4042d.png)

このデータを適切な単位で分割するため、グラフ右上の３点リーダから `Split sample` を選択します。
![スクリーンショット 2023-12-19 17.12.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/2aca61d6-6321-6521-8f7c-59766a23df22.png)

サンプルの分割は、一回ずつの挙動を適切な範囲で選択して上げる必要があります。
（僕はここで適当にやりすぎてサンプル入力を何回もするハメになりました）

![スクリーンショット 2023-12-19 17.12.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/a8694b52-9e9d-04a6-f338-4be6b3af4198.png)

最終的には２０秒間に１秒１回ずつ上下運動を取得し、１秒ずつ分割することで精度もよくなりました。
その後、同じように
- 左右運動（`left and right`）
- 時計回り(`clockwise`)
- 反時計回り(`unticlockwise`)
も入力し、分割していきます。


## 機械学習モデルを構築
ダッシュボードに戻り、画面下部の` Perform train / test split`をクリックします。
確認がでるのでConfirmします。

![スクリーンショット 2023-12-23 1.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/7047742e-c6f2-6204-e48d-4218a19bee4c.png)

サイドバーから`Create impulse`をクリックし、
`Add a processing block` -> `Spectral Analysis` -> `Add a learning block` -> `Classification` -> `Save Impulse`

の順番で選択していきます。


![スクリーンショット 2023-12-23 1.34.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/66aec468-75c1-6d19-c0eb-95266fead33f.png)

サイドバーの`Spectral features` から `Save parameters` をクリック
ページ上部の `Generate features`からページ下部の`Generate features`ボタンをクリックします。
![スクリーンショット 2023-12-23 1.38.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/0dc394b8-b8a8-3796-ac94-8219227ecb89.png)

するとこんな感じで、各データの分布図が表示されると思います。

![スクリーンショット 2023-12-23 1.40.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/969e3ae0-cfdb-c16a-1b53-a752af5b03a6.png)

サイドバーの `Classifier`から `Start training` をクリックします。
処理が終わると、画面右下に`Accuracy`が表示されるはずです。
この値が65%より低いと精度がかなり悪くなるので、サンプル回数を増やしてみてください。


![スクリーンショット 2023-12-23 1.46.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/93778e25-d619-e57d-40d8-6284a99238e8.png)



## Arduino用のSketchをデプロイ

サイドバーの `Model testing`から `Classify all`をクリック
![スクリーンショット 2023-12-23 2.05.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/a1215e96-1941-c305-1d2b-1dceaa983bb2.png)

ここでもAccuracyが低い場合はサンプル回数を増やしてみましょう（画像はAccuracyが低い例）

問題なければ
サイドバーの `Deployment`から `SELECTED DEPLOYMENT`で`Arduino Library`を選択し。
`Build`ボタンをクリックします。
ZIPファイルがDLされるのでそのファイルを
`スケッチ`＞`ライブラリをインクルード`＞`.zip形式のライブラリをインストール`
でインストールします。
[こちら](https://files.seeedstudio.com/wiki/XIAO-BLE-Motion-Recognition/XIAOEI.ino)のファイルをダウンロードし、Arduino IDEで開いたあと、
冒頭にある
`#include <XIAO-BLE-gestures-inferencing.h>`のライブラリ名部分を先程インストールしたライブラリ名に置き換えます。


## 成功
プログラムを実行後、XIAO nRF52840 Senseを動かしながらシリアルモニターを確認しましょう。
画像のように姿勢が推定されていれば成功です！

（left and rgith、up and downの確度が高まっている状態）

![スクリーンショット 2023-12-24 15.04.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242072/a6246c73-a714-3c38-887a-3b6161ecb12c.png)



## 感想
機械学習、よくわからなすぎて手を付けられていなかったのですが、Edge Inpulseは簡単に触ることができてよかったです！
他のデバイスでも試してみようと思います。