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

Math 寻找表示从一个向量到另一个向量旋转的四元数,math,vector,quaternions,Math,Vector,Quaternions,我有两个向量u和v。有没有办法找到表示从u到v的旋转的四元数?所述的问题没有很好的定义:给定向量对没有唯一的旋转。例如,考虑U=和V=的情况。从u到v的一个旋转是围绕z轴的pi/2旋转。从u到v的另一个旋转是围绕向量的pi旋转 别忘了标准化q Richard说的没有唯一的旋转是对的,但上面应该给出“最短弧”,这可能是您需要的。我对四元数不太在行。然而,我为此奋斗了数小时,无法使Polaris878解决方案工作。我尝试过对v1和v2进行预规范化。标准化q。标准化q.xyz。但我还是不明白。结果仍然

我有两个向量u和v。有没有办法找到表示从u到v的旋转的四元数?

所述的问题没有很好的定义:给定向量对没有唯一的旋转。例如,考虑U=和V=的情况。从u到v的一个旋转是围绕z轴的pi/2旋转。从u到v的另一个旋转是围绕向量的pi旋转

别忘了标准化q


Richard说的没有唯一的旋转是对的,但上面应该给出“最短弧”,这可能是您需要的。

我对四元数不太在行。然而,我为此奋斗了数小时,无法使Polaris878解决方案工作。我尝试过对v1和v2进行预规范化。标准化q。标准化q.xyz。但我还是不明白。结果仍然没有给我正确的结果

但最终我找到了一个解决方案。如果它对其他人有帮助,下面是我的工作(python)代码:

如果v1和v2是平行的,如v1==v2或v1==-v2(有一定的公差),则必须做一个特例,其中我认为解决方案应该是四元数(1,0,0,0)(无旋转)或四元数(0,*v1)(180度旋转)

我想出了一个解决方案,我相信Imbrondir正试图提出这个解决方案(尽管有一个小错误,这可能就是为什么邪恶的花栗鼠很难验证它)

假设我们可以构造一个表示绕轴旋转的四元数,如下所示:

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
由于可以通过围绕垂直向量旋转θ(向量之间的角度)来实现从uv的旋转,看起来我们可以直接从点积和叉积的结果构造表示这种旋转的四元数;然而,目前的情况是,θ=角度/2,这意味着这样做将导致所需旋转的两倍

一种解决方案是计算uv之间的中间向量,并使用u中间向量的点和叉积来构造一个四元数,表示u中间向量之间两倍角度的旋转,它带我们一路来到v

有一种特殊情况,u==-v和唯一的中间向量变得无法计算。这是意料之中的,考虑到无限多的“最短弧”旋转可以将我们从u带到v,我们必须简单地围绕与u(或v)正交的任何向量旋转180度,作为我们的特例解。这是通过取u与任何其他与u不平行的向量的归一化叉积来实现的

伪代码紧随其后(显然,在实际情况中,特殊情况必须考虑浮点不精确性——可能是根据某个阈值而不是绝对值检查点积)

还请注意,当u==v时没有特殊情况(生成了标识四元数——请自己查看)

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

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);
}
零旋转的四元数是:

q.w   == dot(u, v)
q.xyz == cross(u, v)
q.w   == 1
q.xyz == (0, 0, 0)
计算中间四元数只需将四元数求和并对结果进行规格化,就像使用向量一样。但是,与向量的情况一样,四元数必须具有相同的大小,否则结果将向具有较大大小的四元数倾斜

由两个向量的点和叉积构成的四元数将具有与这些乘积相同的大小:
length(u)*length(v)
。我们可以放大恒等式四元数,而不是将所有四个分量都除以这个因子。如果你想知道为什么被接受的答案似乎通过使用
sqrt(长度(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)));
}

从算法的角度来看,最快的解决方案是伪代码

 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
 }
确保需要单位四元数(通常,插值需要单位四元数)

注:
非均匀四元数可以用于比单位更快的某些运算。

为什么不使用纯四元数表示向量?最好先将它们正常化。
q1=(0 ux uy uz)“
q2=(0 vx vy vz)
q1 qrot=q2
与q1-1预乘
qrot=q1-1 q2
其中q1-1=q1conj/qnorm
这可以被认为是“左师”。 右除法,这不是您想要的:

QROT,右=Q2-1 Q1

< P>一些答案似乎不考虑交叉乘积可能为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);
四元数的
可以实现如下:

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)

仅使用标准化四元数,我们可以用以下术语表达约瑟夫·汤普森的答案

让q_v=(0,u_x,v_y,v_z)和q_w=(0,v_x,v_y,v_z)并考虑

q=q_v*q_w=(-u点v,u x v)

把q表示为q(q_0,q_1,q_2,q_3)


q_r=(1-q_0,q_1,q_2,q_3)。normalize()

我有一个工作的实现,但你的这个更漂亮,所以我真的希望它能工作。不幸的是,它没有通过我所有的测试用例。我的测试看起来都像我
//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);
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);
}
Quaternion::FromTwoVectors(from, to)