M5Stack社の「CORE2」を使用して3軸(X ,Y ,Z)の振動の大きさ(加速度)を液晶画面でグラフ化して表示できる加速度モニターを紹介します。
サンプルプログラムのコピペで動作可能です。(「BASIC」でも一部修正すれば使用できます。)
1.加速度モニター動作紹介
2.サンプルプログラム(コピペ)
3.振動センサ(MPU6886)について
4.加速度について
5.振動センサの値を取得するプログラム
・動作確認用プログラム(コピペ)
・AruduioIDEのシリアルプロッタで動作確認
6.まとめ
1.加速度モニター動作紹介
作成した加速度モニターの画面は下画像のようになります。
電源をONすると、その時の各軸の加速度の状態で測定値は「0リセット」されます。
「0リセット」ボタンを押すことで手動で「0リセット」することもできます。
X、Y、Z軸それぞれの加速度の「現在値」と測定中の「最大最小値」が保持されて表示されます。
「最大最小値」は「最大最小値リセット」ボタンを押すことで手動でリセットすることもできます。
「測定レンジ」は電源ON時は「±0.2G」ですが「加速度測定レンジ切替え」ボタンを押すことで「±0.2、±0.5、±1.0、±2.0、±5.0、±10G」に切り替えることができます。
本体を左右に揺らすとX軸の加速度が変化し、上下に揺らすとY軸が、本体を持ち上げて手前奥に揺らすとZ軸が変化します。
「加速度測定レンジ」を「±1.0G」に設定して本体を回転させると、地球の重力加速度が「1G」であることが確認できます。
「CORE2」の使い方については、以下のリンクで詳しく紹介しています。
グラフを「チラツキ」なく描画するために、液晶表示ライブラリの「lovyanGFX」を使用しています。
「lovyanGFX」の使い方については以下のリンクで詳しく紹介しています。
2.サンプルプログラム(コピペ)
サンプルプログラムは以下になります。「コピペ」して書き込んでください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
#define M5STACK_MPU6886 // <M5Core2.h>をincludeする前に、IMUモジュールを#defineしておく
#include <M5Core2.h>
#define LGFX_AUTODETECT // 自動認識(D-duino-32 XS, PyBadgeはパネルID読取れないため自動認識の対象から外れているそうです)
#define LGFX_USE_V1 // v1.0.0を有効に(v0からの移行期間の特別措置とのこと。書かない場合は旧v0系で動作)
#include <LovyanGFX.hpp> // lovyanGFXのヘッダを準備
#include <LGFX_AUTODETECT.hpp> // クラス"LGFX"を準備
static LGFX lcd; // LGFXのインスタンスを作成(クラスLGFXを使ってlcdコマンドでいろいろできるようにする)
static LGFX_Sprite canvas(&lcd); // スプライトを使う場合はLGFX_Spriteのインスタンスを作成
// グローバル変数宣言
float accX, accY, accZ; // 加速度格納用
float x, y, z = 0; // 現在値格納用
float x10, y10, z10 = 0; // 0補正値格納用
float ax_data1[6] = {0}; // 加速度最大値格納用
float plot_x[250] = {150}; // グラフプロットデータ格納用
float plot_y[250] = {150};
float plot_z[250] = {150};
int axis = 0; // x軸座標カウント用
int range_select = 1; // y軸レンジ選択用
float y_range = 250; // y軸レンジ換算値用
int sampling = 0; // サンプリング回数カウント用
/**************************** Y軸項目表示 ****************************/
void rangeItem(float y_range) {
canvas.setTextColor(WHITE, BLACK); // 文字色
canvas.setFont(&fonts::Font2); // フォント設定
if (y_range == 5) { // y_range(換算値)が5なら整数表示
canvas.setCursor(5, 93); canvas.printf("%7d", 10);
canvas.setCursor(5, 193); canvas.printf("%7d", -10);
} else { // y_range(換算値)が5でなければ浮動小数表示
canvas.setCursor(5, 93); canvas.printf("%7.1f", 50 / y_range);
canvas.setCursor(5, 193); canvas.printf("%7.1f", -50 / y_range);
}
}
/*************************** 0補正値セット ***************************/
void zeroReset() {
M5.IMU.getAccelData(&accX,&accY,&accZ); // 加速度データ取得
x10 = accX; //取得値を補正値としてセット
y10 = accY;
z10 = accZ;
}
// 初期設定 ----------------------------------------------------------
void setup(){
M5.begin(); // 本体初期化
lcd.init(); // LCD初期化
canvas.setColorDepth(8); // カラーモード設定
canvas.createSprite(lcd.width(), lcd.height()); // canvasサイズ(メモリ描画領域)設定(画面サイズに設定)
M5.IMU.Init(); // 6軸センサ初期化
M5.IMU.SetAccelFsr(M5.IMU.AFS_2G); // 加速度センサースケール初期値 ±2G(2,4,8,16) ※GRAYは「setAccelFsr」(先頭のsが小文字)
canvas.fillScreen(BLACK); // 背景色
canvas.setTextColor(WHITE , BLACK); // 文字色
canvas.setTextSize(1);
canvas.setFont(&fonts::Font4); // フォント設定
canvas.setCursor(20, 0); canvas.print("3-AXIS MONITOR (G)"); // 座標、タイトル表示
canvas.drawRect(60, 90, 250, 120, WHITE); // グラフ枠表示
canvas.drawRect(59, 90, 252, 120, WHITE);
canvas.drawRect(58, 90, 254, 120, WHITE);
canvas.setFont(&fonts::Font2); // フォント設定
canvas.setCursor(40, 144); canvas.print("0"); // Y軸0表示
// 設定ボタン項目表示
canvas.drawRect(20, 217, 70, 23, DARKGREY); // ボタン枠
canvas.drawRect(127, 217, 70, 23, DARKGREY);
canvas.drawRect(234, 217, 70, 23, DARKGREY);
canvas.setTextColor(BLACK, DARKGREY);
canvas.setCursor(24, 221); canvas.print(" 0 SET "); // 0セットボタン
canvas.setCursor(130, 221); canvas.print(" RESET "); // 最大値0リセットボタン
canvas.setCursor(237, 221); canvas.print(" RANGE "); // Y軸レンジ選択ボタン
zeroReset(); // 0リセット
rangeItem(y_range); // y軸レンジ項目表示呼出し
}
// メイン処理 --------------------------------------------------------
void loop() {
float ax_data0[6]; // 加速度測定値格納用
M5.update(); // ボタン状態更新
M5.IMU.getAccelData(&accX,&accY,&accZ); // 加速度データ取得
// 0補正
if (M5.BtnA.wasReleased()) { // ボタンAが押されたら0補正値セット
delay(1000); // 本体安定待ち
zeroReset(); // 0リセット
}
x = accX - x10; // 補正後の数値を表示値としてセット
y = accY - y10;
z = accZ - z10;
// 最大値リセット
if (M5.BtnB.wasReleased()) { // ボタンBが押されたら0リセット
delay(1000); // 本体安定待ち
for (int i = 0; i < 6; i++) {
ax_data1[i] = 0; // 最大値配列0リセット
}
}
// y軸レンジ変更
if (M5.BtnC.wasReleased()) { // ボタンCが押されたらy軸レンジ変更
range_select++; // y軸レンジ選択+1
if (range_select == 7) { // y軸レンジ選択値が7なら
range_select = 1; // 1にセット
}
// 加速度センサスケール選択 ※GRAYは「setAccelFsr」(先頭のsが小文字)
switch (range_select) { // y軸レンジ、加速度センサスケール選択
case 1: y_range = 250; // ±0.2G
M5.IMU.SetAccelFsr(M5.IMU.AFS_2G); // ±2G
break;
case 2: y_range = 100; // ±0.5G
M5.IMU.SetAccelFsr(M5.IMU.AFS_2G); // ±2G
break;
case 3: y_range = 50; // ±1.0G
M5.IMU.SetAccelFsr(M5.IMU.AFS_2G); // ±2G
break;
case 4: y_range = 25; // ±2.0G
M5.IMU.SetAccelFsr(M5.IMU.AFS_4G); // ±4G
break;
case 5: y_range = 10; // ±5.0G
M5.IMU.SetAccelFsr(M5.IMU.AFS_8G); // ±8G
break;
case 6: y_range = 5; // ±10G
M5.IMU.SetAccelFsr(M5.IMU.AFS_16G); // ±16G
break;
}
rangeItem(y_range); // y軸レンジ項目表示呼出し
}
// 最大値の取得
if (x >= 0) { // 0以上なら
ax_data0[0] = x; // +加速度を格納
if (ax_data0[0] > ax_data1[0]) { // 前回の測定値より大きければ
ax_data1[0] = ax_data0[0]; // 測定値を更新
}
} else { // 0より小さければ
ax_data0[1] = x; // -加速度を格納
if (ax_data0[1] < ax_data1[1]) { // 前回の測定値より小さければ
ax_data1[1] = ax_data0[1]; // 測定値を更新
}
}
if (y >= 0) {
ax_data0[2] = y;
if (ax_data0[2] > ax_data1[2]) { // 前回の測定値より大きければ
ax_data1[2] = ax_data0[2]; // 測定値を更新
}
} else {
ax_data0[3] = y;
if (ax_data0[3] < ax_data1[3]) { // 前回の測定値より小さければ
ax_data1[3] = ax_data0[3]; // 測定値を更新
}
}
if (z >= 0) {
ax_data0[4] = z;
if (ax_data0[4] > ax_data1[4]) { // 前回の測定値より大きければ
ax_data1[4] = ax_data0[4]; // 測定値を更新
}
} else {
ax_data0[5] = z;
if (ax_data0[5] < ax_data1[5]) { // 前回の測定値より小さければ
ax_data1[5] = ax_data0[5]; // 測定値を更新
}
}
if (sampling == 5) {
// 加速度表示(現在値 最小〜最大値)
canvas.setFont(&fonts::Font4); // フォント設定
canvas.setTextColor(ORANGE , BLACK); // X軸測定値
canvas.setCursor(30, 24); canvas.printf("X:%7.3f ", x); // 現在値
canvas.setCursor(140, 24); canvas.printf("( %5.2f", ax_data1[1]); // 最小値
canvas.setCursor(220, 24); canvas.printf("~ %5.2f )", ax_data1[0]); // 最大値
canvas.setTextColor(CYAN , BLACK); // Y軸測定値
canvas.setCursor(30, 44); canvas.printf("Y:%7.3f ", y);
canvas.setCursor(140, 44); canvas.printf("( %5.2f", ax_data1[3]);
canvas.setCursor(220, 44); canvas.printf("~ %5.2f )", ax_data1[2]);
canvas.setTextColor(GREEN , BLACK); // Z軸測定値
canvas.setCursor(30, 64); canvas.printf("Z:%7.3f ", z);
canvas.setCursor(140, 64); canvas.printf("( %5.2f", ax_data1[5]);
canvas.setCursor(220, 64); canvas.printf("~ %5.2f )", ax_data1[4]);
// バッテリー残量(MAX約4.2V、限界電圧3V)パーセント換算表示 ※GRAYは使用不可180〜188行までコメントアウトする。
float battery; // バッテリー残量表示用
battery = (M5.Axp.GetBatVoltage() - 3) * 90; // バッテリー残量取得、換算
if (battery > 100) { // 換算値が100以上なら
battery = 100; // 100にする
}
canvas.setCursor(287, 0); canvas.setFont(&fonts::Font2); // 座標、フォント 2(16px)
canvas.setTextColor(DARKGREY, BLACK); // 文字色
canvas.printf("%3.0f%%", battery); // バッテリー残量表示
sampling = 0; // サンプリング回数0リセット
}
sampling++; // サンプリング回数+1
// グラフ目盛表示
canvas.fillRect(61, 91, 248, 118, BLACK); // 枠内リセット
canvas.drawFastHLine(55, 100, 260, DARKGREY); // 横線
canvas.drawFastHLine(55, 150, 260, WHITE);
canvas.drawFastHLine(55, 200, 260, DARKGREY);
canvas.drawFastVLine(110, 90, 120, DARKGREY); // 縦線
canvas.drawFastVLine(160, 90, 120, DARKGREY);
canvas.drawFastVLine(210, 90, 120, DARKGREY);
canvas.drawFastVLine(260, 90, 120, DARKGREY);
// 表示データスクロール
if (axis < 248) { // グラフx軸カウントmaxでなければ
axis++; // グラフx軸カウント+1
} else { // グラフx軸カウントmaxなら
for (int i = 0; i < 249; i++) { // 表示データ全てを
plot_x[i] = plot_x[i + 1]; // 前配列へシフト
plot_y[i] = plot_y[i + 1]; // 前配列へシフト
plot_z[i] = plot_z[i + 1]; // 前配列へシフト
}
}
// 加速度データ換算
plot_x[axis] = 150 - (x * y_range); // 換算データを表示データ配列へ
plot_y[axis] = 150 - (y * y_range);
plot_z[axis] = 150 - (z * y_range);
// 折れ線グラフ表示
for (int i = 1; i <= axis; i++) { // x軸max250まで繰返し
// X軸
if ((91 < plot_x[i]) && (plot_x[i] < 208)) { // 表示データが91より大きくて208以下なら
canvas.drawLine(i+59, plot_x[i-1], i + 60, plot_x[i], ORANGE); // 波形描画
} else if (plot_x[i] <= 91) {
plot_x[i] = 91;
canvas.drawLine(i+59, plot_x[i-1], i + 60, plot_x[i], ORANGE); // 波形描画(max)
} else if (plot_x[i] >= 208) {
plot_x[i] = 208;
canvas.drawLine(i+59, plot_x[i-1], i + 60, plot_x[i], ORANGE); // 波形描画(min)
}
// Y軸
if ((91 < plot_y[i]) && (plot_y[i] < 208)) { // 表示データが91より大きくて208以下なら
canvas.drawLine(i+59, plot_y[i-1], i + 60, plot_y[i], CYAN); // 波形描画
} else if (plot_y[i] <= 91) {
plot_y[i] = 91;
canvas.drawLine(i+59, plot_y[i-1], i + 60, plot_y[i], CYAN); // 波形描画(max)
} else if (plot_y[i] >= 208) {
plot_y[i] = 208;
canvas.drawLine(i+59, plot_y[i-1], i + 60, plot_y[i], CYAN); // 波形描画(min)
}
// Z軸
if ((91 < plot_z[i]) && (plot_z[i] < 208)) { // 表示データが91より大きくて208以下なら
canvas.drawLine(i+59, plot_z[i-1], i + 60, plot_z[i], GREEN); // 波形描画
} else if (plot_z[i] <= 91) {
plot_z[i] = 91;
canvas.drawLine(i+59, plot_z[i-1], i + 60, plot_z[i], GREEN); // 波形描画(max)
} else if (plot_z[i] >= 208) {
plot_z[i] = 208;
canvas.drawLine(i+59, plot_z[i-1], i + 60, plot_z[i], GREEN); // 波形描画(min)
}
}
canvas.pushSprite(0, 0); // メモリ内に描画したcanvasを座標を指定して表示する
}
3.振動センサ(MPU6886)について
M5Stackシリーズで現在採用されている振動センサは「MPU6886」で、「CORE2」「M5StickC PLUS」「ATOM MATRIX」等に搭載されています。
このセンサは6軸のIMU(慣性計測装置:Inertial Measurement Unit)で、6軸とは「3軸加速度計」と「3軸ジャイロスコープ」を組み合わせたものです。(3軸 = X、Y、Z軸)
加速度は各軸方向の速度の変化を測定でき、ジャイロは各軸に対する回転角速度を測定できます。
これらを組み合わせることで移動速度や方向、姿勢角度を算出することができます。
4.加速度について
加速度とは、単位時間あたりの速度の変化率のことをいい、単位は「m/s2(メートル毎秒毎秒)」で表されます。
一見イメージしにくいですが、身近な加速度の1つ重力加速度に例えるとイメージしやすいです。
重力加速度は場所によって微妙に変化しますが、標準重力加速度は「9.80665m/s2」で定義されており、別の単位で「1G(ジー)」として扱われます。
地球上にいる私たちは常に重力加速度「1G 」の影響を受けており、地面に立っているうちはそのまま静止していられますが、地面がなくなって落下したとしましょう。
すると以下の計算式のように私達の落下速度は3秒で時速約100km/hに達します。
このまま3秒間(s)落下(加速)し続けると秒速は 9.8m/s2 × 3s = 29.4m/s に達します。
1時間の3600秒(s)で進む距離は 29.4m/s × 3600s = 105,840m
これを1時間(h)で進む距離の時速に換算すると 105.84km/h となり、時速は100km/h を超えました。さらに落下し続けるとどうなるでしょう?・・・恐ろしいのでやめときます・・・
※それぞれの計算は単位の変化を見ながら計算すると理解しやすいと思います。
5.振動センサの値を取得するプログラム
「ArduinoIDE」のスケッチ例を元に振動センサの各測定値をシリアル出力するプログラムを作成しました。
「CORE2」や「M5StickC PLUS」「ATOM MATRIX」等に書き込んで、ArduinoIDEの「シリアルプロッタ」を使用するとグラフ化されてどのような動きになるか確認できます。
本体のボタンを押すごとに「加速度」「ジャイロ(角速度)」「姿勢角、方向」「加速度から換算した角度」の変化を切り替えて確認することができます。
・動作確認用プログラム(コピペ)
動作確認用プログラムは以下になります。「コピペ」して書き込んでください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
// 振動センサのデータをシリアル出力するプログラム。ArduinoIDEのシリアルプロッタでグラフとして確認します。※液晶表示は何も表示されません。
#define M5STACK_MPU6886 // ヘッダーファイルをincludeする前に、IMUモジュールを#defineしておく
#include <M5Core2.h> // ヘッダーファイル ※AtomMatrixは <M5Atom.h> / CPlusは <M5StickCPlus.h> / GRAYは <M5Stack.h>
// 変数宣言
float accX, accY, accZ; // 加速度格納用
float gyroX, gyroY, gyroZ; // 角速度格納用
float pitch, roll, yaw; // 姿勢角格納用
int mode = 0; // 測定モード選択用
// 初期設定 ----------------------------------------------------------
void setup(){
M5.begin(); // 本体初期化
Serial.begin(9600); // シリアル出力初期化
M5.IMU.Init(); // 6軸センサ初期化
M5.IMU.SetAccelFsr(M5.IMU.AFS_8G); // 加速度センサースケール初期値設定 ±8G(2,4,8,16) ※GRAYは「setAccelFsr」(先頭のsが小文字)
}
// メイン処理 --------------------------------------------------------
void loop() {
M5.update(); // ボタン状態更新
M5.IMU.getAccelData(&accX, &accY, &accZ); // 加速度データ取得
M5.IMU.getGyroData(&gyroX, &gyroY, &gyroZ); // 角速度データ取得
M5.IMU.getAhrsData(&pitch, &roll, &yaw); // 姿勢角データ取得
float x_angle = atan2(accX, accZ) * 180.0 / PI; // X-Z加速度から角度に換算
float y_angle = atan2(accY, accZ) * 180.0 / PI; // Y-Z加速度から角度に換算
// ボタンONで測定モード切り替え
if (M5.BtnA.wasPressed()) { mode++; } // ※Atom Matrixは「M5.Btn.」
if (mode == 4) { mode = 0; }
switch (mode) { // modeによって出力データを切り替え
case 0:
Serial.println("accelX, Y, Z"); // 加速度項目
Serial.printf("%7.2f, %7.2f, %7.2f\n", accX, accY, accZ); // 加速度
break;
case 1:
Serial.println("gyroX, Y, Z"); // ジャイロ項目
Serial.printf("%7.2f, %7.2f, %7.2f\n", gyroX, gyroY, gyroZ);// ジャイロ
break;
case 2:
Serial.println("pitch, roll, yaw"); // 姿勢角
Serial.printf("%7.2f, %7.2f, %7.2f\n", pitch, roll, yaw); // 姿勢角
break;
case 3:
Serial.println("AngleX,Y"); // 角度項目
Serial.printf("%5.1f, %5.1f\n", x_angle, y_angle); // 加速度から換算した角度
break;
}
delay(100);
}
・AruduioIDEのシリアルプロッタで動作確認
「ArduinoIDE」のシリアルプロッタを使用するとシリアル出力データをグラフ化して確認することができます。使い方は以下のようになります。
「ArduinoIDE」を起動したら「シリアルポート」の設定のみ行います。
「ボード」等の設定は不要です。
次に「ツール」→「シリアルプロッタ」をクリックします。
以下のように、シリアルプロッタが表示されるので、本体を揺らしたり、回転させたりしてグラフがどのように変化するか確認してみましょう。
本体のボタンを押すごとに以下のように「加速度」「ジャイロ(角速度)」「姿勢角、方向」「加速度から換算した角度」の変化を切り替えて確認することができます。
加速度
「加速度」では各軸(X、Y、Z)方向に揺らすと、今回製作した加速度モニターと同じ波形が確認できます。
ジャイロ(角速度)
「ジャイロ」は各軸に対して本体を回転させると、回転角速度が表示されます。
姿勢角、方向
「姿勢角」は「ピッチ」「ロール」、「方向」は「ヨー」と呼ばれ、本体がどのような姿勢、向きになっているかを確認できるものですが、そのままのデータでは「ジャイロドリフト」等の誤差(特に「ヨー」が大)が発生するため、センサに合わせて補正を行う必要があります。
加速度から換算した角度
「加速度から換算した角度」では、本体をX軸、Y軸で1回転させると上のようなグラフになりました。
換算に使用している加速度に対して垂直の位置でグラフ波形が乱れるので使用するには工夫がいりそうです。
「ArduinoIDE」のインストールから使い方については、以下のリンクで詳しく紹介しています。
6.まとめ
M5Stackの振動センサを使用した3軸加速度モニターについて紹介しました。
振動センサの「MPU6886」は6軸のIMU(慣性計測装置:Inertial Measurement Unit)で「3軸加速度」と「3軸ジャイロ(角速度)」の測定ができ、これらを組み合わせることで移動速度や方向、姿勢角度を算出することができます。
方向に関して正確に測定するには、補正フィルタや回転行列の計算等ハードルが高そうです。3軸の磁気センサを組み合わせれば少しハードルは下がりそうですが、残念ながら磁気センサを搭載していた「GRAY」は生産中止で現役の機種には搭載されていないので今回は断念しました・・・
今回は加速度のモニターのみとなりましたが、加速度から角度を求めることもできるため、簡易な姿勢制御なども今後やっていければと思います。
加速度の測定だけですが、振動量の測定器としては安価で実現でき、重力加速度がほぼ正確に1Gの測定ができたことを考えると精度もそれなりに出ていると思います。
短時間の振動の確認だけでなく、SDカードにデータを記録できるようにすれば振動の変化を確認するデータロガーとしても使用できそうです。
振動センサーはなかなか奥が深いですが、いろいろと用途はありそうなので今後も理解を深めて紹介していければと思います。
コメント
グラフ表示のプログラムが大変分かりやすく、参考に(というかコピペ)のうえ、GROVEのADXL345用にデータ取得部分を書き換えて、振動のモニタとして活用させていただきたいと思います。
ありがとうございます。
ぜひぜひ使ってください。たまたま私も振動センサの別置きを検討してたところで、CORE2のセンサ外してどうやって延長しようかと思ってましたが、GROVE使った方が楽そうですね(汗)私もやってみます^^