39

I need suggestion about how to detect the amount of movement of an android device. Suppose I have put the phone on a table or bed and then if somebody taps the table or sits or laydown on the bed then I want to detect the movement of the android device.

Actually I know that android has motion sensors APIs but I don't know which sensor to use and what sensor type is best for this type of movement detection.

I would be glad if someone can share some basic demo code.

Surjya Narayana Padhi
  • 7,741
  • 25
  • 81
  • 130
  • 1
    Use the accelerometer (acceleration, movement) and magnometer (compass). Perhaps the lux sensor and proxmity sensor if you're really curious. – Mgamerz Jan 29 '13 at 02:54

6 Answers6

68

Definitely work with the accelerometer:

// Start with some variables
private SensorManager sensorMan;
private Sensor accelerometer;

private float[] mGravity;
private float mAccel;
private float mAccelCurrent;
private float mAccelLast;

// In onCreate method
sensorMan = (SensorManager)getSystemService(SENSOR_SERVICE);
accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mAccel = 0.00f;
mAccelCurrent = SensorManager.GRAVITY_EARTH;
mAccelLast = SensorManager.GRAVITY_EARTH;

// And these:

@Override
public void onResume() {
    super.onResume();
    sensorMan.registerListener(this, accelerometer,
        SensorManager.SENSOR_DELAY_UI);
}

@Override
protected void onPause() {
    super.onPause();
    sensorMan.unregisterListener(this);
}

@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
        mGravity = event.values.clone();
        // Shake detection
        float x = mGravity[0];
        float y = mGravity[1];
        float z = mGravity[2];
        mAccelLast = mAccelCurrent;
        mAccelCurrent = FloatMath.sqrt(x*x + y*y + z*z);
        float delta = mAccelCurrent - mAccelLast;
        mAccel = mAccel * 0.9f + delta;
            // Make this higher or lower according to how much
            // motion you want to detect
        if(mAccel > 3){ 
        // do something
        }
    }

}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // required method
}
anthropomo
  • 4,100
  • 1
  • 24
  • 39
  • 4
    This is not working when I move smoothly. It works only when I do a shake.. How to do for a smooth movement ?? and also when the phone is tilted left or right, it is not working – Gaurav Arora May 15 '13 at 12:41
  • 8
    @BornToWin This particular code snippet is designed to detect a "shake," so a relatively large/fast movement. Take a look at the line `if(mAccel > 3)`. If you decrease that number to 1 or 0.5 or 0.25 or lower, you can "do something" at a lower threshold. If you want to detect tilt, google "Android detect tilt." It is related, but different. – anthropomo May 16 '13 at 00:42
  • How to detect tilt, like in games ? – Gaurav Arora May 16 '13 at 04:49
  • 1
    It's working but doesn't detect flat on the table movements correctly. Also to get it to work I needed to implement an HandlerThread as last parameter at registerListener. – Codebeat May 22 '15 at 19:14
  • 2
    Remember that for API > 23 you have to use ```(float)Math.sqrt(x*x + y*y + z*z);``` to calculate the acceleration. – Paolo Rotolo Sep 23 '16 at 07:11
  • But does the above usage of acceleroemeter save power (and will get the device into sleep when there is no movement) ? according to google documentation, only 'significant motion' sensor is power save and can used with to wakeup from sleep ? – ransh Jan 25 '17 at 06:14
  • 5
    can you explain the formula mAccel = mAccel *0.9f + delta? and what units does the 3 in 'if (mAccel > 3)' correspond to?Im sorry I am new to working with accelerometers – Kunal Shah Feb 02 '18 at 17:27
  • If you want to use for smooth movements, you can use if (delta > 0.5){...} – Dawit Abraham Sep 22 '19 at 11:22
19

I used the following class:

public class MovementDetector implements SensorEventListener {

protected final String TAG = getClass().getSimpleName();

private SensorManager sensorMan;
private Sensor accelerometer;

private MovementDetector() {
}

private static MovementDetector mInstance;

public static MovementDetector getInstance() {
    if (mInstance == null) {
        mInstance = new MovementDetector();
        mInstance.init();
    }
    return mInstance;
}

//////////////////////
private HashSet<Listener> mListeners = new HashSet<MovementDetector.Listener>();

private void init() {
    sensorMan = (SensorManager) GlobalData.getInstance().getContext().getSystemService(Context.SENSOR_SERVICE);
    accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
}

public void start() {
    sensorMan.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}

public void stop() {
    sensorMan.unregisterListener(this);
}

public void addListener(Listener listener) {
    mListeners.add(listener);
}

/* (non-Javadoc)
 * @see android.hardware.SensorEventListener#onSensorChanged(android.hardware.SensorEvent)
 */
@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {

        float x = event.values[0];
        float y = event.values[1];
        float z = event.values[2];

        float diff = (float) Math.sqrt(x * x + y * y + z * z);
        if (diff > 0.5) // 0.5 is a threshold, you can test it and change it
            Log.d(TAG,"Device motion detected!!!!");
        for (Listener listener : mListeners) {
            listener.onMotionDetected(event, diff);
        }
    }

}

/* (non-Javadoc)
 * @see android.hardware.SensorEventListener#onAccuracyChanged(android.hardware.Sensor, int)
 */
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // TODO Auto-generated method stub

}

public interface Listener {
    void onMotionDetected(SensorEvent event, float acceleration);
    }
}

Usage:

On my activity onCrate():

        MovementDetector.getInstance().addListener(new MovementDetector.Listener() {

        @Override
        public void onMotionDetected(SensorEvent event, float acceleration) {

            mMotionDetectionTextView.setText("Acceleration: ["+String.format("%.3f",event.values[0])+","+String.format("%.3f",event.values[1])+","+String.format("%.3f",event.values[2])+"] "+String.format("%.3f", acceleration));
            if (acceleration > SettingsHelper.getInstance().getMotionDetectionThreshold()){
                mMotionDetectionTextView.setTextColor(Color.RED);
            } else {
                mMotionDetectionTextView.setTextColor(Color.WHITE);
            }

        }
    });

On my activity onResume():

MovementDetector.getInstance().start();

On my activity onPause():

MovementDetector.getInstance().stop();
Asaf Pinhassi
  • 15,177
  • 12
  • 106
  • 130
  • what is `GlobalData` ? – Choletski Sep 11 '15 at 09:28
  • An object I created that holds application context instance. Just use a context. – Asaf Pinhassi Sep 11 '15 at 14:50
  • But does the above usage of acceleroemeter save power (and will get the device into sleep when there is no movement) ? according to google documentation, only 'significant motion' sensor is power save and can used with to wakeup from sleep. – ransh Jan 25 '17 at 06:15
  • @ransh It depends what you want to do. Here you just register to the acceleroemeter output, it has nothing to do with power save. You register/unregister from the acceleroemeter at the activity onPause/onResume. The application is not in the foreground anyway when the device is asleep. The "Significant Motion Sensor" is theoretically a different sensor for other purposes (I won't use it in an augmented reality app for example). – Asaf Pinhassi Jan 25 '17 at 07:07
  • @Pinhassi, thanks, yet from my reading it seems that accelerometer arew related to power save. It was mentioned they use wakelocks - and that's prevent power save. – ransh Jan 25 '17 at 08:39
  • @ransh You might be right - this post is 2 years old... Feel free to improve it :) – Asaf Pinhassi Jan 25 '17 at 09:05
  • Thanks for the code. It's really helpful and can be used to serve many purposes. – Ishaan Feb 15 '17 at 12:20
  • If you are moving the device when everything starts up, event.values[0], [1], [2] are not set to 0. They are set to what ever value at the time of initialization. Is there a way to rest the values back to 0? – DDukesterman Jun 18 '19 at 21:39
6

This code is for walking detection (Modified from @anthropomo code)

to get smoother value.

// initialize

private SensorManager sensorMan;
private Sensor accelerometer;

private float[] mGravity;
private double mAccel;
private double mAccelCurrent;
private double mAccelLast;

private boolean sensorRegistered = false;

// onCreate

    sensorMan = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
    accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    mAccel = 0.00f;
    mAccelCurrent = SensorManager.GRAVITY_EARTH;
    mAccelLast = SensorManager.GRAVITY_EARTH;

    sensorMan.registerListener(this, accelerometer,
            SensorManager.SENSOR_DELAY_NORMAL);
    sensorRegistered = true;

// onSensorChanged

private int hitCount = 0;
private double hitSum = 0;
private double hitResult = 0;

private final int SAMPLE_SIZE = 50; // change this sample size as you want, higher is more precise but slow measure.
private final double THRESHOLD = 0.2; // change this threshold as you want, higher is more spike movement

@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
        mGravity = event.values.clone();
        // Shake detection
        double x = mGravity[0];
        double y = mGravity[1];
        double z = mGravity[2];
        mAccelLast = mAccelCurrent;
        mAccelCurrent = Math.sqrt(x * x + y * y + z * z);
        double delta = mAccelCurrent - mAccelLast;
        mAccel = mAccel * 0.9f + delta;

        if (hitCount <= SAMPLE_SIZE) {
            hitCount++;
            hitSum += Math.abs(mAccel);
        } else {
            hitResult = hitSum / SAMPLE_SIZE;

            Log.d(TAG, String.valueOf(hitResult));

            if (hitResult > THRESHOLD) {
                Log.d(TAG, "Walking");
            } else {
                Log.d(TAG, "Stop Walking");
            }

            hitCount = 0;
            hitSum = 0;
            hitResult = 0;
        }
    }
}
P.Zephyrous
  • 139
  • 1
  • 2
  • 7
  • 3
    I don't get why this is multiplied by 0.9: mAccel = mAccel * 0.9f + delta. What is the usage of mAccel? – Lechucico Feb 28 '18 at 21:53
1

I have been working with a similar idea to measure the displacement of the phone. I have found that the LINEAR ACCELERATION (and ACCELERATION) are not accurate enough to correctly measure the displacement.

This code should work a little better:

(ititialize)

private SensorManager sensorManager;
private Sensor accelerometer;
double[] maxAccelerations = new double[3];
double[] position = new double[3];
long[] times = new long[3];
// time combined with maxAcceleration can approximate the change in position,
// with the formula Δpos = (maxAcceleration * time ^ 2) / 6
long currentTime;

(onCreate)

sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION) != null) {
    accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
        sensorManager.registerListener(this, accelerometer, sensorManager.SENSOR_DELAY_FASTEST);
}
currentTime = System.currentTimeMillis();
for(int i=0;i<3;i++){
    times[i]=currentTime;
}
else{
    throw "Error";
    //Which will throw an error, if not the error that is expected. 
}

(onSensorChanged and onAccuracyChanged)

@Override
public void onAccuracyChanged(Sensor ignore, int thisFunction) {
}

@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
        for(int i=0;i<3;i++){
            if(Math.abs(event.values[i])<0.01){
                // Note: this is to try to prevent accelerating time from being counted when the phone is stationary. 0.01 should be
                // changed to an appropriate sensitivity level that can be calculated by finding an average noise level when the phone is stationary.
                times[i]=System.currentTimeMillis();
            }
            if(event.values[i]>maxAccelerations[i] && maxAccelerations[i]>=0){
                maxAccelerations[i]=event.values[i];
            }
            else if(event.values[i]<maxAccelerations[i] && maxAccelerations[i]<=0){
                maxAccelerations[i]=event.values[i];
            }
            else if(event.values[i]>0 && maxAccelerations[i]<0){
                currentTime = System.currentTimeMillis();
                position[i]+=maxAccelerations[i] * (times[i]-currentTime)*(times[i]-currentTime) / 6;
                times[i]=currentTime;
                maxAccelerations[i]=event.values[i];
            }
            else if(event.values[i]<0 && maxAccelerations[i]>0){
                currentTime = System.currentTimeMillis();
                position[i]+=maxAccelerations[i] * (times[i]-currentTime)*(times[i]-currentTime) / 6;
                times[i]=currentTime;
                maxAccelerations[i]=event.values[i];
            }
        }
    }
}
0

While I don't have demo code (since you aren't specific enough), a good start is here: http://developer.android.com/guide/topics/sensors/sensors_motion.html (and other items on the left).

Mgamerz
  • 2,872
  • 2
  • 27
  • 48
0

if you are trying to find the displacement of your phone, you need to find the Linear acceleration acting on your phone rather than the acceleration due to gravity

android has a built in converter to find the LINEAR ACCELERATION acting on your mobile phone

https://github.com/yuvaramsingh94/AndroidSensorTestCode/tree/master

this is a code where you can see how to get the raw value of LINEAR ACCELERATION