天天看點

HMC5883L 轉換方向角與簡易校準方法

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. #define address 0x1E //001 1110b(0x3C>>1), HMC5883的7位i2c位址  
  3. #define MagnetcDeclination 4.43 //筆者所在地磁偏角,請根據情況自行百度  
  4. #define CalThreshold 0  
  5. int offsetX,offsetY,offsetZ;  
  6. void setup()  
  7. {  
  8.   //初始化序列槽和i2c  
  9.   Serial.begin(9600);  
  10.   Wire.begin();  
  11.   //設定HMC5883模式  
  12.   Wire.beginTransmission(address); //開始通信  
  13.   Wire.write(0x00); //選擇配置寄存器A  
  14.   Wire.write(0x70); //0111 0000b,具體配置見資料手冊  
  15.   Wire.endTransmission();  
  16.   Wire.beginTransmission(address);  
  17.   Wire.write(0x02); //選擇模式寄存器  
  18.   Wire.write(0x00); //連續測量模式:0x00,單一測量模式:0x01  
  19.   Wire.endTransmission();  
  20.   calibrateMag();  
  21. }  
  22. void loop()  
  23. {  
  24.   int x,y,z; //三軸資料  
  25.   getRawData(&x,&y,&z);  
  26.   //輸出資料  
  27.   Serial.print("x: ");  
  28.   Serial.print(x);  
  29.   Serial.print("  y: ");  
  30.   Serial.print(y);  
  31.   Serial.print("  z: ");  
  32.   Serial.print(z);  
  33.   Serial.print(" angle(x,y): ");  
  34.   Serial.println(calculateHeading(&x,&y,&z));//輸出x,y平面方向角  
  35.   delay(250);  
  36. }  
  37. void getRawData(int* x ,int* y,int* z)  
  38. {  
  39.   Wire.beginTransmission(address);  
  40.   Wire.write(0x03); //從寄存器3開始讀資料  
  41.   Wire.endTransmission();  
  42.   //每軸的資料都是16位的  
  43.   Wire.requestFrom(address, 6);  
  44.   if(6<=Wire.available()){  
  45.     *x = Wire.read()<<8; //X msb,X軸高8位  
  46.     *x |= Wire.read(); //X lsb,X軸低8位  
  47.     *z = Wire.read()<<8; //Z msb  
  48.     *z |= Wire.read(); //Z lsb  
  49.     *y = Wire.read()<<8; //Y msb  
  50.     *y |= Wire.read(); //Y lsb  
  51.   }  
  52. }  
  53. int calculateHeading(int* x ,int* y,int* z)  
  54. {  
  55.   float headingRadians = atan2((double)((*y)-offsetY),(double)((*x)-offsetX));  
  56.   // 保證資料在0-2*PI之間  
  57.   if(headingRadians < 0)  
  58.     headingRadians += 2*PI;  
  59.   int headingDegrees = headingRadians * 180/M_PI;  
  60.   headingDegrees += MagnetcDeclination; //磁偏角  
  61.   // <span style="font-family: Arial, Helvetica, sans-serif;">保證資料在0-360之間</span>  
  62.   if(headingDegrees > 360)  
  63.     headingDegrees -= 360;  
  64.   return headingDegrees;  
  65. }  
  66. void calibrateMag()  
  67. {  
  68.   int x,y,z; //三軸資料  
  69.   int xMax, xMin, yMax, yMin, zMax, zMin;  
  70.   //初始化  
  71.   getRawData(&x,&y,&z);    
  72.   xMax=xMin=x;  
  73.   yMax=yMin=y;  
  74.   zMax=zMin=z;  
  75.   offsetX = offsetY = offsetZ = 0;  
  76.   Serial.println("Starting Calibration......");  
  77.   Serial.println("Please turn your device around in 20 seconds");  
  78.   for(int i=0;i<200;i++)  
  79.   {  
  80.     getRawData(&x,&y,&z);  
  81.     // 計算最大值與最小值  
  82.     // 計算傳感器繞X,Y,Z軸旋轉時的磁場強度最大值和最小值  
  83.     if (x > xMax)  
  84.       xMax = x;  
  85.     if (x < xMin )  
  86.       xMin = x;  
  87.     if(y > yMax )  
  88.       yMax = y;  
  89.     if(y < yMin )  
  90.       yMin = y;  
  91.     if(z > zMax )  
  92.       zMax = z;  
  93.     if(z < zMin )  
  94.       zMin = z;  
  95.     delay(100);  
  96.     if(i%10 == 0)  
  97.     {  
  98.       Serial.print(xMax);  
  99.       Serial.print(" ");  
  100.       Serial.println(xMin);  
  101.     }  
  102.   }  
  103.   //計算修正量  
  104.   if(abs(xMax - xMin) > CalThreshold )  
  105.     offsetX = (xMax + xMin)/2;  
  106.   if(abs(yMax - yMin) > CalThreshold )  
  107.     offsetY = (yMax + yMin)/2;  
  108.   if(abs(zMax - zMin) > CalThreshold )  
  109.     offsetZ = (zMax +zMin)/2;  
  110.   Serial.print("offsetX:");  
  111.   Serial.print("");  
  112.   Serial.print(offsetX);  
  113.   Serial.print(" offsetY:");  
  114.   Serial.print("");  
  115.   Serial.print(offsetY);  
  116.   Serial.print(" offsetZ:");  
  117.   Serial.print("");  
  118.   Serial.println(offsetZ);  
  119.   delay(5000);    
  120. }  

把冗長的資料手冊讀完了,有人想看的話,可以把自測試模式,空閑模式等的使用方法也寫一下

參考資料:

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

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

繼續閱讀