INDEX(各項目ごとの目次)

[HOME]  [Processing関係]  [Arduino関係]  [マテリアル関係]  [秋葉原工作マップ]

2008年用ですが、部分的に内容を更新しています(2010/06/14)。
また、[建築農業工作ゼミ2009-2010]とも連動していますので、そちらにも幾つかサンプルがあります。
:

ラベル デジタルコンパス の投稿を表示しています。 すべての投稿を表示
ラベル デジタルコンパス の投稿を表示しています。 すべての投稿を表示

8/18/2008

Arduino デジタルコンパス/HMC6352

デジタルコンパス(方位センサ)によって、物体の方位を磁気的に調べることができます。Arduinoで制御可能あるいは入手しやすいデジタルコンパスとして幾つか以下に挙げておきます。

デジタルコンパスの種類:
・「RDCM-802」(秋月電子:¥3400
・「HMC1052L」(Sparkfun:$24.95ストロベリーリナックス:¥3480
・「HMC6352」(Sparkfun:$59.95ストロベリーリナックス:¥7350スイッチサイエンス:¥6980
・「HM55B(日立製)」(Parallax:$29.99日本マイクロボット教育社:¥4450
・「CMPS03」(RoboticsConnection:$55)

それぞれの性能など:
「RDCM-802」は、3ビットの信号で8方位の解像度しかないので、大体の向きを調べるくらいしかできません。
「HMC1052L」は、ArduinoのanalogRead()で入力すれば10ビット(1024段階)の解像度が得られます。
「HMC6352」は、I2C通信で0.0〜359.9度(0.1度ずつ)の分解能があります。
「HM55B」は、シリアル通信で11ビット(2048段階)の解像度が得られます。
「CMPS03」は、I2C通信で3600の分解能。

サンプルソースについて:
「HMC6352」については、WiringのExamplesのサイトにサンプルソース(Standbyモード)があり、I2C通信のライブラリを用いればArduinoでも使用可能になります。ArduinoのPlaygroundにもサンプルがあります。
「HM55B」については、ArduinoのPlaygroundのサイトにサンプルソースがあります。
「CMPS03」については、Arduino用のライブラリがあります。

HMC6352の使い方:
今回は、「HMC6352」デジタルコンパスモジュールの実験をしてみます。
「HMC6352」は、I2C通信で方位データを得るので、I2C通信のライブラリを用います。ライブラリを取り入れるには、メニューバーのSketch>Import Library>Wireを選択することで「#include <Wire.h>」の一文が自動的にプログラムを書く欄に挿入されます。ArduinoでのI2C通信は、SDA端子(データ用端子)はアナログ入力の4番ピン、SCK端子(クロック用端子)はアナログ入力の5番ピンに限定されます。「HMC6352」は、5V電源でも許容範囲ですが今回は3.3Vを使用することにします。接続方法は以下の様になります。



アドレスについて:
I2C通信では、マスターデバイスとスレーブデバイスという関係性があります。マスターデバイスには複数のスレーブデバイスが接続できるメリットがあり、複数のスレーブデバイスはマスターデバイスから制御されることになります。今回の場合は、Arduino基盤がマスターデバイス、HMC6325がスレーブデバイスになります。複数のスレーブデバイスが接続可能であるため、スレーブデバイス側にはどのデバイスであるかを識別するためのアドレスが必要となります。データシートによれば、HMC6325のデフォルトのアドレスは16進数の「0x42」であり、今回はこのアドレスを使うことにします。ただし、識別されるアドレスは8ビット中の上位7ビットであり、「0x42」を1ビット右にシフト「>>」した値になります(16進数の「0x42」は、二進数の「01000010」であり、1ビット右にシフトするということは、二進数の「01000010」の桁を右に一桁ずらすことになるので、「00100001」(=0x21)になるということです/ビットシフトに関しては「Arduino-Processing シリアル通信3」を参照)。

モード切替について:
HMC6325には、マスターデバイスから「A」を送信することで方位を計測開始しデータを読み取る「Standbyモード」と、一旦「A」を送信後データを読み取るごとに自動的に計測/出力する「Queryモード」、そして設定した周期(1Hz、5Hz、10Hz、20Hz、)で連続的に計測/出力した結果を読み取る「Continuousモード」があります。
今回は「Continuousモード(20Hz)」を使うことにします。この設定をするためには、RAM書き込み用コマンド「G」、書き込み先(レジスタ)の「0x74」、そして設定内容の8ビット「0111010」あるいは16進数の「0x72」を送信します。ちなみに、設定内容については、

0x50:Standbyモード(デフォルト)
0x51:Queryモード
0x52:Continuousモード(10Hz,Periodic Set/Reset=ON)
0x72:Continuousモード(20Hz,Periodic Set/Reset=ON)

となります(詳細はデータシートを参照して下さい)。
シリアルモニターを使って読み取り値を出力することにします。


//I2C通信ライブラリを取り込む
#include <Wire.h>

//デジタルコンパスモジュールのアドレス設定
int compassAddress = 0x42 >> 1; //=0x21
//読み込み値(角度)の変数を用意
int reading = 0;

void setup() {
//I2C通信開始
Wire.begin();
//角度表示のためのシリアル通信開始
//Serial.begin(9600);

//Continuous Modeに設定する
Wire.beginTransmission(compassAddress);
//RAM書き込み用コマンド
Wire.send('G');
//書き込み先指定
Wire.send(0x74);
//モード設定
Wire.send(0x72);
//通信終了
Wire.endTransmission();
//処理時間
delayMicroseconds(70);
}

void loop() {
//デバイスに2バイト分のデータを要求する
Wire.requestFrom(compassAddress, 2);
//要求したデータが2バイト分来たら
if(Wire.available()>1){
//1バイト分のデータの読み込み
reading = Wire.receive();
//読み込んだデータを8ビット左シフトしておく
reading = reading << 8;
//次の1バイト分のデータを読み込み
//一つ目のデータと合成(2バイト)
reading += Wire.receive();
//2バイト分のデータを10で割る
reading /= 10;
Serial.println(reading);
}
//処理のために少し待つ(20Hz)
delay(50);
}


まずは、Wire.begin()でI2C通信を開始します。「HMC6325」の初期設定では、「Standbyモード」になっているため、setup(){...}内で「Continuousモード」に切り替えます。設定内容を「HMC6325」のRAMに書き込むために「G」というコマンド、書き込み先となる専用レジスタの「0x74」、設定内容となる8ビットの「0x72」をWire.send()を使って送信します。
方位角度の読み込みは、Wire.requestFrom()でアドレスと何バイト分のデータかを指定して要求します。計測結果は0〜3599までの値(360度を10倍した値)を2バイト(16ビット)で返してきます。Wire.available()によって2バイト分読み込み可能なデータをカウントしたら、Wire.receive()によって、2バイトの値を2回に分けて読み込みます。一度に読み込むことができる値は1バイト(8ビット、0〜255)までなので、最初の1バイトを読み込んだら、その値を8ビット左にビットシフト(8桁左にビットシフトするということは、256倍することと等しくなります)、その値に次の1バイト分のデータを加算します。値は10倍されているので、最終的に10で割って0〜359度の角度として取り込みます(float型を使えば0.0〜359.9で出力します)。20Hzごとに計測する「Continuousモード」に設定したので、delay(50)を挿入してループの周波数も20Hzにしておきました。

測定結果をProcessingへシリアル通信するのであれば、2回に分けて読み取った値(2バイト分)をそのまま1バイトずつ2回送信(合計2バイト)し、Processing側で受け取ってから二つの値を合成すればいいでしょう。値を合成するには、一個目の値を左に8ビットシフト(または256倍)してから二個目の値を足せば0〜3599の値として得ることができるはずです。BYTEフォーマットなら、そのまま2回で送信します。文字列でデリミタ(区切り記号)を挿入して送信する場合は、2回目の送信時にSerial.println()で改行記号を用いて送信します(Processing側での受信については、「Arduino-Processing シリアル通信3(複数の値を送信する場合)」あるいは「Arduino-Processing シリアル通信5(文字列で送信する場合)」を参照して下さい。

BYTEフォーマットで送信する場合:

void loop() {
Wire.requestFrom(compassAddress, 2);
if(Wire.available()>1){
//2回に分けて読み取った値を
//BYTEフォーマットで2個送信する
Serial.print(Wire.receive(),BYTE);//1個目
Serial.print(Wire.receive(),BYTE);//2個目
}
delay(50);
}

あるいは、文字列として送信する場合:

void loop() {
Wire.requestFrom(compassAddress, 2);
if(Wire.available()>1){
//2回に分けて読み取った値を
//DECフォーマットで2個送信する
Serial.print(Wire.receive(),DEC);
Serial.print(",");//デリミタを挿入送信
Serial.println(Wire.receive(),DEC);//改行記号つき
}
delay(50);
}


また、「C」を送信することでキャリブレーションモードに入り、周囲の磁気の影響によるモジュール内素子のゆがみをなくし正常な状態に調整することが出来ます。モジュールを平らな場所に置いてから6秒から3分以内に、ニ周程度回転させ、「E」を送信することで終了します。20秒以上かけて2回転させれば、正確なキャリブレーションになります。

//キャリブレーション開始コマンド送信
Wire.beginTransmission(compassAddress);
Wire.send('C');
Wire.endTransmission();

//30秒ほど待つ(6秒〜3分まで)
//この間に数回モジュールを回転させる
delay(30000);

//キャリブレーション終了コマンド送信
Wire.beginTransmission(compassAddress);
Wire.send('E');
Wire.endTransmission();


デジタルコンパスをモータなどの磁気を発するものの近くに設置すると磁気的影響を受けるので、少し離れた場所に設置したほうがいいでしょう。


[目次:Processing関係]  [HOMEへ戻る]  [目次:Arduino関係]