Electronic – Magnetometer tilt compensation without accelerometer

digital filtermagnetometermath

I need to use the magnetometer in a device to accurately determine how many degrees the user twists (rotates in x-y plane) it. I don't care about absolute orientation, just the angular delta. Of course the user can hold the device in any orientation. Essentially, this is like a doorknob that isn't attached to anything.

I tried atan2(x, y) but the resulting anglular delta varies depending on the device's orientation. Some research suggests that tilt compensation is needed, but all solutions are for correcting compasses based on knowing the gravity vector.

In this application, accelerometer, gravity, gyro data are not available — just the magnetometer. This means that the pitch & roll used in typical solutions aren't available. However, I'm not looking for true headings, just rotation in x-y.

Can anybody explain the math & solution for this? I realize this is strictly speaking a programming question, but this is stuff that only an EE would know.

The application is typically used such that the x-y plane is rarely, if ever, perpendicular to the earth's field, so sqrt(x2+y2) > 0.

The magnetometer is an AK8975 3-axis MEMS soldered to the device with no gimbal-type leveling arrangement. Hard-iron calibration is already taken care of. Sampling rate is 40 Hz whereas the fastest you can twist your hand back & forth is less than 5 Hz. The magnetometer's orientation is totally unknown since the magnetometer's axis is fixed to the device and the device can be in any orientation. Nothing is known or can be assumed about gravity nor the device's orientation or location.


[Later] Did an experiment where I took the device and lay it flat on a swivel chair facing North, then rotated it thru 360. The chair seat is level with the ground. Here's the chart:
x=blue, y=dark green, z=red, sqrt(x^2+y^2+z^2)=aqua

x=blue, y=dark green, z=red, sqrt(x^2+y^2+z^2)=aqua
The x-axis is elapsed seconds, y-axis is uT.

Two things jump out at me:

  1. Shouldn't the aqua-colored magnitude be flat? The trough is 30 vs. 45 for the flat part. Otherwise, is this a tipoff that this sensor is grossly mis-calibrated? Or is this an indication that tilt compensation is needed?
  2. The x & Y peaks are slightly offset and have slightly different ranges, but not by that much. This, I'm willing to shrug off as error, as opposed to the 30:45 difference for the magnitude which seems sufficient to mess up any calculations.
  3. The Z is flat as expected

Best Answer

If the orientation of the device is fixed during the measurement then the computation is relatively straightforward as Dave Tweed has pointed out.

The main issue is calibration. A quick way would to establish at an approximate min, max for each x,y,z direction. Hopefully the zeros will lie roughly on the averages of the min and max. That is, this will give an \$x_\min\$, \$x_\max\$ and similarly for the other axes. From this estimate \$x_\text{zero} = {1 \over 2} (x_\min+x_\max)\$, and \$r_x = {1 \over 2} (x_\max-x_\min)\$, etc.

Then for a reading, compute \$x_\text{est} = { x_\text{meas}-x_\text{zero} \over r_x} \$, etc. Hopefully this will be in the range \$\pm 1\$.

I would check that \$|z_\text{est}|\$ is (1) no larger than some number (\$<1\$ here) so that you get a meaningful direction, and (2) that it remains fairly constant for the duration of a measurement.

To compute an angle, use \$\text{atan2}(y_\text{est} , x_\text{est})\$ (check your particular API). To compute the difference with another (hopefully nearby) angle, just be careful with the \$\pm\pi\$ boundary.

This is obviously a quick overview with many optimisations and sanity checks omitted.