查找表示从一个向量到另一个向量的旋转的四元数
我有两个向量 u 和 v。有没有办法找到表示从 u 到 v 的旋转的四元数?
I have two vectors u and v. Is there a way of finding a quaternion representing the rotation from u to v?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
不要忘记标准化 q。
理查德关于不存在唯一旋转的说法是正确的,但上面应该给出“最短弧线”,这可能正是您所需要的。
Don't forget to normalize q.
Richard is right about there not being a unique rotation, but the above should give the "shortest arc," which is probably what you need.
半程矢量解决方案
我想出了一个我相信 Imbrondir 试图呈现的解决方案(尽管有一个小错误,这可能就是为什么 sinisterchipmunk 无法验证它)。
假设我们可以构造一个表示绕轴旋转的四元数,如下所示:
两个归一化向量的点积和叉积为:
视为从 u 到 v 的旋转> 可以通过围绕垂直向量旋转 theta(向量之间的角度)来实现,看起来我们可以直接从点积和叉积的结果构造一个表示这种旋转的四元数; 然而,就目前情况而言,theta = angle / 2,这意味着这样做会导致所需旋转的两倍。
一种解决方案是计算 u 和 v 之间的向量,并使用 u 和 的点积和叉积half-way 向量来构造一个四元数,表示 u 和 half-way 向量之间角度的旋转两倍,其中带我们一路到达v!
有一种特殊情况,其中 u == -v 和唯一的中途向量变得无法计算。 这是预期的,因为有无限多个“最短弧”旋转可以将我们从 u 带到 v,并且我们必须简单地围绕与 u 正交的任何向量旋转 180 度strong>u(或v)作为我们的特殊情况解决方案。 这是通过获取 u 与任何其他不平行于 u 的向量的归一化叉积来完成的。
接下来是伪代码(显然,实际上,特殊情况必须考虑浮点不准确性——可能通过根据某个阈值而不是绝对值检查点积)。
另请注意,当 u == v 时没有特殊情况(生成单位四元数 - 亲自检查并查看)。
orthogonal
函数返回与给定向量正交的任何向量。 该实现使用具有最正交基向量的叉积。半路四元数解决方案
这实际上是已接受的答案中提出的解决方案,它似乎比中路向量解决方案稍微快一些(根据我的测量,快约 20%,尽管不要相信我的话)。 我将其添加到此处,以防像我这样的其他人对解释感兴趣。
本质上,您可以计算导致所需旋转两倍的四元数(如其他解决方案中详细说明),并找到该角度和零度之间的四元数,而不是使用半程向量计算四元数。
正如我之前所解释的,双倍所需旋转的四元数是:
零旋转的四元数是:
计算中途四元数只是将四元数相加并将结果标准化的问题,就像矢量一样。 然而,与向量的情况一样,四元数必须具有相同的大小,否则结果将偏向具有较大大小的四元数。
由两个向量的点积和叉积构建的四元数将与这些乘积具有相同的量级:
length(u) * length(v)
。 我们可以放大单位四元数,而不是将所有四个分量除以这个因子。 如果您想知道为什么使用sqrt(length(u) ^ 2 * length(v) ^ 2)
接受的答案似乎使问题变得复杂,这是因为向量的平方长度计算起来更快比长度,所以我们可以节省一次sqrt
计算。 结果是:然后对结果进行归一化。 伪代码如下:
Half-Way Vector Solution
I came up with the solution that I believe Imbrondir was trying to present (albeit with a minor mistake, which was probably why sinisterchipmunk had trouble verifying it).
Given that we can construct a quaternion representing a rotation around an axis like so:
And that the dot and cross product of two normalized vectors are:
Seeing as a rotation from u to v can be achieved by rotating by theta (the angle between the vectors) around the perpendicular vector, it looks as though we can directly construct a quaternion representing such a rotation from the results of the dot and cross products; however, as it stands, theta = angle / 2, which means that doing so would result in twice the desired rotation.
One solution is to compute a vector half-way between u and v, and use the dot and cross product of u and the half-way vector to construct a quaternion representing a rotation of twice the angle between u and the half-way vector, which takes us all the way to v!
There is a special case, where u == -v and a unique half-way vector becomes impossible to calculate. This is expected, given the infinitely many "shortest arc" rotations which can take us from u to v, and we must simply rotate by 180 degrees around any vector orthogonal to u (or v) as our special-case solution. This is done by taking the normalized cross product of u with any other vector not parallel to u.
Pseudo code follows (obviously, in reality the special case would have to account for floating point inaccuracies -- probably by checking the dot products against some threshold rather than an absolute value).
Also note that there is no special case when u == v (the identity quaternion is produced -- check and see for yourself).
The
orthogonal
function returns any vector orthogonal to the given vector. This implementation uses the cross product with the most orthogonal basis vector.Half-Way Quaternion Solution
This is actually the solution presented in the accepted answer, and it seems to be marginally faster than the half-way vector solution (~20% faster by my measurements, though don't take my word for it). I'm adding it here in case others like myself are interested in an explanation.
Essentially, instead of calculating a quaternion using a half-way vector, you can calculate the quaternion which results in twice the required rotation (as detailed in the other solution), and find the quaternion half-way between that and zero degrees.
As I explained before, the quaternion for double the required rotation is:
And the quaternion for zero rotation is:
Calculating the half-way quaternion is simply a matter of summing the quaternions and normalizing the result, just like with vectors. However, as is also the case with vectors, the quaternions must have the same magnitude, otherwise the result will be skewed towards the quaternion with the larger magnitude.
A quaternion constructed from the dot and cross product of two vectors will have the same magnitude as those products:
length(u) * length(v)
. Rather than dividing all four components by this factor, we can instead scale up the identity quaternion. And if you were wondering why the accepted answer seemingly complicates matters by usingsqrt(length(u) ^ 2 * length(v) ^ 2)
, it's because the squared length of a vector is quicker to calculate than the length, so we can save onesqrt
calculation. The result is:And then normalize the result. Pseudo code follows:
所述问题没有明确定义:给定的向量对不存在唯一的旋转。 例如,考虑 u = <1, 0, 0> 和 v = <0, 1, 0> 的情况。 从 u 到 v 的一次旋转将是绕 z 轴的 pi / 2 旋转。 从 u 到 v 的另一个旋转是围绕向量 <1, 1, 0> 的 pi 旋转。
The problem as stated is not well-defined: there is not a unique rotation for a given pair of vectors. Consider the case, for example, where u = <1, 0, 0> and v = <0, 1, 0>. One rotation from u to v would be a pi / 2 rotation around the z-axis. Another rotation from u to v would be a pi rotation around the vector <1, 1, 0>.
我对四元数不太擅长。 然而,我为此苦苦挣扎了几个小时,无法使 Polaris878 解决方案发挥作用。 我尝试过预规范化 v1 和 v2。 标准化 q。 标准化 q.xyz。 但我还是不明白。 结果还是没有给我正确的结果。
但最终我找到了一个可行的解决方案。 如果它对其他人有帮助,这是我的工作(python)代码:
如果 v1 和 v2 是并行的,则必须做出特殊情况,例如 v1 == v2 或 v1 == -v2 (具有一定的容忍度),我认为解决方案应该是四元数(1, 0,0,0)(无旋转)或四元数(0, *v1)(180 度旋转)
I'm not much good on Quaternion. However I struggled for hours on this, and could not make Polaris878 solution work. I've tried pre-normalizing v1 and v2. Normalizing q. Normalizing q.xyz. Yet still I don't get it. The result still didn't give me the right result.
In the end though I found a solution that did. If it helps anyone else, here's my working (python) code:
A special case must be made if v1 and v2 are paralell like v1 == v2 or v1 == -v2 (with some tolerance), where I believe the solutions should be Quaternion(1, 0,0,0) (no rotation) or Quaternion(0, *v1) (180 degree rotation)
要找到将
u
旋转到v
的最小旋转四元数,请使用通用解决方案
为什么要进行此通用化?
最重要的是,
R
是最接近Q
的四元数,其局部u
方向全局指向v方向。 因此,
R
是最接近Q
的四元数,它将把u
旋转到v
。这可用于为您提供所有可能的旋转,从
u
旋转到v
,具体取决于Q
的选择。 如果您想要从u
到v
的最小旋转(如其他解决方案所示),请使用Q = quat(1, 0, 0, 0)
。最常见的是,我发现您想要执行的实际操作是一个轴与另一个轴的一般对齐。
示例
假设您想要四元数
Y
,它仅代表Q
的偏航,即绕 y 轴的纯旋转。 我们可以使用以下代码计算Y
。对齐为 4x4 投影矩阵
如果要重复执行相同的对齐操作,因为此操作与将四元数投影到嵌入 4D 空间的 2D 平面上相同,我们可以将此操作表示为与 4x4 投影矩阵的乘法,
A*Q
。To find the quaternion of smallest rotation which rotates
u
tov
, useThe Generalized Solution
Why This Generalization?
Most importantly,
R
is the quaternion closest toQ
whose localu
direction points globally in thev
direction. Consequently,R
is the quaternion closest toQ
which will rotateu
tov
.This can be used to give you all possible rotations which rotate from
u
tov
, depending on the choice ofQ
. If you want the minimal rotation fromu
tov
, as the other solutions give, useQ = quat(1, 0, 0, 0)
.Most commonly, I find that the real operation you want to do is a general alignment of one axis with another.
Example
Say you want the quaternion
Y
which only representsQ
's yaw, the pure rotation about the y axis. We can computeY
with the following.Alignment as a 4x4 Projection Matrix
If you want to perform the same alignment operation repeatedly, because this operation is the same as the projection of a quaternion onto a 2D plane embedded in 4D space, we can represent this operation as the multiplication with 4x4 projection matrix,
A*Q
.从算法的角度来看,最快的解决方案是在伪代码中查找
确保您需要单位四元数(通常,插值需要它)。
笔记:
非单位四元数可以用于比单位更快的某些操作。
From algorithm point of view , the fastest solution looks in pseudocode
Be sure that you need unit quaternions (usualy, it is required for interpolation).
NOTE:
Nonunit quaternions can be used with some operations faster than unit.
为什么不使用纯四元数表示向量? 如果你先将它们标准化也许会更好。
q1 = (0 ux uy uz)'
q2 = (0 vx vy vz)'
q1 qrot = q2
预乘 q1-1
qrot = q1-1 q2
其中 q1-1 = q1conj / qnorm
这可以被认为是“左分裂”。
右除法,这不是你想要的,是:
qrot,right = q2-1 q1
Why not represent the vector using pure quaternions? It's better if you normalize them first perhaps.
q1 = (0 ux uy uz)'
q2 = (0 vx vy vz)'
q1 qrot = q2
Pre-multiply with q1-1
qrot = q1-1 q2
where q1-1 = q1conj / qnorm
This is can be thought of as "left division".
Right division, which is not what you want is:
qrot,right = q2-1 q1
有些答案似乎没有考虑叉积可能为 0 的可能性。下面的代码片段使用角轴表示:
toQuaternion
可以按如下方式实现:如果您使用的是 Eigen 库,你也可以这样做:
Some of the answers don't seem to consider possibility that cross product could be 0. Below snippet uses angle-axis representation:
The
toQuaternion
can be implemented as follows:If you are using Eigen library, you can also just do:
为
如果已知向量 u 到向量 v 单位向量,该函数简化为
根据您的用例,可能需要处理点积为 1(平行向量)和 -1(指向相反方向的向量)时的情况。
One can rotate a vector u to vector v with
If it is known that the vectors u to vector v are unit vectors, the function reduces to
Depending on your use-case, handling the cases when the dot product is 1 (parallel vectors) and -1 (vectors pointing in opposite directions) may be needed.
仅使用归一化四元数,我们可以用以下术语表达约瑟夫·汤普森的答案。
设 q_v = (0, u_x, v_y, v_z) 和 q_w = (0, v_x, v_y, v_z) 并考虑
q = q_v * q_w = (-u dot v, uxv)。
因此将 q 表示为 q(q_0, q_1, q_2, q_3) 我们有
q_r = (1 - q_0, q_1, q_2, q_3).normalize()
Working just with normalized quaternions, we can express Joseph Thompson's answer in the follwing terms.
Let q_v = (0, u_x, v_y, v_z) and q_w = (0, v_x, v_y, v_z) and consider
q = q_v * q_w = (-u dot v, u x v).
So representing q as q(q_0, q_1, q_2, q_3) we have
q_r = (1 - q_0, q_1, q_2, q_3).normalize()
@RicharDunlap 是对的:解决方案有无数种。 而说“最小旋转”则不清楚。
但是有一个“规范”四元数,它将归一化向量
u
映射到归一化向量v
。 准确地说,它将通过法向量u
原点的平面映射到通过法向量v
原点的平面。 这样的描述,可谓是独一无二。这个四元数是:
@RicharDunlap is right: there's an infinity of solutions. And saying the "smallest rotation" is not clear.
But there is a "canonical" quaternion which maps a normalized vector
u
to a normalized vectorv
. Precisely, it maps the plane passing through the origin with normal vectoru
to the plane passing through the origin with normal vectorv
. With such a description, it is unique.This quaternion is: