Существует ли алгоритм для преобразования кватернионных вращений в повороты Эйлера? - PullRequest
27 голосов
/ 23 июня 2009

Существует ли существующий алгоритм для преобразования кватернионного представления вращения в представление угла Эйлера? Порядок поворота для представления Эйлера известен и может быть любым из шести перестановок (т.е. xyz, xzy, yxz, yzx, zxy, zyx). Я видел алгоритмы для фиксированного порядка вращения (обычно заголовок НАСА, банк, соглашение о броске), но не для произвольного порядка вращения.

Кроме того, поскольку существует несколько представлений угла Эйлера в одной ориентации, этот результат будет неоднозначным. Это приемлемо (поскольку ориентация по-прежнему действительна , она может быть не той, которую пользователь ожидает увидеть), однако было бы еще лучше, если бы существовал алгоритм, принимающий пределы вращения (т.е. число степеней свободы и ограничения на каждую степень свободы), что позволило получить «наиболее разумное» представление Эйлера с учетом этих ограничений.

У меня есть ощущение, что эта проблема (или что-то подобное) может существовать в областях динамики IK или твердого тела.


Решено: Я только что понял, что может быть неясно, что я решил эту проблему, следуя алгоритмам Кена Шумейка из Graphics Gems. В то время я отвечал на свой вопрос, но мне кажется, может быть, не совсем понятно, что я так сделал. См. Ответ ниже для более подробной информации.


Просто чтобы уточнить - я знаю, как преобразовать кватернион в так называемое представление Тейт-Брайан - то, что я назвал конвенцией НАСА. Это порядок вращения (в предположении, что ось Z направлена ​​вверх) zxy. Мне нужен алгоритм для всех порядков вращения.

Возможно, решение, таким образом, состоит в том, чтобы взять преобразование порядка zxy и извлечь из него пять других преобразований для других порядков вращения. Наверное, я надеялся, что найдется более «всеобъемлющее» решение. В любом случае, я удивлен, что мне не удалось найти существующие решения там.

Кроме того, и, возможно, это вообще должен быть отдельный вопрос, любое преобразование (конечно, при условии известного порядка вращения) выберет одно представление Эйлера, но на самом деле их много. Например, при заданном порядке вращения yxz два представления (0,0,180) и (180,180,0) эквивалентны (и дадут одинаковый кватернион). Есть ли способ ограничить решение, используя ограничения по степеням свободы? Как у вас в ИК и в динамике твердого тела? то есть в приведенном выше примере, если была только одна степень свободы вокруг оси Z, тогда второе представление может быть проигнорировано.


Я нашел одну статью, которая могла бы быть алгоритмом в этом pdf , но я должен признаться, что мне трудно понять логику и математику. Конечно, есть и другие решения? Произвольный порядок вращения действительно так редок? Конечно, каждый крупный 3D-пакет, который позволяет скелетную анимацию вместе с кватернионной интерполяцией (т.е. Maya, Max, Blender и т. Д.), Должен был решить именно эту проблему?

Ответы [ 8 ]

13 голосов
/ 15 июля 2009

Это похоже на классический случай упущения старой технологии - мне удалось выкопать копию Graphics Gems IV из гаража, и похоже, что у Кена Шумейка есть не только алгоритм для преобразования из углов Эйлера в произвольный порядок вращения, но также отвечает на большинство других моих вопросов по этому вопросу. Ура для книг. Если бы я только мог проголосовать за ответ мистера Шумейка и наградить его репутацией.

Я полагаю, что любой, кто работает с углами Эйлера, должен получить копию Graphics Gems IV из своей локальной библиотеки и прочитать начальную страницу раздела 222. Это должно быть самым ясным и кратким объяснением проблемы, которую я когда-либо читал.


Вот полезная ссылка, которую я нашел с тех пор - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - Это та же система, что и в Shoemake; 24 различных перестановки порядка вращения кодируются как четыре отдельных параметра - внутренняя ось, четность, повторение и фрейм - что затем позволяет сократить алгоритм с 24 до 2. Может быть полезной вики в целом - я не пришел через это раньше.

