Self-balancing Part 2

In this part of the tutorial I will cover how to get the most performance out of the MPU-6050 Accelerometer and Gyroscope module, using the Motion Apps library. This library is really amazing as the author reverse engineered the MPU-6050 so that most of the processing is done through the DMP on the module itself, instead of by the micro-controller! I have found that reading the sensor through the Motion Apps Library is faster than doing the calculations manually, and the readings tend to be significantly more accurate.


Analysing the Output

Here is the code that I used to get the yaw, pitch and roll sensor data. It is based on the “Teapot” demonstration program which comes with the MPU-6050 Motion Apps library. Once again, you need to have the “MPU6050” and the “I2Cdev” libraries installed to use this code.

// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class 
// using DMP (MotionApps v2.0)
// 6/21/2012 by Jeff Rowberg <jeff@rowberg.net>
/* ============================================
I2Cdev device library code is placed under the MIT license
Copyright (c) 2012 Jeff Rowberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================
*/
// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
//MPU6050 mpu(0x69); // <-- use for AD0 high
/* =========================================================================
NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch
depends on the MPU-6050's INT pin being connected to the Arduino's
external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is
digital I/O pin 2.
For the Galileo Gen1/2 Boards, there is no INT pin support. Therefore
the INT pin does not need to be connected, but you should work on getting
the timing of the program right, so that there is no buffer overflow.
* ========================================================================= */
/* =========================================================================
NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error
when using Serial.write(buf, len). The Teapot output uses this method.
The solution requires a modification to the Arduino USBAPI.h file, which
is fortunately simple, but annoying. This will be fixed in the next IDE
release. For more info, see these links:
http://arduino.cc/forum/index.php/topic,109987.0.html
http://code.google.com/p/arduino/issues/detail?id=958
* ========================================================================= */
#define OUTPUT_READABLE_YAWPITCHROLL
// Unccomment if you are using an Arduino-Style Board
// #define ARDUINO_BOARD
// Uncomment if you are using a Galileo Gen1 / 2 Board
#define GALILEO_BOARD
#define LED_PIN 13      // (Galileo/Arduino is 13)
bool blinkState = false;
// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// orientation/motion vars
VectorFloat gravity;    // [x, y, z]            gravity vector
Quaternion q;           // [w, x, y, z]         quaternion container
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
// ================================================================
// ===               INTERRUPT DETECTION ROUTINE                ===
// ================================================================
// This function is not required when using the Galileo 
volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================
void setup() {
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
int TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
while (!Serial);
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(F("MPU6050 connection "));
Serial.print(mpu.testConnection() ? F("successful") : F("failed"));
// wait for ready
Serial.println(F("\nSend any character to begin DMP programming and demo: "));
while (Serial.available() && Serial.read()); // empty buffer
while (!Serial.available());                 // wait for data
while (Serial.available() && Serial.read()); // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// configure LED for output
pinMode(LED_PIN, OUTPUT);
}
// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================
void loop() {
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
#ifdef ARDUINO_BOARD
while (!mpuInterrupt && fifoCount < packetSize) {
}
#endif
#ifdef GALILEO_BOARD
delay(10);
#endif
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
#ifdef OUTPUT_READABLE_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
#endif
// blink LED to indicate activity
blinkState = !blinkState;
digitalWrite(LED_PIN, blinkState);
}
}

From a number of trials, I noticed that the accelerometer-gyroscope module and the Motion Apps library seem to have some type of auto-calibration feature, which requires a couple of seconds to complete. Here is a graph of the output I got directly after initiating the sensor:

Output from the MPU-6050, using Motion Apps
Output from the MPU-6050, using Motion Apps

All of the values float a large amount at the start, especially the yaw data. This then stops after around 13 seconds, probably due to the completion of some auto-calibration process. I repeated this test a number of times and it seems that the sensor can take up to 40 seconds to complete its calibration, on some occasions. Therefore we should take this delay into account in our program. The robot should wait for around 40 seconds, before beginning to use the sensor and starting the main program.

Calibrating the Sensor

