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