寻找代表从一个向量到另一个向量的旋转的四元数

我有两个向量u和v有没有find一个四元数表示从u到v的旋转?

Quaternion q; vector a = crossproduct(v1, v2); q.xyz = a; qw = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2); 

不要忘记正常化q。

理查德是没有一个独特的旋转是正确的,但上面应该给“最短的弧”,这可能是你所需要的。

中途vector解决scheme

我提出了我认为Imbrondir试图提出的解决scheme(虽然只是一个小小的错误,这可能是为什么sinisterchipmunk无法validation它)。

考虑到我们可以构造一个四元数来表示围绕一个轴的旋转,如下所示:

 qw == cos(angle / 2) qx == sin(angle / 2) * axis.x qy == sin(angle / 2) * axis.y qz == sin(angle / 2) * axis.z 

而且两个归一化向量的点和叉积是:

 dot == cos(theta) cross.x == sin(theta) * perpendicular.x cross.y == sin(theta) * perpendicular.y cross.z == sin(theta) * perpendicular.z 

看作是从uv的旋转可以通过围绕垂直向量旋转theta(vector之间的angular度)来实现,看起来好像我们可以从点和交叉乘积的结果直接构造代表这种旋转的四元数; 然而,就目前而言, theta = angle / 2 ,这意味着这样做会导致所需旋转的两倍。

一种解决scheme是计算uv之间的向量的中间值,并使用u中间向量的点和叉积来构造一个四元数,表示u中间向量之间夹angular的两倍的旋转,这需要我们所有的方式来!

有一个特殊情况,其中u == -v和一个独特的中途vector变得不可能计算。 这是可以预料的,考虑到无限多的“最短弧”旋转可以使我们从uv ,并且我们必须简单地围绕与u (或v )正交的任何向量旋转180度作为我们的特例解。 这是通过将u与其他平行于u的vector的归一化叉积完成的。

伪代码如下(显然,实际上,特殊情况下必须考虑到浮点不准确性 – 可能是通过检查点产品而不是某个阈值而不是绝对值)。

另外请注意,当u == v时, 没有特殊的情况(产生身份的四元数 – 请自己检查并查看)。

 // NB the arguments are _not_ axis and angle, but rather the // raw scalar-vector components. Quaternion(float w, Vector3 xyz); Quaternion get_rotation_between(Vector3 u, Vector3 v) { // It is important that the inputs are of equal length when // calculating the half-way vector. u = normalized(u); v = normalized(v); // Unfortunately, we have to check for when u == -v, as u + v // in this case will be (0, 0, 0), which cannot be normalized. if (u == -v) { // 180 degree rotation around any orthogonal vector return Quaternion(0, normalized(orthogonal(u))); } Vector3 half = normalized(u + v); return Quaternion(dot(u, half), cross(u, half)); } 

orthogonal函数返回与给定vector正交的任何vector。 这个实现使用具有最正交基向量的叉积。

 Vector3 orthogonal(Vector3 v) { float x = abs(vx); float y = abs(vy); float z = abs(vz); Vector3 other = x < y ? (x < z ? X_AXIS : Z_AXIS) : (y < z ? Y_AXIS : Z_AXIS); return cross(v, other); } 

半途四元数解决scheme

这实际上是在接受的答案中提出的解决scheme,它似乎比中途向量解决scheme稍微快一点(我的测量结果快了20%,但不要拿我的话来说)。 如果像我这样的其他人对这个解释感兴趣的话,我在这里添加它。

实际上,不是使用中途向量计算四元数,而是计算四元数,导致所需旋转的两倍(如其他解决scheme中所述),并find四元数在零度和零度之间的中途。

正如我之前解释的那样,四元数是所需旋转的两倍:

 qw == dot(u, v) q.xyz == cross(u, v) 

而四元数的零旋转是:

 qw == 1 q.xyz == (0, 0, 0) 

计算中间四元数就是简单地将四元数加起来,使结果正常化,就像使用vector一样。 但是,vector也是如此,四元数必须具有相同的幅度,否则结果将向更大幅度的四元数倾斜。

由两个向量的点和叉积构成的四元数与这些乘积具有相同的幅度: length(u) * length(v) 。 我们可以放大身份四元数,而不是按照这个因子来划分所有四个组件。 如果你想知道为什么接受的答案似乎使用sqrt(length(u) ^ 2 * length(v) ^ 2)使事情复杂化,那是因为向量的平方长度比长度计算更快,所以我们可以保存一个sqrt计算。 结果是:

 qw = dot(u, v) + sqrt(length_2(u) * length_2(v)) q.xyz = cross(u, v) 

然后归一化结果。 伪代码如下:

 Quaternion get_rotation_between(Vector3 u, Vector3 v) { float k_cos_theta = dot(u, v); float k = sqrt(length_2(u) * length_2(v)); if (k_cos_theta / k == -1) { // 180 degree rotation around any orthogonal vector return Quaternion(0, normalized(orthogonal(u))); } return normalized(Quaternion(k_cos_theta + k, cross(u, v))); } 

所述的问题没有明确定义:给定的一对向量没有唯一的旋转。 考虑这种情况,例如,其中u = <1,0,0>和v = <0,1,0> 。 从u到v的一个旋转将围绕z轴进行π/ 2旋转。 从u到v的另一个旋转将是围绕vector<1,1,0>pi旋转。

为什么不使用纯四元数表示vector? 如果你先把它们归一化,那也许会更好。
q 1 =(0 u x u y u z )'
q 2 =(0 v x v y v z )'
q 1 q rot = q 2
与q 1 -1预乘
q rot = q 1 -1 q 2
其中q 1 -1 = q 1 conj / q norm
这可以被认为是“左派”。 正确的划分,这不是你想要的是:
q rot,right = q 2 -1 q 1

我在四元数上不怎么样。 然而,我为此奋斗了好几个小时,不能使Polaris878解决scheme工作。 我已经尝试了预标准化v1和v2。 正常化q。 规范化q.xyz。 但我仍然不明白。 结果仍然没有给我正确的结果。

最后,尽pipe我find了一个解决scheme。 如果它帮助其他人,这是我的工作(python)代码:

 def diffVectors(v1, v2): """ Get rotation Quaternion between 2 vectors """ v1.normalize(), v2.normalize() v = v1+v2 v.normalize() angle = v.dot(v2) axis = v.cross(v2) return Quaternion( angle, *axis ) 

一个特殊的情况下,必须做出v1和v2并列像v1 == v2或v1 == -v2(有一定的宽容),我相信解决scheme应该是四元数(1,0,0,0)(不旋转)或四元数(0,* v1)(180度旋转)

从algorithm的angular度来看,最快的解决scheme是伪代码

  Quaternion shortest_arc(const vector3& v1, const vector3& v2 ) { // input vectors NOT unit Quaternion q( cross(v1, v2), dot(v1, v2) ); // reducing to half angle qw += q.magnitude(); // 4 multiplication instead of 6 and more numerical stable // handling close to 180 degree case //... code skipped return q.normalized(); // normalize if you need UNIT quaternion } 

确保你需要单位四元数(通常,它是插值所必需的)。

注意:非单元四元数可以用于比单元更快的一些操作。