Accelerometer Gyroscope angle Raspberry

Guide to interfacing a Gyro and Accelerometer with a Raspberry Pi

This guide covers how to use an Inertial Measurement Unit (IMU) with a Raspberry Pi . This is an updated guide and improves on the old one found here.

I will explain how to get readings from the IMU and convert these raw readings into usable angles. I will also show how to read some of the information in the datasheets for these devices.

This guide focuses on the BerryIMU. However, the theory and principals below can be applied to any digital IMU, just some minor modifications need to be made.

Git repository here
The code can be pulled down to your Raspberry Pi with;

pi@raspberrypi ~ $ git clone http://github.com/ozzmaker/BerryIMU.git

 

The code for this guide can be found under the gyro_accelerometer_tutorial01_angles directory.

A note about Gyros and Accelerometers

When using the IMU to calculate angles, readings from both the gyro and accelerometer are needed which are then combined. This is because using either on their own will result in inaccurate readings. And a special note about yaw.

Gyros - A gyro measures the rate of rotation, which has to be tracked over time to calculate the current angle. This tracking causes the gyro to drift. However, gyros are good at measuring quick sharp movements.

Accelerometers - Accelerometers are used to sense both static (e.g. gravity) and dynamic (e.g. sudden starts/stops) acceleration. They don’t need to be tracked like a gyro and can measure the current angle at any given time. Accelerometers however are very noisy and are only useful for tracking angles over a long period of time.

Accelerometers cannot measure yaw.   To explain it simply, yaw is when the accelerometer is on a flat level surface and it is rotated clockwise or anticlockwise.  As the Z-Axis readings will not change, we cannot measure yaw.   A gyro and a magnetometer can help you measure yaw. This will be covered in a future guide.

Here is an excellent tutorial about accelerometers and gyros.

Setting up the IMU and I2C

The IMU used for this guide  is a BerryIMUv3  which uses a LSM6DSL,
that consists of a 3-axis gyroscope plus a 3-axis accelerometer and a LIS3MDL which is a 3-axis magnetometer.

  • LSM6DSL - Accelerometer and gyroscope datasheet can be found here.
  • LIS3MDL - Magnetometer datasheet can be found here

 

This IMU communicates via the I2C interface.

The image below shows how to connect the BerryIMU to a Raspberry Pi

BerryIMUv3 and Raspberry Pi I2C hook up

 

The BerryIMUv3 can also be connected using the Raspberry Pi QWIIC connector and cable;

QWIIC connector Raspberry Pi
QWIIC connector for the Raspberry Pi

 

After connecting the BerryIMU to your Raspberry Pi,  you need to enable I2C on your Raspberry Pi and  install the I2C-tools. This guide will show you how.

Once you have i2c enabled, you should see the addresses of the components on the IMU when using i2cdetect;

pi@raspberrypi ~ $ sudo i2cdetect -y 1

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- 1c -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- 6a -- -- -- -- --
70: -- -- -- -- -- -- -- 77

The gyro on the LSM6DSL is using the address of 0x6a and the accelerometer on the LSM6DSL is using 0x1c. This can be verified by looking at section 6.3.1 on the datasheet of the LSM6DSL. The address will be shown as 1101010b.

The magnetometer on the LIS3MDL is using 0x1e and 0x77 is the pressure sensor.
We will not be covering the magnetometer or pressure sensor in this post.

 

BerryIMU

Reading and Writing to the Accelerometer and Gyroscope

We read and write values from different register in the above addresses to do different things. Eg Turn the Gyro on, set the sensitivity level, read accelerometer values. etc..
The git repository includes a header file for the LSM6DSL which lists all the registers used by this sensor.

We use ioctl calls to read and write information to the I2C bus.

Open I2C Bus

First, you need to open the I2C bus device file, as follows;

        char filename[20];
        sprintf(filename, "/dev/i2c-%d", 1);
        file = open(filename, O_RDWR);
        if (file<0) {
                printf("Unable to open I2C bus!");
                exit(1);
        }

As we are using sprintf, you will need to include the header file stdio.h

Selecting the Gyro, Accelerometer or magnetometer
Before we can start writing and reading values from the sensors, we need, we need to select the address of the sensor we want to write to.

if (ioctl(file, I2C_SLAVE, device_address) < 0) {
        printf("Error: Could not select device\n");
}

device_address is the address of the sensor you want to select. E.g. 0x6a for the LSM6DSL.
I2C_SLAVE is defined in i2c-dev.h

You will need to include the file control headers in your program, fcntl.h.
You will also need to include the headers for the i2c bus – linux/i2c-dev.h

If we only had one sensor, we would only need to do this once at the start of the program.  As we have two, we need to select each of the different sensors just before we want to read or write a value.

We should write a function to do this;

void selectDevice(int file, int addr)
{
 char device[3];
 if (ioctl(file, I2C_SLAVE, addr) < 0) {
 printf("Failed to select I2C device.");
 }
}

When we call the function, we will pass the file pointer and the address of the sensor we want to select.  Like this;

selectDevice(file,ACC_ADDRESS);

