Incorrect Accelerometer Sensitivity with MPU-6050

Accelerometer reading at full scale range at +/- 2g

setFullScaleAccelRange(MPU6050_ACCEL_FS_0);

Scale Sensitivity Factor of +/- 2g is 16384 LSB/g

X/Y accel axes should read 0
Z accel axis should read 1g, which is +16384 at a sensitivity of 2g

but i got only a half of g 8192

From Datasheet https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf p.29

AFS_SEL | Full Scale Range | LSB Sensitivity
--------±-----------------±---------------
0 | +/- 2g | 16384 LSB/g
1 | +/- 4g | 8192 LSB/g
2 | +/- 8g | 4096 LSB/g
3 | +/- 16g | 2048 LSB/g

Is somethings wrong?

Thanks in advance

2g.png

Is somethings wrong?

Yes, you did not post the (possibly incorrect) code, using code tags.

Source code:

#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

MPU6050 accelgyro;

int16_t ax, ay, az;
int16_t gx, gy, gz;                  


void setup() {

    Wire.begin();

    Serial.begin(38400);
    
    // initialize device
    Serial.println("Initializing I2C devices...");

    accelgyro.initialize();
    
    accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);

    Serial.println(accelgyro.getFullScaleAccelRange());

    // verify connection
    Serial.println("Testing device connections...");

    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
    
}

void loop() {
    

    accelgyro.getAcceleration(&ax, &ay, &az);

    Serial.print(ax); Serial.print("\t");  Serial.print(ay); Serial.print("\t");  Serial.print(az); Serial.print("\t");
    Serial.print("\n");

}

Output is:

Initializing I2C devices…
0
Testing device connections…
MPU6050 connection successful
-216 856 6976
-200 832 6904
-60 748 7152
-164 812 7144
-160 880 7044
-100 768 7060
-120 828 7120
-88 808 7068
-92 860 7012
-164 840 7056
-116 832 7020
-84 840 7008

MPU6050_raw.ino (1.04 KB)

Take a look at the device data sheet and the library code or documentation and tell us how the function parameter is defined and what it means:

    accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);

Try other values for that parameter and see what happens.

Check the calibration by flipping the accelerometer and measuring gravity with X up and down, Y up and down and Z up and down. If the values are all not approximately equal (except for sign) it needs to be calibrated.

#define MPU6050_ACCEL_FS_2          0x00
#define MPU6050_ACCEL_FS_4          0x01
#define MPU6050_ACCEL_FS_8          0x02
#define MPU6050_ACCEL_FS_16         0x03

#define MPU6050_ACONFIG_AFS_SEL_BIT         4
#define MPU6050_ACONFIG_AFS_SEL_LENGTH      2


void MPU6050::setFullScaleAccelRange(uint8_t range) {
    I2Cdev::writeBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, range);
}

I2Cdev.cpp

bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) {
    //      010 value to write
    // 76543210 bit numbers
    //    xxx   args: bitStart=4, length=3
    // 00011100 mask byte
    // 10101111 original value (sample)
    // 10100011 original & ~mask
    // 10101011 masked | value
    uint8_t b;
    if (readByte(devAddr, regAddr, &b) != 0) {
        uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
        data <<= (bitStart - length + 1); // shift data into correct position
        data &= mask; // zero all non-important bits in data
        b &= ~(mask); // zero all important bits in existing byte
        b |= data; // combine data with existing byte
        return writeByte(devAddr, regAddr, b);
    } else {
        return false;
    }
}

bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
    return writeBytes(devAddr, regAddr, 1, &data);
}

bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data) {
    #ifdef I2CDEV_SERIAL_DEBUG
        Serial.print("I2C (0x");
        Serial.print(devAddr, HEX);
        Serial.print(") writing ");
        Serial.print(length, DEC);
        Serial.print(" bytes to 0x");
        Serial.print(regAddr, HEX);
        Serial.print("...");
    #endif
    uint8_t status = 0;
    #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
        Wire.beginTransmission(devAddr);
        Wire.send((uint8_t) regAddr); // send address
    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
        Wire.beginTransmission(devAddr);
        Wire.write((uint8_t) regAddr); // send address
    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
        Fastwire::beginTransmission(devAddr);
        Fastwire::write(regAddr);
    #endif
    for (uint8_t i = 0; i < length; i++) {
        #ifdef I2CDEV_SERIAL_DEBUG
            Serial.print(data[i], HEX);
            if (i + 1 < length) Serial.print(" ");
        #endif
        #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
            Wire.send((uint8_t) data[i]);
        #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
            Wire.write((uint8_t) data[i]);
        #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
            Fastwire::write((uint8_t) data[i]);
        #endif
    }
    #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
        Wire.endTransmission();
    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
        status = Wire.endTransmission();
    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
        Fastwire::stop();
        //status = Fastwire::endTransmission();
    #endif
    #ifdef I2CDEV_SERIAL_DEBUG
        Serial.println(". Done.");
    #endif
    return status == 0;
}

Register 28 – Accelerometer Configuration

Register (Hex) : 1C

Register (Decimal) : 28

Bit7 Bit6 Bit5 : XA_ST YA_ST ZA_ST

Bit4 Bit3 : AFS_SEL[1:0]

Bit2 Bit1 Bit0 : -

From P.15

Trying to Config at scale +/- 4, 8 and 16 g
always reading values a half of g

for +/-16g values of 1g is 1024 LSB

Trying to Config at scale +/- 4, 8 and 16 g always reading values a half of g

Obviously, this is not correct.

Try reading back the MPU6050 register to see if the scale value has actually been changed by the call. If not, the library isn't working correctly. If it is, the MPU6050 isn't working correctly.

I gave up on those things a long time ago and recommend using the BNO055 instead. This frees up an enormous amount of program space on the Arduino.

get the same result, I'm unfortunately with this things.

Thanks for your time. jremington

#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

// in MPU6050.h
// #define MPU6050_ACCEL_FS_16         0x03 

MPU6050 accelgyro;

int16_t ax, ay, az;                 

void setup() {

    Wire.begin();

    Serial.begin(38400);

    delay(1000);
    
    // initialize device
    Serial.println("Initializing I2C devices...");

    accelgyro.initialize();

    delay(1000);
    
    accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_16);

    Serial.println(accelgyro.getFullScaleAccelRange());

    // verify connection
    Serial.println("Testing device connections...");

    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
    
    delay(1000);
}

void loop() {
    

    accelgyro.getAcceleration(&ax, &ay, &az);

    Serial.print(accelgyro.getFullScaleAccelRange()); Serial.print("\t");

    Serial.print(ax); Serial.print("\t");  

    Serial.print(ay); Serial.print("\t"); 

    Serial.print(az); Serial.print("\t");

    Serial.print("\n");

}

Output:

3 21 297 861 3 15 288 860 3 4 296 857 3 16 292 858 3 14 299 857 3 8 292 871 3 12 287 869 3 11 291 857 3 18 287 860

I'm not familiar with this device so can only offer a couple of observations:

In MPU6050.cpp the comments after MPU6050::getMotion6() state

 * AFS_SEL | Full Scale Range | LSB Sensitivity
 * --------+------------------+----------------
 * 0       | +/- 2g           | 8192 LSB/mg
 * 1       | +/- 4g           | 4096 LSB/mg
 * 2       | +/- 8g           | 2048 LSB/mg
 * 3       | +/- 16g          | 1024 LSB/mg

I wonder why?

And secondly, the self test bits are in the same register as the full-scale settings, is it a coincidence that the self test forces a reading of 0.5g ?

I ran into this same issue with MPU6050 / GY521 with proMicro in combination with MPU6050.cpp and I2Cdev libs. I am using inertial sensors for many years but kind of new to Arduino world. This is the first time I wired up a digital MEMS onto it. Stunned by cost sofar. With the libs provided its incredible how fast first data comes out. But chip documentation is kind of sparse and the details are now becoming a challenge. Detail of factor 2 that is.

Anyway... Earth 1g gravity shows up with close to perfect 0.5 factor sensitivity on X, Y and Z axis in the various FS range settings. Factor 2 could correspond to 1 bit shift but it sounds far fetched.

As Martin-X suggests.. There must be a reason for the 8192 LSB/(m??)g sensitivity in the getMotion6 call. 8192 LSB/g matches what I have. So practical question...

Is it possible for the data sheet to be wrong ?

Hello everybody. Same problem here but unfortunately no answer.

But: In my view the Datasheet is correct. We have 16 Bit = 65536 values -> +-2g= +-32768 ->1g=16384

If we set:

[https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050]

...
#ifdef OUTPUT_READABLE_REALACCEL
    // display real acceleration, adjusted to remove gravity
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetAccel(&aa, fifoBuffer);
    mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
    
    Serial.print(gravity.z);

        Serial.print("ACC real\t");
        Serial.print(aaReal.y);
        Serial.print("\t");
        Serial.print(aaReal.x);
        Serial.print("\t");
        Serial.println(aaReal.z);
    

#endif

1g is about +8192 After measure up all other values are to low. (Factor 0.5)

...let us check the "MPU6050_6Axis_MotionApps20.h" line 658

uint8_t MPU6050::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) {
   // get rid of the gravity component (+1g = +8192 in standard DMP FIFO packet, sensitivity is 2g)
   v -> x = vRaw -> x - gravity -> x*8192;
   v -> y = vRaw -> y - gravity -> y*8192;
   v -> z = vRaw -> z - gravity -> z*8192;
   return 0;
}

It says 8192 is standard for 2g. Pretty confusing.

Compared to the gyro: the lib and the data sheet values are the same:

Datasheet:

FS_SEL  Full Scale Range  LSB Sensitivity
0           ± 250 °/s            131 LSB/°/s
1           ± 500 °/s            65.5 LSB/°/s
2           ± 1000 °/s          32.8 LSB/°/s
3           ± 2000 °/s          16.4 LSB/°/s

Lib:

* FS_SEL | Full Scale Range   | LSB Sensitivity
* -------+--------------------+----------------
* 0      | +/- 250 degrees/s  | 131 LSB/deg/s
* 1      | +/- 500 degrees/s  | 65.5 LSB/deg/s
* 2      | +/- 1000 degrees/s | 32.8 LSB/deg/s
* 3      | +/- 2000 degrees/s | 16.4 LSB/deg/s

Either we make a mistake or there is somthing wrong with the settings in jRowbergs lib. Does anyone have an idea?

There is a problem somewhere, but it may not matter. Accelerometers need to be calibrated for best results, and if 1g = 8192, that would be the calibration factor. For more on calibration see https://thecavepearlproject.org/2015/05/22/calibrating-any-compass-or-accelerometer-for-arduino/

Calibration:
Yes, thats right. I've set the offsets with an other skript and the results are pretty good.
In position of rest i get about x:0, y:0, z:8192 for acc. Currently i multiply with these factor so i get the right values for g-force.

But i think this is not entirely correct because we loose the half of measuring resolution.

My current solution:

#ifdef OUTPUT_READABLE_REALACCEL
    // display real acceleration, adjusted to remove gravity
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetAccel(&aa, fifoBuffer);
    mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);

    raccX = aaReal.x * 1 / 8192.0;
    raccY = aaReal.y * 1 / 8192.0;
    raccZ = aaReal.z * 1 / 8192.0;

#endif

and i get:
Data.jpg

Data.jpg

The resolution (1 part in 8192) is much, much higher than the accuracy (somewhere around 1%, depending on calibration).

Ok, good point. Thats true. I didn't think about that at the first moment.

But its not the best solution. Maybe the lib will get an update. Thx anyway.

Hi,

Sorry to up this topic, i have the same issue with a 0.5 factor on my values.

Do you now have an answer to this issue?

