From an ideal three axis magnetometer with readings X, Y and Z from orthogonal axis, we can compute the Radius R of a sphere. For easy coding let us represent the three components X, Y, and Z in a vector M=[X, Y, and Z]. This M[0] is the X axis reading, M[1] is the Y axis reading and M[2] is the Z axis reading. The radius R = Sqrt(M[0]^2+M[1]^2+M[2]^2).
For an ideal magnetometer the radius R is a constant for a given external magnetic field.
In practice each axis of the magnetometer will not be centered and may have a different gain from the other two axis. Centering consist of finding an offset value to subtract from the raw readings for each axis. Gain adjustment finds a value that makes the output of each axis equal when positioned in the same magnetic field.
Coding note
I’m using a LSM9DS1 IMU chip connected to a Pico. Since there is recent interest in the Puck magnetometer, I will try to point out where there are differences and give my best guess as to how to use this technique with a Puck.
How the calibrations are implemented
We start with a raw reading from the magnetometer. Mr[i], where Mr is the raw reading vector containing raw values for the X, Y and Z, axis. To get the calibrated reading in vector Mc, the following equation is used:
Mc[i]=( Mr[i] – Moffest[i] ) * Scale * Mgain[i],
Where Mc[i] is the calibrated vector component,
Mr[i] is the raw vector component,
Scale is used to convert ADC count to engineering units such as Gauss. (Can omit for Puck),
Mgain[i] is the gain factor for the vector component,
and i is the component index 0...2.
The raw magnetometer readings from the LSM9DS1 IMU are in ADC counts and the Scale (there are 4 different ones) is the full scale engineering units divided by 32768.
For example 4 Gauss/ 32768 = 0.0001220703125 = Scale.
For the LSM9DS1 I used the minmax class in a loop that can be stopped using a menu system.
case "4"://read and display data
menulevel=10;sst="";
readall(W);
nn=setInterval(function () {
readall(W);
count++;
if(count>1000)clearInterval(nn);
}, 100);
break;if(count>1000)clearInterval(nn);
xxxx
function readall(W){
if(W.magAvailable()){
W.readMag();
T.process(W.m);
}//endif
}//end readall
Due to budget I don't own a Puck yet, so this code may need some enhancement.
For a Puck the Puck.on('mag', function() { ... }); would be used something like this:
var T=new minmax[];
var M=[0,0,0];
var Moffset=[0,0,0];
var Mgain=[1,1,1];
function onMag(p) {
M[0]=(p.x-Moffset[0])*Mgain[0];
M[1]=(p.y-Moffset[1])*Mgain[1];
M[2]=(p.z-Moffset[2])*Mgain[2];
T.process(M);
}
Running the code
The idea is to run the code and then point each end of each axis towards the magnetic pole. For my location in North America it is North and about 60 degrees below the horizon due to magnetic dip. This process is continued until the readings for each axis stop changing.
At the start: (readings are in raw ADC counts)
At this point for the LSM9DS1, I used a different program to write the offsets (avg)to ROM. For the Puck copy the offsets (avg) to the Moffset[].
To evaluate the calibration data were collected using code like this:
function readall(W){
var avg= [ 3163.5, -196, 1172.5 ];
var Gain= [ 1.04741583688, 0.93101994942, 1];
var R1,R2,R3;
var raw=[0,0,0];
var Roff=[0,0,0];
var Rcal=[0,0,0];
var i;
if(W.magAvailable()){
W.readMag();
// T.process(W.m);
for(i=0;i<3;i++){
raw[i]=W.m[i];
Roff[i]=raw[i]-avg[i];
Rcal[i]=Roff[i]*Gain[i];
R1=Math.sqrt(raw[0]*raw[0]+raw[1]*raw[1]+raw[2]*raw[2]);
R2=Math.sqrt(Roff[0]*Roff[0]+Roff[1]*Roff[1]+Roff[2]*Roff[2]);
R3=Math.sqrt(Rcal[0]*Rcal[0]+Rcal[1]*Rcal[1]+Rcal[2]*Rcal[2]);
}//next i
console.log(R1,R2,R3);
}//endif
}//end readall
The magnetometer was pointed in many different orientations as data were collected. R1 is the radius from raw data, R2 is the radius from raw data - offset, and R3 is the radius from the gain times the raw data - offset. Excel was used to perform descriptive statistics on the radius values R1, R2, and R3.
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
Q&D Magnetometer Calibrations
The Ideal Magnetometer
From an ideal three axis magnetometer with readings X, Y and Z from orthogonal axis, we can compute the Radius R of a sphere. For easy coding let us represent the three components X, Y, and Z in a vector M=[X, Y, and Z]. This M[0] is the X axis reading, M[1] is the Y axis reading and M[2] is the Z axis reading. The radius R = Sqrt(M[0]^2+M[1]^2+M[2]^2).
For an ideal magnetometer the radius R is a constant for a given external magnetic field.
In practice each axis of the magnetometer will not be centered and may have a different gain from the other two axis. Centering consist of finding an offset value to subtract from the raw readings for each axis. Gain adjustment finds a value that makes the output of each axis equal when positioned in the same magnetic field.
Coding note
I’m using a LSM9DS1 IMU chip connected to a Pico. Since there is recent interest in the Puck magnetometer, I will try to point out where there are differences and give my best guess as to how to use this technique with a Puck.
How the calibrations are implemented
We start with a raw reading from the magnetometer. Mr[i], where Mr is the raw reading vector containing raw values for the X, Y and Z, axis. To get the calibrated reading in vector Mc, the following equation is used:
Mc[i]=( Mr[i] – Moffest[i] ) * Scale * Mgain[i],
Where Mc[i] is the calibrated vector component,
Mr[i] is the raw vector component,
Scale is used to convert ADC count to engineering units such as Gauss. (Can omit for Puck),
Mgain[i] is the gain factor for the vector component,
and i is the component index 0...2.
The raw magnetometer readings from the LSM9DS1 IMU are in ADC counts and the Scale (there are 4 different ones) is the full scale engineering units divided by 32768.
For example 4 Gauss/ 32768 = 0.0001220703125 = Scale.
The min/max Method
For the LSM9DS1 I used the minmax class in a loop that can be stopped using a menu system.
Due to budget I don't own a Puck yet, so this code may need some enhancement.
For a Puck the Puck.on('mag', function() { ... }); would be used something like this:
Running the code
The idea is to run the code and then point each end of each axis towards the magnetic pole. For my location in North America it is North and about 60 degrees below the horizon due to magnetic dip. This process is continued until the readings for each axis stop changing.
At the start: (readings are in raw ADC counts)
After readings stop changing:
At this point for the LSM9DS1, I used a different program to write the offsets (avg)to ROM. For the Puck copy the offsets (avg) to the Moffset[].
To evaluate the calibration data were collected using code like this:
The magnetometer was pointed in many different orientations as data were collected. R1 is the radius from raw data, R2 is the radius from raw data - offset, and R3 is the radius from the gain times the raw data - offset. Excel was used to perform descriptive statistics on the radius values R1, R2, and R3.