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

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

Я работаю над симулятором движения с 2 DOF (pitch and roll). Я читаю матрицу преобразования из игры, и мне нужно получить углы и отправить аппаратное обеспечение для управления двигателями. Поскольку углы Эйлера имеют особенности, я не могу их использовать. Он ведет себя следующим образом:

BeneficialOrganicAchillestang.gif

когда ему это должно понравиться:

PoliticalComplexHalibut.gif

Я подготовил онлайн-пример, чтобы лучше показать проблему:

// Get euler angles from model matrix
var mat = model.matrix;
mat.transpose();

var e = new THREE.Euler();
e.setFromRotationMatrix(mat, 'XZY');
var v = e.toVector3();

var pitch = -v.z;
var roll  = -v.x;

http://jsfiddle.net/qajro0ny/3/

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

  • На симуляторе нет оси рыскания.
  • Даже если есть ось рыскания, двигатели просто не ведут себя как компьютерная графика, т.е. им нужно время, чтобы добраться до целевого положения.

Я читал о блокировке карданного вала и даже реализовал фильтр Эйлера, но это не сработало, как ожидалось. Большинство советов о блокировке карданов состояли в том, чтобы использовать кватернионы, но я не могу управлять физическим двигателем с кватернионом (или я могу?).

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

Мне нужно обработать это каким-либо другим способом.

Я попробовал умножить оси векторы на матрицу, а затем с помощью кросс-точечного произведения получить углы, но это тоже не удалось. Я думаю, что для того, чтобы получить это право, должна быть также осевая репроцессия, но я не мог понять это. Но что-то говорит мне, что это правильный способ сделать это. Это было примерно так: http://jsfiddle.net/qajro0ny/53/

Тогда я придумал другую идею. Я знаю предыдущую позицию, поэтому, возможно, выполните следующие действия:

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

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

Это выглядело так: http://jsfiddle.net/qajro0ny/52/

И та же логика, но непосредственно с матрицами: http://jsfiddle.net/qajro0ny/54/

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

Я мог бы не понимать или пропустить что-то, поэтому вот все, что я нашел и попробовал:

Ссылки: http://pastebin.com/3G0dYLvu

Код: http://pastebin.com/PiZKwE2t (я собрал все это вместе, чтобы оно было грязным)

Мне что-то не хватает, или я смотрю на это с неправильного угла.

4b9b3361

Ответ 1

Если вы знаете, что матрица содержит только два вращения (в данном порядке T = RZ * RX), вы можете сделать что-то вроде следующего:

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

function calculateAngles() {
    var mat = model.matrix;

    //calculates the angle from the local x-axis
    var pitch = Math.atan2(mat.elements[1], mat.elements[0]);

    //this matrix is used to remove the first rotation
    var invPitch = new THREE.Matrix4();
    invPitch.makeRotationZ(-pitch);

    //this matrix will only contain the roll rotation
    //  rollRot = RZ^-1 * T
    //          = RZ^-1 * RZ * RX
    //          = RX
    var rollRot = new THREE.Matrix4();
    rollRot.multiplyMatrices(invPitch, mat);

    //calculate the remaining angle from the local y-axis
    var roll  = Math.atan2(rollRot.elements[6], rollRot.elements[5]);

    updateSimAngles(pitch, roll);
}

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

Ответ 2

Ваше представление об использовании дельтов вращения звучит довольно многообещающе.

Я не совсем уверен, что вы имеете в виду, когда "фреймы не синхронизировались". Могу себе представить, что ваши вычисления с кватернионами могут быть не точными на 100% (вы используете плавающие точки?), Что приводит к небольшим ошибкам, которые складываются и, наконец, приводят к асинхронным движениям.

Дело в том, что вы должны использовать unit-quaternions для представления поворотов. Вы можете сделать это в теоретической модели, но если вы представляете кватернионы по 4 числам с плавающей запятой, ваши кватернионы в большинстве случаев не будут единичными кватернионами, а только будут действительно близкими (их норма 1+e для некоторых небольших - и возможно отрицательное значение e). Это нормально, так как вы не заметите этих небольших различий, однако, если вы бросаете тонны операций на свои кватернионы (которые вы делаете, постоянно двигая моделью и вычисляя дельт), эти небольшие ошибки складываются. Таким образом, вам нужно постоянно перенормировать свои кватернионы, чтобы максимально приблизить их к квартальным кватернионам, чтобы ваши расчеты, особенно переход к углам Эйлера, оставались (почти) точными.

Ответ 3

Я добавил два цента в http://jsfiddle.net/qajro0ny/58/, в основном применяя работы http://blogs.msdn.com/b/mikepelton/archive/2004/10/29/249501.aspx, который я наткнулся несколько лет назад. Это в основном сводится к

var a = THREE.Math.clamp(-mat.elements[6], -1, 1);
var xPitch = Math.asin(a);
var yYaw = 0;
var zRoll = 0;
if (Math.cos(xPitch) > 0.0001)
{
  zRoll = -Math.atan2(mat.elements[4], mat.elements[5]);
  yYaw = -Math.atan2(mat.elements[2], mat.elements[10]);
}
else
{
  zRoll = -Math.atan2(-mat.elements[1], mat.elements[0]);
}

Я заметил, что предполагаемое отображение рыскания, высоты тона, валка к осям (y, x, z), используемое в левой системе координат слева, отличается от того, которое вы используете OpenGL/WebGL, поэтому, возможно, это должно быть наконец, разобрался.

Надеюсь, это поможет.

Ответ 4

Я предполагаю, что вы на самом деле могли управлять физическими двигателями с кватернионами. Помните, что кватернион представляет собой ось вращения (x, y, z) и угол. Я полагаю, что у вас есть три двигателя (для каждой оси), скорость вращения которых вы можете масштабировать. Теперь, масштабируя скорость вращения этих трех двигателей, вы можете установить определенную ось вращения, и, тщательно измерив время, вы можете установить определенный угол поворота. Это должно быть проще, чем преобразовать в углы углов Эйлера.