While most of use have moved on to 3D accelerometer/magnetometer compasses or even IMUs for our robots, there are still interesting uses for the HMC6352 2D magnetometer. I wanted to make a high resolution frictionless shaft position indicator out of mine (http://www.sparkfun.com/products/7915). Fortunately, I have an earlier version of the linked Spark Fun breakout board, which has pads for adding resistors to reduce the amplifier gain so that a magnet can be used to drive the compass. This feature has been removed from the latest board, which was not a wise move. My particular board also has the North arrow incorrectly pointing West, as found by experiment and in agreement with the chip orientation on the breakout board.
With the stock amplifier gain, when exposed to even modest magnetic fields, the chip undergoes some sort of automatic adjustment which can result in lockup and/or wildly incorrect output. Cycling the power does not help. The solution is to send the “reset S/R bridge offset” command, the effect of which is not explained in the datasheet. The S/R reset does not affect the results of the built-in “user calibration procedure”, which is also poorly documented.
So, I looked closer, examining the raw magnetometer data and the result of the user calibration command. The raw magnetometer data are positive integers, i.e. the ADC results, and the result of the user calibration procedure is just to calculate and subtract an average offset in the X and Y measurements. In my HMC6352, the gains of the X and Y amplifiers are significantly different, which seems to be true for all inexpensive magnetometers and this leads to magnetic bearings that are far less accurate than the data sheet claims.
Inspired by various efforts to extract the best possible data out of 3D magnetometer/accelerometers, I wrote a 2D version of the procedure described in http://sailboatinstruments.blogspot.com … ation.html for the HMC6352. As is usual I fit an ellipse to the data and rotate/rescale the ellipse to a circle. This makes a big difference, up to 5 degrees, in the accuracy of the reported angle of the magnetic field vector.
The output of the code posted below, on data collected from the HMC6352 in the Earth’s magnetic field (after using the built-in user calibration procedure) was the following matrix and offset vector. As you can see, the correction is about 9% in magnitude for the X axis with a significant rotational component.
The figure below shows the raw data for two complete revolutions of the compass, while held level (blue circles) and the rescaled data points (green circles). The fitted ellipse is the solid line.scaled rotation matrix and vector to apply: Q*(XY-XY0)
(1.0886 0.0407)*(X - ( -1.4))
(0.0407 1.0187)*(Y - ( -8.0))
The figure below shows the difference in magnetic bearings in degrees, obtained from the data before and after calibration by using the atan2 function.
The current code is written for MATLAB. If there is sufficient interest, I could write a version in C.
Caveat: this code has not been thoroughly tested and will probably fail in pathological cases!
%
% File: magcal_2d.m
% This MATLAB program calculates the calibration parameters for a 2D magnetometer.
% As is customary, data for one or two complete revolutions of the magnetometer
% should be collected while held level, points closely spaced if possible.
%
% uses published Matlab function EllipseDirectFit.m
% http://www.mathworks.com/matlabcentral/fileexchange/22684-ellipse-fit-direct-method
%
% First step: collect data and produce a CSV file (comma separated values) of
% magnetometer X and Y values (can be raw).
%
% Second step: from Matlab File Menu, import CSV file of (max, magy)
% measurements into array magxy.
%
% Third step: execute magcal_2d.m
%
% This work was inspired by the 3D procedure described in:
% http://sailboatinstruments.blogspot.com/2011/08/improved-magnetometer-calibration.html
%
A = EllipseDirectFit(magxy);
% modified coefficients of equation for ellipse
% from http://mathworld.wolfram.com/Ellipse.html
a = A(1);
b = A(2)/2;
c = A(3);
d = A(4)/2;
f = A(5)/2;
g = A(6);
% X0, Y0 offset (centroid of ellipse)
x0 = (c*d - b*f)/(b^2 - a*c);
y0 = (a*f - b*d)/(b^2 - a*c);
% semimajor and semiminor axes
numer = 2*(a*f*f+c*d*d+g*b*b-2*b*d*f-a*c*g);
denom1 = (b*b-a*c)*( sqrt((a-c)^2 + 4*b*b) - (a+c));
denom2 = (b*b-a*c)*(-sqrt((a-c)^2 + 4*b*b) - (a+c));
a_axis = sqrt(numer/denom1);
b_axis = sqrt(numer/denom2);
% angle of ellipse semimajor axis wrt X-axis
if (a < c) theta = 0.5*acotd((a-c)/(2*b));
else theta = 90.0 + 0.5*acotd((a-c)/(2*b));
end
s=sprintf('x0 = %5.2f y0 = %5.2f a = %5.2f, b= %5.2f, theta(d) = %4.1f', ...
x0,y0,a_axis,b_axis, theta);
disp(s);
% rotation matrix to align semimajor axis to X-axis
ct=cosd(theta);
st=sind(theta);
R = [ct st; -st ct];
xy0 = [x0 y0];
%rescale vector, correct for difference in magnetometer X & Y gains
scale = [b_axis/a_axis,1];
% final result: matrix to align ellipse axes wrt coordinates system, normalize X and Y gains and rotate back.
Q = R^-1*([scale(1) 0; 0, scale(2)]*R);
% correct the input data
for i = 1 : length(magxy)
xy(i,:) = ( Q*(magxy(i,:)-xy0)' )';
end
% replot scaled data. Set "hold on" in EllipseDirectFit.m
scatter(xy(:,1),xy(:,2));
% residual plot
figure;
p1 = (180./3.14159).*atan2(magxy(:,2),magxy(:,1));
p2 = (180./3.14159).*atan2(xy(:,2),xy(:,1));
xp = 1:length(magxy);
hold on
title('Residual bearings in degrees after rescaling');
plot(xp,p1-p2,'-g');
disp(' ');
disp('scaled rotation matrix and vector to apply: Q*(XY-XY0)');
s = sprintf('(%6.4f %6.4f)*(X - (%5.1f))',Q(1,1),Q(1,2),x0);
disp(s);
s = sprintf('(%6.4f %6.4f)*(Y - (%5.1f))',Q(2,1),Q(2,2),y0);
disp(s);