По работе у меня возникла необходимость переводить координаты объекта из углов Эйлера в кватернионы и обратно.

В ходе разбирательства пришлось прочитать несколько статей на Хабре, посвященных кватернионам и углам Эйлера, Википедию и просто методички и статьи разных ВУЗов. Для удобства приведу ссылки на статьи, с Хабра:

Каверзные кватернионы
Заметки о вращении вектора кватернионом
Кватернионы для чайников
Кручу-верчу, запутать хочу. Углы Эйлера и Gimbal lock

Формулы для пересчёта углов Эйлера в кватеринионы и обратно найти можно, но

Опишу коротко суть проблемы:

  1. Тело в трёхмерном пространстве имеет 6 степеней свободы: 3 координаты и 3 угла поворота.
  2. С координатами всё хорошо, например, если они (4,5,2), то это означает, что тело нужно сдвинуть относительно начала координат на +4 единицы по оси X, на +5 единиц по оси Y и на +2 единицы по оси Z. При этом порядок сдвига не важен. Можно сначала сдвинуть по X, потом по Y, потом по Z, а можно в другой последовательности. От перемены мест слагаемых сумма не меняется.
  3. С поворотами всё гораздо хуже. Иногда может сложиться ощущение, что для них тоже просто достаточно задать углы поворота вокруг трёх осей и этого будет достаточно (например: перевернуть предмет на 180 градусов вокруг оси X, потом на 180 градусов вокруг оси Y, а затем на 90 градусов вокруг оси Z — в каком порядке не поворачивай — результат будет один и тот же). Эта ловушка возникает оттого, что нам легче всего оперировать углами типа 90 или 180 градусов, а они-то как раз и представляют из себя очень частный случай. В общем случае порядок поворотов имеет значение.

А как же быть с законом, говорящим, что от перестановки мест слагаемых сумма не меняется? Дело в том, что композиция нескольких поворотов соответствует уже не сумме векторов (как в случае с операциями параллельного переноса), а произведению. И произведению не просто чисел, а специальных объектов — матриц поворота, например — на которые коммутативность «обычного» умножения не распространяется. В зависимости от порядка выбора осей поворота и от того, будут ли поворачиваться оси вместе с объектом или поворачиваться будет только объект, можно выделить 24 типа описаний поворотов. Очень часто углы поворота вокруг осей называются углами Эйлера. Иногда, в некоторых источниках эти углы называются углами Тэйт-Брайана либо углами Эйлера в зависимости от того, все три оси, вокруг которых делается вращение разные (углы Тэйт-Брайана), либо же первая и последняя оси — одна и та же. Также эти углы называют angles of extrinsic rotation — если оси неподвижны или angles of intrinsic rotation — если оси вращаются вместе с объектом.
Чтоб не запутаться, приведу все типы вращений здесь:
Тейт-Брайана, внутренние:
ZYXr; YZXr; XZYr; ZXYr; YXZr; XYZr.
Эйлера, внутренние:
XYXr; XZXr; YZYr; YXYr; ZXZr; ZYZr.
Тейт-Брайана, внешние:
ZYXs; YZXs; XZYs; ZXYs; YXZs; XYZs.
Эйлера, внешние:
XYXs; XZXs; YZYs; YXYs; ZXZs; ZYZs.
Внешние углы комплементарны внутренним, прочитанным задом наперёд, например: внешние углы Эйлера 10, 20, 30 градусов в формате XYXs это то же самое, что и внутренние углы Эйлера 30, 20, 10 градусов в формате XYXr.

Собственно, об этом уже было сказано много раз. Зачем же писать новую статью? Дело в том, что информации о том, как переводить из углов Эйлера в кватернион и обратно — не так уж и много. И в большинстве случаев описывается только 1 или 2, 3, 6 систем углов Эйлера. Но не все 24. И по аналогии вывести остальные (и не ошибиться) не очень-то и просто. Во время «откапывания истины» мне удалось найти несколько онлайн-конвертеров из углов в кватернионы и по тому, в каком направлении увеличивается их возможность по конвертации можно понять, сколько ещё вариантов осталось не охвачено:
quat.zachbennett.com — один тип углов
energid.com — один тип углов
onlineconversion.com — один тип углов
quaternions.online — три типа углов
andre-gaschler.com — шесть типов углов

