「加速度センサーって名前は聞いたことあるけど、実際どうやって使うの?」

そんな疑問を持っている方、多いんじゃないでしょうか。スマホの画面回転、ゲームコントローラーの傾き検知、ドローンの姿勢制御…これらはすべて加速度センサー+ジャイロセンサーの組み合わせで実現されています。

今回は、Arduinoで最もよく使われるセンサーモジュールのひとつ、MPU-6050を使って、傾き(角度)の検知・振動の検知から、ノイズを抑えるカルマンフィルタの実装まで、一気に解説していきます。

「カルマンフィルタ?なんか難しそう…」と思ったかもしれません。でも大丈夫です。数式の話は最小限にして、「なぜ必要か」「どう使うか」に絞ってわかりやすく説明します。ぜひ最後まで読んでみてください 😊

この記事で学べること・対象読者

Arduino sensor gyroscope
Arduino sensor gyroscope / Photo by Lisha Dunlap via Pexels

この記事はこんな方に向けています。

  • Arduinoの基本操作(Lチカ・シリアル通信)は経験済みの初〜中級者
  • MPU-6050を入手したけど動かし方がわからない方
  • 加速度センサーとジャイロセンサーの値がノイズだらけで困っている方
  • カルマンフィルタの仕組みを実装レベルで理解したい方

この記事を読み終えると、こんなことができるようになります ✅

  • MPU-6050の配線とI2C通信の設定
  • 加速度・ジャイロの生データの読み取り
  • 加速度センサーで傾き角度を計算
  • 振動の検知と閾値による判定
  • カルマンフィルタで滑らかな角度データを取得

MPU-6050ってどんなセンサー?

MPU-6050は、加速度センサージャイロセンサーが1チップに収まった6軸IMU(慣性計測ユニット)です。

Amazonや秋月電子で300〜500円程度で購入できる非常にコスパの高いモジュールで、Arduinoとの組み合わせでよく使われています。

ざっくりとした特徴をまとめるとこんな感じです。

  • 📐 加速度センサー(3軸): X・Y・Z方向の加速度を測定。重力加速度を基準に傾きを計算できる
  • 🔄 ジャイロセンサー(3軸): X・Y・Z軸まわりの角速度を測定。回転速度がわかる
  • 🌡️ 温度センサー: チップ内部の温度も測定可能
  • 🔌 通信方式: I2C(アドレス 0x68 または 0x69)
  • 動作電圧: 3.3V〜5V対応(モジュール上でレギュレーター内蔵が多い)

加速度センサーとジャイロセンサーの違い

ここが少しわかりにくいポイントなので整理しておきましょう。

加速度センサーは「今どの方向に力がかかっているか」を測ります。静止状態では地球の重力(約9.8m/s²)だけが働くので、傾きを計算できます。ただし、振動や衝撃があると値が乱れやすいです。

ジャイロセンサーは「どれくらいの速さで回転しているか(角速度)」を測ります。角速度を時間積分すると角度が得られますが、少しずつ誤差が蓄積(ドリフト)していく欠点があります。

つまり、どちらか一方だけでは正確な傾き角度を取るのが難しいんですよね。そこで両者の長所を組み合わせるのがカルマンフィルタです。これについては後ほど詳しく説明します。

回路図・配線方法

MPU-6050とArduino UNOの接続はとてもシンプルです。I2C通信なので使うピンは4本だけ。

MPU-6050 ピン Arduino UNO ピン
VCC 5V(または3.3V)
GND GND
SCL A5(SCL)
SDA A4(SDA)
AD0 GND(アドレス 0x68 に固定)
INT 未接続(今回は使わない)

⚠️ 確認ポイント: モジュールによってはすでにプルアップ抵抗が実装されています。「GY-521」という名前のモジュールが一般的で、これには5Vレギュレーターとプルアップ抵抗が内蔵されているので安心して5Vに接続できます。

まずはライブラリのインストールと動作確認

MPU-6050を扱うには、ライブラリを使うのが一番スムーズです。Arduino IDEのライブラリマネージャーから「MPU6050」by Electronic Cats(または Jeff Rowberg版)をインストールしてください。

インストール後、まずシリアルモニターで生データが出力されるか確認しましょう。

// MPU-6050 生データ取得サンプル
#include <Wire.h>
#include <MPU6050.h>

MPU6050 mpu;

void setup() {
  Serial.begin(115200);
  Wire.begin();

  mpu.initialize();

  // 接続確認
  if (mpu.testConnection()) {
    Serial.println("MPU-6050 接続OK!");
  } else {
    Serial.println("MPU-6050 接続失敗…配線を確認してください");
    while (1); // ここで止める
  }
}

