tilt compensated magnetometer

I'm trying to use n0m1's compass & accelerometer library with an ADXL335 accelerometer & an HCM5883L magnetometer (both from Sparkfun).

I have the accelerometer & magnetometer on a breadboard next to each other, with the XYZ diagrams printed on the PCBs both indicating the same directions. But however I pass the data into the library function (switching them around, negating or not negating them, etc.) the compensated heading is always completely wrong.

These are my observations

  • When I tilt the unit in the direction of the X arrows on the PCBs, the reading from the accelerometer X increases & the reading from the magnetometer X decreases
  • When I tilt the unit in the direction of the Y arrows on the PCBs, the reading from the accelerometer Y increases & the reading from the magnetometer Y decreases
  • When I invert the unit to affect Z, the reading from the accelerometer increases & the reading from the magnetometer decreases

This led me to believe that all I need to do is negate all of the readings from either the accelerometer or magnetometer to get a sensible compensated heading? But I have tried doing this;

compCompass(magX, magY, magZ, -(accelX), -(accelY), -(accelZ))

and the compensated heading is still nonesense.

Am I doing something wrong here? I've been stuck on this since last week, it's really starting to annoy me!

Define what you mean by "tilt in the direction of the X arrows" - I would understand that to mean an imaginary normal to and above the PCB would travel in the direction of the arrow. In that case the X acceleration should go negative since the acceleration vector is now pointing away from the X arrow.

Test that you understand your axes correctly. With the Z arrow pointing upwards the Z accel should be positive, with the X arrow pointing upwards the X accel should be positive and if the Y arrow is pointing upwards the Y accel should be positive. The board may be mismarked, you may be reading the wrong registers, or whatever.

Then check that the same thing happens with the magnetometer (you have to establish the 3-D direction of the magnetic field of course).

I started trying to figure out the axes properly yesterday; I thought I had it figured out already, but I started reading the Freescale Semi application note that the library I'm trying to use claims to be based upon & it uses the 'NED' (North, East , Down) convention which is different to what I had assumed.

It looks like I need to invert the Y & Z axes of the magnetometer & the X axis of the accelerometer but I'm not sure how to do so - it can't be as simple as putting a minus sign in front?

The accelerometer produces readings between about 265 for -1g & 405 for +1g. So if an axis is the wrong way round it reads 265 for +1g & 405 for -1g. If I simply put a minus sign in front then it will read -265 for +1g & -405 for -1g, which means that +1g is now a bigger number than -1g which is fine.

However atan2(405,346) doesn't give the same result as atan2(-265,346) so the minus sign approach won't work. Will I have to map the axis to the correct scale or something?

Edit: turns out map() should work fine;

Note that the "lower bounds" of either range may be larger or smaller than the "upper bounds" so the map() function may be used to reverse a range of numbers,

Edit: Even with the axes all appropriately inverted or not in accordance with the 'NED' convention, it still doesn't work at all :frowning: This is completely stalling my project :`(

You have to convert the ADC output to signed values where 0 represents zero acceleration - this is fundamental to all such sensors.

Normally sensors output V/2 for a zero value (if the sensor takes 3.3V then this is 1.65V, which will read as 338 on the analogRead() for a 5V Arduino. So you should subtract 338 from each of your readings.

Are the mag and accl mounted horizontally (maximum rotation never carries it past vertical)?

Has the mag been calibrated to account for the hardware you are using?

Post your debug outputs so we can see what you're doing.

MarkT:
You have to convert the ADC output to signed values where 0 represents zero acceleration - this is fundamental to all such sensors.

Normally sensors output V/2 for a zero value (if the sensor takes 3.3V then this is 1.65V, which will read as 338 on the analogRead() for a 5V Arduino. So you should subtract 338 from each of your readings.

This is probably it. I asked the write of the library if I would have to do anything special, like scaling, because I was using an analogue accelerometer rather than an I2C one, but he assured me I could just bung in the raw values. I'll try this probably first thing on Monday & see what the difference is, but it sounds promising.

VccDood:
Are the mag and accl mounted horizontally (maximum rotation never carries it past vertical)?

My testing has been with them both mounted horizontally, but in theory this library should allow for 360 degree tilt compensation of the magnetometer (at least according to the application note that the library is based on).

VccDood:
Has the mag been calibrated to account for the hardware you are using?

The library has a hard iron offset solver. Hopefully once I've followed MarkT's advice the readings I get after applying the result of the hard iron offset solver will be good.

I actually ordered a MMA8452 accelerometer (the same model as the library author's example code is written for) because it was cheap & I hoped that it would alleviate my problems. It arrived this morning, I've connected it up, installed the library & corrected the axes so that both it & the magnetometer match the NED system.

The raw x/y/z readings from both the accelerometer & the magnetometer are correct (eg the accelerometer axes read 1g when the +ve direction is pointed down, -1g when +ve direction pointed up), the pitch & roll calculations from the library are correct (eg clockwise rotation about x & y produce +ve roll & pitch respectively, anticlockwise produce -ve). But the yaw calculation from the library is wrong - it produces -ve yaw when rotated clockwise around the +ve z axis & +ve yaw when rotated anticlockwise around +ve z axis.

Most irritating :frowning:

cjdavies:
The raw x/y/z readings from both the accelerometer & the magnetometer are correct (eg the accelerometer axes read 1g when the +ve direction is pointed down, -1g when +ve direction pointed up),

That's definitely wrong, if +Z axis points up to the sky, the Z reading should be +1g.

I should clarify that rather bald statement since it may defy intuition at a first reading.

Imagine you are in a lift. You can feel the push of the floor of the lift on your feet - if you are at rest (w.r.t. the building) then you'll feel your normal weight (i.e. m * g ) as a push up against your feet. (The floor feels it as a push down from your feet - action and reaction are equal and opposite)

Now I accelerate the lift towards the roof (that's in a +Z direction) - you'll feel your apparent weight increase during this acceleration, so you'll feel m * (g+a) as your weight where a is the acceleration upwards relative to the building.

Now suppose the cable fails - the lift now accelerates downwards (w.r.t. the building - i.e. that's an acceleration of -g). You feel yourself weightless (i.e. m*(g-g) == 0)

Replace yourself by the test-mass inside the accelerometer and the lift by the accelerometer package and you'll see that it registers gravity as +g in an upwards direction if stationary (w.r.t the earth's surface).