(1) Why does it matter? (2) The MPU6050 is outdated. Much better sensors are available from Pololu, Adafruit, etc.

I solved it!

Summarising the problem:

The product specifications for the 6000/6050 lead us to believe that the accelerometer output is a 16 bit signed integer, i.e. it ranges from -32768 <–> 32768. Furthermore, for a full scale range of -2g <-> 2g, we get a LSB/g sensitivity of 32768 / 2 = 16384 LSB/g, hence an acceleration of 1g should output +16384. However, many users are experiencing problems with an acceleration of 1g outputting +8192, exactly half of what it should be. This behaviour is replicated for other full scale ranges (+/-4g, +/-8g, +/-16g), but we do not seem to encounter this error with the gyroscope (although it is hard to verify directly as I have not strapped the MPU to a motor or anything!).

I knew the problem must lie with the datasheet so I started by reading the 6000/6050 Register Map and Descriptions document, version 4.2, and noticed an entry in the Revision History table:

10/07/2011 3.0 Updates for Rev D silicon:
Updated accelerometer sensitivity specifications (sections 4.6, 4.8, 4.10, 4.23)

This indicates that they changed the sensitivity specifications for revision D of the board.

In the product specification for the chip, version 3.4, they have a diagram explaining the codes written on the chip. There is a letter in the bottom right labelled “Rev Code”. On my chip, it is a “C” so I take this to mean Rev C (see attached photo). Hence this version applies to a newer version of the board than mine (which I brought cheaply off Ebay).

Furthermore I also noticed an update in the main 6000/6050 Product Specification Revision History table:

10/18/2011 3.0
For Rev D parts. Updated accelerometer specifications in Table 6.2. Updated
accelerometer specification note (sections 8.2, 8.3, & 8.4). Updated qualification
test plan (section 12.2).

So it is probably safe to conclude that, since this table lists all the LSB/g sensitivity measurements and was updated from Rev C to Rev D, it most likely had the values we would expect (namely 8192LSB/g, 4096LSB/g, 2048LSB/g, 1024LSB/g) in the older versions (Revsision < 3.0), rather than the ones we see in Revision 3.4 of the specification (namely 16384LSB/g, 8192LSB/g, 4096LSB/g, 2048LSB/g).

I was going to leave it here, content that they had changed the accelerometer between the revisions of the chip, and I had an older version.

But to prove my theory I wanted to find an old version of the datasheet.

After much digging, I discovered datasheetarchive.org, where I managed to find an old version of the Product Specification that shows the crucial Table 6.2, before the update to chip Rev D. This was Product Specification Revision 2.0, published 05/19/2011.

And lo and behold: the table was exactly as we would predict! That is to say that all the accelerometer sensitivities were halved in this older revision of the specification.

Curiously, the two’s complement ADC word length was still defined to be 16 bit. I found this confusing as that would mean that in full scale range of -2g <–> 2g, with the sensitivity of 8192LSB/g, the range maximum would be reached at an output of 16384, representing an acceleration of 2g. But what would this mean for outputs above 16384? Are they undefined? Well from experimenting with the chip I think that the continue to represent larger accelerations. I.e. I reckon that if you could get the chip to output 32768, this would represent an acceleration of 4g. Confusing!

Finally, one more thing I found was that the initial calibration tolerance for zero-g output under the z axis is given as +/- 200mg in the table under this older specification, whereas it is +/- 80mg in the newer specification. This indicates to me that it was not an MPU encoding change between revisions, but an actual change of the accelerometer, since the tolerance has changed. I have not thought about this too much as it isn’t too important to me, but may interest someone!

Older Revision 2.0 of the datasheet that applies to Rev C of the MPU6050

The MPU-6050 is no longer being manufactured, but thanks for the historical note.

Indeed, I'm sure it will help some people when they search here for similar issue!

It is quite rare for silicon revisions to change behaviour, normally they are process improvements or bug fixes (which manufacturer's are all too coy about mentioning except in small print).