void loop() {
  int16_t ax, ay, az; // 加速度の生データ(16bit整数)
  int16_t gx, gy, gz; // ジャイロの生データ

  // データ取得
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

  // シリアルモニターに出力
  Serial.print("加速度: ");
  Serial.print(ax); Serial.print(" , ");
  Serial.print(ay); Serial.print(" , ");
  Serial.println(az);

  Serial.print("ジャイロ: ");
  Serial.print(gx); Serial.print(" , ");
  Serial.print(gy); Serial.print(" , ");
  Serial.println(gz);

  delay(200);
}

シリアルモニターを115200bpsで開いて、数値がどんどん流れてくれば接続成功です 🎉

加速度センサーで傾き角度を計算する

生データを取得できたら、次は傾き角度の計算です。加速度センサーだけで傾きを求めるには、arctanを使った三角関数の計算をします。

イメージとしては、XYZ軸それぞれにかかる重力成分の比率から「どのくらい傾いているか」を逆算する感じです。

// 加速度センサーのみで傾き角度を計算するサンプル
#include <Wire.h>
#include <MPU6050.h>
#include <math.h> // atan2を使うために必要

MPU6050 mpu;

void setup() {
  Serial.begin(115200);
  Wire.begin();
  mpu.initialize();

  if (!mpu.testConnection()) {
    Serial.println("接続エラー");
    while (1);
  }
}

void loop() {
  int16_t ax, ay, az, gx, gy, gz;
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

  // 生データをg(重力加速度)単位に変換
  // デフォルトのレンジは±2g → LSB感度 = 16384
  float ax_g = ax / 16384.0;
  float ay_g = ay / 16384.0;
  float az_g = az / 16384.0;

  // X軸まわりの傾き角度(ピッチ角)を計算
  float pitch = atan2(ay_g, sqrt(ax_g * ax_g + az_g * az_g)) * 180.0 / PI;

  // Y軸まわりの傾き角度(ロール角)を計算
  float roll = atan2(-ax_g, az_g) * 180.0 / PI;

  Serial.print("ピッチ角: "); Serial.print(pitch, 2);
  Serial.print(" deg  ロール角: "); Serial.println(roll, 2);

  delay(100);
}

ポイントをまとめるとこんな感じです。

  • 生データを実際の加速度(g単位)に変換するため16384.0で割る(±2gのデフォルトレンジ設定の場合)
  • atan2()関数を使うことで-180〜+180度の範囲で角度を得られる
  • ピッチはY軸とZ軸の合成、ロールはX軸とZ軸の関係で計算する

モジュールを実際に傾けながらシリアルモニターの値を確認してみてください。値が変化すれば成功です ✅

振動の検知と閾値による判定


次は振動の検知です。MPU-6050は振動センサーとしても使えます。やり方はとてもシンプルで、加速度の合計値(ノルム)を計算して閾値と比較するだけです。

// 振動検知サンプル
#include <Wire.h>
#include <MPU6050.h>
#include <math.h>

MPU6050 mpu;

// 振動判定の閾値(この値を調整して感度を変える)
const float VIBRATION_THRESHOLD = 1.2; // 単位:g

void setup() {
  Serial.begin(115200);
  Wire.begin();
  mpu.initialize();
}

void loop() {
  int16_t ax, ay, az, gx, gy, gz;
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

  // g単位に変換
  float ax_g = ax / 16384.0;
  float ay_g = ay / 16384.0;
  float az_g = az / 16384.0;

  // 加速度の合計値(ベクトルのノルム)を計算
  float total_accel = sqrt(ax_g * ax_g + ay_g * ay_g + az_g * az_g);

  // 静止状態では約1g(重力のみ)なので、そこからのズレで振動を判定
  float accel_deviation = abs(total_accel - 1.0);

  Serial.print("加速度合計: "); Serial.print(total_accel, 3);
  Serial.print(" g  偏差: "); Serial.print(accel_deviation, 3);

  if (accel_deviation > VIBRATION_THRESHOLD - 1.0) {
    Serial.print(" 【振動検知!】");
  }
  Serial.println();

  delay(50); // 振動検知は高頻度でサンプリング
}

静止状態では重力だけで合計約1gになります。それより大きくズレたときを「振動あり」と判定する仕組みです。閾値(VIBRATION_THRESHOLD)の値を調整することで感度を変えられます。

カルマンフィルタで滑らかな角度を得る

ここからが今回のメインディッシュです 🍽️

加速度センサーだけで計算した角度は、振動や外乱でガタガタします。ジャイロだけだとドリフト(じわじわズレていく)が起きます。

