Using BMM270 Accel/Gyro and BMI150 Magnetomer Sensors with Teensy 4.x

mjs513

Senior Member+
Lately been trying to consolidate/organize my IMU efforts and decided to grab a couple of new sensors that are used on the Nano Sense V2, the BMI270/BMM150 specifically. For reference Sparkfun has a BMI270 sensor and Waveshare has a BMM150. However found that M5Stack has a single module with both sensors along with a BMP280 pressure sensor.

One of the things that the BMI270 is a aux bus that another i2c sensor can be attached to which how the M5Stack module is configured. The Sparkfun board does have this as an option but does not provide the interface software to accomplish this. While the M5Stack does. I did find a library that does this that I slightly modified for my use to add in a couple of extra config functions for the gyro, accel and magnetomer. This was originally developed by yahyatawil called BMI270_AUX_BMM150. My modifed version is at: https://github.com/mjs513/BMI270_AUX_BMM150/tree/add_functions with example on how to get the data from all three sensors. The three new functions added were:

Code:
    void setAcellConfig(uint8_t odr, uint8_t range, uint8_t band_width);
    void setGyroConfig(uint8_t odr, uint8_t range, uint8_t band_width);
    void setMagConfig(uint8_t pwrmode, uint8_t preset);

along with the ability now to specify the what you want for the constructor and wire port you want to use.

Running this on a T4 with Madgwick's new fusion algorithm was interesting to get working properly. Key to using it is that the update rate has to be based on the gyro update rate only!!!! Good thing to keep in mind when using it. The fusion algorithm along with the Teensy example can be found at: https://github.com/mjs513/Fusion/tree/Fusion+Teensy-Examples

I posted a quick video of it in action using x-imu3 Gui.


Sensor calibration was done using a custom 6 point calibration and MotionCal for the Magnetometer. More in the next post
 
Lets talk calibration now that I slept.

6-point Calibration:

The 6-point tumble calibration is a method used to calibrate magnetometers (and other 3-axis sensors like accelerometers) by measuring the sensor’s output in six different orientations. This technique helps to determine and correct for offsets, gains, and cross-axis sensitivities. - mscopilot

Back in 2018 while experimenting with using @brtaylor's uNav INS code @Don Kelly developed a 6 point cal tool for the MPU-9250 which he updated in following posts. I have since updated it to use with the my changes for the MPU-9250 lib and the BMI150/BMM270 sensors. This tool can be found at: https://github.com/mjs513/IMU-Calibration-Methods/tree/main/6-point Calibration. There is a PDF with instructions for use with the tool that Don put together. But for those that don't know 6point calibration here is an explanation from CoPilot

Positioning: The sensor is placed in six different orientations:​
+X axis up​
-X axis up​
+Y axis up​
-Y axis up​
+Z axis up​
-Z axis up​
Data Collection: In each position, the sensor’s output is recorded. Ideally, these measurements should reflect the true magnetic field vector in each orientation.​
Calculations:​
Offsets: The average of the positive and negative measurements for each axis gives the offset. For example, the offset for the X-axis is calculated as ((X_{+} + X_{-}) / 2).​
Gains: The difference between the positive and negative measurements for each axis gives the gain. For example, the gain for the X-axis is calculated as ((X_{+} - X_{-}) / 2).​
Cross-axis Sensitivities: These are calculated to correct for any misalignment between the sensor axes.​
Correction: The calculated offsets, gains, and cross-axis sensitivities are then used to correct the raw sensor data, ensuring accurate readings.​

After running the sketch offsets and biases will look like
Code:
Printing IMU:
IMU Biases:
Accel X Bias:         -0.044756
Accel X Scale Factor: 0.813677
Accel Y Bias:         -0.044756
Accel Y Scale Factor: 0.813208
Accel Z Bias:         0.215011
Accel Z Scale Factor: 0.808723
Gyro X Bias:          0.000000
Gyro Y Bias:          0.000000
Gyro Z Bias:          0.000000
Mag X Bias:           -7.099262
Mag X Scale Factor:   0.981731
Mag Y Bias:           24.530724
Mag Y Scale Factor:   1.000557
Mag Z Bias:           -8.135109
Mag Z Scale Factor:   1.018384

