【9軸センサ】MPU9250の使い方とArduinoプログラム

ドローンを製作する上で,MPU9250という9軸(加速度,ジャイロ,磁気センサ)のセンサをArduinoを使用して使ってみました.(ちなみに温度も取れます)

使い方を理解するためにも,ライブラリを使用しないで動かしてみました.


  • 接続方法

まず,MPU9250はI2CとSPI通信が選択できますが,今回は,I2Cを選択しました.

I2Cは2端子しかないので接続はとても簡単です. SCLとSDL端子をそれぞれのデバイスのSCLとSDLに接続するだけです.

ただし,今回は,使用電圧が違うので途中にI2Cバス用双方向電圧レベル変換モジュール(PCA)を挟まないといけません.(Arduino Mega:5V,MPU9250:2.4 – 3.6V).

これは,信号の電圧を変換してくれるデバイスです.

回路図は以下の通りです. Arduino Megaの20番ピン(SDA)と,21番ピン(SCL)を使用しました.
「<ー」はArduinoのGNDです.
変換モジュールには,5Vと3.3Vの入力端子があり,5V側と3.3V側があるので注意しましょう.MPUの入力はすべて3.3Vです.


  • データの概要

まず,MPU9250の特徴として,加速度&ジャイロセンサと,地磁気センサは違うスレーブアドレスです.

つまり,2つのI2Cデバイスが1つのパッケージに入っているようなものです. よって,データを読み取るためには,データを2回に分けて取得する必要があります.

この2つを分けて説明していきたいと思います.


  • 加速度とジャイロのデータ取得方法

加速度とジャイロのスレーブアドレスは,0x68 です.

まず,最初に使用するレンジを設定します.

i2cWriteByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x00);//スリープモードを解除
i2cWriteByte(MPU9250_ADDRESS, ACCEL_CONFIG, ACCEL_FS_SEL_16G);//加速度センサの測定レンジの設定
accRange = 16.0;//計算で使用するので,選択したレンジを入力する
i2cWriteByte(MPU9250_ADDRESS, GYRO_CONFIG, GYRO_FS_SEL_2000DPS);//ジャイロセンサの測定レンジの設定
gyroRange = 2000;//計算で使用するので,選択したレンジを入力する

取得したデータは,内部レジスタの0x3bから14バイトに渡って,加速度X,Y,Z,ジャイロX,Y,Zの順で並んでいます.これをi2cReadを使用して読み取ります(この関数は,一番下にあります)

i2cRead(MPU9250_ADDRESS, 0x3b, 14, accGyroTempData); //0x3bから,14バイト分をaccGyroDataにいれる

それぞれのデータは,16ビットですが,8ビットずつに2つに分かれています.よって,この2つの8ビットを足し合わせないといけません.

//Acc
ax = (accGyroTempData[0] << 8) | accGyroTempData[1];//accGyroTempData[0]を左に8シフトし(<<),accGyroTempData[1]を足し合わせる(|)
ay = (accGyroTempData[2] << 8) | accGyroTempData[3];//accGyroTempData[2]を左に8シフトし(<<),accGyroTempData[3]を足し合わせる(|)
az = (accGyroTempData[4] << 8) | accGyroTempData[5];//accGyroTempData[4]を左に8シフトし(<<),accGyroTempData[5]を足し合わせる(|)
//Gyro
gx = (accGyroTempData[8] << 8) | accGyroTempData[9];//accGyroTempData[8]を左に8シフトし(<<),accGyroTempData[9]を足し合わせる(|)
gy = (accGyroTempData[10] << 8) | accGyroTempData[11];//accGyroTempData[10]を左に8シフトし(<<),accGyroTempData[11]を足し合わせる(|)
gz = (accGyroTempData[12] << 8) | accGyroTempData[13];//accGyroTempData[12]を左に8シフトし(<<),accGyroTempData[13]を足し合わせる(|)

そして,単位を変換します.

accX = ax * accRange / 32768.0;//[G]に変換
accY = ay * accRange / 32768.0;//[G]に変換
accZ = az * accRange / 32768.0;//[G]に変換
gyroX = (gx - driftX) * gyroRange / 32768.0;//[deg/s]に変換
gyroY = (gy - driftY) * gyroRange / 32768.0;//[deg/s]に変換
gyroZ = (gz - driftZ) * gyroRange / 32768.0;//[deg/s]に変換


  • 磁気センサのデータ取得方法

