How to convert Game Rotation Vector sensor result to axis angles

2.5k views Asked by At

Refer to the code below, I got 4 numbers from the Game_Rotation_Vector sensor event and what I want to achieve is to get the axis angle rotation of my phone.

eg. If I rotate the phone screen counter clock wise for 90 degress, I want to get the actual value 90deg from the sensor data.

Can anyone provide the code to convert the 4 game rotation vector values to 3 axis angle values?

2

There are 2 answers

5
weston On

The values are placed into the unit quaternion like so:

sensors_event_t.data[0] = rot_axis.x*sin(theta/2);
sensors_event_t.data[1] = rot_axis.y*sin(theta/2);
sensors_event_t.data[2] = rot_axis.z*sin(theta/2);
sensors_event_t.data[3] = cos(theta/2)

So the reverse of that is just:

double theta = Math.acos(vals[3])*2;
double sinv = Math.sin(theta/2);

double xa = vals[0]/sinv;
double ya = vals[1]/sinv;
double za = vals[2]/sinv;

But xa, xy, and xz aren't angles, they're coordinates of an axis as described in the documentation. I'm not sure what you want with these, you are likely better off trying to achieve your unstated goal with the unit quaternion directly.

The reason a unit quaternion is used is to avoid gimble lock, basically you cannot describe every possible rotation in terms of three angles, roll/pitch/yaw, for example at a certain pitch and yaw angle you can lose the ability to roll.

So one way to make use of this is to convert into a rotation matrix:

Source

if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
    // convert the rotation-vector to a 4x4 matrix. the matrix
    // is interpreted by Open GL as the inverse of the
    // rotation-vector, which is what we want.
    SensorManager.getRotationMatrixFromVector(
            mRotationMatrix , event.values);
}
0
Johann Baron Lanteigne On

So we know that the rotation vector data is an unit quaternion.

Luckily, there is a page on wikipedia about quaternions conversion.

https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles

Two examples are provided. One in C++, one in Python. I will add them here.

C++:

static void toEulerAngle(const Quaterniond& q, double& roll, double& pitch, double& yaw)
{
    // roll (x-axis rotation)
    double sinr = +2.0 * (q.w() * q.x() + q.y() * q.z());
    double cosr = +1.0 - 2.0 * (q.x() * q.x() + q.y() * q.y());
    roll = atan2(sinr, cosr);

    // pitch (y-axis rotation)
    double sinp = +2.0 * (q.w() * q.y() - q.z() * q.x());
    if (fabs(sinp) >= 1)
        pitch = copysign(M_PI / 2, sinp); // use 90 degrees if out of range
    else
        pitch = asin(sinp);

    // yaw (z-axis rotation)
    double siny = +2.0 * (q.w() * q.z() + q.x() * q.y());
    double cosy = +1.0 - 2.0 * (q.y() * q.y() + q.z() * q.z());  
    yaw = atan2(siny, cosy);
}

Python:

import math

def quaternion_to_euler_angle(w, x, y, z):
    ysqr = y * y

    t0 = +2.0 * (w * x + y * z)
    t1 = +1.0 - 2.0 * (x * x + ysqr)
    X = math.degrees(math.atan2(t0, t1))

    t2 = +2.0 * (w * y - z * x)
    t2 = +1.0 if t2 > +1.0 else t2
    t2 = -1.0 if t2 < -1.0 else t2
    Y = math.degrees(math.asin(t2))

    t3 = +2.0 * (w * z + x * y)
    t4 = +1.0 - 2.0 * (ysqr + z * z)
    Z = math.degrees(math.atan2(t3, t4))

    return X, Y, Z

Note that in Android's unit quaternion, the Scalar component of the rotation vector is the last variable (of the four). In these code snippets, it's the first variable.

I hope this helps!