PR

Arduinoで9軸センサー(加速度・ジャイロ・地磁気)を使ってみる

記事内に広告が含まれています。

 Arduinoから9軸センサー(MPU9250/9255)の加速度、角速度、地磁気の値を取得してみました。

 MPU9250モジュール品を入手したつもりだったのですがどうも中身はMPU9255だったみたいです。ただまぁ違いはほとんどないみたいで、ともに3軸の加速度、ジャイロ(角速度)、地磁気が測定できるものです。

 ▼入手した9軸センサーモジュール(MPU9255)▼

 さらっとデータシート見比べただけですが加速度、ジャイロの部分は、6軸センサーのMPU6050と使い方は基本同じようです。MPU6050用に作ったソース(スケッチ)をそのままで使っても、MPU9255から問題無く加速度、ジャイロ値が取得できました。

 ですので今回は地磁気センサーの取得部分をメインに説明したいと思います。記事前半で使い方を、後半で取得した地磁気センサーの値を可視化してます。記事最後に今回使用したスケッチ全文を掲載してます。

*加速度、ジャイロ(MPU6050)についてはここら辺をご覧ください*
 MPU6050の使い方
 加速度から角度の算出
 角速度から角度の算出
 6軸センサーから角度算出

スポンサーリンク

Arduinoとの結線

 MPU9250/9255とArduinoの結線です。

MPU9250 Vcc ⇔ Arduino 3.3V
MPU9250 GND ⇔ Arduini GND
MPU9250 SCL ⇔ Arduino SCL(or A5pin)
MPU9250 SDA ⇔ Arduino SDA(or A4pin)

 I2C通信前提です。スレーブアドレスはAD0「LOW」で「0x68」、AD0「HIGH」で「0x69」となります。

 「who am I」を試したところ、MPU9250であれば「71」が返るはずなのですが、「73」が返ってきました。記事冒頭でも記載しましたが「73」はMPU9255のようですね・・。

スケッチ(ソース)

 抜粋して説明します。スケッチ全体は記事最後に記載してあります。

 加速度センサー、ジャイロ(角速度)部分はMPU6050と使い方は同じなので、地磁気センサーの使い方を中心に説明します。

 まずは地磁気センサーの設定です。

  Wire.beginTransmission(0x68);
  Wire.write(0x37);
  Wire.write(0x02);
  Wire.endTransmission();

  Wire.beginTransmission(0x0C);
  Wire.write(0x0A);
  Wire.write(0x16);
  Wire.endTransmission();

 MPU9250/9255の地磁気センサーは別IC(AK8963)となっているようで、まずはスレーブアドレス「0x68」内にある「INT_PIN_CFG(0x37)」レジスタのバイパスを「ENABLE」 にする必要があります。これで地磁気センサーへアクセスできるようになります。地磁気センサーのスレーブアドレスは「0x0C」になります。

 でスレーブアドレス「0x0C」の「CNTL1(0x0A)」レジスタでモードの設定を行います。下位4bitでモードの設定、5bit目で読取り値の分解能が設定できます。

 分解能は「0」で14bit出力、「1」で16bit出力。モードは連続モードや単発測定モードなどいろいろあります。今回は16bit分解能で、100Hzサンプリングの連続測定モードで設定してます。

 その他、いろいろ機能があるので詳しくはデータシートを見ましょう。最低限の設定はこれだけです。次に地磁気値の読取りです。

▼地磁気センサー値の読取り▼

  Wire.beginTransmission(0x0C);
  Wire.write(0x02);
  Wire.endTransmission();
  Wire.requestFrom(0x0C, 1);

  uint8_t ST1 = Wire.read();
  if (ST1 & 0x01) {
    Wire.beginTransmission(0x0C);
    Wire.write(0x03);
    Wire.endTransmission();
    Wire.requestFrom(0x0C, 7);
    uint8_t i = 0;
    uint8_t buf[7];
    while (Wire.available()) {
      buf[i++] = Wire.read();
    }
    if (!(buf[6] & 0x08)) {
      mx = ((int16_t)buf[1] << 8) | buf[0];
      my = ((int16_t)buf[3] << 8) | buf[2];
      mz = ((int16_t)buf[5] << 8) | buf[4];
    }
  }

 最初に、「ST1(0x02)」レジスタで読出し準備ができているかを確認してます。準備ができていたら地磁気の値を読取ります。

 地磁気の値は「0x03」レジスタから順に6byteで格納されています。が7byte目(ST2レジスタ)まで読み出します。ST2レジスタまで読み込まないとデータ保護が解除されなようです。

 ST2レジスタではステータスが確認できるので値のオーバーフローを確認してます。最後に地磁気の値を変数へ格納します。地磁気の値はx,y,z軸の順番でそれぞれ下位8bitから順番に並んでいます。

 これで地磁気センサー値の取得は完了です。実際に取得できた値を見てみました。