磁気センサのスレーブアドレスは,0x0c です.

磁気センサを使用するためには,まず設定が必要です.

i2cWriteByte(MPU9250_ADDRESS, INT_PIN_CFG, 0x02);//bypass mode(磁気センサが使用出来るようになる)
i2cWriteByte(AK8963_ADDRESS, CNTL1, CNTL1_MODE_SEL_100HZ);//磁気センサのAD変換開始

磁気センサは,ST2というレジスタまで読み込まないと次の計測結果が入ってこないようになっています.

//COMPASS////////////////////////////////////////////////////////////////////////////
i2cRead(AK8963_ADDRESS, ST1, 1, &ST1Bit);//読み出し準備ができたか確認
if ((ST1Bit & 0x01)) {
i2cRead(AK8963_ADDRESS, 0x03, 7, magneticData);//7番目の0x09(ST2)まで読まないとデータが更新されない
}

そして,データの単位を変更します.

magX = (mx + 344.0f) / 32768.0f * 4800.0f;//[uT]に変換
magY = (my - 234.0f) / 32768.0f * 4800.0f;//[uT]に変換
magZ = (mz - 410.0f) / 32768.0f * 4800.0f;//[uT]に変換


  • 全体のプログラム

全体のプログラムです.無断転載を禁止します.

あとは,各自でジャイロのドリフトを補正したり,クォータニオン(おすすめ)などを使って工夫してみてください. ジャイロと加速度センサのレンジは変更できます.

