Search code examples
androidvectoraccelerometersensorsgyroscope

Getting Direction Vector in Android


How can I get a direction vector representing the direction the back of the device is pointing relative to earth coordinates?

For example, if place down on a desk (screen facing up) it should read [0,0,-1] and if held vertically facing north it should read [1,0,0], etc.

I know how to calculate it from heading, pitch, and roll, as long as those are relative to earth coordinates. To be clear here I am not looking for angular velocity, but the actual current angle relative to the plane tangent to the earth. So if the device is held vertically and facing north, the angle "alpha" should read 0 or 360, the angle "beta" should read 90, and "gamma" should read 0. I can't figure out how to get these values either.

I've been reading the API all day and I still cannot find how to get either of these things.

public void onSensorChanged(SensorEvent event) {
    // ?    
}

Thanks for any insights.


Solution

  • SensorManager.getRotationMatrix() does what's outlined below, written before I found this out. I'll leave the added explanation because if you want to correct for the difference between magnetic and true north you'll still need it.

    The rough algorithm is to get the rotation matrix, multiply vector [0,0,-1] by it, then adjust this to your coordinate system. Why? Android docs give the coordinate systems for device and world

    deviceworld

    Note [0,0,-1] in Android device coords points perpendicular backward away from the screen. If you multiply rotation matrix R by this vector, you'll get [0,0,-1] in world coords when the device is on the table facing up as you desire. When it's upright facing north, you'll get [0,-1,0], which indicates that you've chosen a coordinate system where x and y are swapped with respect to the Android system, but that's simply a change of conventions.

    Note R * [0,0,-1]^T is just the third column of R negated. From this I get the pseudocode:

    getRotationMatrix(R);
    Let v = first three elements of third column of R.
    swap v[0] and v[1]
    

    This ought to get what you want.

    Additional information on what getRotationMatrix() is doing follows.


    You need both accerometer data to establish the direction "down" and magnetometer data to determine the direction "north." You'll have to assume the accelerometers are sensing only gravity (the device is stationary or moving at a steady velocity). Then you need to project the magnetometer vector onto the plane perpendicular to the gravity vector (because the magnetic field is not generally tangent to the earth's surface). This gives you two axes. The third is orthogonal, so can be computed by cross product. This gives you the earth coordinate vectors in the device system. It looks like you want the inverse: device coordinates in earth coordinates. For this just construct the matrix of direction cosines and invert.

    I will add that the above discussion assumes that the magnetometer vector points north. I think (from high school science!) it's actually toward magnetic south, but have no device at hand so can't try it. Of course magnetic north/south is different from true by zero to 180 degrees depending where you are on the earth. You can retrieve GPS coordinates and compute an actual offset.

    If you are not familiar with the math needed to do these, I can explain further, but it will have to be later.