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

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

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 库
 
#define address 0x1E //001 1110b(0x3C>>1), HMC5883的7位i2c地址
#define MagnetcDeclination 4.43 //笔者所在地磁偏角,请根据情况自行百度
#define CalThreshold 0
 
int offsetX,offsetY,offsetZ;
 
void setup()
{
  //初始化串口和i2c
  Serial.begin(9600);
  Wire.begin();
 
  //设置HMC5883模式
  Wire.beginTransmission(address); //开始通信
  Wire.write(0x00); //选择配置寄存器A
  Wire.write(0x70); //0111 0000b,具体配置见数据手册
  Wire.endTransmission();
 
  Wire.beginTransmission(address);
  Wire.write(0x02); //选择模式寄存器
  Wire.write(0x00); //连续测量模式:0x00,单一测量模式:0x01
  Wire.endTransmission();
 
  calibrateMag();
}
void loop()
{
  int x,y,z; //三轴数据
  getRawData(&x,&y,&z);
 
  //输出数据
  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));//输出x,y平面方向角
 
  delay(250);
}
 
void getRawData(int* x ,int* y,int* z)
{
  Wire.beginTransmission(address);
  Wire.write(0x03); //从寄存器3开始读数据
  Wire.endTransmission();
  //每轴的数据都是16位的
  Wire.requestFrom(address, 6);
  if(6<=Wire.available()){
    *x = Wire.read()<<8; //X msb,X轴高8位
    *x |= Wire.read(); //X lsb,X轴低8位
    *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));
  // 保证数据在0-2*PI之间
  if(headingRadians < 0)
    headingRadians += 2*PI;
 
  int headingDegrees = headingRadians * 180/M_PI;
  headingDegrees += MagnetcDeclination; //磁偏角
 
  // <span style="font-family: Arial, Helvetica, sans-serif;">保证数据在0-360之间</span>
  if(headingDegrees > 360)
    headingDegrees -= 360;
 
  return headingDegrees;
}
 
void calibrateMag()
{
  int x,y,z; //三轴数据
  int xMax, xMin, yMax, yMin, zMax, zMin;
  //初始化
  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);
    // 计算最大值与最小值
    // 计算传感器绕X,Y,Z轴旋转时的磁场强度最大值和最小值
    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);
    }
  }
  //计算修正量
  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


  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值