できること

第41回 Arduinoでセンサを使った開発が劇的に楽になる!8種のセンサを持つロームセンサ評価キットを試してみた(地磁気センサ応用編)

1-P1220662

前回は、デバプラ編集部さんから送られてきたロームセンサ評価キットの中で、地磁気センサをご紹介しました。センサ評価キットの使いやすさは前回の記事でわかっていただけたと思いますので、今回は地磁気センサの値の読み方から、他のパーツと組み合わせてみるなどの使い方応用編です!

今回の電子工作レシピ

完成までの時間目安:60分
必要なパーツ

 

↓目次です!

  1. 地磁気センサから値・方角を読み取るには?
  2. 地磁気センサの仕組み~センサの値をまどわす伏角(ふっかく)と偏角(へんかく)?
  3. 地磁気センサの最小・最大値を調べて方角を検出
  4. ステッピングモーターと組み合わせてコンパスを作ってみる
  5. まとめ

1.地磁気センサから値・方角を読み取るには?

まずは、地磁気センサの値がどう変化するのか、実際にArduinoに載せた地磁気センサをぐりぐり動かして値を確認してみます。前回利用したサンプルプログラムだと、シリアルモニタで確認する際に小数点が動いて見づらいため、確認用として整数型で表示されるように修正しました。動画では、あらかじめGPSやジャイロコンパスを利用した方角検出の精度が高いアプリから正確な北(真北)の方角を調べて紙に記載しています。その方角に地磁気センサを合わせたときのプログラム側で取得できているXYZ軸それぞれの値を見てみましょう。

BM1422GMVの表示プログラム

Code-Example
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 地磁気センサの状態

図1 地磁気センサの状態

 

図2 地磁気センサのX軸の最大値

図2 地磁気センサのX軸の最大値

動画でX軸が最大になる位置はこの角度でした。あれれ、図1で書いてあるように、センサの値は地磁気の強さなので、本来であれば真北になったときに最大値になるはず…?わからなくなってきました。どういうことでしょうか。ぐりぐりセンサを動かしてみるとY軸も大体X軸と同じ方向で止まっていますね。何か実験しているこの部屋の中に地球とは違う磁場が存在してしまっているのでしょうか…。

ちなみに、Z軸が最大値を示す箇所を調べると写真1のようになりました。ほぼ逆さまです。やはり地面から何か得体の知れない地磁気が放出されているのでは…汗。

 

 

写真1 Z軸が0になる状態その2

写真1 Z軸が最大値になった状態

 

少しばかり怖くなってきたので、地磁気センサが最大値を指し示し必死に訴えている(ように見えた)センサの数値のナゾについて、地磁気センサのことを学んで紐解いてみたいと思います。

地磁気センサの2軸と3軸センサの違い

まず、地磁気センサには大きく2種類のタイプがあり、2軸(XY)と3軸(XYZ)で検出できるものが存在します。2軸タイプは、XY軸なので、単純に水平状態のときに方角を検出することができますが、傾いた状態などでは正常に方角を検出できません。3軸タイプではXY軸にあわせて、傾きのZ軸が加わるので、Z軸の傾きの度合いに応じて、XY軸の値に補正をかけて、方角を検出することができます。

今回センサ評価キットに入っているセンサは3軸センサなので2軸よりも詳細なデータを扱うことができるということですね、ふむふむ。

 

2.地磁気センサの仕組み~センサの値をまどわす伏角(ふっかく)と偏角(へんかく)って何?

では、次にセンサの最大値のナゾに切り込んでいこうと思います。

 

まず、そもそも地磁気ってなんだろう!?

 

そうなんです、そもそも私、地磁気センサを扱うのに「地磁気」自体のことをまったく知りませんでした(お恥ずかしい…)。
なんか地球が出している磁力?磁場、そんなあやふやなイメージしかありません…。

 

地磁気とは?

ここで、地磁気自体のことと地磁気センサの仕組みを学んでみましょう。地磁気センサは、簡単に言えば磁石からでている磁気を感知できるセンサです。図4のように地球は大きな磁石のようなもので、磁気が地球を取り巻いています。地磁気センサでは、方位磁石と同じように、この地磁気を検出できるセンサです。

図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 地球の磁石は北極点からちょっとずれている

図5 地球の磁石は北極点からちょっとずれている

また、ここで国土地理院の電子国土Webでズレの度合いを調べることができます。

図6 東京の磁北線

図6 東京の磁北線

 

東京駅、きっちり7.0°ずれてますね!

 

図7 宗谷岬の磁北線

図7 宗谷岬の磁北線

 

日本最北端の稚内、あれ、7.0°じゃなくて9.8°????

じゃあ、南に行ってみると…

 

図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 地磁気センサの値をグラフにプロット

図9 地磁気センサの値をグラフにプロット

 

では、この式を使ってArduinoで実装してみましょう!

 

4.ステッピングモーターと組み合わせてコンパスを作ってみる

今回は、地磁気センサとステッピングモーターを組み合わせてコンパスを作ってみます。丸い木の板にArduinoと地磁気センサを載せて、台座をステッピングモーターで回転させることで、常に北を指す仕様で実装してみます。ステッピングモーターを逆さまにして、その上にArduinoなどが載る台座をおきます。ステッピングモーターの回路は第37回を参考にします。

写真2 ベースとなるステッピングモーター

写真2 ベースとなるステッピングモーター

 

写真3 台座の上に載せるArduino・地磁気センサなどのパーツ

写真3 台座の上に載せるArduino・地磁気センサなどのパーツ

最初は、ステッピングモーターの台座は1枚で考えていましたが、実装している際にステッピングモーターの磁力が地磁気センサの数値にノイズとして検出されてしまうことがわかったため、ボルトをかませてノイズがのらない高さまで底上げしました。

写真4 全て載せた状態

写真4 全て載せた状態、地磁気センサはボルトで底上げ

プログラムは、角度を検出して磁北になったら止まるようにしています。ステッピングモーターを早くすると磁北を過ぎてしまう場合もあるので、すこしrdの変数にif条件文内で余裕を持たせています。

Code-Example
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でコンパスを作るだけだと車輪の再発明になってしまいますが、この基本的な動作を踏まえて応用を考えてみるのも楽しいですよね。次回はもっと踏み込んでセンサ評価キットを使ってみたいと思います!

 

この連載の記事

電子工作や新しいデバイスをこよなく愛するエンジニア。日常生活のちょっとしたことを電子工作で作って試して、おもしろく過ごしたいと日々考えています。

Tello+Scratch+SQ11でプチ動画を撮る!