M5StickC PLUSの加速度センサで振動測定と周波数特性
この記事では、M5StickC PLUS内蔵の加速度センサを使って、振動周波数をLCDへグラフ表示してみました。FFTを使って、振動の周波数特性をグラフで表示します。
M5StickC PLUSは、Arduino互換機であるESP32 PICOをベースに、LCDやボタン、各種センサ、バッテリーなどをひとつのケースにパッケージしたM5Stackシリーズ製品のひとつです。M5StickC PLUSの使い方は M5StickC PLUSでArduinoをはじめよう! をご覧ください。
▼ 半導体不足のため入手困難な場合は、ひとつ前のモデルのM5StickCも選択に入れてみてください。
センサのひとつに、加速度センサおよびジャイロセンサの慣性計測装置(IMU)が内蔵されてます。M5StickC PLUSでは、それぞれXYZ軸の合計6軸が測れるMPU6886というセンサデバイスが使われてます。MPU6886とはI2Cで通信されてますが、関数ひとつでデータを読み取れます。
はじめに
本記事を書くにあたって、SWITCH SCIENCEさんのこちらの記事を大変参考にさせていただきました。ありがとうございます。 M5StickCで振動を測定する-AmbientでIoTをはじめよう
ただし、こちらの記事は「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と違って、M5StickC PLUSの場合はM5.IMUで慣性装置にアクセスできるようです。
加速度センサのデータを取得するサンプリング間隔を20msに設定しました。よって、50Hz程度までの振動を検知できることになります。
M5.IMU.SetAccelFsr(M5.IMU.AFS_4G)で加速度の感度を設定できます。詳しくはライブラリのソースコードをご確認ください。 M5StickC-Plus/MPU6886.hatmaster·m5stack/M5StickC-Plus·GitHubところで、M5StickC PLUSに内臓の加速度センサの軸の方向は、写真のような向きになります。今回はZ軸のみのデータを使いますので、振動を測定する際の向きにご注意ください。
また、水平静止状態では、Z軸方向に重力加速度1000mGが常にかかることになります。よって、グラフの位置が静止状態で真ん中になるようにMINZ MAXZの値を調整しました。
Aボタンで計測をストップできるようにしてます。AボタンはattachInterruptを使った割り込み処理になってます。割り込み処理に関してはこちらで少し触れました。
map関数は、数値をある範囲から別の範囲に変換してくれる便利な関数です。ここでは、加速度センサのmG値をLCD画面サイズに合うように比例変換させてます。 map(変換したい数値, 現在の範囲の下限, 現在の範囲の上限, 変換後の範囲の下限, 変換後の範囲の上限) のように指定します。 詳しくはこちらの記事を参考にしてみてください。 map()-ArduinoReferenceFFTで振動の周波数特性
さて、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);
}
自分の場合ですと、クロスバイクの振動測定に使えそうです。ハンドルのブレなどのチューニングに役立ててみます。