【问题标题】:issue combining 2 Euler angles with GLM将 2 个欧拉角与 GLM 结合起来的问题
【发布时间】:2018-07-22 07:34:30
【问题描述】:

我有以下代码:

#define GLM_ENABLE_EXPERIMENTAL

#include <iostream>
#include <glm/glm.hpp>
#include <glm/ext.hpp>

// combines 2 XYZ euler angles given in degrees
glm::vec3   EulerCombine(const glm::vec3& first, const glm::vec3& second)
{
        glm::vec3 output;
        glm::mat4 m1 = glm::eulerAngleXYZ(glm::radians(first.x), glm::radians(first.y), glm::radians(first.z));
        glm::mat4 m2 = glm::eulerAngleXYZ(glm::radians(second.x), glm::radians(second.y), glm::radians(second.z));
        glm::extractEulerAngleXYZ(m2 * m1, output.x, output.y,  output.z);
        return glm::degrees(output);
}

// applies the XYZ euler rotation on p
glm::vec3   EulerRotate(const glm::vec3& p, const glm::vec3& euler)
{
        glm::vec3 output;
        output = glm::rotateX(p, glm::radians(euler.x));
        output = glm::rotateY(output, glm::radians(euler.y));
        output = glm::rotateZ(output, glm::radians(euler.z));
        return output;
}

int     main(void)
{
        glm::vec3 euler1(30, 20, 90); // euler angles in degrees
        glm::vec3 euler2(20, 30, 10);
        glm::vec3 euler3 = EulerCombine(euler1, euler2);
        glm::vec3 p(-10, 7, 23);

        glm::vec3 result1 = EulerRotate(EulerRotate(p, euler1), euler2);
        glm::vec3 result2 = EulerRotate(p, euler3);

        std::cout << result1.x << " " << result1.y << " " << result1.z << std::endl;
        std::cout << result2.x << " " << result2.y << " " << result2.z << std::endl;
}

但它会打印:

17.9056 -6.99702 17.5622
17.2369 4.15094 19.0699

这是一个问题,因为 result1result2(正在打印的矢量)应该是相同的。

我觉得我的 EulerCombine 函数必须是正确的,因为它几乎完全是 glm,但这意味着 EulerRotate 不正确 - 但我认为 XYZ 欧拉角意味着你先在 X 轴上旋转它,然后在 Y 轴上旋转其次是Z轴。

怎么了?

【问题讨论】:

  • 正确的轮换顺序 - 每次都很容易绊倒......恕我直言,你必须在EulerRotate()glm::rotateX(glm::rotateY(glm::rotateZ(p, glm::radians(euler.z)), glm::radians(euler.y)), glm::radians(euler.x))申请。如果我只能在我们的软件中找到一些我做对的代码......(......我会写一个答案。)
  • 我认为 XYZ 欧拉角意味着你先在 X 轴上旋转它,然后在 Y 轴上再在 Z 轴上旋转。 把它转过来:p 绕 Z 轴旋转,旋转p' 绕 Y 轴,p'' 绕 X 轴旋转。旋转不是可交换的(与平移相反)-因此顺序非常重要(并且使旋转在某种程度上变得复杂)。

标签: c++ glm-math euler-angles


【解决方案1】:

正如 OP 已经怀疑的那样:EulerRotate() 的实现是错误的。应该是:

glm::vec3 EulerRotate(const glm::vec3& p, const glm::vec3& euler)
{
  return glm::rotateX(glm::rotateY(glm::rotateZ(p, glm::radians(euler.z)),
    glm::radians(euler.y)), glm::radians(euler.x));
}

也可以写成(更具可读性):

glm::vec3 EulerRotate(const glm::vec3& p, const glm::vec3& euler)
{
  const glm::vec3 p1 = glm::rotateZ(p, glm::radians(euler.z));
  const glm::vec3 p2 = glm::rotateY(p1, glm::radians(euler.y));
  const glm::vec3 p3 = glm::rotateX(p2, glm::radians(euler.x));
  return p3;
}

Euler angles对应的变换矩阵可以这样拆分:

MrXYZ = MrX · MrY · MrZ

因此:

P' = MrXYZ · P = MrX · M rY · MrZ · P

这是

P' = P 绕 Z 旋转,绕 Y 旋转,绕 X 旋转。

听起来很简单——在实际工作中正确地做到这一点有时会让我发疯。 3D 空间中的旋转不可交换 → 顺序非常重要。

