micro:bit 磁気センサ(micro:bitの方位角の測定)

2022.10.30 2021.10.29 Coskx Lab

1 はじめに

micro:bitには方位測定のための便利な「方位(°)」ブロックが有りますが,磁気センサの値から方位角(磁北とy軸+のなす角)を求める方がより応用が広くなります。
micro:bitには磁気センサが3軸方向にが組み込まれています。これを直接使うとmicro:bitがどの方向(方位角)に向いているかを計算で求めることができます。 また,3軸センサの測定値を直接使うため,測定誤差を小さくするための工夫もできます。
この資料では水平置きしたmicro:bitの方位角を求めていますが,同様に考えれば,鉛直方向に立てられた状態のmicro:bitの方位角を求めることもできるでしょう。

2 使用環境

3 地磁気

方位磁石を見ていると,地磁気の磁力線は水平面内に存在するように感じられますが,地面に突き刺さっています。

国内で使用する方位磁石は北側が薄く軽くできていて,磁針は水平を保っています。
どのくらいの角度で突き刺さっているかを表す角度は伏角,真北と磁北の隔たりは偏角と呼ばれています。
偏角と伏角は場所により少しずつ違います。また年月とともに変化しています。
最近の東京付近では,次のようなデータになっています。
偏角約7.3度
伏角約49.6度
全磁力47μT (マイクロテスラ 磁束密度の単位)
水平分力30μT
鉛直分力35μT



国土地理院 https://vldb.gsi.go.jp/sokuchi/geomag/menu_04/index.html

3 micro:bitの3軸磁気センサ

micro:bitには互いに90度の方向を測定する磁気センサが3つ付いています。
micro:bitのx軸+を地面に突き刺さっている全磁力の方向に向けるとx軸磁気センサ値は約47μT(最大値),x軸-を向けると約-47μT(最小値)になるはずです。
同様にy軸+を地面に突き刺さっている全磁力の方向に向けるとy軸磁気センサ値は約47μT(最大値),y軸-を向けると約-47μT(最小値)になるはずです。
さらに同様にz軸+を地面に突き刺さっている全磁力の方向に向けるとz軸磁気センサ値は約47μT(最大値),z軸-を向けると約-47μT(最小値)になるはずです。






micro:bitは磁気センサを使うプログラムの起動時にLED画面に「TILT TO FILL SCREEN」が表示されることがあります。この表示が現れたら,その後に続くLED画面で,micro:bitをいろいろな姿勢にします。
全てのLEDが点灯したら,磁気センサの較正(キャリブレーション)が終了し,磁気センサが正しく動作するようになります。
しかし,較正(キャリブレーション)であっても,3軸磁気センサ測定値には誤差が含まれます。
実際,手元のmicro:bit v2では,較正(キャリブレーション)後であるにもかかわらず,次のような値が得られています。
 MinMax
x軸-4940
y軸-4345
z軸-4244
これは,感度の不均一や0点のずれのように見えます。各軸の最大値最小値が許容できないほどずれている場合には,追加補正をするようにします。


4 micro:bitの方位測定

LEDスクリーンを上に向け,水平に置いたmicro:bitのy軸の方位角を求めます。
x軸磁気センサとy軸磁気センサの値を使います。x軸磁気センサ値は,地磁気水平分力のx軸成分で,y軸磁気センサ値は,地磁気水平分力のy軸成分です。

水平置きされたmicro:bitを上から見たところ

磁北とy軸+のなす角を求めるにはtanの逆関数atanを使うのですが,4つの象限で値が得られるatan2関数を使います。



θ[rad] = atan2(q, p) で求められます。方位角は次のプログラムで求めます。関数anan2の引数の順番に気を付けます。
関数atan2は戻り値が[rad]単位なので[deg]に変換するため57.29578を掛けています。



micro:bitでこのプログラムが起動し,Makecode画面左のShow data deviceボタンを押すと,次のように得られた表示が連続して表示されます。


-49
-47
-48
-48
-48
-49
-49
-49
-48
-50
-49
-49
-48
-48
-49

ここで求められている方位角は磁北とy軸+のなす角で,磁北を0度とし,時計回りに角度が増えます。ただし,値の範囲は-180度から180度になります。
(なお,方位角の表現では右手系(磁北を0度とし,反時計回り)で測定する定義もあります。その場合は求められた方位角をー1倍すれば求められ,方位磁石の真西が90度になります,飛行機や自動運転車などの角度制御では,ヘディング角としてこの定義がよく使われます。)

