我正在尝试使用.NET / C#将使用欧拉角度描述的3D旋转转换为矩阵然后返回。我的约定是:
我的试用期是:
Euler to matrix (为了简化,我删除了x,y,z翻译部分)
Matrix3D matrix = new Matrix3D() {
M11 = cosH * cosB - sinH * sinP * sinB,
M12 = - sinB * cosP,
M13 = sinH * cosB + cosH * sinP * sinB,
M21 = cosH * sinB + sinH * sinP * cosB,
M22 = cosB * cosP,
M23 = sinB * sinH - cosH * sinP * cosB,
M31 = - sinH * cosP,
M32 = - sinP,
M33 = cosH * cosP,
};
Matrix to Euler
const double RD_TO_DEG = 180 / Math.PI;
double h, p, b; // angles in degrees
// extract pitch
double sinP = -matrix.M23;
if (sinP >= 1) {
p = 90; } // pole
else if (sinP <= -1) {
p = -90; } // pole
else {
p = Math.Asin(sinP) * RD_TO_DEG; }
// extract heading and bank
if (sinP < -0.9999 || sinP > 0.9999) { // account for small angle errors
h = Math.Atan2(-matrix.M31, matrix.M11) * RD_TO_DEG;
b = 0; }
else {
h = Math.Atan2(matrix.M13, matrix.M33) * RD_TO_DEG;
b = Math.Atan2(matrix.M21, matrix.M22) * RD_TO_DEG; }
一定是错的。如果我采用3个角度,将它们转换为矩阵并将矩阵转换回角度,结果如果与初始值不同。
我浏览了几个不同公式的网站,从euclideanspace.com开始,但我现在完全迷失了,找不到合适的计算方法。我很感激一点帮助。船上有数学家吗?
答案 0 :(得分:10)
首先,应该:
sinP = -matrix.M32
编辑:完整解决方案
我的推导:
Rx(P)=| 1 0 0 |
| 0 cos P -sin P |
| 0 sin P cos P |
Ry(H)=| cos H 0 sin H |
| 0 1 0 |
| -sin H 0 cos H |
Rz(B)=| cos B -sin B 0 |
| sin B cos B 0 |
| 0 0 1 |
乘以您的订购:
R = Ry(H)*Rx(P)*Rz(B)
= | cos H*cos B+sin H*sin P*sin B cos B*sin H*sin P-sin B*cos H cos P*sin H |
| cos P*sin B cos B*cos P -sin P |
| sin B*cos H*sin P-sin H*cos B sin H*sin B+cos B*cos H*sin P cos P*cos H |
提供反向推导:
tan B = M12 / M22
sin P = -M32
tan H = M31 / M33
答案 1 :(得分:3)
你的想法是错的:“一定是错的。如果我采取3个角度,将它们转换成矩阵并将矩阵转换回角度,结果如果与初始值不同。”它会很漂亮,但不一定是真的。通常,不止一个欧拉角的三重态(固定惯例)导致空间中的相同方向。但这并不意味着在您的计算中没有错误。 来自维基百科: “例如,假设我们使用上面的zyz约定;那么我们有以下等价对: (90°,45°,-105°)≡(-270°,-315°,255°)360°的倍数 (72°,0°,0°)≡(40°,0°,32°)单一对齐 (45°,60°,-30°)≡(-135°,-60°,150°)双稳态翻转 “
答案 2 :(得分:3)
这些功能有很多组合,因为答案会根据您的惯例而变化。我通常使用DirectX和与Unity相同的约定。加上我的背景是flightims,space和maps,所以yaw然后是pitch,然后roll匹配lat / lon风格。
不清楚约定或具有不匹配的组合/分解功能可能导致非常奇怪的错误。另外值得注意的是,多组欧拉角可以产生相同的方向。
约定(如上所述):
要转换为OpenGL版本,请查看this。
我已经采取Mike Tunnicliffe的答案并将其转换为C ++代码并将其添加到我的库中。我希望其他人可以节省一些时间。
值得注意的是,compose函数清除第4列和转换组件以进行标识,而分解函数假定3x3旋转元素包含纯旋转(即没有缩放等)。
首先是从Eulers生成矩阵的代码:
//====================================================================================================
// MatrixFromYawPitchRoll
//
// Create matrix based on provided yaw (heading), pitch and roll (bank).
//
// Assumptions:
// Euler: X = Pitch, Y = Yaw, Z = Roll
// Applied: Yaw then pitch then roll
// Axes: X = Right, Y = Up, Z = Forward
// DirectX: Matrices are row major (http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html)
//
// Code is based on Mike Tunnicliffe's answer to this question:
// https://stackoverflow.com/questions/1996957/conversion-euler-to-matrix-and-matrix-to-euler
inline void MatrixFromYawPitchRoll(
const DirectX::SimpleMath::Vector3& euler,
DirectX::SimpleMath::Matrix& mat)
{
float cosY = cosf(euler.y); // Yaw
float sinY = sinf(euler.y);
float cosP = cosf(euler.x); // Pitch
float sinP = sinf(euler.x);
float cosR = cosf(euler.z); // Roll
float sinR = sinf(euler.z);
mat = DirectX::SimpleMath::Matrix::Identity;
mat._11 = cosY * cosR + sinY * sinP * sinR;
mat._21 = cosR * sinY * sinP - sinR * cosY;
mat._31 = cosP * sinY;
mat._12 = cosP * sinR;
mat._22 = cosR * cosP;
mat._32 = -sinP;
mat._13 = sinR * cosY * sinP - sinY * cosR;
mat._23 = sinY * sinR + cosR * cosY * sinP;
mat._33 = cosP * cosY;
}
然后编码从矩阵中找回欧拉角:
//====================================================================================================
// MatrixDecomposeYawPitchRoll
//
// Extract the rotation contained in the provided matrix as yaw (heading), pitch and roll (bank) in
// radiuans.
//
// Assumptions:
// Euler: X = Pitch, Y = Yaw, Z = Roll
// Applied: Yaw then pitch then roll
// Axes: X = Right, Y = Up, Z = Forward
// DirectX: Matrices are row major (http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html)
//
// Code is based on Mike Tunnicliffe's answer to this question:
// https://stackoverflow.com/questions/1996957/conversion-euler-to-matrix-and-matrix-to-euler
inline void MatrixDecomposeYawPitchRoll(
const DirectX::SimpleMath::Matrix& mat,
DirectX::SimpleMath::Vector3& euler)
{
euler.x = asinf(-mat._32); // Pitch
if (cosf(euler.x) > 0.0001) // Not at poles
{
euler.y = atan2f(mat._31, mat._33); // Yaw
euler.z = atan2f(mat._12, mat._22); // Roll
}
else
{
euler.y = 0.0f; // Yaw
euler.z = atan2f(-mat._21, mat._11); // Roll
}
}