More examples required?

Forums:

According to the spec for the magnetometer,
"The device can be used in conjunction with a 3-axis accelerometer to realize an
orientation independent electronic compass that can provide accurate heading
information. "

Are there any Python examples we can draw on to achieve this ? Or perhaps some other documentation we can draw on to achieve the same?

Thanks

piborg's picture

To turn XLoBorg into a fully orientation independent compass requires a fairly complex piece of code, not really suitable for someone learning how to program.

If you just want a simple compass heading then the example provided here would be a good place to start.
If you want a precise compass which is corrected for any orientation then the best place to start would be to read Freescale's explanation of how this is done, which can be found here, but it is a fairly large amount of effort to get working.

Also there is a tread on raspberrypi.org on the subject here.

Thanks for additional links. The freescale doc is interesting and I'll certainly have a go at it. I'm a pro developer but I will probably need help with the maths. TGF t'internet, I say! ;-)

# estimate pitch and row using euleros angles
def getpitchroll():
ax,ay,az = XLoBorg.ReadAccelerometer()
magnitude = math.sqrt((ax * ax) + (ay * ay) + (az * az))
#print magnitude
ax = ax / magnitude
ay = ay / magnitude
az = az / magnitude
pitch = -math.atan2(ax, math.sqrt(pow(ay,2) + pow(az, 2)))
roll = math.atan2(ay, az)
return pitch, roll

A little background: I'm replacing the control unit on my sailboat autohelm. The existing unit has a motor drive unit that is perfect. The control unit died. I'm using a PI with a motor driver from Polulu to steer the boat.

I'd like to steer either a compass course (tilt compensated) or a gps heading. The gps is easy ... mostly.

Here is the tilt compensation code (C) (somewhat empirically derived). It uses some vector math I found here: http://paulbourke.net/geometry/rotate/.

There are a few problems:
- data is very noisy
- there are some points of discontinuity (I didn't bother to fix these due to the other issues)
- the data is just wrong ... even when the unit is flat (pitch = roll = 0). If you rotate 90deg, the data doesn't change by 90deg.
- I'm hoping I've made a bunch of mistakes ... and someone can help me see the truth!
- for now I've bailed on the tilt-compensated compass and focusing on steering by gps heading.

I've implemented the Freescale algorithm bit it didn't give good results. There are a number of algs on the web (search for "tilt-compensated compass". I also implemented the Madgwick (http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms) algorithm with equally poor results. Here is what I ended up with:

// Return the heading in degrees
static double calcHeading()
{
XYZ m;
float mag[3], accel[3];
float pitch, roll, yaw;

XLoReadCompassuT( mag );
XLoReadAccelerometerRaw( accel );

roll = atan2( accel[1], accel[2] );
pitch = atan2( accel[0], sqrt( accel[1]*accel[1] + accel[2]*accel[2] ) );

m.x = mag[0]; m.y = mag[1]; m.z = mag[2];
m = ArbitraryRotate( ArbitraryRotate( m, -roll, CD_Data.xAxis ), pitch, CD_Data.yAxis );

yaw = RadToDeg( atan2( m.y, m.x ) );
LogData( mag, accel, RadToDeg( pitch ), RadToDeg( roll ), yaw );

LogDebug( "Heading(%3.1f,%3.1f) %3.1f\n", pitch, roll, yaw );

return( (double)yaw );
}

piborg's picture

This is a more difficult problem then it first appears, magnetics are affected by the environment around them.

There are a few questions I have regarding what you are currently doing:

1. The Madgwick code (which I found at http://www.x-io.co.uk/open-source-imu-and-ahrs-algorithms/) uses a three-axis gyroscope input along with the accelerometer and magnetometer, do you also have a gyroscope or did you remove these from the algorithm in some way?

2. From a lot of what I have read it is generally shown that the readings can be drastically affected when changing the environment, did you perform any calibrations when the XLoBorg and Raspberry Pi were mounted in the same way as when you tested the corrected compass readings?

3. Your code appears to use different functions from the ones provided by our example C library, are they returning different values?

The chips used on the XLoBorg are of the type often used in mobile phones, I presume these are good enough for what you are trying to do and that this is a purely a problem with getting an algorithm to work as intended.

The code you have seems to rotate the magnetometer into the correct plane (using the accelerometer), but as far as I can tell does not correct for errors in the magnetometer readings.

In particular there is usually an offset for each axis to put zero in the correct place, you may also want a gain term to ensure x, y, and z are all of equal levels (the sensors are only factory calibrated for the chip), they end up different because of things such as magnetic materials in the Raspberry Pi itself.

Hi: My first response is: "wow" ... someone is listening and is willing to help me solve this!! Thank you for your response!

1) The Madgwick algorithm was one of the first that I looked at. I also have a TI SensorTag which has compass, accelerometer and gyro. I got the SensorTag first, then found the XLoBorg. The XLoBorg is easier to interface and connect. The SensorTag is nice because I can mount it anywhere on the boat and have the control electronics elsewhere.

2) I confess, I don't understand the calibration process AT ALL. I have compared the compensated (not calibrated) XLoBorg data with the compass on my phone as well as other compasses (digital and traditional). I have tested these while on the boat (little or no ferrous interference) and get the same kind of data: 90 degree rotation does not yield 90 degree change in data.

3) Different functions: I think you mean the data collection functions: XLoReadCompassuT() and XLoReadCompassRaw(). XLoReadCompassuT() simply calls the raw function and converts to uT (divide by 10). Here is the code:
void XLoReadCompassuT(float* pdata)
{
if( pdata != NULL )
{
XLoReadCompassRaw( pdata );
// The Mag3110 has 10 counts per microTesla
pdata[0] /= 10.0;
pdata[1] /= 10.0;
pdata[2] /= 10.0;
}
}

