生産現場IoTへの挑戦 #01　~M5Stick Cで振動測定してみる編~

#1.はじめに
最近仕事場で生産現場のIoT化の相談を受けるようになりました。私は化学畑出身の身でIT・電子電機スキルは電子工作歴1年程度というハナクソみたいなレベルですが、面白そうなので首を突っ込んでは頭を抱えて試行錯誤しています。
できることなら、この記事を見てさらに入門者が増えたり同じ悩みを集合知で解決しつつレベルアップできればと思い積極的に情報公開してまいります。初心者でもがんばれば実行できる範囲のIoTテクニックとしてご笑覧くださると幸いです。。

今回はFacebookのM5Stack User Group Japanで投稿したM5StickCを使った振動測定についてのまとめです。
実用のためには、収集したデータをサーバー等に送りFFTで周波数解析をする予定ですが、まずは M5Stick C に内蔵のIMUを使用してデータ収集するための設定方法等について記事にしました。

#2.目的
今回紹介する方法で生産設備の油圧モーターの振動測定をすると、次のようなグラフが得られました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">M5Stack Cで、工場の油圧モーターの振動を測定。内蔵MPUの設定を変更してサンプリングレートを1kHzまで向上。 <a href="https://t.co/Dh7f7Smyz1">pic.twitter.com/Dh7f7Smyz1</a></p>&mdash; AIRPOCKET＠rastaman vibration (@AirpocketRobot) <a href="https://twitter.com/AirpocketRobot/status/1353612018465398784?ref_src=twsrc%5Etfw">January 25, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
最終的な到達目標は、振動データを利用して油圧モーターの故障の予知をし、ラインストップを未然に予防する事です。
この記事ではM5StickCを用いて振動測定し、IoTデバイスとしての可能性を調べました。
最終的にはデータベースへ送信して周波数解析を行う必要がありますが、今回は振動データのサンプリングと内蔵lcdへのグラフ表示までを実装しています。当面の目標値として、加速度データのサンプリングレート1kHz以上としました。これにより500Hz以下の振動を（おそらく）解析できます。

