# 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.

In this guide 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. Eg  Pololu MinIMU, Adafruit IMU and Sparkfun IMUs

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.

Here is why;
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.

## Setting up the IMU and I2C

The IMU used for this guide  is a BerryIMU which uses a LSM9DS0, which consists of a 3-axis gyroscope, a 3-axis accelerometer and a 3-axis magnetometer.
The datasheet is needed if you want to use this device;LSM9DS0

This IMU communicates via the I2C interface.

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

Or BerryIMU can sit right on top of the GPIO pins on a Raspberry Pi A, B, B+ and A+.   The first 6 GPIOs are used as shown below.

Once connected, 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: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- 6a -- -- -- --
70: -- -- -- -- -- -- -- 77

The gyro  on the LSM9DS0 is using the address of 0x6a and the accelerometer and magnetometer are using 0x1e, this can be verified with page 33 on the datasheet.

Quoted from the datasheet with the address highlighted;
"The SDO/SA0 pins (SDO_XM/SA0_XM or SDO_G/SA0_G) can be used to modify the least significant bit of the device address. If the SA0 pin is connected to the voltage supply, LSb is ‘1’ (ex. address 0011101b) else if SA0 pad is connected to ground, the LSb value is ‘0’ (ex. address 0011110b)."

We will not be covering the magnetometer in this post.

## 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 LSM9DS0  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 or Accelerometer
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, <em>device_address</em>) < 0) {
printf("Error: Could not select magnetometer\n");
}
```

device_address is the address of the sensor you want to select. E.g. 0x6a for the LSM9DS0.
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(CTRL_REG1_XM, 0b01100111); //  z,y,x axis enabled, continuos update,  100Hz data rate
writeAccReg(CTRL_REG2_XM, 0b00100000); // +/- 16G full scale
```

To enable to accelerometer, we need to write a byte to the CTRL_REG1_XM(0x20) register on the LSM9DS0. This can be see on page 55 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_REG2_XM(0x21) register to set our sensitivity. This information is on page 56 of the datasheet.

## Enable the Gyro

Enabling the gyro is very similar to the accelerometer.

``` // Enable Gyro
writeGyrReg(CTRL_REG1_G, 0b00001111); // Normal power mode, all axes enabled
writeGyrReg(CTRL_REG4_G, 0b00110000); // Continuos update, 2000 dps full scale

```

The values for CTRL_REG1_G and CTRL_REG4_G can be found on page 41 and 44 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.

```void readACC(int  *a)
{
uint8_t block[6];

*a = (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 accelerometer, by passing the accelerometer address of ACC_ADDRESS or 0x1E  to the selectDevice() function.
3. Using the readBlock() function from i2c-dev.h, we read 6 bytes starting at OUT_X_L_A (0x28). This is shown on page 61 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

```
{
uint8_t block[6];

*g = (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 13 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.
I have 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

```

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

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

## 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. Paul says:

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

4. Paul says:

well, that was easy to fix.

was needed for my device.

1. AJ says:

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

Yet Paul says his fix was to add:

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

5. Have or are you guys working on a Python implementation of this?

6. Steven says:

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. you can check the i2c addresses of connected devices by using `sudo i2cdetect -y 1`

1. Steven says:

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!

7. Tyler says:

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?

8. Peter says:

Hi,

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

I have one question:

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

1. Peter says:

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

Thanks for the tutorial, clearly explained how to fuse the sensor data in a simple way!

10. Pandrei says:

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.

11. Daniel says:

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

1. just redirect the output to a file
`sudo ./gyro_accelerometer_tutorial01 > textfile.csv`

12. you need to run it with sudo
`sudo ./gyro_accelerometer_tutorial01 `

13. cameron says:

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.

14. Christian says:

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. Christian says:

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
IOError: [Errno 5] Input/output error

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

2. Do you see the IMU?
What do you get when you do `sudo i2cdetect -y 1`

3. Christian says:

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!!!

4. 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

15. Robert says:

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?

16. Robert says:

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?

17. gilles says:

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.

1. Robert says:

I had to link libstdc++ in the .c file, if that helps..

18. AJ says:

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. Abby says:

Having the same problem with increasing values only on the Gyroscope readings

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.

19. Daniel says:

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?

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. Daniel says:

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
IOError: [Errno 5] Input/output error

any suggestion?

thanks again

2. Are you using a BerryIMU?
What is the output of `sudo i2cdetect -y 1`?

3. Daniel says:

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: -- -- -- -- -- -- -- --

20. edit LSM9DS0.py and change the address values to match your device.

1. Daniel says:

it works now perfect ,using both c and or py. Thanks a lot!!

21. Daniel says:

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))`

1. DT can be anything really... it only has to be constant as this value is used later in the code to work out the angle of the gyro

22. Klaus says:

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.

23. Tobias says:

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!

24. Hi,

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

25. Herbert Päffgen says:

the key for understanding this is 'Operator precedence'

Best Regards!

26. lf matyas says:

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.

27. 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.

28. Bryan says:

What are the units for the raw accelerometer output? It doesn't appear to be Gs or m/s/s

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. Bryan says:

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!

29. Bryan says:

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. This is the noise from the gyro. All gyroscopes have noise, this why the two sensors need to be fused together.

1. Bryan says:

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.

30. 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?

31. 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...

32. kiran says:

hello mark,

What about the z angle for accmeter ??

Regards
Kiran

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. Benoit says:

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. Benoit says:

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:
Will I get a stable result of the angle Z with this code?
Regards.
Benoit

4. Benoit says:

Thank you for the clarification.

5. Peter says:

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 ?

33. thanos says:

Hey mark, why not convert the mg in g as we did in the gyroscope that convert the mdps in dps?

Thanks.

34. Raj says:

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?

35. 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
IOError: [Errno 5] Input/output error

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

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

36. 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.

I have also tried....

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.

37. 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.

38. Yasir says:

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. Yasir says:

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?

39. 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

40. Matthew says:

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!

41. Jarred says:

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.

1. Jarred says:

Thank you! I see it now, that makes perfect sense.

42. MiloA says:

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??

43. 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!

44. Sophie says:

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!!

45. Floyd says:

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?

1. Floyd says:

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. you need to make sure you are in the directory where the files are

3. Floyd says:

Im sorry. but the previous error was not what i meant.
but this one below comes out.

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);
^~~~~~~~~~~~

4. Have you modified any of the files? can you try and re download from git again.

5. Floyd says:

Thanks!

one more question.
how do i save these files in csv?
or are they already in csv?

46. Tongmao says:

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.

47. AJ says:

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?

48. AJ says:

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.

49. Jossie says:

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

50. Chuck says:

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

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