5 方位角データの平均化

方位角データとしては平均化して使う方が良い場合があります。
次のプログラムでは,10回平均した方位角を取得する関数を使うようになっています。




関数azimuth10は,10回平均の方位角値を返す関数なので,「ずっと」ブロック中の「シリアル通信 1行に書き出す」で使えます。
方位角の連続取得の場合には,このプログラムにあるように20msecの間隔が必要です。(補足3 短インターバル繰り返し測定)
また,この値を所望の変数に代入することもできます。
平均化したときの様子は次のようになりました。まだ少し揺れがありますが,1度以下の揺れになりました。
ただし,10回の平均値を求めるのに,200ms=0.2secかかることになります。
用途によって平均化回数を考える必要があります。

-38
-38
-38
-38
-38
-37
-38
-38
-38
-38
-38
-38
-37
-38
-38
-37
-37

6 まとめ

micro:bitの3軸磁気センサの値から,micro:bitの方位角(磁北とy軸+のなす角)を求めました。
水平置きしたmicro:bitだけでなく鉛直に立てて保持された場合でも座標系を考慮すれば応用できます。x軸センサ値・y軸センサ値の代わりにx軸センサ値・z軸センサ値を使うことになるでしょう。

注意 3軸磁気センサを連続使用するときは,最低でも20msは間隔をとる必要があります。micro:bit側の測定値更新が20msほどかかるので,それ以下の間隔での測定値取得では,同じ値を取り込んでしまうことになります。特に測定値の平均化のときは,平均化が無意味になってしまうので注意が必要です。
「ずっと」ブロックを使って1ループで1回だけ測定する場合は,「ずっと」ブロックの舞台裏で20msの一時停止(pause)が行われるため問題ありません。
(「補足3 短インターバル繰り返し測定」「補足4 磁気センサデータ更新間隔の確認」参照)



補足1 測定上の誤差

micro:bitでの方位角の検出における誤差は次のような原因があります。

A micro:bit自身に起因する誤差

  1. 方位角センサ(磁気センサ)はキャリブレーション(コンパスの調整)されても,3個の磁気センサにおいて感度のばらつきとオフセットがあります。
    そのため,あまり正確な方位角測定は期待できません。(10度から20度ほど誤差があります。)
  2. micro:bit v2にはマグネティックスピーカが付きました。スピーカの磁気回路は閉じているはずですが,磁束の漏れが検出されます。
    漏れた磁束が磁気センサに一定のオフセットを生じさせている可能性があります。
    スマートフォンでもマグネットスピーカと磁気センサが搭載されていますが,距離が離れています。
    micro:bitはコンパクトにまとめられているためスピーカと磁気センサの距離が20mmほどしかありません。
  3. モータやサーボモータなど磁石を使った部品と一緒に1つのシステムを構成した場合,micro:bitとモータなどとの距離はできるだけ離したとしても200mmほどとなります。
    モータやサーボモータの漏れ磁束は鉄片などを吸着するほどは大きくないのですが,磁気センサには影響を与えています。

B 環境に起因する誤差

  1. 方位角の検出においては,環境も重要です。
    磁石や鉄製の器具の周囲では,方位角検出に大きな誤差が生じます。(スマートフォンの方位角検出でも大きな影響を受けることがあります。)
  2. 磁石や鉄製の器具が見当たらないところであっても,床下に鉄製の構造物があるような場合には,方位角検出で大きな影響を受けることがあります。

上記のような影響があるため,次のようなことが起こります。誤差が生ずることを念頭に,実際に方位角測定を行って,評価してから使う必要があります

  1. 絶対方位:地理上の位置により磁北の方角は決まっています。micro:bitで磁北を見つけたとしても,環境によって磁場がゆがめられた分,ずれが生じています。
  2. リニアリティ:ある場所において,micro:bitをある方角に向けてから10度ずつ(0度から350度まで)回転させてみます。
    しかし,センサから取得できる方位角は同じよう10度ずつ増加するようには変化しません。
    測定器では測定対象の変化に比例して測定結果が得られなければならないのですが,その比例状態からはずれてしまいます。このずれは特定の方角で最大になり10度ほどになります。
  3. 再現性:ある場所において,ある向きにmicro:bitを固定し,方位角を測定します。
    micro:bitを固定したまま,しばらくしてから方位角を測定すると,ほぼ同じ値が得られます。
    これは何回やっても同じです。(再現性があります)