#3.M5StickCについて
測定はM5Stick Cのみで完結しますのでハードウェアの工作は必要ありません。
今回は私物として積んでいた[M5StickC](https://www.switch-science.com/catalog/6350/)を使用していますが、最新バージョンは[M5StickC Plus](https://www.switch-science.com/catalog/6470/)という製品になります。振動測定上は特別スペックが上がるということはないはずなのでM5StickCでも問題ないでしょう。コードも共有できると思いますが未確認です。
SWITCH SCIENCEさんのショップなら、旧版の[M5StickC](https://www.switch-science.com/catalog/6350/)は送料税込み1,850円、最新の[M5StickC Plus](https://www.switch-science.com/catalog/6470/)は2,400円でした。入手性の面ではM5StickCの方が古い製品ですので終売となる可能性が高いかもしれません。
M5StackシリーズもMPUを搭載していますので同様の利用は可能かと思いますが、センサーは軽量な方が測定値に影響を与えにくいかと思いますのでM5StickCの方が適していそうです。

スペックに差はあれど[Amazonの振動測定ツールはセンサー5個のスターターキットで715ドル](https://www.itmedia.co.jp/news/articles/2012/02/news096.html)するそうなので、様子見するためのイニシャルコスト面でM5StickCは破格じゃないでしょうか（ランニングコストや学習コストの差は不明）
AWS利用の面では、M5Stack Core2 for AWSの役立て所として良いかもしれません（想像）


#4.ソースコード
今回作成したコードは[SWITCH SCIENCEさんの記事](http://pages.switch-science.com/letsiot/vibration/)を参考にさせていただきました。
この記事を見てM5StickCを購入される方はSWITCH SCIENCEさんからどうぞ。（私は関係者ではありません。念のため。。。）

```cpp
#include <M5StickC.h>

#define SAMPLE_PERIOD 60      // サンプリング間隔(micro秒)
#define SAMPLE_SIZE 150       // 振動解析を本気でするならもっと大きい方が良いですが、lcdにグラフ描画するなら150。大きくするとグラフがはみ出します。
#define X0 5                  // 横軸の描画開始座標
#define MINZ 0                // 縦軸の最小値
#define MAXZ 2000             // 縦軸の最大値

//レジスタ書き込み用関数　ヘッダファイルで触ってもOK。
void I2C_Write_NBytes(uint8_t driver_Addr, uint8_t start_Addr, uint8_t number_Bytes, uint8_t *write_Buffer){
    Wire1.beginTransmission(driver_Addr);
    Wire1.write(start_Addr);
    Wire1.write(*write_Buffer);
    Wire1.endTransmission();
}

void setup() {
    M5.begin();
    M5.Lcd.setRotation(3);
    M5.MPU6886.Init();           // MPU6886を初期設定する
    Wire1.setClock(100000);      // オンボードデバイスのI2C通信速度設定 デフォルト100k、Fast mode 400kまでが規定値。MPU6886のサンプリングレートがボトルネックなので100kで良い。

    unsigned char regdata = 0x00;                             //次行で設定するレジスタ設定値
    I2C_Write_NBytes(MPU6886_ADDRESS,0x19,1,&regdata);        //レジスタ0x19,SAMPLE LATE DIVIDERに設定値を書き込み。デフォルトはregdata = 0x05、0x00で最速
}

void loop() {
    float ax[SAMPLE_SIZE], ay[SAMPLE_SIZE], az[SAMPLE_SIZE];  // 加速度データを読み出す変数
    long t;                                                   //サンプリング周期測定用変数
    long deltaT[SAMPLE_SIZE];                                 //サンプリング周期記録用変数

    M5.Lcd.fillScreen(BLACK);                                 // 画面をクリア

    for (int i = 0; i < SAMPLE_SIZE; i++) {                   //SAMPLE_SIZE回のデータを測定
        t = micros();                                         //サンプリングレート計算用の時間記録
        M5.MPU6886.getAccelData(&ax[i],&ay[i],&az[i]);        // MPU6886から加速度を取得
        az[i] *= 1000;                                        // mGに変換

        if (i > 1) {                                          //グラフ描画部分。処理時間は15μsecぐらいしかかからないのであまり気にしない。
            int y0 = map((int)(az[i - 1]), MINZ, MAXZ, M5.Lcd.height(), 0);
            int y1 = map((int)(az[i]), MINZ, MAXZ, M5.Lcd.height(), 0);
            M5.Lcd.drawLine(i - 1 + X0, y0, i + X0, y1, GREEN);
        }

        delayMicroseconds(SAMPLE_PERIOD);                     //サンプリングレート調整用の遅延処理
        deltaT[i] = micros() - t;                             //サンプリングレート計算
    }

    //Serial.printでのデータ確認をしたいのですが、計測と同時に通信すると時間が食われてサンプリングレートが
    //落ちるので、SAMPLE_SIZE個のデータを採集後に、まとめてシリアル出力した方が無難。
    //不要な場合は以下のforループをコメントアウトで測定サイクルアップ。
/* 
    for (int i = 0; i < SAMPLE_SIZE; i++){
        Serial.print("測定周期(μsec)：");
        Serial.print(deltaT[i]);
        Serial.print(" , Z軸方向加速度(mG)：");
        Serial.print(az[i]);
        Serial.print(" , サンプル番号：");
        Serial.println(i);  
    }
*/
}
```
センサーのサンプリングレートを変更するために内蔵MPU6886のレジスタを触っています。ヘッダファイルの書き換えが苦にならない方は、ヘッダファイルで設定した方がスッキリしますが、ここでは変更部分が見える様にしました。
Serial.print部分は測定ループの外に出してサンプリングレートを安定化させています。
I2Cの通信速度を早くすると時間が稼げるので、Serial.print同期化＋割り込み処理で安定して測定できそうな気もします。

#5.まとめ
当初の目標であった1kHzでのサンプリングは達成出来たため、M5StickCでの予知保全に可能性が見えてきました。
一方でMPU6886の仕様理解が不十分、割り込み処理を使ったサンプリングレートの安定化、サンプリングレートの更なる高速化などの課題も残っています。
また、測定データの取り出し方もまだ考えていないため、次は通信によるデータ取出し方法を検討、選定しようかと思います。

#6.謝辞
コーディングの参考にさせていただいたSWITCH SCIENCEさんと、いろいろなアドバイスをいただいたM5stack User Group Japanの方々に感謝いたします。ありがとうございました。