Enable the Accelerometer

 // Enable accelerometer.
		writeAccReg(LSM6DSL_CTRL1_XL,0b10011111);       // ODR 3.33 KHz, +/- 8g , BW = 400hz
		writeAccReg(LSM6DSL_CTRL8_XL,0b11001000);       // Low pass filter enabled, BW9, composite filter
		writeAccReg(LSM6DSL_CTRL3_C,0b01000100);        // Enable Block Data update, increment during multi byte read

To enable to accelerometer, we need to write a byte to the CTRL_REG1_XL(0x10) register on the LSM6DSL, which also sets the ouput data rate(3.33KHz) and sensitivity (+/- 8g), This can be see on page 60 of the datasheet, where you will also find other values that can be used.

After this, we then need to write a byte to the CTRL_REG8_XL(0x17) to enable the low pass filter. This information is on page 66 of the datasheet.

And finally CTRL3_C is set to force the accelerometer to increment the register  address automatically when a multi byte read is performed.

Enable the Gyro

Enabling the gyro is very similar to the accelerometer.

 // Enable Gyro
		//Enable  gyroscope
		writeGyrReg(LSM6DSL_CTRL2_G,0b10011100);        // ODR 3.3 kHz, 2000 dps

Writing byte to CTRL2_G(0x11) to enable the gyro. This setting is found on page 61 of the datasheet.

Reading the Values from the Accelerometer and Gyro

Once you enable the accelerometer and gyro, you can start reading the raw values from these sensors.
Reading the accelerometer

void readACC(int  a[])
{
        uint8_t block[6];
        selectDevice(file,LSM6DSL_ADDRESS);
        readBlock(LSM6DSL_OUTX_L_XL, sizeof(block), block);    
	
	// Combine readings for each axis.
	a[0] = (int16_t)(block[0] | block[1] << 8);
	a[1] = (int16_t)(block[2] | block[3] << 8);
	a[2] = (int16_t)(block[4] | block[5] << 8);
}

The above function will read the raw values from the accelerometer, here is a description of what is happening;

  1. An array of 6 bytes is first created to store the values.
  2. The I2C slave address is selected for the LSM6DSL(Accel & gyro), by passing the address of LSM6DSL_ADDRESS or 0x6a  to the selectDevice() function.
  3. Using the readBlock() function from i2c-dev.h, we read 6 bytes starting at OUTX_L_XL (0x28). This is shown on page 74 of the datasheet.
  4. The values are expressed in 2’s complement (MSB for the sign and then 15 bits for the value) so we need to combine;
    block[0] & block[1] for X axis
    block[2] & block[3] for Y axis
    block[4] & block[5] for Z axis

Reading the gyro

void readGYR(int g[])
{
	uint8_t block[6];
	selectDevice(file,LSM6DSL_ADDRESS);
	readBlock(LSM6DSL_OUTX_L_G, sizeof(block), block);
	// Combine readings for each axis.
	g[0] = (int16_t)(block[0] | block[1] << 8);
	g[1] = (int16_t)(block[2] | block[3] << 8);
	g[2] = (int16_t)(block[4] | block[5] << 8);
} 

The above function reads the values from the gyro. It is very similar to the readACC() function.
Once we have the raw values, we can start converting them into meaningful angle values.

 

Convert the Raw Values to Usable Angles

Gyro

        //Convert Gyro raw to degrees per second
        rate_gyr_x = (float) gyrRaw[0]  * G_GAIN;
        rate_gyr_y = (float) gyrRaw[1]  * G_GAIN;
        rate_gyr_z = (float) gyrRaw[2]  * G_GAIN; 

For the gyro, we need to work out how fast it is rotating in degrees per second(dps). This is easily done by multiplying the raw value by the sensitivity level that was used when enabling the gyro. G_GAIN = 0.07, which is based off a sensitivity level of 2000 dps.  Looking at the table on page 3 of the datasheet, we can see that we need to multiple it by 0.07 to get degrees per second. The table shows it as 70 mdps (milli degrees per second). Another example: Looking at the table, if we chose a sensitivity level of 250dps when enabling the gyro, then we would have to multiply the raw values by 0.00875.

We now need to track this gyro overtime.

         //Calculate the angles from the gyro
         gyroXangle+=rate_gyr_x*DT;
         gyroYangle+=rate_gyr_y*DT;
         gyroZangle+=rate_gyr_z*DT; 

DT = loop period. I use a loop period of 20ms. so DT = 0.02.
gyroXangle = is the current X angle calculated from the gyro X data.
DT set to 20ms, this is how long it takes to complete one cycle of the main loop. This loop period has to be constant and accurate, otherwise your gyro will drift more then it should.

Accelerometer

         //Convert Accelerometer values to degrees
        AccXangle = (float) (atan2(accRaw[1],accRaw[2])+M_PI)*RAD_TO_DEG;
        AccYangle = (float) (atan2(accRaw[2],accRaw[0])+M_PI)*RAD_TO_DEG;

The angles can be calculated by using trigonometry and the raw values from the other accelerometer axis. This is done using the Atan2 function to return the principal value of the tangent of the other angles, expressed in radians. We add π to the radians so that we get a result between 0 and 2. We then convert the radians to degrees by multiplying the radians by 57.29578 (180/π).
M_PI = 3.14159265358979323846
RAD_TO_DEG = 57.29578 , 1 radian = 57.29578 degrees

