I want to build an app that calculates accurate Distance travelled by iPhone (not long distance) using Gyro+Accelerometer. No need for GPS here.
How should I approach this problem?
I want to build an app that calculates accurate Distance travelled by iPhone (not long distance) using Gyro+Accelerometer. No need for GPS here.
How should I approach this problem?
Basic calculus behind this problem is in the expression
(and similar expressions for displacements in y and z) and basic geometry is the Pythagorean theorem
So, once you have your accelerometer signals passed through a low-pass filter and binned in time with sampling interval dt, you can find the displacement in x as (pardon my C...)
float dx=0.0f;
float vx=0.0f;
for (int i=1; i<n; i++)
{
vx+=(acceleration_x[i-1] + acceleration_x[i])/2.0f*dt;
dx+=vx*dt;
}
and similarly for dy and dz. Here
float acceleration_x[n];
contains x-acceleration values from start to end of measurement at times 0, dt, 2*dt, 3*dt, ... (n-1)*dt.
To find the total displacement, you just do
dl=sqrt(dx*dx + dy*dy + dz*dz);
Gyroscope is not necessary for this, but if you are measuring linear distances, you can use the gyroscope reading to control that rotation of the device was not too large. If rotation was too strong, make the user re-do the measurement.
You get position by integrating the linear acceleration twice but the error is horrible. It is useless in practice.
Here is an explanation why (Google Tech Talk) at 23:20. I highly recommend this video.
Similar questions:
track small movements of iphone with no GPS
What is the real world accuracy of phone accelerometers when used for positioning?
how to calculate phone's movement in the vertical direction from rest?
iOS: Movement Precision in 3D Space
How to use Accelerometer to measure distance for Android Application Development
Distance moved by Accelerometer
Update (24 Feb 2013): @Simon Yes, if you know more about the movement, for example a person walking and the sensor is on his foot, then you can do a lot more. These are called
domain specific assumptions.
They break miserably if the assumptions do not hold and can be quite cumbersome to implement. Nevertheless, if they work, you can do fun things. See the links in my answer Android accelerometer accuracy (Inertial navigation) at indoor positioning.
You should use the Core Motion interface like described in Simple iPhone motion detect. Especially all rotations can be tracked very accurately. If you plan to do something related to linear movements this is very hard stuff. Have a look at Getting displacement from accelerometer data with Core Motion.
I took a crack at this and gave up (late at night, didn't seem to be getting anywhere). This is for a Unity3d project.
If anyone wants to pick up where I left off, I would be happy to elaborate on what all this stuff does.
Basically after some of what turned out to be false positives, I thought I'd try and filter this using a low pass filter, then attempted to remove bounces by finding a trend, then (acc_x[i-1]+acc_x[i])/2.
It looks like the false positive is still coming from the tilt, which I attempted to remove..
If this code is useful or leads you someplace, please let me know!
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// rbi.noli@gmail.com
/// </summary>
public class AccelerometerInput : MonoBehaviour
{
Transform myTransform;
Gyroscope gyro;
GyroCam gyroCam;
void Awake()
{
gyroCam= FindObjectOfType<GyroCam> ();
myTransform = transform;
if (SystemInfo.supportsGyroscope) {
gyro = Input.gyro;
gyro.enabled = true;
}
}
bool shouldBeInitialized = false;
void Update ()
{
transform.Translate (GetAccelerometer ());// * Time.deltaTime * speed);
//GetComponent<Rigidbody> ().AddForce (GetAccelerometer ());
}
public float speed = 10.0F;
public Vector3 dir;
public float f;
Vector3 GetAccelerometer()
{
dir = Input.acceleration;
dir.x *= gyro.attitude.x;
dir.z *= gyro.attitude.z;
if (Mathf.Abs (dir.x) < .001f)
dir.x = 0;
dir.y = 0;
if (Mathf.Abs (dir.z) < .001f)
dir.z = 0;
RecordPointsForFilter (dir);
//print ("Direction : " + dir.ToString("F7"));
return TestPointsForVelocity();
}
Vector3[] points = new Vector3[20];
int index;
void RecordPointsForFilter(Vector3 recentPoint)
{
if (index >= 20)
index = 0;
points [index] = EvaluateTrend (recentPoint);;
index++;
}
//try to remove bounces
float xTrend = 0;
float zTrend = 0;
float lastTrendyX = 0;
float lastTrendyZ = 0;
Vector3 EvaluateTrend(Vector3 recentPoint)
{
//if the last few points were positive, and this point is negative, don't pass it along
//accumulate points into a trend
if (recentPoint.x > 0)
xTrend += .01f;
else
xTrend -= .1f;
if (recentPoint.z > 0)
zTrend += .1f;
else
zTrend -= .1f;
//if point matches trend, keep it
if (xTrend > 0) {
if (recentPoint.x > 0)
lastTrendyX = recentPoint.x;
} else // xTrend < 0
if (recentPoint.x < 0)
lastTrendyX = recentPoint.x;
if (zTrend > 0) {
if (recentPoint.z > 0)
lastTrendyZ = recentPoint.z;
} else // xTrend < 0
if (recentPoint.z < 0)
lastTrendyZ = recentPoint.z;
return new Vector3( lastTrendyX, 0, lastTrendyZ);
}
Vector3 TestPointsForVelocity()
{
float x = 0;
float z = 0;
float xAcc = 0;
float zAcc = 0;
int successfulHits = 0;
for(int i = 0; i < points.Length; i++)
{
if(points[i]!=null)
{
successfulHits ++;
xAcc += points[i].x;
zAcc += points[i].z;
}
}
x = xAcc / successfulHits;
z = zAcc / successfulHits;
return new Vector3 (x, 0, z);
}
}
Here is the answer. Somebody asked before.
There is an app called RangeFinder doing the same thing ( available in App Store ) .
(acc_x[i-1]+acc_x[i])/2 is a low pass filter, it is the mean value between two measures in time
also look at here : http://www.freescale.com/files/sensors/doc/app_note/AN3397.pdf pag :3