Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: How do I get the angle out of the MPU9250?

  1. #1
    Member
    Join Date
    Dec 2016
    Location
    Buena Park ca
    Posts
    96

    How do I get the angle out of the MPU9250?

    I installed it and connected it to the IC2 and all seems fine.

    I tought the IMU.getGyroX_rads() will give me the angle, (inclination) of the IMU, but instead I get angular acceleration,

    I moved it from flat to 45deg and it went back to zero. So I am assuming it showed the accel, not the final angle.


    Here is my serial print code

    Serial.print(IMU.getGyroX_rads(),6);
    Serial.print (" DEG ");
    float Angle = degrees (IMU.getGyroX_rads());
    Serial.println (Angle,2);

    My serial read:

    0.008125 DEG 0.47. -- IMU was flat
    0.016115 DEG 0.92
    0.027167 DEG 1.56
    0.286963 DEG 16.44
    0.264858 DEG 15.18
    0.246615 DEG 14.13. --- while in motion
    0.214790 DEG 12.31
    0.076037 DEG 4.36
    0.132497 DEG 7.59
    0.105998 DEG 6.07
    -0.047669 DEG -2.73
    0.022906 DEG 1.31
    0.029298 DEG 1.68
    -0.015977 DEG -0.92. -- was at 45 deg, static


    my code is standard, the Basic_I2C file,



    Please help,

    Thank you.

    Mitch

  2. #2
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    447
    You need to apply a filter to the raw sensor measurements in deg/s and m/ss to angular degrees. The easiest is called a complementary filter, others include Madgwick and Kalman filters. Joop Brokking has some excellent videos on setting up a complementary filter (for use in a drone, but can be used for anything):

    https://www.youtube.com/channel/UCpJ...P84TXQtwiRNm1g

    Otherwise, lots of sources on google...

  3. #3
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    What Don Kelly said. I'm using the Arduino implementation of Madgwick filter for a drone and balancing scooter, it's easy enough to use and works nicely https://github.com/arduino-libraries/MadgwickAHRS. You just tell it at what rate you read the sensor, update it with that rate and you'll get quite robust pitch, roll and yaw.

    For extra performance, it's good to do a simple calibration for the sensor, so that there's no offset when it's not moved (~0 rad/s, acceleraion 0 on all axis except for the one against gravity, ~9.81 m/s on that) and lower the Beta value, which make it rely more on the gyro and thus give less error when moving it around. Too low beta will cause it to drift, though.

  4. #4
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,323
    What library are you using? Don is correct but some libs have the filter as part of the library?

    IMU.getGyroX_rads is giving returning the gyro rate in the x direction not the pitch or roll or yaw, that's why you need something like the madgwick filter

  5. #5
    Member
    Join Date
    Dec 2016
    Location
    Buena Park ca
    Posts
    96
    Maybe is easier to just use the MPU6150? It seems that it provides my angles without all the trouble.

    right now I am using the https://github.com/bolderflight/MPU9250. library

    Is there a MPU9250 that would do all the filtering for me and give me angles? Not acceleration.

    Thanks

  6. #6
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    The keyword with these is "DMP" - the integrated motion processing engine in these old Invensens IMUs Quick googling found that the sparkfun library can use that, and there's pitch roll and yaw computed. I have never used that library so cannot comment more. https://github.com/sparkfun/SparkFun...rduino_Library
    I used the DMP to get orientation with MPU6050, but it is a finicky black box. I find it is much more simple to do the filtering outside the IMU, just a few library calls instead of fidgeting around with the fifo and the poorly documented, esoteric code that is the DMP. Matter of preference, of course.

  7. #7
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,323
    I agree 100% with @Jopj. I much prefer using either the madgwick or Mahoney filtes with the library you referenced.

    If you look at the PRs associated with the library you will see that someone already added the Madgwick filter to library so you don't have tool https://github.com/bolderflight/MPU9250/pull/26

  8. #8
    Member
    Join Date
    Dec 2016
    Location
    Buena Park ca
    Posts
    96
    I got the library from Sparkfun to work and give me the Yaw, Pitch, Roll:

    If you need it here it is, nice and stable .

    Code:
    /* MPU9250 Basic Example Code
     by: Kris Winer
     date: April 1, 2014
     license: Beerware - Use this code however you'd like. If you
     find it useful you can buy me a beer some time.
     Modified by Brent Wilkins July 19, 2016
    
     Demonstrate basic MPU-9250 functionality including parameterizing the register
     addresses, initializing the sensor, getting properly scaled accelerometer,
     gyroscope, and magnetometer data out. Added display functions to allow display
     to on breadboard monitor. Addition of 9 DoF sensor fusion using open source
     Madgwick and Mahony filter algorithms. Sketch runs on the 3.3 V 8 MHz Pro Mini
     and the Teensy 3.1.
    
     SDA and SCL should have external pull-up resistors (to 3.3V).
     10k resistors are on the EMSENSR-9250 breakout board.
    
     Hardware setup:
     MPU9250 Breakout --------- Arduino
     VDD ---------------------- 3.3V
     VDDI --------------------- 3.3V
     SDA ----------------------- A4
     SCL ----------------------- A5
     GND ---------------------- GND
     */
    
    #include "quaternionFilters.h"
    #include "MPU9250.h"
    
    
    
    #ifdef LCD
    #include <Adafruit_GFX.h>
    #include <Adafruit_PCD8544.h>
    
    // Using NOKIA 5110 monochrome 84 x 48 pixel display
    // pin 9 - Serial clock out (SCLK)
    // pin 8 - Serial data out (DIN)
    // pin 7 - Data/Command select (D/C)
    // pin 5 - LCD chip select (CS)
    // pin 6 - LCD reset (RST)
    Adafruit_PCD8544 display = Adafruit_PCD8544(9, 8, 7, 5, 6);
    #endif // LCD
    
    #define AHRS true         // Set to false for basic data read
    #define SerialDebug true  // Set to true to get Serial output for debugging
    
    // Pin definitions
    int intPin = 12;  // These can be changed, 2 and 3 are the Arduinos ext int pins
    int myLed  = 13;  // Set up pin 13 led for toggling
    
    #define I2Cclock 400000
    #define I2Cport Wire
    #define MPU9250_ADDRESS MPU9250_ADDRESS_AD0   // Use either this line or the next to select which I2C address your device is using
    //#define MPU9250_ADDRESS MPU9250_ADDRESS_AD1
    
    MPU9250 myIMU(MPU9250_ADDRESS, I2Cport, I2Cclock);
    
    void setup()
    {
      Wire.begin();
      // TWBR = 12;  // 400 kbit/sec I2C speed
      Serial.begin(38400);
    
      while(!Serial){};
    
      // Set up the interrupt pin, its set as active high, push-pull
      pinMode(intPin, INPUT);
      digitalWrite(intPin, LOW);
      pinMode(myLed, OUTPUT);
      digitalWrite(myLed, HIGH);
    
    
    
      // Read the WHO_AM_I register, this is a good test of communication
      byte c = myIMU.readByte(MPU9250_ADDRESS, WHO_AM_I_MPU9250);
      Serial.print(F("MPU9250 I AM 0x"));
      Serial.print(c, HEX);
      Serial.print(F(" I should be 0x"));
      Serial.println(0x71, HEX);
    
    
    
      if (c == 0x71) // WHO_AM_I should always be 0x71
      {
        Serial.println(F("MPU9250 is online..."));
    
        // Start by performing self test and reporting values
        myIMU.MPU9250SelfTest(myIMU.selfTest);
        Serial.print(F("x-axis self test: acceleration trim within : "));
        Serial.print(myIMU.selfTest[0],1); Serial.println("% of factory value");
        Serial.print(F("y-axis self test: acceleration trim within : "));
        Serial.print(myIMU.selfTest[1],1); Serial.println("% of factory value");
        Serial.print(F("z-axis self test: acceleration trim within : "));
        Serial.print(myIMU.selfTest[2],1); Serial.println("% of factory value");
        Serial.print(F("x-axis self test: gyration trim within : "));
        Serial.print(myIMU.selfTest[3],1); Serial.println("% of factory value");
        Serial.print(F("y-axis self test: gyration trim within : "));
        Serial.print(myIMU.selfTest[4],1); Serial.println("% of factory value");
        Serial.print(F("z-axis self test: gyration trim within : "));
        Serial.print(myIMU.selfTest[5],1); Serial.println("% of factory value");
    
        // Calibrate gyro and accelerometers, load biases in bias registers
        myIMU.calibrateMPU9250(myIMU.gyroBias, myIMU.accelBias);
    
    
        myIMU.initMPU9250();
        // Initialize device for active mode read of acclerometer, gyroscope, and
        // temperature
        Serial.println("MPU9250 initialized for active data mode....");
    
        // Read the WHO_AM_I register of the magnetometer, this is a good test of
        // communication
        byte d = myIMU.readByte(AK8963_ADDRESS, WHO_AM_I_AK8963);
        Serial.print("AK8963 ");
        Serial.print("I AM 0x");
        Serial.print(d, HEX);
        Serial.print(" I should be 0x");
        Serial.println(0x48, HEX);
    
    
        if (d != 0x48)
        {
          // Communication failed, stop here
          Serial.println(F("Communication failed, abort!"));
          Serial.flush();
          abort();
        }
    
        // Get magnetometer calibration from AK8963 ROM
        myIMU.initAK8963(myIMU.factoryMagCalibration);
        // Initialize device for active mode read of magnetometer
        Serial.println("AK8963 initialized for active data mode....");
    
        if (SerialDebug)
        {
          //  Serial.println("Calibration values: ");
          Serial.print("X-Axis factory sensitivity adjustment value ");
          Serial.println(myIMU.factoryMagCalibration[0], 2);
          Serial.print("Y-Axis factory sensitivity adjustment value ");
          Serial.println(myIMU.factoryMagCalibration[1], 2);
          Serial.print("Z-Axis factory sensitivity adjustment value ");
          Serial.println(myIMU.factoryMagCalibration[2], 2);
        }
    
        // Get sensor resolutions, only need to do this once
        myIMU.getAres();
        myIMU.getGres();
        myIMU.getMres();
    
        // The next call delays for 4 seconds, and then records about 15 seconds of
        // data to calculate bias and scale.
    //    myIMU.magCalMPU9250(myIMU.magBias, myIMU.magScale);
        Serial.println("AK8963 mag biases (mG)");
        Serial.println(myIMU.magBias[0]);
        Serial.println(myIMU.magBias[1]);
        Serial.println(myIMU.magBias[2]);
    
        Serial.println("AK8963 mag scale (mG)");
        Serial.println(myIMU.magScale[0]);
        Serial.println(myIMU.magScale[1]);
        Serial.println(myIMU.magScale[2]);
    //    delay(2000); // Add delay to see results before serial spew of data
    
        if(SerialDebug)
        {
          Serial.println("Magnetometer:");
          Serial.print("X-Axis sensitivity adjustment value ");
          Serial.println(myIMU.factoryMagCalibration[0], 2);
          Serial.print("Y-Axis sensitivity adjustment value ");
          Serial.println(myIMU.factoryMagCalibration[1], 2);
          Serial.print("Z-Axis sensitivity adjustment value ");
          Serial.println(myIMU.factoryMagCalibration[2], 2);
        }
    
    
      } // if (c == 0x71)
      else
      {
        Serial.print("Could not connect to MPU9250: 0x");
        Serial.println(c, HEX);
    
        // Communication failed, stop here
        Serial.println(F("Communication failed, abort!"));
        Serial.flush();
        abort();
      }
    }
    
    void loop()
    {
      // If intPin goes high, all data registers have new data
      // On interrupt, check if data ready interrupt
      if (myIMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
      {
        myIMU.readAccelData(myIMU.accelCount);  // Read the x/y/z adc values
    
        // Now we'll calculate the accleration value into actual g's
        // This depends on scale being set
        myIMU.ax = (float)myIMU.accelCount[0] * myIMU.aRes; // - myIMU.accelBias[0];
        myIMU.ay = (float)myIMU.accelCount[1] * myIMU.aRes; // - myIMU.accelBias[1];
        myIMU.az = (float)myIMU.accelCount[2] * myIMU.aRes; // - myIMU.accelBias[2];
    
        myIMU.readGyroData(myIMU.gyroCount);  // Read the x/y/z adc values
    
        // Calculate the gyro value into actual degrees per second
        // This depends on scale being set
        myIMU.gx = (float)myIMU.gyroCount[0] * myIMU.gRes;
        myIMU.gy = (float)myIMU.gyroCount[1] * myIMU.gRes;
        myIMU.gz = (float)myIMU.gyroCount[2] * myIMU.gRes;
    
        myIMU.readMagData(myIMU.magCount);  // Read the x/y/z adc values
    
        // Calculate the magnetometer values in milliGauss
        // Include factory calibration per data sheet and user environmental
        // corrections
        // Get actual magnetometer value, this depends on scale being set
        myIMU.mx = (float)myIMU.magCount[0] * myIMU.mRes
                   * myIMU.factoryMagCalibration[0] - myIMU.magBias[0];
        myIMU.my = (float)myIMU.magCount[1] * myIMU.mRes
                   * myIMU.factoryMagCalibration[1] - myIMU.magBias[1];
        myIMU.mz = (float)myIMU.magCount[2] * myIMU.mRes
                   * myIMU.factoryMagCalibration[2] - myIMU.magBias[2];
      } // if (readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
    
      // Must be called before updating quaternions!
      myIMU.updateTime();
    
      // Sensors x (y)-axis of the accelerometer is aligned with the y (x)-axis of
      // the magnetometer; the magnetometer z-axis (+ down) is opposite to z-axis
      // (+ up) of accelerometer and gyro! We have to make some allowance for this
      // orientationmismatch in feeding the output to the quaternion filter. For the
      // MPU-9250, we have chosen a magnetic rotation that keeps the sensor forward
      // along the x-axis just like in the LSM9DS0 sensor. This rotation can be
      // modified to allow any convenient orientation convention. This is ok by
      // aircraft orientation standards! Pass gyro rate as rad/s
      MahonyQuaternionUpdate(myIMU.ax, myIMU.ay, myIMU.az, myIMU.gx * DEG_TO_RAD,
                             myIMU.gy * DEG_TO_RAD, myIMU.gz * DEG_TO_RAD, myIMU.my,
                             myIMU.mx, myIMU.mz, myIMU.deltat);
    
      if (!AHRS)
      {
        myIMU.delt_t = millis() - myIMU.count;
        if (myIMU.delt_t > 500)
        {
          if(SerialDebug)
          {
            // Print acceleration values in milligs!
            Serial.print("X-acceleration: "); Serial.print(1000 * myIMU.ax);
            Serial.print(" mg ");
            Serial.print("Y-acceleration: "); Serial.print(1000 * myIMU.ay);
            Serial.print(" mg ");
            Serial.print("Z-acceleration: "); Serial.print(1000 * myIMU.az);
            Serial.println(" mg ");
    
            // Print gyro values in degree/sec
            Serial.print("X-gyro rate: "); Serial.print(myIMU.gx, 3);
            Serial.print(" degrees/sec ");
            Serial.print("Y-gyro rate: "); Serial.print(myIMU.gy, 3);
            Serial.print(" degrees/sec ");
            Serial.print("Z-gyro rate: "); Serial.print(myIMU.gz, 3);
            Serial.println(" degrees/sec");
    
            // Print mag values in degree/sec
            Serial.print("X-mag field: "); Serial.print(myIMU.mx);
            Serial.print(" mG ");
            Serial.print("Y-mag field: "); Serial.print(myIMU.my);
            Serial.print(" mG ");
            Serial.print("Z-mag field: "); Serial.print(myIMU.mz);
            Serial.println(" mG");
    
    
            Serial.print("rate = ");
            Serial.print((float)myIMU.sumCount / myIMU.sum, 2);
            Serial.println(" Hz");
    
            myIMU.tempCount = myIMU.readTempData();  // Read the adc values
            // Temperature in degrees Centigrade
            myIMU.temperature = ((float) myIMU.tempCount) / 333.87 + 21.0;
            // Print temperature in degrees Centigrade
            Serial.print("Temperature is ");  Serial.print(myIMU.temperature, 1);
            Serial.println(" degrees C");
          }
    
          myIMU.count = millis();
          digitalWrite(myLed, !digitalRead(myLed));  // toggle led
        } // if (myIMU.delt_t > 500)
      } // if (!AHRS)
      else
      {
        // Serial print and/or display at 0.5 s rate independent of data rates
        myIMU.delt_t = millis() - myIMU.count;
    
        // update LCD once per half-second independent of read rate
        if (myIMU.delt_t > 500)
        {
         
            Serial.print("ax = ");  Serial.print((int)1000 * myIMU.ax);
            Serial.print(" ay = "); Serial.print((int)1000 * myIMU.ay);
            Serial.print(" az = "); Serial.print((int)1000 * myIMU.az);
            Serial.println(" mg");
    
            Serial.print("gx = ");  Serial.print(myIMU.gx, 2);
            Serial.print(" gy = "); Serial.print(myIMU.gy, 2);
            Serial.print(" gz = "); Serial.print(myIMU.gz, 2);
            Serial.println(" deg/s");
    
            Serial.print("mx = ");  Serial.print((int)myIMU.mx);
            Serial.print(" my = "); Serial.print((int)myIMU.my);
            Serial.print(" mz = "); Serial.print((int)myIMU.mz);
            Serial.println(" mG");
    
            Serial.print("q0 = ");  Serial.print(*getQ());
            Serial.print(" qx = "); Serial.print(*(getQ() + 1));
            Serial.print(" qy = "); Serial.print(*(getQ() + 2));
            Serial.print(" qz = "); Serial.println(*(getQ() + 3));
          
    
    // Define output variables from updated quaternion---these are Tait-Bryan
    // angles, commonly used in aircraft orientation. In this coordinate system,
    // the positive z-axis is down toward Earth. Yaw is the angle between Sensor
    // x-axis and Earth magnetic North (or true North if corrected for local
    // declination, looking down on the sensor positive yaw is counterclockwise.
    // Pitch is angle between sensor x-axis and Earth ground plane, toward the
    // Earth is positive, up toward the sky is negative. Roll is angle between
    // sensor y-axis and Earth ground plane, y-axis up is positive roll. These
    // arise from the definition of the homogeneous rotation matrix constructed
    // from quaternions. Tait-Bryan angles as well as Euler angles are
    // non-commutative; that is, the get the correct orientation the rotations
    // must be applied in the correct order which for this configuration is yaw,
    // pitch, and then roll.
    // For more see
    // http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
    // which has additional links.
          myIMU.yaw   = atan2(2.0f * (*(getQ()+1) * *(getQ()+2) + *getQ()
                        * *(getQ()+3)), *getQ() * *getQ() + *(getQ()+1)
                        * *(getQ()+1) - *(getQ()+2) * *(getQ()+2) - *(getQ()+3)
                        * *(getQ()+3));
          myIMU.pitch = -asin(2.0f * (*(getQ()+1) * *(getQ()+3) - *getQ()
                        * *(getQ()+2)));
          myIMU.roll  = atan2(2.0f * (*getQ() * *(getQ()+1) + *(getQ()+2)
                        * *(getQ()+3)), *getQ() * *getQ() - *(getQ()+1)
                        * *(getQ()+1) - *(getQ()+2) * *(getQ()+2) + *(getQ()+3)
                        * *(getQ()+3));
          myIMU.pitch *= RAD_TO_DEG;
          myIMU.yaw   *= RAD_TO_DEG;
    
          // Declination of SparkFun Electronics (4005'26.6"N 10511'05.9"W) is
          // 	8 30' E   0 21' (or 8.5) on 2016-07-19
          // - http://www.ngdc.noaa.gov/geomag-web/#declination
          myIMU.yaw  -= 8.5;
          myIMU.roll *= RAD_TO_DEG;
    
        
            Serial.print("Yaw, Pitch, Roll: ");
            Serial.print(myIMU.yaw, 2);
            Serial.print(", ");
            Serial.print(myIMU.pitch, 2);
            Serial.print(", ");
            Serial.println(myIMU.roll, 2);
    
            Serial.print("rate = ");
            Serial.print((float)myIMU.sumCount / myIMU.sum, 2);
            Serial.println(" Hz");
          
    
    
          myIMU.count = millis();
          myIMU.sumCount = 0;
          myIMU.sum = 0;
        } // if (myIMU.delt_t > 500)
      } // if (AHRS)
    }

  9. #9
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    447
    Just a heads-up that you may want to calibrate your sensors, especially the mag sensors. Otherwise your yaw will not be relative to North, and will drift pretty badly. Lots of info out there on MPU9250 calibration, including some good discussions and routines from Kris Winer (who's routine you're using).

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •