5

I am attempting to calculate displacement using the accelerometer on an Android device (Sensor.TYPE_LINEAR_ACCELERATION). Here is my OnSensorChanged() method:

public void onSensorChanged(SensorEvent event) {
    accelX = event.values[0];
    accelY = event.values[1];
    accelZ = event.values[2];

    long currentTime = System.currentTimeMillis() / 1000;

    if(prevTime == 0) prevTime = currentTime;

    long interval = currentTime - prevTime;
    prevTime = currentTime;     

    velX += accelX * interval;
    velY += accelY * interval;
    velZ += accelZ * interval;

    distX += prevVelX + velX * interval;
    distY += prevVelY + velY * interval;
    distZ += prevVelZ + velZ * interval;

    prevAccelX = accelX;        
    prevAccelY = accelY;        
    prevAccelZ = accelZ;

    prevVelX = velX;
    prevVelY = velY;
    prevVelZ = velZ;
}

Velocity, however, doesn't return to 0 after any sort of movement. Deceleration has little effect unless it is very sudden. This leads to some very inaccurate distance values.

The accelerometer is also very noisy, so I added this:

accelX = (accelX * accelKFactor) + prevAccelX * (1 - accelKFactor);
accelY = (accelY * accelKFactor) + prevAccelY * (1 - accelKFactor);
accelY = (accelX * accelKFactor) + prevAccelY * (1 - accelKFactor);

Where accelKFactor = .01

Am I missing something obvious?

My end goal is simply to be able to tell if the device has moved more than ~10 ft or not, but right now, my data is pretty much useless.

EDIT:

I found part of the problem. currentTime was a long, but I was dividing the system time in ms by 1000 to get seconds. So velocity could only really update every 1s. Changing currentTime to a double helps, but does not entirely solve the problem.

JohannB
  • 357
  • 6
  • 21
  • Unfortunately, tracking position solely by double integration of acceleration just isn't practical. Even very expensive commercial inertial navigation systems drift about a foot per second. The commodity sensors in your phone will be much worse. A possible duplicate of your question is [here](http://stackoverflow.com/questions/7829097/android-accelerometer-accuracy-inertial-navigation). – rhashimoto Jun 02 '15 at 19:21
  • Thanks for the comment @rhashimoto. I understand what you're saying, but again, my goal is only to be able to decide if a device has moved more than 10 ft. Thus, I don't need a whole lot of accuracy. I'm just hoping to make some sort of sense out of the accel data. – JohannB Jun 03 '15 at 17:09
  • I don't think you'll be able to tell that the device *hasn't* moved 10 feet, i.e. it will always think it is zooming off somewhere. But maybe you'll come up with something clever. The breakthroughs are made by people who ignore what they are told is "impossible" :-). – rhashimoto Jun 03 '15 at 17:18

2 Answers2

1

SensorEvent has a timestamp field, which is the time in nanoseconds. Use this instead of System.currentTimeMillis(). Here's an example from the documentation:

private static final float NS2S = 1.0f / 1000000000.0f;
 private float timestamp;

 public void onSensorChanged(SensorEvent event) {
      // This timestep's delta rotation to be multiplied by the current rotation
      // after computing it from the gyro sample data.
      if (timestamp != 0) {
          final float dT = (event.timestamp - timestamp) * NS2S;

Tracking displacement this way is extremely sensitive to initial conditions, so this should help.

Also, you may have better luck with Location.getSpeed(). Consider using the accelerometer only as a backup if you can't get a signal.

bleezy
  • 26
  • 7
  • Ah, nanosecond resolution is much more helpful. The data is still far from perfect, but I accepted this answer because I'm not sure how much better I can get. I'll keep playing around with it for a while though. – JohannB Jun 03 '15 at 17:12
  • Also, I agree that Location.getSpeed() is a good idea, but as I mentioned in my question, I need at least < 10 ft resolution. I'm not convinced Gps can always give me that. – JohannB Jun 03 '15 at 17:15
0

Please check the below code

public class MainActivity extends Activity implements SensorEventListener {
    private SensorManager sensorManager;
    float appliedAcceleration = 0;
    float currentAcceleration = 0;
    float velocity = 0;
    Date lastUpdatedate;
    double calibration = Double.NaN;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        lastUpdatedate = new Date(System.currentTimeMillis());
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        double x = event.values[0];
        double y = event.values[1];
        double z = event.values[2];
        double a = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
        if (calibration == Double.NaN)
            calibration = a;
        else {
            Date timeNow = new Date(System.currentTimeMillis());
            long timeDelta = timeNow.getTime()-lastUpdatedate.getTime();
            lastUpdatedate.setTime(timeNow.getTime());
            float deltaVelocity = appliedAcceleration * (timeDelta/1000);
            appliedAcceleration = currentAcceleration;
            velocity += deltaVelocity;
            final double mph = (Math.round(100*velocity / 1.6 * 3.6))/100;
            Log.i("SensorTestActivity","SPEEDDDDD=== "+mph+"     "+velocity);
            currentAcceleration = (float)a;
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    @Override
    protected void onStart() {
        super.onStart();
        sensorManager.registerListener(this,
                sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onStop() {
        super.onStop();
        sensorManager.unregisterListener(this);
    }
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
gpuser
  • 1,143
  • 1
  • 9
  • 6