我遇到的问题是,需要将XYZ固定轴旋转转换为绕Z、X'和Z''的欧拉旋转。
以下是相关矩阵:
X:
Y:
Z:
将它们组合在一起,即Rz(psi) Ry(phi) Rx(theta) = Rxyz(theta,phi,psi),则得到:
Rxyz:
我想要的特定欧拉角的旋转矩阵为:
Euler:
所以我的最初计划是比较矩阵元素,并从中提取所需的角度。我制定了下面这个计划:
但是在某些情况下,这种方法行不通。最明显的情况是当Cos(theta)Cos(phi)=1时;因为此时Cos(beta)=1,所以Sin[beta]=0。其中Sin(beta)代表代码中的s2。这种情况只会在Cos(theta)和cos(phi)=+/-1时出现。
因此,我可以很快排除可能的情况;当theta或phi=0、180、360、540等时,Cos(theta)和Cos(phi)为+/-1。所以我只需要针对这些情况进行不同的处理。
最终,我得到了以下代码:
我认为问题出在Math.acos和Math.asin的工作方式上,有人能想到解决办法吗?
编辑:Math.asin和Math.acos返回- pi / 2到pi / 2和0到pi之间的值。这是不含糊的,所以我认为问题不在这里。似乎我在数学方面可能有些错误,但我看不出我的推理中哪里有漏洞...
编辑2:对于任何不知道欧拉旋转如何工作的人,它是这样的:
即,围绕Z旋转,然后围绕新的X轴(X')旋转,最后围绕新的Z''轴旋转。
以下是相关矩阵:
X:
![X](https://i.imgur.com/J5G1f.jpg)
![Y](https://i.imgur.com/9pDts.jpg)
![Z](https://i.imgur.com/o67aJ.jpg)
Rxyz:
![Rxyz](https://i.imgur.com/8UQM6.jpg)
Euler:
![Euler](https://i.imgur.com/jSSGL.jpg)
![Code](https://i.imgur.com/yQhmL.jpg)
因此,我可以很快排除可能的情况;当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 旋转导致 c2==1 的四种情况。
但现在问题出现了:
我上面所做的一切对我来说是有意义的,但它并没有给出正确的角度;
以下是一些输出,每对中,第一个是 theta phi psi 角度,第二个是相应的 alpha beta gamma 线。忽略舍入误差,某些角度似乎偏差大约
[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之间的值。这是不含糊的,所以我认为问题不在这里。似乎我在数学方面可能有些错误,但我看不出我的推理中哪里有漏洞...
编辑2:对于任何不知道欧拉旋转如何工作的人,它是这样的:
即,围绕Z旋转,然后围绕新的X轴(X')旋转,最后围绕新的Z''轴旋转。