将欧拉转换为矩阵以及矩阵转换为欧拉

问题描述 投票:0回答:4

我正在尝试使用 .NET/C# 将用欧拉角描述的 3D 旋转转换为矩阵,然后再转换回来。我的约定是:

  • 左手系统(x 右,y 上,z 向前)
  • 旋转顺序:绕 y 前进、绕 x 俯仰、绕 z 倾斜
  • 使用左手定则旋转为正(拇指指向+无穷大)

我的试用是:

欧拉到矩阵(为了简化我删除了x,y,z翻译部分)

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,
};

矩阵到欧拉

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; }

一定是错的。如果我取 3 个角度,将它们转换为矩阵,然后将矩阵转换回角度,结果与初始值不同。

我浏览了几个具有不同公式的网站,从 euclideanspace.com 开始,但我现在完全迷失了,找不到正确的计算。我很感激你的一点帮助。船上有数学家吗?

c# math matrix 3d euler-angles
4个回答
12
投票

首先应该:

sinP = -matrix.M32

编辑:完整解决方案如下

我的推导:

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 |

乘以您的订单:

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 |

这给出了反向推导:

tan B = M12/M22

sin P = -M32

tan H = M31/M33


8
投票

这些函数有大量的组合,答案会根据您的约定而变化。我通常使用 DirectX 和与 Unity 相同的约定。另外,我的背景是飞行模拟、空间和地图,所以偏航、俯仰、滚动也符合纬度/经度风格。

不清楚约定或不匹配的组合/分解函数可能会导致非常奇怪的错误。还值得记住的是,多组欧拉角可以产生相同的方向。

约定(如上所述):

  • 欧拉角:X = 俯仰,Y = 偏航,Z = 滚动
  • 欧拉顺序:应用旋转,偏航,然后俯仰,然后滚动
  • 轴:+X 向右、+Y 向上、+Z 向前
  • 矩阵:DirectX 约定(使用 MS DirectXTK 中的 SimpleMath.h)

要转换为 OpenGL 版本,请查看 this

我已经采用了 Mike Tunnicliffe 的答案并将其转换为 C++ 代码并将其添加到我的库中。我希望其他人使用它可以节省一些时间。

值得注意的是,compose 函数将第 4 列和平移分量清除为同一,并且 decompose 函数假设 3x3 旋转元素包含纯旋转(即没有缩放等)。

首先是从欧拉生成矩阵的代码:

//====================================================================================================
// 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;
}

然后编码以从矩阵返回欧拉角:

//====================================================================================================
// 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
    }
}

3
投票

你的想法是错误的:“这一定是错误的。如果我取3个角度,将它们转换为矩阵,然后将矩阵转换回角度,结果将与初始值不同。”这本来很美好,但不一定是真的。一般来说,超过一个三元组的欧拉角(固定约定)会导致空间中的相同方向。但这并不意味着您的计算没有错误。 来自维基百科:

“例如,假设我们使用上面的 zyz 约定;那么我们有以下等价对:

(90°, 45°, −105°) ≡ (−270°, −315°, 255°)   360°的倍数

(72°, 0°, 0°) ≡ (40°, 0°, 32°)   奇异对齐

(45°, 60°, −30°) ≡ (−135°, −60°, 150°)   双稳态翻转 ”


0
投票

多年来,我见过许多 MatrixToEulerAngles 的实现,它们通常只涵盖一种情况(轴顺序),当它没有达到“我的特殊情况”时,结果让我有点困惑。 最后我在这里找到了Euler.js中捆绑的所需公式: 在此输入链接描述

© www.soinside.com 2019 - 2024. All rights reserved.