3b) Correcting for errors in magnetometer readings: This is the part I need to learn about. I was wondering if the gain/offset of each axis is the same. If not, how do I calibrate these? I'm careful to keep any ferrous materials (other than those in the PI) away from the unit. Once I learn how to do this, maybe it will all come into alignment! When I get it all working, I'll share the full code on this forum.

Cheers,
...Peter

piborg's picture

That function name makes sense now :)

I must admit that I do not know a huge amount about calibrating magnetometers myself, but there are a couple of things I think are probably useful from basic calibration practices.

The main problem really comes from an axis reading different values for the same thing from another, simply put they have 'zero' in different places and a slightly different sensitivity to changes.

As a first step to fix the zero I would do the following for each axis in turn:

  1. Place the board such that we get a reasonably large reading for our axis, ensuring it is fixed in position
  2. Take a large number of readings over time and average them, we will call this value A
  3. Place the board such that we have that axis inverted, ensuring it is fixed in position
  4. The readings for that axis should be the other side of zero now
  5. Take a large number of readings over time and average them, we will call this value B
  6. Our approximate offset can be calculated using axisOffset = (A - B) / 2
  7. Our reading should now be zeroed properly by using axisReading - axisOffset

Our readings should now be:
xZeroed = mag[0] - xOffset
yZeroed = mag[1] - yOffset
zZeroed = mag[2] - zOffset

It would be easiest to write these constants in to the C code directly, personally I would read them in from a simple file so that they can be easily changed later if needed.

The calibration of the gains is a little more tricky, the main thing we need is for them to all match each other.

What I would do is the following after the offset corrections have been applied:

  1. Place the board such that the X axis reading is large, ensuring it is fixed in position
  2. Take a large number of readings over time and average them, we will call this value A
  3. Place the board such that the Y axis is now facing the same way the X axis was, ensuring it is fixed in position
  4. Take a large number of readings over time and average them, we will call this value B
  5. Place the board such that the Z axis is now facing the same way the X and Y axis were, ensuring it is fixed in position
  6. Take a large number of readings over time and average them, we will call this value C
  7. If any of A, B, or C are negative make them positive
  8. Our approximate correlation gain for X will simply be 1 and can be ignored (we will make Y and Z look like X)
  9. Our approximate correlation gain for Y can be calculated using yGain = A / B
  10. Our approximate correlation gain for Z can be calculated using zGain = A / C

Our final readings should now be:
xCal = xZeroed
yCal = yZeroed * yGain
zCal = zZeroed * zGain

These values can now be plugged in to your code as
m.x = xCal; m.y = yCal; m.z = zCal;
where you currently are using the mag[*] values

