20150209_200640

地磁気センサーHMC5883LのモジュールがAmazonで480円で出ていましたので使ってみました。(eBayでは1ドルというのもありました。)
Weaver氏提供のライブラリでとりあえず値を読みだしてみましたが、誤差が大きいようです。
   0deg --- 0.0
90deg --- 2.2(1.57)
180deg --- 4.2(3.14)
270deg --- 5.1(4.71)
* 単位はラジアン、( )内は正しい値

#include "mbed.h"
#include "HMC5883L.h"

Serial pc(USBTX, USBRX); 
HMC5883L compass(p28, p27);

int main() {
    compass.init();
    while(1) {
        pc.printf("raw=%f\r\n",compass.getHeadingXY());
        wait(0.2);
     }
}

HMC5883Lからはx,y,z軸のraw dataを読み取り、このうちx,y軸の値をもとにatan()を計算して方角を知るようになっています。センサー基板を平面上で360°回転させて得られるx,y軸のraw dataをプロットしたのがしたの図です。
fig1
きれいな円(楕円)になりましたが、原点が中心からずれています。これを強制的に補正するコードを追加します。コードで補正といっても、実際に手で基板を回してやる必要があります。1回転する間のx軸、z軸の最大値、最小値を求めてその中心が円の中心となるように座標を移動します。

#include "mbed.h"
#include "HMC5883L.h"

#define STOP 0  //initial
#define CAL 1   //calibration
#define RUN 2   //run
//#define M_PI 3.141592

Serial pc(USBTX, USBRX); 
HMC5883L compass(p28, p27);
Ticker interrupt;

double heading0 = 0.0;
double heading1 = 0.0;
double heading2 = 0.0;
double heading3 = 0.0;
double headingLPF = 0.0;
double initHeading;
double tgtHeading;
double preHeading = 0.0;
unsigned char mode;

int maxX, minX, maxY, minY;
int ofsX = 0;                   //calibration x
int ofsY = 0;                   //calibration y
int counter = 0;

void intrpt() {
    int16_t raw[3];
    
    compass.getXYZ(raw);
    double heading = atan2(static_cast<double>(raw[2]-ofsY), static_cast<double>(raw[0]-ofsX)); //y=raw[2]
    if(heading < 0)heading += 2*M_PI;
    if(heading > 2*M_PI)heading -= 2*M_PI;
    heading3 = heading2;
    heading2 = heading1;
    heading1 = heading0;
    heading0 = heading;
    headingLPF = (heading0 + heading1 + heading2 + heading3)/4; //low pass filter
    switch(mode) {
    case STOP:
        if(counter == 50) {
            initHeading = headingLPF;
            mode = CAL;
            maxX = raw[0];
            minX = raw[0];
            maxY = raw[2];
            minY = raw[2];
            counter = 0;
        }
        break;
    case CAL:
        if(raw[0] > maxX)maxX = raw[0];
        if(raw[0] < minX)minX = raw[0];
        if(raw[2] > maxY)maxY = raw[2];
        if(raw[2] < minY)minY = raw[2];
        if((counter > 50) 
            && (headingLPF > (initHeading-0.01)) 
            && (headingLPF < (initHeading+0.01))) {
            ofsX = (maxX + minX)/2;
            ofsY = (maxY + minY)/2;
            mode = RUN;
            counter = 0;
            pc.printf("ofs=%d,%d\r\n",ofsX,ofsY);
        }
        break;
    case RUN:
        //pc.printf("heading=%f\r\n",headingLPF);
        break;
    }
    counter++;
}

int main() {
    mode = STOP;
    compass.init();
    interrupt.attach(&intrpt, 0.04);
    while(1) {
     }
}

下が補正後のプロット図です。この状態でもまだ数度の誤差がみられました。楕円が斜めに傾いているのが原因かもしれません。
fig2