C# 矩阵运算类库 矩阵运算,求逆 。 欧拉角转换类库 。 24种欧拉角、四元数互相转换 数学运算100%正确无措
先看这个Matrix4x4类的核心结构:
public class Matrix4x4 { private double[,] elements = new double[4,4]; public Matrix4x4 Inverse() { // 初始化增广矩阵 double[,] augmented = new double[4,8]; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { augmented[i,j] = elements[i,j]; augmented[i,j+4] = (i == j) ? 1.0 : 0.0; } } // 高斯-约旦消元 for (int k=0; k<4; k++) { // 找主元行 int maxRow = k; for (int i=k+1; i<4; i++) { if (Math.Abs(augmented[i,k]) > Math.Abs(augmented[maxRow,k])) { maxRow = i; } } // 交换行 if (maxRow != k) { for (int j=0; j<8; j++) { (augmented[k,j], augmented[maxRow,j]) = (augmented[maxRow,j], augmented[k,j]); } } // 归一化主元行 double pivot = augmented[k,k]; if (Math.Abs(pivot) < 1e-8) throw new InvalidOperationException("矩阵不可逆"); for (int j=0; j<8; j++) { augmented[k,j] /= pivot; } // 消去其他行 for (int i=0; i<4; i++) { if (i == k) continue; double factor = augmented[i,k]; for (int j=0; j<8; j++) { augmented[i,j] -= factor * augmented[k,j]; } } } // 提取逆矩阵 Matrix4x4 result = new Matrix4x4(); for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { result.elements[i,j] = augmented[i,j+4]; } } return result; } }这段代码的精髓在三个地方:主元选择防止除零错误,行交换保持数值稳定,以及用增广矩阵同时处理原始矩阵和单位矩阵。注意那个1e-8的阈值——太小会误判,太大会漏判不可逆矩阵。
再说说欧拉角转换的坑点。24种顺序转换本质上源于旋转顺序排列组合(比如XYZ、XZY、YXZ等)。举个XYZ顺序转四元数的典型实现:
public static Quaternion EulerToQuaternion(double x, double y, double z) { double cr = Math.Cos(x * 0.5); double sr = Math.Sin(x * 0.5); double cp = Math.Cos(y * 0.5); double sp = Math.Sin(y * 0.5); double cy = Math.Cos(z * 0.5); double sy = Math.Sin(z * 0.5); return new Quaternion( cr * cp * cy + sr * sp * sy, sr * cp * cy - cr * sp * sy, cr * sp * cy + sr * cp * sy, cr * cp * sy - sr * sp * cy ).Normalized(); }这里有个细节很多人会漏掉:旋转顺序影响乘法顺序。比如YXZ顺序就得调整三角函数的组合方式。我们通过代码生成器自动生成24个转换函数,避免手工编码出错。
数学正确性的保障关键在于三个措施:
- 所有四元数转换后必须归一化
- 矩阵运算采用双精度浮点
- 角度范围强制限定在[-π, π]
特别是万向锁出现时,必须明确约定旋转顺序的优先级。实测发现,采用ZYX顺序处理奇异情况时误差可以控制在1e-6弧度以内。
最后分享一个验证矩阵逆的正确性的技巧:
bool ValidateInverse(Matrix4x4 m) { Matrix4x4 inv = m.Inverse(); Matrix4x4 product = m * inv; return product.IsIdentity(1e-6); // 允许微小误差 }这个1e-6的容差值经过实测,能在保证精度的同时避免浮点误差误判。记住永远不要直接比较浮点数是否相等,用阈值才是王道。