This is a little time consuming and probably not the most precise calibration method, but it should be better then using the raw data directly

Thank you for the calibration details. This procedure makes sense to me.

I'm wondering two things:

1) With phones etc, they ask you to move the unit in a figure 8 a few times to do the calibration. I might search a bit to see if I can find the algorithm that uses this method.

2) To perform the manual calibration (not the figure 8) would it make sense to leave the unit fixed and place a small rare-earth magnet centered on each face of a 6-side cube that surrounds the unit. Is there any harm to the sensor by being close to a strong magnet?

Cheers,
...Peter

piborg's picture

1. The figure 8 calibration is likely to be much better, I am not sure how it actually works though.

2. You should not do this, being close to a magnet will not harm the sensor but it will calibrate to those magnets instead of the earths magnetic field.

On a side note there are a few things you will want to be the same when you calibrate the unit as when it is operated:
1. Fixed magnetic sources should be as far away as possible.
These include magnets, speakers, transformers, et cetera.
2. Magnetisable metals such as iron and steel should ideally be far away as well, but this is not always possible and they should be in the same place when performing the calibration.
These include things such as metal plates, screws, springs, et cetera.
3. Electrical magnetic sources should also be kept far away when possible, bear in mind these may change the readings if they are turned on and off as well as causing noise during operation.
These include motors, speakers, coils, transformers, et cetera.

I believe the basic principle of the figure 8 calibration is that it is trying to do a mathematical fit between the accelerometer readings and the magnetometer readings based on what the earths magnetic field is supposed to look like.

Hi Again: Well, I'm sheepishly happy that I roughly calibrated my unit and now the compass results are MUCH more meaningful! Here is how I performed the calibration:

It turns out the range of data for each axis is slightly different, but the biggest correction factor is the zero-offset. The calibration process I used was:

Determine magnetic North using another compass.
Determine the Magnetic Inclination (70 degrees for me).
These two determine the direction to point the digital compass for calibration.

Take raw compass readings pointing each axis at the magnetic source (North and 70 deg down). Take compass readings pointing directly at the magnetic source and directly away from the magnetic source. Do this for the X, Y, and Z axis of the magnemometer.

Here is my summary data (averaged over 100 samples for each direction):

Axis    Min,Max Range   Scale   ZeroOffset
X      -274,776 1,050   95.25%  -250.78
Y      -319,742 1,061   94.26%  -211.51
Z      -341,663 1,004   99.60%  -161.40

Once I entered the zero offset and scale (so each axis has a full scale range from -500 to +500) I'm now getting reasonable values from my system!

More later when I've done some on-the-water testing.

piborg's picture

Glad to hear you made progress, nice idea with the calibration orientation.

In order to follow this calibration method you'd presumably need to improvise some sort of jig to hold the XLoBorg at the correct angle and inclination. In fact this is unnecessary. Much easier to simply lay it flat on your desk, aligned with the front edge, then rotate it through 180 degrees for the 2nd reading, and upside down for the 3rd. Whichever way your desk is pointing and whatever the magnetic inclination at your location, rotating the module through 180 degrees will reverse the true magnetic field it sees whilst parasitic effects remain unchanged. Averaging the readings will therefore always cancel the true field and leave you with the offset.

As for scale factor, the absolute value of the earth's magnetic field varies from place to place so there's no reason to scale it to a particular range, so long as your display can cope with the range it's likely to encounter.

Regards - Philip

piborg's picture

The reason for adjusting the scales is that the arctan calculations towards the top of this thread assume that the axes are all of the same ranges.

The actual value they are scaled to does not really matter, what may matter is that they all read the same when pointed in the same direction.

For example if X reads 500 when pointed directly north then Y should also read 500 when it is pointed directly north.

OK, yes, but a 10% difference in the sensitivities of the X and Y sensors will only give a 2.7 degree error in the heading on a course bearing of 45 degrees on my calculations, but errors in estimation of the offset will have considerably greater effect, and with no offset correction my results for heading were pretty much meaningless.

piborg's picture

In the case above they were trying to automatically steer a boat based on compass heading.

For that task a 2.7 degree error could be very bad, about 4.7 m out of position over 100 m of travel.

This is always the problem with calibration, how accurate you need to be depends on what you are trying to do.

Subscribe to Comments for "More examples required?"