この2つの弱点を補い合うのがカルマンフィルタです。

カルマンフィルタのイメージ

難しく聞こえますが、イメージはこんな感じです。

「ジャイロの積分値(短期的には信頼できる)と加速度からの計算値(長期的には信頼できる)を、どちらをどのくらい信頼するかを自動で調整しながら混ぜ合わせる」

この「信頼度の調整」を数学的に最適化したのがカルマンフィルタです。実装上は以下の2段階で動きます。

  1. 予測ステップ: ジャイロの角速度から次の角度を予測する
  2. 更新ステップ: 加速度センサーの計測値を使って予測を補正する

では実際のコードを見てみましょう。

// カルマンフィルタで傾き角度を安定化するサンプル
#include <Wire.h>
#include <MPU6050.h>
#include <math.h>

MPU6050 mpu;

// ===== カルマンフィルタのパラメータ =====
// Q: プロセスノイズ共分散(小さいほどジャイロを信頼)
// R: 観測ノイズ共分散(小さいほど加速度センサーを信頼)
float Q_angle  = 0.001; // 角度のプロセスノイズ
float Q_gyro   = 0.003; // ジャイロのバイアスノイズ
float R_angle  = 0.03;  // 加速度からの角度の観測ノイズ

// カルマンフィルタの状態変数(ロール角用)
float kalman_angle_roll = 0.0;   // 推定角度
float kalman_bias_roll  = 0.0;   // ジャイロのバイアス推定値
float P_roll[2][2] = {{0, 0}, {0, 0}}; // 誤差共分散行列

// カルマンフィルタのメイン関数(ロール角計算用)
float kalmanUpdate(float newAngle, float newRate, float dt) {
  // ===== 予測ステップ =====
  // ジャイロの角速度からバイアスを引いた値で角度を予測
  float rate = newRate - kalman_bias_roll;
  kalman_angle_roll += dt * rate;

  // 誤差共分散行列を更新
  P_roll[0][0] += dt * (dt * P_roll[1][1] - P_roll[0][1] - P_roll[1][0] + Q_angle);
  P_roll[0][1] -= dt * P_roll[1][1];
  P_roll[1][0] -= dt * P_roll[1][1];
  P_roll[1][1] += Q_gyro * dt;

  // ===== 更新ステップ =====
  // カルマンゲイン(どれくらい加速度センサーを信頼するか)を計算
  float S = P_roll[0][0] + R_angle;
  float K[2];
  K[0] = P_roll[0][0] / S;
  K[1] = P_roll[1][0] / S;

  // 観測値と予測値の差(イノベーション)で補正
  float y = newAngle - kalman_angle_roll;
  kalman_angle_roll += K[0] * y;
  kalman_bias_roll  += K[1] * y;

  // 誤差共分散行列の更新
  P_roll[0][0] -= K[0] * P_roll[0][0];
  P_roll[0][1] -= K[0] * P_roll[0][1];
  P_roll[1][0] -= K[1] * P_roll[0][0];
  P_roll[1][1] -= K[1] * P_roll[0][1];

  return kalman_angle_roll;
}

unsigned long prevTime = 0;

void setup() {
  Serial.begin(115200);
  Wire.begin();
  mpu.initialize();
  prevTime = micros();
}

void loop() {
  int16_t ax, ay, az, gx, gy, gz;
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

  // 経過時間をマイクロ秒で計算
  unsigned long now = micros();
  float dt = (now - prevTime) / 1000000.0; // 秒に変換
  prevTime = now;

  // g単位に変換
  float ax_g = ax / 16384.0;
  float ay_g = ay / 16384.0;
  float az_g = az / 16384.0;

  // deg/s単位に変換(デフォルトレンジ±250deg/s → LSB = 131.0)
  float gx_dps = gx / 131.0;

  // 加速度センサーからのロール角(生の計算値)
  float accel_roll = atan2(-ax_g, az_g) * 180.0 / PI;

  // カルマンフィルタで滑らかなロール角を計算
  float filtered_roll = kalmanUpdate(accel_roll, gx_dps, dt);

  // 比較のため両方を出力
  Serial.print("生の角度: "); Serial.print(accel_roll, 2);
  Serial.print(" deg  カルマン後: "); Serial.print(filtered_roll, 2);
  Serial.println(" deg");

  delay(10);
}

ここが重要です。コードの中で特に押さえてほしいポイントをまとめます。

  • dt(デルタタイム)は正確に計算することが大事。millis()よりmicros()のほうが精度が高い
  • Q_angle・Q_gyro・R_angleの3つのパラメータを調整することでフィルターの挙動が変わる
  • R_angleを大きくすると加速度センサーへの信頼度が下がり、応答がゆっくりになる
  • Q_gyroを大きくするとバイアス推定が速くなるが不安定になりやすい

