通过四元数旋转vector3


25

我正在尝试通过给定的四元数旋转vector3。

我知道这是真的

v=qvq1

我知道是的逆,但是如何将向量的乘法映射到四元数以得到向量呢?q1qmagnitude(q)

我发现您可以将视为矩阵,并将和转换为矩阵,然后将从矩阵转换为向量,但这似乎只是为了获得向量而已。有没有我可以使用的更清洁的实现?vqqv

Answers:


36

内森·里德(Nathan Reed)和泰德隆(teodron)暴露后,将向量v旋转单位长度四元数q的公式为:

1)从v中创建一个纯四元数p。这仅意味着添加第四个坐标0:

p=(vx,vy,vz,0)p=(v,0)

2)将其与q预乘,并将其与共轭q *后乘:

p=q×p×q

3)这将导致另一个纯四元数,可以将其转换为向量:

v=(px,py,pz)

该向量被旋转了。vvq


这是可行的,但远非最佳。四元数乘法意味着无数次操作。我对诸如此类的各种实现感到很好奇,并决定查找这些实现来自何处。这是我的发现。

我们还可以将q描述为3维向量u和标量s的组合

q=(ux,uy,uz,s)q=(u,s)

根据四元数乘法的规则,并且由于单位长度四元数的共轭只是反函数,所以我们得到:

p=qpq=(u,s)(v,0)(u,s)=(sv+u×v,uv)(u,s)=((uv)(u)+s(sv+u× v)+(sv+u×v)×(u),)=((uv)u+s2v+s(u×v)+sv×(u)+(u×v)×(u),)

标量部分(椭圆)为零,如此处所述。有趣的是矢量部分,也就是我们的旋转矢量v'。可以使用一些基本的向量身份进行简化:

v=(uv)u+s2v+s(u×v)+s(u×v)+u×(u×v)=(uv)u+s2v+2s(u×v)+(uv)u(uu)v=2(uv)u+(s2uu)v+2s(u×v)

现在这更加理想 ; 两个点积,一个叉积和一些附加项:大约一半的业务量。这将在源代码中给出类似的内容(假设一些通用的矢量数学库):

void rotate_vector_by_quaternion(const Vector3& v, const Quaternion& q, Vector3& vprime)
{
    // Extract the vector part of the quaternion
    Vector3 u(q.x, q.y, q.z);

    // Extract the scalar part of the quaternion
    float s = q.w;

    // Do the math
    vprime = 2.0f * dot(u, v) * u
          + (s*s - dot(u, u)) * v
          + 2.0f * s * cross(u, v);
}

希望得到更好的书面回应。并且考虑到大多数性能怪胎倾向于使用内在函数执行矢量运算,因此您确实获得了相当大的提速(即使对于普通的四元数乘法,尤其是在英特尔架构上)。
teodron

最终结果看起来与Rodrigues的旋转公式相似-无论如何,它具有相同的基向量;我必须挖掘一些触发身份,以查看系数是否匹配。
内森·里德

@NathanReed这似乎是获得相同结果的另一种方法。我也想知道这是否匹配。感谢您指出了这一点!
Laurent Couvidou 2013年

1
我正在检查GLM对此的实现,它的实现似乎有所不同,即如下所示:vprime = v + ((cross(u, v) * s) + cross(u, cross(u, v)) * 2.0f这是否是类似的优化?它看起来有些相似,但并不相同-它仅使用叉积,不使用点积。原始源代码可以在官方GLM存储库的type_quat.inl文件中找到,该文件中包含operator*一个四元数和一个矢量(vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v)
j00hi

7

首先,q ^(-1)不是-q / magnitude(q); 它是q * /(magnitude(q))^ 2(q *是共轭;除实数之外的所有分量)。当然,如果所有四元数都已经归一化(通常在旋转系统中),则可以按量级进行除法运算。

至于与向量的乘积,您只需通过将quat的实数分量设置为零并将其ijk分量设置为向量的xyz来将向量扩展为四元数。然后,执行四元数乘法以获得v',然后再次提取ijk分量。(v'的实部应始终为零,加上或减去一些浮点误差。)


5

第一个观察结果:的逆q不是-q/magnitude(q),那是完全错误的。四元数的旋转意味着这些4D复数等价物具有统一范数,因此位于该4D空间中的S3单位球体上。quat是一元的事实意味着其范数是norm(q)^2=q*conjugate(q)=1,这意味着quat的逆是其共轭。

