Pages: [1] 2   Go Down
Author Topic: HMC5883L compass huge error [SOLVED]  (Read 11845 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It's a compass when it's held level.  It's repeatable to within 1 degree when you hold it level and don't move, in any direction.  But it can be off by 90 degrees in an absolute sense.  Not accurate at all for finding North.  Any ideas?  Mistakes in code?

It's mounted upside down in my application so I fixed it in the code by 360-heading.  It seems to work very differently when I mount it right side up, but upon further analysis, the error is the same just reversed?

Could I use a simple translation table to translate the error degrees to the correct number?  
At least it's consistent!

I don't see the point of making it 3D using accell data on my board.  The Z data is consistent and constant when I hold it level, it is near the maximum amount when you rotate it.  So I don't understand how knowing the orientation, if it's changing, plus Z data, will help?

It's not as accurate in an absolute sense as this:
HMC6452  
No complicated math if you hold it near level!

Code:
/*
Arduino example for interfacing with the HMC5883L
by: Jordan McConnell SparkFun Electronics
created on: 6/30/11
*/
#include <Wire.h> //I2C Arduino Library
#define address 0x1E //0011110b, I2C 7bit address of HMC5883

int cnt=0;
int x,y,z;
long xx,yy,zz;

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 HMC5883
  Wire.send(0x02); //select mode register
  Wire.send(0x00); //continuous measurement mode
  Wire.endTransmission();
}
void loop(){  
  //Tell the HMC5883 where to begin reading data
  Wire.beginTransmission(address);
  Wire.send(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.receive()<<8; //X msb
    x |= Wire.receive(); //X lsb
    z = Wire.receive()<<8; //Z msb
    z |= Wire.receive(); //Z lsb
    y = Wire.receive()<<8; //Y msb
    y |= Wire.receive(); //Y lsb
  }
  /*
  //Print out values of each axis
  Serial.print("x: ");
  Serial.print(x);
  Serial.print("  y: ");
  Serial.print(y);
  Serial.print("  z: ");
  Serial.println(z);
  */
//Serial.print(z);Serial.write(32);
//repeatable within 1 deg, but off by 90 deg?
if(avgxyz())
if(zz>4800){  //near level 5200 max down, in MA
  //Serial.print(xx);Serial.write(32);Serial.println(yy);
  float heading=atan2(yy,xx);
  heading*=(180/M_PI);
  //declination here degrees
  if(heading<0)heading+=360;
  if(heading>360)heading-=360;  //for + declination only  
  //upside down on board so...
  heading=360-heading;
  Serial.println(heading);
  }
delay(50);  //50 or 150 more acc 3s
}
boolean avgxyz(){
const int N=20;  //matches delay 20*50=1s
boolean b;
cnt++;
if(cnt%N==1)xx=yy=zz=0;
xx+=x;
yy+=y;
zz+=z;
if(b=!(cnt%N)){
  xx=xx*10/N;yy=yy*10/N;zz=zz*10/N; }
return(b);
}
« Last Edit: April 10, 2012, 12:33:17 pm by sbright33 » Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Offline Offline
Edison Member
*
Karma: 35
Posts: 1429
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think this might be the problem:
Code:
  if(6<=Wire.available()){
The processor may not have read all 6 bytes when you get here but you don't stop and wait. You will go straight through and process the variable x, y and z as if you had read something.
Try changing this:
Code:
  if(6<=Wire.available()){
    x = Wire.receive()<<8; //X msb
    x |= Wire.receive(); //X lsb
    z = Wire.receive()<<8; //Z msb
    z |= Wire.receive(); //Z lsb
    y = Wire.receive()<<8; //Y msb
    y |= Wire.receive(); //Y lsb
  }

to this:
Code:
  while(Wire.available() < 6);
  x = Wire.receive()<<8; //X msb
  x |= Wire.receive(); //X lsb
  z = Wire.receive()<<8; //Z msb
  z |= Wire.receive(); //Z lsb
  y = Wire.receive()<<8; //Y msb
  y |= Wire.receive(); //Y lsb

Pete
Logged

Where are the Nick Gammons of yesteryear?

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wire.requestFrom is blocking. That part is fine. Changing to waiting for it to become available simply builds in a potential endless loop. Either you get 6 bytes or you don't. Waiting doesn't change that.
Logged

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Good points to both of you.  I didn't like the way the code looked so I fixed it anyway.  I don't see how that can possibly be related to my problem.  Funny, it seemed to help a little!  I'd like to point out that it is probably a hardware issue.  I have used other example code without modification and it still cannot find North and South correctly.  How can I fix this problem?  Lookup table?  The number are consistently wrong.  When I draw lines on a paper and put it back to those lines, it always reads exactly the same number of degrees when it's kept level.  But off by 20-90 degrees from the orientation of my house.  I've moved it 2 feet from my laptop to avoid interference.  It seems consistent no matter where I place the unit before I rotate it.  Any more ideas?
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Change the gain maybe? See the examples in the datasheet.
Logged

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Tried all the gain settings using a different Example and library code. 
When I face it's arbitrary North I get 0, 50, 130, 320 for N,E,S,W
That is 0,-40,-50,+50 errors
or 50,0,0,100 errors if I use S as my reference.
What next?
Use the other chip...
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You are taking me out of my area of expertise here, and I don't have the chip to hand. All I can suggest is Googling for someone who might have sample code. If their exact code works, and yours doesn't maybe a bad chip?

I must admit I couldn't find much except a blog written in Swedish.
Logged

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It's a popular chip.  I've tried 3 different examples.  All the same results. 
The X reading is consistent.  The Y reading is consistent.  Perhaps they need to be scaled to match each other in magnitude?
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I don't think just scaling will do it for me.  Here's the results I've obtained so far.  It is very much dependent on holding it exactly level.  Even a few degrees of tilt effect the heading, although it does not effect the Z data.  Which means I cannot use tilt compensation to fix this problem?  The older 2D chip does not suffer from this problem, it can tilt 10-20 degrees from level without much difference.

North is arbitrary where y=0.  It's not far off from reality.
For W,S - I did not turn exactly 90 degrees, instead I turned until the appropriate? variable was 0.

Direction X Y
N 2800 0
W 0 1100  peak value is 1400 further south when x>0 about 200
S 0 -2800  this makes no sense compared to North.  The wrong value x is 0, should be y?
E 2000 -2000 exactly 90 degrees from N

0 +max and -max 0
are right next to each other when facing West
These should be W and S.

It reads 0 -max when it's facing about S, should read this when facing E.

It doesn't matter which library or sample code I use.
I need some tissues for my issues...
Any ideas to work AROUND this bug?

Many others have a similar problem online.  Could it be they shipped 100's of bad chips with this defect?
http://arduino.cc/forum/index.php?topic=55159.20;wap2
« Last Edit: April 10, 2012, 10:05:36 am by sbright33 » Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Could it be too close to the 328 processor?  The other chip didn't mind.

It's a simple algorithm to transform the wrong heading to the right one:

if heading <90 then heading*=2;
if heading >=90 <270
   heading=180+(heading-90)/2
//else already correct within degree or 2

But why?

New problem:
At E,W it's extremely sensitive to tilt, but not N,S;
The Z value does not change when it's level or near level, so there's no way to adjust the X,Y heading?
I think this chip is a piece of junk, and many others agree?
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PROBLEM SOLVED!

Just tilt it 90 degrees around the Y axis marked on the board.
Modify the code like this:
//if(zz>4800)
{
  float heading=atan2(yy,zz);  //zz instead of xx

Not so sensitive to tilting anymore!
But still not as good as old chip...

Tilting problem solved without any complex calculations (below) !!!

Pretend the board/chip is a solar panel.  If you first tilt it 0-30 degrees up towards the N or W, not down toward the ground, then tilting +-15 doesn't change the heading output AT ALL.  This solves the tilt problem without any calculation.  I noticed this with my 2-axis chip as well.  It has to do with Earth's inclination angle of the magnetic field.

If you first tilt it 5 degrees in the wrong direction, then +-1 degree makes a huge difference in heading, this is bad.

My other chip should be tilted towards the North.  Why this difference between chips?

70 degrees in USA???  Oh, 20 degrees from straight down.
http://en.wikipedia.org/wiki/Earth%27s_magnetic_field

Let's say you had a servo to tilt the sensor any angle you want around the Y axis.  If you don't know which way you're facing, how would you know which way to tilt it???  The answer is simple.  Maximize the raw X value (the new orientation).

Let's say you don't have a servo, and can't control the tilt angle.  But it's on a moving Jeep or Quadcopter/plane.
Just WAIT until the X value is near it's const int maximum value.  Then read Y,Z.

Tilt problem solved!

Next minor issue:
My breakout board has 3 chips on it.  Accell, Gyro, and this one.
This chip requires the board to stand upright.
The other 2 work best when it's lying flat.
They have been calibrated that way.
Otherwise I have to change the code to swap the sensors.
That's a problem...
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Learned some more from experimenting:
If you turn my board upside down, it doesn't work as well, even after fixing 360-heading.
If you tilt it too far, much past 30, even in the "good" direction, it reads the OPPOSITE heading!
Tricky.
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

0
Offline Offline
Newbie
*
Karma: 0
Posts: 45
Krazatchu Design Systems
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You will want to implement some tilt compensation for the compass, using the accelerometer to figure roll, pitch and yaw.
Here's a library we (my friend and I) wrote: http://n0m1.com/2012/02/27/6dof-arduino-compass-accelerometer/
Logged

Michael
----------------------
http://www.krazatchu.ca

Offline Offline
Edison Member
*
Karma: 6
Posts: leet
If you're not living on the Edge, you're taking up too much space!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So I just take the x,y,z values from here:
http://code.bildr.org/download/959.zip

and plug them into here directly?  Combining the 2 sketches.
sixDOF.compCompass(-(raw.ZAxis), -(raw.XAxis), raw.YAxis, -(accel.z()), -(accel.x()), accel.y(), true);

Mine are raw values also?
Why do the first 2 - Z,X have minus signs, Y does not?
Do I include these signs before my x,y,z too?

I don't see how this can work for small tilt angles, because the Z value doesn't change, in my case.
Can you explain how it works without this value?
Let's say you have tilt and X,Y from HMC5883, but no Z.
Logged

If you fall... I'll be there for you!
-Floor

Skype Brighteyes3333
(262) 696-9619

0
Offline Offline
Newbie
*
Karma: 0
Posts: 45
Krazatchu Design Systems
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
So I just take the x,y,z values from here:
http://code.bildr.org/download/959.zip
and plug them into here directly?  Combining the 2 sketches.

Yes, that is correct. Raw values or scaled doesn't matter, raw is faster and more accurate.
In process, the library scales everything up with an equal multiplier to reduce quantization losses.

Quote
sixDOF.compCompass(-(raw.ZAxis), -(raw.XAxis), raw.YAxis, -(accel.z()), -(accel.x()), accel.y(), true);
Mine are raw values also?
Why do the first 2 - Z,X have minus signs, Y does not?
Do I include these signs before my x,y,z too?

You may want to play with the arrangement and sign of xyz depending on the orientation of your accelerometer and compass.
My PCB is mounted vertically and the xy of the accelerometer was not the same as the xy of the compass.
This is why I have (-z, -x, +y).  The default order is (+x, +y, +z).

Quote
I don't see how this can work for small tilt angles, because the Z value doesn't change, in my case.
Can you explain how it works without this value?
Let's say you have tilt and X,Y from HMC5883, but no Z.

The libraries trigonometry is non-linear and ratiometric.
Non-linear as in on axis effect is the lowest, while a right angle axis will have the highest effect.
Ratiometric as in a change in only one axis effects the ratio and therefore the end result.
Logged

Michael
----------------------
http://www.krazatchu.ca

Pages: [1] 2   Go Up
Jump to: