Arduino 加速度&ジャイロセンサーを確認する

近年スマートフォンやドローン市場が急激に拡大している中、それらのデバイスで活躍してるのが様々なセンサー。今日はその中の一つ加速度センサーとジャイロセンサーが一体となった「MPU-6050」をI2Cで通信して試しに動作確認したいと思います。

用意するもの


  1. Arduino nano
  2. 加速度&ジャイロセンサー(GY-521)
    MPU-6050 使用 3軸ジャイロスコープ・3軸加速度センサー モジュール
  3. ジャンパー
  4. 場合によってはプルアップ抵抗1kΩ程度
  5. レベルシフタ(必要理由は後述)
    8ビットロジックレベル双方向コンバータモジュール

I2Cとは


2線からなる双方向の通信インターフェイス。

詳細はwikiなどにあるので省略してプログラムに対して波形の動きを説明します。

I2C

図が小さいかもの時はここ

あるアドレスにデータを書き込んだ時の動作。
(下項目プログラムのsetup()内の処理が近いかも)

Wire.beginTransmission();でArduinoをマスタとして接続先のデバイスアドレスを設定。最初のWire.write();でデバイスのレジスタアドレスを指定し、次のWire.write();で書き込みデータを指定。最後にWire.endTransmission();が実行されてI2C通信が開始されます。

グラフの前後にあるSTARTとSTOPは、I2Cの開始と終了を意味しています。SCLがHighでSDAがLowに立ち下がった時、通信開始の合図となります。STOPはSCLがHighでSDAがHighに立ち上がった時となります。この条件によりデータの動作はSCLがLowの時にSDAはHighに立ち上がり又はLowに立下りすることによって誤STOP/STARTしないようになってるようです。

グラフ中央のスレーブ送信、ACKはスレーブ側のデバイスが送られてきたデータを認識できているか判別しています。スレーブ側がデータを確認したらクロック9回目でLowとします。もし、ACKがHighの場合はマスタ側にエラーとして認識されます。busyはスレーブが処理か何らかの影響で返事が返せない時、スレーブ側がSCLをLowにすることでSCLはマスタ側からHighにすることができず一時停止状態になります。

実際に安価オシロで取得した波形です。

2018-01-07 (1)

上がセンサー側、下がArduino側。

センサー側で読み取ると、最初の7bitが1101000=0x68となりbit8はライト要求なのでLow。そして、9クロックのACKはSDAがLowになっているので受信OKと言うこと。

次はまた7bitのレジスタアドレス(波形はPWR_MGMT_1を選択)を送りbit8はライトなのでLowで9クロックのACKもOK。

残りは書き込むデータ0x00。こちらもACKがLowで問題なし。

MPU-6050センサーモジュールについて


TDKグループのInvenSense社製の6軸モーショントラッキングセンサー。

同シリーズにMPU-6000があります。それにはSPIがあるのですが、今回使用するMPU-6050には前回確認したSPIインターフェイスがありません!!

と、プログラムの流用ができないことは残念ですが、I2Cを使用する丁度いい機会かな?

このデバイスを使う上で注意する点は動作電圧が5V不可。入れたら壊れます。
ただ、今回使う「GY-521」は電源入力にLDOレギュレーターが実装されているためVCCについては5V供給で問題ありません。問題なのは信号ラインでデータシートの絶対最大定格にもあるように”-0.5V~VCC+0.5V”が限度でLDO3.3Vで動作しているので信号電圧は最大で3.8Vまでかと思います。レベルシフタICなどを入れてArduinoの信号電圧を3.3Vへ落としてあげる必要があります。まあ、参考に他サイト見たら直でも大丈夫そうなこと書いてあったけど自己責任。

  • レベルシフタICについて:TI電圧レベル変換
    とりあえず持っておくと便利かもしれません。

I2Cの通信速度は通常100kHz、ファストモードで400kHz

GY-521ピンアサイン

  1. VCC:3.3V
  2. GND
  3. SCL:I2Cクロック
  4. SDA:I2Cデータ
  5. XDA:オプション。I2Cデータ。
    内部でSDAをバイパスして外部のセンサ類に接続する(Pass-Through Mode)
  6. XCL:オプション。I2Cクロック。同上
  7. ADO:I2CスレーブアドレスLSB
  8. INT:割り込み用デジタル信号出力

詳しいことはデータシート。それとレジスタマップを。
https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/

MPU-6050のモジュール回路図
http://playground.arduino.cc/uploads/Main/MPU6050-V1-SCH.jpg

回路構成を考える


上項でSPIが使えない事態になったので2線式のI2Cを使用します。

Arduinoとセンサーの電圧が異なるためレベルシフタICを間に入れます。

回路2

プログラミングをする


プログラムはWireを流用します。

参考になりそうなのが結構あります。
Arduino.cc MPU-6050 Accelerometer + Gyro

#include <Wire.h>

