我有两个向量u和v。有没有办法找到表示从u到v旋转的四元数?
我有两个向量u和v。有没有办法找到表示从u到v旋转的四元数?
Answers:
Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);
不要忘记标准化q。
理查德(Richard)认为没有唯一的轮换是正确的,但是上面的代码应该给出“最短弧线”,这可能就是您所需要的。
sqrt((v1.Length ^ 2) * (v2.Length ^ 2))
简化为v1.Length * v2.Length
。我无法获得任何变化以产生明智的结果。
我提出了我相信Imbrondir试图提出的解决方案(尽管有一个小错误,这可能就是为什么sinisterchipmunk难以验证它的原因)。
假定我们可以构造一个表示围绕轴旋转的四元数,如下所示:
q.w == cos(angle / 2)
q.x == sin(angle / 2) * axis.x
q.y == sin(angle / 2) * axis.y
q.z == 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
看到从u到v的旋转可以通过围绕垂直矢量旋转theta(矢量之间的角度)来实现,看来我们可以根据点和叉积的结果直接构造一个表示该旋转的四元数; 但是,就目前而言,theta = angle / 2,这意味着这样做将导致所需旋转次数增加一倍。
一种解决方案是计算u和v之间的向量,并使用u和中途向量的点积和叉积构造一个四元数,该四元数表示u和中途向量之间的角度旋转两倍,这将我们带到v!
有一种特殊情况,其中u == -v,并且无法计算唯一的中途矢量。鉴于可以将我们从u转到v的无数次“最短弧”旋转,这是预料之中的,作为特殊情况的解决方案,我们必须围绕与u(或v)正交的任何矢量简单地旋转180度。这是通过将u的归一化叉积与任何其他不平行于u的向量进行的。
后面是伪代码(很明显,实际上,这种特殊情况必须考虑到浮点错误-可能是通过对照某个阈值而非绝对值来检查点积)。
还要注意,当u == v时,没有特殊情况(生成四元数-请自己检查一下)。
// N.B. 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
函数返回与给定向量正交的任何向量。此实现使用具有最正交基矢量的叉积。
Vector3 orthogonal(Vector3 v)
{
float x = abs(v.x);
float y = abs(v.y);
float z = abs(v.z);
Vector3 other = x < y ? (x < z ? X_AXIS : Z_AXIS) : (y < z ? Y_AXIS : Z_AXIS);
return cross(v, other);
}
这实际上是公认的答案中提出的解决方案,它似乎比中途矢量解决方案略快(通过我的测量,速度提高了约20%,尽管我不敢相信。)我在这里添加它,以防其他人像我这样对解释感兴趣。
本质上,您可以计算导致所需旋转次数增加一倍的四元数(如另一种解决方案中所述),而不是使用中途矢量来计算四元数,并找到介于零度和零度之间的四元数。
正如我之前解释的,将所需旋转量加倍的四元数为:
q.w == dot(u, v)
q.xyz == cross(u, v)
零旋转的四元数为:
q.w == 1
q.xyz == (0, 0, 0)
就像向量一样,计算四元数四元仅是对四元数求和并将结果归一化的问题。但是,与向量一样,四元数必须具有相同的大小,否则结果将偏向具有较大值的四元数。
由两个向量的点和叉积构成的四元数将具有与这些乘积相同的大小:length(u) * length(v)
。与其将所有四个分量都除以该因子,我们还可以扩大单位四元数。并且,如果您想知道为什么使用来接受答案似乎很复杂sqrt(length(u) ^ 2 * length(v) ^ 2)
,那是因为向量的平方长度比长度的平方要快,所以我们可以节省一个sqrt
计算。结果是:
q.w = 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轴的pi / 2圈。从u到v的另一个旋转将是围绕向量<1,1,0>的pi旋转。
我对Quaternion不太好。但是,我为此奋斗了几个小时,无法使用Polaris878解决方案。我尝试过对v1和v2进行预规范化。标准化q。标准化q.xyz。但是我还是不明白。结果仍然没有给我正确的结果。
最后,我找到了一个可行的解决方案。如果对其他人有帮助,这是我的工作(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并存(具有一定的容忍度),则必须进行特殊处理,我认为解决方案应为Quaternion(1、0、0、0)(不旋转)或四元数(0,* v1)(旋转180度)
quat = diffVectors(v1, v2); assert quat * v1 == v2
。
angle
从点产品中获得价值,所以这根本不可能奏效。
一些答案似乎没有考虑叉积可能为0的可能性。下面的代码段使用了角度轴表示:
//v1, v2 are assumed to be normalized
Vector3 axis = v1.cross(v2);
if (axis == Vector3::Zero())
axis = up();
else
axis = axis.normalized();
return toQuaternion(axis, ang);
该toQuaternion
可以实现如下:
static Quaternion toQuaternion(const Vector3& axis, float angle)
{
auto s = std::sin(angle / 2);
auto u = axis.normalized();
return Quaternion(std::cos(angle / 2), u.x() * s, u.y() * s, u.z() * s);
}
如果您使用的是Eigen库,则还可以执行以下操作:
Quaternion::FromTwoVectors(from, to)
toQuaternion(axis, ang)
->您忘记指定了什么ang
angle
四元数的轴角表示的一部分,以弧度为单位。
从算法的角度来看,最快的解决方案是伪代码
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
q.w += 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
}
确保需要单位四元数(通常,插值是必需的)。
注意:非单位四元数可以比单元更快地用于某些操作。
crossproduct
在这些情况下将是无效的,因此您首先需要分别检查dot(v1, v2) > 0.999999
和dot(v1, v2) < -0.999999
,并为并行矢量返回同一性quat,或者为相反矢量返回180度旋转(绕任何轴)。