两个四元数之间的区别

27

已解决


我正在制作一个引擎中的3D传送门系统(类似于Portal游戏)。每个传送门都有其保存在四元数中的自身方向。为了在其中一个传送门中呈现虚拟场景,我需要计算两个四元数之间的差异,并将结果用于旋转虚拟场景。

当在左墙上创建第一个传送门和右墙上创建第二个传送门时,从一个传送门到另一个传送门的旋转将只在一个轴上进行,但例如当第一个传送门创建在地板上,而第二个传送门创建在右墙上时,从一个传送门到另一个传送门的旋转可能涉及两个轴,这就是问题所在,因为旋转出错了。

我认为问题存在是因为例如 X 轴和 Z 轴的方向被一起存储在一个四元数中,而我需要它们分别来手动相乘 X * Z(或 Z * X),但如何使用一个四元数(即差异四元数)进行操作?还是有其他方法可以正确旋转场景吗?

编辑:

这张图片展示了两个传送门P1和P2,箭头显示它们的旋转方向。当我看进入P1时,我将看到P2所看到的内容。为了找到所需的旋转,使主场景与此图中的虚拟场景相同,我正在执行以下操作:

  1. 获取四元数P2到四元数P1的差异
  2. 在Y轴(传送门的上方)上将结果旋转180度
  3. 使用结果旋转虚拟场景

以上方法仅在差异发生在一个轴上时有效。当一个传送门位于地板或天花板上时,这种方法将不起作用,因为差异四元数是在多个轴上构建的。如建议的那样,我尝试将P1的四元数乘以P2的四元数,并颠倒顺序,但这并没有奏效。

图片描述

编辑2:

为了找到从P2到P1的差异,我正在执行以下操作:

Quat q1 = P1->getOrientation();
Quat q2 = P2->getOrientation();

Quat diff = Quat::diff(q2, q1);  // q2 * diff = q1 //

这里是Quat::diff函数:

GE::Quat GE::Quat::diff(const Quat &a, const Quat &b)
{
    Quat inv = a;
    inv.inverse();
    return inv * b;
}

逆元:

void GE::Quat::inverse()
{
    Quat q = (*this);
    q.conjugate();
    (*this) = q / Quat::dot((*this), (*this));
}

变位:

void GE::Quat::conjugate()
{
    Quat q;
    q.x = -this->x;
    q.y = -this->y;
    q.z = -this->z;
    q.w = this->w;

    (*this) = q;
}

点积:

float GE::Quat::dot(const Quat &q1, const Quat &q2)
{
    return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;
}

操作符*:

const GE::Quat GE::Quat::operator* ( const Quat &q) const
{
    Quat qu;
    qu.x = this->w*q.x + this->x*q.w + this->y*q.z - this->z*q.y;
    qu.y = this->w*q.y + this->y*q.w + this->z*q.x - this->x*q.z;
    qu.z = this->w*q.z + this->z*q.w + this->x*q.y - this->y*q.x;
    qu.w = this->w*q.w - this->x*q.x - this->y*q.y - this->z*q.z;
    return qu;
}

操作符/:

const GE::Quat GE::Quat::operator/ (float s) const
{
    Quat q = (*this);
    return Quat(q.x / s, q.y / s, q.z / s, q.w / s);
}

所有这些东西都是有效的,因为我已经使用GLM库进行了测试


1
你是如何计算P2和P1的四元数之间的差异的?为了找到将q1转换为q2的四元数q',您必须找到q1的逆,即q1^'1,并左乘q2。q' = q2*q1^-1。请参考https://dev59.com/TXI-5IYBdhLWcg3woJ9J。 - AndyG
我正在按照你说的方式进行。我已经编辑了我的问题,展示了找到差异的整个过程。 - Tom
5个回答

53

如果你想找到一个四元数diff,使得diff * q1 == q2,那么你需要使用乘法逆元:

diff * q1 = q2  --->  diff = q2 * inverse(q1)

where:  inverse(q1) = conjugate(q1) / abs(q1)

and:  conjugate( quaternion(re, i, j, k) ) = quaternion(re, -i, -j, -k)

如果您的四元数是旋转四元数,则它们应该都是单位四元数。这使得查找逆变得容易:由于abs(q1) = 1,因此您可以通过仅否定ijk分量来找到inverse(q1) = conjugate(q1)


然而,对于您描述的基于场景的几何配置,您可能实际上不想执行上述操作,因为您还需要正确计算平移。

做到一切正确的最直接的方法是将您的四元数转换为4x4旋转矩阵,并以适当的顺序与4x4平移矩阵相乘,如大多数介绍性计算机图形学文本所述。

当然,通过手动合成欧几里德变换,同时将您的旋转保留在四元数形式并逐步将四元数应用于单独的平移向量,也是可能的。然而,这种方法往往技术上晦涩且容易出错:4x4矩阵形式是常规形式的好理由之一是似乎更容易正确完成。