Just like any sensor, the MPU-6050 needs to be calibrated before it is used for the first time. What we want to do is remove the zero-error; this is where the sensor is totally level, but it thinks that it is angled slightly. Therefore we need to adjust the offsets in order to counteract this error. Fortunately I found a program that can calibrate the MPU-6050 for us! In order to use the program, all you have to do is to upload the sketch and then place the accel-gyro module in a flat and level position. The program will make an average of a few hundred readings and display the offsets required to remove zero error.

// Arduino sketch that returns calibration offsets for MPU6050 
//   Version 1.1  (31th January 2014)
// Done by Luis RΓ³denas <luisrodenaslorda@gmail.com>
// Based on the I2Cdev library and previous work by Jeff Rowberg <jeff@rowberg.net>
// Updates (of the library) should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
// These offsets were meant to calibrate MPU6050's internal DMP, but can be also useful for reading sensors. 
// The effect of temperature has not been taken into account so I can't promise that it will work if you 
// calibrate indoors and then use it outdoors. Best is to calibrate and use at the same room temperature.
/* ==========  LICENSE  ==================================
I2Cdev device library code is placed under the MIT license
Copyright (c) 2011 Jeff Rowberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=========================================================
*/
// I2Cdev and MPU6050 must be installed as libraries
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
///////////////////////////////////   CONFIGURATION   /////////////////////////////
//Change this 3 variables if you want to fine tune the skecth to your needs.
int buffersize=1000;     //Amount of readings used to average, make it higher to get more precision but sketch will be slower  (default:1000)
int acel_deadzone=8;     //Acelerometer error allowed, make it lower to get more precision, but sketch may not converge  (default:8)
int giro_deadzone=1;     //Giro error allowed, make it lower to get more precision, but sketch may not converge  (default:1)
// default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
//MPU6050 accelgyro;
MPU6050 accelgyro(0x68); // <-- use for AD0 high
int16_t ax, ay, az,gx, gy, gz;
int mean_ax,mean_ay,mean_az,mean_gx,mean_gy,mean_gz,state=0;
int ax_offset,ay_offset,az_offset,gx_offset,gy_offset,gz_offset;
///////////////////////////////////   SETUP   ////////////////////////////////////
void setup() {
// join I2C bus (I2Cdev library doesn't do this automatically)
Wire.begin();
// COMMENT NEXT LINE IF YOU ARE USING ARDUINO DUE
TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz). Leonardo measured 250kHz.
// initialize serial communication
Serial.begin(115200);
// initialize device
accelgyro.initialize();
// wait for ready
while (Serial.available() && Serial.read()); // empty buffer
while (!Serial.available()){
Serial.println(F("Send any character to start sketch.\n"));
delay(1500);
}                
while (Serial.available() && Serial.read()); // empty buffer again
// start message
Serial.println("\nMPU6050 Calibration Sketch");
delay(2000);
Serial.println("\nYour MPU6050 should be placed in horizontal position, with package letters facing up. \nDon't touch it until you see a finish message.\n");
delay(3000);
// verify connection
Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
delay(1000);
// reset offsets
accelgyro.setXAccelOffset(0);
accelgyro.setYAccelOffset(0);
accelgyro.setZAccelOffset(0);
accelgyro.setXGyroOffset(0);
accelgyro.setYGyroOffset(0);
accelgyro.setZGyroOffset(0);
}
///////////////////////////////////   LOOP   ////////////////////////////////////
void loop() {
if (state==0){
Serial.println("\nReading sensors for first time...");
meansensors();
state++;
delay(1000);
}
if (state==1) {
Serial.println("\nCalculating offsets...");
calibration();
state++;
delay(1000);
}
if (state==2) {
meansensors();
Serial.println("\nFINISHED!");
Serial.print("\nSensor readings with offsets:\t");
Serial.print(mean_ax); 
Serial.print("\t");
Serial.print(mean_ay); 
Serial.print("\t");
Serial.print(mean_az); 
Serial.print("\t");
Serial.print(mean_gx); 
Serial.print("\t");
Serial.print(mean_gy); 
Serial.print("\t");
Serial.println(mean_gz);
Serial.print("Your offsets:\t");
Serial.print(ax_offset); 
Serial.print("\t");
Serial.print(ay_offset); 
Serial.print("\t");
Serial.print(az_offset); 
Serial.print("\t");
Serial.print(gx_offset); 
Serial.print("\t");
Serial.print(gy_offset); 
Serial.print("\t");
Serial.println(gz_offset); 
Serial.println("\nData is printed as: acelX acelY acelZ giroX giroY giroZ");
Serial.println("Check that your sensor readings are close to 0 0 16384 0 0 0");
Serial.println("If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)");
while (1);
}
}
///////////////////////////////////   FUNCTIONS   ////////////////////////////////////
void meansensors(){
long i=0,buff_ax=0,buff_ay=0,buff_az=0,buff_gx=0,buff_gy=0,buff_gz=0;
while (i<(buffersize+101)){
// read raw accel/gyro measurements from device
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
if (i>100 && i<=(buffersize+100)){ //First 100 measures are discarded
buff_ax=buff_ax+ax;
buff_ay=buff_ay+ay;
buff_az=buff_az+az;
buff_gx=buff_gx+gx;
buff_gy=buff_gy+gy;
buff_gz=buff_gz+gz;
}
if (i==(buffersize+100)){
mean_ax=buff_ax/buffersize;
mean_ay=buff_ay/buffersize;
mean_az=buff_az/buffersize;
mean_gx=buff_gx/buffersize;
mean_gy=buff_gy/buffersize;
mean_gz=buff_gz/buffersize;
}
i++;
delay(2); //Needed so we don't get repeated measures
}
}
void calibration(){
ax_offset=-mean_ax/8;
ay_offset=-mean_ay/8;
az_offset=(16384-mean_az)/8;
gx_offset=-mean_gx/4;
gy_offset=-mean_gy/4;
gz_offset=-mean_gz/4;
while (1){
int ready=0;
accelgyro.setXAccelOffset(ax_offset);
accelgyro.setYAccelOffset(ay_offset);
accelgyro.setZAccelOffset(az_offset);
accelgyro.setXGyroOffset(gx_offset);
accelgyro.setYGyroOffset(gy_offset);
accelgyro.setZGyroOffset(gz_offset);
meansensors();
Serial.println("...");
if (abs(mean_ax)<=acel_deadzone) ready++;
else ax_offset=ax_offset-mean_ax/acel_deadzone;
if (abs(mean_ay)<=acel_deadzone) ready++;
else ay_offset=ay_offset-mean_ay/acel_deadzone;
if (abs(16384-mean_az)<=acel_deadzone) ready++;
else az_offset=az_offset+(16384-mean_az)/acel_deadzone;
if (abs(mean_gx)<=giro_deadzone) ready++;
else gx_offset=gx_offset-mean_gx/(giro_deadzone+1);
if (abs(mean_gy)<=giro_deadzone) ready++;
else gy_offset=gy_offset-mean_gy/(giro_deadzone+1);
if (abs(mean_gz)<=giro_deadzone) ready++;
else gz_offset=gz_offset-mean_gz/(giro_deadzone+1);
if (ready==6) break;
}
}