補足2 追加補正

キャリブレーション(コンパスの調整,LEDを全点灯させる)を行っても,まだ大きな測定誤差が残ります。 そのため,x,y軸のセンサの特性をそろえて,追加補正することを考えます。 x軸+を水平面内で磁北に向けると,x軸センサ値は最大値mag_x_max(正の値で絶対値が最大)になります。
次にx軸-を水平面内で磁北に向けると,x軸センサ値は最小値mag_x_min(負の値で絶対値が最大)になります。
本来x軸+を水平面内で磁北に向けると30μT,x軸-を水平面内で磁北に向けると-30μTが得られるはずです。
x軸センサ値がmag_x_minのときは-30,mag_x_maxのときは30になるような一次変換すると,追加補正されたx軸センサ値が得られます。
同様に考えでy軸センサ値を一次変換すれば,追加補正されたy軸センサ値が得られます。

例 最大値mag_x_maxの求め方
次のプログラムで,測定値mag_x_tmpと最大値mag_x_maxの表示を見ながらmicro:bitのx軸+を磁北方向に向けてその周囲を何回も揺動(往復)させます。

 mag_x_max = -100
 気が済むまでループ {
   mag_x_tmp = x軸地磁気センサ平均値取得関数
     (50回平均くらい,連続取得では最低20msecのインターバルが必要)
   mag_x_maxとmag_x_tmpを表示
   if (mag_x_max < mag_x_tmp) mag_x_max = mag_x_tmp
 }



この図のようにx軸を磁北周辺で揺動させます。
この作業をしていると,最大値mag_x_maxが更新されて,大きな値になっていきます。
(micro:bitを水平面内で回転させても良いのですが,x軸+が磁北付近を通過する回数が多くとれず,またまばらな測定しか行われないので効率が悪いです。揺動させる方が良いと思います。)

例 最小値mag_x_minの求め方
次のプログラムで,測定値mag_x_tmpと最小値mag_x_minの表示を見ながらmicro:bitのx軸-を磁北方向に向けてその周囲を何回も揺動(往復)させます。

 mag_x_min = 100
 気が済むまでループ {
   mag_x_tmp = x軸地磁気センサ平均値取得関数
     (50回平均くらい,連続取得では最低20msecのインターバルが必要)
   mag_x_minとmag_x_tmpを表示
   if (mag_x_tmp < mag_x_min) mag_x_min = mag_x_tmp
 }

この作業をしていると,最大値mag_x_minが更新されて,小さな値になっていきます。

補足3 短インターバル繰り返し測定

磁気センサは,内部でAD変換・I2C通信しており,その結果をプログラムで磁気センサ値として受け取っています。
AD変換・I2C通信は一定の時間を必要とするため,短インターバル繰り返しで磁気センサ値を受け取ろうとすると,思い通りの測定値が得られないことがあります。(実は舞台裏でセンサ値の更新周期が設定されています。)
micro;bitを絶えず動かしながら(取得されるべきセンサ値は不規則に変化するはず)
 (1)インターバル無し
 (2)インターバル10ms
 (3)インターバル20ms
で16回連続測定し,得られた磁気センサ値を観察してみました。

(1)インターバル無し(測定間隔ほぼ0)
16回の測定するときは,時間のかかる測定値表示をせず,配列に測定値を保存して,最速で測定します。測定終了後に,配列に保存されていた測定値をコンソールに表示する手法をとりました。




その結果は次の通りになりました。
16回の取得データはすべて同じ値でした。
データの更新が間に合わず,同じデータを受け取り続けていたと考えられます。 これでは使えません。

started
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078
-16.078, -16.078, -16.078

(2)インターバル10ms(測定間隔10ms)
16回の測定するときは,時間のかかる測定値表示をせず,配列に測定値を保存して,10msのpause(一時停止)を行っています。測定終了後に,配列に保存されていた測定値をコンソールに表示する手法をとりました。




