M5StickC PLUSの加速度センサーで振動測定と周波数特性

M5StickC PLUSの加速度センサーで振動測定と周波数特性
M5StickC PLUSの加速度センサーで振動測定と周波数特性

この記事では、M5StickC PLUS内蔵の加速度センサーを使って、振動周波数をLCDへグラフ表示してみました。FFTを使って、振動の周波数特性をグラフで表示します。ぜひご参考になさってみてください。

M5StickC PLUSは、Arduino互換機であるESP32 PICOをベースに、LCDやボタン、各種センサー、バッテリーなどをひとつのケースにパッケージしたM5Stackシリーズ製品のひとつです。M5StickC PLUSの使い方はM5StickC PLUSでArduinoをはじめよう!をご参考になさってみてください。

センサーのひとつに、加速度センサおよびジャイロセンサの慣性計測装置(IMU)が内蔵されています。M5StickC PLUSでは、それぞれXYZ軸の合計6軸が測れるMPU6886というセンサデバイスが使われています。MPU6886とはI2Cで通信されていますが、関数ひとつでデータを読み取れるようになっています。

はじめに

本記事を書くにあたって、SWITCH SCIENCEさんのこちらの記事を大変参考にさせていただきました。ありがとうございます。

ただし、こちらの記事は「M5StickC PLUS」ではなく「M5StickC」の内容となっています。残念ながら、M5StickC PLUSではそのままだと動作できませんでした。

そこで色々調べながらコードを少し修正し、なんとか動作できるようにしてみました。

プログラム

こちらが、加速度センサで検知した振動をグラフにプロットするプログラムです。

#include <M5StickCPlus.h>

#define SAMPLE_PERIOD 20    // サンプリング間隔(ms)
#define SAMPLE_SIZE 240     // サンプリング間隔(20) x 画面幅(240) = 4.8s
#define BUTTON_A 37

bool isPause = false;

void setup() {
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.IMU.Init();
  M5.IMU.SetAccelFsr(M5.IMU.AFS_4G);
  pinMode(BUTTON_A, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON_A), handleInterrupt, FALLING);
}

float ax, ay, az[SAMPLE_SIZE];


#define X0 5  // 横軸の描画開始座標

// 水平静止で重力加速度1000mGが常にかかることを考慮する
#define MINZ -1000  // 縦軸の最小値 mG
#define MAXZ 3000  // 縦軸の最大値 mG



void handleInterrupt() {
  isPause = !isPause;
}

void loop() {
  if (!isPause) {
    M5.Lcd.fillScreen(BLACK); // 画面クリア

    for (int i = 0; i < SAMPLE_SIZE; i++) {
      if (isPause) break;
      
      M5.IMU.getAccelData(&ax,&ay,&az[i]);  // IMUから加速度を取得
  
      az[i] *= 1000;  // mGに変換
      if (i == 0) continue;
      
//        Serial.println(az[i]); // シリアルモニタは115200baudで通信
  
      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, YELLOW);
      delay(SAMPLE_PERIOD);
    }
  }
}

M5StickC PLUSで振動の波形
M5StickC PLUSで振動の波形

このように、振動の波形をリアルタイム表示できるようになりました。

プログラムの解説

M5StickCと違って、M5StickC PLUSの場合はM5.IMUで慣性装置にアクセスできるようです。

加速度センサのデータを取得するサンプリング間隔を20msに設定しました。よって、50Hz程度までの振動を検知できることになります。

M5.IMU.SetAccelFsr(M5.IMU.AFS_4G)で加速度の感度を設定できます。詳しくはライブラリのソースコードをご確認ください。

ところで、M5StickC PLUSに内臓の加速度センサの軸の方向は、写真のような向きになります。今回はZ軸のみのデータを使いますので、振動を測定する際の向きにご注意ください。

加速度センサの軸の方向
加速度センサの軸の方向

また、水平静止状態では、Z軸方向に重力加速度1000mGが常にかかることになります。よって、グラフの位置が静止状態で真ん中になるようにMINZ MAXZの値を調整しました。

