Using MPU6050 vertically with Y axis aligned with gravity

Nícolas
Posts: 2
Joined: Sun Apr 19, 2020 1:09 am

Using MPU6050 vertically with Y axis aligned with gravity

Postby Nícolas » Sun Apr 19, 2020 1:44 am

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 (https://github.com/jrowberg/i2cdevlib/t ... no/MPU6050), 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: https://www.i2cdevlib.com/forums/topic/ ... ientation/, 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!

This is my code (the same from Jeff, but I changed some things to work on ESP32):
  1. // I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0)
  2. // 6/21/2012 by Jeff Rowberg <jeff@rowberg.net>
  3. // Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
  4. //
  5. // Changelog:
  6. //      2019-07-08 - Added Auto Calibration and offset generator
  7. //       - and altered FIFO retrieval sequence to avoid using blocking code
  8. //      2016-04-18 - Eliminated a potential infinite loop
  9. //      2013-05-08 - added seamless Fastwire support
  10. //                 - added note about gyro calibration
  11. //      2012-06-21 - added note about Arduino 1.0.1 + Leonardo compatibility error
  12. //      2012-06-20 - improved FIFO overflow handling and simplified read process
  13. //      2012-06-19 - completely rearranged DMP initialization code and simplification
  14. //      2012-06-13 - pull gyro and accel data from FIFO packet instead of reading directly
  15. //      2012-06-09 - fix broken FIFO read sequence and change interrupt detection to RISING
  16. //      2012-06-05 - add gravity-compensated initial reference frame acceleration output
  17. //                 - add 3D math helper file to DMP6 example sketch
  18. //                 - add Euler output and Yaw/Pitch/Roll output formats
  19. //      2012-06-04 - remove accel offset clearing for better results (thanks Sungon Lee)
  20. //      2012-06-01 - fixed gyro sensitivity to be 2000 deg/sec instead of 250
  21. //      2012-05-30 - basic DMP initialization working
  22.  
  23. /* ============================================
  24. I2Cdev device library code is placed under the MIT license
  25. Copyright (c) 2012 Jeff Rowberg
  26.  
  27. Permission is hereby granted, free of charge, to any person obtaining a copy
  28. of this software and associated documentation files (the "Software"), to deal
  29. in the Software without restriction, including without limitation the rights
  30. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  31. copies of the Software, and to permit persons to whom the Software is
  32. furnished to do so, subject to the following conditions:
  33.  
  34. The above copyright notice and this permission notice shall be included in
  35. all copies or substantial portions of the Software.
  36.  
  37. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  38. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  39. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  40. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  41. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  42. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  43. THE SOFTWARE.
  44. ===============================================
  45. */
  46.  
  47. // I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
  48. // for both classes must be in the include path of your project
  49. #include "I2Cdev.h"
  50.  
  51. #include "MPU6050_6Axis_MotionApps20.h"
  52. //#include "MPU6050.h" // not necessary if using MotionApps include file
  53.  
  54. // Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
  55. // is used in I2Cdev.h
  56. #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  57.     #include "Wire.h"
  58. #endif
  59.  
  60. // class default I2C address is 0x68
  61. // specific I2C addresses may be passed as a parameter here
  62. // AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
  63. // AD0 high = 0x69
  64. MPU6050 mpu;
  65. //MPU6050 mpu(0x69); // <-- use for AD0 high
  66.  
  67. /* =========================================================================
  68.    NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch
  69.    depends on the MPU-6050's INT pin being connected to the Arduino's
  70.    external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is
  71.    digital I/O pin 2.
  72.  * ========================================================================= */
  73.  
  74. /* =========================================================================
  75.    NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error
  76.    when using Serial.write(buf, len). The Teapot output uses this method.
  77.    The solution requires a modification to the Arduino USBAPI.h file, which
  78.    is fortunately simple, but annoying. This will be fixed in the next IDE
  79.    release. For more info, see these links:
  80.  
  81.    http://arduino.cc/forum/index.php/topic,109987.0.html
  82.    http://code.google.com/p/arduino/issues/detail?id=958
  83.  * ========================================================================= */
  84.  
  85.  
  86.  
  87. // uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual
  88. // quaternion components in a [w, x, y, z] format (not best for parsing
  89. // on a remote host such as Processing or something though)
  90. //#define OUTPUT_READABLE_QUATERNION
  91.  
  92. // uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles
  93. // (in degrees) calculated from the quaternions coming from the FIFO.
  94. // Note that Euler angles suffer from gimbal lock (for more info, see
  95. // http://en.wikipedia.org/wiki/Gimbal_lock)
  96. //#define OUTPUT_READABLE_EULER
  97.  
  98. // uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/
  99. // pitch/roll angles (in degrees) calculated from the quaternions coming
  100. // from the FIFO. Note this also requires gravity vector calculations.
  101. // Also note that yaw/pitch/roll angles suffer from gimbal lock (for
  102. // more info, see: http://en.wikipedia.org/wiki/Gimbal_lock)
  103. //#define OUTPUT_READABLE_YAWPITCHROLL
  104.  
  105. // uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration
  106. // components with gravity removed. This acceleration reference frame is
  107. // not compensated for orientation, so +X is always +X according to the
  108. // sensor, just without the effects of gravity. If you want acceleration
  109. // compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead.
  110. //#define OUTPUT_READABLE_REALACCEL
  111.  
  112. // uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration
  113. // components with gravity removed and adjusted for the world frame of
  114. // reference (yaw is relative to initial orientation, since no magnetometer
  115. // is present in this case). Could be quite handy in some cases.
  116. //#define OUTPUT_READABLE_WORLDACCEL
  117.  
  118. // uncomment "OUTPUT_TEAPOT" if you want output that matches the
  119. // format used for the InvenSense teapot demo
  120. #define OUTPUT_TEAPOT
  121.  
  122.  
  123.  
  124. #define INTERRUPT_PIN 18  // use pin 2 on Arduino Uno & most boards
  125. #define LED_PIN 2 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
  126. bool blinkState = false;
  127.  
  128. // MPU control/status vars
  129. bool dmpReady = false;  // set true if DMP init was successful
  130. uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
  131. uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
  132. uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
  133. uint16_t fifoCount;     // count of all bytes currently in FIFO
  134. uint8_t fifoBuffer[64]; // FIFO storage buffer
  135.  
  136. // orientation/motion vars
  137. Quaternion q;           // [w, x, y, z]         quaternion container
  138. VectorInt16 aa;         // [x, y, z]            accel sensor measurements
  139. VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
  140. VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
  141. VectorFloat gravity;    // [x, y, z]            gravity vector
  142. float euler[3];         // [psi, theta, phi]    Euler angle container
  143. float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
  144.  
  145. // packet structure for InvenSense teapot demo
  146. uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
  147.  
  148.  
  149.  
  150. // ================================================================
  151. // ===               INTERRUPT DETECTION ROUTINE                ===
  152. // ================================================================
  153.  
  154. volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
  155. void dmpDataReady() {
  156.     mpuInterrupt = true;
  157. }
  158.  
  159.  
  160.  
  161. // ================================================================
  162. // ===                      INITIAL SETUP                       ===
  163. // ================================================================
  164.  
  165. void setup() {
  166.     // join I2C bus (I2Cdev library doesn't do this automatically)
  167.     //#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  168.         Wire.begin(21,22);
  169.         Wire.setClock(100000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  170.     //#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  171.       //  Fastwire::setup(400, true);
  172.     //#endif
  173.  
  174.     // initialize serial communication
  175.     // (115200 chosen because it is required for Teapot Demo output, but it's
  176.     // really up to you depending on your project)
  177.     Serial.begin(115200);
  178.     while (!Serial); // wait for Leonardo enumeration, others continue immediately
  179.  
  180.     // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3V or Arduino
  181.     // Pro Mini running at 3.3V, cannot handle this baud rate reliably due to
  182.     // the baud timing being too misaligned with processor ticks. You must use
  183.     // 38400 or slower in these cases, or use some kind of external separate
  184.     // crystal solution for the UART timer.
  185.  
  186.     // initialize device
  187.     Serial.println(F("Initializing I2C devices..."));
  188.     mpu.initialize();
  189.     pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  190.  
  191.     // verify connection
  192.     Serial.println(F("Testing device connections..."));
  193.     Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
  194.  
  195.     // wait for ready
  196.     Serial.println(F("\nSend any character to begin DMP programming and demo: "));
  197.     while (Serial.available() && Serial.read()); // empty buffer
  198.     while (!Serial.available());                 // wait for data
  199.     while (Serial.available() && Serial.read()); // empty buffer again
  200.  
  201.     // load and configure the DMP
  202.     Serial.println(F("Initializing DMP..."));
  203.     devStatus = mpu.dmpInitialize();
  204.  
  205.     // supply your own gyro offsets here, scaled for min sensitivity
  206.     mpu.setXGyroOffset(220);
  207.     mpu.setYGyroOffset(76);
  208.     mpu.setZGyroOffset(-85);
  209.     mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
  210.  
  211.     // make sure it worked (returns 0 if so)
  212.     if (devStatus == 0) {
  213.         // Calibration Time: generate offsets and calibrate our MPU6050
  214.         mpu.CalibrateAccel(6);
  215.         mpu.CalibrateGyro(6);
  216.         mpu.PrintActiveOffsets();
  217.         // turn on the DMP, now that it's ready
  218.         Serial.println(F("Enabling DMP..."));
  219.         mpu.setDMPEnabled(true);
  220.  
  221.         // enable Arduino interrupt detection
  222.         Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
  223.         Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
  224.         Serial.println(F(")..."));
  225.         attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
  226.         mpuIntStatus = mpu.getIntStatus();
  227.  
  228.         // set our DMP Ready flag so the main loop() function knows it's okay to use it
  229.         Serial.println(F("DMP ready! Waiting for first interrupt..."));
  230.         dmpReady = true;
  231.  
  232.         // get expected DMP packet size for later comparison
  233.         packetSize = mpu.dmpGetFIFOPacketSize();
  234.     } else {
  235.         // ERROR!
  236.         // 1 = initial memory load failed
  237.         // 2 = DMP configuration updates failed
  238.         // (if it's going to break, usually the code will be 1)
  239.         Serial.print(F("DMP Initialization failed (code "));
  240.         Serial.print(devStatus);
  241.         Serial.println(F(")"));
  242.     }
  243.  
  244.     // configure LED for output
  245.     pinMode(LED_PIN, OUTPUT);
  246. }
  247.  
  248.  
  249.  
  250. // ================================================================
  251. // ===                    MAIN PROGRAM LOOP                     ===
  252. // ================================================================
  253.  
  254. void loop() {
  255.     // if programming failed, don't try to do anything
  256.     if (!dmpReady) return;
  257.  
  258.     // wait for MPU interrupt or extra packet(s) available
  259.     while (!mpuInterrupt && fifoCount < packetSize) {
  260.         if (mpuInterrupt && fifoCount < packetSize) {
  261.           // try to get out of the infinite loop
  262.           fifoCount = mpu.getFIFOCount();
  263.         }  
  264.         // other program behavior stuff here
  265.         // .
  266.         // .
  267.         // .
  268.         // if you are really paranoid you can frequently test in between other
  269.         // stuff to see if mpuInterrupt is true, and if so, "break;" from the
  270.         // while() loop to immediately process the MPU data
  271.         // .
  272.         // .
  273.         // .
  274.     }
  275.  
  276.     // reset interrupt flag and get INT_STATUS byte
  277.     mpuInterrupt = false;
  278.     mpuIntStatus = mpu.getIntStatus();
  279.  
  280.     // get current FIFO count
  281.     fifoCount = mpu.getFIFOCount();
  282.   if(fifoCount < packetSize){
  283.           //Lets go back and wait for another interrupt. We shouldn't be here, we got an interrupt from another event
  284.       // This is blocking so don't do it   while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
  285.   }
  286.     // check for overflow (this should never happen unless our code is too inefficient)
  287.     else if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) {
  288.         // reset so we can continue cleanly
  289.         mpu.resetFIFO();
  290.       //  fifoCount = mpu.getFIFOCount();  // will be zero after reset no need to ask
  291.         Serial.println(F("FIFO overflow!"));
  292.  
  293.     // otherwise, check for DMP data ready interrupt (this should happen frequently)
  294.     } else if (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT)) {
  295.  
  296.         // read a packet from FIFO
  297.   while(fifoCount >= packetSize){ // Lets catch up to NOW, someone is using the dreaded delay()!
  298.     mpu.getFIFOBytes(fifoBuffer, packetSize);
  299.     // track FIFO count here in case there is > 1 packet available
  300.     // (this lets us immediately read more without waiting for an interrupt)
  301.     fifoCount -= packetSize;
  302.   }
  303.         #ifdef OUTPUT_READABLE_QUATERNION
  304.             // display quaternion values in easy matrix form: w x y z
  305.             mpu.dmpGetQuaternion(&q, fifoBuffer);
  306.             Serial.print("quat\t");
  307.             Serial.print(q.w);
  308.             Serial.print("\t");
  309.             Serial.print(q.x);
  310.             Serial.print("\t");
  311.             Serial.print(q.y);
  312.             Serial.print("\t");
  313.             Serial.println(q.z);
  314.         #endif
  315.  
  316.         #ifdef OUTPUT_READABLE_EULER
  317.             // display Euler angles in degrees
  318.             mpu.dmpGetQuaternion(&q, fifoBuffer);
  319.             mpu.dmpGetEuler(euler, &q);
  320.             Serial.print("euler\t");
  321.             Serial.print(euler[0] * 180/M_PI);
  322.             Serial.print("\t");
  323.             Serial.print(euler[1] * 180/M_PI);
  324.             Serial.print("\t");
  325.             Serial.println(euler[2] * 180/M_PI);
  326.         #endif
  327.  
  328.         #ifdef OUTPUT_READABLE_YAWPITCHROLL
  329.             // display Euler angles in degrees
  330.             mpu.dmpGetQuaternion(&q, fifoBuffer);
  331.             mpu.dmpGetGravity(&gravity, &q);
  332.             mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
  333.             Serial.print("ypr\t");
  334.             Serial.print(ypr[0] * 180/M_PI);
  335.             Serial.print("\t");
  336.             Serial.print(ypr[1] * 180/M_PI);
  337.             Serial.print("\t");
  338.             Serial.println(ypr[2] * 180/M_PI);
  339.         #endif
  340.  
  341.         #ifdef OUTPUT_READABLE_REALACCEL
  342.             // display real acceleration, adjusted to remove gravity
  343.             mpu.dmpGetQuaternion(&q, fifoBuffer);
  344.             mpu.dmpGetAccel(&aa, fifoBuffer);
  345.             mpu.dmpGetGravity(&gravity, &q);
  346.             mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
  347.             Serial.print("areal\t");
  348.             Serial.print(aaReal.x);
  349.             Serial.print("\t");
  350.             Serial.print(aaReal.y);
  351.             Serial.print("\t");
  352.             Serial.println(aaReal.z);
  353.         #endif
  354.  
  355.         #ifdef OUTPUT_READABLE_WORLDACCEL
  356.             // display initial world-frame acceleration, adjusted to remove gravity
  357.             // and rotated based on known orientation from quaternion
  358.             mpu.dmpGetQuaternion(&q, fifoBuffer);
  359.             mpu.dmpGetAccel(&aa, fifoBuffer);
  360.             mpu.dmpGetGravity(&gravity, &q);
  361.             mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
  362.             mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
  363.             Serial.print("aworld\t");
  364.             Serial.print(aaWorld.x);
  365.             Serial.print("\t");
  366.             Serial.print(aaWorld.y);
  367.             Serial.print("\t");
  368.             Serial.println(aaWorld.z);
  369.         #endif
  370.    
  371.         #ifdef OUTPUT_TEAPOT
  372.             // display quaternion values in InvenSense Teapot demo format:
  373.             teapotPacket[2] = fifoBuffer[0];
  374.             teapotPacket[3] = fifoBuffer[1];
  375.             teapotPacket[4] = fifoBuffer[4];
  376.             teapotPacket[5] = fifoBuffer[5];
  377.             teapotPacket[6] = fifoBuffer[8];
  378.             teapotPacket[7] = fifoBuffer[9];
  379.             teapotPacket[8] = fifoBuffer[12];
  380.             teapotPacket[9] = fifoBuffer[13];
  381.             Serial.write(teapotPacket, 14);
  382.             teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
  383.         #endif
  384.  
  385.         // blink LED to indicate activity
  386.         blinkState = !blinkState;
  387.         digitalWrite(LED_PIN, blinkState);
  388.     }
  389. }

