Угол Эйлера к Кватерниону, затем кватернион к углу Эйлера
Я использую lib glm (http://glm.g-truc.net/) для тестового кватерниона, но у меня есть проблема; когда я преобразую угол Эйлера в кватернион, то немедленно кватернион в углы Эйлера, мой результат полностью отличается от моих начальных углов Эйлера. Это нормально? Может быть потому, что повороты не communative?
код:
#include <glmquaternion.hpp>
#include <math.h>
#define PI M_PI
#define RADTODEG(x) ( (x) * 180.0 / PI )
#define DEGTORAD(x) ( (x) * PI / 180.0 )
int main( void )
{
float RotX = 90.f;
float RotY = 180.f;
float RotZ = -270.f;
if ( RotX || RotY || RotZ )
{
std::cout << "Init: x= " << RotX << ", y= " << RotY << ", z= " << RotZ << "n";
glm::quat key_quat(glm::detail::tvec3<float>(DEGTORAD( RotX ),
DEGTORAD( RotY ),
DEGTORAD( RotZ )));
glm::detail::tvec3<float> v = glm::eulerAngles(key_quat);
/* // the result is even worse with this code here
RotX = RADTODEG(v.x);
RotY = RADTODEG(v.y);
RotZ = RADTODEG(v.z);
*/
RotX = v.x;
RotY = v.y;
RotZ = v.z;
std::cout << "Final: x= " << RotX << ", y= " << RotY << ", z= " << RotZ << "n";
}
return (0);
}
результат:
Init: x= 90, y= 180, z= -270
Final: x= -90, y= -3.41509e-006, z= -90
спасибо заранее o/
4 ответов
Да, это нормально. есть 2 пути для представления того же вращения с углами Эйлера.
Мне лично не нравятся углы Эйлера,они портят стабильность вашего приложения. Я бы избегал их. Кроме того, они не очень удобно либо.
взгляните на эту страницу. В нем есть все, что вам нужно (даже некоторые образцы кода!) для работы с 3D-преобразований.
Если вам понадобятся углы кватерниона к Эйлеру, но вам нужен произвольный порядок вращения, я наткнулся на сайт с кодом преобразования. Иногда весь фокус в том, чтобы найти правильный порядок вращения. (Кстати, порядки, которые имеют одну и ту же букву дважды, как XYX, являются правильными углами Эйлера, но такие, как XYZ, являются углами Тейта-Брайана).
вот ссылка: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;
}
}
Эйлера -> Кватернион
извлеченный из трех.js.
вот кусок кода, который работает для меня:
function eulerToQuaternion(eulerXYZ) {
var c1 = Math.cos(eulerXYZ[0] / 2),
c2 = Math.cos(eulerXYZ[1] / 2),
c3 = Math.cos(eulerXYZ[2] / 2),
s1 = Math.sin(eulerXYZ[0] / 2),
s2 = Math.sin(eulerXYZ[1] / 2),
s3 = Math.sin(eulerXYZ[2] / 2),
x = s1 * c2 * c3 + c1 * s2 * s3,
y = c1 * s2 * c3 - s1 * c2 * s3,
z = c1 * c2 * s3 + s1 * s2 * c3,
w = c1 * c2 * c3 - s1 * s2 * s3;
return [x, y, z, w];
};
function calculate() {
var quat = eulerToQuaternion([document.querySelector('#x').value, document.querySelector('#y').value, document.querySelector('#z').value]);
document.querySelector('#result').innerHTML = quat.join(' ');
}
<h3>Euler radians in XYZ order:</h3>
<fieldset>
<label>X:
<input id="x" value="1.5" />
</label>
<label>Y:
<input id="y" value="1" />
</label>
<label>Z:
<input id="z" value="0" />
</label>
<button onClick="calculate()">To Quaternion</button>
</fieldset>
<h3>X Y Z W result:</h3>
<div id="result"></div>