к старой предоставленной ссылке, похоже, не работает здесь является еще одной копией "Вычисление углов Эйлера из матрицы вращения ».

10 голосов
/ 23 июня 2009

В правосторонней декартовой системе координат с направленной вверх осью Z сделайте следующее:

struct Quaternion
{
    double w, x, y, z;
};

void GetEulerAngles(Quaternion q, double& yaw, double& pitch, double& roll)
{
    const double w2 = q.w*q.w;
    const double x2 = q.x*q.x;
    const double y2 = q.y*q.y;
    const double z2 = q.z*q.z;
    const double unitLength = w2 + x2 + y2 + z2;    // Normalised == 1, otherwise correction divisor.
    const double abcd = q.w*q.x + q.y*q.z;
    const double eps = 1e-7;    // TODO: pick from your math lib instead of hardcoding.
    const double pi = 3.14159265358979323846;   // TODO: pick from your math lib instead of hardcoding.
    if (abcd > (0.5-eps)*unitLength)
    {
        yaw = 2 * atan2(q.y, q.w);
        pitch = pi;
        roll = 0;
    }
    else if (abcd < (-0.5+eps)*unitLength)
    {
        yaw = -2 * ::atan2(q.y, q.w);
        pitch = -pi;
        roll = 0;
    }
    else
    {
        const double adbc = q.w*q.z - q.x*q.y;
        const double acbd = q.w*q.y - q.x*q.z;
        yaw = ::atan2(2*adbc, 1 - 2*(z2+x2));
        pitch = ::asin(2*abcd/unitLength);
        roll = ::atan2(2*acbd, 1 - 2*(y2+x2));
    }
}
7 голосов
/ 16 декабря 2014

Я искал подобное решение в течение нескольких дней, и, наконец, я наткнулся на этот сайт, на котором есть алгоритм преобразования кватернионов в произвольные вращения Эйлера и Тейт-Брайана!

Вот ссылка: http://bediyap.com/programming/convert-quaternion-to-euler-rotations/

А вот код:

///////////////////////////////
// Quaternion to Euler
///////////////////////////////
enum RotSeq{zyx, zyz, zxy, zxz, yxz, yxy, yzx, yzy, xyz, xyx, xzy,xzx};

void twoaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r11, r12 );
  res[1] = acos ( r21 );
  res[2] = atan2( r31, r32 );
}

void threeaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r31, r32 );
  res[1] = asin ( r21 );
  res[2] = atan2( r11, r12 );
}

void quaternion2Euler(const Quaternion& q, double res[], RotSeq rotSeq)
{
    switch(rotSeq){
    case zyx:
      threeaxisrot( 2*(q.x*q.y + q.w*q.z),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    -2*(q.x*q.z - q.w*q.y),
                     2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                     res);
      break;

    case zyz:
      twoaxisrot( 2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.z + q.w*q.y),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.z - q.w*q.y),
                  res);
      break;

    case zxy:
      threeaxisrot( -2*(q.x*q.y - q.w*q.z),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      2*(q.y*q.z + q.w*q.x),
                     -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                      res);
      break;

    case zxz:
      twoaxisrot( 2*(q.x*q.z + q.w*q.y),
                  -2*(q.y*q.z - q.w*q.x),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.x*q.z - q.w*q.y),
                   2*(q.y*q.z + q.w*q.x),
                   res);
      break;

    case yxz:
      threeaxisrot( 2*(q.x*q.z + q.w*q.y),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    -2*(q.y*q.z - q.w*q.x),
                     2*(q.x*q.y + q.w*q.z),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                     res);
      break;

    case yxy:
      twoaxisrot( 2*(q.x*q.y - q.w*q.z),
                   2*(q.y*q.z + q.w*q.x),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.x*q.y + q.w*q.z),
                  -2*(q.y*q.z - q.w*q.x),
                  res);
      break;

    case yzx:
      threeaxisrot( -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                      2*(q.x*q.y + q.w*q.z),
                     -2*(q.y*q.z - q.w*q.x),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      res);
      break;

    case yzy:
      twoaxisrot( 2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.y - q.w*q.z),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.y + q.w*q.z),
                   res);
      break;

    case xyz:
      threeaxisrot( -2*(q.y*q.z - q.w*q.x),
                    q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    2*(q.x*q.z + q.w*q.y),
                   -2*(q.x*q.y - q.w*q.z),
                    q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    res);
      break;

    case xyx:
      twoaxisrot( 2*(q.x*q.y + q.w*q.z),
                  -2*(q.x*q.z - q.w*q.y),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.y - q.w*q.z),
                   2*(q.x*q.z + q.w*q.y),
                   res);
      break;

    case xzy:
      threeaxisrot( 2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                    -2*(q.x*q.y - q.w*q.z),
                     2*(q.x*q.z + q.w*q.y),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                     res);
      break;

    case xzx:
      twoaxisrot( 2*(q.x*q.z - q.w*q.y),
                   2*(q.x*q.y + q.w*q.z),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.z + q.w*q.y),
                  -2*(q.x*q.y - q.w*q.z),
                  res);
      break;
    default:
      std::cout << "Unknown rotation sequence" << std::endl;
      break;
   }
}
3 голосов
/ 17 апреля 2017

