Подтвердить что ты не робот

Компонент вращения кватерниона вокруг оси

У меня возникли проблемы с поиском хорошей информации по этой теме. В основном я хочу найти компонент вращения кватерниона, то есть вокруг данной оси (не обязательно X, Y или Z - любой произвольный единичный вектор). Аналогично проектированию кватерниона на вектор. Поэтому, если бы я попросил поворот вокруг некоторой оси, параллельной оси кватерниона, я бы получил тот же кватернион. Если бы я попросил поворот вокруг оси, ортогональной оси кватерниона, я бы выбрал кватернион идентичности. И между... хорошо, что я хотел бы знать, как это работает:)

4b9b3361

Ответ 1

Я провел другой день, пытаясь найти то же самое для редактора анимации; вот как я это сделал:

  1. Возьмите ось, вокруг которой хотите найти вращение, и найдите для нее ортогональный вектор.
  2. Поверните этот новый вектор, используя свой кватернион.
  3. Проецируйте этот повернутый вектор на плоскость, нормалью которой является ваша ось
  4. Акос точечного произведения этого проецируемого вектора и исходного ортогонального - ваш angular.

    public static float FindQuaternionTwist(Quaternion q, Vector3 axis)
    {
        axis.Normalize();
    
        // Get the plane the axis is a normal of
        Vector3 orthonormal1, orthonormal2;
        ExMath.FindOrthonormals(axis, out orthonormal1, out orthonormal2);
    
        Vector3 transformed = Vector3.Transform(orthonormal1, q);
    
        // Project transformed vector onto plane
        Vector3 flattened = transformed - (Vector3.Dot(transformed, axis) * axis);
        flattened.Normalize();
    
        // Get angle between original vector and projected transform to get angle around normal
        float a = (float)Math.Acos((double)Vector3.Dot(orthonormal1, flattened));
    
        return a;
    }
    

Вот код для поиска ортонормированных значений, однако, вы, вероятно, сможете добиться большего успеха, если вам нужен только один для вышеуказанного метода:

private static Matrix OrthoX = Matrix.CreateRotationX(MathHelper.ToRadians(90));
private static Matrix OrthoY = Matrix.CreateRotationY(MathHelper.ToRadians(90));

public static void FindOrthonormals(Vector3 normal, out Vector3 orthonormal1, out Vector3 orthonormal2)
{
    Vector3 w = Vector3.Transform(normal, OrthoX);
    float dot = Vector3.Dot(normal, w);
    if (Math.Abs(dot) > 0.6)
    {
        w = Vector3.Transform(normal, OrthoY);
    }
    w.Normalize();

    orthonormal1 = Vector3.Cross(normal, w);
    orthonormal1.Normalize();
    orthonormal2 = Vector3.Cross(normal, orthonormal1);
    orthonormal2.Normalize();
}

Хотя вышеперечисленное работает, вы можете обнаружить, что оно не ведет себя так, как вы ожидаете. Например, если ваш кватернион поворачивает вектор на 90 градусов. вокруг X и 90 град. вокруг Y вы найдете, если вы разложите вращение вокруг Z, это будет 90 градусов. также. Если вы представляете вектор, совершающий эти вращения, то это имеет смысл, но в зависимости от вашего приложения это может быть нежелательным поведением. Для моего приложения - ограничения скелетных суставов - я получил гибридную систему. Матрицы/кваты использовались повсеместно, но когда дело дошло до метода ограничения соединений, я использовал внутренние углы Эйлера, разлагая кват вращения на повороты вокруг X, Y, Z каждый раз.

Удачи, надеюсь, это помогло.

Ответ 2

Существует элегантное решение этой проблемы, особенно подходящее для кватернионов. Это известно как "разложение твист колебания":

в псевдокоде

/**
   Decompose the rotation on to 2 parts.
   1. Twist - rotation around the "direction" vector
   2. Swing - rotation around axis that is perpendicular to "direction" vector
   The rotation can be composed back by 
   rotation = swing * twist

   has singularity in case of swing_rotation close to 180 degrees rotation.
   if the input quaternion is of non-unit length, the outputs are non-unit as well
   otherwise, outputs are both unit
*/
inline void swing_twist_decomposition( const xxquaternion& rotation,
                                       const vector3&      direction,
                                       xxquaternion&       swing,
                                       xxquaternion&       twist)
{
    vector3 ra( rotation.x, rotation.y, rotation.z ); // rotation axis
    vector3 p = projection( ra, direction ); // return projection v1 on to v2  (parallel component)
    twist.set( p.x, p.y, p.z, rotation.w );
    twist.normalize();
    swing = rotation * twist.conjugated();
}

А длинный ответ и вывод этого кода можно найти здесьhttp://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/

Ответ 3

Я попытался реализовать sebf-ответ, кажется хорошим, за исключением того, что выбор выбора вектора на шаге 1:

  • Возьмите ось, в которой вы хотите найти поворот, и найдите ортогонального вектора к нему.

недостаточно для повторяемых результатов. Я разработал это на бумаге, и я предлагаю следующий курс действий для выбора вектора, ортогонального "оси, которую вы хотите найти вращение вокруг", то есть оси наблюдения. Существует плоскость, ортогональная оси наблюдения. Вы должны проецировать ось вращения вашего кватерниона на этот самолет. Использование этого результирующего вектора в качестве вектора, ортогонального оси наблюдения, даст хорошие результаты.

Благодаря sebf для правильного курса.

Ответ 4

Код для Unity3d

// We have some given data
Quaternion rotation = ...;
Vector3 directionAxis = ...;

// Transform quaternion to angle-axis form
rotation.ToAngleAxis(out float angle, out Vector3 rotationAxis);

// Projection magnitude is what we found - a component of a quaternion rotation around an axis to some direction axis
float proj = Vector3.Project(rotationAxis.normalized, directionAxis.normalized).magnitude;