Using MPU6050 vertically with Y axis aligned with gravity

Hello everyone!

I am trying to use the MPU6050 sensor in a vertical position, (i.e. with the Y-axis aligned with gravity and perpendicular to the ground), and thus acquire the rotation data (yaw) on this axis.

I know that the Z-axis suffers drift in the MPU6050, because it does not have a magnetometer. I also know that Euler Angles suffer from Gimbal Lock.

But I was using Jeff Rowberg’s code (i2cdevlib/Arduino/MPU6050 at master · jrowberg/i2cdevlib · GitHub), which uses quaternions, but I’m still stuck in this job.

I tried to change the MPU6050_6Axis_MotionApps20.h file, for the Y axis to become vertical, following these instructions from another forum: Change default axes orientation - MPU-6050 6-axis accelerometer/gyroscope (InvenSense) - I2Cdevlib Forums, but without success. When I rotate the Y-axis, already positioned vertically, I don’t receive correct data, when " #define OUTPUT_READABLE_YAWPITCHROLL " is uncommented. I also tried to uncomment " #define OUTPUT_READABLE_QUATERNION " and see what happens, but incorrects values came too (when I rotate the sensor and stop it, the values change, but a little bit after the previous values come back).

However, I also tried to use Teapot output, unccomenting " #define OUTPUT_TEAPOT ", of Jeff’s code, and using Processing software, I was able to rotate the MPU6050 and the graphic output (airplane) was responding correctly!

My problem is: I need the vertical axis rotation data, when the MPU6050 is arranged vertically. So, when the MPU6050 is positioned vertically, I need the Y-axis rotation data. This is my problem.

If the Teapot works correctly, why can’t I get the correct data from Euler Angles or Quaternions?

Can someone help me? Thanks in advance!

My code (.ino file) is attached here!

MPU6050DMP6.ino (17 KB)

It is simply not possible to get the absolute yaw angle (rotation about vertical axis) with that sensor. Which axis you define to be vertical is irrelevant.

Gravity allows you to measure tilt and roll, by using the accelerometer to define "down", but you need another external reference (like a magnetometer) to get yaw.

You need a 9DOF sensor and good software to estimate 3D orientation for a moving object. The BNO055 is self contained, with its own MPU and fairly easy to use, but not very accurate.

Thanks for your reply jremington.

But how does the Teapot Output work, so? When the sensor is placed vertically, and then I rotate it, the Teapot works well and reproduces the correct movement.

Can't I get the "value" of this movement to use as an angle, or quaternion?

My application consists of analyzing the rotation of the trunk of the human body, so the sensor will be vertical when the person is standing, and I need to know when the rotation of the trunk will occur (one sensor will be close to the neck and the other at the end of the back, and then I will compare both sensors).

Just for example, I attached two prints that show the correct output from Teapot demo and what I need for my project.

vertical rotating.png

vertical without rotating.png

A relative value of yaw can be calculated from the gyro, but it will slowly drift.

The yaw angle will not be referenced to North, except possibly by accident.

Right.

I can't get the BNO055, but I'll get the MPU9250 to work. So, knowing that I will use it in the vertical position, do I need to calibrate it (accel/gyro/magnetometer) in this position or in a flat position?

The same routine for the MPU6050 can be used to calibrate the MPU9250's acceleration and gyroscope, right?

And for the magnetometer, do you know any code to calibrate it?

Thanks for your attention.

The MPU-9250 will work well, but ONLY if both the accelerometer and magnetometer are carefully calibrated, or the orientation results will be useless. Calibration is a full 3D operation (see below).

Most of the available MPU-9250 software packages doesn’t work well because they do not include calibration code, and people just give up. The Sparkfun and Kris Winer MPU-9250 packages have never worked, because of a major program bug that they won’t bother to fix.

I rewrote the Madgwick/Mahony AHRS package so that it works with the MPU-9250, and have attached it along with a program to collect calibration data. It is essential to perform that calibration step.

The best calibration procedure and tutorial is this one: Tutorial: How to calibrate a compass (and accelerometer) with Arduino | Underwater Arduino Data Loggers

If you decide to tackle the problem and run into trouble, post again and I’ll try to help.

New_MPU9250.zip (237 KB)

Hello jremington.

Only today I was able to work on this project.

First, thanks for your files!

Second, some questions (PS.: I’m using ESP32 DEVKIT V1 board):

