How to properly calculate linear acceleration using AcceleroMeter in Android

Learn how to properly calculate linear acceleration using accelerometer in android with practical examples, diagrams, and best practices. Covers android, android-sensors development techniques with...

Calculating Linear Acceleration with Android Accelerometer

Hero image for How to properly calculate linear acceleration using AcceleroMeter in Android

Learn how to accurately derive linear acceleration from raw accelerometer data on Android devices, accounting for gravity and sensor noise.

Android devices come equipped with various sensors, including accelerometers, which measure the acceleration applied to the device. While raw accelerometer data provides the total acceleration (including gravity), many applications require linear acceleration – the acceleration due to motion, excluding the constant pull of gravity. This article will guide you through the process of properly calculating linear acceleration using the Android SensorManager and filtering out gravitational forces.

Understanding Accelerometer Data

The TYPE_ACCELEROMETER sensor in Android reports the sum of the acceleration of the device (m/s²) along the X, Y, and Z axes, including the force of gravity. Gravity itself is a form of acceleration, typically around 9.81 m/s² downwards. When the device is at rest on a flat surface, the accelerometer will report approximately [0, 0, 9.81] m/s² (assuming Z is upwards) or [0, 0, -9.81] m/s² (assuming Z is downwards), not [0, 0, 0]. To get the true linear acceleration, we must subtract the gravitational component from the raw sensor readings.

flowchart TD
    A[Raw Accelerometer Data] --> B{Apply Low-Pass Filter for Gravity}
    B --> C[Estimate Gravity Vector]
    C --> D{Subtract Gravity from Raw Data}
    D --> E[Linear Acceleration]

Process for deriving linear acceleration from raw accelerometer data.

Implementing a Low-Pass Filter for Gravity

To isolate the gravity component, a common technique is to use a low-pass filter. A low-pass filter allows low-frequency signals (like gravity, which changes slowly) to pass through while attenuating high-frequency signals (like sudden movements). The Android documentation suggests a simple exponential moving average filter for this purpose. We maintain an estimate of the gravity vector and update it with each new accelerometer reading.

public class LinearAccelerationSensor implements SensorEventListener {

    private static final float ALPHA = 0.8f; // Constant for the low-pass filter
    private float[] gravity = new float[3];
    private float[] linearAcceleration = new float[3];

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            // Isolate the force of gravity with the low-pass filter.
            gravity[0] = ALPHA * gravity[0] + (1 - ALPHA) * event.values[0];
            gravity[1] = ALPHA * gravity[1] + (1 - ALPHA) * event.values[1];
            gravity[2] = ALPHA * gravity[2] + (1 - ALPHA) * event.values[2];

            // Remove the gravity contribution with the high-pass filter.
            linearAcceleration[0] = event.values[0] - gravity[0];
            linearAcceleration[1] = event.values[1] - gravity[1];
            linearAcceleration[2] = event.values[2] - gravity[2];

            // Now 'linearAcceleration' contains the device's acceleration due to motion.
            // You can use these values for your application logic.
            // Log.d("LinearAccel", String.format("X: %.2f, Y: %.2f, Z: %.2f",
            //         linearAcceleration[0], linearAcceleration[1], linearAcceleration[2]));
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
    }

    public float[] getLinearAcceleration() {
        return linearAcceleration;
    }
}

Java code for a SensorEventListener that calculates linear acceleration.

Registering and Unregistering the Sensor Listener

To use the LinearAccelerationSensor class, you need to register it with the Android SensorManager when your activity or service starts and unregister it when it pauses or stops to conserve battery and system resources. This is typically done in the onResume() and onPause() lifecycle methods of an Activity.

public class MainActivity extends AppCompatActivity {

    private SensorManager sensorManager;
    private Sensor accelerometer;
    private LinearAccelerationSensor linearAccelerationSensor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        if (sensorManager != null) {
            accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            if (accelerometer == null) {
                // Device does not have an accelerometer
                Log.e("Sensor", "Accelerometer not available!");
            } else {
                linearAccelerationSensor = new LinearAccelerationSensor();
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (accelerometer != null) {
            sensorManager.registerListener(linearAccelerationSensor, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (accelerometer != null) {
            sensorManager.unregisterListener(linearAccelerationSensor);
        }
    }
}

Example MainActivity demonstrating sensor registration and unregistration.