Единственное место, где я смог найти описание преобразований для всех 24 типов углов — это книга «Graphics Gems IV». Репозитарий с исходниками от этой книги находится здесь: Исходники к книге Graphics Gems IV. Если говорить про код преобразования из углов Эйлера в кватернионы и обратно, то эти исходники в репозитарии находятся здесь: .../GraphicsGems/gemsiv/euler_angle. Но у них есть один недостаток: с целью сделать максимально общую функцию расчёта углов и кватернионов, автор очень сильно усложнил код. Т.е. код получился очень компактным, но плохо подходящим для перевода на другие языки или для оптимизации под конкретные случаи. Так как мне очень нужно было разобраться со всеми 24-мя случаями, то пришлось этот код немного поисследовать и развернуть его в набор простых случаев. Также я написал небольшие юнит-тесты и проверил, что мой код работает корректно. Т.к. эти юнит-тесты используют код, скомпилированный из исходников от книги Graphics Gems, то выкладывать их (юнит-тесты) я не стал.

Не буду приводить в тексте статьи свои исходники (они написаны на языке Octave). Дам лишь ссылку на репозитарий и прокомментирую его содержимое:

eul_to_quat.m — аналог матлабовской функции eul2quat
quat_to_eul.m — аналог матлабовской функции quat2eul

Обеих функций в Octave нет. В Matlab поддерживаются только 6 типов углов Эйлера на неподвижных осях. В моих реализациях поддерживаются все 24 типа. При этом типы с буквой r на конце (например, XYZr) означают, что оси вращаются вместе с объектом. Типы с буквой s на конце (например, XYZs) означают, что оси остаются неподвижными.