Aボタンで計測をストップできるようにしています。AボタンはattachInterruptを使った割り込み処理になっています。割り込み処理に関してはこちらで少し触れましたのでご参考になさってください。

map関数は、数値をある範囲から別の範囲に変換してくれる便利な関数です。ここでは、加速度センサのmG値をLCD画面サイズに合うように比例変換させています。

map(変換したい数値, 現在の範囲の下限, 現在の範囲の上限, 変換後の範囲の下限, 変換後の範囲の上限) のように指定します。

詳しくはこちらの記事を参考にしてみてください。

FFTで振動の周波数特性

さて、SWITCH SCIENCEの記事にある通り、ここからさらにFFTで周波数特性を調べると面白そうですね。さきほどの記事内のプログラムを、少し修正させてもらいました。

#include <M5StickCPlus.h>
#include "arduinoFFT.h"

#define SAMPLE_PERIOD 5    // サンプリング間隔(mS)

const uint16_t FFTsamples = 64; //This value MUST ALWAYS be a power of 2
double vReal[FFTsamples];
double vImag[FFTsamples];
const double samplingFrequency = 1000.0 / (double)SAMPLE_PERIOD;
arduinoFFT FFT = arduinoFFT(vReal, vImag, FFTsamples, samplingFrequency);

int Y0 = 15;
int _height = 135 - Y0;
int _width = 240;
float dmax = 1000.0; // Sensitive

void drawChart(int nsamples) {
    int band_width = floor(_width / nsamples);
    int band_pad = band_width - 1;

    for (int band = 0; band < nsamples; band++) {
        int hpos = band * band_width;
        float d = vReal[band];
        if (d > dmax) d = dmax;
        int h = (int)((d / dmax) * (_height));
        M5.Lcd.fillRect(hpos, _height - h, band_pad, h, BLACK);
        if ((band % 8) == 0) {
            M5.Lcd.setCursor(hpos, _height);
            M5.Lcd.printf("%dHz", (int)((band * 1.0 * samplingFrequency) / FFTsamples));
        }
    }
}


void setup() {
  M5.begin();
  M5.Axp.ScreenBreath(9);
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(WHITE);
  M5.Lcd.setTextColor(BLACK);
  M5.Lcd.setTextSize(2);
  M5.IMU.Init();
  M5.IMU.SetAccelFsr(M5.IMU.AFS_4G);
}


void DCRemoval(double *vData, uint16_t samples) {
    double mean = 0;
    for (uint16_t i = 0; i < samples; i++) {
        mean += vData[i];
    }
    mean /= samples;
    for (uint16_t i = 0; i < samples; i++) {
        vData[i] -= mean;
    }
}



void loop() {
  for (int i = 0; i < FFTsamples; i++) {
    float ax, ay, az;
    long t = micros();
    M5.IMU.getAccelData(&ax,&ay,&az);  // MPU6886から加速度を取得
    vReal[i] = az * 1000;  // mGに変換
    vImag[i] = 0;
    delayMicroseconds(SAMPLE_PERIOD * 1000 - (micros() - t));
  }

  DCRemoval(vReal, FFTsamples);  // 直流分を除去
  FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);  // 窓関数
  FFT.Compute(FFT_FORWARD); // FFT処理(複素数で計算)
  FFT.ComplexToMagnitude(); // 複素数を実数に変換
  double x = FFT.MajorPeak();

  M5.Lcd.fillScreen(WHITE);  // 画面をクリア
  drawChart(FFTsamples / 2);
  M5.Lcd.setCursor(40, 0);
  M5.Lcd.printf("Peak: %.0fHz", x);
}

M5StickC PLUSで振動周波数特性
M5StickC PLUSで振動周波数特性

自分の場合ですと、クロスバイクの振動測定に使えそうです。ハンドルのブレなどのチューニングに役立ててみたいと思います。

記事に関するご質問などがあればTwitterへお返事ください。
この記事で紹介した商品
M5Stackのオススメ参考書
M5Stack製品
M5StickCで使えるHat
関連記事