kriswiner / MPU9250

Arduino sketches for MPU9250 9DoF with AHRS sensor fusion
1.04k stars 471 forks source link

MPU-9250 application notes? #279

Closed liudr closed 6 years ago

liudr commented 6 years ago

Kris,

Thank you so much for this library! I have an application in mind, to track the orientation of a pole for any change of tilt or orientation due to ground movements. So I need to calibrate the sensor once and load the correct bias etc. and never recalibrate it so it wouldn't show changes due to recalibration. I would like to find application notes for self-test and calibration etc. that invenSense promises in the spec sheet. I created a developer's account at invenSense but there's nothing to download. Where can I find important notes on how the sensors work and properly calibrate etc.? Thank you again!

kriswiner commented 6 years ago

https://github.com/kriswiner/MPU9250/tree/master/Documents

On Sun, Jun 3, 2018 at 7:43 AM, liudr notifications@github.com wrote:

Kris,

Thank you so much for this library! I have an application in mind, to track the orientation of a pole for any change of tilt or orientation due to ground movements. So I need to calibrate the sensor once and load the correct bias etc. and never recalibrate it so it wouldn't show changes due to recalibration. I would like to find application notes for self-test and calibration etc. that invenSense promises in the spec sheet. I created a developer's account at invenSense but there's nothing to download. Where can I find important notes on how the sensors work and properly calibrate etc.? Thank you again!

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/279, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qn-zFrzF2CRNP35zuCy-ZL6_c4xUks5t4_YCgaJpZM4UYG_Z .

liudr commented 6 years ago

Thank you Kris! I'm going through the notes, your code, and the sample program output. I started with sparkfun's port of your code and then found your repo here.

Strange, their code files have no "modified" dates and I see that they stripped off your credits. Their github dates are about 2 years ago. I really shouldn't have started with their code. With their code, I was having trouble with yaw and the magnetic field results are strange. I'll take my device outdoors and do some more tests while reading your code.

Just an interesting point, the old code sparkfun has uses uint16_t arrays for self test results (200 results to be accumulated) while your code uses int32_t. I suddenly lost a lot faith in sparkfun engineering, specifically between the u and 16.

kriswiner commented 6 years ago

Funny that Sparkfun designed their breakout (with lots of errors) and pointed to my sketches without a word to me until after the fact. I could have helped them with their hardware design as well as pointing them to the latest (and most correct) Arduino sketches. Instead a lot of people are using their board and the old sketch of mine and getting disappointing results. Oh well, what can you do...

On Tue, Jun 5, 2018 at 12:22 PM, liudr notifications@github.com wrote:

Thank you Kris! I'm going through the notes, your code, and the sample program output. I started with sparkfun's port of your code and then found your repo here.

Strange, their code files have no "modified" dates and I see that they stripped off your credits. Their github dates are about 2 years ago. I really shouldn't have started with their code. With their code, I was having trouble with yaw and the magnetic field results are strange. I'll take my device outdoors and do some more tests while reading your code.

Just an interesting point, the old code sparkfun has uses uint16_t arrays for self test results (200 results to be accumulated) while your code uses int32_t. I suddenly lost a lot faith in sparkfun engineering, specifically between the u and 16.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/279#issuecomment-394829480, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qkEs0dNjrKuS7pSdXC_wbzr5USD7ks5t5tpegaJpZM4UYG_Z .

liudr commented 6 years ago

That's sad. They made their own boards and sell them. They really should have an up-to-date library to go with the boards. I did some more testing. I was surprised to find out that the magnetic inclination in my area is a whopping 72 degrees downward! That must have been the reason I wasn't able to make sense out of the magnetic field readings and the yaw was very weird. When I had the sensor right-side-up, z axis reads around 20, then when I had it up-side-down, it's about -1000! I also realized that the magnetic field in my office varies a lot by location. Where my chair is, the field is changing 20% if I moved my magnetic probe (not MPU9250) about 1 foot or more. I'll have to carefully read your calibration routine and see what makes sense to my application and what should be removed.

