編集履歴一覧に戻る
airpocketのアイコン画像

airpocket が 2022年04月14日16時59分21秒 に編集

コメント無し

タイトルの変更

-

Spresenseで高速89振動サンプリング

+

Spresenseで高速振動サンプリング

メイン画像の変更

メイン画像が設定されました

本文の変更

# はじめに SPRESENSEを使ってエッジAIについて勉強しようと思ったのですが、テーマとして振動解析を行いたいと思います。 私の知っている範囲で安価かつ最も高速な加速度サンプリングできるセンサーはMPU6886でしたので、M5StackのUnit IMUを使って3kHz程度での加速度サンプリングを行います。 # 動作の様子 @[youtube](https://www.youtube.com/watch?v=GW_zNRTFTOs)

+

画面上に「335」と表示されているのがサンプリング周期の平均値(usec)です。概ね3kHzで測定できています。 測定は随時行っていますが、設定値以上の加速度を検出すると50回さかのぼった時点からの値を320個記録します。 描画を同時に行うとサンプリング周期を上げられないため、320個のデータ記録後にグラフ描画しています。

-

# 環境

+

# 開発環境

プログラミングはArduino IDEで行います。 こちらのサイトなどを参考にArduino IDEにSpresenseのボードライブラリを導入してください。 測定した加速度はディスプレイにグラフ表示します。ディスプレイにはSpresenseでは定番のILI9341(SPI接続版)を使いました。 ディスプレイへの表示は、[LovyanGFXのdevelopブランチ](https://github.com/lovyan03/LovyanGFX/tree/develop)のライブラリを使用しています。TwitterでつぶやいたところLovyanさんが即対応してくださいました。感謝します。 # MPU6886 MPU6886用のライブラリはM5Stack用ライブラリをちょびっと改変して使用しています。 githubに公開している4つのファイルをダウンロードして、inoファイルと同じフォルダに保存してください。 変更内容はI2Cのインスタンス名をWire1からWireに変更した点と、処理の高速化のためZ軸データのみ読み込む関数を追加した点です。 # 高速化の手法 デフォルトの設定では、数百ヘルツでのサイクルでしたデータを取得できませんが、レジストリを書き換えて設定を変更することで高速化を実現しています。 ## 測定したデータをすべて使用する。 デフォルト設定では測定したデータの内、5回に1回だけを使用して残り4回は使用していませんのですべてのデータを使用するようにします。 ## LPFをバイパスする。 LPFを使用すると処理に時間がかかりますので、LPFはバイパスします。 ## Z軸方向のデータのみ読み取る これはライブラリの関数の問題ですが、標準のライブラリでは3軸の加速度すべてを読み取る設計となっています。解析したい振動の種類にもよりますが、1軸のみの振動でも良いのであれば、残りの2軸のデータを読み取る時間がロスになります。 今回は1軸データのみを使用する予定ですのでZ軸データのみを読み取る関数を追加しました。 以上の方法をすべて採用すると一秒間に3000回のデータを読み取れます。 # 使用したプログラム 今回使用したプログラムは次の通りです。 雑な作りですがとりあえず動きます。 ```cpp #include <Wire.h> #include <LGFX_SPRESENSE_sample.hpp> #include "MPU6886.h" #define SAMPLE_SIZE 320 // #define PLAY_BACK 50 #define SAMPLE_CYCLE 333 #define X0 5 // 横軸の描画開始座標 #define MINZ -16000 // 縦軸の最小値 #define MAXZ 16000 // 縦軸の最大値 static unsigned long timeLog[SAMPLE_SIZE]; //サンプリング周期記録用 static unsigned long startTime; static unsigned long finishTime; static unsigned long timeNow; float ax[SAMPLE_SIZE], ay[SAMPLE_SIZE], az[SAMPLE_SIZE]; // 加速度データを読み出す変数 static unsigned long counter = PLAY_BACK; unsigned int delayTime = 100; boolean serialOut = true; boolean graphOut = true; static LGFX lcd; MPU6886 IMU; void serialOutput(int i, float* az, long* timeLog); void output(float* az, long* timeLog); void drawGraphLine(int i, float* az); unsigned int popAppend(); unsigned int test(); void setup() { lcd.init(); lcd.fillScreen(TFT_BLACK); lcd.setCursor(0,0); lcd.setFont(&fonts::Font0); lcd.setTextColor(TFT_WHITE, TFT_BLACK); lcd.setTextSize(2); lcd.setRotation(1); Wire.begin(); Wire.setClock(500000); Serial.begin(115200); Serial.println("start"); IMU.Init(); timeLog[SAMPLE_SIZE - 1] = micros(); } void loop() { int i = 0; popAppend(); if (az[SAMPLE_SIZE - 1] > 1.5 && counter >= SAMPLE_SIZE) { counter = PLAY_BACK; startTime = micros(); } if (counter == SAMPLE_SIZE && micros() > 3000000){ finishTime = micros(); lcd.fillScreen(TFT_BLACK); // 画面をクリア lcd.setCursor(0, 0); lcd.println((finishTime - startTime)/(SAMPLE_SIZE - PLAY_BACK)); output(az, timeLog); } } void drawGraphLine(int i, float* az){ if (i > 1) { int y0 = map((int)(az[i - 1] * 1000), MINZ, MAXZ, lcd.height(), 0); int y1 = map((int)(az[i] * 1000), MINZ, MAXZ, lcd.height(), 0); lcd.drawLine(i - 1 + X0, y0, i + X0, y1, TFT_GREEN); } } void serialOutput(int i, float* az, long* timeLog){ Serial.print("cycle(usec):"); Serial.print(timeLog[i + 1] - timeLog[i]); Serial.print(" , Zaxis accelerration(mG):"); Serial.print(az[i]); Serial.print(" , sample num:"); Serial.println(i); } void output(float* az, long* timeLog){ for (int i = 0; i < SAMPLE_SIZE; i++){ if (serialOut == true) serialOutput(i, az, timeLog); if (graphOut == true) drawGraphLine(i, az); } } unsigned int test(){ // timeNow = micros(); counter++; float azNow = 0; int16_t azRaw; uint8_t buf[2]; while ((micros() - timeLog[SAMPLE_SIZE - 1]) < SAMPLE_CYCLE - 10) delayMicroseconds(3); timeNow = micros(); Wire.beginTransmission(0x68); Wire.write(0x3F); Wire.endTransmission(); uint8_t i = 0; Wire.requestFrom(0x68,2); //! Put read results in the Rx buffer while (Wire.available()) { buf[i++] = Wire.read(); } azRaw = ((int16_t)buf[0]<<8)|buf[1]; azNow = (float)azRaw * IMU.aRes; for (int i = 0; i < SAMPLE_SIZE-1; i++){ az[i] = az[i + 1]; timeLog[i] = timeLog[i + 1]; } timeLog[SAMPLE_SIZE - 1] = timeNow; az[SAMPLE_SIZE -1] = azNow; counter++; return SAMPLE_CYCLE; } unsigned int popAppend(){ float azNow = 0; while ((micros() - timeLog[SAMPLE_SIZE - 1]) < SAMPLE_CYCLE - 20) delayMicroseconds(2); timeNow = micros(); IMU.getAccelZData(&azNow); for (int i = 0; i < SAMPLE_SIZE-1; i++){ az[i] = az[i + 1]; timeLog[i] = timeLog[i + 1]; } timeLog[SAMPLE_SIZE - 1] = timeNow; az[SAMPLE_SIZE -1] = azNow; counter++; return SAMPLE_CYCLE; } ``` このプログラムではデータをディスプレイに表示しておしまいですが、AIの学習用データにしなければならないのでラベリング情報とセットでMicroSDカードに保存する予定です。