Conversão Euler para Matrix e Matrix para Euler
-
22-09-2019 - |
Pergunta
Estou tentando converter uma rotação 3D descrita no termo dos ângulos de Euler em uma matriz e depois atrás, usando .NET/C#. Minhas convenções são:
- Sistema canhoto (x direita, y top, z para frente)
- Ordem de rotações: indo em torno de Y, arrementa em volta x, banco em torno de z
- As rotações são positivas usando a regra da esquerda (apontando o polegar para +infinito)
Meu julgamento é:
Euler para Matrix (Eu removi a parte de tradução x, y, z para simplificação)
Matrix3D matrix = new Matrix3D() {
M11 = cosH * cosB - sinH * sinP * sinB,
M12 = - sinB * cosP,
M13 = sinH * cosB + cosH * sinP * sinB,
M21 = cosH * sinB + sinH * sinP * cosB,
M22 = cosB * cosP,
M23 = sinB * sinH - cosH * sinP * cosB,
M31 = - sinH * cosP,
M32 = - sinP,
M33 = cosH * cosP,
};
Matriz para Euler
const double RD_TO_DEG = 180 / Math.PI;
double h, p, b; // angles in degrees
// extract pitch
double sinP = -matrix.M23;
if (sinP >= 1) {
p = 90; } // pole
else if (sinP <= -1) {
p = -90; } // pole
else {
p = Math.Asin(sinP) * RD_TO_DEG; }
// extract heading and bank
if (sinP < -0.9999 || sinP > 0.9999) { // account for small angle errors
h = Math.Atan2(-matrix.M31, matrix.M11) * RD_TO_DEG;
b = 0; }
else {
h = Math.Atan2(matrix.M13, matrix.M33) * RD_TO_DEG;
b = Math.Atan2(matrix.M21, matrix.M22) * RD_TO_DEG; }
Deve estar errado. Se eu pegar 3 ângulos, convertê -los em uma matriz e converter a matriz de volta em ângulos, o resultado se diferente dos valores íntimos.
Eu naveguei em vários sites com fórmulas diferentes, começando com euclideanspace.com, mas agora estou completamente perdido e não consigo encontrar os cálculos certos. Agradeço uma pequena ajuda. Existe um matemático a bordo?
Solução
Em primeiro lugar, deveria:
sinP = -matrix.M32
EDITAR: Solução completa segue
Minha derivação:
Rx(P)=| 1 0 0 |
| 0 cos P -sin P |
| 0 sin P cos P |
Ry(H)=| cos H 0 sin H |
| 0 1 0 |
| -sin H 0 cos H |
Rz(B)=| cos B -sin B 0 |
| sin B cos B 0 |
| 0 0 1 |
Multiplicado com o seu pedido:
R = Ry(H)*Rx(P)*Rz(B)
= | cos H*cos B+sin H*sin P*sin B cos B*sin H*sin P-sin B*cos H cos P*sin H |
| cos P*sin B cos B*cos P -sin P |
| sin B*cos H*sin P-sin H*cos B sin H*sin B+cos B*cos H*sin P cos P*cos H |
Que dá derivações reversas:
tan b = m12/m22
sin p = -m32
tan h = m31/m33
Outras dicas
Sua ideia está errada: "Deve estar errado. Se eu levar 3 ângulos, convertê -los em uma matriz e converter a matriz de volta em ângulos, o resultado se diferente dos valores ínticos". Teria sido bonito, mas não é necessariamente verdadeiro. Em geral, mais de um trigêmeo de ângulos de Euler (fixado a convenção) leva à mesma orientação no espaço. Isso não significa que, no seu cálculo, não existe um erro. Da Wikipedia: "Por exemplo, suponha que usemos a convenção ZYZ acima; então temos os seguintes pares equivalentes: (90 °, 45 °, −105 °) ≡ (−270 °, −315 °, 255 °) de múltiplos de 360 ° (72 °, 0 °, 0 °) ≡ (40 °, 0 °, 32 °) alinhamento singular (45 °, 60 °, −30 °) ≡ (−135 °, −60 °, 150 °) FILHA "
Há um grande número de combinações dessas funções à medida que a resposta muda, dependendo de suas convenções. Normalmente, estou usando o DirectX e as mesmas convenções que a unidade. Além disso, meu plano de fundo é o Flightstims, o espaço e os mapas, então a guinada e depois o rolo corresponde ao estilo Lat/Lon também.
Não estar claro sobre as convenções ou ter funções incompatíveis de composição/decomposição pode levar a bugs muito ímpares. Também vale a pena ter em mente que vários conjuntos de ângulos de Euler podem produzir a mesma orientação.
Convenções (como acima):
- Ângulos de Euler: x = pitch, y = guinada, z = rolo
- Ordem de Euler: rotação aplicada, guinada e depois enrole
- Eixos: +x à direita, +y para cima, +z para a frente
- Matrizes: Convenções DirectX (usando simplemath.h de ms Directxtk)
Para converter para a versão OpenGL, dê uma olhada isto.
eu tenho tomado Mike TunnicliffeA resposta e converteu -a ao código C ++ e a adicionou à minha biblioteca. Espero que outras pessoas economizem algum tempo usando -o.
Vale a pena notar que a função de composição limpa a 4ª coluna e o componente de tradução para a identidade, e a função decomposta assume que o elemento de rotação 3x3 contém rotação pura (ou seja, sem escala etc.).
Em primeiro lugar, o código para gerar uma matriz de Eulers:
//====================================================================================================
// MatrixFromYawPitchRoll
//
// Create matrix based on provided yaw (heading), pitch and roll (bank).
//
// Assumptions:
// Euler: X = Pitch, Y = Yaw, Z = Roll
// Applied: Yaw then pitch then roll
// Axes: X = Right, Y = Up, Z = Forward
// DirectX: Matrices are row major (http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html)
//
// Code is based on Mike Tunnicliffe's answer to this question:
// https://stackoverflow.com/questions/1996957/conversion-euler-to-matrix-and-matrix-to-euler
inline void MatrixFromYawPitchRoll(
const DirectX::SimpleMath::Vector3& euler,
DirectX::SimpleMath::Matrix& mat)
{
float cosY = cosf(euler.y); // Yaw
float sinY = sinf(euler.y);
float cosP = cosf(euler.x); // Pitch
float sinP = sinf(euler.x);
float cosR = cosf(euler.z); // Roll
float sinR = sinf(euler.z);
mat = DirectX::SimpleMath::Matrix::Identity;
mat._11 = cosY * cosR + sinY * sinP * sinR;
mat._21 = cosR * sinY * sinP - sinR * cosY;
mat._31 = cosP * sinY;
mat._12 = cosP * sinR;
mat._22 = cosR * cosP;
mat._32 = -sinP;
mat._13 = sinR * cosY * sinP - sinY * cosR;
mat._23 = sinY * sinR + cosR * cosY * sinP;
mat._33 = cosP * cosY;
}
Em seguida, código para recuperar os ângulos de Euler da Matrix:
//====================================================================================================
// MatrixDecomposeYawPitchRoll
//
// Extract the rotation contained in the provided matrix as yaw (heading), pitch and roll (bank) in
// radiuans.
//
// Assumptions:
// Euler: X = Pitch, Y = Yaw, Z = Roll
// Applied: Yaw then pitch then roll
// Axes: X = Right, Y = Up, Z = Forward
// DirectX: Matrices are row major (http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html)
//
// Code is based on Mike Tunnicliffe's answer to this question:
// https://stackoverflow.com/questions/1996957/conversion-euler-to-matrix-and-matrix-to-euler
inline void MatrixDecomposeYawPitchRoll(
const DirectX::SimpleMath::Matrix& mat,
DirectX::SimpleMath::Vector3& euler)
{
euler.x = asinf(-mat._32); // Pitch
if (cosf(euler.x) > 0.0001) // Not at poles
{
euler.y = atan2f(mat._31, mat._33); // Yaw
euler.z = atan2f(mat._12, mat._22); // Roll
}
else
{
euler.y = 0.0f; // Yaw
euler.z = atan2f(-mat._21, mat._11); // Roll
}
}