Seb*_*ski 20 normalization quaternions
我知道如果我想旋转矢量,则需要对四元数进行归一化.
但有没有任何理由不自动规范化四元数?如果有,那么四元数运算会导致非标准化四元数?
对不起,如果这个问题有点模糊.我还在尝试绕着四元数缠绕我.
Dav*_*men 33
迟到的反应; 这个答案适用于将来遇到这个问题的人,而不是提问者.
我不同意有关仅偶尔标准化四元数的其他两个答案.使用四元数来旋转/变换矢量或生成旋转/变换矩阵的标准公式隐含地假设四元数是标准化的.使用非标准化四元数产生的误差与四元数的平方成正比.最好避免二次误差增长.
如果经常标准化,则不需要平方根.一阶近似非常有效.这是我用于四元数的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));
}
Run Code Online (Sandbox Code Playgroud)
请注意,我使用的是第一阶Padé逼近2.0/(1.0+qmagsq)而不是第一阶泰勒展开0.5*(3.0-qmagsq)来估算1.0/std::sqrt(qmagsq).该近似值如果有效,则通过简单除法替换平方根调用.关键是要找出何时有效,这就是幻数2.107342e-08发挥作用的地方.
为什么Padé接近?两个原因.一个是,对于qmagsq接近一个的值,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传播四元数的方法.即使您使用的是不是Lie组积分器的高阶积分技术,您仍然在某处使用Euler步骤.
小智 9
产生四元数的任何操作都需要进行标准化,因为浮点进动错误将导致它不是单位长度.
我建议不要出于性能原因自动执行标准化的标准例程.任何有能力的程序员都应该了解精度问题并且能够在必要时对数量进行标准化 - 并且并不总是需要具有单位长度的四元数.
矢量运算也是如此.
有趣的是,构建旋转矩阵是一种不需要标准化四元数的操作,可以节省一个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)
Run Code Online (Sandbox Code Playgroud)
(用MATLAB-ish表示法)表示四元数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].
Run Code Online (Sandbox Code Playgroud)
乘以翻译矩阵等,像往常一样进行完整的转换.这样你可以这样做,例如,
[xh yh zh wh]' = ... * OtherM * M * [xold yold zold 1]';
[xnew ynew znew] = [xh yh zh] / wh.
Run Code Online (Sandbox Code Playgroud)
当然,仍然建议至少偶尔标准化四元数(其他操作也可能需要).