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

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

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.
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;
- An array of 6 bytes is first created to store the values.
- The I2C slave address is selected for the LSM6DSL(Accel & gyro), by passing the address of LSM6DSL_ADDRESS or 0x6a to the selectDevice() function.
- 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.
- 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
Run the Program
Guides and Tutorials
- Guide to interfacing a Gyro and Accelerometer with a Raspberry Pi - Kalman Filter
- Create a Digital Compass with the Raspberry Pi – Part 1 – “The Basics”
- Create a Digital Compass with the Raspberry Pi – Part 2 – “Tilt Compensation”
- Create a Digital Compass with the Raspberry Pi – Part 3 – “Calibration”
- Create a Digital Compass with the Raspberry Pi – Part 4- “Smartphone Replica”
- Converting values from an Accelerometer to Gs – “ Proper Acceleration”
- Using the BerryIMUv3 on a Raspberry Pi Pico
- How to Create an Inclinometer using a Raspberry Pi and an IMU
- Raspberry Pi Digital Spirit Level
- Double tap detection with BerryIMUv3
- Connect BerryIMUv3 via SPI
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?
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.
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
well, that was easy to fix.
#define GYR_ADDRESS 0x6B
was needed for my device.
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?
No… change it in LSM9DS0.h then compile
Have or are you guys working on a Python implementation of this?
We have just released some python code. See here for more details: https://ozzmaker.com/python-code-for-berryimu-accelerometer-gyroscope-magnetometer-pressure-sensor/
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?
you can check the i2c addresses of connected devices by using
sudo i2cdetect -y 1
what addresses do you have?
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!
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?
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.
This is to do with the seven bit nature of i2c
Have a look at ‘6.1.1 i2c operations’ in the datasheet, this is explained well;
https://ozzmaker.com/wp-content/uploads/2014/12/LSM9DS0.pdf
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!
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.
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
just redirect the output to a file
sudo ./gyro_accelerometer_tutorial01 > textfile.csv
The code is C and Python
you need to run it with sudo
sudo ./gyro_accelerometer_tutorial01
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.
Does it work with the example C code?
Can someone please what is ” ioctl(file,I2C_SLAVE,addr) ” acctually doing.
Is it putting I2C_slave=addr or what?
because here
http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/include/linux/i2c-dev.h?v=2.6.25#L41
I2C_SLAVE is defined as 0x0703, but that need not be the address.
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.
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!
This article explains the code at http://github.com/ozzmaker/BerryIMU.git
If you just want the angles, pull down the code, compile and then run
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!
Do you see the IMU?
What do you get when you do
sudo i2cdetect -y 1
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!!!
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
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?
that was done using David’s code;
https://github.com/DavidEGrayson/minimu9-ahrs
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?
This is the actual code that does the displaying, you need to pipe the output of the above code into this one; https://github.com/DavidEGrayson/ahrs-visualizer.
Have a look in asset folder in the above link. It contains the image files you can maybe re-size.
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.
I had to link libstdc++ in the .c file, if that helps..
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!!
Having the same problem with increasing values only on the Gyroscope readings
The is the expected behavior of the gyro. All gyros exhibit this.
This is caused by noise.
From the guide;
This is why both values need to be fused together to get an accurate result.
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
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);
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
Are you using a BerryIMU?
What is the output of
sudo i2cdetect -y 1
?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: — — — — — — — —
edit LSM9DS0.py and change the address values to match your device.
it works now perfect ,using both c and or py. Thanks a lot!!
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
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))
How did you find the main loop duration period (DT) ??
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
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.
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!
Have you done everything on this page; https://ozzmaker.com/i2c/
I think you are missing
sudo apt install libi2c-dev
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
For the first question, you are correct.
without typcasting, we would lose the bits… you are correct here
(int16_t)(block[0] | block[1] << 8)
Notice the parentheses around both blocks.
My understanding is that the order of precedence will typecast both values first;http://en.cppreference.com/w/c/language/operator_precedence
Oh thanks for your reply!
the key for understanding this is ‘Operator precedence’
Best Regards!
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.
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.
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.
What are the units for the raw accelerometer output? It doesn’t appear to be Gs or m/s/s
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
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!
Nevermind, found the answer here: https://github.com/mwilliams03/BerryIMU/issues/1
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.
This is the noise from the gyro. All gyroscopes have noise, this why the two sensors need to be fused together.
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.
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?
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…
hello mark,
What about the z angle for accmeter ??
Regards
Kiran
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 .
Thanks for the quick reply !!
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
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.
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
That would work if your IMU is flat.
If it is going to be tilted, then you need to use tilt compensation on the compass;https://github.com/mwilliams03/BerryIMU/blob/master/compass_tutorial02_tilt_compensation/compass_tutorial02.c
Thank you for the clarification.
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 ?
Hey mark, why not convert the mg in g as we did in the gyroscope that convert the mdps in dps?
Thanks.
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?
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!
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
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.
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.
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?
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
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.
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?
is berry imu can connect into 32 servo controller?..
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
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!
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
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.
Thank you! I see it now, that makes perfect sense.
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??
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!
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!!
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!
What is the error?
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
you need to make sure you are in the directory where the files are
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);
^~~~~~~~~~~~
Have you modified any of the files? can you try and re download from git again.
Thanks!
one more question.
how do i save these files in csv?
or are they already in csv?
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.
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?
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.
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
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