Существует ли алгоритм преобразования кватернионных вращений в угловые вращения Эйлера?

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

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

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


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


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

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

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


я отследил одну бумагу, которая может быть алгоритмом в этот pdf

8 ответов


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

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


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

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


в правой декартовой системе координат с осью 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));
    }
}

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

вот ссылка: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;
   }
}

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


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

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

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

* * R * * * zyx * = * * R * * * x* (phi ) * * * R * * * y* (тэта ) * * * R * * * z* (psi ), где элементы стать

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: вычислите матрицу вращения из вашего кватерниона (см. Википедия), для элементов вам нужно вычислить углы, как в 3) выше.

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


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

ссылка 1

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

Ссылка 2


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


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

спасибо frodo2975 по второй ссылке.