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

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

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

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

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

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

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

刚刚接触Arduino,望高手指教

[cpp]  view plain  copy
  1. #include <Wire.h> //I2C 库  
  2.    
  3. #define address 0x1E //001 1110b(0x3C>>1), HMC5883的7位i2c地址  
  4. #define MagnetcDeclination 4.43 //笔者所在地磁偏角,请根据情况自行百度  
  5. #define CalThreshold 0  
  6.    
  7. int offsetX,offsetY,offsetZ;  
  8.    
  9. void setup()  
  10. {  
  11.   //初始化串口和i2c  
  12.   Serial.begin(9600);  
  13.   Wire.begin();  
  14.    
  15.   //设置HMC5883模式  
  16.   Wire.beginTransmission(address); //开始通信  
  17.   Wire.write(0x00); //选择配置寄存器A  
  18.   Wire.write(0x70); //0111 0000b,具体配置见数据手册  
  19.   Wire.endTransmission();  
  20.    
  21.   Wire.beginTransmission(address);  
  22.   Wire.write(0x02); //选择模式寄存器  
  23.   Wire.write(0x00); //连续测量模式:0x00,单一测量模式:0x01  
  24.   Wire.endTransmission();  
  25.    
  26.   calibrateMag();  
  27. }  
  28. void loop()  
  29. {  
  30.   int x,y,z; //三轴数据  
  31.   getRawData(&x,&y,&z);  
  32.    
  33.   //输出数据  
  34.   Serial.print("x: ");  
  35.   Serial.print(x);  
  36.   Serial.print("  y: ");  
  37.   Serial.print(y);  
  38.   Serial.print("  z: ");  
  39.   Serial.print(z);  
  40.   Serial.print(" angle(x,y): ");  
  41.   Serial.println(calculateHeading(&x,&y,&z));//输出x,y平面方向角  
  42.    
  43.   delay(250);  
  44. }  
  45.    
  46. void getRawData(int* x ,int* y,int* z)  
  47. {  
  48.   Wire.beginTransmission(address);  
  49.   Wire.write(0x03); //从寄存器3开始读数据  
  50.   Wire.endTransmission();  
  51.   //每轴的数据都是16位的  
  52.   Wire.requestFrom(address, 6);  
  53.   if(6<=Wire.available()){  
  54.     *x = Wire.read()<<8; //X msb,X轴高8位  
  55.     *x |= Wire.read(); //X lsb,X轴低8位  
  56.     *z = Wire.read()<<8; //Z msb  
  57.     *z |= Wire.read(); //Z lsb  
  58.     *y = Wire.read()<<8; //Y msb  
  59.     *y |= Wire.read(); //Y lsb  
  60.   }  
  61. }  
  62.    
  63. int calculateHeading(int* x ,int* y,int* z)  
  64. {  
  65.   float headingRadians = atan2((double)((*y)-offsetY),(double)((*x)-offsetX));  
  66.   // 保证数据在0-2*PI之间  
  67.   if(headingRadians < 0)  
  68.     headingRadians += 2*PI;  
  69.    
  70.   int headingDegrees = headingRadians * 180/M_PI;  
  71.   headingDegrees += MagnetcDeclination; //磁偏角  
  72.    
  73.   // <span style="font-family: Arial, Helvetica, sans-serif;">保证数据在0-360之间</span>  
  74.   if(headingDegrees > 360)  
  75.     headingDegrees -= 360;  
  76.    
  77.   return headingDegrees;  
  78. }  
  79.    
  80. void calibrateMag()  
  81. {  
  82.   int x,y,z; //三轴数据  
  83.   int xMax, xMin, yMax, yMin, zMax, zMin;  
  84.   //初始化  
  85.   getRawData(&x,&y,&z);    
  86.   xMax=xMin=x;  
  87.   yMax=yMin=y;  
  88.   zMax=zMin=z;  
  89.   offsetX = offsetY = offsetZ = 0;  
  90.    
  91.   Serial.println("Starting Calibration......");  
  92.   Serial.println("Please turn your device around in 20 seconds");  
  93.    
  94.   for(int i=0;i<200;i++)  
  95.   {  
  96.     getRawData(&x,&y,&z);  
  97.     // 计算最大值与最小值  
  98.     // 计算传感器绕X,Y,Z轴旋转时的磁场强度最大值和最小值  
  99.     if (x > xMax)  
  100.       xMax = x;  
  101.     if (x < xMin )  
  102.       xMin = x;  
  103.     if(y > yMax )  
  104.       yMax = y;  
  105.     if(y < yMin )  
  106.       yMin = y;  
  107.     if(z > zMax )  
  108.       zMax = z;  
  109.     if(z < zMin )  
  110.       zMin = z;  
  111.    
  112.     delay(100);  
  113.    
  114.     if(i%10 == 0)  
  115.     {  
  116.       Serial.print(xMax);  
  117.       Serial.print(" ");  
  118.       Serial.println(xMin);  
  119.     }  
  120.   }  
  121.   //计算修正量  
  122.   if(abs(xMax - xMin) > CalThreshold )  
  123.     offsetX = (xMax + xMin)/2;  
  124.   if(abs(yMax - yMin) > CalThreshold )  
  125.     offsetY = (yMax + yMin)/2;  
  126.   if(abs(zMax - zMin) > CalThreshold )  
  127.     offsetZ = (zMax +zMin)/2;  
  128.    
  129.   Serial.print("offsetX:");  
  130.   Serial.print("");  
  131.   Serial.print(offsetX);  
  132.   Serial.print(" offsetY:");  
  133.   Serial.print("");  
  134.   Serial.print(offsetY);  
  135.   Serial.print(" offsetZ:");  
  136.   Serial.print("");  
  137.   Serial.println(offsetZ);  
  138.    
  139.   delay(5000);    
  140. }  
把冗长的数据手册读完了,有人想看的话,可以把自测试模式,空闲模式等的使用方法也写一下

参考资料:

https://www.sparkfun.com/tutorials/301


转自:http://blog.csdn.net/do335maomao/article/details/43916467

  • 7
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值