我准备了一个样本来证明这一点。由于我手头没有glm(并且不愿意将其安装在我身边),我使用了自己的代码(我曾经为另一个示例做准备)并尝试尽可能接近 OP 代码:

#include <iostream>
#include "linMath.h"

double radians(double);
Vec3 degrees(Vec3 angles);
Mat4x4 eulerAngleXYZ(double rX, double rY, double rZ);
void extractEulerAngles(const Mat4x4 &mat, double &rX, double &rY, double &rZ);
Vec3 rotateX(const Vec3 &p, double angle);
Vec3 rotateY(const Vec3 &p, double angle);
Vec3 rotateZ(const Vec3 &p, double angle);

// combines 2 XYZ euler angles given in degrees
Vec3 eulerCombine(const Vec3 &first, const Vec3 &second)
{
  const Mat4x4 mat1 = eulerAngleXYZ(radians(first.x), radians(first.y), radians(first.z));
  const Mat4x4 mat2 = eulerAngleXYZ(radians(second.x), radians(second.y), radians(second.z));
  Vec3 output;
  extractEulerAngles(mat2 * mat1, output.x, output.y, output.z);
  return degrees(output);
}

// applies the XYZ euler rotation on p
Vec3 eulerRotate(const Vec3 &p, const Vec3 &euler)
{
#ifndef FIX // Theo:
  Vec3 output;
  output = rotateX(p, radians(euler.x));
  output = rotateY(output, radians(euler.y));
  output = rotateZ(output, radians(euler.z));
  return output;
#else // Dirk:
  return rotateX(rotateY(rotateZ(p, radians(euler.z)), radians(euler.y)), radians(euler.x));
#endif // FIX
}

int main()
{
  Vec3 euler1(30, 20, 90); // euler angles in degrees
  Vec3 euler2(20, 30, 10);
  Vec3 euler3 = eulerCombine(euler1, euler2);
  Vec3 p(-10, 7, 23);
  
  Vec3 result1 = eulerRotate(eulerRotate(p, euler1), euler2);
  Vec3 result2 = eulerRotate(p, euler3);

  std::cout << result1.x << " " << result1.y << " " << result1.z << std::endl;
  std::cout << result2.x << " " << result2.y << " " << result2.z << std::endl;
  // done
  return 0;
}

double radians(double angle) { return degToRad(angle); }

Vec3 degrees(Vec3 angles)
{
  return Vec3(radToDeg(angles.x), radToDeg(angles.y), radToDeg(angles.z));
}

Mat4x4 eulerAngleXYZ(double rX, double rY, double rZ)
{
  return Mat4x4(InitRotX, rX) * Mat4x4(InitRotY, rY) * Mat4x4(InitRotZ, rZ);
}

void extractEulerAngles(const Mat4x4 &mat, double &rX, double &rY, double &rZ)
{
  decompose(mat, RotX, RotY, RotZ, rX, rY, rZ);
}

Vec3 rotateX(const Vec3 &p, double angle)
{
  const Vec4 p_ = Mat4x4(InitRotX, angle) * Vec4(p, 1.0);
  return Vec3(p_.x, p_.y, p_.z);
}

Vec3 rotateY(const Vec3 &p, double angle)
{
  const Vec4 p_ = Mat4x4(InitRotY, angle) * Vec4(p, 1.0);
  return Vec3(p_.x, p_.y, p_.z);
}

Vec3 rotateZ(const Vec3 &p, double angle)
{
  const Vec4 p_ = Mat4x4(InitRotZ, angle) * Vec4(p, 1.0);
  return Vec3(p_.x, p_.y, p_.z);
}

首先我尝试了eulerRotate() 函数的代码,该函数类似于 OP 转换顺序并得到以下输出:

17.9056 -6.99702 17.5622
17.2369 4.15094 19.0698

看来我正确地再现了 OP 的问题。现在,我定义了FIX 以使用正确的转换顺序并得到以下输出:

12.1019 -22.7589 3.68476
12.1019 -22.7589 3.68476

Live Demo on Wandbox

现在,两种计算都提供了与预期相同的结果。

我认为 XYZ 欧拉角意味着你在 X 轴上旋转它,然后是 Y 轴,然后是 Z 轴。

不完全是。如上所示,必须从右向左解释。

【讨论】:

    猜你喜欢
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 2020-06-09
    • 2021-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    相关资源
    最近更新 更多