如何根据3D陀螺仪数据更新四元数?

11

我有一个初始四元数,q0。接着我获得角速度测量值,然后积分这些速度,以得到每秒约50个角度。如何基于这3个角度生成一个新的四元数?我不能只制作3个四元数,对吧?

具体来说,我要更新当前方向的四元数Q.new,通过乘以Q.update quaternion来实现。我该如何用这三个角度创建Q.update quaternion呢?

谢谢!


不幸的消息是:它比那复杂得多。例如,参见使用基于四元数的间接卡尔曼滤波器进行方向估计,并进行外部加速度自适应估计。顺便问一下,你打算如何处理陀螺仪漂移? - Ali
这个问题似乎不属于帮助中心定义的编程范围之内。 - Ali
抱歉,伙计们。我刚开始做软件开发工作两周了,这是我在这里的第一个问题(有趣的是,我是个机械工程师 :D)。你们是最棒的!关于漂移?我还不知道。 - user3598726
5个回答

12

请原谅我的回复有些晚了,但是所有的答案似乎都太复杂了。像我这样的人可能更喜欢这种更“方便”的方法:

假设omega=(alpha,beta,gamma) 是陀螺仪测量得到的角速度矢量。然后我们进行旋转。

theta = ||omega||*dt; //length of angular velocity vector

许多单位(根据陀螺仪而定,可能是度或弧度)周围

v = omega / ( ||omega|| ); // normalized orientation of angular velocity vector

因此,我们可以构造旋转四元数如下:

Q.update = (cos(theta/2),v_x * sin(theta/2), v_y * sin(theta/2), v_z * sin(theta/2));

现在仅需通过 Q.update 旋转我们当前的旋转即可。这很简单:

Q.new = multiply_quaternions(Q.update,Q.new); 
// note that Q.update * Q.new != Q.new * Q.update for quaternions

完成了。四元数很美妙,不是吗?

这里有一些关于陀螺仪和四元数的幻灯片可能会有用: http://stanford.edu/class/ee267/lectures/lecture10.pdf


2
你为什么使用 Q.new = multiply_quaternions(Q.update,Q.new) 而不是 Q.new = multiply_quaternions(Q.new,Q.update)?在你分享的脚本第26页,他们在右侧乘以 Q.update。 - jrichner
这是一个很好的解决方案,但如果omega不随时间改变怎么办?那么归一化将会是无穷大。 - anishtain4

6

我猜你正在整合欧拉角,因为你喜欢让自己变得困难。首先,陀螺仪不能直接集成到你的欧拉角中。如果你问这个问题,我会假设你也不知道如何正确地从你的陀螺仪测量中找到欧拉角的速率变化。你需要一个转换矩阵才能使其工作。我强烈建议你购买Farrell的"Aided Navigation"一书。在第57页上,他解释了如何计算转换矩阵以将您的陀螺仪速率转换为欧拉速率。但是,为什么要麻烦呢?当你可以直接从四元数和陀螺仪数据中获得速率变化四元数时。

rate of change quaternion  = qdot
quaternion = q
gyro quaternion = w = [0,gyrox,gyroy,gyroz]

所以
qdot = 0.5 *  q Ⓧ w

在这里,Ⓧ代表四元数积。在这里要注意坐标系的问题。陀螺仪表示传感器坐标系相对于惯性坐标系的角速度,这个旋转需要用四元数表示从传感器坐标系到惯性坐标系的类似旋转。在这种情况下,它应该表示从惯性坐标系到陀螺仪坐标系的旋转。如果我们忽略地球的旋转等因素,上述方程式是成立的。

请注意“辅助导航”。我认为他对四元数的处理非常令人困惑。


关于滚动、俯仰、偏航四元数乘法的顺序,因为在四元数乘法中,滚动 x 俯仰不等于俯仰 x 滚动。 - user818117
请您详细说明一下吗?为什么陀螺仪数据可以与旋转四元数的值为0配合使用,而在 qdot = 0.5 * q Ⓧ w 中的0.5是什么意思?我们应该对四元数q进行标量乘法吗?时间尺度去哪了? - Ariksu
1
关于使用0的问题...在这个答案中定义的所谓陀螺四元数表示三维空间中的一个向量,因此它具有等于零的“标量”部分。这是为了允许旋转四元数(具有非零标量部分)在使用四元数乘法时对向量进行旋转。关键信息在于四元数可以表示向量(具有三个基本分量)和旋转(即围绕方向的角度转动)。这些是完全不同的事情。尽管如此,这确实令人困惑。 - J.E.Tkaczyk
关于滚动x俯仰不等于俯仰x滚动。这可以通过理解三个角度(gyroX * dt,gyroY * dt和gyroZ * dt)不是欧拉角来解决。它们是旋转向量的组成部分。也就是说,它们是同时绕x、y和z轴发生的旋转,就像绕旋转轴的转动一样。根据使用的约定,这种相同旋转有多个欧拉角表示。最好坚持与四元数密切相关的轴/角度表示。 - J.E.Tkaczyk
关于系数0.5。我认为这与绕有分量(x,y,z)的轴的旋转角度g对应的四元数有关:[cos g/2,sin g/2 x (x,y,z)]。请注意半角度。但是,此答案未正确构建四元数。请参见下面的更好的答案。还可以在这里描述四元数旋转 - J.E.Tkaczyk

