17

I am going to develop Android application which needs to read x,y,z coordinates of phone on 3D space.

I would like to write a simple code and test on the device..

I am using ginger bread on both the device and emulator.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Ruwantha
  • 2,603
  • 5
  • 30
  • 44
  • Possible duplicate of [Using accelerometer, gyroscope and compass to calculate device's movement in 3D world](http://stackoverflow.com/questions/8264518/using-accelerometer-gyroscope-and-compass-to-calculate-devices-movement-in-3d) superset that also asks for rotation state. – Ciro Santilli OurBigBook.com Apr 30 '16 at 08:05

2 Answers2

18

To get position from acceleration you need to integrate it twice.

Integrating acceleration gives you velocity and integrating the velocity gives you the position.

Keep in mind that integrating noise creates drift and integrating drift creates A LOT of drift, the android sensors tend to generate quite a lot of noise.

On my Galaxy S3 I have been able to get the drift in position down to 0.02 m in 5 seconds using Google's Linear Accelerometer composite sensor.

I am not sure if you can use the linear accelerometer sensor on gingerbread. If you can't you will have to remove the gravity before integrating.

If you haven't already, read everything here http://developer.android.com/guide/topics/sensors/sensors_motion.html

A great talk about the motion sensors in android

http://www.youtube.com/watch?v=C7JQ7Rpwn2k

Code:

static final float NS2S = 1.0f / 1000000000.0f;
float[] last_values = null;
float[] velocity = null;
float[] position = null;
long last_timestamp = 0;

@Override
public void onSensorChanged(SensorEvent event) {
    if(last_values != null){
        float dt = (event.timestamp - last_timestamp) * NS2S;

        for(int index = 0; index < 3;++index){
            velocity[index] += (event.values[index] + last_values[index])/2 * dt;
            position[index] += velocity[index] * dt;
        }
    }
    else{
        last_values = new float[3];
        velocity = new float[3];
        position = new float[3];
        velocity[0] = velocity[1] = velocity[2] = 0f;
        position[0] = position[1] = position[2] = 0f;
    }
    System.arraycopy(event.values, 0, last_values, 0, 3);
    last_timestamp = event.timestamp;
}

Now you have the position in 3d space, keep in mind it assumes that the phone is stationary when it starts sampling.

If you don't remove gravity it will soon be very far away.

This doesn't filter the data at all and will generate a lot of drift.

spontus
  • 193
  • 5
  • 15
  • 1
    It's not just sensor noise that generates drift. The main limitation of this approach is that it is fundamentally impossible to detect a constant speed with an accelerometer. So integrating might seemingly give reasonable results when you just shake your phone a bit, but for other types of motion, for example when driving or cycling, the drift will be huge. – Junuxx Jun 27 '12 at 22:27
  • You're absolutely right. I am using the accelerometer to track a car and for that I have implemented a kalman filter to further reduce the drift. I also calculate the accuracy of the accelerometer by measuring the noise when it is stationary, this is then used to weight the data in the filter. I am getting fairly good results so far. – spontus Jun 29 '12 at 09:33
  • Here you will also get a lot of "drift" as you are calling it because you calculate all of this with newtonian physics, which is inaccurate. Do Runge-kutta or some other more accurate way of calculating position to get better results. – Automatico Mar 05 '13 at 01:14
  • 2
    @spontus Using your code with the Linear Acceleration sensor, my Position values with the device sitting still on a desk increase about 1 per second in x and y, and about 10 per second in z. Is this what you'd expect? I know this method is known to not be very accurate, but is it supposed to be THIS bad? – David Doria Sep 16 '13 at 18:43
  • @DavidDoria The ~10 per second is because of 9.8 m/s^2 gravity acceleration. You need to remove this somehow as the answer says. – JacKeown Jan 25 '20 at 20:01
8

Read this tutorial.

brief summary of the above given tutorial ::

first get an instance of SensorManager and Sensor.
Inside onCreate() ::

mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);

after this, override onSensorChanged(SensorEvent event) and use event.values[] to get the co-ordinates.

@Override
public void onSensorChanged(SensorEvent event) {
    float x = event.values[0];
    float y = event.values[1];
    float z = event.values[2];
}
Eight
  • 4,194
  • 5
  • 30
  • 51