How to calculate distance based on phone acceleration

Demian picture Demian · Dec 1, 2010 · Viewed 23.2k times · Source

I want to build something like this but using an android phone: http://www.youtube.com/watch?v=WOt9mb5QqRs

I've already built an app that sends sensor information via socket (still looking for a good websocket implementation for android). I intend to use that information to interact with a web app, so for example i would be able to move an image based on the phone movement. The problem is that I tried to calculate distance based on the accelerometer data but the results are really bad. I wonder if anyone could help me with the correct equation, but first of all, is it possible to do this?

Till now I'm using the following equations:

velocity = acceleration * time;

distance = velocity * time + (acceleration * time^2) / 2;

then I translate distance from meters per second to pixels based on monitor screen resolution.

that's calculated with javascript in the browser every time i receive sensor data, which is every ~80ms.

Answer

slebetman picture slebetman · Dec 1, 2010

The basics is simple. In the analog world you use continuous math which is:

velocity = integrate(acceleration)
distance = integrate(velocity)

and in the digital world it is even easier, you use discrete math where integration becomes summation:

velocity = sum(acceleration)
distance = sum(velocity)

Just keep adding up all the values of acceleration you read and you eventually get distance.

The big problem with this is that on planet Earth there is a constant acceleration downwards of approximately 10m/s/s due to gravity. Figuring out which part of your vector is gravity is the hard part.

BTW, gravity is how accelerometers can detect tilt. So however you do it, unless you can calculate the tilt independently of the accelerometers (for example with the help of gyros) your code will mostly be measuring tilt instead of distance.


HA! I just realized from my last statement that a lot of iPhone apps won't work in space :-P


Additional answer:

Based on a "comment" posted by the OP (as an answer either below or above this answer) it looks like I need to provide further explanation. The implementation is really-really simple that people not familiar with the maths will think it must be more complicated than that. The pseudocode is as follows:

// Set distance to zero at start-up:
var distance_X = 0
var velocity_X = 0

function update_acceleration_X (acceleration_X) {
    velocity_X = velocity_X + acceleration_X
    distance_X = distance_X + velocity_X
}

// To use the distance value just read the distance_X variable:
function get_distance_X_and_reset () {
    x = distance_X
    distance_X = 0
    return x
}

Distance is always measured from where the software first starts unless you reset the distance variable to zero. The accelerometer must constantly be read (preferably at the rate the accelerometer itself measures forces) and the values of velocity and distance updated accordingly. When you want to know the distance from the starting point just read the distance variable.

Several things: any amount of tilt, no matter how slight, will add drift. Meaning that there will always be a small amount of constant acceleration in one direction or the other unless the angle of tilt itself is constantly tracked. Even nuclear submarines, equipped with high precision accelerometers and gyros because GPS doesn't work under water, need to periodically surface and sync with GPS to correct this drift.

Second, the accelerometer measures force, not movement. Any kind of force is measured. I mentioned gravity but it also measures bumps caused by friction with a table, your pulse as your heartbeat and breathing cause your hand to shake slightly, anything. The good news is that over the long run all these forces will average out and the formula will still be correct. But in the short run it means your reading is going to be noisy. There are a lot of tricks people have come up with to minimize this noise using things like Weiner and Kalman filters.

Third, as you may have noticed, the accelerometer reading is not constant. I don't simply mean that the values are different each time you read them, that is obvious, but it also changes values in-between readings. Every value we miss affects our accuracy so it is important to read the values as often as possible. Now, the good news is that in the long run all these errors caused by missing values should average out as they are mostly caused by jerky movements or vibrations and our formula is again still correct. But it means again that in the short run this adds noise to our system. If you use a good perdictive filter like a Kalman filter then it should be able to account for this but weaker filters may need some help. One way of doing this is to average out each acceleration reading with the previous reading. Note that it must be the previous "real" reading, not the previous averaged reading.

More accuracy than this goes into the realm of Inertial Measurement Units (IMU) and inertial guidance and a lot of fairly hairy vector and matrix maths. There are open source projects doing this though (less than 10 years ago this stuff was strictly military since, you know, submarines and cruise missiles use them).

These Sparkfun articles have some nice links at the bottom and some reference code:

http://www.sparkfun.com/products/9268

http://www.sparkfun.com/products/8454

Hope all this helps. And if anyone else have links to any article which may help please comment.


Example

Of course if you want real units you need to scale for sample rate. For example accelerating at 9m/s/s for 80ms means your velocity is (9m/s/s * 0.08s) = 0.72m/s. The above pseudocode is simplified assuming you don't care about units. The final values will still represent distance as a number it's just that the number has little relation to any real world unit of measurement. You can simply apply a scaling function at the end calibrated to your pixel valus. Anyway, here's an example with real-world units to clarify what's happening:

given the following acceleration readings:
9m/s/s
3m/s/s
0m/s/s
0m/s/s
0m/s/s
-5m/s/s
-7m/s/s

assuming an 80ms sample rate
we can derive the following velocities:
0.72m/s (what you get from accelerating 9m/s for 80ms)
0.96m/s
0.96m/s
0.96m/s
0.96m/s
0.56m/s
0m/s

from that we can derive the following distances:
57.6mm (what you get from moving at 0.72m/s for 80ms)
134.4mm
211.2mm
288mm
364.8mm
409.6mm

Now, if you take the derived distances and do a reverse calculation as per usual (v = (s2-s1)/t and a = (v2-v1)/t) you should get the acceleration readings back.