Как решить проблему блокировки карданного подвеса с помощью накопительных матричных преобразований

12

Я читаю онлайн книгу Джейсона МакКессона «Обучение современному программированию в трехмерной графике».

На данный момент я имею дело с проблемой блокировки карданного подвеса и с тем, как ее решить с помощью кватернионов.

Однако прямо здесь, на странице Quaternions .

Часть проблемы заключается в том, что мы пытаемся сохранить ориентацию в виде серии из 3-х накопленных осевых вращений. Ориентации - это ориентации, а не вращения. И ориентации, конечно, не серия вращений. Поэтому нам нужно рассматривать ориентацию корабля как ориентацию, как определенную величину.

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

Также:

Первой мыслью в этом направлении было бы сохранение ориентации в виде матрицы. Когда приходит время изменить ориентацию, мы просто применяем преобразование к этой матрице, сохраняя результат как новую текущую ориентацию.

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

Понятие, которое я понимаю, однако я не понимаю, как, если накопление матричных преобразований является решением этой проблемы, как код, приведенный на предыдущей странице , не только это.

Вот код:

void display()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutil::MatrixStack currMatrix;
    currMatrix.Translate(glm::vec3(0.0f, 0.0f, -200.0f));
    currMatrix.RotateX(g_angles.fAngleX);
    DrawGimbal(currMatrix, GIMBAL_X_AXIS, glm::vec4(0.4f, 0.4f, 1.0f, 1.0f));
    currMatrix.RotateY(g_angles.fAngleY);
    DrawGimbal(currMatrix, GIMBAL_Y_AXIS, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
    currMatrix.RotateZ(g_angles.fAngleZ);
    DrawGimbal(currMatrix, GIMBAL_Z_AXIS, glm::vec4(1.0f, 0.3f, 0.3f, 1.0f));

    glUseProgram(theProgram);
    currMatrix.Scale(3.0, 3.0, 3.0);
    currMatrix.RotateX(-90);
    //Set the base color for this object.
    glUniform4f(baseColorUnif, 1.0, 1.0, 1.0, 1.0);
    glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));

    g_pObject->Render("tint");

    glUseProgram(0);

    glutSwapBuffers();
}

Насколько я понимаю, не то, что он делает (модифицирует матрицу в стеке), рассматривает накопление матриц, поскольку автор объединил все отдельные преобразования вращения в одну матрицу, которая хранится в верхней части стека.

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

Одна вещь, которая не имеет смысла для меня: если матрица определяет разницу между двумя «пробелами», то почему вращение вокруг оси Y для, скажем, вращения, не ставит точку в «пространстве прокрутки» «который затем может быть снова преобразован по отношению к этому броску ... Другими словами, никакие дальнейшие преобразования в этой точке не должны относиться к этому новому« пространству вращения »и, следовательно, не должны иметь вращение относительно предыдущего» Модельное пространство », вызывающее карданный замок.

Вот почему блокировка кардана происходит правильно? Это потому, что мы вращаем объект вокруг осей X, Y и Z, а не вращаем объект вокруг его собственных относительных осей. Или я не прав?

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

Итак, в заключение:

  • В чем разница между вращением и ориентацией?
  • Почему код связан не с примером накопления матричных преобразований?
  • Какова реальная, конкретная цель матрицы, если я ошибся?
  • Как можно решить проблему блокировки карданного подвеса с помощью накопления матричных преобразований?
  • Кроме того, в качестве бонуса: почему преобразования после поворота по-прежнему относятся к «пространству модели»?
  • Еще один бонус: я ошибаюсь в предположении, что после трансформации будут происходить дальнейшие трансформации относительно тока?

Кроме того, если это не подразумевается, я использую OpenGL, GLSL, C ++ и GLM, так что примеры и объяснения в терминах этого очень ценятся, если не нужны.

Чем больше деталей, тем лучше!

Заранее спасибо.

Люк Сан Антонио Бялецкий
источник

Ответы:

11

Я не уверен в хорошем способе предвосхищать это, кроме того, я надеюсь, что к концу это будет хорошо связано. Тем не менее, давайте погрузимся в:

Вращение и ориентация различны, потому что первый описывает преобразование, а второй описывает состояние. Вращение - это то, как объект попадает в ориентацию , а ориентация - это локальное вращаемое пространство объекта . Это может быть напрямую связано с тем, как эти два представлены математически: матрица хранит преобразования из одного координатного пространства в другое (у вас это было правильно), а кватернион напрямую описывает ориентацию. Таким образом, матрица может описывать только то, как объект попадает в ориентацию через серию вращений. Проблема с этим, однако, Gimbal Lock.

Замок на карданном подвесе демонстрирует сложность приведения объекта в ориентацию с помощью серии вращений. Проблема возникает, когда по крайней мере две оси вращения совпадают:

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