//akiracing.com (無断転載を禁ず)
#include <Wire.h>
/////////////////////////MPU9250////////////////////////////
#define MPU9250_ADDRESS 0x68//I2CでのMPU9250のスレーブアドレス
#define PWR_MGMT_1 0x6b//電源管理のアドレス,スリープモード解除用
#define INT_PIN_CFG 0x37//磁気センサのバイパスモード設定用のアドレス
#define ACCEL_CONFIG 0x1c//加速度センサ設定用のアドレス
#define ACCEL_FS_SEL_2G 0x00//加速度センサのレンジ(2G)
#define ACCEL_FS_SEL_4G 0x08//加速度センサのレンジ(4G)
#define ACCEL_FS_SEL_8G 0x10//加速度センサのレンジ(8G)
#define ACCEL_FS_SEL_16G 0x18//加速度センサのレンジ(16G)
#define GYRO_CONFIG 0x1b//ジャイロセンサ設定用のアドレス
#define GYRO_FS_SEL_250DPS 0x00//ジャイロセンサのレンジ(250DPS)
#define GYRO_FS_SEL_500DPS 0x08//ジャイロセンサのレンジ(500DPS)
#define GYRO_FS_SEL_1000DPS 0x10//ジャイロセンサのレンジ(1000DPS)
#define GYRO_FS_SEL_2000DPS 0x18//ジャイロセンサのレンジ(2000DPS)
#define AK8963_ADDRESS 0x0c//磁気センサのスレーブアドレス
#define CNTL1 0x0a//磁気センサ設定用のアドレス
#define CNTL1_MODE_SEL_8HZ 0x12//磁気センサの出力周期(8Hz)
#define CNTL1_MODE_SEL_100HZ 0x16//磁気センサの出力周期(100Hz)
#define ST1 0x02//データ読み込み用フラッグのアドレス
///////////////////////////////////////////////////////////
volatile float accRange;//計算で使用するので,選択したレンジを入力する定数
volatile float gyroRange;//計算で使用するので,選択したレンジを入力する定数
volatile uint8_t accGyroTempData[14];//センサからのデータ格納用配列
volatile uint8_t magneticData[7];//センサからのデータ格納用配列
volatile uint8_t ST1Bit;//磁気センサのフラッグ
volatile int16_t ax = 0;//16bitの出力データ
volatile int16_t ay = 0;//16bitの出力データ
volatile int16_t az = 0;//16bitの出力データ
volatile int16_t gx = 0;//16bitの出力データ
volatile int16_t gy = 0;//16bitの出力データ
volatile int16_t gz = 0;//16bitの出力データ
volatile int16_t tempMPU9250Raw = 0;//16bitの出力データ
volatile int16_t mx = 0;//16bitの出力データ
volatile int16_t my = 0;//16bitの出力データ
volatile int16_t mz = 0;//16bitの出力データ
volatile float accX = 0;//加速度センサから求めた重力加速度
volatile float accY = 0;//加速度センサから求めた重力加速度
volatile float accZ = 0;//加速度センサから求めた重力加速度
volatile float gyroX = 0;//ジャイロセンサから求めた角速度
volatile float gyroY = 0;//ジャイロセンサから求めた角速度
volatile float gyroZ = 0;//ジャイロセンサから求めた角速度
volatile float tempMPU9250 = 0;//MPU9250の温度
volatile float magX = 0;//磁気センサから求めたuT
volatile float magY = 0;//磁気センサから求めたuT
volatile float magZ = 0;//磁気センサから求めたuT
void setup() {
Wire.begin();//I2C通信を開始する
Serial.begin(9600);//シリアル通信を開始する
i2cWriteByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x00);//スリープモードを解除
i2cWriteByte(MPU9250_ADDRESS, ACCEL_CONFIG, ACCEL_FS_SEL_16G);//加速度センサの測定レンジの設定
accRange = 16.0;//計算で使用するので,選択したレンジを入力する
i2cWriteByte(MPU9250_ADDRESS, GYRO_CONFIG, GYRO_FS_SEL_2000DPS);//ジャイロセンサの測定レンジの設定
gyroRange = 2000.0;//計算で使用するので,選択したレンジを入力する
i2cWriteByte(MPU9250_ADDRESS, INT_PIN_CFG, 0x02);//bypass mode(磁気センサが使用出来るようになる)
i2cWriteByte(AK8963_ADDRESS, CNTL1, CNTL1_MODE_SEL_100HZ);//磁気センサのAD変換開始
}
void loop() {
MPU9250();
Serial.print("ax: ");
Serial.print(accX);
Serial.print("\t");
Serial.print("ay: ");
Serial.print(accY);
Serial.print("\t");
Serial.print("az: ");
Serial.print(accZ);
Serial.print("\t");
Serial.print("gx: ");
Serial.print(gyroX);
Serial.print("\t");
Serial.print("gy: ");
Serial.print(gyroY);
Serial.print("\t");
Serial.print("gz: ");
Serial.print(gyroZ);
Serial.print("\t");
Serial.print("mx: ");
Serial.print(magX);
Serial.print("\t");
Serial.print("my: ");
Serial.print(magY);
Serial.print("\t");
Serial.print("mz: ");
Serial.print(magZ);
Serial.print("\t");
Serial.print("temp: ");
Serial.print(tempMPU9250);
Serial.println("\t");
}
void MPU9250() {
//akiracing.com (無断転載を禁ず)
//ACC&GRYO///////////////////////////////////////////////////////////////////////////
i2cRead(MPU9250_ADDRESS, 0x3b, 14, accGyroTempData); //0x3bから,14バイト分をaccGyroDataにいれる
//COMPASS////////////////////////////////////////////////////////////////////////////
i2cRead(AK8963_ADDRESS, ST1, 1, &ST1Bit);//読み出し準備ができたか確認
if ((ST1Bit & 0x01)) {
i2cRead(AK8963_ADDRESS, 0x03, 7, magneticData);//7番目の0x09(ST2)まで読まないとデータが更新されない
}
//Acc
ax = (accGyroTempData[0] << 8) | accGyroTempData[1];//accGyroTempData[0]を左に8シフトし(<<),accGyroTempData[1]を足し合わせる(|)
ay = (accGyroTempData[2] << 8) | accGyroTempData[3];//accGyroTempData[2]を左に8シフトし(<<),accGyroTempData[3]を足し合わせる(|)
az = (accGyroTempData[4] << 8) | accGyroTempData[5];//accGyroTempData[4]を左に8シフトし(<<),accGyroTempData[5]を足し合わせる(|)
//Gyro
gx = (accGyroTempData[8] << 8) | accGyroTempData[9];//accGyroTempData[8]を左に8シフトし(<<),accGyroTempData[9]を足し合わせる(|)
gy = (accGyroTempData[10] << 8) | accGyroTempData[11];//accGyroTempData[10]を左に8シフトし(<<),accGyroTempData[11]を足し合わせる(|)
gz = (accGyroTempData[12] << 8) | accGyroTempData[13];//accGyroTempData[12]を左に8シフトし(<<),accGyroTempData[13]を足し合わせる(|)
//Temp
tempMPU9250Raw = (accGyroTempData[6] << 8) | accGyroTempData[7];//accGyroTempData[6]を左に8シフトし(<<),accGyroTempData[7]を足し合わせる(|)
//Magneto
mx = (magneticData[3] << 8) | magneticData[2];//センサの軸が違うので順番が加速度とジャイロと違う
my = (magneticData[1] << 8) | magneticData[0];//magneticData[1]を左に8シフトし(<<),magneticData[0]を足し合わせる(|)
mz = -((magneticData[5] << 8) | magneticData[4]);//加速度,ジャイロセンサと軸の向きが逆なので-を掛ける
accX = ax * accRange / 32768.0;//[G]に変換
accY = ay * accRange / 32768.0;//[G]に変換
accZ = az * accRange / 32768.0;//[G]に変換
gyroX = gx * gyroRange / 32768.0;//[deg/s]に変換
gyroY = gy * gyroRange / 32768.0;//[deg/s]に変換
gyroZ = gz * gyroRange / 32768.0;//[deg/s]に変換
tempMPU9250 = ((tempMPU9250Raw - 0.0) / 333.87) + 21.0f;
//MPU-9250 Product Specification Revision 1.0のP12の値と,
//MPU-9250Register Map and Descriptions Revision 1.4のP33の式を使用
magX = mx / 32768.0f * 4800.0f;//[uT]に変換
magY = my / 32768.0f * 4800.0f;//[uT]に変換
magZ = mz / 32768.0f * 4800.0f;//[uT]に変換
/*magX = (mx + 344.0f) / 32768.0f * 4921.0f * 10.0f;//[mGauss]に変換
magY = (my - 234.0f) / 32768.0f * 4921.0f * 10.0f;//[mGauss]に変換
magZ = (mz - 410.0f) / 32768.0f * 4921.0f * 10.0f;//[mGauss]に変換*/
}
void i2cRead(uint8_t Address, uint8_t Register, uint8_t NBytes, volatile uint8_t* Data) {//指定したアドレスのデータを読む関数
Wire.beginTransmission(Address);//指定したアドレスと通信を始める
Wire.write(Register);//レジスタを書き込む
Wire.endTransmission();//通信を終了する
Wire.requestFrom(Address, NBytes);//スレーブからNByteのデータを要求する
uint8_t index = 0;
while (Wire.available()) {
Data[index++] = Wire.read();//データを読み込む
}
}
void i2cWriteByte(uint8_t Address, uint8_t Register, volatile uint8_t Data) {//指定したアドレスにデータを書き込む関数
Wire.beginTransmission(Address);//指定したアドレスと通信を始める
Wire.write(Register);//指定するレジスタを書き込む
Wire.write(Data);//データを書き込む
Wire.endTransmission();//通信を終了する
}