You will need to include the math header file in your program, math.h and when compiling you will also need to link the math library. ‘-lm’

Combining Angles from the Accelerometer and Gyro

Once you have the both the angles from both sensors, you will have to combined them to overcome the gyro drift and the accelerometer noise.
We can do this by using a filter which will trust the gyro for short periods of time and the accelerometer for longer periods of time.

There are two filers you could use, the Kalman Filter or the Complementary Filter. We will used the Complementary filter as it is simple to understand and less CPU intensive. The Kalman filter is way to far complex and would be very processor intensive.

Complementary filter;

Current angle = 98% x (current angle + gyro rotation rate) + (2% * Accelerometer angle)

or

        CFangleX=AA*(CFangleX+rate_gyr_x*DT) +(1 - AA) * AccXangle;
        CFangleY=AA*(CFangleY+rate_gyr_y*DT) +(1 - AA) * AccYangle;

CFangleX & CFangleY are the final angles to use.

Timing

The gyro tracking will not be accurate if you can not run a well timed loop.
The code includes the mymillis() function for timing.

At the start of each loop, we get the current time;

startInt = mymillis();

then at the end of the loop, we make sure that at least 20ms has passed before continuing;

//Each loop should be at least 20ms.
while(mymillis() - startInt < 20)
{
        usleep(100);
}

If your loop takes longer than 20ms to run, 45ms for example. Jut update the above value to make sure that every loop cycle runs at the same speed and set this value in DT for the gyro tracking.

 Convert readings -/+ 180 Degrees

 AccXangle -= (float)180.0;
 if (AccYangle > 90)
       AccYangle -= (float)270;
 else
        AccYangle += (float)90;

The above code will convert the values for the X and Y axis on the accelerometer so that the value is 0 when the IMU is upright. This isn't needed, however I like all axis to be 0 when the device is upright.

 

Display the values in the Console

The code below will print the readings to the console in with color coding to make the values easier to read.

printf ("GyroX  %7.3f \t AccXangle \e[m %7.3f \t \033[22;31mCFangleX %7.3f\033[0m\t GyroY  %7.3f \t AccYangle %7.3f \t \033[22;36mCFangleY %7.3f\t\033[0m\n",gyroXangle,AccXangle,CFangleX,gyroYangle,AccYangle,CFangleY);

 

Compile the Code

pi@raspberrypi ~ $ gcc -o gyro_accelerometer_tutorial01 -l rt gyro_accelerometer_tutorial01.c -lm

Run the Program

pi@raspberrypi ~ $ sudo ./gyro_accelerometer_tutorial01

 

Guides and Tutorials

