Search code examples
javaandroidmathrotationquaternions

Rotation Vector Sensor Values to Azimuth, Roll, and Pitch


I am trying to convert the 5 values returned when using the Rotation Vector Sensor Type to roll, azimuth, and pitch.

The code I am using to do so is the following.

@Override
public void onSensorChanged(SensorEvent event) {
    double[] g = convertFloatsToDoubles(event.values.clone());

    double norm = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2] + g[3] * g[3]);

    g[0] /= norm;
    g[1] /= norm;
    g[2] /= norm;
    g[3] /= norm;

    double xAng = (2 * Math.acos(g[0])) * (180 / Math.PI);
    double yAng = (2 * Math.acos(g[1])) * (180 / Math.PI);
    double zAng = (2 * Math.acos(g[2])) * (180 / Math.PI);
}

private double[] convertFloatsToDoubles(float[] input)
{
    if (input == null)
        return null;
        
    double[] output = new double[input.length];
    
    for (int i = 0; i < input.length; i++)
        output[i] = input[i];
        
    return output;
}

The issue is the values that are returned by variables xAng and yAng seem to be restricted to 80 - 280.

As for zAng (which I think is the azimuth), it is working like a compass but when it returns 0 it appears to be about 12 degrees off magnetic South.

I assume I have done something wrong with the maths used but I am unsure of what exactly.


The values for Sensor.TYPE_ROTATION_VECTOR are defined here as:

values[0]: x*sin(θ/2)

values[1]: y*sin(θ/2)

values[2]: z*sin(θ/2)

values[3]: cos(θ/2)

values[4]: estimated heading Accuracy (in radians) (-1 if unavailable)


Solution

  • In case it helps anyone looking to accomplish the same task. The maths was being handled completely incorrectly.

    Below onSensorChanged has been updated so it returns the correct values in degrees.

    @Override
    public void onSensorChanged(SensorEvent event) {
        //Get Rotation Vector Sensor Values
        double[] g = convertFloatsToDoubles(event.values.clone());
    
        //Normalise
        double norm = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2] + g[3] * g[3]);
        g[0] /= norm;
        g[1] /= norm;
        g[2] /= norm;
        g[3] /= norm;
    
        //Set values to commonly known quaternion letter representatives
        double x = g[0];
        double y = g[1];
        double z = g[2];
        double w = g[3];
    
        //Calculate Pitch in degrees (-180 to 180)
        double sinP = 2.0 * (w * x + y * z);
        double cosP = 1.0 - 2.0 * (x * x + y * y);
        double pitch = Math.atan2(sinP, cosP) * (180 / Math.PI);
    
        //Calculate Tilt in degrees (-90 to 90)
        double tilt;
        double sinT = 2.0 * (w * y - z * x);
        if (Math.abs(sinT) >= 1)
            tilt = Math.copySign(Math.PI / 2, sinT) * (180 / Math.PI);
        else
            tilt = Math.asin(sinT) * (180 / Math.PI);
    
        //Calculate Azimuth in degrees (0 to 360; 0 = North, 90 = East, 180 = South, 270 = West)
        double sinA = 2.0 * (w * z + x * y);
        double cosA = 1.0 - 2.0 * (y * y + z * z);
        double azimuth = Math.atan2(sinA, cosA) * (180 / Math.PI);
    }