度/秒到轴角

Degrees/sec to axis-angle

提问人:wiswasi 提问时间:10/6/2023 更新时间:10/7/2023 访问量:106

问:

我有一张以度/秒(X、Y、Z)为单位的陀螺仪读数。我需要在 C 中将其转换为轴角格式(X、Y、Z、THETA)。 这是我想出的:

void gyroToAxisAngle(double gyro[3], double axis_angle[4]) {
    double gyroRad[3];
    // Convert gyro values from degrees per second to radians per second
    gyroRad[0] = gyro[0] * M_PI / 180.0;
    gyroRad[1] = gyro[1] * M_PI / 180.0;
    gyroRad[2] = gyro[2] * M_PI / 180.0;

    // Calculate the angular velocity magnitude
    double gyroMagnitude = sqrt(gyroRad[0] * gyroRad[0] + gyroRad[1] * gyroRad[1] + gyroRad[2] * gyroRad[2]);

    // Calculate the axis of rotation
    axis_angle[0] = gyroRad[0] / gyroMagnitude;
    axis_angle[1] = gyroRad[1] / gyroMagnitude;
    axis_angle[2] = gyroRad[2] / gyroMagnitude;

    // Calculate the rotation angle (theta)
    axis_angle[3] = gyroMagnitude;
}

让我们以这些陀螺仪值为例,结果值为 , 。{-22.767, 145.174, -47.1}XYZ: (-0.147539, 0.940781, -0.305225)Theta: 2.693255

谁能确认我是否正确转换,或者是否有更准确的转换方式?谢谢!

c 数学 物理 三角学

评论

1赞 Erdal Küçük 10/6/2023
看一看:数学 - 将欧拉转换为轴角。请注意“问题”部分!
1赞 Erdal Küçük 10/6/2023
我还推荐以下 wiki 文章:欧拉角和轴角表示
1赞 Erdal Küçük 10/6/2023
如果你阅读了建议的文章,你会发现在如何解释角度(旋转顺序)方面是有区别的。
3赞 Surge 10/6/2023
还有一个需要仔细考虑的问题:当输入为 0 时会发生什么?您将遇到除以零的问题。当输入为 0 时,您需要有一个“默认旋转角度”,或者在将参数传递给函数之前检查参数。
2赞 chux - Reinstate Monica 10/7/2023
@Surge,此外,如果所有 3 个输入都小于 ,我们将除以零问题。sqrt(DBL_TRUE_MIN)

答:

0赞 chux - Reinstate Monica 10/6/2023 #1

是否有更准确的转换方式?

除以 0.0 @ 激

当所有输入为零(甚至所有输入小于幅度)时,代码将除以 0.0。sqrt(DBL_TRUE_MIN)

在这种情况下,需要一些替代代码。

if (gyroMagnitude == 0.0) {
  // TDB code needed to handle all tiny cases.
} else {
  // The usual
}

溢出

尽管 OP 的值不太可能,但代码可能希望更好地处理溢出。在这种情况下,可以使用 TBD 替代代码或更广泛的类型。

再多看几点

是的,使用更宽的数学(当比 时更宽)和 。long doubledoubleFLT_EVAL_METHOD < 2

也许不是 OP 所寻求的几点改进,但它是:

// long double constant - degrees to radians
#defined D2R (3.1415926535897932384626433832795L / 180)

void gyroToAxisAngle(const double gyro[3], double axis_angle[4]) {
    // Convert gyro values from degrees per second to radians per second
    long double gr0 = gyro[0] * D2R;
    long double gr1 = gyro[1] * D2R;
    long double gr2 = gyro[2] * D2R;

    // Calculate the angular velocity magnitude
    long double gyroMagnitude = sqrtl(gr0*gr0 + gr1*gr1 + gr2*gr2);

    // Calculate the axis of rotation
    axis_angle[0] = (double) (gr0 / gyroMagnitude);
    axis_angle[1] = (double) (gr1 / gyroMagnitude);
    axis_angle[2] = (double) (gr2 / gyroMagnitude);

    // Calculate the rotation angle (theta)
    axis_angle[3] = (double) gyroMagnitude;
}

我估计 OP 的结果会好 1 或 2 ULP


使用(或)打印,而不是用于评估准确性。"%.17g""%a""%f"


[编辑]

由于都是归一化的角度,因此失去了度/弧度单位,因此无需先转换为弧度。这种不必要的转换会失去一点准确性,并且是可以避免的工作。只需要度数到弧度的转换。axis_angle[0 to 2]axis_angle[3]

#include <float.h>
#include <math.h>

// Rather than  a * pi / 180, insure only 1 operation. 
#define DEGS2RADS(a) ((a) * (3.1415926535897932384626433832795/180))

void gyroToAxisAngle(const double gyro[3], double axis_angle[4]) {
  // Conversion to radians is not needed as code is forming a normalized vector.
  // Conversion slightly increases accuracy. 

  // Calculate the angular velocity magnitude
  double gyroMagnitude = sqrt(
      gyro[0] * gyro[0] + gyro[1] * gyro[1] + gyro[2] * gyro[2]);

  // See below code to handle extremes, 
  // or just handle the tiny value case with your own code
  if (gyroMagnitude == 0.0) {
    // TBD Code to handle tiny or the zero case.
    return;
  }

  axis_angle[0] = gyro[0] / gyroMagnitude;
  axis_angle[1] = gyro[1] / gyroMagnitude;
  axis_angle[2] = gyro[2] / gyroMagnitude;
  axis_angle[3] = DEGS2RADS(gyroMagnitude);
}

要处理极端情况:

  // If all the gyroRad are small, scale them up, centered about exponent = 0 
  #define SCALE (-DBL_MIN_EXP*3/4)
  if (gyroMagnitude < DBL_MIN) {
    // Scaled up versions of gyroRad_scaled[]
    double g_x[3];
    g_x[0] = scalbn(gyro[0], SCALE);
    g_x[1] = scalbn(gyro[1], SCALE);
    g_x[2] = scalbn(gyro[2], SCALE);
    gyroMagnitude = sqrt(g_x[0] * g_x[0] + g_x[1] * g_x[1] + g_x[2] * g_x[2]);
    // All gryo[] are zeros. 
    if (gyroMagnitude == 0.0) {
      // Make all same magnitude
      double mag = 1.0 / sqrt(3.0);
      axis_angle[0] = axis_angle[1] = axis_angle[2] = axis_angle[3] = 0.0;
      return;
    }
    gyroMagnitude = scalbn(gyroMagnitude, -SCALE);
  } else if (gyroMagnitude > DBL_MAX) {
    // TBD Code
    return;
  }

评论

0赞 chux - Reinstate Monica 10/6/2023
注意:如果 OP 通常转换正确,则无法确认,
1赞 Ian Abbott 10/6/2023
gyro[]包含角速度,而不是角度,因此范围缩小是不合适的。fmod
0赞 chux - Reinstate Monica 10/7/2023
@IanAbbott 同意范围缩小不适合角速度。答案已修改。
0赞 Ian Abbott 10/7/2023
您的特殊情况使向量过大。它应该是一个单位向量。我不认为方向太重要,但如果你想保留这些东西以获得 8 个方向的选择,用结果替换就可以了。if (gyroMagnitude == 0.0)copysign1.0sqrt(3)/3
0赞 Ian Abbott 10/7/2023
同样,对于您的特殊情况,难道不应该使用符号 from 而不是相反的方式分配给 吗?axis_angle[x]gyroRad[x]