getrasa
Posts: 1
Joined: Thu May 14, 2020 10:52 pm

Re: Using MPU6050 vertically with Y axis aligned with gravity

Postby getrasa » Thu May 14, 2020 10:54 pm

I'm having the same problem. Did u manage to find a solution?

Nícolas
Posts: 2
Joined: Sun Apr 19, 2020 1:09 am

Re: Using MPU6050 vertically with Y axis aligned with gravity

Postby Nícolas » Fri May 22, 2020 7:21 pm

getrasa wrote:
Thu May 14, 2020 10:54 pm
I'm having the same problem. Did u manage to find a solution?
Hi getrasa. I also asked this question on the Arduino forum: https://forum.arduino.cc/index.php?topic=678703.0

In short, you need an IMU with a magnetometer, like the MPU9250, to get correct measurements.

intensite
Posts: 1
Joined: Sun Sep 13, 2020 11:40 pm

Re: Using MPU6050 vertically with Y axis aligned with gravity

Postby intensite » Mon Sep 14, 2020 1:46 am

I am experiencing the same issue while using using Jeff Rowberg's code on a ESP32.

I designed, and had manufactured, a few circuit boards for flight control of hobby model rockets. Unfortunately the bord and its MPU-6050, is mounted vertically inside the rocket tube.