5
您可以直接将角速度积分以获得角位置(作为欧拉角),然后将欧拉角转换为四元数,最后将四元数相乘以累积方向。假设您的输入是由角速度的三维向量给出:omega = (alpha, beta, gamma),单位是每秒度。要获得欧拉角E,以度为单位,请将omega乘以时间变化dt。这将导致以下结果:
Vector3D omega = new Vector3D(alpha, beta, gamma);
Vector3D E = omega * dt;

通过将当前时间减去上次更新时间,您可以得到dt。从陀螺仪数据中获取三维欧拉角后,通过以下公式将其转换为四元数(w,x,y,z)(来源于维基百科):

float w = cos(E.x/2) * cos(E.y/2) * cos(E.z/2) + sin(E.x/2) * sin(E.y/2) * sin(E.z/2);
float x = sin(E.x/2) * cos(E.y/2) * cos(E.z/2) - cos(E.x/2) * sin(E.y/2) * sin(E.z/2);
float y = cos(E.x/2) * sin(E.y/2) * cos(E.z/2) + sin(E.x/2) * cos(E.y/2) * sin(E.z/2);
float z = cos(E.x/2) * cos(E.y/2) * sin(E.z/2) - sin(E.x/2) * sin(E.y/2) * cos(E.z/2);

Quaternion q = new Quaternion(w, x, y, z);

只需将上述两个代码片段复制并粘贴到您的Q.update()方法中,然后返回四元数即可。如果您想知道方程式的工作原理,请查看维基链接并仔细阅读。


2
我相当确定这是错误的。第一部分很接近。角速度的积分给出了角位移的变化,这是从起始方向旋转的结果。旋转向量具有x、y和z三个分量,但是这些不是欧拉角。旋转发生在旋转轴周围,并同时绕三个轴旋转。这里的另一个答案给出了x、y、z与四元数之间相当接近的关系。欧拉角将旋转描述为一组连续的三个旋转,并且有多种顺序约定。 - J.E.Tkaczyk
1
将角速度集成不会给出欧拉角。陀螺仪数据提供了身体参考系中的变化率,这与欧拉角不同。 - user3207838
这是错误的,因为 E.x=pi/2Ex=pi/2+2*pi 给出了相同的旋转。 - anishtain4

0

-1 如我在评论中所说,这比那复杂得多。请删除此答案。 - Ali
我假设他已经有了相对于上一个方向的旋转(alpha、beta和gamma),只是想通过将两个四元数相乘来更新当前方向。他最初问的问题是:“如何基于这三个角度创建一个四元数?我不能只创建三个四元数,是吗?” 对于这个问题,我不明白为什么我的回答会得到-1分。然而,如果你的理解是他的问题是:“如何从陀螺仪获取方向”,那当然了,这会更加复杂,还需要考虑陀螺仪漂移等其他因素才能进行整合。 - edu_
是的,对我来说似乎他最终想要追踪方向;从他的评论中可以看出,他不了解陀螺仪漂移及如何处理它。告诉他如何更新不但不能帮助他,反而会误导他走向错误的轨道。越早意识到困难,就越好,这才是我认为的真正帮助。 - Ali
同意。我的回答是在他发表评论之前回答了他最初的问题。无论如何,如果他有一个IMU(即陀螺仪和加速度计),我建议他研究Madgwick的方向滤波器。在http://www.x-io.co.uk/上有很多资源。 - edu_
我知道漂移问题,我使用互补滤波器和加速度计进行校正 :D 我已经弄清楚了一切,等代码准备好后我会发布的。 - user3598726

0

1) 更新本身很简单。只需从角速度的“轴角”表示中创建四元数。

顺便提一下,“角速度”的常见表示方式是三个标量的向量。该向量与局部对象框架中的瞬时旋转轴平行。因此,您的方程将如下所示:Q.new=Q.update(alfa,beta,gamma)*Q.new。 http://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation#Unit_quaternions

并且要考虑到积分时间(最简单的近似,欧拉积分) Q.new = Q.fromVector(angularVelocity * deltaTime)*Q.new

还要注意,angularVelocity * deltaTime会产生“指数映射”旋转,可以轻松地转换为四元数。

对于精确的积分,应使用更复杂的近似方法。但是您必须了解测量的确切含义(测量时间、噪声等)。

2) 从您的问题中无法确定函数update的含义。 (alfa,beta,gamma)参数是什么?如果这是关于3个正交轴的瞬时旋转速度,则可以简单地从中构建角速度。只需确保使用正确的单位(每秒弧度)。

3) 获取加速度计数据的有用集成方式对于简短的答案来说过于复杂。每个硬件都应该使用自己的定制属性进行处理。此外,它还应该融合线性加速度数据以避免漂移。


就像我之前所说的一样,信息太少了。我猜它可能是这样的:update(alfa,beta,gamma) { Vector3 r = Vector3(alfa,beta,gamma) * deltaTimeSec; double l = r.length();Quaternion rQ = (0.5r.xsin(l)/l, 0.5r.ysin(l)/l), 0.5r.zsin(l)/l, 0.5*cos(l) ); return rQ; } - minorlogic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接