2
四元数是SO(3)的双重映射。特别地,q和-q代表相同的旋转。这意味着计算q1*共轭(q2)有一种更简单的方法:不要取q2的虚部,而是取其实部的相反数,然后相乘即可。 - David Hammen
@comingstorm 要进行翻译,我将差分四元数转换为旋转矩阵,并将其乘以绕Y轴180度的旋转矩阵,然后我否定P2向量位置,接下来通过旋转矩阵变换此向量,然后将得到的位置添加到P1位置。这是我开始绘制虚拟场景的位置。如果我将所有四元数都改为旋转矩阵,并在计算中仅使用矩阵,是否会有任何区别? - Tom

6

我解决了我的问题。事实证明,我不需要任何两个旋转之间的差异。只需将一个旋转乘以180度旋转中的旋转,然后乘以第二个旋转的逆(使用矩阵)即可:

Matrix m1 = p1->getOrientation().toMatrix();
Matrix m2 = p2->getOrientation().toMatrix();
Matrix model = m1 * Matrix::rotation(180, Vector3(0,1,0)) * Matrix::inverse(m2);

并且翻译是这样计算的:

Vector3 position = -p2->getPosition();
position = model * position + p1->getPosition();
model = Matrix::translation(position) * model;

3
很高兴你解决了问题,汤姆。感谢你在这里发布,以便其他人将来可以看到它。 - AndyG

1

四元数的工作方式如下:本地参考框架表示为虚四元数方向i,j,k。例如,对于站在门1门口并朝箭头方向看的观察者,方向i可能代表箭头方向,j向上,k = ij指向观察者右侧。在由四元数q1表示的全局坐标中,3D坐标轴为

q1*(i,j,k)*q1^-1=q1*(i,j,k)*q1',

其中q'是共轭,对于单位四元数而言,共轭就是其逆。

现在的任务是找到一个单位四元数q,使得在全局坐标系下,局部坐标系1中表示的方向q*(i,j,k)*q'与旋转后的局部坐标系2方向重合。从图示来看,这意味着前进变成了后退,左侧变成了右侧。

q1*q*(i,j,k)*q'*q1'=q2*(-i,j,-k)*q2'
                   =q2*j*(i,j,k)*j'*q2'

这可以通过等式相等轻松实现

q1*q=q2*j or q=q1'*q2*j.

但是细节可能有所不同,主要是另一个轴可以代表方向“上”,而不是j。


如果草图的全局系统从底部开始,那么全局-i指向垂直方向前方,全局-j朝上,全局-k向右。因此,local1-(i,j,k)为global-(-i,j,-k),结果如下:
q1=j. 

local2-(i,j,k)是全局坐标系下的-(-k,j,i),可以通过以下方式实现

q2=sqrt(0.5)*(1+j), 

自从

(1+j)*i*(1-j)=i*(1-j)^2=-2*i*j=-2*k and 
(1+j)*k*(1-j)=(1+j)^2*k= 2*j*k= 2*i

与您实现中的实际值进行比较,将指示必须更改轴和四元数方向的分配。

1
不,您需要将两个四元数相乘以获得所需的最终四元数。
假设您的第一个旋转是“q1”,第二个旋转是“q2”。您想按照这个顺序应用它们。
生成的四元数将是“q2 * q1”,它将表示您的复合旋转(请记住,四元数使用左乘法,因此通过从左侧相乘来将“q2”应用于“q1”)。 参考
有关计算单个四元数的简要教程,请参阅我的以前的stackoverflow答案

编辑:

为了澄清,您在旋转矩阵和欧拉角方面也会遇到类似的问题。您定义X、Y和Z的变换,然后将它们相乘以获得结果变换矩阵(wiki)。在这里您有同样的问题。旋转矩阵和四元数在表示旋转方面大部分是等效的。四元数之所以更受欢迎,主要是因为它们更容易表示(并且更容易解决万向节锁定问题)。

谢谢您的回答。我尝试把q1和q2相乘,但是没有成功。我已编辑了我的问题以使其更清晰。 - Tom

-3

检查 https://www.emis.de/proceedings/Varna/vol1/GEOM09.pdf

想象一下从 Q1 到 Q2 获取 dQ,我将解释为什么 dQ= Q1*·Q2,而不是 Q2·Q1*

这会旋转框架,而不是物体。 对于 R3 中的任何向量 v,算子 L(v) 的旋转作用为 L(v) = Q*·v·Q

它不是 Q·v·Q*,这是物体旋转作用。

如果您旋转 Q1 然后 Q1* 然后 Q2,则可以写出 (Q1·Q1*·Q2)*·v·(Q1·Q1*·Q2) = (Q1*·Q2)*·Q1*·v·Q1·(Q1*·Q2) = dQ*·Q1*·v·Q1·dQ

因此,dQ = Q1*·Q2


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