1. Examining the codes, I deduced that only the VDD, GND, SDA and SCL pins are used in the MPU9250, right?

2. What do I need to do first: calibrate the sensor via the MPU9250.ino file or via the link tutorial that you published? That was not very clear to me, sorry.

3. When I tried to compile the MPU9250_cal, two erros appeared:

First one:

Arduino: 1.8.12 (Windows 10), Placa:"DOIT ESP32 DEVKIT V1, 80MHz, 115200, None"

In file included from sketch\libs/MPU9250.cpp:37:0,

                 from C:\Users\Nícolas\Desktop\UCS\Aulas\TCC\New_MPU9250\MPU9250_cal\MPU9250_cal.ino:15:

sketch\libs/MPU9250.h:41:26: fatal error: avr/pgmspace.h: No such file or directory

compilation terminated.

exit status 1
Erro compilando para a placa DOIT ESP32 DEVKIT V1

So, in the MPU9250.h file, I commented out the line 41 “#include <avr/pgmspace.h>”. I think it’s not being used, correct?

Second one:

Arduino: 1.8.12 (Windows 10), Placa:"DOIT ESP32 DEVKIT V1, 80MHz, 115200, None"

In file included from sketch\MPU9250_cal.ino.cpp:1:0:

sketch\libs/I2Cdev.cpp: In static member function 'static int8_t I2Cdev::readBytes(uint8_t, uint8_t, uint8_t, uint8_t*, uint16_t)':

