Как преобразовать результат датчика вектора вращения игры к углам оси

Обратитесь к коду ниже, я получил 4 номера от события датчика Game_Rotation_Vector, и то, что я хочу достичь, это получить угол поворота оси моего телефона.

например. Если я поворачиваю счетчик часов экрана телефона для 90 degress, я хочу получить фактическое значение 90deg из данных датчика.

Может ли кто-нибудь предоставить код для преобразования 4 значений вектора вращения игры в 3 значения угла оси?

2 ответа

  1. Значения помещены в кватернион блока как так:

    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)
    

    Так что обратная сторона этого просто:

    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;
    

    Но xa, xy и xz-это не углы, это координаты оси, как описано в документации. Я не уверен, что вы хотите с ними, вам, вероятно, лучше попытаться достичь своей невысказанной цели с помощью блока quaternion напрямую.

    Причина , по которой используется блок кватернион, заключается в том, чтобы избежать блокировки gimble, в основном вы не можете описать каждое возможное вращение с точки зрения трех углов, крена/шага/рыскания, например, при определенном шаге и угле рыскания вы можете потерять способность катиться.

    Таким образом, один из способов использования этого заключается в преобразовании в матрицу вращения:

    Источник

    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);
    }
    
  2. Таким образом, мы знаем, что данные вектора вращения являются единицей кватерниона.

    К счастью, в Википедии есть страница о конверсии кватернионов.

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

    Приведены два примера. Один на C++, один на Python. Я добавлю их здесь.

    С++:

    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);
    }
    

    Питон:

    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
    

    Обратите внимание, что в единице кватерниона Android скалярная составляющая вектора вращения является последней переменной (из четырех). В этих фрагментах кода это первая переменная.

    Надеюсь, это поможет!