我知道如果要旋转矢量,则需要对四元数进行归一化。
但是有什么理由不自动规范化四元数吗?如果有,哪些四元数运算会导致非归一化四元数?
- 将两个四元数相乘?
- 点积?
对不起,如果这个问题有点模糊。我仍在尝试围绕四元数。
我知道如果要旋转矢量,则需要对四元数进行归一化。
但是有什么理由不自动规范化四元数吗?如果有,哪些四元数运算会导致非归一化四元数?
对不起,如果这个问题有点模糊。我仍在尝试围绕四元数。
迟到的反应;这个答案适用于将来遇到这个问题的人,而不是提问者。
我不同意其他两个关于仅偶尔标准化四元数的答案。使用四元数来旋转/变换向量或生成旋转/变换矩阵的标准公式隐含地假设四元数是归一化的。使用非归一化四元数导致的误差与四元数幅度的平方成正比。最好避免二次误差增长。
如果您经常标准化,则不需要平方根。一阶近似效果很好。这是我将四元数用作 IEEE 双精度数的方法,有点程式化:
double qmagsq = quat.square_magnitude();
if (std::abs(1.0 - qmagsq) < 2.107342e-08) {
quat.scale (2.0 / (1.0 + qmagsq));
}
else {
quat.scale (1.0 / std::sqrt(qmagsq));
}
请注意,我使用一阶 Padé 近似值2.0/(1.0+qmagsq)
而不是一阶泰勒展开0.5*(3.0-qmagsq)
来估计1.0/std::sqrt(qmagsq)
. 如果有效,这个近似值会用一个简单的除法代替平方根调用。关键是要找到该近似值何时有效,这就是神奇数字 2.107342e-08 发挥作用的地方。
为什么要使用 Padé 近似值?两个原因。一种是对于qmagsq
接近 1 的值,1+qmagsq
损失的精度比3-qmagsq
. 另一个是与泰勒展开式相比,Padé 近似值将误差减少了三倍。对于qmagsq
0 到 2 之间的值,此近似值的误差小于(1-qmagsq)^2 / 8
. 幻数 2.107342e-08 表示此错误超过 IEEE 双倍 ULP 的一半。如果您采取合理的小步骤,则四元数大小的平方将始终在该限制内。你永远不会打电话sqrt
。
如果您使用李群积分技术来传播四元数,则这种“始终标准化”范式的一个例外可能是。如果您不知道这意味着什么,您可能正在使用等价物q(t+Δt) = q(t) + dq(t)/dt*Δt
来传播四元数。即使您使用不是李群积分器的高阶积分技术,您仍在某处使用该欧拉步骤。
任何产生四元数的操作都需要归一化,因为浮点进动错误会导致它不是单位长度。
出于性能原因,我建议不要使用标准例程自动执行规范化。任何称职的程序员都应该意识到精度问题,并能够在必要时对数量进行规范化——并不总是需要有一个单位长度的四元数。
向量运算也是如此。
有趣的是,构建旋转矩阵是一种不需要归一化四元数的操作,为您节省了一个sqrt
:
M = [w*w+x*x-y*y-z*z, 2*(-w*z+x*y), 2*(w*y+x*z);
2*(w*z+x*y), w*w-x*x+y*y-z*z, 2*(-w*x+y*z);
2*(-w*y+x*z), 2*(w*x+y*z), w*w-x*x-y*y+z*z] / (w*w+x*x+y*y+z*z)
(在 MATLAB-ish 表示法中)用于 quaternion w+x*i+y*j+z*k
。
此外,如果您使用齐次坐标和 4x4 变换矩阵,您还可以省去一些除法操作:只需将 3x3 旋转部分作为四元数归一化,然后将其平方长度放入 (4,4) 元素:
M = [w*w+x*x-y*y-z*z, 2*(-w*z+x*y), 2*(w*y+x*z), 0;
2*(w*z+x*y), w*w-x*x+y*y-z*z, 2*(-w*x+y*z), 0;
2*(-w*y+x*z), 2*(w*x+y*z), w*w-x*x-y*y+z*z, 0;
0, 0, 0, w*w+x*x+y*y+z*z].
像往常一样乘以平移矩阵等,以获得完整的变换。这样你可以做,例如,
[xh yh zh wh]' = ... * OtherM * M * [xold yold zold 1]';
[xnew ynew znew] = [xh yh zh] / wh.
当然,仍然建议至少偶尔标准化四元数(其他操作也可能需要它)。
如果一个单位四元数是通过对其一阶时间导数进行数值积分获得的,积分器可以使用简单的误差反馈自动对其进行归一化。
令q表示 4 x 1 列的四元数矩阵,dq表示其时间导数。然后将dq+0.5(1-qq)q/tau发送到积分器以代替dq并使用合适的时间常数tau将连续归一化q。qq表示内积。
我模拟了一个保守的、铰接式 Bricard 机构在无重力空间中漂浮 360 万秒,也就是将近 42 天。四元数代表浮动基体的方向。使用0.5 秒的时间常数tau ,总能量保持恒定在百万分之一以内。在数值积分器 DE 中使用了 10^-12 的绝对误差容限和零的相对误差容限。
http://www.amazon.com/Computer-Solution-Ordinary-Differential-Equations/dp/0716704617/
四元数通常通过数值积分获得。如果它们没有在积分器内部归一化,那么幅度和相位误差将会累积。一个归一化的四元数沿着一个单位球体移动,它的一阶导数与该球体相切。如果四元数偏离单位球面,它将开始累积在积分器外部进行归一化无法校正的相位误差。因此,四元数必须在数值积分器内连续归一化,以最小化相位误差。
你的问题是模棱两可的,但如果你需要标准化一个四元数很简单
q_normalized = q /square(norm(q))
其中,q = q1 +q2i +q3 j +q4 k 范数 (q) = (q1)^2 + (q2)^2 + (q3)^2) + (q4)^4
如果要向我解释你的问题
使用 NONunit 四元数可能是有效的。
只有少数操作需要单位长度,例如插值。
一些技巧:
所以不需要只使用单位四元数,这只是常见的做法。对于每个用例,您都可以决定是否使用规范化。我个人更喜欢使用非单位四元数。
警告:通常,在使用单位四元数时,我们会忘记数值错误。例如,从/到矩阵四元数转换并认为它仍然是单位会使数值不稳定,矩阵被缩放,提取的四元数是无效的。你可以很容易地做这样的实验。