The sketch outputs the required accelerometer and gyroscope offsets through the serial monitor. All you have to do is plug them into the initialisation code at the start of your program, and you are good to go! Please note that the required offsets vary significantly from sensor to sensor, so you have to repeat the calibration program above for each MPU-6050 sensor you are using.


That concludes the basic calibration of the MPU-6050; the sensor should now be more than accurate enough for most applications, such as self-balancing robots and quad-copters.

Part 4: The PID Controller

Updated: 23rd May 2019 – Reformatted post

This Post Has 47 Comments

  1. Hi,
    great tuto. Unfortunatly I’ve got the same error as Amit. I am also using NodeMCU esp8266 12E and GY 521 in arduino IDE end get an error message. The sensor work when I try to get RAW value readings using the scripts provided on the Arduino website. It seemed to me that the problem is a matter of the interrupt pin. I don’t know which pin to use at the NodeMCU for interrupt.

    Message:
    Soft WDT reset
    >>>stack>>>
    ctx: cont
    sp: 3ffffde0 end: 3fffffc0 offset: 01b0
    3fffff90: feefeffe feefeffe feefeffe 3ffeef00
    3fffffa0: 3fffdad0 00000000 3ffeeed0 40204d7c
    3fffffb0: feefeffe feefeffe 3ffe8520 4010150d
    <<<stack<<<
    ets Jan 8 2013,rst cause:2, boot mode:(3,6)
    load 0x4010f000, len 1384, room 16
    tail 8
    chksum 0x2d
    csum 0x2d
    v951aeffa
    ~ld

    1. I know this a a late reply, but may be helpful to other people. The program does not use interrupts, so that is not the issue. I was reading through https://github.com/esp8266/Arduino/issues/2414 where many similar instability issues were reported. Solutions which have worked for people in the thread include:
      > Ensure that the power supply delivers 3.3v at a current of at least 300mA
      > Try changing the flash mode in the IDE to DOUT, which is the slowest but most compatible mode
      > Clearing the flash memory of the esp (Tools > Erase Flash > All contents)
      > Check whether you have an authentic esp8266 board (the official NodeMCU should be fine)

  2. Hi, Simon! My serial monitor displays
    Calculating Offsets


    … for more than a minute. My connections were correct, my MPU6050 is placed horizontally and I’m not touching it. Can you help me solve this problem? Thank you.

    1. Have you tested the sensor with the test program which simply outputs the raw sensor data [link]? The values should remain relatively constant when the sensor isn’t moving.

      If everything else seems right but it still isn’t converging, then increase the giro- and accel- deadzone values on lines 43 and 44. This will decrease the accuracy, but should speed up the calibration.

    2. Hi I am completely new in this field. In my project I have to calibrate the reading of the accelerometer and I am using NodeMCU esp8266 12E and GY 521 in arduino IDE but when I am running this code then I am getting error message. Can u suggest me what kind of necessary change are required in order to get offset values to get more accurate reading?

    3. What is the error you are getting? Does the sensor work when you try to get RAW value readings using the scripts provided on the Arduino website?

  3. Hi! The website that has the calibration for MPU5060 is not opening; can you post/send me the zip file please? I have read multiple sources that mentions this website, but nobody has the actual file posted πŸ™

    1. Yeah it looks as if that website is down at the moment; I did write this post 4 years ago, so these things happen. The code is the same as the one which I included in the post. You can just hover over it and press the ‘copy’ button in the top right to use it.

  4. Hi Simom!

    I am working on a project related to gait measurement,
    is there an way to automatically adjust the calibration point every time the external power is applied?

    1. The MPU6050 does not have any integrated feature to support this, but you could program the microcontroller to do this for you. The calibration of the sensor does not change between power cycles, so there may not be a need to repeat this process each time…

      Simon

  5. What coding language are you using in this tutorial?
    By the way, this tutorial was super helpful. Thank you!

    1. I am using C++, which is the standard programming language for the Arduino micro-controller.

  6. Hi, was looking exactly for this. Thanks a bunch.
    Facing problems with the calibration though.
    All I’ve been getting is three dots(…) and I’ve been waiting for like 10 minutes straight.
    Could this be due to the fact that I’m using a Nano board instead of a UNO ?
    P.S. i changed the address from 0x68 to 0x6B since the latter seems to be the address for my Nano.
    Thanks !

    1. The i2c address should be the one of the sensor; but I guess as long as it says “Connection Successful”, the communication is working correctly. It shouldn’t matter which type of micro-controller you are using.

      If the “…” keeps going for a long time, it means that the sensor values are not converging. This can occur if the sensor is not orientated totally level (x and y axis flat, z axis pointing up), or if the sensor keeps moving during calibration.

  7. Thank you so much for this – I was pulling my hair out trying to get some sense out of my 6050 DMP -Roll/Pitch/Yaw – and was going to throw the chip away but I tried your calibration routine and now it is really accurate.

  8. Hi,

    I have success with running the code however, all it prints is “send any character to start sketch”. I’m not sure what this means, nor how to do it haha. Any help is appreciated, thanks. (Trying to calibrate for my self-balancing robot using Arduino Uno)

    1. Type any letter or character in the comment bar at the top of the serial monitor and press enter, the sketch will then start.

  9. Is the calibration persistent across power cycles of the 6050? In other words, do the set___offset commands write to flash memory on the 6050 or does the calibration need to be performed each time it is power cycled?

    My guess is that it’s persistent, but wanted to double check.

    1. [Updated Comment] I’ve just had a look through the i2cdevlib code, and the offsets are sent directly to the MPU6050 where they presumably are saved. To test whether the offsets are persistent, you could set the offset using: void MPU6050::setXAccelOffset(int16_t offset) and then check to see what the offset is after you’ve cycled the power: int16_t MPU6050::getXAccelOffset()

  10. Hello, i’m working with this IMU and checking out your calibration code, I didn’t understand why you divid by 8 and by 4…

    ax_offset = -mean_ax / 8;
    ay_offset = -mean_ay / 8;
    az_offset = (16384 – mean_az) / 8;

    gx_offset = -mean_gx / 4;
    gy_offset = -mean_gy / 4;
    gz_offset = -mean_gz / 4;

    Could you please explain that??

    Thank you very much and than you for this piece of art! πŸ™‚

  11. Hello while i am connecting my gyro mpu 6050 with arduino and then calibrate error is displayed during callibration is gyro is failed but that time green light is there in my gyro can anyone plss suggest what is the problem

    1. From your very vague description, a lot of things could be causing the issue. How did you connect the pins? What code are you using? What exactly was displayed on the serial monitor when you tried to run the code. Have you tested the sensor with the sample code in the MPU6050 library?

    2. The connection which I have made is right and code is also right because first when I have used with made my drone it is working good but the problem in my drone is motor stops running after 1st spin so I think to remove bread board connection and to make a new soldiered connection but after soldiering when I have done whole connection I used to callibrate while callibrateing any error is displayed gyro mpu 6050 is failed can it be the reason than my gyro get bursted while soldiering but I have soldiered very carefully

    3. It sounds as if you got a ‘brownout’ when the motors turned on. This means that the motors used up most of the power supplied by the battery, causing the Arduino to reset. You can look this up online to see the best way to fix it.

      You may also have a second problem if the serial monitor says: “MPU6050 connection failed”. Usually this happens if the connections are wrong, or if the sensor is broken. Often the sensor board works at 3.3v, so a common way that people fry it is by supplying 5v by accident. You should check the schematics for your sensor to see what voltage it expects, and if there is an integrated voltage converter.

  12. I have been trying to run this script to calibrate my sensor. I see it has been stuck on it for approx 20 mins. It just says calculating offsets and then 3X …

    Am I doing something wrong here or is this the expected behavior ?

    1. The code shouldn’t take more than a minute to calculate the offsets. There must be something going wrong. Make sure that the sensor is held flat, and is kept completely still while calibrating. Are you able to read the raw accelerometer and gyroscope values from the sensor using the example sketch in the MPU6050 library? What board are you using?

      If you still have issues, you can post a question to the person who wrote the calibration sketch here .

  13. Hello, is there some way that it shows the angle of degrees on Processing for the teapot demo? When I use the calibration code, I want it to start at zero and I want that value to show on Processing. What would be the code to obtain such degree on Processing ? thank you

  14. I don’t know how to use the numbers from Serial. Ex, from this code I got 49.85 1.85 0.06. How can I put them to code?

    1. Hi, from the calibration sketch you should get six numbers. You can then plug these numbers into the offsets in the “setup” method of your code.

      For example:

      void setup() {
      mpu.initialize();
      // Set up offsets
      mpu.setXGyroOffset(93);
      mpu.setYGyroOffset(-15);
      mpu.setZGyroOffset(30);
      mpu.setXAccelOffset(-2500);
      mpu.setYAccelOffset(1783);
      mpu.setZAccelOffset(877);
      // Rest of setup code goes here
      }
  15. Hey Simon, i used your code to calculate the offset values but i don’t know why these values are fluctuating and as much as i know offset values are quite stable values!!
    Do u have any suggestions why this is happening?

    Thanks in advance

    1. You’re right, the offsets should be stable as long as you keep the IMU in the same orientation while completing the calibration.

      Try running the calibration twice in a row without touching the sensor. How big is the difference between the two sets of values?

    2. The fluctuation seems to be of +/-20 …
      The sensor has been untouched in the whole calibration process so i don’t think this is because of that.
      How should i smoothen out this fluctuation to get a stable offset value and also will these value(if calculated correctly) would give me 2 values close to zero when substracted from raw adc values (for x and y axis) and for z axis closer to 16380(for 2g range)??

  16. Hi Simon,
    I am also a Irish Electronic Engineering Student (GMIT represent!), using a Galileo for a quadcopter for my 3rd year project! I noticed in your code you dont use the interrupt method of reading the MPU6050 because it is incompatible with the Galileo! Do you have a better understanding of why this is? I’m porting my code from a working arduino quadcopter and I’ve encountered this interrupt problem and I can’t get my head around it! I’d like to find a way of making it work because the loop way my cause timing issues with the more code I add! Any info would be much apprieciated!
    Thanks,
    Liam

    1. Hi Liam! Interrupts were not yet implemented on the Galileo when I was doing my project, but I think that they are now supported on pins 2 and 3. The Galileo supports edge triggers, so RISING, FALLING and CHANGE should work.

      By the way, I’d be interested to hear how you get on with your quadcopter! That is a project I’ve been thinking of building for a while.

    2. Thanks for the reply!
      I’ve tried implementing them but seems like the interrupt pulse is too quick for the Galileo! Why that is I have no idea! I’m just reading the MPU in loop for now, trying to keep the FIFO from overflowing! The Galileo is proving to be incredibly NOT-compatible with Arduino! Also, did you have any issues with the I2C bus on the Galileo? Seems to crash randomly when the I2C bus is busy! Can’t really find a conclusive answer anywhere on the interweb!

    3. Yeah, I agree that the Galileo isn’t great if you just want to use the Arduino features. The porting isn’t great and it no longer is properly supported, as Intel is putting more emphasis on the Arduino 101.

      I had similar problems with the I2C connection; the Galileo seems to crash if it receives unexpected garbage data. The only way to really prevent that is to use a short cable and shield it with some grounded foil… :L

      I’ve just tested the MPU-6050 with interrupts on my Galileo, and it does work. I can send you the source code if you want.

  17. I didn’t know about the extra long self-calibration that the Motion App does. Do you know how to make it short or stop it? Or perhaps load previous calibrations?

    Have you tried saving the offset registers after the calibrations, and then applying them before starting Motion App? I am hoping doing this would speed up the calibration.

    1. I haven’t yet tried any of the solutions that you mentioned. The time taken for calibration in my post above is a worst case scenario; I found that usually it takes less than 15 seconds. For the purposes of my project, trying to mess with the Motion Apps library would probably have taken a lot longer than the amount of time I had to wait while the calibration was occurring!

      If you do find a solution, I would still be interested to hear it!
      Simon

  18. Why mean_ax,mean_ay,mean_az is divided by 8 ???
    Why mean_gx,mean_gy,mean_gz is divided by 4 ???

    Thanks!!

    1. Hi! I didn’t write this auto-calibration program myself, but I assume it is related to the standard scaling factor of the accel and gyro being used. There are actually a number of people asking this same question on the original thread, but I don’t see any definite answer.

      I guess the best way to find out what the division does is to change the number and see how the program runs! I’ll have a closer look at it later this week.

    2. Thanks!! πŸ™‚

  19. Hi Simon,
    Let me start off by saying You Did An Amazing Job!!!!!….. with this build. I have been researching this type of design for some number of months now(since beginning of last year) and the way you tackled and designed your project from the ground up has provided a priceless level of education for me. My hat go’s off to you….in many words, Thank You!!!!!
    If you have the means I would like contribute a donation for all you superior efforts.

    Regards
    Dave Pina

    1. Thanks for the kind words! It’s good to know that people do benefit from my posts! Don’t worry about donations, I don’t run this blog to make any money.

      Regards,
      Simon B.

  20. Is 49N*cm enough motor torque for this project?
    Thanks,
    D

    1. I’m sorry for the delayed reply; for some reason your comment landed in the spam folder! I think that 49N*cm should work, but it might be better to have a slightly higher torque (eg. between 55-60N*cm).

    1. Probably the 12v, 280rpm motor would be alright for a self-balancing robot.

      Simon B.

Leave a Reply

1 × four =