【问题标题】:Get pitch and roll from matrix without singularities从没有奇点的矩阵中获取俯仰和滚动
【发布时间】:2015-09-14 07:24:12
【问题描述】:

我正在研究具有 2 DOF(俯仰和滚动)的运动模拟器。我正在从游戏中读取变换矩阵,我需要获取角度并发送到硬件以驱动电机。 由于欧拉角有奇点,我不能真正使用它们。它的行为如下:


(来源:gfycat.com

什么时候应该是这样的:


(来源:gfycat.com

我准备了在线示例以更好地说明问题:

// Get euler angles from model matrix
var mat = model.matrix;
mat.transpose();

var e = new THREE.Euler();
e.setFromRotationMatrix(mat, 'XZY');
var v = e.toVector3();

var pitch = -v.z;
var roll  = -v.x;

http://jsfiddle.net/qajro0ny/3/

据我了解,这里有两个问题。

  1. 模拟器上没有偏航轴。
  2. 即使有偏航轴,电机也不会像计算机图形那样运行,即它们需要时间才能到达目标位置。

我已经阅读了有关万向节锁的信息,甚至实现了欧拉过滤器,但这并没有按预期工作。 大多数关于云台锁定的建议是使用四元数,但我不能用四元数驱动物理电机(或者我可以吗?)。

轴顺序在这里并不重要,因为改变它只会将奇点从一个轴移动到另一个轴。

我需要以其他方式处理这个问题。

我尝试将轴向量乘以矩阵,然后使用叉积和点积来获得角度,但这也失败了。我认为还应该涉及到轴重投影才能做到这一点,但我无法弄清楚。但有件事告诉我,这是正确的做法。是这样的:http://jsfiddle.net/qajro0ny/53/

然后我想出了不同的想法。我知道以前的职位,所以也许做以下事情:

  1. 将矩阵转换为四元数
  2. 计算当前和前一个四元数之间的差异
  3. 将得到的四元数转换为欧拉角
  4. 将这些角度添加到静态俯仰、横滚和偏航变量中。

所以我尝试了,并且......它成功了!任何方向都没有奇点,俯仰、滚动和偏航的完美 360 度旋转。完美的解决方案!除了......它不是。帧没有同步,所以过了一会儿,角度偏离了它们应该的样子。我一直在考虑某种同步机制,但我认为这不是正确的方法。

看起来像这样:http://jsfiddle.net/qajro0ny/52/

同样的逻辑,但直接使用矩阵:http://jsfiddle.net/qajro0ny/54/

我在网上到处搜索,我已经阅读了几十篇论文和其他问题/帖子,我简直不敢相信没有什么真正适合我的案例。

我可能不明白或错过了什么,所以这里是我找到并尝试过的所有内容:

链接:http://pastebin.com/3G0dYLvu

代码:http://pastebin.com/PiZKwE2t(我把它们放在一起,所以很乱)

我一定是遗漏了什么,或者我从错误的角度看这个。

【问题讨论】:

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


    【解决方案1】:

    如果您知道矩阵只包含两个旋转(按照给定的顺序T = RZ * RX),那么您可以执行以下操作:

    局部 x 轴不受第二次旋转的影响。因此,您可以仅使用局部 x 轴计算第一个角度。然后你可以从矩阵中移除这个旋转并从其他两个轴中的任何一个计算剩余的角度:

    function calculateAngles() {
        var mat = model.matrix;
    
        //calculates the angle from the local x-axis
        var pitch = Math.atan2(mat.elements[1], mat.elements[0]);
    
        //this matrix is used to remove the first rotation
        var invPitch = new THREE.Matrix4();
        invPitch.makeRotationZ(-pitch);
    
        //this matrix will only contain the roll rotation
        //  rollRot = RZ^-1 * T
        //          = RZ^-1 * RZ * RX
        //          = RX
        var rollRot = new THREE.Matrix4();
        rollRot.multiplyMatrices(invPitch, mat);
    
        //calculate the remaining angle from the local y-axis
        var roll  = Math.atan2(rollRot.elements[6], rollRot.elements[5]);
    
        updateSimAngles(pitch, roll);
    }
    

    这当然只有在给定矩阵符合要求时才有效。它不能包含第三次旋转。否则,您可能需要找到非线性最小二乘解。

    【讨论】:

    • 谢谢,但不幸的是我的矩阵包含第三次旋转。您能否详细说明非线性最小二乘解决方案?我阅读了有关 Levenberg–Marquardt 算法的信息,但不知道如何使用它来解决我的问题。
    • Levenberg Marquardt 可能是一个不错的选择。基本上,您想找到 || T - RZ(pitch) * RX(roll) ||_2 的最小化器 pitchroll 。大多数库都允许您或多或少地直接插入该定义。但我不知道任何 JavaScript 库。此外,如果第三次旋转相对较大,您将始终获得翻转方向。您可以尝试使用正则化术语(上一个和当前解决方案的差异)来规避这一点。但我不确定这是否能完成这项工作。
    【解决方案2】:

    您关于使用旋转增量的想法实际上听起来很有希望。

    不过,我不太确定您所说的“帧未同步”是什么意思。我可以想象您使用四元数的计算可能不是 100% 精确(您使用浮点数吗?)这会导致小错误叠加并最终导致异步运动。

    关键是您应该使用单位四元数来表示旋转。您可以在理论模型中做到这一点,但如果您用 4 个浮点数表示四元数,则在大多数情况下,您的四元数不会是单位四元数,而只会非常接近(它们的标准是 1+e 对于一些小的 - 和可能为负 - 值 e)。这没关系,因为您不会注意到这些微小的差异,但是如果您在四元数上进行大量操作(您通过不断地旋转模型并计算增量来完成),这些小错误将会堆积起来。因此,您需要不断地重新归一化您的四元数,以使它们尽可能接近单位四元数,以便您的计算 - 特别是转换回欧拉角 - 保持(几乎)准确。

    【讨论】:

    • 即使我纯粹在 64 位双精度上操作(包括游戏中的初始矩阵)并在每次计算后归一化四元数,在持续移动几分钟后,角度仍然有很大偏差。我每秒读取矩阵 50 次。我现在正在尝试创建某种混合解决方案,使用四元数来跟踪正确的方向,但从欧拉角计算最终角度。
    • 你可以在这个小提琴中看到它的作用:jsfiddle.net/qajro0ny/52。也许我在某个地方犯了错误?当然这只是 JS 中的一个例子,但在 C++ 中的行为几乎相同
    【解决方案3】:

    我在http://jsfiddle.net/qajro0ny/58/ 中添加了我的两分钱,基本上应用了我几年前偶然发现的http://blogs.msdn.com/b/mikepelton/archive/2004/10/29/249501.aspx 的作品。它基本上归结为

    var a = THREE.Math.clamp(-mat.elements[6], -1, 1);
    var xPitch = Math.asin(a);
    var yYaw = 0;
    var zRoll = 0;
    if (Math.cos(xPitch) > 0.0001)
    {
      zRoll = -Math.atan2(mat.elements[4], mat.elements[5]);
      yYaw = -Math.atan2(mat.elements[2], mat.elements[10]);
    }
    else
    {
      zRoll = -Math.atan2(-mat.elements[1], mat.elements[0]);
    }
    

    我注意到在 DirectX 左手坐标系中使用的假设的偏航、俯仰、滚动到轴 (y,x,z) 的映射与您使用 OpenGL/WebGL 的映射不同,因此可能需要使用这个映射终于解决了。

    我希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      我想你实际上可以用四元数驱动物理电机。请记住,四元数代表一个旋转轴 (x, y, z) 和一个角度。 我想您有三个电机(每个轴),您可以缩放其旋转速度。现在,通过缩放这三个电机的旋转速度,您可以设置特定的旋转轴,通过仔细测量时间,您可以设置特定的旋转角度。它应该比转换为欧拉角增量更容易。

      【讨论】:

      • 问题是,我没有第三台电机,没有that design。我需要以某种方式取消第三次旋转。但如果我能可靠地取消第三次旋转,我可以很容易地从中计算出欧拉角。
      • 好的。所以,假设你有一些“目标”四元数——你想要模拟的旋转。你有两个电机,它们在某些轴上应用旋转。您可以定义两个对应于这些已知轴但角度未知的四元数。比这些四元数的乘法必须尽可能接近“目标”四元数,这可以给你未知的角度......
      • 虽然这意味着一些复杂的数学...... :)
      • 有趣的想法。我会调查的(虽然我不太擅长复杂的数学:))。谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 2017-04-09
      • 2014-02-20
      相关资源
      最近更新 更多