 # Converting values from an Accelerometer to G

In this post I will show how to convert the raw values read from an accelerometer to 'Gs'.

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

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

The code for this guide can be found under the python-BerryIMU-measure-G directory.

An accelerometer measures proper acceleration, which is the acceleration it experiences relative to freefall. This is most commonly called "G-Force" (G)

For example, an accelerometer at resting on a table would measure 1G ( 9.81 m/s2) straight upwards. By contrast, accelerometers in free fall and accelerating due to the gravity of Earth will measure zero.

The accelerometer used by the BerryIMU is a MEMS sensors(LSM9DS0), which outputs the raw readings as mg/LSB.
Most MEMS accelerometers use this output format.

mg = milli-G's (just like milliliters)
1mG = 0.001 G's of acceleration, so 1000mG = 1G.
LSB = Least Significant bit, which is the last bit on the right. The LSM9DS0 outputs a 16 bit value for the accelerometer readings.

If you look at the characteristics of the LSM9DS0 in the datasheet, you can see the sensitivity levels for the accelerometer highlighted in red below and the corresponding values for the LSB, which are highlighted in blue. You can download the datasheet here;

The raw values from the accelerometer are  multiplied by the sensitive level to get the value in G. Let's use FS ±2 g as an example sensitivity level.  As the range is -2 to +2, this would be a total of 4g.  Or 4,000 Milli-Gs.
The output is 16 bits. 16 bits equals 65,535.   This means we can get 65,535 different readings for the range  between -2 and +2. (or -2,000 MilliGs and +2,000 MilliGs)

4,000 MilliGs / 65,535 = 0.061

Each time the LSB changes by one, the value changes by 0.061, which is the value highlighted in blue in the table above.

For FS ±8 g, the range would be -8 to +8, which is a total of 16,000 MilliGs.
16,000 MilliGs / 65,535 = 0.244

Example when using ±2g sensitivity
In the table below, every time the raw values increments by one, the final calculated value(which is MilliG) increments by 0.061

```RAW  	BINARY	LSB value for +/-2G	Calc MilliG
16	10000		0.061		0.976
17	10001		0.061		1.037
18	10010		0.061		1.098```

The above values of 16,17 and 18 above a very low and only used for illustration.
If your accelerometer is horizontal and resting and at rest when using a sensitive level of ±2g, the raw value for Z should hover  around 16,500.
16,500 X 0.061 = 1006 MilliGs or 1G

Example when using ±8g sensitivity
In the table below, every time the raw values increments by one, the final calculated value(which is MilliG) increments by 0.244

```RAW  	BINARY	LSB value for +/-2G	Calc MilliG
16	10000		0.244		3.904
17	10001		0.244		4.148
18	10010		0.244		4.392```

If you accelerometer is horizontal and at rest, when using a sensitive level of ±8g, the raw value for Z should hover  around 4,475.

4,175
X 0.244 = 1018.7 MilliGs or 1G

# The Code

The two above examples are easy to implement in python;
±8g Sensitivity

```writeACC(CTRL_REG2_XM, 0b00010000) #+/- 8G full scale
print("G Value for Z axis %f G" % ((ACCz * 0.244)/1000))
```

The first line above is used to initialise the accelerometer with a sensitivity level of ±2g.
The second line prints the calculated value as G using the raw values from the accelerometer.

±2g Sensitivity

```writeACC(CTRL_REG2_XM, 0b00000000) #+/- 2G full scale
print("G Value for Z axis %f G" % ((ACCz * 0.061)/1000))
```

The first line above is used to initialise the accelerometer with a sensitivity level of ±2g.
The second line prints the calculated value as G uses using raw values from the accelerometer.
Below is a snippet from the main program;

```import smbus
import time
import math
from LSM9DS0 import *
import datetime
bus = smbus.SMBus(1)

def writeACC(register,value):
return -1

acc_combined = (acc_l | acc_h <<8)

return acc_combined  if acc_combined < 32768 else acc_combined - 65536

acc_combined = (acc_l | acc_h <<8)

return acc_combined  if acc_combined < 32768 else acc_combined - 65536

acc_combined = (acc_l | acc_h <<8)

return acc_combined  if acc_combined < 32768 else acc_combined - 65536

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

while True:

#Read the accelerometer,gyroscope and magnetometer values

print("##### X = %f G  #####" % ((ACCx * 0.244)/1000)),
print(" Y =   %fG  #####" % ((ACCy * 0.244)/1000)),
print(" Z =  %fG  #####" % ((ACCz * 0.244)/1000))

#slow program down a bit, makes the output more readable
time.sleep(0.03)
```