27件のコメント

  1. こちらのプログラムのコードを使用させてもらえないでしょうか?
    MPU9250の使用のために公開されている各種ライブラリを試したのですが、どうしてもこちらのサイトに掲載されているコードしか動かず、非常に四苦八苦しています。
    今回は商用での利用も考えているため、使用させていただくにあたり何らかの条件がありましたら、
    可能な限りの対応をさせていただきます。
    検討よろしくお願いします。

    1. 具体的にどのような用途でしょうか?
      安全に関わるものへの使用は避けて頂きたいです。

      1. 返答いただいたのに2週間経過してまして申し訳ありません。

        安全にかかわるようなことではなく、iotの用途で3軸の合成加速度を取得して
        加速度の有無で装置が動作しているかしていないかを取得する用途です。

        例えば、プレス加工機が動いている際に、加速度があると動作しているが、加速度がほとんどないと動作を止めていることを感知するなどです。

        なぜか既存ライブラリだとうまく動かず、ライブラリを用いないakira様のだと動くので、使わせてもらえると幸いです。
        こちらのサイトから引用させてもらったとの趣旨はソースコード上に必ず残すようにしますので、検討いただけると幸いです。
        よろしくお願いします。

        1. 分かりました。
          誤動作を起こす可能性もあるので気をつけてください。

          使って頂いて大丈夫です。

          1. 許可いただきありがとうございます。
            自己責任かつ、転載元をソースに記載したまま使用させてもらいます。
            いいもの作れるように頑張ります。

  2. 初めまして
    ドローンを製作している者です.

    magX = mx / 32768.0f * 4800.0f; //[uT]に変換

    の32768.0f * 4800.0f 部分で、32768は16bitの変換についてだと思うのですが、4800の部分はどのようなものでしょうか.AK8963の概要(p.2)に測定レンジ4900μTとあるのですが関係があるのでしょうか.
    回答していただけると嬉しいです.

      1. 回答ありごとうございます。
        そのシートに書いてあったのですね、見落としてました。初歩的なものへの対応ありがとうございます。
        プログラムに詳しく説明が付いていて理解しやすく書かれていてとても読みやすいです。これからも頑張ってください。

  3. はじめまして
    愛知県の大学生です

    講義でmpu-9250の動作確認をするためにこちらのコードを使わさせていただきたいです。

  4. 趣味で釣りをしております。
    これをボートの方位センサーに使用できますか?
    色々なセンサーを試しているのですが磁方位誤差が数度もあり、また少しのピッチやロールに対しても誤差が大きくなって使い物になりませんでした。
    このMPU9250で方位センサーとして使用できますでしょうか?
    趣旨とかけ離れた質問で申し訳ございません。

    1. 数分間でしたら磁気なしでも誤差なく使えますが、釣りでしたら長時間使うと思うので少しずつ角度がズレてしまいます。なので、磁気センサは必須になります。

      磁気センサと加速度、ジャイロを使うと1番正確な正確な角度はでます。

      1. 早速ご回答頂きましてありがとうございます。
        今まで9軸で色々試しましたが良い結果にならず苦労しています。
        もう少し頑張ってみます。

  5. はじめまして。
    現在大学院生で、加速度センサを用いて様々な運動の動きを解析するための
    研究をしております。その研究のために、こちらのコードを使わせていただいてよろしいでしょうか。
    よろしくお願いいたします。

    1. 使っても大丈夫です.
      正確な値が出力されているかは,ご自身でご確認ください.

  6. いつも参考にさせていただいております。(以前、一度問い合わせをさせていただいたことがあります。)
    私もMPU9250を自身のコードで使っていますが、静止体につけたジャイロの値に不定期(数秒に1回)に異常値が
    生じます。正常値は±1.0度/秒以下で推移しますがたまに ±15度/秒となります。
    ちなみに新品でも発生しております。こういった経験はお持ちでしょうか?ちなみにジャイロのフルスケ-ルをけても異常値が吐き出されます。
    よろしくご教示いただきますと幸いです。

    1. 異常値が出たことは今のところないです.
      新品でも出るようならば,プログラムか回路の問題ではないでしょうか.
      他に実行されているプログラムが影響していたりするのかもしれません.

  7. arduino uno r3を用いて、同様のセンサーを用いて加速度の取得を行おうとしています。しかし、付属の説明書や他のウェブサイトを参照しても、接続方法が分からず、動かすことができず、手を焼いております。そこで、お聞きしたいのですが、

    arduino uno r3を使う場合には、 どのように配線すればよいのか、図などを用いてご教示いただけませんでしょうか?

    また、その配線にて動作を確認する際に、本サイトにて公開されているコードを使用させていただけませんでしょうか?

    1. 使用については大丈夫です.
      正確な値が出力されているかは,ご自身でご確認ください.

      「MPU9250 配線」などで検索すれば,たくさん結果が出てくると思うので,そちらを参考にしてみてください.
      基本的には,SPIとI2Cという接続方式が使えます.

  8. こちらの方のプログラムを使用させて貰えないでしょうか?
    研究にて、9軸センサーを使用しており、なかなか苦労していたところこのプログラムを見つけ研究で試してみようと思ったからです。使用許可が降りた際、参考文献としても掲載致しますのでよろしくお願いします。

    1. 使って大丈夫です。
      精度などについては、ご自身で御確認ください。

    2. こんにちは。

      某高専機械科の者です。
      自主実験において、
      サーボモータの制御用に使用したいです。ご承諾お願いします。

  9. 某高専機械科の者です。
    自主実験において、
    サーボモータの制御用に使用したいです。ご承諾いただきたいです。

  10. こちらのプログラムを使用させてもらえないでしょうか?
    高校生なのですが個人研究でMPU9250を使用していまして、既存のArduinoライブラリだと正確に動作しませんでした。ライブラリなしのこのプログラムなら正確に動作するのではないかと思い、一部参考にさせていただきたいです。よろしくお願いします。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です