添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在实际项目中,有时我们会对一个物体的姿态矩阵进行多次测量,或者从多个不同的基准点同时进行测量。这些测量最后得到的结果可能比较接近,但并不相同,我们希望结合这些结果得到一个理论上更为准确的“平均矩阵”。

从数学角度讲,哪种平均方法都是可以的,我们这里只是根据实际工程用途计算出更符合直觉的“平均矩阵”。

如果是对一个物体的位置进行测量,得到很多结果,我们可以直接求这些点的几何平均值,或者最小二乘法点。

那如果结果是一堆矩阵,大家都会疑惑:直接矩阵相加,求平均值?这个问题在网上有了答案,一般对旋转矩阵来说,求四元数的平均值会比较好。 获得多个旋转矩阵,如何获得平均旋转? - fly qq的回答 - 知乎

(a) 欧拉角求平均;(b)四元数求平均;(c)切空间内平均;(d)数值求解结果

那么还要解决的是缩放问题,由于旋转之后各个轴的缩放倍数可能会改变,所以我们假设缩放是统一的,也就是 scaleX = scaleY = scaleZ 。所以需要计算出统一的缩放比例就可以了。

那么如何衡量矩阵的缩放大小呢?在几何中可以理解为 x轴,y 轴,z 轴组成的立方体(或平行四边体)的体积,而在代数中则可以用矩阵的行列式的值来表示。

对于立方体来说,体积还需要开三次方根,才是边长。开根号运算应尽量避免。所以可以提供一个不带缩放的函数方法。

对于 3x3 矩阵直接求行列式就行,而对于 4x4 矩阵则需要求其左上角 3x3 矩阵的行列式。但是!!我们使用的 4x4 矩阵非常特殊,每一列最后一个向量是 0,这些 0 会与最后一列的前三项相乘,抵消这些平移带来的影响。从几何角度讲:平移不会改变 x轴,y 轴,z 轴组成的立方体(或平行四边体)的体积。

static func averageMatrix(_ matrices:[simd_float4x4], needScale:Bool = false) -> simd_float4x4 {
    var averagePosition = simd_float4.zero
    var averageScale:Float = 0
    var averageRotation = simd_quatf()
    for matrix in matrices {
        averagePosition += matrix.columns.3
        if needScale {
            let d = matrix.determinant
            averageScale += cbrtf(d)
        averageRotation += simd_quatf(matrix)
    let count = Float(matrices.count)
    averagePosition /= count
    averageRotation = averageRotation.normalized
    if needScale {
        averageScale /= count
        let s = simd_float4x4(diagonal: simd_float4(averageScale, averageScale, averageScale, 1))
        let r = simd_float4x4(averageRotation)
        let t = simd_float4x4(translation: averagePosition)
        //复合变换约定顺序:缩放 —> 旋转 —> 平移
        return t * r * s
    } else {
        let r = simd_float4x4(averageRotation)
        let t = simd_float4x4(translation: averagePosition)
        //复合变换约定顺序:缩放 —> 旋转 —> 平移
        return t * r
复制代码
  • 私信