sketch\libs/I2Cdev.cpp:278:62: error: 'BUFFER_LENGTH' was not declared in this scope

             for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {

                                                              ^

C:\Users\Nícolas\AppData\Local\Arduino15\packages\esp32-husarnet\hardware\esp32\1.0.5\cores\esp32/Arduino.h:181:24: note: in definition of macro 'min'

 #define min(a,b) ((a)<(b)?(a):(b))

                        ^

sketch\libs/I2Cdev.cpp: In static member function 'static int8_t I2Cdev::readWords(uint8_t, uint8_t, uint8_t, uint16_t*, uint16_t)':

sketch\libs/I2Cdev.cpp:418:70: error: 'BUFFER_LENGTH' was not declared in this scope

             for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) {

                                                                      ^

C:\Users\Nícolas\AppData\Local\Arduino15\packages\esp32-husarnet\hardware\esp32\1.0.5\cores\esp32/Arduino.h:181:24: note: in definition of macro 'min'

 #define min(a,b) ((a)<(b)?(a):(b))

                        ^

exit status 1
Erro compilando para a placa DOIT ESP32 DEVKIT V1

What’s the size of the BUFFER_LENGTH in this case? There is another NBWIRE_BUFFER_LENGTH that is set to 32.

Thanks for your help!

only the VDD, GND, SDA and SCL pins are used in the MPU9250

Correct. You may need to add pullup resistors (2.2 to 4.7K) to the SDA and SCL lines, if they aren't already on the board. I2C won't work without them.

The code was developed for AVR-based Arduinos. I can't help with porting that to ESP32.

BUFFER_LENGTH is 32. I have no idea why the ESP32 compiler would complain about the "min" macro, but it is trivial to rewrite that line of code.

I would recommend picking up a couple of $3 eBay 3.3V Pro Minis, and it should work out of the box.

To calibrate the sensor, you need to read through the tutorial, use the supplied calibration program to collect calibration data, then run the Magneto program to calculate the corrections.

Apparently, the sensor is working fine, except the magnetomer.

Running MPU9250._cal twice, after the print "Test device connections ..." was printed: MPU9250 ??, but when I move the sensor, the values change accordingly, and the pins are connected correctly.

I received these values:

First:

Done. Gyro offsets (raw) -323.1, 107.0, 190.7

Done. rms A = 12783.33, rms M = 0.00

Second:

Done. Gyro offsets (raw) -325.3, 109.9, 194.0

Done. rms A = 12881.50, rms M = 0.00

But the magnetometer has always been printed zero. Is something being forgotten?

EDIT 1:

I saw that this part of code is not being used:

I2Cdev::writeByte(devAddr, MPU9250_RA_INT_PIN_CFG, 0x02); //set i2c bypass enable pin to true to access magnetometer
	delay(10);

Maybe that's the problem?

EDIT 2:

I added those lines of code and I think it worked.
The output:

Done. Gyro offsets (raw) -333.7, 107.2, 197.4

Done. rms A = 13133.11, rms M = 79.29

I saw that this part of code is not being used:

Can you explain? How did you decide it was "not being used"?

In any case, the results look OK in the end.

I use a spreadsheet to read in the 6 calibration values, and write out two separate .txt files (with blank separators), one for the accelerometer and one for the magnetometer. You need to run magneto separately for each one.

For the magnetometer, I use "Norm" of 1000 or 10000, to get enough significant figures in the scale matrix, then divide the diagonal entries by 1000 or 10000 for actual use.

The scale factors you get should not be much different than the numbers left in the code after calibrating my sensor, but the offsets might be very different.

jremington:
Can you explain? How did you decide it was "not being used"?

In the "getMag_raw" function, these first two lines of code are not present. But in the MPU9250.cpp file, in the function "getMotion9", these lines are present. Without them, the I2C can't access the magnetometer, right? So I added at the beginning of "getMag_raw" function.

jremington:
For the magnetometer, I use "Norm" of 1000 or 10000, to get enough significant figures in the scale matrix, then divide the diagonal entries by 1000 or 10000 for actual use.

Isn't that "Norm" value for the accelerometer? Reading the tutorial, that's what I understood. And for the magnetometer, the value can be from my city, through this link: Magnetic Field Calculators ? What is the "most correct"?

Questions:

1. Are the Combined bias (b) output the offsets? Looking through your code, that's what look like (for me, the bias values are more similar to the calibration serial output):

float A_cal[6] = {515.0, 279.0, 751.0, 5.96e-5, 6.26e-5, 6.06e-5}; // 0..2 offset xyz, 3..5 scale xyz
float M_cal[6] = {18.0, 28.3, -39.6, 0.01403, 0.01414, 0.01387}; // can make both 3x3 to handle off-diagonal corrections

2. Which column do I use for the XYZ scale (or offset) value? I didn't understand when you said "divide the diagonal entries".

3. Are the MU9250_cal output values in microTesla?

re: "those two lines" -- They should be earlier in setup(). I recall making a very small change in the calibration program, and probably forget to test it again.

  1. Combined bias is the same as offset.

  2. This comment "// 0..2 offset xyz, 3..5 scale xyz" indicates that array entries 0, 1, 2 are the offsets, and 3,4,5 are the scale factors.

Magneto calculates scale factors such that the average measurement has vector magnitude "Norm", a number that you input. You want "Norm" to be 1.0, but then (for the accelerometer) you get very few useful digits in the correction matrix. Run Magneto with different norm values and you should see what I mean.

  1. The actual units are irrelevant for navigation, and are eventually discarded as mentioned above.
  1. Combined bias is the same as offset.

Right. I was confused because you said that the offsets might be very different from the serial output, but it was the opposite for me.

  1. This comment "// 0..2 offset xyz, 3..5 scale xyz" indicates that array entries 0, 1, 2 are the offsets, and 3,4,5 are the scale factors.

Ok, I understood this. But the Magneto has the Combined Bias and 9 other values (correction for combined scale factors). Ok, the Combined Bias are the offset values. But what about the others 9 values? What are the scale factors that I should use? The first column? Second column? Third column? Or maybe first, second or third line? That's my doubt, because I have 9 values, but in the code I only need 3.

The diagonal entries of the correction matrix are the axial scale factors. The off-diagonal terms are the cross terms, which are often required for the full correction of the magnetometer data.

See this forum post for more information: https://forum.pololu.com/t/correcting-the-balboa-magnetometer/14315

I cleaned up the calibration program a bit, tested it and replaced the original code attachment in Reply #6.

Example accelerometer calibration:

The offsets are 739.7, 400.7, 897.4 and the scale factors are (after dividing by 10000) 6.160e-5, 6.058e-5, 6.003e-5.

Thanks for your explanations!

I was doing some tests, and the magnetometer is very sensitive to metal objects, as expected.

Considering that I’ll use this sensor in a industrial environment, do you think I need to perform the calibration procedure close to these objects? Or does it not matter?

It should be obvious that if the surroundings distort the Earth's magnetic field, the magnetometer must be calibrated in place.

Ship captains calibrate their magnetometers by turning the entire ship through 360 degrees, twice.

Yes, it should be. :grinning:

The Mahony code seems to be working very well after calibration, according to what I needed.

Thank you very much for your time and files!