Комментарии (29)


  1. Hedgehogues
    13.02.2018 18:45
    -1

    Резиновой слух "… Повороты — это уже не сумма, а произведение… ". Это не то и не другое, если быть точным. Это некая операция. Поэтому сумма и произведение — это некорректно. Именно потому что это не сумма и не произведение чисел, оно и не коммутативно


    1. DoctorMoriarty
      13.02.2018 19:25

      >

      Это не то и не другое, если быть точным. Это некая операция.


      Умножение на комплексное число z, такое что модуль z = 1 — является поворотом плоскости на угол, равный аргументу z
      © В.И. Арнольд, «Геометрия комплексных чисел, кватернионов и спинов», МЦНМО, 2002


      1. Hedgehogues
        13.02.2018 20:24

        Автор аротивопоставил умножение сложения и мне это показалось странным. Вот и все


        1. Kyoki
          14.02.2018 11:33

          А вы алгебру дальше школьной математики видели? Их коммутативность это частный случай.


    1. DoctorMoriarty
      13.02.2018 19:45

      Естественно, что цитата касается не пространственного поворота, а поворота на плоскости (пример — если мы будем рассматривать ось y как чисто мнимую, а x как чисто действительную, то повороту на 90 градусов против часовой стрелки вполне соответствует умножение на i) — но кватернионы это расширение комплексных чисел etc. А вот умножение кватернионов как раз некоммутативно — не потому, разумеется, что это «не умножение». Просто такова алгебра кватернионов.


      1. tminnigaliev Автор
        13.02.2018 19:59

        DoctorMoriarty, спасибо за дополнения и пояснения. Там у меня изначально было некоторое костноязычие при изложении, и оно, действительно, могло бросаться в глаза. Hedgehogues обратил на это внимание, я поправил. В любом случае, за любые замечания — спасибо. Над стилем изложения мне тоже нужно будет поработать.


    1. GlukKazan
      13.02.2018 20:57

      Произведение матриц не коммутативно, но от этого оно не перестаёт быть произведением.


    1. lgorSL
      14.02.2018 00:43

      то не то и не другое, если быть точным. Это некая операция.

      Умножение — более фундаментальная сущность, чем какие-то там числа.
      Кватернионы единичной длины — группа SU(2). Группа (любая) по определению имеет операцию умножения, и коммутативности никто не требует.


      Именно потому что это не сумма и не произведение чисел, оно и не коммутативно.

      Это вообще бред. Вот я возьму строчки вида "aaa" (произвольное количество символов "a") и определю на них сложение как конкатенацию: "a" + "aa" = "aa" + "a" = "aaa", оно будет коммутативным и ассоциативным. А вот если взять строчки из символов "a" и "б", то на них сложение вдруг перестанет быть коммутативным, но останется ассоциативным: "аб" + "ба" != "ба" + "aб", ("a"+"б") + "c" == "a" + ("б" + "c")


  1. tminnigaliev Автор
    13.02.2018 18:53

    Спасибо. Поправил.


  1. aamonster
    13.02.2018 22:07
    +1

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


    1. tminnigaliev Автор
      13.02.2018 22:56

      Aamonster, очень часто эффективные инструменты характеризуются более высоким порогом вхождения, что не всегда удобно для большинства пользователей. Например, есть программа для моделирования, которую любят мейкеры (3д-печатники) — OpenSCAD. В ней повороты задаются углами Эйлера, потому что их проще представить себе в голове.
      В моём случае углы Эйлера нужны из-за особенностей специфики алгоритма, который я пытаюсь сформулировать. Если получится заставить этот алгоритм работать и если мне разрешат — я про него напишу.


    1. Jef239
      14.02.2018 01:39

      Вы когда-нибудь пытались самолет посадить? Ну там на тренажере или в игрушке? Ну или беспилотником поуправлять. Попробуйте это сделать по кватернионам. Получится?

      Так что человеку — только углы Эйлера: курс, крен, тангаж (дифферент). И никаких кватеринионов. И системе управления (что морской, что самолетной) тоже только углы Эйлера нужны.

      P.S. Мы как раз и делаем приборы для определения курса, крена и дифферента по сигналам GPS/ГЛОНАСС. Пока что ни один заказчик не просил выдавать кватернионы. Всем только углы Эйлера нужны.


      1. Bookvarenko
        14.02.2018 20:02

        А если кубик надо покатать к примеру? Я пробовал — словил gimbal lock


        1. Jef239
          14.02.2018 20:29

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

          Несмотря на gimbal lock, мыслим мы, увы, в терминах углов Эйлера. И пока роботы не заменят людей — так и будет дальше.


        1. Jef239
          14.02.2018 22:05

          Чтобы было понятней — если вы на самолете или корабле словили gimbal lock — вам уже ничто не поможет. Ибо это положение — носом к центру Земли. Ну разве что авиабомба так падает. Ни курса ни крена, один дифферент -90 градусов


          1. lgorSL
            15.02.2018 12:35

            Эм, на самолёте или спутнике вполне может быть и "носом вверх" и "носом вниз".
            Если бы я реализовывал софт для них, я бы внутри использовал кватернионы. Как Вы в терминах углов Эйлера представите угловую скорость? Как производные по этим трём углам? Это же убого получится — спутник, например, вращается с постоянной скоростью, а нам приходится постоянно пересчитывать углы и угловые скорости. А если он вращается вокруг неудачной оси, то в окрестности gimbal lock ошибка вычислений станет большой.
            Или, например, как найти оптимальный поворот их положения А в Б?


            1. Jef239
              16.02.2018 11:28

              Мы с вами о разном. Вы о реализации софта, а я о том, что человек думает в терминах углов Эйлера. И все системы управления транспортными средствами — работают в углах Эйлера. Угловая скорость тоже в углах Эйлера, что на кораблях, что на самолетах.

              Вы видели хоть одного человека, который думает в терминах кватернионов? Что там будет вместо «поверни направо» и «наклонись вперед»?

              Я не скажу про управление спутником — не знаю, как там расположены двигатели ориентации. Но у корабля и самолета — оси выделены естественным путем.

              Что касается моей области (спутниковая навигация), то в ИКД ГЛОНАСС в расчете положения фазового центра передающей антенны (приложение Т) используются углы Эйлера. Ну просто потому, что человеку так понятней.


              1. lgorSL
                16.02.2018 12:39

                Вы видели хоть одного человека, который думает в терминах кватернионов? Что там будет вместо «поверни направо» и «наклонись вперед»?

                Можно определить операцию логарифмирования от кватерниона — это будет вектор, направленный вдоль оси вращения и длиной, равной углу поворота. Почти как кватернион, там тоже в координатах x,y,z спрятана ось вращения. Ещё есть обратная операция — экспонента. Например, у нас есть вектор угловой скорости, мы его умножаем на время t, и потом делаем экспоненту и получаем текущую ориентацию.


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


                Вращение кубика рубика — тоже в углах Эйлера описывается.

                Забавно, но я хочу привести это как пример использования кватернионов)


                1. Поворот всего кубика на 90 градусов вокруг оси х — обозначается как "x". Аналогично с осями y,z.
                2. Поворот правой части вокруг оси, смотрящей право, по часовой стрелке — R (right)

                Второй пример не очень удачный — можно сказать, что правую грань больше никак и не покрутить. Но тут есть нюанс (возможно, я ошибаюсь, кубиком Рубика не увлекался): повороты L и R делаются в разные стороны. Т.е., R делается по часовой стрелке, если смотреть справа, а L — если смотреть слева.


                С точки зрения поворотов относительно вращающего, это немножко нелогично. А вот если смотреть на это как "R — вращение вокруг оси, направленной вправо, L — вокруг оси, направленной влево", то всё становится понятно.


                Вы видели хоть одного человека, который думает в терминах кватернионов? Что там будет вместо «поверни направо» и «наклонись вперед»?

                Короче, я утверждаю, что люди довольно часто мыслят в терминах "поворот вокруг такой-то оси на такой-то угол", и такая формулировка ближе к кватернионам, чем к углам Эйлера.


                Причём если человек внутри корабля или самолёта, ему почему-то привычнее думать в углах Эйлера, а если вращает что-то перед собой (тот же кубик рубика), то становится проще представить ось вращения и крутить вокруг неё.


                1. Jef239
                  16.02.2018 14:41

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


                  Люди действительно мыслят в таких терминах. Но когда это сумма поворотов по трем ортогональным осям — это углы Эйлера. А когда из трех поворотов по трем осям суммируется один поворот по суммарной оси — это уже кватернион.

                  Так вот те же кубики Рубика записываются как суперпозиция простых поворотов. То есть именно углы Эйлера.

                  А вот пни перекатывают действительно чем-то близким к кватернионам. :-) Уж больно кривые оси у выкорчеванного пня.


          1. Bookvarenko
            15.02.2018 13:00

            Чтобы было ещё более понятно — не [кубик-рубик-вращать], а катать по плоской поверхности обляпаный разными текстурами кубик, переваливая его через грань.


            1. Jef239
              16.02.2018 16:02

              Так не вы кубик катаете, а ваша программа. А программе — конечно кватернионы удобней. А вот человека, думающего в терминах кватернионов я не встречал.

              Попробуйте, выразите это в терминах кватернионов


    1. iKest
      14.02.2018 09:43

      Ну, к примеру, я так и не разобрался, как с помощью кватернионов задавать ограничения соеденений при расчете инверсной кинематики, с углами Эйлера это делается легко и понятно...


  1. blandger
    13.02.2018 23:18

    tminnigaliev, как вы оцениваете возможность эти преобразования залить в прошивку arduino? Достаточно заморочно пересчитывать? Те считывает поворотв/углы, чтобы получить координаты/смещения? Добавил в закладки, думаю пригодится.


    1. tminnigaliev Автор
      13.02.2018 23:31

      Blandger, формулы там простые, т.е. в ардуино их запрограммировать можно. Но из вашей формулировки задачи мне кажется, что я недостаточно понятно описал, что делают две описанных функции. Повторю кратко: есть несколько способов задания поворота объекта в трёхмерном пространстве:
      — с помощью углов Эйлера/Тейт-Брайана
      — с помощью матриц поворота
      — с помощью кватернионов
      способ задания положения объекта с помощью углов Эйлера — самый неоднозначный как с точки зрения целесообразности, так и с точки зрения возможности внести путаницу. Поэтому хочется уметь преобразовывать углы Эйлера во что-то более удобное (например, кватернионы). С другой стороны, иногда возникает обратная задача, например, нужно нарисовать объект в программе, которая понимает только углы Эйлера (т.к. расчитана на пользователя-человека), а данные этой программе даёт другая программа, которой внутри себя проще оперировать кватернионами. Для этого две описанных функции и нужны: одна переводит из углов в кватернионы, вторая обратно.
      В вашем вопросе прозвучала необходимость переводить углы в координаты. Это точно не относится к теме статьи. Возможно, вы имели в виду решение прямой кинематической задачи (расчёт координат манипулятора по углам и кинематической модели робота). Эта задача тоже легко решается средствами, доступными на Ардуино, но это другая задача, не рассмотренная в данной статье.


  1. oam2oam
    14.02.2018 08:13

    По поводу всех возможных преобразований есть такая работа: Representing Attitude: Euler Angles, Unit Quaternions, and Rotation Vectors (James Diebel). В ней очень компактно приведены все основные преобразования (кстати, вместе с производными).


    1. tminnigaliev Автор
      14.02.2018 08:19

      Oam2oam, спасибо за подсказку, статья интересная, сохраню её себе на будущее.


  1. Nick_mentat
    14.02.2018 11:15

    У углов Эйлера есть много соглашений о том, в каком порядке их отсчитывать, и к каким осям поворота они относятся. Пишешь функцию, потом берёшь датчик — а он внутри насыпает тебе углы по другой конвенции. И все расчёты возвращают чепуху. По хорошему нужно написать не одну, а 12 пар таких функций, и к ним плюс функции преобразования к радианам, градусам и полным оборотам vs абсолютных углов. Короче, меня этот вопрос тоже долгое время бесил, и мне в итоге пришлось писать свою библиотеку…


    1. tminnigaliev Автор
      14.02.2018 11:43

      Nick_mentat, видимо, я сделал недостаточный упор на это в статье. Если совсем по-хорошему, то не 12, а 24 пары. И как раз они все и рассмотрены. У обеих функций есть параметр order, который как раз и задаёт порядок осей. Например,
      XYZr означает, что первый угол отсчитывается вокруг оси X, второй вокруг оси Y, третий вокруг оси Z, при этом сами оси поворачиваются (т.е. после первого поворота оси Y и Z превращаются в Y' и Z', после второго поворота ось Z' превращается в Z")
      ZXZs означает, что первый угол отсчитывается вокруг оси Z, при этом оси остаются на месте, второй угол вокруг оси X, и третий угол снова вокруг той же самой оси Z.

      Таким образом, весь «зоопарк» порядков осей покрывается за счёт параметра order. Предполагается, что если датчик вам возвращает чепуху, вы можете его показания конвертировать с помощью одной из приведённых функций и попробовать по очереди каждый из 24 порядков, на том, на котором результат будет совпадать с вашими ожиданиями, можно будет остановиться.


      1. Nick_mentat
        14.02.2018 12:40

        Да, я всё понял, просто недавно пришлось по работе выписывать вручную этот зоопарк. Очень больная тема для меня, прямо в самое сердце статья пришлась… Спасибо.