## 32 thoughts on “Converting values from an Accelerometer to G”

1. What filter would be appropriate for the accelerometer data? It doesn't drift much, and at most is slightly offset(from mounting) which can easy be corrected with calibration. But the signal is quite noisy. I was thinking of using a low pass filter or a median filter. Are there more suitable alternatives or a worked example?

1. Both filters would work and would do a good job.
Note that both would add a delay, which is in most cases negligible.
-A median filter would have a constant delay. It is relative to the table size
-A low pass filter would have a variable delay. It is relative to how much noise there is and you filter factor

I have code for two of them below. It is being used for compass, but you can just change it to the accelerometer values.

`//Low Pass float kLowPassFilterFactor = 0.1; float kHighPassFilterFactor = 0.1;`

`*mag_raw = *mag_raw * kLowPassFilterFactor + oldXMagRawValue*(1 - kLowPassFilterFactor); *(mag_raw+1) = *(mag_raw+1) * kLowPassFilterFactor + oldYMagRawValue*(1 - kLowPassFilterFactor); *(mag_raw+2) = *(mag_raw+2) * kLowPassFilterFactor + oldZMagRawValue*(1 - kLowPassFilterFactor);`

`oldXMagRawValue = *mag_raw; oldYMagRawValue = *(mag_raw+1); oldZMagRawValue = *(mag_raw+2);`

`//Median #define MEDIANTABLESIZE 3`

` for (f = MEDIANTABLESIZE-1; f > 0 ; f--){ medianTable1.x[f] = medianTable1.x[f-1]; medianTable1.y[f] = medianTable1.y[f-1]; medianTable1.z[f] = medianTable1.z[f-1]; } medianTable1.x = *mag_raw; medianTable1.y = *(mag_raw+1); medianTable1.z = *(mag_raw+2);`

` medianTable2 = medianTable1;`

` qsort(medianTable2.x, MEDIANTABLESIZE, sizeof(int), cmpfunc); qsort(medianTable2.y, MEDIANTABLESIZE, sizeof(int), cmpfunc); qsort(medianTable2.z, MEDIANTABLESIZE, sizeof(int), cmpfunc);`

` *mag_raw = medianTable2.x[(int)MEDIANTABLESIZE/2]; *(mag_raw+1) = medianTable2.y[(int)MEDIANTABLESIZE/2]; *(mag_raw+2) = medianTable2.z[(int)MEDIANTABLESIZE/2];`

`int cmpfunc (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); }`

2. izaq09 says:

Hi,

Thank you very much for your guide. It has provided me a lot of help.

For ±16 g, 32,000 MilliGs / 65,535 = 0.488, which is not same as the linear acceleration sensitivity (0.732) in the datasheet. In this case, which one should we use? The one obtained through calculation or the sensitivity in datasheet?

In fact, I had found something similar in your guide in interfacing gyroscope and accelerometer. In that case, you are using sensitivity.
https://ozzmaker.com/berryimu/

Thank you.

Regards,
Izaq

1. you have ±16 g = 32,000 MilliG. I should be ±16 g = 16,000 MilliG.

3. N says:

Hey Mark,

How come for ±16 g, it's 16,000 MilliG, when for ±8 g, it's 16,000 MilliG...?

Thanks.
N

1. My comment above was incorrect. I got confused my my own math 🙂

I have to look into to why 0.732 is used for ±16 g.

1. Rory says:

Did you ever discover why 0.732 is used?

2. Yes and No 🙂

I spoke to ST who make the LSM9DS1, they told me that not all 16 bits are used for ±16 G.
I asked how many are used and they told me they couldn't give me this information as it is proprietary.

3. Another way to explain the 0.732 is that the widest range is not ±16 g, but ±24 g !!!

4. Yifan Jia says:

May I ask the fastest frequency of getting the data, I found the sample code can provide data around 100Hz sampling rate, may I increase it to 1000Hz?

Thanks

1. The output data rate is configured using the first 4 bits in the register below;
`writeACC(CTRL_REG1_XM, 0b01100111) #z,y,x axis enabled, continuos update, 100Hz data rate `
0110 = 100Hz

to set it to 1600Hz, sit it to 1010
`writeACC(CTRL_REG1_XM, 0b10100111) #z,y,x axis enabled, continuos update, 100Hz data rate `

This is documented on page 55 and 56 of the datasheet.

1. Yifan says:

Thank you! This is very helpful!

5. Eric Olson says:

In the example code (python-berryIMU-measure-G) the value .224 is used for the 8G scale. This value should be .244 per the table included in this article. Couldn't figure out why I wasn't reading 1G at level with no motion so double-checked code.

That said, great job. You've made the process of deploying one of these units as a vibration sensor in a large scale environment exceptionally easy. Saved me at least fifty hours with your efforts and this product. Thank you very much! 🙂

6. RSC says:

excellent article.. brilliant..

7. Vishal says:

hi, Will u plz help me how to convert X,Y,Z axis raw data of LIS3DH to acceleration and how to detect sudden acceleration and harsh breaking (deacceleration) in car.

8. Will says:

Why are you using "G" and "g" in the beginning? Are they not the same?

9. Payel says:

can you please explain why we are shifting value 8 bits left and then adding it.Below lines
acc_combined = (acc_l | acc_h <<8)

return acc_combined if acc_combined < 32768 else acc_combined - 65536

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.

10. JAMES says:

You said that at resting the raw values should be as below:

+-2g - z = 16500
+-8g - z = 4475

Which is similar to what I have at resting. For x/y, I'm getting the below:

+-2g - x = 65100
+-8g - x = 65420

+-2g - y = 524
+-8g - y = 128

Are these values close to the average, or is my x-values completly wrong?

1. if the IMU is flat.. you should only be getting large values on Z. you may get some small values on X and Y, this would be because the IMU isn't absolutely horizontal.

Your X vales look incorrect. Are you using unmodified code from the git repo?

11. Aaron says:

Thank you sir, your work and time spent on this guide and the IMU/gps boards is appreciated. I have learned a lot. There is enough information here to get started, that is extremely valuable to using something! Seems like people always forget that... anyway, thanks!

12. Rocket Man says:

Is the Max G's of Acceleration a known quantity of what this sensor can detect? I'm trying to use it to collect data in my high powered rocket going supersonic, and I will probably need something that can sense an acceleration of up to 20 G's of force

13. Ravindra kumar says:

how i can detect the orientation and static tilt

14. Ahmed says:

writeACC(CTRL_REG2_XM, 0b00000000) #+/- 2G full scale
print("G Value for Z axis %f G" % ((ACCz * 0.061)/1000))
********************************************************************
Why divide ACCz * 0.061 by 1000 ??
**********************************************************

Hi,
I am using ADXL335 accelerometer, I am getting the values but after some time, the sensor gives maximum value even if it is not moving. For eg,
1678 1987 4056
4058 290 1679
this happens after every 10-12 readings
Can you tell me why is this happening?

16. Hi, I am using LSM303AGR 2G normal mode 10 bit. So each bit will be 4000mg/ 2^10 = 3.90625 is this right?

17. Ben in Seattle says:

How common is it for accelerometer manufacturers to pull tricks like ST did for the ±16G range where they list the LSB as .732 when it should be .488?

The calculation would be simple to understand and wouldn't require a datasheet if we could just scale the range the normal way: multiply by the max possible output (e.g., 4G) and divide by max possible input (e.g., 32768, for 𝑛=16 bits).

f(𝑥) = 𝑥 × rangemax ÷ 2⁽ⁿ¯¹⁾

What is with the LSB malarkey? Is it to deal with the fact that two's complement binary numbers have one less negative number than positive? If so, the difference seems so minimal (<.0015%), that there is more error from the datasheet rounding to three places of precision.

Also, I agree with Noel that the range should have simply read ±24G since 24 ÷ 32768 = .000732421875. I suspect errata.

18. armin says:

Hey there,

which unit does the raw data has? (mg/lsb?)
does that mean that I only have to multiply my raw data which i get from my accelerometer with the corresponding value (e.g: 0.183) to get the maximum acceleration?

greetings, armin

19. Bosley says:

Hi!

I’m looking to purchase this GPS/IMU for a project and I have a minor question. For the python examples listed here I see that they work for the BerryGPS v1-3. Will these also work for the v4? Or is there an updated version of these example scripts for the current iteration of the GPS/IMU?

Thanks!

1. The python code works for BerryIMUv3 and BerryGPS-IMUv4. These two devices use the same IMU.

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