When I initialise / calibrate the device laying flat and then bring it vertical, all the angles are calculated correctly based on the initial orientation. However, if initialized/calibrated vertically (Y axis perpendicular to earth's gravity) when the rocket is in its launchpad, the rotation is not calculated correctly and the values do not make any sense.

I am confident that the problem is related to the DMP code that is injected in the IMU during the initialisation phase. Something about axis transformation matrix. Basically I think the default axis orientation is hard-coded in the DMP code portion of Jeff Rowberg's library. The Z axis perpendicular to earth's gravity.

I am still investigating a workaround. I have noticed that there is a library for the IMU-9250 from Sparkfun that allows changing the axis transformation matrix. Maybe I could extract this portion .

Please let me know if you have a solution to the calibration orientation.

marian
Posts: 1
Joined: Fri Feb 12, 2021 8:43 am

Re: Using MPU6050 vertically with Y axis aligned with gravity

Postby marian » Fri Feb 12, 2021 8:46 am

I'd a similar problem and solved it by commenting out the following lines from Jeff Rowberg code:

[Codebox]
// mpu.CalibrateAccel(6);
// mpu.CalibrateGyro(6);
// mpu.PrintActiveOffsets();
[/Codebox]

Who is online

Users browsing this forum: No registered users and 59 guests