2017年7月29日土曜日

ArduinoでMPU9250(加速度センサ、磁気センサ)を使う方法


欲しい値を取得するのに思っていたよりも時間がかかったので、メモを残します。

目的

9軸センサ MPU9250で加速度と磁気を取得したい。

使ったもの

Arduino


aliexpressで買いました。
秋月電子でも買えます。
今回はNANOを使いましたが、ProMicroもお勧めです。

MPU9250


9軸センサモジュールです。
加速度と角速度が取れる6軸線センサMPU6500と、3軸磁気センサAK8963が合体したモジュールです。
amazonスイッチサイエンスaliexpressなどで買えます。

ブレッドボード


部品を配置するのに使いました。
秋月電子やスイッチサイエンスなど、電子部品取扱店で買えます。

ジャンパワイヤ


部品を配線するのに使いました。
秋月電子やスイッチサイエンスなど、電子部品取扱店で買えます。

PC + ArduinoIDE

Arduinoのプログラムと、値の確認に使いました。
ArduinoIDEは下記のページからダウンロードできます。

Download the Arduino IDE

配線

このように配線しました。

Arduino (Nano) MPU9250
5V VCC
SDA(A4) SDA
SCL(A5) SCL
GND GND


NanoやUno以外のArduinoだとI2Cに割り当てられているピンが異なる場合があるので、wireライブラリの説明ページでピンを確認してください。

ライブラリの準備

使いやすそうなライブラリが見当たらなかったので、自分が最低限欲しい情報だけ取り出すライブラリを作りました。


ArduinoIDEのライブラリマネージャーを使ってインストールできます。


9250とかで検索すると出てくると思います。


実行

ライブラリをインストールできたらサンプルプログラムが使えるので、それを開いてArduinoに書き込みます。
File -> Examples -> MPU9250_asukiaaa -> getData


シリアルモニタを115200bpsで開くと、値を確認できます。


自分の磁気センサはXが最大110、最低10、Yが最大120、最低20位だったので、Xに対して-60、Yに対して-70のoffsetを設定すると、水平方向の磁気がそれっぽく取れるようになりました。
mySensor.magXOffset = -60;
mySensor.magYOffset = -70;

モジュールによってふさわしいoffsetの値は多分異なると思うので、複数のハードに対して同じプログラムを動かしたい場合は、offsetの値をNANDメモリなどに値を書き込んで実行時に読みだすなどの仕組みが必要になりそうです。

まとめ

Arduinoで加速度センサと磁気センサの出力を取得できました。
磁気センサの値はoffsetでそれらしい値に補正できましたが、別のMPU9250モジュールを利用する場合はoffsetの値を変える必要があるので、プログラムを使い回すときは注意が必要そうです。

共有する情報は以上です。

参考

AK8963
SparkFun_MPU-9250
jrowberg/i2cdevlib/Arduino/AK8963

22 件のコメント :

moss さんのコメント...

はじめまして、マイコン初心者の者です。
ESP32-DevKitC のマイコンと 私も同じMPU9250 センサーを買いました。
MPU9250_asukiaaa のライブラリを使わせていただいています。ありがとうございます。
Arduino 1.8.5 IDEで コードを送ったのですが、 センサーのデータが取得できませんでした。
これはやっぱり ESP32-DevKitC は別の製品だからでしょうか?

もし、よろしければ、どの部分をどのように変更すればデータが取得できるかヒントを教えていただけないでしょうか?
よろしくお願いいたします。

Asuki Kono さんのコメント...

自分もESP32の開発基板でMPU9250の値を取得したことがあるので、多分動くと思いますよ。
動かない原因としては、下記の点が気になります。

- SDA、SCLピンの指定が合っているか
- setWireでi2c接続に必要なWireをライブラリに渡しているか
- MPU9250が動いているか


# SDA、SCLピンの指定が合っているか
ESP32はいくつかのピンをI2Cに割り当てられますが、フォーラムの情報によると、34-39は入力だけなので、それ以外のピンを割り当てる必要があるようです。
Wire.beginに渡す第一引数がSDA、第二引数がSCLです。

Wire.begin(26, 25);


# setWireでi2c接続に必要なWireをライブラリに渡しているか
自分の作ったライブラリは、i2cに必要なクラスを渡す仕組みになっています。
下記のような記述で、Wireを設定できていますか?

mySensor.setWire(&Wire);


# MPU9250が動いているか
MPU9250が動いていないとライブラリは使えません。
こちらこちらのプログラムを利用して、I2Cデバイスのスキャンをして、MPU9250が動いていることを確認してみるのはいかがでしょう?
その場合も回路に合わせてSDA, SCLのピンを合わせる必要があります。
これでも動作を確認できない場合、お使いのMPU9250モジュールが壊れている可能性があります。


