两个四元数的区别

Posted

技术标签:

【中文标题】两个四元数的区别【英文标题】:Difference between the two quaternions 【发布时间】:2014-04-05 03:08:46 【问题描述】:

已解决


我正在我的引擎中制作一个 3D 门户系统(例如 Portal 游戏)。每个门户都有自己的方向保存在一个四元数中。要在其中一个门户中渲染虚拟场景,我需要计算两个四元数之间的差异,并将结果用于旋转虚拟场景。

当在左墙上创建第一个传送门,在右墙上创建第二个传送门时,从一个到另一个的旋转将仅在一个轴上发生,但例如当第一个传送门将在地板上创建时,以及右边墙上的第二个,从一个到另一个的旋转可能是在两个轴上,这就是问题,因为旋转出错了。

我认为问题的存在是因为例如 X 轴和 Z 轴的方向一起存储在一个四元数中,我需要分别手动乘以 X * Z (或 Z * @ 987654329@),但是如何只用一个四元数(差异四元数)呢?还是有其他方法来纠正旋转场景?

编辑:

这张图片上有两个传送门 P1 和 P2,箭头显示它们是如何旋转的。当我查看 P1 时,我会看到 P2 中的内容。为了找到我需要旋转主场景的旋转,就像这张图片中的虚拟场景一样,我正在执行以下操作:

    从四元数 P2 到四元数 P1 的差异 Y 轴旋转 180 度的结果(传送门的 UP) 使用结果旋转虚拟场景

仅当差异仅发生在一个轴上时,上述方法才有效。当一个门户在地板上或天花板上时,这将不起作用,因为差异四元数构建在多个轴上。正如建议的那样,我尝试将 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 library 测试过它

【问题讨论】:

你如何计算 P2 和 P1 的四元数之间的差异?要找到将 q1 转换为 q2 的四元数 q',您必须找到 q1 的倒数,q1^'1 并左乘以 q2。 q' = q2*q1^-1 参考参考参考***.com/questions/1755631/… 我按照你说的做。我已经编辑了我的问题,以展示寻找差异的整个过程。 【参考方案1】:

如果你想找到一个四元数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,您的inverse(q1) = conjugate(q1) 可以通过取反ijk 组件来找到。


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

正确执行所有操作的最直接方法是将四元数转换为 4x4 旋转矩阵,并以适当的顺序将它们与 4x4 平移矩阵相乘,如大多数介绍性计算机图形学文本中所述。

当然可以手动组合欧几里得变换,保持四元数形式的旋转,同时将四元数增量应用于单独的平移向量。然而,这种方法在技术上往往晦涩难懂,而且容易出现编码错误:4x4 矩阵形式之所以是传统形式是有充分理由的,其中一个重要原因是这样似乎更容易正确处理。

【讨论】:

+1。四元数是 SO(3) 的双重映射。特别是,q 和 -q 表示相同的旋转。这意味着计算 q1*conjugate(q2) 有一种更简单的方法:对 q2 的实部求反,而不是虚部求反,然后相乘。 @comingstorm 为了翻译,我将差分四元数转换为旋转矩阵,并将其乘以旋转矩阵在 Y 轴上 180 度,然后我否定 P2 向量位置,接下来转换这个向量通过旋转矩阵,然后将得到的位置添加到 P1 位置。这就是我开始绘制虚拟场景的位置。如果我把所有四元数都改成旋转矩阵,只用矩阵计算,会有什么不同吗?【参考方案2】:

我解决了我的问题。事实证明,我不需要两次旋转之间的任何区别。只需将一个旋转乘以 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】:

不,你必须将两个四元数相乘才能得到你想要的最后一个四元数。

假设您的第一个轮换是q1,第二个轮换是q2。您想按该顺序应用它们。

得到的四元数将是q2 * q1,它将代表您的复合旋转(回想一下,四元数使用左乘法,所以q2 通过从左乘法应用于q1

Reference

有关计算单个四元数的简要教程,请参阅我的previous stack overflow answer

编辑:

澄清一下,旋转矩阵和欧拉角会遇到类似的问题。您定义关于 X、Y 和 Z 的变换,然后将它们相乘以获得结果变换矩阵 (wiki)。你在这里有同样的问题。旋转矩阵和四元数在大多数表示旋转的方式上是等价的。四元数是首选,主要是因为它们更容易表示(并且更容易解决万向节锁定问题)

【讨论】:

感谢您的回答。我试图将 q1 和 q2 相乘,但这不起作用。我已将我的问题编辑得更清楚【参考方案4】:

四元数的工作方式如下:局部参考系表示为虚构的四元数方向 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。


如果草图的全局系统是从底部开始的,那么global-i在垂直方向上指向前方,global-j向上,global-k向右,那么local1-(i,j,k)是全局-(-i,j,-k),给出

q1=j. 

local2-(i,j,k) 是global-(-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

将此值与您的实现中的实际值进行比较将表明必须如何更改轴和四元数方向的分配。

【讨论】:

【参考方案5】:

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

假设从 Q1 到 Q2 得到 dQ,我将解释为什么 dQ = Q1*·Q2,而不是 Q2·Q1*

这会旋转框架,而不是对象。对于 R3 中的任意向量 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

【讨论】:

以上是关于两个四元数的区别的主要内容,如果未能解决你的问题,请参考以下文章

3D图形:矩阵、欧拉角、四元数与方位的故事

基于四元数的 3D 相机应该累积四元数还是欧拉角?

Unity3D 四元数的常用方法

如何在Unity中获得2个四元数的相对旋转

几何系列四元数的基础

unityvector3和四元数的区别