111 thoughts on “Guide to interfacing a Gyro and Accelerometer with a Raspberry Pi”

  1. Hi,

    Regarding the filtering,
    Current angle = 98% x (current angle + gyro rotation rate) + (2% * Accelerometer angle)

    It looks like the current angle is weighed heavily by the gyro reading and weighs the accelerometer reading very little.. but how does this in principle filter the drift of the gyro?

  2. This works as the gyro only reports rate of turn. If the IMU was not moving and it is at a 90 degress angle, the gryo would report ‘0’ (Zero) and the accelerometer would report 90 degress.

  3. awesome project. I bought some parts from adafruit. Seems its using the same gyro as yours here, but when I run gyro_accelerometer_tutorial01 , I get

    Failed to write byte to I2C Gyr

    this is writing the 0x20 register in the enableIMU() function. ( I added some debug )

    I2cdetect shows valid devices, and this code –
    https://github.com/mpolaczyk/Gyro-L3GD20-Python/blob/master/L3GD20.py seems to work.
    Any hints you can give me on whats going on would be really appreciated, as Im looking forward to having the off road inclinometer in my truck

    Thanks, Paul

    1. I am getting the same error as Paul:
      Failed to write byte to I2C Gyr

      Yet Paul says his fix was to add:
      #define GYR_ADDRESS 0x6B

      Where does he add this line?
      Do I add it to “gyro_accelerometer_tutorial01.c”, then compile?

  4. Hi,

    Great tutorial, I am fairly new to using i2c and to programming in general so this site has helped me out a lot. I cloned the Berry IMU library and am now using the gyro_accelermoter_turtorial03_Kalman_filter directory.

    I’m having some problems getting the Gyro and acclerometer to work in tandem though.
    I got the gyro working, I’m using the same one you are, an L3GD20H, but can’t get my accemerometer module, a sparkfun ADXL345, to work. I keep getting the message

    “failed to write byte to 12C Acc”

    I think this is because the ADXL345 uses an address of 0x53 which is not the same as the LSM9DS0 ‘s accelerometer address 0x1E.

    To try to solve this I went into the LSM9DS0.h and changed the ACC_ADDRESS to 0x53 but got the same error message. I also tried messing around with the accelerometer enable portion of the post but didn’t get anywhere.

    Do you have any ideas?

      1. It turns out that I connected the CS pin on the ADXL low instead of high and forgot to recheck the i2c addresses to make sure that the device was enabled. It works now, thanks!

  5. Great tutorial this works great! I was wondering if you knew whether or not configuring I2C will affect the GPIO pins? I configured I2C and implemented this code and I also placed some SPI. However, after doing this, a simple code that should turn on my GPIO pins no longer worked. Have you heard of anything like this?

  6. Hi,

    Thanks for this tutorial, it really helped me getting everything set up and running.

    I have one question:
    why when using i2c_smbus_read_block_data you send command (0x80 | OUT_X_L_A) instead of just register address (OUT_X_L_A)?

    I know it won’t work with just address (I checked), but I’d like to know why.

      1. Thank you very much, somehow I didn’t notice this when reading through datasheet. Now everything is clear.

  7. A smoothing filter probably should be added to the gyro output and remove the initial gyro offert from the output. In the current code, when still the gyro output is not necessarily 0, which in turn adds a offset to the resulting angle.

  8. Amazing explanation, Great tutorial this works grea with an sparkfun imu LSM9DS0 6dof. Do you have an example how can I write the output data in a SD card? thanks a lot

  9. So my ACCX and ACCY angles aren’t updating when I run the example berry_imu python code. I would prefer to not have to scrap all of my motor controller and motion detection python code and rewrite it all in C.

    1. Can’t really dive into the details, but the actual value of I2C_SLAVE is indeed 0x0703. But that means nothing. Its just a register address(sort of) that contains the instruction to change the address of the active I2C Slave.
      There are a lot of things happening one layer down and we don’t really need to care too much about that, unless you have a particular reason to. That, I can’t really help with.

  10. So when you get to the Open I2C Bus part where are you entering this code into? I am really lost when I get to this part I am not very fimilar with Raspberry Pi but i need to get it to give me angles thanks!

      1. Thanks! That really helped! I keep getting an i2c error that says: unable to open i2c bus (followed the link for setting up the i2c)

        Is the i2c needed for the C and pythons program? When I used the python program i got an error stating:

        File “berryIMU.py”, line 126, in
        writeACC(CTRL_REG1_XM, 0b01100111)#z,y,x axis enabled, continuous update, 100 Hz data rate
        File “berryIMU,py”, line 38, in writeACC
        bus.write_byte_data(ACC_ADDRESS , register, value)
        IOError: [Errno 5] Input/output error

        Sorry for all the comments i am not very good with the Rasberry Pi!

      2. No I do not see the IMU when I do sudo i2cdetect -y 1, what should i do from here?

        Another side question, could this be used with an Arduino since it has c code right?
        Thanks!!!

      3. I would double check your connections. Are you using the jumper cables or have you soldered on the female header and have it connected to the GPIO pins?
        It can definitely be used on an Arduino. However the code would have to be changed to support Arduino specific functions, like i2c read and write. The math will work

  11. In your demo video a graphical BerryIMU was displayed on a screen, rotating relative to its real-time position. How could I go about setting up a graphical representation of the acc/gyro components?

  12. Regarding the minimu9-ahrs visualization, is there a way to shrink the size and dimensions of the displayed 3d image? Is there some sort of configuration file where that can be modified?

  13. Hi,

    Really nice tutorial,
    Is there is any c++ I2C lib or a way to compile the sensor.c in a c++ projet, I have some error with like O_RDWR missing.

    cheers.

  14. Hello Mark,
    Thank you for taking the time to create this, write it up, and answer questions!

    With your help (in the above comments), I was able to modify LSM9DS0.h with the correct address in my i2c bus.
    I used i2cdetect -y 1 and I saw that it was at 6b, so I entered “0x6B”.
    The other values are believe are right, as they are the 0x1E.

    My problem is that I have zero on the AccXangle, and a static -90.000 in the AccYangle.

    In addition to that, the values keep climbing for ever.
    For example, the values of GyroX keep climbing (they suprassed 370.000, and I Ctrl+C then).

    Do I have something wrongly configured?
    Or is something amiss?

    Thank you very much for your help!!

      1. The is the expected behavior of the gyro. All gyros exhibit this.
        This is caused by noise.

        From the guide;

        Gyros – A gyro measures the rate of rotation, which has to be tracked over time to calculate the current angle. This tracking causes the gyro to drift. However, gyros are good at measuring quick sharp movements.

        Accelerometers – Accelerometers are used to sense both static (e.g. gravity) and dynamic (e.g. sudden starts/stops) acceleration. They don’t need to be tracked like a gyro and can measure the current angle at any given time. Accelerometers however are very noisy and are only useful for tracking angles over a long period of time.

        This is why both values need to be fused together to get an accurate result.

  15. Hi. Is the same result running the script from sudo ./gyro_accelerometer_tutorial01 than from the python in BerryIMU/python-LSM9DS0-gryo-accel-compass $ berryIMU.py

    And another question is
    When I watch results from the sudo ./gyro_accelerometer_tutorial01 in screen everthing is ok, but when I ouput to a *.csv or *.txt file,, some errors appears in between the data

    Example:

    “Loop Time 20 GyroX -113.579 AccXangle [m -179.498 [22;31mCFangleX -173.466[0m GyroY -219.676 AccYangle 178.223 [22;36mCFangleY 177.599 [0m”

    any suggestions?

    thanks in advace

    1. The data should be the same for both the python and the C code.
      The “errors” you are seeing is the ASCII escapes sequences use to output the data in different colours on the terminal.
      Replace the printf statement in gyro_accelerometer_tutorial01.c with this;
      printf (” GyroX %7.3f \t AccXangle %7.3f \t CFangleX %7.3f \t GyroY %7.3f \t AccYangle %7.3f \t CFangleY %7.3f\t\n”,gyroXangle,AccXangle,CFangleX,gyroYangle,AccYangle,CFangleY);

      1. Thank you. tutorial01, 02, y 03 are working fine without ascii escapes due to colours in terminal. But, when I tried to run berryIMU.py it gives me the following

        pi@raspberrypi ~/BerryIMU/python-LSM9DS0-gryo-accel-compass $ sudo python berryIMU.py
        Traceback (most recent call last):
        File “berryIMU.py”, line 126, in
        writeACC(CTRL_REG1_XM, 0b01100111) #z,y,x axis enabled, continuos update, 100Hz data rate
        File “berryIMU.py”, line 38, in writeACC
        bus.write_byte_data(ACC_ADDRESS , register, value)
        IOError: [Errno 5] Input/output error

        any suggestion?

        thanks again

      2. No. I am using the LSM9DS0
        output of sudo i2detect -y 1 is

        0 1 2 3 4 5 6 7 8 9 a b c d e f
        00: — — — — — — — — — — — — —
        10: — — — — — — — — — — — — — 1d — —
        20: — — — — — — — — — — — — — — — —
        30: — — — — — — — — — — — — — — — —
        40: — — — — — — — — — — — — — — — —
        50: — — — — — — — — — — — — — — — —
        60: — — — — — — — — — — — 6b — — — —
        70: — — — — — — — —

  16. Is it possible to change the output rate of data that we can see in screen or and of csv? I mean, I want to calculate then average speeds, maximum angle of inclination, etc etc, but the number of rows in the current outputs is huge. Thanks again

    1. yes, that is easy. Just remove the unneeded values from the print statement;
      in C:
      printf (" GyroX %7.3f \t AccXangle \e[m %7.3f \t \033[22;31mCFangleX %7.3f\033[0m\t GyroY %7.3f \t AccYangle %7.3f \t \033[22;36mCFangleY %7.3f\t\033[0m\n",gyroXangle,AccXangle,CFangleX,gyroYangle,AccYangle,CFangleY);
      Python;
      print ("\033[1;34;40mACCX Angle %5.2f ACCY Angle %5.2f\033[1;31;40m\tGRYX Angle %5.2f GYRY Angle %5.2f GYRZ Angle %5.2f \033[1;35;40m \tCFangleX Angle %5.2f \033[1;36;40m CFangleY Angle %5.2f \33[1;32;40m HEADING %5.2f \33[1;37;40m tiltCompensatedHeading %5.2f\033[0m " % (AccXangle, AccYangle,gyroXangle,gyroYangle,gyroZangle,CFangleX,CFangleY,heading,tiltCompensatedHeading))

  17. Hello,
    We are working with a raspberry pi and a Sparkfun 6DOF IMU (ITG-3200/ADXL345). We try to get the angle measurements from the gyroscope and accelerometer with python but we have some issues with the register map. ITG-3200 is set to 0x68 and ADXL345 to 0x53. We changed the following parameters for ITG-3200 and it works.
    OUT_X_L_G = 0x1E
    OUT_X_H_G = 0x1D
    OUT_Y_L_G = 0x20
    OUT_Y_H_G = 0x1F
    OUT_Z_L_G = 0x22
    OUT_Z_H_G = 0x21

    There is a problem with initialize the gyroscope. How must we changed the register map for sparkfun?

    writeGRY(CTRL_REG1_G, 0b00001111) #Normal power mode, all axes enabled
    writeGRY(CTRL_REG4_G, 0b00110000) #Continuos update, 2000 dps full scale

    A next Problem is to give the right register map parameters for ADXL345 and also the initialization of the the accelerometer.
    OUT_X_L_A = 0x32
    OUT_X_H_A = 0x33
    OUT_Y_L_A = 0x34
    OUT_Y_H_A = 0x35
    OUT_Z_L_A = 0x36
    OUT_Z_H_A = 0x37

    #initialise the accelerometer
    writeACC(CTRL_REG1_XM, 0b01100111) #z,y,x axis enabled, continuos update, 100Hz data rate
    writeACC(CTRL_REG2_XM, 0b00100000) #+/- 16G full scale

    We hope you have a idea.
    Thanks a lot.

  18. Hi Mark
    Thank you for the great tutorial.
    But when I compile my Project, there is an error. So the “I2C_SMBUS_BLOCK_MAX” ist undeclared.
    Can you help me?

    Best Regards!

  19. Hi,
    your tutorial is really great!

    I am not experienced in C-Coding.
    I could not understand the line of code:

    *a = (int16_t)(block[0] | block[1] << 8);

    the first problem was in my understanding, that in block[1] is the High-Byte of the 2-Byte value.
    Therefore i have to shift it on the left side and add it to the 'low value' by 'bitwise or'

    Is it correct?

    The second problem is: the size of block[1] is only 8 Bits, because the type is uint8_t
    I think the shift-Operation

    block[1] << 8

    will lose all bits? I would understand this code:

    ((int16_t) block[1] )<<8

    Here you see Casting before shifting. But the Casting-Operation in your Code is after shifting???
    Is it an 'inside-knowledge', that shifting would implizit growing the size???

    Best regards and thanks again for your tutorial

  20. Don’t wish to point out a negative, but that accel angle you are computing with atan is for the vector of acceleration. it is tranlation and not rotation. you cannot combine with gyro data. one sensor is used for position, velocity, acceleration, and the other sensor is used for heading and rotation rates.

    1. I am going to have to disagree with you.
      The formulas above are well known and used widely by a large number of IMUs.
      The calculations above are not ‘combining’ both data, it is using a complementary filter to get the best values from each sensor. Accelerometer is best for long term values, gryo for short term.
      ” heading and rotation rates.” it is impossible to get accurate readings for the heading and rotation rate from one sensor….if you are talking about the gryo… then you again are incorrect. The heading is best sourced from the compass.

  21. Some of us are less familiar with executing files. Been a while since I navigated files using Unix or Dos commands. In the areas where you mention compiling code for the gyro accelerometer , you might mention that we need to cd BerryIMU or cd B press tab to the directory BerryIMU and use ls to list the next directories containing the files. After that , cd directory_name into the directories containing the files and then execute the gcc statement in the same directory as the file. Otherwise we get a message saying no file found.

    I bought your accelerometer and am looking forward to following the directions and making it work.

    1. The values are ms/LSB. They need to be multiplied by the sensitivity level set at CTRL_REG2_XM.
      E.g. if you have set 16g, then you would multiple the raw value by 0.732.

      I just did a test, A stationary BerryIMU on a flat surface, set to 16g shows a raw value of around 1380 for the Z axis from the accelerometer. I then multiply this by 0.732, it gives me 1010.16mg, or 1.01G

      1. Thanks for the reply…I guess I found the link around the same time you posted. The calculation works the same for me, around 1G for the z axis on a flat surface. Thanks for the help!

  22. Any idea why the gyroscope doesn’t read 0 when the sensor is at rest. It’s on a table not moving, but the readings are all over the map.

      1. I expected the accelerometer to have noise but it is actually very stable compared to the gyro. At rest the gyro shows as much as 5 degrees per second on an axis. This seems high to me.

  23. 5 deg drift is a bit high, however this can be fixed when using a Kalman or Complementory filter. You can also use a high/low pass filter or a median filter.
    Are you using the Python or C code?

  24. I wand to estimate my location using BerryIMU.
    If I plan to integrate twice the accelerometer readings twice (in all three directions).
    Kalman Filter would be handy in this case, thanks for that !!!

    BUT I do not know how to find my instant acceleration in each direction. You only presented angle estimations in your tutorials.

    Can you help me with that, please?

    Thank you…

    1. when rotating the IMU on the Z axis, the reading for Z from the accelerometer will always be 1g.. so the value is of no help. If the IMU was on its side, then the Z axis can be used, but then the X or Y will always be 1g.
      The code would be;
      AccZangle = (math.atan2(ACCy,ACCx)+M_PI)*RAD_TO_DEG
      When the IMU is flat, the gyro Z axis can be fused with the magnetometer to get good Z axis angles .

      1. Hello Mark,

        For combining Zangle from the Accelerometer and Gyro, can I use the code :
        CFangleZ=AA*(CFangleZ+rate_gyr_z*DT) +(1 – AA) * AccZangle; as to the angles X and Y ?
        This will allow me to display the final angle Z result.

        Regards.
        Benoit

      2. Yes, but keep in mind that if your accelerometer flat and parallel with the Z plane, the acceleromter value will not change as it should always be measuring 1g.

      3. Indeed, the value of CFangleZ does not change when I turn the module in Z axis
        So it would involve the magnetometer by adding rather the code:
        heading = 180 * atan2(magRawY,magRawX)/M_PI;
        Will I get a stable result of the angle Z with this code?
        Regards.
        Benoit

      4. Hi Mark, I was just wondering why the Accelerometer angles are calculated in X like atan2(x/z) and for y its atan2 (z/y) shouldn’t be likewise ?

  25. Can the Berry IMU have its position registered in 3D space? ie bringing it closer to an object of reference will make a virtual image larger?

  26. Hi there, trying to use BerryIMU and BerryIMU.py on a Raspberry Pi 3. I2C is all working…

    sudo /usr/sbin/i2cdetect -y 1

    reports…

    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: — — — — — — — — — — — — —
    10: — — — — — — — — — — — — — — 1e —
    20: — — — — — — — — — — — — — — — —
    30: — — — — — — — — — — — — — — — —
    40: — — — — — — — — — — — — — — — —
    50: — — — — — — — — — — — — — — — —
    60: — — — — — — — — 68 — — — — — — —
    70: — — — — — — — 77

    Python smbus etc is also installed but sadly,

    sudo python BerryIMU.py produces…

    File “berryIMU.py”, line 221, in
    writeACC(CTRL_REG1_XM, 0b01100111) #z,y,x axis enabled, continuos update, 100Hz data rate
    File “berryIMU.py”, line 133, in writeACC
    bus.write_byte_data(ACC_ADDRESS , register, value)
    IOError: [Errno 5] Input/output error

    Saw a v. similar post earlier but I’m using the BerryIMU board.

    All help gratefully received!

    1. Your BerryIMU is showing 68 for the gyro… I have never seen it to this before.
      Open up LSM9DS0.py and change the address for the gryo
      from;
      GYR_ADDRESS = 0x68
      to
      GYR_ADDRESS = 0x68

      see if this helps

  27. Hi Mark, thanks for getting back to me. Am getting some very weird results here.

    So, using i2c detect -y 1 does not produce the same result every time which I am struggling to get my head round tbh.

    You commented that you’d never seen 68 for the address for the gyro – it usually appears on 6a but then randomly appears on 68. Additionally, one of the sensors seems to disappear sometimes from the i2cdetect scan…

    This….

    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: — — — — — — — — — — — — —
    10: — — — — — — — — — — — — — — — —
    20: — — — — — — — — — — — — — — — —
    30: — — — — — — — — — — — — — — — —
    40: — — — — — — — — — — — — — — — —
    50: — — — — — — — — — — — — — — — —
    60: — — — — — — — — — — 6a — — — — —
    70: — — — — — — — 77

    Compared to……

    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: — — — — — — — — — — — — —
    10: — — — — — — — — — — — — — — 1e —
    20: — — — — — — — — — — — — — — — —
    30: — — — — — — — — — — — — — — — —
    40: — — — — — — — — — — — — — — — —
    50: — — — — — — — — — — — — — — — —
    60: — — — — — — — — — — 6a — — — — —
    70: — — — — — — — 77

    And then sometimes I get….

    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: — — — — — — — — — — — — —
    10: — — — — — — — — — — — — — — 1e —
    20: — — — — — — — — — — — — — — — —
    30: — — — — — — — — — — — — — — — —
    40: — — — — — — — — — — — — — — — —
    50: — — — — — — — — — — — — — — — —
    60: — — — — — — — — 68 — — — — — — —
    70: — — — — — — — 77

    This is v. odd. I wanted to ensure there wasn’t a duff connection so started with brand new crimped connections at the Pi end and have soldered the connections to the 4 way header on the BerryIMU.

    Additionally, I have tried all combinations of the 3 variables in the opening lines of the LSM9DS0.py file. The following settings occasionally work, most often not, and when they do work it fails after somewhere between 5 and 30 seconds.

    MAG_ADDRESS = 0x1e
    ACC_ADDRESS = 0x77
    GYR_ADDRESS = 0x6a

    I have also tried….

    MAG_ADDRESS = 0x77
    ACC_ADDRESS = 0x1e
    GYR_ADDRESS = 0x6a

    If it does start working, sometimes I can see the data being streamed correctly, on other occasions the GYR value just climbs and climbs, moving the board has no effect.

    I have no other devices attached. I’ve now tried this on Pi 3 and a Pi 2 and I get same results on both devices.

    Any ideas?!

    Cheers,

    DP.

  28. And…. sorted. Pins on 4 way header were bent over and I suspect this had introduced a micro break somewhere. De-soldered and soldered on the 6-way header – works perfectly.

    Superb little board. 🙂

    Thanks to Mark for helping out!

    DP.

  29. I have a question related to the 20ms loop. Is this the minimum time you need between two reads or can you reduce it?
    The reason why I am asking is that I have the berryIMU mounted on a high speed servo and I am trying to read the angles of the movements. Now if I use the 20ms loop then I am only going to get small amount of angle readings and I need more.
    I have tried to reduce the loop timing to 10ms but then I am getting an interesting issue with the angles reported by the unit. Its giving inaccurate angles in terms that its higher than the actual angles. so as a result If I rotate my servo from 0 to 90 degrees, the gyro will report angles between 0 and 110 degrees.
    Any ideas or suggestions on how to solve this?

    1. Yes, it is okay to speed up the loop.. even 1ms is fine. (if you can get it that fast).
      do you get the correct value when looking at the result of the complementary filter? E.g CFangleX

      1. No, even the complementary filter gives the wrong values. I have taken a closer look today at some of the data and repeated a number of experiments. The gyroXangle gives perfect angles but the problem is that it drifts slowly after sometime. I am guessing that the AccXangle is very sensitive to the fast servo movements and its causing the wrong angles reading whether its the CFangleX or the KalmanX.

        Do you think I can reset the gyroXangle from time to time and overcome the drift? I am alternating between 0 and 90 degrees. So I could simply reset the value every time.

      2. You can reset the gyroXangle if you like, i dont think it will make much difference.
        You can play around with this value. It decides how much of either the gyro or accel is used in the complementary filter.
        #define AA 0.97 // complementary filter constant
        Are you using C or the Python code?
        Have you tried the Kalman filter?

  30. Hi, Is it possible to use the Python code as a library? I just want to read only the GRYZ figure in a Python program and then use this as a variable. All tips gratefully received.

    Thx

  31. Hey, I have been working on creating a VR headset using this and a raspberry pi, does the code output the rotation velocity (3 o/sec) or the overall total rotations(90 o) if I’m using the Kalman filter code? Thanks in advance!

  32. Hello,
    Great tutorial! I am trying to understand why you shift the OR and shifting by 8 parts of reading in the data. What exactly is this doing?
    Thank you

    1. you need to shift to get the 16 bit value from two 8 bit values.
      Each axis is represented as two bytes by the IMU. You read the high and low bytes from one axis. E.g X for the accelerometer. And then combine them. shifting and OR is needed to combine them
      X_Low = 212 or 11010100 in binary
      X_High = 25 or 00011001 in binary

      X_axis = (int16_t)(X_Low | X_High << 8); 6,612 axis = (int16_t)(212 | 25 << 8); or in binary 00011001 11010100 = (11010100 | 00011001 <<8 ) You will see that 00011001 is now the first part of the 16 bit value and 11010100 is the second half of the 16 bit value.

  33. I have a question for this equation stated in the original code:
    equation 1: CFangleY=
    AA*(CFangleY+rate_gyr_y*DT) +(1 – AA) * AccYangle;

    If we really want ‘CFangleY’ then solving for it yields:
    CFangleY =
    ((AA*rate_gyr_y*DT) + AccYangle – (AA*AccYangle)) / (1 -AA).

    Same thing goes for CFangleX.

    The problem with equation 1) above is that the initial value of CFangleY is 0.

    Am I correct??

  34. Hi Mark and everybody.

    I’m working on a java project and need to calculate the pitch of the device. I’m really new to this kind of work, is there any example to follow?

    Thanks!

  35. As someone completely new to programming on a Raspberry Pi, this was very helpful!

    For the project I am working on I am required to write data from three different IMU sensors onto the same SD card, preferable the same CSV file. Would you have any tips and ideas on how to do this?

    Thanks for the article!!

  36. Hello,
    i was wondering, when i tried to run berryIMU.py it is working just fine. But when i tried it with tutorial 01, 02, 03 it all becomes an error..

    would you give some suggestion on how to do this?

    Thanks in advance!

      1. when i try to compile, it becomes like this:

        pi@raspberrypi:~ $ gcc -o gyro_accelerometer_tutorial01 -l rt gyro_accelerometer_tutorial01.c -lm
        gcc: error: gyro_accelerometer_tutorial01.c: No such file or directory

      2. Im sorry. but the previous error was not what i meant.
        but this one below comes out.
        would you please help me ?

        In file included from sensor.c:25:0,
        from gyro_accelerometer_tutorial01.c:34:
        LSM9DS0.h:52:19: error: expected ‘)’ before string constant
        sprintf(filename, “/dev/i2c-%d”, 1);
        ^~~~~~~~~~~~~
        LSM9DS0.h:53:1: warning: data definition has no type or storage class
        file = open(filename, O_RDWR);
        ^~~~
        LSM9DS0.h:53:1: warning: type defaults to ‘int’ in declaration of ‘file’ [-Wimplicit-int]
        LSM9DS0.h:53:13: error: ‘filename’ undeclared here (not in a function)
        file = open(filename, O_RDWR);
        ^~~~~~~~
        LSM9DS0.h:54:1: error: expected identifier or ‘(’ before ‘if’
        if (file<0) {
        ^~
        LSM9DS0.h:57:12: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before numeric constant
        }THS_L_M 0x14
        ^~~~
        gyro_accelerometer_tutorial01.c: In function ‘mymillis’:
        gyro_accelerometer_tutorial01.c:57:2: warning: implicit declaration of function ‘gettimeofday’ [-Wimplicit-function-declaration]
        gettimeofday(&tv, NULL);
        ^~~~~~~~~~~~

  37. This post is so helpful that can provide me a method to use some other sensors with Rpi. However, when I use the accelerometer MMA8451, I can only get three constant values for X, Y and Z. And the value of X is always 1 and the others’ are about +/- 31 after multiplied by sensitivity. The raw value of X is -nan and the values of Y and Z are the same as multiplied ones. A similar problem has also come out when I use the LSM9DS0. The raw value of X is -nan.

    I’m now working at a simple navigation system which requires the accelerometer and gyroscope. However, I have many questions about the project. Can you help me?

    Thanks a lot and sorry for my poor expression.

  38. Hi,

    My BerryIMU code isn’t showing any changes in accelerometer data. Each time I run the code, it gives me a constant, unchanging value for ACCx, ACCy, and ACCz (24539 to be exact).
    Is this a problem with the BerryIMU chip or with my setup? What measures can I take to see what’s going wrong here?

  39. I should also mention that the gyro and magnetometer both work fine, and the accelerometer is the only part I’m not getting an output from.
    If it helps, AccXangle and AccYangle both show -45 and 45, respectively.

  40. I am completely new to everything, I have been following the instructions up to having to write al the code, “Open I2C Bus”, where am I suppose to write it into, am I to create a nano py file? if so what do I name them.

    Thanks

  41. Great guide however I had a question. When calculating the Accelerometer angles, why did you not multiply the raw values by the sensitivity level like you did for the gyroscope?

    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.