いかがでしょう?
お役に立てれば嬉しいです。

moss さんのコメント...

asuki さん

動きました!数値でました!!本当にありがとうございます!!連絡が遅くなってしまい申し訳ありません。
先日、丸一日やってうまくいかなくて本当に苦労しました。おかげさまで一発でうまく行きまして本当に感謝です。
また、貴サイトで勉強させていただきます。すごく親切に教えていただきまして、ありがとうございました。

Asuki Kono さんのコメント...

期待通りに動いたようで良かったです。
他の記事も何かの参考になれば嬉しいです。

mangoooooou さんのコメント...

初めまして。すぐ、実験できとてもよいライブラリです。助かりました。
設定を自分で細かくしたいと思い、頑張ってみたのですが、スレーブアドレスが0x68以外見つかりません(本当はak8963のアドレス0x0Cもほしい)。mpu9250側で何か設定しないといけないのでしょうか?

Asuki Kono さんのコメント...

AK8963を使う場合は、beginMag関数(ソースコード参照)で行っているように、MPU9250に対してAK8963を起動させるための信号を送る必要があります。
サンプルプログラムでmagXなどの磁気センサに関する情報が取得できているのであれば、起動のための信号を送ればAK8963は動くと思います。
上記のサンプルプログラムの出力は、どのようになっていますか?

mangoooooou さんのコメント...

MPU9250の0x37に0x02を送ったところ、解決しました。お騒がせしました。すみません。

Asuki Kono さんのコメント...

解決されたようで良かったです。
謝罪されると罪悪感を感じるので。謝罪よりも感謝していただける方が、個人的に嬉しいです。

mangoooooou さんのコメント...

丁寧な返信とてもありがたいです。
現在、電磁石の磁界を調べることを目的に実験しているのですが、その前段階として、センサーの感度調整をしたいのですが、地磁気ベクトルのノルムは一定であるはずなのですが、一定でないのはどのように調整するとよいと思いますか?
固定につかう、ねじ自体にも磁性が多少あったりするので、オフセットと感度両方を調整する方法を考えあぐねています。

mangoooooou さんのコメント...

磁石を近づけて大きく変動させた後(オーバーフローはさせない程度)、磁石を離すと元のデータとは違う値で安定するのですが(650->600みたいに)どう思いますか?

mangoooooou さんのコメント...

Mpu9250のxyzの表示方向とak8963のレジスタxyzによる方向が違う気がします。xとyが逆だと、磁石をちかづけて最も変化した軸を見たところ感じたのですが、どう思いますか?

Asuki Kono さんのコメント...

情報共有ありがとうございます。

> 磁石を近づけて大きく変動させた後、磁石を離すと元のデータとは違う値で安定する

こういうことが起こるのですか、知りませんでした。
温度によっても適切なオフセット値が変わるように思うので、変になったら補正し直すのが一つの解決策でしょうか。

> Mpu9250のxyzの表示方向とak8963のレジスタxyzによる方向が違う気がします。xとyが逆だと、磁石をちかづけて最も変化した軸を見たところ感じたのですが、どう思いますか?

自分も磁石を近づけて試してみたところ、加速度の軸に磁気センサの軸合わせるには、下記のような変換が必要そうな動きをしていました。
y = -x
x = -y
z = -z

しかし、プログラムを見直しても、MPU9250のレジスタマップ(PDF)のHXL to HZH: Measurement Dataに沿って値は取得できているように見えます。
(AK8963_RA_HXL(0x03)から7バイト取得し、先頭からそれぞれ、XのLow,XのHigh,YのLow,YのHigh,ZのLow,ZのHighに利用。)

自分の把握していない軸の違いがあるのかもしれませんが、何が正しいのかは、今のところよく分かりません。

値を取得した後、使いたい軸の向きに合わせて独自に変数を定義して、それにセンサの値を保持して使ってもらうのが、今の所の解決策のように思います。

Unknown さんのコメント...

はじめまして。
非常に有用な情報を提供いただきましてありがとうございます。
MPU9250_asukiaaaのサンプルソフトウェアで何ら問題なく動作しています。
一点気になることがありまして、お知恵を拝借できればと思い投稿させていただきました。

