Raspberry Pi BerryIMU

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.

BerryIMU Raspberry Pi Gyroscope Accelerometer

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.

LSM9DS1 BerryIMU

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):
        bus.write_byte_data(ACC_ADDRESS , register, value)
        return -1


def readACCx():
        acc_l = bus.read_byte_data(ACC_ADDRESS, OUT_X_L_A)
        acc_h = bus.read_byte_data(ACC_ADDRESS, OUT_X_H_A)
	acc_combined = (acc_l | acc_h <<8)
	return acc_combined  if acc_combined < 32768 else acc_combined - 65536

def readACCy():
        acc_l = bus.read_byte_data(ACC_ADDRESS, OUT_Y_L_A)
        acc_h = bus.read_byte_data(ACC_ADDRESS, OUT_Y_H_A)
	acc_combined = (acc_l | acc_h <<8)
	return acc_combined  if acc_combined < 32768 else acc_combined - 65536

def readACCz():
        acc_l = bus.read_byte_data(ACC_ADDRESS, OUT_Z_L_A)
        acc_h = bus.read_byte_data(ACC_ADDRESS, OUT_Z_H_A)
	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
	ACCx = readACCx()
	ACCy = readACCy()
	ACCz = readACCz()
	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)

 

Guides and Tutorials

34 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[0] = *mag_raw;
      medianTable1.y[0] = *(mag_raw+1);
      medianTable1.z[0] = *(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. 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. 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. 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.

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

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

  6. can you please explain why we are shifting value 8 bits left and then adding it.Below lines
    def readACCx():
    acc_l = bus.read_byte_data(ACC_ADDRESS, OUT_X_L_A)
    acc_h = bus.read_byte_data(ACC_ADDRESS, OUT_X_H_A)
    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.

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

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

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

  10. 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 ??
    **********************************************************

  11. 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
    …(likewise 8-9 readings)
    4058 290 1679
    this happens after every 10-12 readings
    Can you tell me why is this happening?

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

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

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

  15. Hi,
    Great explanation, thanks.
    The point I wonder, is there any way to calculate resting values such as, 16500 for the ±2g. My sensor also gives the same value, but I don’t understand the logic behind it.

    Thanks!

Leave a Reply

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

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