MPU9250で回転角度を検知する
はじめまして。ライターとして起用していただきました、ありか(@hakase0617)です。
秋田高専で高専ロボコンを5年間やっていました。 ロボコン界隈には卒業後も関わり続けているため、そういった若きエンジニアの手助けができたらと思っています!
マネジメントや教育に興味があり、その一環でIoT、AIの勉強をしています。 将来的にはエンジニアを育成したり、一緒にお仕事ができたりしたら楽しそうだなって思います。
9軸センサとは
早速ですが、今日はMPU9250というセンサを紹介します。 これは「9軸センサ」と呼ばれるものです。
え、9軸ってx,y,zとあとは何!?
これは
- 加速度センサ x,y,z
- 角速度センサ x,y,z
- 地磁気センサ x,y,z
の3軸ずつ、合わせて9軸というものです。 まずは各センサの紹介からいきます。
加速度センサとは
加速度とは、「どのくらいの速度が変化しているか」という指標です。 車を運転しているイメージをしてください。 アクセルを思いっきり踏んで、時速20km/hで走っていた車を時速60km/hまで加速させたとします。 この時、体は後ろに引っ張られる感じがします。 あの感じが加速度です。
加速度センサでは、物が動いているかどうかということも検出できます。 加速度センサの応用例としてはカメラの手ぶれ補正やスマホの画面回転、ハードディスクの揺れ検知などが該当します。 ロボットの姿勢制御には欠かせない技術でもあります。
角速度センサとは
角速度とは読んで字のごとく、回転角の速度です。 ものが回っている時の速さの単位に「回転数」(rpmなど)や周波数(Hzなど)がありますが、これらは「何回回ったか」を示す指標です。 回っているものの「角度」に注目して速さを表す指標が角速度です。
地磁気センサとは
地磁気センサでは、磁力の強さを検知出来ます。 地球は大きな磁石ですが丸いので、地球上のどの位置にいるかで地磁気の強さは異なります。 そのため、地磁気の強さやら向きやらを測定すると自分がいる位置が分かり、自分が向いている方角も分かるというものです。
9軸センサで何が出来るか
さて、これら3つのセンサが合体した9軸センサで、何ができるのでしょうか?
角速度センサで「自分がどれだけ回ったのか」が分かります。
しかし弱点があります。 角速度センサだけでは、今「動いている」のか「止まっている」のかは分かりません。 そこで加速度センサの出番です。加速度センサは上記状態を把握できます。 これらを合わせて「6軸センサ」と呼ぶのですが、この6軸センサを使うと「回転角度」が分かります。
じゃあ6軸でいいじゃん、と思ってしまいますが、6軸センサには弱点があります。 ずばり、「ドリフト」です。
角速度センサは「積分方式」で回転角度を検出します。 どういうことかというと、「15度動いた」「20度動いた」「10度動いた」を合わせて「45度動いた」といった風に、足し算で算出します。 しかし、みんなの敵である「誤差」という概念がここで登場します。
さっきの例で、毎回+1度の誤差が生じてしまうとします。 すると 16度+21度+11度=48度 となり、誤差がどんどん大きくなっていきます。 つまり、試行回数(時間経過)とともに誤差が大きくなっていくではありませんか。
これを解決するためには、別の方法で校正を行う必要があります。 そこで「地磁気センサ」を使います。
地磁気センサは、簡単に言うと「方位磁針」です。そのため、北を向いたまま10分経とうが1時間経とうが、地磁気センサは「北を向いている」という状態を教えてくれます。
まとめると、 角速度センサ→何度回ったかが分かる 加速度センサ→回ったかが分かる 地磁気センサ→ドリフトを打ち消す といった役割を持ち、回転を検知するセンサとなります。
実際に使ってみよう
MPU9250を使って、実際に回転角度を測定してみましょう。 今回は開発環境にArduinoを選定しました。 ライブラリが公開されており、初心者である僕でもサクッと動作確認が可能な点に魅力を感じました。
今回は入門として、角速度センサのみで回転角度を検出してみます。
開発環境
- OS:Windows10
- ArduinoIDE
- Arduino UNO
- MPU9250
- ジャンパワイヤ5本
- ブレッドボード
スケッチ
#include <Wire.h>
#define FIL 0.9 //フィルタ
#define AVE 100 //測定回数
#define GYRO 30 //ドリフト許容値
int emp[6] = {};
int data = 0; //生データ格納 1から順に加速度xyz角速度xyz
long datasum = 0; //合計データ格納 平均算出用
int th = 0; //閾値
long integral = 0; //積分値
int degree; //回転角度
int rock; //ロック機構により導かれた初期値
char input;
void setup() {
Wire.begin(); Serial.begin(115200); Wire.begin(0x68);
Wire.beginTransmission(0x68); Wire.write(0x6B); Wire.write(0x00); Wire.endTransmission();
Wire.beginTransmission(0x68); Wire.write(0x1C); Wire.write(0x10); Wire.endTransmission();
Wire.beginTransmission(0x68); Wire.write(0x1B); Wire.write(0x08); Wire.endTransmission();
Wire.beginTransmission(0x68); Wire.write(0x1A); Wire.write(0x05); Wire.endTransmission();
Serial.print("*****************\nCalibrating now\nplease turn once and press 'f'\n");
while (1) {
reading();
datasum = 0;
input = Serial.read();
if (input == 'f') {
rock = integral / 360;
Serial.print("Ready!");
Serial.print("\n*******\n");
integral = 0;
delay(500);
break;
}
}
}
void loop() {
reading();
degree = abs(integral / rock);
Serial.println(degree);
datasum = 0;
}
void reading() {
for (int i = 0; i < AVE; i++) {
Wire.beginTransmission(0x68); Wire.write(0x3B); Wire.endTransmission(); Wire.requestFrom(0x68, 14);
while (Wire.available() < 14);
emp[0] = Wire.read() << 8 | Wire.read();
emp[1] = Wire.read() << 8 | Wire.read();
emp[2] = Wire.read() << 8 | Wire.read();
emp[3] = Wire.read() << 8 | Wire.read();
emp[4] = Wire.read() << 8 | Wire.read();
emp[5] = Wire.read() << 8 | Wire.read();
data = Wire.read() << 8 | Wire.read();
datasum += data;
}
datasum /= AVE;
if (th == 0) {
th = datasum;
}
if (abs(datasum - th) > GYRO) { //誤差が小さい場合はスルー ドリフト対策
integral += datasum - th;
}
}
Arduinoとの接続
Arduino | MPU9250 |
---|---|
5V | VCC |
GND | GND |
A2 | INT |
A4 | SDA |
A5 | SCL |
実物
スケッチ簡易解説
今回は角速度センサのz軸のみを使用します。 z軸、つまり地面と鉛直な方向の回転が分かる、早い話が自分がどれだけ回ったかが分かります。
まず、角速度センサを使うにあたって、センサから返ってくる値を調べる必要があります。 センサは「何度回ったよ」とは教えてくれません。 「4253」「71469」みたいな無機質な値を教えてくれます。 そのため、その値がいくつで360度に相当するのかを確認する必要があります。
どうすればいいのか、答えは「360度回せばいい」のです。そのままです。
今回はArduinoを起動した際に、まず1回転して値を取得して、それから角度検出をする、というスケッチを作りました。
while (1) {
reading();
datasum = 0;
input = Serial.read();
if (input == 'f') {
rock = integral / 360;
Serial.print("Ready!");
Serial.print("\n*******\n");
integral = 0;
delay(500);
break;
}
}
reading()関数の中で実際に値を取得するのですが、1回転した合図として「Arduinoのシリアル通信で’f’を送ってあげた時にwhile()から抜けて角度検出モードに移行します。
void reading() {
for (int i = 0; i < AVE; i++) {
Wire.beginTransmission(0x68); Wire.write(0x3B); Wire.endTransmission(); Wire.requestFrom(0x68, 14);
while (Wire.available() < 14);
emp[0] = Wire.read() << 8 | Wire.read();
emp[1] = Wire.read() << 8 | Wire.read();
emp[2] = Wire.read() << 8 | Wire.read();
emp[3] = Wire.read() << 8 | Wire.read();
emp[4] = Wire.read() << 8 | Wire.read();
emp[5] = Wire.read() << 8 | Wire.read();
data = Wire.read() << 8 | Wire.read();
datasum += data;
}
datasum /= AVE;
if (th == 0) {
th = datasum;
}
if (abs(datasum - th) > GYRO) { //誤差が小さい場合はスルー ドリフト対策
integral += datasum - th;
}
}
そのreading()の中では、平均を取るためにAVEで指定された回数値を格納し続けています。 emp[]で色々格納していますが、上から 加速度x軸、y軸、z軸 温度 角速度x軸、y軸、z軸 という風に格納されていきます。そのため今回は角速度z軸のみを取り出すためこういった書き方です。
動かしてみよう
リード線を数本接続しただけで動作確認が出来るなんて、とっても楽ですね! 早速動かしてみます。
できました! 角度が表示されています。
何に使えるの?
今回注目したのは、自律移動ロボットの位置制御です。 ロボットコンテストなどでは、コントローラなどを用いず自動でロボットが移動することを求められるケースが多々あります。 その自動ロボットの位置制御には、どのような方法が用いられているのでしょうか。
おそらく一般的なものは、モータにロータリエンコーダなどを取り付けて 「モータがどのくらい回ったか」 を検出し、位置を割り出す方法だと思います。
ですが、この方法には弱点があります。 例えば、移動するフィールドが平らではなかったり、滑りやすい障害物(落ち葉など)があったり、タイヤが走行中に滑ったりといった「タイヤとフィールドの関係」で誤差が生じてしまいます。
この誤差をなんとかしたい! そんな時に便利なのがこの9軸センサです。
自身がどれくらい動いたか、どれくらい回ったかが分かるこのセンサがあれば、たとえタイヤが空回りしてしまっても、「今いる位置」を知ることが可能なのです!
最後に
今回は9軸センサのうちたった1軸しか使っていませんが、回転を検出することができました。 より高度なスケッチを作ることでより高度な測定が可能になるため、これからも勉強を続けていきたいです。