実際にシリアルプロッターで「生の角度」と「カルマン後」の2つのラインを見比べると、カルマンフィルタの効果が一目でわかりますよ 📈

パラメータ調整のコツ


カルマンフィルタを使いこなすうえで避けて通れないのがパラメータ調整です。最初は難しく感じるかもしれませんが、こんな方針で調整すると迷いにくいです。

  • まずR_angle(観測ノイズ)を調整してみる。大きくすると応答が遅く安定、小さくすると速く反応するが揺れる
  • Q_angleはシステムの予測精度。基本的にはあまり触らなくてOK(0.001前後で試す)
  • Q_gyroはジャイロのドリフト速度に関係。長時間使ってドリフトが気になるなら大きくしてみる
  • 静止状態と動作状態の両方でテストして、ちょうど良いバランスを探す

応用アイデア:どんなものに使える?

MPU-6050+カルマンフィルタを使った傾き検知は、さまざまなプロジェクトに応用できます。

  • 🤖 倒立振子ロボット: リアルタイムで傾きを検知し、モーターでバランスを取る
  • 🚗 自動水平補正カメラ台: 台が傾いてもサーボモーターで自動補正
  • 📦 輸送物の衝撃記録: 物流中の衝撃・転倒をSDカードにログ
  • 🎮 傾きコントローラー: Arduinoの傾きでゲームを操作
  • 🔔 転倒検知アラーム: 高齢者の転倒・バイクの倒れを検知して通知

「まずはシリアルモニターで値を見るだけ」から始めて、慣れてきたら応用プロジェクトに挑戦してみてください!

よくあるトラブルと対処法

MPU-6050を使い始めると、よく遭遇するトラブルがあります。事前に知っておくとスムーズです。

Q: シリアルモニターに「接続失敗」と出る

→ SDA/SCLのピン接続を確認。Arduino UNOではSDA=A4、SCL=A5です。I2Cスキャナースケッチで0x68が検出されるか確認するのが確実です。

Q: 値が全部0になる

mpu.initialize()が正常に動いていない可能性あり。5V電源が安定しているか確認。ブレッドボードの接触不良も多い原因です。

Q: 角度値がガタガタで使えない

→ 今回紹介したカルマンフィルタを適用してください。それでも改善しない場合はR_angleの値を大きくしてみましょう。

Q: 静止状態なのに角度が徐々にズレていく

→ ジャイロのドリフトです。カルマンフィルタが機能していれば自動補正されますが、Q_gyroを少し大きくすると改善することがあります。

まとめ

今回はMPU-6050を使った傾き・振動検知の方法を、回路配線からカルマンフィルタの実装まで一気に解説しました。

学んだことをまとめると:

  • MPU-6050はI2C接続で加速度・ジャイロを同時に取得できる高コスパなセンサー
  • 加速度センサーはノイズに弱く、ジャイロはドリフトが起きる。それぞれ弱点がある
  • カルマンフィルタは2つのセンサーを組み合わせて、安定した角度データを得られる強力な手法
  • パラメータ(Q・R)の調整で応答速度と安定性をバランスよく設定できる

最初はカルマンフィルタのコードが難しく見えるかもしれません。でも「予測して→補正する」という2ステップの繰り返しだと思うと、だんだんつかめてくるはずです。

まずは配線して生データを見るところから始めて、ステップアップしていきましょう!ぜひ手を動かして試してみてください 🚀

📡 Arduinoをもっと深く学ぼう!

Arduino・ラズパイ・ロボットプログラミングを体系的に学びたい方へ。おすすめのUdemyコースや電子部品もまとめています。

Arduinoロボット入門のおすすめコース・部品を見る →

📚 関連商品・おすすめ書籍

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

もしも

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

初心者に定番のPython入門書

Amazonで見る

Arduinoをはじめよう 第4版 (Make: PROJECTS)

もしも

Arduinoをはじめよう 第4版 (Make: PROJECTS)

Arduino公式推薦の定番入門書

Amazonで見る

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

もしも

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

電子工作とプログラミングを同時に学べる

Amazonで見る

※本記事にはアフィリエイトリンクが含まれます。

ABOUT ME
やまちゃん
これまで学生と社会人を合わせて5000人以上にプログラミング学習を指導。 ゼロからイチをわかりやすく解説する専門家として活動しており、本業ではArduinoを用いたIoT開発とロボットプログラミングが専門。 Pythonを用いたアプリ開発、ウェブアプリケーションの開発で業務の効率化をサポートしています。