#define MPU_ADD     104   //0x68
#define PWR_MGMT_1  107   //0x6B
#define ACCEL_XOUT  59    //0x3B

int Accel_X = 0;
int Accel_Y = 0;
int Accel_Z = 0;
int Temp    = 0;
int Gyro_X  = 0;
int Gyro_Y  = 0;
int Gyro_Z  = 0;

void setup() {
  Serial.begin(9600);
  while (!Serial)

  Serial.println("/------MPU-6050 setting------/");

  
  Wire.begin();
  Wire.setClock(3400000);
  /***************************MPU-6050初期設定***************************
   MPU-6050
   address : 1101000 (AD0=0)
  **********************************************************************/
  Wire.beginTransmission(MPU_ADD);  // 接続先アドレス
  /**********************************************************************
   REG(0x68) PWR_MGMT_1 : パワーモード及びクロック設定
   ※電源投入時、スリープモードになっている。
   データ:0x00
   温度センサー : 0 (Enable)
   CLKSEL : 0 (Internal 8MHz oscillator)
  **********************************************************************/
  Wire.write(PWR_MGMT_1);           // PWR_MGMT_1 レジスタ選択
  Wire.write(0x00);                 // PWR_MGMT_1 データ 0x00
  Wire.endTransmission(true);       // 終了設定(stopリクエストあり)
                                    // ここのタイミングでデータ送る
  /**************************MPU-6050設定終了***************************/
  
  Serial.println("/-----START Arduino nano-----/");
}

void loop() {
  /**********************************************************************
  MPU-6050
  加速度レジスタ:XOUT(0x3B,0x3C),YOUT(0x3D,0x3E),ZOUT(0x3F,0x40)
  温度レジスタ:TEMP_OUT(0x41,0x42)
  ジャイロレジスタ:G_XOUT(0x43,0x44),G_YOUT(0x45,0x46),G_ZOUT(0x47,0x48)
  **********************************************************************/
  
  Wire.beginTransmission(MPU_ADD);      // 接続先アドレス
  Wire.write(ACCEL_XOUT);               // ACCEL_XOUT レジスタ選択
  Wire.endTransmission(false);          // リスタートリクエスト
  
  Wire.requestFrom(MPU_ADD,14,true);   // ACCEL_XOUT(0x3B)から14byte(0x40)まで データ送信要求
  Accel_X = Wire.read()<<8|Wire.read(); // 加速度 X軸 (2byteが分割されてるので8bitシフトして格納)
  Accel_Y = Wire.read()<<8|Wire.read(); // 14byte送られ続ける
  Accel_Z = Wire.read()<<8|Wire.read();
  Temp = Wire.read()<<8|Wire.read();
  Gyro_X = Wire.read()<<8|Wire.read();
  Gyro_Y = Wire.read()<<8|Wire.read();
  Gyro_Z = Wire.read()<<8|Wire.read();

  /**********************************************************************
  シリアル表示
  **********************************************************************/

  Serial.print("K: ");
  Serial.print(Accel_X);
  Serial.print(" : ");
  Serial.print(Accel_Y);
  Serial.print(" : ");
  Serial.print(Accel_Z);
  Serial.print(", G: ");
  Serial.print(Gyro_X/131);
  Serial.print(" : ");
  Serial.print(Gyro_Y/131);
  Serial.print(" : ");
  Serial.print(Gyro_Z/131);
  Serial.print(", T: ");
  Serial.print(Temp / 340.00 + 36.53);
  Serial.print("        \r");

  delay(100);
}

動作確認


取り出した値をそのままグラフにしてみました。

センサーをX,Y,Zの順で360度回転させたものです。(手ぶれすごい

2018-01-07

とりあえず動いてるみたい??Z軸がよくわからん。

I2Cが動いてることは確かみたいなので今回はこれで終了にします。

角度算出が出来るようなので次回以降にでも記事にしたいと思います。

以下やってしまった事


一度トラブルがあったのでメモっておきます。

今回センサーを扱うということでフラットケーブルをArduinoとセンサー間に使いました。
40本 ジャンパワイヤブ レッドボードワイヤ

信号の並びはVCC、GND、”SCL”、”SDA”の4本。

これだと動かないのです。

なぜなら、SCLとSDAが隣り合わせになっているので。
特にSCLはクロックで信号を送る際、一定間隔で立ち上がり立ち下がりしています。
そうすると、配線上から微弱ながらもコモンモードノイズが発生し外部に影響を与えます。

つまり、SDAがそのノイズに影響され送られるべき信号に歪みが生じセンサー側が読み取れないことがあるのです。

通常の1本線ジャンパーであればノイズが微弱なので距離を離しながらだったら4本で動くかと思いますが、フラットケーブルを使う場合は信号の並びをVCC、GND、SCL、GND、SDA、GNDとGNDで囲んであげるとシールドの役目を果たしノイズの影響を軽減できます。

参考になるサイト1:実用ノイズ対策技術

参考になるサイト2:村田製作所 ノイズ対策 基礎講座

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中