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

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

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

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

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


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


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

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

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


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

4b9b3361

Ответ 1

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

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


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

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

Ответ 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));
    }
}

Ответ 3

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

Ответ 4

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

Шаг 1. Убедитесь в том, какое соглашение для вращения Euler вы хотите, скажем, zyx.

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

** R *** zyx * = ** R *** x * (phi) * ** R *** y * (theta) * ** 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. Вычислите матрицу вращения из вашего кватерниона (см. wikipedia), для элементов вам нужно вычислить углы, как в 3) выше.

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

Ответ 5

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

Ссылка 1

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

Ссылка 2

Ответ 6

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

Здесь ссылка: 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;
   }
}

Ответ 7

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

Ответ 8

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

Благодаря frodo2975 для второй ссылки.