which when put into Fusion will look like
Code:
//Min - max
float acc_off[] = {-0.024255, 0.192162, -0.205655};
float acc_scale[]  = {0.813405, 0.812885, 0.807327};
float magn_off[]  = {-8.812010, 23.329735, -1.055010};
float magn_scale[]  = {1.009141, 0.976412, -1.055010};

and for the corrections:
Code:
  //uncomment if using 6point cal
  //val[6] = (mx - magn_off[0]) * magn_scale[0];
  //val[7] = (my - magn_off[1]) * magn_scale[1];
  //val[8] = (mz - magn_off[2]) * magn_scale[2];

The sketch also allows you see noise values;
Code:
Static IMU Sensor Sigma Values:
axb Sigma (m/ss):  0.026049  axMax: 0.475129  axMin: -0.703895
ayb Sigma (m/ss):  0.032561  ayMax: 0.170108  ayMin: -0.703895
azb Sigma (m/ss):  0.016178  azMax: -11.854771  azMin: -11.995549
gxb Sigma (deg/s): 0.001844  gxMax: 0.000000  gxMin: -0.034907
gyb Sigma (deg/s): 0.000000  gyMax: 0.000000  gyMin: 0.000000
gzb Sigma (deg/s): 0.010666  gzMax: 0.244346  gzMin: 0.000000
hxb Sigma (uT):    1.620109  hxMax: 10.000000  hxMin: 1.000000
hyb Sigma (uT):    1.300371  hyMax: 39.000000  hyMin: 30.000000
hzb Sigma (uT):    1.986021  hzMax: 38.000000  hzMin: 24.000000


MotionCal

MotionCal is a magnetometer calibration tool that PRJC developed to the sensors on the PropShield but can be used for other sensors. Adafruit actually has an old write up on how to use MotionCal. It does seem you have to do a conversion of the values coming from the sensor which I have done in the interface sketch: https://github.com/mjs513/IMU-Calibration-Methods/tree/main/PRJC Motion Cal/examples. You can not use the one from the site link. If you do this right you will get something that looks like:
BMI150_MotionCal.png


Note that the test sketch uses dual serial so I can send continuous data to MotionCal. When you hit Send Cal button it sends the calibration data to the serial monitor while motion cal continues so you can have some fun.

In the Fusion Sketch the data input looks like:

C++:
  const FusionMatrix softIronMatrix = {1.0300, 0.0006, -0.0011, 0.0006, 0.9830, -0.0009, -0.0011, -0.0009, 0.9877};
  const FusionVector hardIronOffset = {-8.151, 23.420, -9.036};

A good write up for the various calibration tools can be found at: Tutorial: How to calibrate a compass (and accelerometer) with Arduino
 
Last edited:
Great stuff

Thanks Kurt.

Almost forgot. I put together a sketch that combines several AHRS methods that can be tested with the MPUT-9250 or the M5Stack Board:
Code:
// Set filter type:
//  0 = Madgwick implementation of Mahoney DCM
//  1 = Madgwick Gradient Descent, in Quaternion form
//  2 = xxxxxxxxxx
//  3 = Madwick Original Paper AHRS
//  4 = DCM Implementation - miniIMU-9 implementation
//  5 = Complimentary Filter
//  6 = uNavAHRS
//  7 = DCM Algorithm -
//  8 = PJRC NXPMotionSense
// Select the filter type here
#define MARG 3
 

Attachments

  • MPU9250_filters_test-241114a.zip
    66.8 KB · Views: 13
Thanks so much for all your work on adapting for the m5stack boards! I have one on order and have been looking for ways to apply the filters.
 
Thanks so much for all your work on adapting for the m5stack boards! I have one on order and have been looking for ways to apply the filters.
Let us know how it works out for you. Oh, by the way I ordered a set of these cables from Amazon to make it easier to connect to the Teensy:

1732047816436.png
 
