从Euler ZXZ旋转转换为固定轴XYZ旋转

时间:2012-03-19 17:31:26

标签: java math rotation trigonometry rotational-matrices

我遇到的问题是,我需要从XYZ固定轴旋转转换为关于Z的欧拉旋转,然后是X',然后是Z''。

以下是相关的矩阵:

X:X

Y:Y

Z:Z

组合,如Rz(psi)Ry(phi)Rx(θ)= Rxyz(theta,phi,psi);他们给出了:

Rxyz:Rxyz

我想要的欧拉角特定惯例的旋转矩阵;是这样的:

Euler:Euler

所以我最初的计划是比较矩阵元素,并提取我想要的角度;我想出了这个(最后的实际当前代码):

Code

但这在几种情况下不起作用。当Cos(theta)Cos(phi)== 1时最明显的是;从那以后,Cos(beta)= 1,因此Sinβ= 0.其中Sin(beta)是代码中的s2。只有当Cos(theta)和cos(phi)= +/- 1时才会发生这种情况。

所以我马上就可以排除可能出现的情况;

当theta或phi = 0,180,360,540,...,则Cos(theta)和Cos(phi)为+/- 1;

所以我只需要针对这些情况做不同的事情;

我最终得到了这段代码:

public static double[] ZXZtoEuler(double θ, double φ, double ψ){

    θ *= Math.PI/180.0;
    φ *= Math.PI/180.0;
    ψ *= Math.PI/180.0;

    double α = -1;
    double β = -1;
    double γ = -1;

    double c2 = Math.cos(θ) * Math.cos(φ);

    β = Math.acos(r(c2));

    if(eq(c2,1) || eq(c2,-1)){
        if(eq(Math.cos(θ),1)){
            if(eq(Math.cos(φ),1)){
                α = 0.0;
                γ = ψ;
            }else if(eq(Math.cos(φ),-1)){
                α = 0.0;
                γ = Math.PI - ψ;
            }
        }else if(eq(Math.cos(θ),-1)){
            if(eq(Math.cos(φ),1)){
                α = 0.0;
                γ = -ψ;
            }else if(eq(Math.cos(φ),-1)){
                α = 0.0;
                γ = ψ + Math.PI;
            }
        }
    }else{

        //original way

        double s2 = Math.sin(β);

        double c3 = ( Math.sin(θ) * Math.cos(φ) )/ s2;
        double s1 = ( Math.sin(θ) * Math.sin(ψ) + Math.cos(θ) * Math.sin(φ) * Math.cos(ψ) )/s2;

        γ = Math.acos(r(c3));
        α = Math.asin(r(s1));

    }

    α *= 180/Math.PI;
    β *= 180/Math.PI;
    γ *= 180/Math.PI;

    return new double[] {r(α), r(β), r(γ)};
}

其中r和eq只是两个简单函数;

public static double r(double a){
    double prec = 1000000000.0;
    return Math.round(a*prec)/prec;
}

static double thresh = 1E-4;
public static boolean eq(double a, double b){
    return (Math.abs(a-b) < thresh);
}

eq只是比较测试的数字,而r是为了防止浮点错误推动数字超出Math.acos / Math.asin的范围并给我NaN结果;

(即偶尔我会以Math.acos(1.000000000000000004)或其他东西结束。)

其中考虑了围绕x和y旋转的4种情况,其中c2 == 1。

但现在问题出现了;

我上面所做的一切对我来说都是有道理的,但它没有给出正确的角度;

这是一些输出,在每对中,第一个是theta phi psi角度,每对中的第二个是相应的alpha beta伽马线。忽略舍入误差,似乎是通过约

得到一些角度
[0.0, 0.0, 0.0] - correct!
[0.0, 0.0, 0.0]

[0.0, 0.0, 45.0] - correct!
[0.0, 0.0, 45.0]

[0.0, 0.0, 90.0] - correct!
[0.0, 0.0, 90.0]

[0.0, 0.0, 135.0] - correct!
[0.0, 0.0, 135.0]

[0.0, 0.0, 180.0] - correct
[0.0, 0.0, 180.0]

[0.0, 0.0, 225.0] - correct
[0.0, 0.0, 225.0]

[0.0, 0.0, 270.0] - correct
[0.0, 0.0, 270.0]

[0.0, 0.0, 315.0] - correct
[0.0, 0.0, 315.0]

[0.0, 45.0, 0.0] - incorrect: should be [90, 45, -90]
[90.0, 44.999982, 90.0]

[0.0, 45.0, 45.0]
[45.000018, 44.999982, 90.0]

[0.0, 45.0, 90.0]
[0.0, 44.999982, 90.0]

[0.0, 45.0, 135.0]
[-45.000018, 44.999982, 90.0]

[0.0, 45.0, 180.0]
[-90.0, 44.999982, 90.0]

[0.0, 45.0, 225.0]
[-45.000018, 44.999982, 90.0]

[0.0, 45.0, 270.0]
[0.0, 44.999982, 90.0]

[0.0, 45.0, 315.0]
[45.000018, 44.999982, 90.0]

[0.0, 90.0, 0.0]
[90.0, 90.0, 90.0]

[0.0, 90.0, 45.0]
[45.000018, 90.0, 90.0]

[0.0, 90.0, 90.0]
[0.0, 90.0, 90.0]

[0.0, 90.0, 135.0]
[-45.000018, 90.0, 90.0]

[0.0, 90.0, 180.0]
[-90.0, 90.0, 90.0]

[0.0, 90.0, 225.0]
[-45.000018, 90.0, 90.0]

我认为这是由于Math.acos和Math.asin的工作方式,有人能想到解决方案吗?

编辑:math.asin和math.acos分别在-pi / 2和pi / 2以及0和pi之间返回值。这不是含糊不清的,所以我认为问题不在这里。好像我可能在某个地方有数学错误,但我看不出我的推理漏洞......

EDIT2:对于任何人如何不知道Euler旋转如何工作,就像这样:

Euler Angles Gif

也就是说,围绕 Z 旋转,然后围绕新 X 轴( X')旋转,然后围绕新 Z'旋转'轴。

1 个答案:

答案 0 :(得分:1)

我还没有完全弄清楚这一点,但我注意到了一件事:你使用arccos / arcsin函数,好像cos / sin是双射的,只是采用它们的逆。但是,在获取arccos时,请考虑general solutions到弧函数。例如,当cos y = x时,有两个(好的,无限多个)解决方案:

  • y = arccos x + 2kPI,其中k element Z
  • y = 2PI - arccos x + 2kPI,k如上

使用k=-1,最后一个等式缩减为

  • y = -arccos x

总的来说,y = +- arccos x。这基本上归结为cosx=0轴对称的事实。类似的论点适用于arcsin,导致y = PI - asin xk=0的一般解决方案中sin y = x

这立即适用于您的代码。声明γ = Math.acos(r(c3));必须以某种方式考虑到标志。我在努力解决这个问题,必须有一个标准来解决“不正确”的解决方案。