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/mwilliams03/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,475
 X 0.224 = 1002.4 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.224)/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.224)/1000)),
	print(" Y =   %fG  #####" % ((ACCy * 0.224)/1000)),
	print(" Z =  %fG  #####" % ((ACCz * 0.224)/1000))

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

 

6 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.
    http://ozzmaker.com/berryimu/

    Thank you.

    Regards,
    Izaq

Leave a Reply

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