スポンサーリンク

地磁気センサーの取得値を見てみる

 まずはセンサーを水平において、ぐるぐると(2次元で)回転させてみました。

▼2次元結果▼

 x軸とy軸の取得値を散布図でグラフ化してます。思いのほかきれいな円となりました。これだけでも角度(方位)は見てとれますね。ただ原点(円の中心)ずれているのでキャリブレーションは必要です。2次元(水平)でセンサーを使うならこれで十分角度(方位)が算出できそうです。

▼横軸に角度(方位)▼

 横軸を角度(方位)にして、x軸、y軸の地磁気センサー値をそれぞれグラフ化してます。ここではセンサー値をキャリブレーションしてます。正弦曲線になっていて、x軸、y軸の逆正接取れば角度になるのがわかります。

 せっかくの3軸センサーなのでセンサーをぐるんぐるん回して、x、y、zの3軸の値も取ってみました。

▼3次元結果▼

 3軸の値を3次元散布図でグラフ化してます。少しわかり難いですがこちらも思いのほかきれいな球面が出ました。キャリブレーションしていないので原点(球中心)はずれてますけど・・・。球面でセンサー姿勢(角度)が把握できそう。結構遊べそうです。

スポンサーリンク

スケッチ全体

 サンプルスケッチ全文です。地磁気センサーの生値をそのままシリアル出力してます

// 2018/11/04 imo lab.
// https://garchiving.com/

#include "Wire.h"
int16_t mx, my, mz;

void setup() {
  Wire.begin();
  TWBR = 24;
  Serial.begin(115200);
  setupMPU9255();
}

void loop() {
  readCompass();
  Serial.print(mx); Serial.print(",");
  Serial.print(my); Serial.print(",");
  Serial.println(mz);
  delay(10);
}

void readCompass() {
  Wire.beginTransmission(0x0C);
  Wire.write(0x02);
  Wire.endTransmission();
  Wire.requestFrom(0x0C, 1);

  uint8_t ST1 = Wire.read();
  if (ST1 & 0x01) {
    Wire.beginTransmission(0x0C);
    Wire.write(0x03);
    Wire.endTransmission();
    Wire.requestFrom(0x0C, 7);
    uint8_t i = 0;
    uint8_t buf[7];
    while (Wire.available()) {
      buf[i++] = Wire.read();
    }
    if (!(buf[6] & 0x08)) {
      mx = ((int16_t)buf[1] << 8) | buf[0];
      my = ((int16_t)buf[3] << 8) | buf[2];
      mz = ((int16_t)buf[5] << 8) | buf[4];
    }
  }
}

void setupMPU9255() {
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();

  Wire.beginTransmission(0x68);
  Wire.write(0x1A);
  Wire.write(0x05);
  Wire.endTransmission();

  Wire.beginTransmission(0x68);
  Wire.write(0x37);
  Wire.write(0x02);
  Wire.endTransmission();

  Wire.beginTransmission(0x0C);
  Wire.write(0x0A);
  Wire.write(0x16);
  Wire.endTransmission();
  delay(500);
}

スポンサーリンク
スポンサーリンク

コメント

  1. はれ より:

    TWBR = 24;の部分は何をしているんですか?
    ここがコンパイルしたときにエラーになります
    int TWBR = 24;とするとコンパイルできるのですが…

    • imo より:

      はれさん、サイト拝見くださり有難うございます。
      コンパイルはなぜでしょうかね。気になって確認してみましたがこのままでもうまくコンパイルされますね。ボードはUNOでしょうか?
      TWBR=24はI2Cの通信速度(周波数)を125KHz(確かデフォルトは100KHzだったかと思います)に変えているだけですので無くても問題ないと思います。
      intつけてコンパイルが通るということはそもそもTWBRが機能してないですね・・。

  2. kei より:

    このプログラムで得たmxmymzをmagwickfilterに応用すればyaw回転のドリフトをなくすことが出来ますか?

    • imo より:

      keiさん、サイト拝見下さり有難うございます。
      返信遅くなりました。
      Yaw回転のドリフト対策になると思います。

タイトルとURLをコピーしました