前回は、デバプラ編集部さんから送られてきたロームセンサ評価キットの中で、地磁気センサをご紹介しました。センサ評価キットの使いやすさは前回の記事でわかっていただけたと思いますので、今回は地磁気センサの値の読み方から、他のパーツと組み合わせてみるなどの使い方応用編です!
今回の電子工作レシピ
完成までの時間目安:60分
必要なパーツ
- Arduino本体(Arduino UNO R3)https://www.switch-science.com/catalog/789/
- ロームセンサ評価キット http://www.rohm.co.jp/web/japan/sensor-shield-support
- 42mm ステッピングモータ 12V 2相
https://strawberry-linux.com/catalog/items?code=12026 - L6470 ステッピングモータ・ドライバキット
https://strawberry-linux.com/catalog/items?code=12023 - 木板・ネジ
↓目次です!
- 地磁気センサから値・方角を読み取るには?
- 地磁気センサの仕組み~センサの値をまどわす伏角(ふっかく)と偏角(へんかく)?
- 地磁気センサの最小・最大値を調べて方角を検出
- ステッピングモーターと組み合わせてコンパスを作ってみる
- まとめ
1.地磁気センサから値・方角を読み取るには?
まずは、地磁気センサの値がどう変化するのか、実際にArduinoに載せた地磁気センサをぐりぐり動かして値を確認してみます。前回利用したサンプルプログラムだと、シリアルモニタで確認する際に小数点が動いて見づらいため、確認用として整数型で表示されるように修正しました。動画では、あらかじめGPSやジャイロコンパスを利用した方角検出の精度が高いアプリから正確な北(真北)の方角を調べて紙に記載しています。その方角に地磁気センサを合わせたときのプログラム側で取得できているXYZ軸それぞれの値を見てみましょう。
BM1422GMVの表示プログラム
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include <Wire.h> #include <BM1422.h> BM1422 bm1422(BM1422_DEVICE_ADDRESS_0E); void setup() { byte rc; Serial.begin(9600); while (!Serial); Wire.begin(); rc = bm1422.init(); } void loop() { byte rc; float mag[3]; rc = bm1422.get_val(mag); int x = mag[0]; int y = mag[1]; int z = mag[2]; if (rc == 0) { Serial.print( "X=" ); Serial.print(x); Serial.print( " Y=" ); Serial.print(y); Serial.print( " Z=" ); Serial.println(z); } delay(500); } |
動画を見ていただければわかりますが、それぞれセンサの最大値を示す箇所をArduinoのシリアルモニタにあらわれる数字を確認しながら最大値を探していきます。
図1 地磁気センサの状態
図2 地磁気センサのX軸の最大値
動画でX軸が最大になる位置はこの角度でした。あれれ、図1で書いてあるように、センサの値は地磁気の強さなので、本来であれば真北になったときに最大値になるはず…?わからなくなってきました。どういうことでしょうか。ぐりぐりセンサを動かしてみるとY軸も大体X軸と同じ方向で止まっていますね。何か実験しているこの部屋の中に地球とは違う磁場が存在してしまっているのでしょうか…。
ちなみに、Z軸が最大値を示す箇所を調べると写真1のようになりました。ほぼ逆さまです。やはり地面から何か得体の知れない地磁気が放出されているのでは…汗。
写真1 Z軸が最大値になった状態
少しばかり怖くなってきたので、地磁気センサが最大値を指し示し必死に訴えている(ように見えた)センサの数値のナゾについて、地磁気センサのことを学んで紐解いてみたいと思います。
地磁気センサの2軸と3軸センサの違い
まず、地磁気センサには大きく2種類のタイプがあり、2軸(XY)と3軸(XYZ)で検出できるものが存在します。2軸タイプは、XY軸なので、単純に水平状態のときに方角を検出することができますが、傾いた状態などでは正常に方角を検出できません。3軸タイプではXY軸にあわせて、傾きのZ軸が加わるので、Z軸の傾きの度合いに応じて、XY軸の値に補正をかけて、方角を検出することができます。
今回センサ評価キットに入っているセンサは3軸センサなので2軸よりも詳細なデータを扱うことができるということですね、ふむふむ。
2.地磁気センサの仕組み~センサの値をまどわす伏角(ふっかく)と偏角(へんかく)って何?
では、次にセンサの最大値のナゾに切り込んでいこうと思います。
まず、そもそも地磁気ってなんだろう!?
そうなんです、そもそも私、地磁気センサを扱うのに「地磁気」自体のことをまったく知りませんでした(お恥ずかしい…)。
なんか地球が出している磁力?磁場、そんなあやふやなイメージしかありません…。
地磁気とは?
ここで、地磁気自体のことと地磁気センサの仕組みを学んでみましょう。地磁気センサは、簡単に言えば磁石からでている磁気を感知できるセンサです。図4のように地球は大きな磁石のようなもので、磁気が地球を取り巻いています。地磁気センサでは、方位磁石と同じように、この地磁気を検出できるセンサです。
図4 地球は大きな磁石
ここで、ちょっと科学の勉強になってしまうのですが、実際に方位磁石を持って北極点もしくは南極点を目指した場合、そのままだといつまでたっても北極点に到達することはできません。それは、地球のしくみにヒントがあります。国土地理院や気象庁のウェブサイトにわかりやすく解説が載っていますが、地磁気は地球の中心から少しずれています。地球内部の活動などの影響で変化していくのですが、2015年のデータでは西におよそ7度ずれている(後で詳細解説)とのことです。不思議ですね。そして、数万年~数十万年単位で地磁気は反転しているらしいです。とすると、地磁気センサを使って未来永劫生き残る耐久性を持ったデバイスを作っても数万年たてば地球自体の軸がずれてしまうので補正かけてあげないといけないですね…
地磁気を知る – 国土地理院
http://vldb.gsi.go.jp/sokuchi/geomag/menu_01/
地球の電磁気のQ&A – 気象庁
http://www.kakioka-jma.go.jp/knowledge/qanda.html
図5 地球の磁石は北極点からちょっとずれている
また、ここで国土地理院の電子国土Webでズレの度合いを調べることができます。
図6 東京の磁北線
東京駅、きっちり7.0°ずれてますね!
図7 宗谷岬の磁北線
日本最北端の稚内、あれ、7.0°じゃなくて9.8°????
じゃあ、南に行ってみると…
図8 那覇市の磁北線
沖縄那覇市は、7.0°じゃなくて4.4°
もう何がなんだかわからなくなってきました…
ということで、図の真ん中に記されている赤い数値がズレの度合いですが、東京駅は7度に対して、日本最北端宗谷岬では9.8度、沖縄那覇市では4.4度と大きなひらきが確認できますね。
このズレって場所によって違うという現実だけがわかりました。ナゾが深まるばかりです…。
伏角(ふっかく)と偏角(へんかく)の存在
実はこの地球の地磁気の最大値と真北のズレの角度は「伏角(ふっかく)」と「偏角(へんかく)」と呼ばれています。地磁気のずれは、7度と書きましたが、実はこのズレは計測する位置で変化します。そのズレを偏角と呼びます。日本国内でいうと、北に行くほどズレが大きくなり、南にいくと小さくなるらしいです。また、東西南北を左右(XY軸)と見立てた場合、上下の水平(Z軸)もずれています。それを伏角と呼びます。伏角は図4・5で示した、地磁気の流れに対する角度で、東京だと地面にめり込む形で49度くらい傾いています。だからさっきZ軸が逆さ近くで最大値になるのはそういうことが関連しているのですね。
以上のことから、正確な方角を示す場合、この伏角・偏角が絡むため、GPSで地球上のどこの位置にいるのかを踏まえてズレを補正する、という感じでかなり大変そうですね…。整理すると、地磁気センサなどを扱う場合、北といっても真北なのか磁北なのかを明確にしておかないと、取得したい数値に違いが出てしまう、ということになりますね。
- 真北 ・・・ 地球の一番北
- 磁北 ・・・ 電子磁石や地磁気センサが示す北の位置(地磁気が最大の方向)
3.磁気センサの最小・最大値を調べて方角を検出
では、磁気センサを実際につかってArduinoで方角を検出できるようにしていきます。
地磁気センサをはじめとして、電子部品のセンサは基本的にセンサの状態=電気抵抗値が変化するので、その変化の値=抵抗を通して変化する電圧の値をArduino側で計測することで、センサの値を読んでいます。今回利用する地磁気センサのXYZ軸の最小・最大値を調べてみましょう。調べる値は、水平時と、水平関係ない場合の2種類です。ちなみにこの数値は試す環境で変わりますので、実験をする場合は実際にセンサをつないで試してみてください。
軸 | 水平時最小 | 水平時最大 | 最小(Min) | 最大(Max) |
---|---|---|---|---|
X | -90 | 1 | -121 | 21 |
Y | -84 | 7 | -115 | 36 |
Z | -98 | -92 | -102 | 30 |
今回は、水平時での方角について算出してみたいと思います。下記の図9は地磁気センサを1周回した値をシリアルモニタからコピーして、XY軸の分布図にしたものです。きれいな円になっていますね。これが水平時の場合、アークタンジェントを使った式で角度を算出することができます。中点を軸に角度を検出したい点を式に当てはめることで、角度を算出することができます。プログラム側では、中点と、数値の範囲を予め確認して設定したうえで、Arduinoに書き込む必要がありますね。
図9 地磁気センサの値をグラフにプロット
では、この式を使ってArduinoで実装してみましょう!
4.ステッピングモーターと組み合わせてコンパスを作ってみる
今回は、地磁気センサとステッピングモーターを組み合わせてコンパスを作ってみます。丸い木の板にArduinoと地磁気センサを載せて、台座をステッピングモーターで回転させることで、常に北を指す仕様で実装してみます。ステッピングモーターを逆さまにして、その上にArduinoなどが載る台座をおきます。ステッピングモーターの回路は第37回を参考にします。
写真2 ベースとなるステッピングモーター
写真3 台座の上に載せるArduino・地磁気センサなどのパーツ
最初は、ステッピングモーターの台座は1枚で考えていましたが、実装している際にステッピングモーターの磁力が地磁気センサの数値にノイズとして検出されてしまうことがわかったため、ボルトをかませてノイズがのらない高さまで底上げしました。
写真4 全て載せた状態、地磁気センサはボルトで底上げ
プログラムは、角度を検出して磁北になったら止まるようにしています。ステッピングモーターを早くすると磁北を過ぎてしまう場合もあるので、すこしrdの変数にif条件文内で余裕を持たせています。
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | #include <Arduino.h> #include <SPI.h> #include <Wire.h> #include <BM1422.h> BM1422 bm1422(BM1422_DEVICE_ADDRESS_0E); int _px = -44.5; //中点x int _py = -38.5; //中点y #define PIN_SPI_MOSI 11 #define PIN_SPI_MISO 12 #define PIN_SPI_SCK 13 #define PIN_SPI_SS 10 void setup() { delay(1000); Serial.begin(9600); //ステッピングモーター用のピンの準備 pinMode(PIN_SPI_MOSI, OUTPUT); pinMode(PIN_SPI_MISO, INPUT); pinMode(PIN_SPI_SCK, OUTPUT); pinMode(PIN_SPI_SS, OUTPUT); digitalWrite(PIN_SPI_SS, HIGH); //SPI通信の開始宣言 SPI.begin(); SPI.setDataMode(SPI_MODE3); //SCKの立上りでテータを送受信、アイドル時はpinをHIGHに設定 SPI.setBitOrder(MSBFIRST); //MSBから送信 //L6470の利用設定 L6470_setup(); byte rc; Serial.begin(9600); while (!Serial); Wire.begin(); rc = bm1422.init(); } //********************************************** //SPI通信するための関数 //********************************************** void L6470_send(unsigned char value){ digitalWrite(PIN_SPI_SS, LOW); SPI.transfer(value); //制御信号をSPI通信で送る digitalWrite(PIN_SPI_SS, HIGH); } //指定した角度回す void moveStepper( int val, int r=1){ //360度 - 1回転させる処理 if (r == 1){ L6470_send(0x50); //Run(DIR,SPD),0x51:正転,0x50:逆転 } else { L6470_send(0x51); //Run(DIR,SPD),0x51:正転,0x50:逆転 } L6470_send(0x00); L6470_send(0x20); //回転スピードの設定 L6470_send(0x00); double rad = val*9; delay(rad); //1604msで約1回転 L6470_send(0xB8); //急停止(ハードストップ) } //********************************************** // L6470のセットアップ //********************************************** void L6470_setup(){ //デバイス設定 L6470_send(0x00); L6470_send(0x00); L6470_send(0x00); L6470_send(0x00); L6470_send(0xc0); //最大回転スピード設定 L6470_send(0x07); //レジスタアドレス L6470_send(0x20); //値(10bit),デフォルト0x41 //モータ停止中の電圧設定 L6470_send(0x09); //レジスタアドレス L6470_send(0xFF); //値(8bit),デフォルト0x29 //モータ定速回転時の電圧設定 L6470_send(0x0a); //レジスタアドレス L6470_send(0xFF); //値(8bit),デフォルト0x29 //加速中の電圧設定 L6470_send(0x0b); //レジスタアドレス L6470_send(0xFF); //値(8bit),デフォルト0x29 //減速中の電圧設定 L6470_send(0x0c); //レジスタアドレス L6470_send(0xFF); //値(8bit),デフォルト0x29 //フルステップ,ハーフステップ,1/4,1/8,…,1/128ステップの設定 L6470_send(0x16); //レジスタアドレス L6470_send(0x00); //値(8bit) } /** 角度を求める **/ double getDirection( double x, double y){ double dir = 0; dir = atan ((y - _py)/(x - _px)); return dir; } void loop(){ byte rc; float mag[3]; float rd = 0; rc = bm1422.get_val(mag); rd = getDirection(mag[0],mag[1]); int x = mag[0]; int y = mag[1]; Serial.print( "rd=" ); Serial.print(rd); Serial.print( " x=" ); Serial.print(x); Serial.print( " y=" ); Serial.println(y); //磁北ではないときステッピングモーターを回転させる //ステッピングモーターの回転数に応じてrdに余裕を持たせています if (rd <= 0.3 && rd >= -0.3 && x > 0){ //磁北の状態 Serial.println( "North!" ); } else if (rd > 0){ moveStepper(2,-1); } else { moveStepper(2); } delay(2); } |
これで、磁北を常に向き続けるコンパスが完成しました!これをより正確に真北を向けるようにするには偏角・伏角などの値を調整してあげる、という流れになりますね!
5.まとめ
今回は、地磁気センサを理解するために理科や数学のような知識がでてきましたね!また、Arduinoでコンパスを作るだけだと車輪の再発明になってしまいますが、この基本的な動作を踏まえて応用を考えてみるのも楽しいですよね。次回はもっと踏み込んでセンサ評価キットを使ってみたいと思います!
この連載の記事
- 第40回 Arduinoでセンサを使った開発が劇的に楽になる!8種のセンサを持つロームセンサ評価キットを試してみた(導入&地磁気センサ編)
- 第41回 Arduinoでセンサを使った開発が劇的に楽になる!8種のセンサを持つロームセンサ評価キットを試してみた(地磁気センサ応用編)(このページの記事)