Для тех, кто наткнулся на эту страницу во время поиска в Google, я недавно нашел производные для этих преобразований для всех 12 внутренних Тейт-Брайана (1-2-3, 3-2-1 и т. Д.) И Правильного Эйлера (1-2 -1, 3-1-3 и т. Д.) Последовательности вращения в следующих двух ссылках:

Спасибо frodo2975 за вторую ссылку.

3 голосов
/ 06 марта 2010

Вот статья, которую я написал о преобразовании кватернионов в углы Эйлера.

Ссылка 1

Я также разместил в этом месте ряд документов, в которых обсуждаются различные аспекты кватернионов, углов Эйлера и матриц вращения (DCM).

Ссылка 2

3 голосов
/ 15 января 2010

Я решаю это так:

шаг 1 : убедитесь, какое соглашение для вращения Эйлера вы хотите, скажем, zyx .

шаг 2 : Рассчитать аналитическую матрицу вращения для вращения. Например, если вы хотите R ( zyx ),

** R *** zyx * = ** R *** x * ( phi ) * ** R *** y * ( theta ) * ** R *** z * ( фунтов на квадратный дюйм ), где элементы становятся

R11 =  cos(theta)*cos(psi)
R12 = -cos(theta)*sin(psi)
R13 =  sin(theta)
R21 =  sin(psi)*cos(phi) + sin(theta)*cos(psi)*sin(phi)
R22 =  cos(psi)*cos(phi) - sin(theta)*sin(psi)*sin(phi)
R23 = -cos(theta)*sin(phi)
R31 =  sin(psi)*sin(phi) - sin(theta)*cos(psi)*cos(phi)
R32 =  cos(psi)sin(phi) + sin(theta)*sin(psi)*cos(phi)
R33 =  cos(theta)*cos(phi) 

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

tan(phi) = -R23/R33

sin(theta) = -R13

tan(psi) = -R12/R11

шаг 4 : Вычислите матрицу вращения из вашего кватерниона (см. wikipedia ), для элементов, которые необходимо вычислить в углах, как в 3) выше.

Другие условные обозначения могут быть вычислены с использованием той же процедуры.

3 голосов
/ 27 октября 2009

Я разместил свою статью под названием «Преобразование углов кватерниона в Эйлера для произвольной последовательности вращения с использованием геометрических методов» на моем веб-сайте noelhughes.net. У меня также есть алгоритмы для преобразования любого набора углов Эйлера в кватернион и кватернион в / из матрицы косинуса направления, которую я опубликую на этих выходных. Они также есть на сайте Мартина Бейкерса, хотя их немного сложно найти. Гугл мое имя, Ноэль Хьюз и кватернионы, и ты должен его найти.

2 голосов
/ 23 июня 2009

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

...