【问题标题】:Conversion euler to matrix and matrix to euler将欧拉转换为矩阵和矩阵转换为欧拉
【发布时间】:2011-01-01 02:28:09
【问题描述】:

我正在尝试使用 .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 开始,但我现在完全迷路了,找不到正确的计算方法。我很感激一点帮助。船上有数学家吗?

【问题讨论】:

  • 大部分文献都会用右手坐标系和旋转角度来表达这些操作。您最好翻转一些符号以将数据放入右手系统,进行矩阵运算,然后转换回左手系统。这可能会花费一些额外的操作,但会更容易理解!
  • 这是一个具有连续“关节”的系统,可以旋转以移动相机。实际上,我必须在左手系统中维护 HPB 值,因为用户必须以这种方式输入和查看。最后,我将它们转换为一个右手系统,具有不同的航向原点,以便能够使用右手的 WPF 3D 进行绘制。

标签: c# math matrix 3d euler-angles


【解决方案1】:

首先,应该:

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 |

这给出了反向推导:

棕褐色 B = M12/M22

罪 P = -M32

棕褐色 H = M31/M33

【讨论】:

  • 我试过了,但还是不行。目前在 euler->matrix->euler 过程中,如果我在 HPB 中使用单个角度(例如 h,0,0),则符号是结果的变化(-h,0,0)。我想知道是否可以单独调试每个转换,例如网络上是否有一个页面,其中包含转换示例和矩阵元素的值。我找到了一些,但不是在左手系统/左手旋转中。
  • 我导出了原始旋转矩阵,但我确实想出了一个不同的答案,但我认为我的数学是错误的(或只是等价的):| cosHcosB+sinHsinPsinB | cosBsinHsinP-sinBcosH | cosPsinH | | cosPsinB | cosBcosP | -sinP | | sinBcosHsinP-sinHcosB | sinHsinB+cosBcosHsinP | cosPcosH |
  • 从我上面评论中的矩阵来看,反转如下:tan B = M12/M22, sin P = -M32, tan H = M31/M33
  • 是的,这太完美了!我不是数学家,虽然我确实了解其中的原理,但这对我来说绝对不容易理清不同的手性和变化并重用不是为我使用的那些编写的代码。但是您确实在几分钟之内得到了确切的答案,而我只需要复制粘贴即可。非常感谢先生!我对此表示由衷的感谢。
  • 我已经重做我的答案以包括上述评论。很高兴它有帮助而且是正确的(距离我的数学学位有几年了):)
【解决方案2】:

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

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

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

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

(45°, 60°, -30°) ≡ (-135°, -60°, 150°)   双稳态翻转 "

【讨论】:

    【解决方案3】:

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

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

    约定(如上):

    • 欧拉角:X = Pitch,Y = Yaw,Z = Roll
    • 欧拉顺序:应用旋转,偏航然后俯仰然后滚动
    • 轴:+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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-14
      相关资源
      最近更新 更多