その結果は次の通りになりました。
ところどころで,同じデータが2回続けて得られています。
データの更新が完全には間に合わず,同じデータを受け取ってしまうことがあると考えられます。 これでは使えません。

started
-7.978, 48.16, 29.33
-13.378, 44.86, 33.53
-13.378, 44.86, 33.53  ←
-17.878, 40.36, 35.33
-17.878, 40.36, 35.33  ←
-23.278, 37.66, 37.73
-28.228, 31.51, 38.93
-28.228, 31.51, 38.93  ←
-34.528, 26.41, 39.53
-34.528, 26.41, 39.53  ←
-38.428, 22.51, 39.08
-39.178, 18.46, 39.83
-39.178, 18.46, 39.83  ←
-42.178, 16.06, 39.23
-42.178, 16.06, 39.23  ←
-44.578, 12.76, 38.63

(3)インターバル20ms(測定間隔20ms)
16回の測定するときは,時間のかかる測定値表示をせず,配列に測定値を保存して,20msのpause(一時停止)を行っています。測定終了後に,配列に保存されていた測定値をコンソールに表示する手法をとりました。




その結果は次の通りになりました。
同じデータが繰り返し取得されることなく,測定できています。

started
-2.128, 32.71, 38.78
-2.728, 33.61, 37.88
-3.778, 35.86, 36.08
-4.078, 36.61, 35.93
-3.778, 38.11, 34.73
-4.828, 40.06, 31.88
-5.728, 41.86, 29.93
-6.028, 41.86, 28.13
-6.628, 42.91, 27.38
-9.178, 43.81, 26.48
-11.428, 44.71, 25.13
-11.878, 44.86, 24.53
-12.028, 45.76, 23.93
-14.278, 45.46, 23.48
-15.028, 45.61, 23.33
-16.378, 44.86, 22.13

このテストから,磁気センサでの測定には,20msほどのインターバルが必要なことがわかりました。
なお,「ずっと」ブロックでは舞台裏において20msのpause(一時停止)が行われているため,「ずっと」ブロック中ではプログラム中にpause(一時停止)を書かなくても最新の測定値を得ることができます。
参考 「ずっと」ブロックの動作 

補足4 磁気センサデータ更新間隔の確認

補足3にて,磁気センサでの測定には,20msほどのインターバルが必要なことがわかりました。
舞台裏で,磁気センサデータを取得して,バッファ変数に保存している作業が20msec毎に行われていて,MakeCodeで作成しているプログラム側からセンサ値を読むと,このバッファ変数が返されるため,短いインターバルの磁気センサデータ取得は出来ないことがわかりました。
もう少し,直接的に,磁気センサデータを取得して,バッファ変数に保存している作業が20msec毎に行われている様子を観察するため,次のようなテストを行いました。
連続して磁気センサデータを取得して,センサ値が変化したら,その時刻(稼働時間[μsec])を表示するという作業を行います。
磁気センサ値はノイズを含むため,連続取得するとほとんどの場合は毎回異なる値が得られるはずです。
使用したプログラムは次のものです。




実行結果は長いので最初の部分だけ示します。
1行は取得データ,取得データが変化した時刻で出来ています。

2.17, 3927308
1.42, 3966164
1.27, 3985559
1.87, 4004954
1.42, 4024375
1.57, 4043770
0.82, 4064038
0.97, 4082582
2.02, 4101993
1.27, 4121386
0.67, 4140782
0.97, 4160183
1.12, 4181308
1.42, 4198993
 
 :   :
 
1.87, 7400958
1.27, 7421975

取得データが変化した時刻の差分をとると,値が変化した時間間隔がわかります。
値が変化した時間間隔をグラフ化したものを次に示します。



ほとんどの時間間隔が20000μsec=20msecになりました。
40msec,60msecも有りますが,これはたまたまセンサ値がノイズで変化しなかった場合です。20msecの2倍3倍になっています。
これにより,磁気センサデータ更新間隔は20msecであることが確認出来ました。
またこのプログラムでは100000ループするのに約3.5秒かかっています。1ループ当たり35μsecかかっています。500回以上無意味に同じ値を取得していることになります。
センサ値取得間隔を無視した形でセンサ値を多くとって平均値を求める試みは,意図に反した値の平均化になるので注意が必要なことが確認されました。