Skip to main content
Visitor II
February 26, 2025
Question

LSM303AGR Calibration

  • February 26, 2025
  • 3 replies
  • 1123 views

Hello everyone,

I'm contacting you today in order to understand my error during the calibration of my LSM303AGR sensor located on a PCB. The ultimate goal is to create a compass with tilt compensation but I try to get a good precision with magnetometer first. Of course, I took care to read up on the Internet before opening this post, but all the ideas seem to be divergent and I can't find my way around.

First, I got the raw acceleration and magnitude data from my sensor. So far, so good. As part of my calibration, I generated a data set during which I rotate the sensor in all possible directions to cover all angles. I then measured the hard iron to eliminate offsets on each axis :

 

 

 

def calculate_hard_iron(data):
 max_vals = np.max(data, axis=0)
 min_vals = np.min(data, axis=0)
 return (max_vals + min_vals) / 2

 

 

 

Then I calculated the soft iron by normalizing my data while preserving the centering :

 

 

 

def calculate_soft_iron(data):

 # Centering data
 mean = np.mean(data, axis=0)
 centered_data = data - mean

 # Find the scaling factors for each axis
 ranges = np.ptp(centered_data, axis=0) # peak to peak range
 scale_factors = np.max(ranges) / ranges
 
 # Normalize
 corrected_data = centered_data * scale_factors
 avg_radius = np.mean(np.sqrt(np.sum(corrected_data**2, axis=1)))
 scale = 1.0 / avg_radius
 
 # Apply final scaling
 corrected_data = corrected_data * scale
 transformation_matrix = np.diag(scale_factors * scale)
 
 return corrected_data, transformation_matrix

 

 

 

I arrive at the following resultt :

Aquatwix1_0-1740583867072.png

Following this, I am able to identify my hard iron vector and my soft iron matrix :

Hard Iron Offset: [-368.0, 390.5, 185.5]

Soft Iron Matrix:
[[0.0020697, 0, 0]
[0, 0.00216311, 0]
[0, 0, 0.00205197]]

Apriori, I should be able to interpret a movement using the parameters calculated in the calibration step. So I decide to generate a dataset in which I perform a 360° turn on XY plan, and here is the result :

Aquatwix1_1-1740584081813.png

What stands out is that the offset problem in the data set subsequently caused a loss of information. However, I did deduce the data from the offset measured during the calibration phase. What did I do wrong ?

Thank you for your attention.

 

    This topic has been closed for replies.

    3 replies

    ST Employee
    March 6, 2025

    From your plot it is evident that hard-iron offset has not been compensated correctly. When you perform a rotation around the vertical, with the mag in the horizontal flat plan, the mag data points always describe a circle/ellipse in a 2D horizontal plane, all points will have the same Z coordinate.

    When hard-iron is successfully compensated, the points in the X-Y plot will describe a circle/ellipse centered in the origin which is not the case looking at your plot. This is the biggest source of error (soft-iron effects are usually minor) and this is evident in your case: the scaling factors along the diagonal are very similar to each other. Note that the absolute scaling of the compensated mag does not matter because when computing the orientation (yaw/heading angle) only ratios matters (usually atan2 function is utilized on tilt-compensated data as explained in design tip DT0058).

    The method to compute the hard-iron is ok but accuracy will highly depend on how good is the sweep on each axis. Similarly for the method used to compute the scaling factors. A more robust method is discussed in design tip DT0059.

     

    Aquatwix1Author
    Visitor II
    March 12, 2025

    Dear @Andrea VITALI ,

    Thank you for your reply. I appreciate your help ! In fact, the last screens I shared were about a rotation on the XY plane with a tilt. That's why the z was not constant. However, the problem is the same with or without tilt.

    I read DT0058 and DT0059 and tried to apply the approach. However, the result is the same even if it seems better than my version. The offsets are not fully compensated.

    Here's the calibration step, which looks clean :

    Aquatwix1_0-1741795824831.png

    I can get the parameters required for my application :

    - Hard-Iron : [-559.5, 373.5, 1081.0 ]
    - Gain : [499.5, 529.5, 561.0 ]

    Here is an example in a 360° configuration in 2D without tilt. I stopped the rotation every 90 degrees for few seconds. I get this :

    Aquatwix1_1-1741795980740.png

    Aquatwix1_3-1741796305838.png

    Here is an example in a 360° configuration in 2D with tilt. I stopped the rotation every 90 degrees for few seconds. I get this :

    Aquatwix1_2-1741796042152.png

    Aquatwix1_4-1741796351037.png

    If you have any ideas on how to solve this problem, I would be grateful.

    Regards

    ST Employee
    March 12, 2025

    The hard-iron is compensated when you do the calibration. But when you do the 2D rotation in the horizontal plane (no tilt) it is clear there is hard-iron.

    Are you changing the location of the magnetometer?

    The calibration should be performed in the same environment where the mag is utilized. Generally speaking, it is not possible to calibrate 'at the factory' and then utilize 'in the field' because the magnetic environment can be dramatically different.

    Possible sources of hard-iron are: permanent magnets, ferromagnetic materials that become magnetized (iron, steel and such alloys), wires (including neighboring PCB tracks) that carry large currents.

    Is there anything like that in the environment where you do the 2D rotation?

     

     

    Visitor II
    October 17, 2025

    This post is very useful, thanks for detailed measurements! Did you resolve this?

    I am having a lot of trouble making sense of my magnetometer output. I align each axis with the local earth magnetic field (measured with high quality conventional hiker's compass) and expect to see each axis have maximum positive and negative reading with other axes zero when the axis is aligned with earth field. But nothing makes sense here. When I looked at the data from these calibration attempts, it looked reasonable, fairly spherical but with a significant 20-30uT offset from origin. But when I tried to use first  min/max and then fancy ellipsoid calibration, nothing worked to make a useful magnetic compass (I'm only interested in a boat compass, essentially 2d compass heading).

    Later I got a clue from a youtube video where the presenter suggested to check header pins and socket used to connect daughterboard to motherboard. I checked my pins and they are indeed weakly magnetic (you can test by setting them up at a slight angle and see if a magnet pulls them over). However, now it appears that I magnetized my pins or perhaps the magnetometer itself and I now have a very large DC offset.

    I can post some data later, but I'm curious if you resolved things to your satisfaction?

    Visitor II
    October 17, 2025

    I realized now something that I did something without thinking; I placed my 9-DOF IMU  daughterboard very close to my dual coil power relay on my PCB. The relay has a magnet (or two) that holds the relay in each latched state. That causes a huge hard iron offset of about 250uT.  Not sure if this can be compensated.

    Aquatwix1Author
    Visitor II
    October 17, 2025

    Hello @tobidelbruck,

    I am glad to see that this article can help people think things through.

    In fact, I managed to solve my problem after running a few tests in a different environment. The data had been generated with the IMU on the printed circuit board, inside the main system, and in a laboratory equipped with a few devices that could cause interference.

    I decided to test the sensor in a suitable environment by removing the daughter board from the main system and performing my tests in an open space, without any potential sources of interference.

    The results were significantly better, even though the final result (azimuth) still had a small error (3-4°) depending on the quality of the calibration. In any case, I considered this result sufficient for my application.

    I hope you find the source of your problem, but be careful of interference, as I mentioned. I spent a lot of time on the software, when the problem was elsewhere. Feel free to share your results ! :)