在3D中合并多个旋转矩阵

8
我已经为每个轴计算了轴角旋转。如果仅应用三个旋转中的一个,对象将按预期旋转。
如果我像通常那样将旋转矩阵相乘,即组合旋转时,我得不到所需的结果,因为第一个旋转影响其他旋转,因此最终结果不是我想要的。
我想对对象应用每个旋转,就好像以前没有旋转过一样。
我想这是一个简单的任务,但似乎我没有搜索到正确的关键字。(标题也不完美...(欢迎建议))
感谢您提供的每个提示/帮助。
4个回答

4
您正在经历陀螺仪锁定。这不是一个数学问题,而是一种逻辑谬误。为了描述方便,请尝试使用您的手机:
  1. 将其面朝上放在桌子上,充电连接器在底部。
  2. 顺时针绕z轴旋转90度。因此,充电连接器现在在左侧。
  3. 绕x轴旋转180度(假设充电口在顶部或底部,则x轴为手机的长轴)。
  4. 手机现在颠倒过来,充电口在左侧。
但是:
  1. 将其面朝上放在桌子上,充电连接器在底部。
  2. 绕x轴旋转180度。因此,充电连接器现在在顶部。
  3. 顺时针绕z轴旋转90度。
  4. 手机现在颠倒过来,充电口在右侧
如果指令是绕x轴旋转180度并顺时针绕z轴旋转90度,则哪一个是错误的?两者都不是错误的。指令是有歧义的。
个别旋转的顺序总是很重要的,因为第二个会影响第一个所做的工作,第三个会影响第一和第二个所做的工作,以此类推。改变顺序只会改变谁影响谁。
定向通常直接存储为矩阵或四元数,因为它是明确的,并且可以正确有序地保留动作的顺序。

2
我知道顺序很重要,而且我的当前问题不是万向节锁定。我知道我不能直接使用我拥有的三个旋转。问题是如何修改它们,以便我可以使用它们。 - user2479595
根据我的回答:如果你只有“绕x旋转x度,绕y旋转y度,绕z旋转z度”这些信息,那么就没有一个“正确”的最终方向。无论你是否意识到,这正是所描述的万向节锁问题。 - Tommy
但必须有一个描述我所寻找的内容。并且必须有一种方法来转换我的当前值。 - user2479595
@Tommy 当然,我们知道顺序很重要。问题是这样的:我们有两个旋转矩阵,它们确定了沿着两个不同轴的所需旋转。但是旋转必须按顺序应用。第一个旋转将初始状态更改为其他状态,因此第二个旋转不再有效。如何考虑第一个旋转的变化?或者您是说这是无法解决的?让我们听听实际的答案。 - Ladislav Ondris
@LadislavOndris 如果没有任何有用的信息,那么问题可能出在我这里;例如,可能会有帮助的是,没有办法应用两个独立的矩阵,使得一个不影响另一个。但最好单独提出问题,并希望能吸引更多温和的回答者。 - Tommy
显示剩余4条评论

2
我在搜索不同的问题时偶然发现了这个问题。长话短说,你很可能是以相反的顺序乘以矩阵。
链接旋转有点令人费解。如果你有一个旋转链 R1*R2*R3*R4,那么你有两种概念化的方式:内禀和外在。
内禀视图从右到左工作。你从 R4 开始,一直到 R1。R4 采用初始框架并产生一个围绕 R4 的旋转轴旋转的新框架。R4 的轴在初始框架中指定。R3 取此旋转后的框架并将其围绕 R3 的旋转轴旋转。在这里,R3 的轴在 R4 的旋转框架中指定。这继续下去,每次更改表达旋转轴的框架。
外在视图从左到右工作。你从 R1 开始,一直到 R4。R1 的输出框架围绕 R1 的轴旋转。R1 的轴在初始框架中表示。然后 R2 取 R1 的输出框架和 R2 的输出框架,并将它们都围绕 R2 的轴旋转。R2 的轴再次在初始框架中表示。这继续上升链,每次围绕初始框架中表达的某个轴旋转所有先前的框架。
已经感到困惑了吗?更好的是,这两种方式完全等效。它们只是看待同一件事情的两种方式。就像我说的,链接旋转有点令人费解。
它的底线是,如果你有一堆在初始参考框架中表示的旋转矩阵,则外部视图最有意义。如果你想首先围绕 R1 然后围绕 R2 等旋转,则最左边的矩阵是你想要最后旋转的矩阵,即你将计算 Rn*Rn-1*...*R3*R2*R1

