3D加速度计计算方向。

38

我有三个轴的加速度计数值(通常只有重力时包含介于-1.0和1.0之间的数据):

  float Rx;
  float Ry;
  float Rz;

我进行了一些计算,然后得到了每个轴的角度。

  float R =  sqrt(pow(Rx,2)+pow(Ry,2)+pow(Rz,2));
  float Arx = acos(Rx/R)*180/M_PI;
  float Ary = acos(Ry/R)*180/M_PI;
  float Arz = acos(Rz/R)*180/M_PI;

然后我在OpenGL中设置了盒子角度的值

rquad = Arx;
yquad = Ary;

这会旋转我的盒子:

glRotatef(yquad,1.0f,0.0f,0.0f);
glRotatef(rquad,0.0f,1.0f,0.0f);

它适用于半球。我想要使用整个球体,我知道我必须使用Arz值才能使其工作,但我不知道如何将其用于此旋转中。你能帮帮我吗?

更新: 在我的情况下,最终答案是:

  rquad = -atan2(Rx/R, Rz/R)*180/M_PI;
  yquad = -atan2(Ry/R, Rz/R)*180/M_PI;
5个回答

52

正确的答案是:

Roll = atan2(Y, Z) * 180/M_PI;
Pitch = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;

来源: https://www.nxp.com/docs/en/application-note/AN3461.pdf(第10页,公式25和26)

uesp的回答是错误的。在俯仰角和翻滚角都超过45度之后,看起来像是可以接受的近似值。

我可能假设了不同的方向约定,但即使您以任何一种一致的方式交换轴并反转值,uesp的计算也永远不会等价。


3
我知道这个问题很老了,但我不喜欢看到错误的答案。我本人也在寻找答案,而且在谷歌上找到了数百个结果,其中大部分都有同样错误的回答。 - matteo
2
顺便提一下,我忘了提及源文件,其中包含非常详尽的解释:http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf - matteo
1
假设您正在使用链接源中的方程25和26(顺便说一下,这是一个很棒的资源),那么俯仰角不应该是atan2(-X,sqrt(Y * Y + Z + Z))吗? - uesp
是的,抱歉,那是一个打字错误,实际上它是atan2(X,sqrt(Y * Y + Z * Z)),如果我们假设当加速度计与重力轴对齐并指向上方时,加速度计会给出正值,这至少是我的手机所做的。在文章中,他们假设相反的约定。我还没有深入研究为什么Roll不需要另一个减号,但我已经尝试过了,它可以工作。 - matteo
@matteo所提及的文档链接已更改至新位置https://www.nxp.com/docs/en/application-note/AN3461.pdf。由于编辑队列已满,我无法编辑答案。 - Sergey V.
显示剩余2条评论

20

虽然Matteo的回答是正确的,但它并没有提供完整的解决方案: 公式是正确的:

Roll = atan2(Y, Z) * 180/M_PI;
Pitch = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;

然而,当俯仰角为+90/-90度且X轴垂直指向上/下时,理想的加速度计归一化输出应该是:

accX = -1  / accX = 1 
accY = 0
accZ = 0

这意味着滚转角为0度;正确的。但在实际应用中,加速度计输出存在噪声,你可能得到更接近于:

accX = -1  / accX = 1 
accY = 0.003
accZ = 0.004

这可能看起来很小,但会导致滚转角度约为30度,这是不正确的。

显然的直觉是过滤掉最后一位数字,但这会影响精度,这并不总是可接受的。

妥协方案在参考应用笔记中有很好的解释:在计算翻滚角时,将加速度计的X轴读数中包含极小百分比。

Roll  = atan2( Y,   sign* sqrt(Z*Z+ miu*X*X));
sign  = 1 if accZ>0, -1 otherwise 
miu = 0.001

用这种方法引入的误差比之前的情况要小得多:在上述相同条件下测量滚动时,误差只有2-3度。


accYY是相同的吗?因为您在这个答案中提到了YaccY,我不知道您是否指的是不同的值。 - Dekike
我也在stackoverflow上提出了这个问题。我不知道你是否能帮助我解决我的疑惑... https://stackoverflow.com/questions/60174039/correct-way-of-calculating-the-roll-of-an-animal-using-3d-acceleration - Dekike

5

我尝试了推荐的解决方案(Matteo的),一开始似乎效果很好,但我注意到当俯仰角接近90度时(从大约70度开始,但在不同手机上可能不一致),横滚突然激增。当俯仰角为90度时,本应该在0左右的横滚现在已经超过100并持续增加到180。我正在尝试想出一种数学方法来防止这种情况发生,如果我将横滚限制在+90/-90范围内,它会正常运行,但我得不到我想要的范围(+180/-180): Math.atan2(y,Math.sqrt((xx) + (zz))) *(180 / Math.PI))


我遇到了同样的问题,一旦设备接近“倒置”的中间位置,滚动就会非常快地翻转。我尝试通过将sgn(Z)添加到其他计算中来抵消这种情况,或者只是在接近时翻转,但这并不简单。如果我交换坐标系的轴,然后交换我的俯仰/翻滚输出,我可以得到相同的结果,但是翻滚会引起问题,而不是俯仰。 - rajkosto
我遇到了同样的问题。当镜头朝前时,情况看起来相对不错,但是当向下看或者向上看时,整个摄像机会迅速转动。你找到解决办法了吗? - Andres Hernandez

1
对于roll,我发现你可以使用arctan(y/sqrt(X*X)+(z*z)),这将给出-90/90的滚转角度,符合航空标准而不会出现俯仰问题。

是否有可能获得从磁力计计算偏航角的最终公式。可能类似于俯仰方程,因为您希望使用180/-180来获取完整的旋转。我假设是atan2(z,sqrt(xx + yy)然后使用磁北作为参考进行弧度转换。或者如果我想使用所有3个轴,我需要修改公式吗? - Ian Bloomfield

0

我使用以下计算方法将我们的加速度计读数转换为横滚和俯仰值:

Roll = atan2( sqrt(Y*Y + X*X), Z) * 180/M_PI;
Pitch = atan2( sqrt(X*X + Z*Z), Y) * 180/M_PI;

你可能需要交换X/Y/Z值或者根据加速度计的定义对Roll/Pitch进行翻译。将它们用于显示只需要简单的操作:
glRotatef (Pitch, 0.0f, 0.0f, 1.0f);
glRotatef (Roll,  1.0f, 0.0f, 0.0f);

谢谢回答,但我尝试过了,得到的俯仰和翻滚角是0°-180°。我想要得到0°-360°。 - Roland Soós
滚转方程式是错误的。请看我的答案(我不会得到任何荣誉,我在其他地方找到了它)。 - matteo
1
我必须承认,我的代码和这个答案确实基于其他许多来源,但(不幸的是)从未遇到您发布的源。我的方程式确实与您的源中的任何内容都不匹配,尽管它们接近方程式38和39,除了atan2()参数的反转。我本来也会猜测俯仰角会有问题,而不是横滚角,因为之前的测试显示横滚角在-90度到+90度范围内工作正常,而俯仰角从未经过测试。我需要更详细地研究并找出原因... - uesp
错误的是roll,但如果在你的方程式中pitch保持为0,在-90到90度之间roll确实是正确的。当pitch不为零时,就会变成错误,并且当pitch超过约45度时就会变得明显。如果“ pitch从未经过测试”意味着在你的测试中只变化了roll并将pitch保持接近于零,那就可以解释为什么你没有发现roll公式中的错误。 - matteo

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