BTW, cool purple boards. I have an idea where they were printed :)

liudr commented 6 years ago

Kris,

I read all your code and did some comparison with sparkfun's version and some sample code. I now have a good idea how the initialization and calibration process takes place. Could you critique my list below?

Thanks.

For my intended application I might skip accelerometer calibration. For magnetometer calibration (I've read about figure-8 motion) I didn't find a calibration routine. Also, my magnetometer is pretty off, possibly factory trim is not done well. I would get something like 50mG on z axis and the -1,000mG on z axis when I turned the device upside down. I decided to find the right trim value so up and down are approximately opposite values, such as +-525mG. Hope this is the right way. We have a large inclination angle.

Accelerometer and gyroscope Determine device address from value of ADO, 0x68 or 0x69. ADO is defined in .h file and pre-compiler directives are used to assign value to MPU9250_ADDRESS. Detect using WHO_AM_I register value

Self test:

  1. Read and accumulate 200 values from each axis with self-test turned off
  2. Read and accumulate 200 values from each axis with self-test turned on
  3. Divide accumulated values by 200. Subtract values between self-test turned off and on to find diff.
  4. Read self-test values from device registers and calculate factory trims
  5. Compare the diff with factory trims. If the difference between the two are within device tolerance, such as 50%, self-test is passed.

Calibrate:

  1. Call calibrateMPU9250().
  2. Set up the IMU to accumulate 40 results using its FIFO for each axis.
  3. Collect these values and average them.
  4. Assume az is along the vertical direction and subtract a value equivalent to 1g from it (or add to it depending on whether az>0 or not. This is not a good strategy.
  5. Push the gyro averages to the device. This should work well for gyro when the object is at rest.
  6. Load scaled gyro bias in units of g to arrays specified in parameters.
  7. These parameters are not used in any MPUread function calls. All read functions are very basic, read two bytes and assemble into 16-bit signed integer. You need to call functions such as getGres to get the multiplier to convert the integers into proper values in correct units.
  8. Results from the call are stored in myIMU.gyroBias.
  9. Scale accelerometer average to the scale accepted by the unit
  10. Load bias from unit's registers (factory trim), combine with average bias and push to device, while preserving the LSB of low byte in each axis for temperature compensation purposes. This will not work for accelerometer when we need to track its static tilt.
  11. There might be some issues handling the temperature compensation bit according to Kris's comments. If this will not work, maybe we can store the bias in program and apply after reading raw data.
  12. Results from the call are stored in myIMU.accelBias.

Initialize: Wake the sensor and set proper config for active data collection.

Read data:

  1. Read the interrupt bit. If it is set, data is ready. Proceed with read.
  2. Call readGyroData() or readAccelData() and obtain raw data.
  3. Call getGres() or getAres() to get resolution multiplier in gRes or aRes.
  4. Multiple each axis by gRes or aRes to get proper value.

Read temperature data:

  1. Call readTempData to get raw data. Then temperature= (raw data)/333.87 + 21.0 in DegC. Magnetomer Detect the mag. using WHO_AM_I register value There is no self-test for the magnetometer.

Initialize:

  1. Power down unit.
  2. Read sensitivity adjustment values from device ROM.
  3. Calculate scale-adjusted values and return these values. destination[0] = (float)(rawData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc.

Calibration: Do back-forth measurements and estimate bias to be removed from data.

Read data:

  1. Call readMagData() to obtain raw data.
  2. Call getMres()to get resolution multiplier in mRes.
  3. Multiple each axis by mRes and the sensitivity adjustment from initialization, then add a bias (in mG unit). This bias is loaded to a bias array with values obtained from back-forth calibration. Quarteron and Yaw, pitch, roll Call updateTime() in loop (only present in SPE library and code sample). Updata myIMU.yaw to subtract the declination angle. The system may have to be updated at 200Hz to get results correctly. I'll see if it works under less frequency, when the main loop is idle.
kriswiner commented 6 years ago

I'm not sure what this is supposed to be. Here https://github.com/kriswiner/Ladybug/blob/master/MPU9250_MS5637_BasicAHRS2_Ladybug.ino is a modern sketch, I wouldn't use Sparkfun's.

On Fri, Jun 8, 2018 at 8:38 PM, liudr notifications@github.com wrote:

Kris,

I read all your code and did some comparison with sparkfun's version and some sample code. I now have a good idea how the initialization and calibration process takes place. Could you critique my list below?

Thanks.

For my intended application I might skip accelerometer calibration. For magnetometer calibration (I've read about figure-8 motion) I didn't find a calibration routine. Also, my magnetometer is pretty off, possibly factory trim is not done well. I would get something like 50mG on z axis and the -1,000mG on z axis when I turned the device upside down. I decided to find the right trim value so up and down are approximately opposite values, such as +-525mG. Hope this is the right way. We have a large inclination angle.

Accelerometer and gyroscope Determine device address from value of ADO, 0x68 or 0x69. ADO is defined in .h file and pre-compiler directives are used to assign value to MPU9250_ADDRESS. Detect using WHO_AM_I register value Self test:

  1. Read and accumulate 200 values from each axis with self-test turned off
  2. Read and accumulate 200 values from each axis with self-test turned on
  3. Divide accumulated values by 200. Subtract values between self-test turned off and on to find diff.
  4. Read self-test values from device registers and calculate factory trims
  5. Compare the diff with factory trims. If the difference between the two are within device tolerance, such as 50%, self-test is passed. Calibrate:
  6. Call calibrateMPU9250().
  7. Set up the IMU to accumulate 40 results using its FIFO for each axis.
  8. Collect these values and average them.
  9. Assume az is along the vertical direction and subtract a value equivalent to 1g from it (or add to it depending on whether az>0 or not. This is not a good strategy.
  10. Push the gyro averages to the device. This should work well for gyro when the object is at rest.
  11. Load scaled gyro bias in units of g to arrays specified in parameters.
  12. These parameters are not used in any MPUread function calls. All read functions are very basic, read two bytes and assemble into 16-bit signed integer. You need to call functions such as getGres to get the multiplier to convert the integers into proper values in correct units.
  13. Results from the call are stored in myIMU.gyroBias.
  14. Scale accelerometer average to the scale accepted by the unit
  15. Load bias from unit's registers (factory trim), combine with average bias and push to device, while preserving the LSB of low byte in each axis for temperature compensation purposes. This will not work for accelerometer when we need to track its static tilt.
  16. There might be some issues handling the temperature compensation bit according to Kris's comments. If this will not work, maybe we can store the bias in program and apply after reading raw data.
  17. Results from the call are stored in myIMU.accelBias. Initialize: Wake the sensor and set proper config for active data collection. Read data:
  18. Read the interrupt bit. If it is set, data is ready. Proceed with read.
  19. Call readGyroData() or readAccelData() and obtain raw data.
  20. Call getGres() or getAres() to get resolution multiplier in gRes or aRes.
  21. Multiple each axis by gRes or aRes to get proper value. Read temperature data:
  22. Call readTempData to get raw data. Then temperature= (raw data)/333.87 + 21.0 in DegC. Magnetomer Detect the mag. using WHO_AM_I register value There is no self-test for the magnetometer. Initialize:
  23. Power down unit.
  24. Read sensitivity adjustment values from device ROM.
  25. Calculate scale-adjusted values and return these values. destination[0] = (float)(rawData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc. Calibration: Do back-forth measurements and estimate bias to be removed from data. Read data:
  26. Call readMagData() to obtain raw data.
  27. Call getMres()to get resolution multiplier in mRes.
  28. Multiple each axis by mRes and the sensitivity adjustment from initialization, then add a bias (in mG unit). This bias is loaded to a bias array with values obtained from back-forth calibration. Quarteron and Yaw, pitch, roll Call updateTime() in loop (only present in SPE library and code sample). Updata myIMU.yaw to subtract the declination angle. The system may have to be updated at 200Hz to get results correctly. I'll see if it works under less frequency, when the main loop is idle.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/279#issuecomment-395936702, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qtkNs2XxgMHGD9sQz-Y9ygD63i_zks5t60MmgaJpZM4UYG_Z .

liudr commented 6 years ago

Kris,

I am running your sample code now. In the loop() function, you're running MadgwickQuaternionUpdate() 50 times with the same a, g, and m values. What is the reason of doing this? Thanks.

kriswiner commented 6 years ago

Madgwick and Mahony algorithms are iterative, like a steepest descent algorithm, so they need to be run multiple times per data sample if your MCU can do this. On STM32L4 without the constraint this fusion algorithm runs at 50 kHz, way too fast, so I put in an iteration counter to limit the number. For best results, run the accel and gyro at 1 kHz, the mag at 100 Hz, and the fusion algorithm at 10 - 20 iterations per gyro sample.

On Wed, Jun 13, 2018 at 3:29 PM, liudr notifications@github.com wrote:

Kris,

I am running your sample code now. In the loop() function, you're running MadgwickQuaternionUpdate() 50 times with the same a, g, and m values. What is the reason of doing this? Thanks.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/279#issuecomment-397108490, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qiT6gSji2s91lvW7S-dYwt8PwbXTks5t8ZJRgaJpZM4UYG_Z .

liudr commented 6 years ago

Got it. Thanks. I'm now looking at the magnetic calibration with the figure-8 method. In the function call, you return two sets of calibration factors. The biases that are supposed to be subtracted from each axis, and the scales that are supposed to be multiplied to each axis. I don't understand the reason behind the scale. The scales are calculated from raw data without the sensitivity factors multiplied so they are supposed to have different sensitivities. You averaged three axis and gave each axis the average over its own axis readout. This boosts readouts of the axis with lower sensitivity and reduces readouts of the axis with higher sensitivity. It seems to be doing the job of the sensitivity factor you extracted from the device ROM. The final readout in loop() has both sensitivity factor and this scale factor multiplied to the raw data. I'm a bit confused.

kriswiner commented 6 years ago

The calibration includes the fuze ROM factory sensitivity adjustment. Unavoidable board stresses will change this:

https://github.com/kriswiner/MPU6050/wiki/Simple-and-Effective-Magnetometer-Calibration

On Thu, Jun 14, 2018 at 9:10 AM, liudr notifications@github.com wrote:

Got it. Thanks. I'm now looking at the magnetic calibration with the figure-8 method. In the function call, you return two sets of calibration factors. The biases that are supposed to be subtracted from each axis, and the scales that are supposed to be multiplied to each axis. I don't understand the reason behind the scale. The scales are calculated from raw data without the sensitivity factors multiplied so they are supposed to have different sensitivities. You averaged three axis and gave each axis the average over its own axis readout. This boosts readouts of the axis with lower sensitivity and reduces readouts of the axis with higher sensitivity. It seems to be doing the job of the sensitivity factor you extracted from the device ROM. The final readout in loop() has both sensitivity factor and this scale factor multiplied to the raw data. I'm a bit confused.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/279#issuecomment-397350707, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qtDcW7eLack9_qEaKZXJk3DXDMZsks5t8or0gaJpZM4UYG_Z .

liudr commented 6 years ago

Kris, Just wanted to say thanks for all your help. I wrote a blog post about my calibration of the magnetic sensor. Hope others find it useful: https://liudr.wordpress.com/2018/06/24/calibrate-a-magnetic-sensor/

kriswiner commented 6 years ago

Very nice! I am glad you got it all sorted out...

Kris

On Sun, Jun 24, 2018 at 6:24 PM, liudr notifications@github.com wrote:

Closed #279 https://github.com/kriswiner/MPU9250/issues/279.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/279#event-1697849554, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qtTmHy_ZmOwQ7jImcLppUQ1axz2-ks5uADvHgaJpZM4UYG_Z .