do335maomao 发表于 2015-2-8 23:01:35

HMC5883L转换方向角与简易校准方法

本帖最后由 do335maomao 于 2015-2-9 11:24 编辑




研究了一晚上稍微有点成果分享下

HMC5883L使用i2c接口,接线很容易

以Arduino Uno为例:
SDA to A4
SCL to A5
Vcc to 3.3V
GND to GND

基本原理很简单:
方向角其实就是X轴和Y轴读数的反正切
而校准其实就是要排除环境中的磁场对地磁场的干扰
另外别忘了当地的磁偏角

如下代码没有使用专门的传感器库
上电后先进行20秒校准,请把传感器任意乱转,各个方向都要转到
然后就会显示校准值,然后持续显示初始值和方向角
不知道怎么在ide里面用中文写注释,所以就保留英文了

个人测试下来和手机上的指南针相差不超过5度,更精细的校准待研究

刚刚接触Arduino,望高手指教




#include <Wire.h> //I2C Arduino Library

#define address 0x1E //001 1110b(0x3C>>1), I2C 7bit address of HMC5883
#define MagnetcDeclination 4.43 //Shanghai
#define CalThreshold 0

int offsetX,offsetY,offsetZ;

void setup()
{
//Initialize Serial and I2C communications
Serial.begin(9600);
Wire.begin();

//Put the HMC5883 IC into the correct operating mode
Wire.beginTransmission(address); //open communication with HMC5883r
Wire.write(0x00); //select configuration register A
Wire.write(0x70); //0111 0000b configuration
Wire.endTransmission();

Wire.beginTransmission(address);
Wire.write(0x02); //select mode register
Wire.write(0x00); //set continuous measurement mode:0x00,single-measurement mode:0x01
Wire.endTransmission();

calibrateMag();
}
void loop()
{
int x,y,z; //triple axis data
getRawData(&x,&y,&z);

//Print out values of each axis
Serial.print("x: ");
Serial.print(x);
Serial.print("y: ");
Serial.print(y);
Serial.print("z: ");
Serial.print(z);
Serial.print(" angle(x,y): ");
Serial.println(calculateHeading(&x,&y,&z));

delay(250);
}

void getRawData(int* x ,int* y,int* z)
{
//Tell the HMC5883L where to begin reading data
Wire.beginTransmission(address);
Wire.write(0x03); //select register 3, X MSB register
Wire.endTransmission();
//Read data from each axis, 2 registers per axis
Wire.requestFrom(address, 6);
if(6<=Wire.available()){
    *x = Wire.read()<<8; //X msb
    *x |= Wire.read(); //X lsb
    *z = Wire.read()<<8; //Z msb
    *z |= Wire.read(); //Z lsb
    *y = Wire.read()<<8; //Y msb
    *y |= Wire.read(); //Y lsb
}
}

int calculateHeading(int* x ,int* y,int* z)
{
float headingRadians = atan2((double)((*y)-offsetY),(double)((*x)-offsetX));
// Correct for when signs are reversed.
if(headingRadians < 0)
    headingRadians += 2*PI;
   
int headingDegrees = headingRadians * 180/M_PI;
headingDegrees += MagnetcDeclination; //the magnetc-declination angle

// Check for wrap due to addition of declination.
if(headingDegrees > 360)
    headingDegrees -= 360;

return headingDegrees;
}

void calibrateMag()
{
int x,y,z; //triple axis data
int xMax, xMin, yMax, yMin, zMax, zMin;
//initialize the variables
getRawData(&x,&y,&z);
xMax=xMin=x;
yMax=yMin=y;
zMax=zMin=z;
offsetX = offsetY = offsetZ = 0;

Serial.println("Starting Calibration......");
Serial.println("Please turn your device around in 20 seconds");

for(int i=0;i<200;i++)
{
    getRawData(&x,&y,&z);
    //get Max and Min
    // this routine will capture the max and min values of the mag X, Y, and Z data while the
    // compass is being rotated 360 degrees through the level plane and the upright plane.
    // i.e. horizontal and vertical circles.
    // This function should be invoked while making continuous measurements on the magnetometers
    if (x > xMax)
      xMax = x;
    if (x < xMin )
      xMin = x;
    if(y > yMax )
      yMax = y;
    if(y < yMin )
      yMin = y;
    if(z > zMax )
      zMax = z;
    if(z < zMin )
      zMin = z;
      
    delay(100);
   
    if(i%10 == 0)
    {
      Serial.print(xMax);
      Serial.print(" ");
      Serial.println(xMin);
    }
}
//compute offsets
if(abs(xMax - xMin) > CalThreshold )
    offsetX = (xMax + xMin)/2;
if(abs(yMax - yMin) > CalThreshold )
    offsetY = (yMax + yMin)/2;
if(abs(zMax - zMin) > CalThreshold )
    offsetZ = (zMax +zMin)/2;
   
Serial.print("offsetX:");
Serial.print("");
Serial.print(offsetX);
Serial.print(" offsetY:");
Serial.print("");
Serial.print(offsetY);
Serial.print(" offsetZ:");
Serial.print("");
Serial.println(offsetZ);

delay(5000);
}




把冗长的数据手册读完了,有人想看的话,可以把自测试模式,空闲模式等的使用方法也写一下

参考资料:
https://www.sparkfun.com/tutorials/301

hardstudy12345 发表于 2015-2-9 09:02:34

正在研究这个,谢谢楼主分享!

kevinfrankchen 发表于 2015-2-27 14:48:06

我用的GY86模块,电子罗盘也是HMC5883L,一直显示I2C通讯失败,但是MPU6050这些I2C都没问题,楼主能点拨一下吗?谢谢

do335maomao 发表于 2015-2-28 14:59:13

kevinfrankchen 发表于 2015-2-27 14:48 static/image/common/back.gif
我用的GY86模块,电子罗盘也是HMC5883L,一直显示I2C通讯失败,但是MPU6050这些I2C都没问题,楼主能点拨一下 ...

gy86上的hmc5883是连在mpu6050的辅助i2c口上的,需要设置mpu6050才能使用罗盘

kevinfrankchen 发表于 2015-2-28 15:40:59

do335maomao 发表于 2015-2-28 14:59 static/image/common/back.gif
gy86上的hmc5883是连在mpu6050的辅助i2c口上的,需要设置mpu6050才能使用罗盘

你好,那你有相关的历程吗?我还以为那个模块坏了,谢谢

do335maomao 发表于 2015-2-28 16:41:11

kevinfrankchen 发表于 2015-2-28 15:40 static/image/common/back.gif
你好,那你有相关的历程吗?我还以为那个模块坏了,谢谢

这个我具体也只是看过数据手册,建议你也看一下

frankpian 发表于 2015-5-29 16:02:19

楼主你好,我看到你的calibrateMag()部分代码的时候,看到只打印出xMax和xMin值,请问校准只需要X轴获取最大和最小即可么?

小白不懂,万分感谢

天天向上/tp 发表于 2015-11-26 19:02:06


请问M_PI是一个什么数??我一直没找到

Dreamer-Men 发表于 2016-4-22 20:14:05

LZ竟然没用HMC5883L库!
页: [1]
查看完整版本: HMC5883L转换方向角与简易校准方法