如果单位四元数写为q=(w,x,y,z)=(cos(t),sin(t)v),则其共轭数conjugate(q)=(w,-x,-y,-z)==(cos(t),-sin(t)v),其中t是旋转角的一半,而v是旋转轴(当然是单位矢量)。

当那个汉密尔顿花花公子决定使用更高维度的复数等价物时,他还偶然发现了一些不错的性质。例如,如果您使用一个完全纯的四元数q=(0,x,y,z)(没有标量部分w!),则可以认为该废话是矢量(实际上是人们可能将其称为S3球的赤道的quat)。 !-如果我们考虑当今19世纪人们在技术上受到的损害在我们看来似乎是弯曲的东西,那么请多加注意。因此,汉密尔顿以quat形式获取了该矢量:v=(0,x,y,z)考虑了quat 的几何特性,进行了一系列实验。

INPUT: _v=(x,y,z)_ a random 3D vector to rotate about an __u__ unit axis by an angle of _theta_

OUTPUT: q*(0,_v_)*conjugate(q)

哪里

 q = (cos(theta/2), sin(theta/2)*u)
 conjugate(q) = inverse(q) = (cos(theta/2), -sin(theta/2)*u)
 norm(q)=magnitude(q)=|q|=1

观察:q *(0,v)* conj(q)必须是(0,v')形式的另一个夸脱。我不会详细解释为什么会发生这种情况,但是如果您通过这种方法旋转一个纯虚构的四元数(在我们的例子中是一个矢量!),那么您必须得到一个类似的对象:纯虚构的四元数。 ...,您将其虚构的部分作为结果。有了它,带螺母壳的四元数旋转的奇妙世界。

注意:对于那些使用过分使用的短语的人:quats很好,因为它们避免了'em gimbal lock ..应该首先释放他们的想象力!!夸脱仅仅是“优雅的”数学工具,可以通过使用其他方法完全避免,我发现在几何上完全等效的是轴角方法。

代码我喜欢C ++库非常简单,但具有3D图形实验人员所需的所有矩阵,矢量和quat操作,而不必花费超过15分钟的时间来学习。如果您不是C ++新手,则在15分钟内完成。祝好运!


为您的笔记+1。我敢打赌,大多数人如果尝试都无法实现真正​​的万向节锁定,这已经成为进行旋转时任何意外行为的通俗易懂的说法。
史蒂夫·H

大多数人无法构建合适的万向架机构,并认为如果将3个旋转矩阵链接在一起,它们将自动以“欧拉角”表示形式结束。万向架的东西只是最简单的机械臂式旋转之一尝试执行逆向运动学时可能经历冗余的关节(其自由度大于产生所需方向的实际所需的自由度)。哦,好,那是另一个话题,但是我认为最好不要在CG程序员中引起这个“传奇”问题的炒作
。– teodron

Nitpickery:虽然轴角等效,因为两种表示都可以唯一地表示SO(3)中的所有旋转(好的,对通常的双覆盖进行模运算),当然它们之间来回转换很简单,四元数确实有比所有其他非矩阵表示形式更易于编写的优势。
史蒂文·斯塔德尼基

由于它们在任何面向对象的编程语言中均具有良好的行为,因此它们具有易于编写的优点,尤其是在使用运算符重载时。我不确定,但也许甚至它们的球面插值属性都保留了轴角(也许除了SQUAD ?!)。
teodron


-1

我尝试手工解决此问题,并提出了以下等式/方法:

// inside quaterion class
// quaternion defined as (r, i, j, k)
Vector3 rotateVector(const Vector3 & _V)const{
    Vector3 vec();   // any constructor will do
    vec.x = 2*(r*_V.z*j + i*_V.z*k - r*_V.y*k + i*_V.y*j) + _V.x*(r*r + i*i - j*j - k*k);
    vec.y = 2*(r*_V.x*k + i*_V.x*j - r*_V.z*i + j*_V.z*k) + _V.y*(r*r - i*i + j*j - k*k);
    vec.z = 2*(r*_V.y*i - r*_V.x*j + i*_V.x*k + j*_V.y*k) + _V.z*(r*r - i*i - j*j + k*k);
    return vec;
}

如果有人愿意查看我使用的http://pastebin.com/8QHQqGbv的 mt派生,我将不胜感激,我建议将其复制到支持侧面滚动的文本编辑器中

用我的记法,我用q ^(-1)表示共轭(而不是逆)和不同的标识符,但我希望它是可以遵循的。我认为多数是正确的,尤其是在证明向量的实部会消失的地方。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.