Will do and thank you for the link to the cable. Have you been satisfied with the results? I hope to be able to use the m5atom bmm270 / bmi150 combination to give a heading either published over a socket or on a display for a small sailboat without having to purchase and connect a 9 dof breakout board.
 
Will do and thank you for the link to the cable. Have you been satisfied with the results? I hope to be able to use the m5atom bmm270 / bmi150 combination to give a heading either published over a socket or on a display for a small sailboat.
Haven't used it yet in any projects but based on testing using Madgwick Fusion algorithm seems to work well. With the other filters I tested I never fine tuned the gains for the combo so jury is out with using those but expect them to work when the gains are tuned.
 
I received the m5stack atoms3r but am having a tough time getting the output correct for motioncal. Is there a way to send sample serial monitor output that goes into motioncal?
 
I received the m5stack atoms3r but am having a tough time getting the output correct for motioncal. Is there a way to send sample serial monitor output that goes into motioncal?
Well you are going to have a have a slight problem. Th atoms3r is a ESP32-S3 microcontroller not a teensy and does not have a BMM150 and BMI270 sensors on board. The motioncal examples I have are for dual serial on a Teensy which in theory could get adapted to run on a ESP32-S3. But again - no sensors unless you also have one of these attached: https://shop.m5stack.com/products/6-dof-imu-pro-mini-unit-bmi270-bmm150-bmp280. Let me know your config I do have a ESP32-s3 someplace.

EDIT: According to this https://github.com/m5stack/M5AtomS3 it has a 6-axis gyro sensor MPU6886 on board but no magnetometer.
 
Thanks so much for the quick response. While it does have a different microcontroller, it does use a bmm150 and bmi270. The m5unified appears to provide a wrapper around the bosch library. https://shop.m5stack.com/products/atoms3r-dev-kit I tried to adapt your code to fit and am getting the following serial output sample:

Raw:980,197,46,-12,-22,-35,1842,-2647,1876
Raw:867,147,-58,-8,-54,-30,1452,-2797,2295
Raw:770,131,-121,-4,-56,-26,1452,-2797,2295
Raw:720,166,-13,-3,-37,-23,1452,-2797,2295
Raw:786,147,-44,0,-15,-12,1452,-2797,2295
Raw:522,138,-101,4,1,-5,763,-2857,2205
Raw:911,108,-115,10,-10,2,763,-2857,2205
Raw:824,90,-125,14,14,6,763,-2857,2205
Raw:868,51,-90,19,19,13,73,-2407,2295

Points are all over in motioncal and acceleration and gyro data show 0.

Thank you!
 
While it does have a different microcontroller, it does use a bmm150 and bmi270. The m5unified appears to provide a wrapper around the bosch library. https://shop.m5stack.com/products/atoms3r-dev-kit I tried to adapt your code to fit and am getting the following serial output sample:
was looking at the wrong one. Can you post your code so I can give it a try and see what you did. Because the data you posted is showing accel and gyro data as well as magnetometer data.

May not get to it to later today as in middle of doing a few things.

ps. Motioncal only gives you calibration data for the magnetometer - accel and gyro have to calibrated differently.
 
Absolutely, https://gist.github.com/russellkt/ef138ce1938d1c2b7314877a9529ca30 . Any help appreciated and completely understand there might be a delay. Also, here's some sample output of the mag x,y,z values:

mx:-211.572113 my:212.044464 mz:-79.290131
mx:-217.569672 my:227.038361 mz:-64.296234
mx:-208.573334 my:221.040802 mz:-28.310884
mx:-199.576996 my:236.034698 mz:25.667139
mx:-208.573334 my:239.033478 mz:-70.293793
mx:-217.569672 my:221.040802 mz:-43.304779
mx:-208.573334 my:233.035919 mz:-37.307220
mx:-211.572113 my:242.032257 mz:-25.312105
mx:-190.580658 my:227.038361 mz:-34.308441
 
Last edited:
Sorry but it took me a while to sort through their library. First thing is there axis orientation is setup up different than mine but with that said they already do the realignment within in their library so you do not need to change it to match what is in my sketch. So you code should just be:

Code:
   // Match axis orientation from example
   val[0] = ax;
   val[1] = ay;
   val[2] = az;
  
   val[3] = gx * deg2rad;
   val[4] = gy * deg2rad;
   val[5] = gz * deg2rad;
  
   val[6] = mx;
   val[7] = my;
   val[8] = mz;


The 1st value is a dt (on order of 30ms, next 3 are accel, then 3 gyro and last 3 are mx, my and mz
Code:
40, -1.006677, -0.011365, 0.737512, 0.000000, -0.000000, -0.000000, 20.000000, -28.000000, -2.000000
30, -1.004285, -0.010767, 0.738708, 0.000000, -0.000000, -0.000000, 23.000000, -25.000000, 0.000000
20, -1.007275, -0.011365, 0.736914, 0.000000, -0.000000, -0.000000, 23.000000, -25.000000, 0.000000
40, -1.004883, -0.007178, 0.738110, 0.000000, -0.000000, -0.000000, 19.000000, -25.000000, -4.000000
30, -1.006079, -0.008972, 0.738708, 0.000000, -0.000000, -0.000000, 22.000000, -27.000000, -1.000000
50, -1.008472, -0.007776, 0.738708, 0.000000, -0.000000, -0.000000, 20.000000, -24.000000, 4.000000
9, -1.009070, -0.005981, 0.737512, 0.000000, -0.000000, -0.000000, 20.000000, -24.000000, 4.000000
31, -1.004883, -0.008374, 0.736914, 0.000000, -0.000000, -0.000000, 23.000000, -25.000000, -8.000000
50, -1.005481, -0.009570, 0.738708, 0.000000, -0.000000, -0.000000, 21.000000, -26.000000, 0.000000

as for the differences in magnetometer values they look to be off by a factor of 10. So my guess is that there values are in milliGauss where mine are in microTelsas - so all you would have to do is multiply by 0.2 which is the conversion factor:
Code:
   val[6] = mx * 0.1;  //conversion from mG to uT
   val[7] = my * 0.1;
   val[8] = mz * 0.1;

Accel should be in g's and gryo data should be output to the sketch in radians per second otherwise you would have to make adjustments to the sketch. Havent got that far in their code yet.
 
Just a quick follow up. Looks like what you are getting is raw data that hasn't been scaled yet you have to call
Code:
  void BMI270_Class::getConvertParam(imu_convert_param_t* param) const
  {
    param->mag_res = 10.0f * 4912.0f / 32760.0f;
    param->temp_offset = 23.0f;
    param->temp_res = 1.0f / 512.0f;
  }

to get the conversions for temp and mag. But to be honest have no clue from their code what their scale factors are or what they are setting the accelerometer and gyro are. Sorry.
 
Thanks so much for your help! I was unable to find the scaling factors but after doing a simple calibration of the magnetometer, I'm getting reasonable results. Now, I'm working on adding a Madgwick filter to handle the tilt compensation. I'll share once I get some results.
 
Thanks so much for your help! I was unable to find the scaling factors but after doing a simple calibration of the magnetometer, I'm getting reasonable results. Now, I'm working on adding a Madgwick filter to handle the tilt compensation. I'll share once I get some results.
Well if you want you can always give the library I posted here a shot on the board you have - should still work.

Good luck with the AHRS.
 
With my poor coding abilities, I've spent quite a bit of time and have found the following: 1) had to remap axes for both accelerometer and magnetometer so that ax = ay ay = ax, and mx = my my = mx which gets me decent headings when staying flat. I'm able to get tilt compensaton on pitch, but when rolling to the right I am getting large variations. Went back and looked at the calibration data and realized I'm getting a huge difference between the positive(around 500) while minimum is around -30 which to me explains the large heading variation when rolling right. I've recalibrated a few times and keep getting similar results so believe I'm moving on to a different sensor to verify it's not just my poor coding skills. Thanks again for all the work you put into this and for your help!
 
I did not since I had other issues and the m5unified library already having the imu functions. However, I did use your example and the algorithms as a reference and greatly appreciate it.
 
Back
Top