ストロベリーリナックスの説明書(https://strawberry-linux.com/pub/mpu-9250-manual.pdf)では
『最初はスリープモードになっていてセンシングは行われていません。まず MPU-9250 の内部アドレス 0x6B に 0x00 を書き込 みます。』と記述されています。

MPU9250_asukiaaaのサンプルソフトウェアでは
PWR_MGMT_1[0x6B]のレジスタをReadすると0b00000001となっていています。
そこで、mySensor.setWire(&Wire)関数のI2C初期化関数の直後に
PWR_MGMT_1[0x6B]のレジスタにストロベリーリナックスの説明書どおり、0b00000000を書き込んでみましした。
結果に違いはなく、両者とも正常に動いているみたいです。
DatasheetではPWR_MGMT_1[0x6B]レジスタのbit6がSleepの様なのですが、
Setupの段階で、明示的にPWR_MGMT_1[0x6B]に値をセットした方が良いと思われますでしょうか?
その場合は、0b00000000と0b00000001のどちらを推奨されますか?

お忙しいところ、恐れ入りますが、ご教授願えれば助かります。

Asuki Kono さんのコメント...

コメントありがとうございます。

MPU9520のデータシートの31ページには「In PWR_MGMT_1 (0x6B) make CYCLE =0, SLEEP = 0 and STANDBY = 0」と説明があります。
レジスタマップの40-41ページに0x6Bの各ビットは

7: H_RESET 内部レジスタを初期値に戻す
6: SLEEP スリープモードになる
5: CYCLE スリープでもスタンバイでもない時に、LP_ACCEL_ODRで指定された動作をする?(よく分かってないです。)
4: STANDBY スタンバイモードになる(センサーは起動したまま配線を無効化?)
3: PD_PTAT Power down internal PTAT voltage generator and PTAT ADC(よく分かりません。)
2-0: CLKSEL 周波数の参照元を指定 0か6: 20MHzの内部振動子を利用、2-5: 最も良い振動源を自動選択、7: クロックを止めてリセット時にタイミング生成器を稼働?(よく分かってないです。)

であると説明があります。

以上の情報から推測しますと、0x6BがB**000***であれば良いということなので、レジスタの元の値が0x01だったのであれば、0x01で良いと思います。
(振動源の自動選択の結果によっては、0x00と0x01は同じ結果になる可能性があります。)
先頭から2番目のH_RESETを1にするとレジスタが初期化されてしまうようなので、そのビットは1にしないように注意するのが良さそうです。

いかがでしょう?

Asuki Kono さんのコメント...

手元にあるMPU9250を確認したところ、0x6Bのレジスタの値は0x01でした。
SLEEP, STANDBY, CYCLEが無効になっていたので、たまたま動いていたようです。
レジスタを書き換えても再起動すると同じ値になるので、自分でそれらのビットを有効にしなければ、問題なく動くようです。
しかしながら、データシートに指定されている動かし方なので、SLEEP, STANDBY, CYCLEを無効にする処理を追加しました。
MPU9250_asukiaaaのバージョン1.3.3から上記の処理が加わります。

情報ありがとうございます。

Unknown さんのコメント...

Asuki Kono様

修正コードを確認いたしました。
PWR_MGMT_1[0x6B]のレジスタのSetup時の処理ですが、
bits &= ~B00111000だと、
CYCLE=0
GYRO_STANDBY=0
PD_PTAT=0
となりませんでしょうか?
これにCLKSEL=0の処理を加えて
bits &= ~B00111000ではなく、
bits &= ~B01110111
とするのが適切の様に思うのですがいかがでしょうか?

潜在的なエラーを指摘するつもりで投稿しているつもりは全くありませんが、お気にさわればこの投稿を削除してください。

Asuki Kono さんのコメント...

指摘ありがとうございます。
ビットが1つずれてたので修正しました。
(7のH_RESETを先頭から2番目と誤認識していたため、このように間違えてしまいました。)
バージョン1.3.4から正しいビット設定で起動してくれるようになります。

> これにCLKSEL=0の処理を加えて
ストロベリーリナックスの説明書には初期化動作としてCLKSELも0にするような指示がありますが、メーカーのデータシートではCLKSELについて触れられていないようです。
ストロベリーリナックスの説明書では説明簡略化のためにCLKSELも0にしているのではないかと推測します。
CLKSELの設定に意味があるのか自分はわからないので、CLKSELについては触らないことにしました。

Unknown さんのコメント...

Asuki Kono様

恐れ多くも、指摘するつもりは一切ございません。
疑問に思ったことが解決できました。
非常に勉強になりました。
ありがとうございます。

Asuki Kono さんのコメント...

こちらこそ情報ありがとうございました。

> 指摘するつもりは一切ございません。
個人的に間違っている箇所は指摘して欲しいですし、先ほどのやりとりはそうされていたと思いますよ。
何か間違いを見つけましたら、今回のように根拠となるurlなどの情報を添えて、お気軽にご指摘ください。

Unknown さんのコメント...

初めまして.同様にarduinoとMPU9250を使用し卒業研究を行っているのですが,私の研究にてMPU9250_asukiaaaのライブラリを使用させていただくことは可能でしょうか.よろしくお願いします.

Asuki Kono さんのコメント...

MITライセンスで公開しているので問題ありません。
ご自由にお使いください。

Unknown さんのコメント...

ありがとうございます!