我不明白这如何解决原问题。如果我有在初始参考框架中表示的旋转,并将旋转一个接一个地堆叠,那么这并不起作用,因为第二个旋转不会应用于初始状态(仅适用于第一个旋转)。 - Ladislav Ondris
对于每个“外禀”欧拉旋转,您可以通过简单地颠倒顺序找到一个等效的“内在”欧拉旋转。对于序列 R1、R2、R3,乘积 R1*R2*R3 是“内在”的,R2 将围绕 R1 的框架旋转。 R3*R2*R1 是“外在”的(对于该顺序),并且围绕初始框架旋转 R2;同时,对于顺序 R3、R2、R1R3*R2*R1 也是“内在”的,它将 R2 围绕 R3 的框架旋转,这只是表达相同数量的不同等效方式。 - FirefoxMetzger

0

不要单独应用旋转,您可以从三个初始基向量创建一个变换到三个目标基向量。

例如:

    // Transform the base vectors.
    transform := BaseVecTrans(
        [3]mgl32.Vec3{
            {1, 0, 0},
            {0, 1, 0},
            {0, 0, 1}},
        [3]mgl32.Vec3{
            {longitudinal.X, longitudinal.Y, longitudinal.Z},
            {upward.X, upward.Y, upward.Z},
            {normal.X, normal.Y, normal.Z},
        },
    )

transform变量返回一个4x4的变换矩阵。

Go语言中的实现代码如下:

func BaseVecTrans(initVecs, destVecs [3]mgl32.Vec3) mgl32.Mat4 {
    // Create a 3x3 matrix from the initial vectors and its inverse.
    A := mgl32.Mat3FromCols(initVecs[0], initVecs[1], initVecs[2])
    AInv := A.Inv()

    // Create a 3x3 matrix from the destination vectors.
    B := mgl32.Mat3FromCols(destVecs[0], destVecs[1], destVecs[2])

    // Compute the rotation matrix that transforms A to B.
    R := B.Mul3(AInv)

    // Create a 4x4 transformation matrix from the rotation matrix and a translation vector.
    transform := mgl32.Ident4()
    transform.SetRow(0, mgl32.Vec4{R.At(0, 0), R.At(0, 1), R.At(0, 2), 0})
    transform.SetRow(1, mgl32.Vec4{R.At(1, 0), R.At(1, 1), R.At(1, 2), 0})
    transform.SetRow(2, mgl32.Vec4{R.At(2, 0), R.At(2, 1), R.At(2, 2), 0})
    transform.SetRow(3, mgl32.Vec4{0, 0, 0, 1})

    return transform
}

-2

旋转矩阵的组成并不是一件简单的事情。我建议将您的旋转矩阵表示为四元数。四元数具有非常有用的属性。将两个四元数相乘将给出第三个四元数,将其放回矩阵形式中,即为两个输入矩阵的确切组合。我认为Boost库有相关代码,但我个人没有使用过。

编辑:您可以从(归一化的)轴向量和旋转角度获得旋转矩阵。请参阅维基百科上的文章


我正在使用的库提供四元数。所以那部分已经完成了;)。所以我必须在三个向量上使用叉积来获得我要查找的四元数表示?我认为这与乘法旋转矩阵是相同的,不是吗? - user2479595
@user2479595,您应该能够为每个轴初始化旋转。通常,一个轴向量和一个旋转角度就足以得到一个旋转矩阵。一旦您有了三个矩阵,您可以将它们表示为四元数,将它们相乘,然后将结果表达为另一个旋转矩阵。您也可以看看Tommy的答案。 - AlexG
四元数的乘法相当于一个接一个地应用旋转矩阵,这不是所要求的。 - Ladislav Ondris
@LadislavOndris 重新阅读5年前我最初回答后面的第二和第三段,我仍然相信这正是所问之处。如果您可以读心,请随便分享您的想法。 - AlexG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接