Теперь я на самом деле скептически отношусь к тому, что накопление матриц является полным решением этой проблемы, потому что накопление матриц (следовательно, накопление вращений) - это именно то, что может вызвать проблему блокировки Гимбала в первую очередь. Правильный способ обработки преобразования кватернионом состоит в том, чтобы выполнить умножение кватерниона на точке:

pTransformed = q * pAsQuaternion * qConjugate

или путем преобразования кватерниона в матрицу и преобразования точки с использованием этой матрицы.

Простое вращение матрицы (например, поворот на 45 градусов) всегда будет определяться в глобальном пространстве. Если вы хотите применить преобразование в локальном пространстве, вам придется преобразовать преобразование в локальное пространство этих объектов. Звучит странно, поэтому я уточню. Вот где важен порядок вращений. Я рекомендую взять книгу здесь, чтобы вы могли следить за изменениями.

Начните с книжной квартиры, ее обложка обращена к потолку и ориентирована так, как будто вы собираетесь ее открыть и начать читать. Теперь наклоните переднюю часть книги на 45 градусов (передняя обложка должна быть примерно обращена к вам):

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(45);

Теперь предположим, что вы хотите отрегулировать угол наклона книги на 45 градусов (я думаю, что я предполагаю правую систему координат, так что это изменит заголовок влево), и вы хотите, чтобы это применялось к локальной книге координатное пространство, так что обложка книги все еще будет стоять перед вами:

bookMatrix.RotateY(45);

Проблема в том, что это вращение происходит в глобальном координатном пространстве, поэтому обложка книги будет обращена к вашему правому плечу. Чтобы это изменение курса произошло в локальном координатном пространстве, вы должны сначала применить его!

glutil::MatrixStack bookMatrix;
bookMatrix.RotateY(45);
bookMatrix.RotateX(45);

Попробуйте! Начните книгу с потолка снова. Измените его отклонение на 45 градусов, а затем наклоните его на 45 градусов вдоль глобальной оси X (бегущей слева направо). Эту ориентацию вы ожидали с шагом 45 и рысканием 45 в локальном пространстве книги.

Что это значит? Все, что на самом деле сводится к тому, что порядок операций имеет значение. Сделанные преобразования сначала становятся локальными преобразованиями в контексте преобразований, выполняемых впоследствии. Становится много, чтобы обернуть голову, и вот так кватернионы избавляют от многих неприятностей. Пропустить все, что зависит от заказа.

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

Чтобы подвести итог и обратиться к вашему первоначальному вопросу: преобразования накопительной матрицы действительно не решат проблему блокировки Gimbal, если преобразования не будут тщательно выбраны и применены в точном порядке. Поэтому всегда используйте кватернионы и применяйте кватернионы к точкам, используя умножение кватернионов.

Надеюсь это поможет :)

kevintodisco
источник
4
просто для записи, кватернионы могут по-прежнему вводить карданный замок, если описываются через углы Эйлера; как вы будете делать то же самое вычисление другим способом (кватернионы, а не матрицы)
concept3d
1
@ concept3d - поздравляю с упоминанием этого! Важно понять, что делает механизм карданного подвеса склонным к потере степени свободы: это похоже на роботизированное соединение, по своей сути описывающее переопределенную систему уравнений. Если вы строите этот механизм с кватернионами, матрицами или магией, у вас все равно останутся неоднозначности - он понимает его и не использует его, во-первых, является реальным решением (если вам не нужно использовать его для каких-то демонстрационных или технических целей) ,
Теодрон
кватернионы трудно представить, так как я всегда думаю об этом, они (единичные кватернионы) представляют пространство 3-сферы, следовательно, могут представлять любую ориентацию, в то время как я понимаю, что каждый из углов Эйлера представляет круги / туро, следовательно, это не полная сфера. не очень точный способ представления ориентации (3 круга / тора не могут действительно генерировать каждую возможную ориентацию, если они не вращаются независимо, что невозможно в случае углов Эйлера), не уверен, что я объяснил точно :)
concept3d
1

Матричные накопления могут фактически решить замок Gimbal. Накапливая повороты, вы добавляете подвески, позволяющие любое произвольное вращение. Диаграмма, которую предоставил ktodisco, показывает блокировку карданного подвеса на левой диаграмме. Матрица для этой ориентации может быть определена как:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);

Из-за вращения y-кардана X и Z-карданы теперь заблокированы, поэтому мы потеряли один градус движения. На данный момент у нас нет рыскания (локальный y, глобальный z) с использованием этих трех гимбалов. Но, добавив еще один кардан, я могу вращаться вокруг y:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);
bookMatrix.RotateY(90);

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

Джастин Эрлих
источник