首页 > 其他分享 >LIO-SAM代码解析:mapOptmization.cpp(二)

LIO-SAM代码解析:mapOptmization.cpp(二)

时间:2025-01-12 15:59:16浏览次数:3  
标签:float mathbf SAM 0.1 LIO mapOptmization y0 x0 z0

文章目录


1. cornerOptimization

void cornerOptimization() 
{
    // 更新点的位姿变换参数,使点从当前坐标系映射到全局地图坐标系
    updatePointAssociateToMap();

    // 使用多线程并行优化处理每个角点
    #pragma omp parallel for num_threads(numberOfCores)
    for (int i = 0; i < laserCloudCornerLastDSNum; i++) 
    {
        PointType pointOri, pointSel, coeff;          // 定义原始点、选中的点和优化系数
        std::vector<int> pointSearchInd;             // 存储最近邻点的索引
        std::vector<float> pointSearchSqDis;         // 存储最近邻点的距离平方

        pointOri = laserCloudCornerLastDS->points[i]; // 获取当前点
        pointAssociateToMap(&pointOri, &pointSel);   // 将当前点映射到全局坐标系
        kdtreeCornerFromMap->nearestKSearch(pointSel, 5, pointSearchInd, pointSearchSqDis); // 查找最近的5个邻点

        // 初始化协方差矩阵相关变量
        cv::Mat matA1(3, 3, CV_32F, cv::Scalar::all(0)); // 协方差矩阵
        cv::Mat matD1(1, 3, CV_32F, cv::Scalar::all(0)); // 特征值矩阵
        cv::Mat matV1(3, 3, CV_32F, cv::Scalar::all(0)); // 特征向量矩阵
                    
        // 如果第五个最近邻点的距离小于1.0,则认为该点具有一定的局部平面结构
        if (pointSearchSqDis[4] < 1.0) {
            float cx = 0, cy = 0, cz = 0; // 初始化质心坐标
            for (int j = 0; j < 5; j++) {
                // 计算5个最近邻点的质心
                cx += laserCloudCornerFromMapDS->points[pointSearchInd[j]].x;
                cy += laserCloudCornerFromMapDS->points[pointSearchInd[j]].y;
                cz += laserCloudCornerFromMapDS->points[pointSearchInd[j]].z;
            }
            cx /= 5; cy /= 5; cz /= 5; // 计算质心坐标

            // 初始化协方差矩阵元素
            float a11 = 0, a12 = 0, a13 = 0, a22 = 0, a23 = 0, a33 = 0;
            for (int j = 0; j < 5; j++) {
                // 计算每个邻点相对于质心的偏移量
                float ax = laserCloudCornerFromMapDS->points[pointSearchInd[j]].x - cx;
                float ay = laserCloudCornerFromMapDS->points[pointSearchInd[j]].y - cy;
                float az = laserCloudCornerFromMapDS->points[pointSearchInd[j]].z - cz;

                // 计算协方差矩阵的各个元素
                a11 += ax * ax; a12 += ax * ay; a13 += ax * az;
                a22 += ay * ay; a23 += ay * az;
                a33 += az * az;
            }
            // 取平均值
            a11 /= 5; a12 /= 5; a13 /= 5; a22 /= 5; a23 /= 5; a33 /= 5;

            // 填充协方差矩阵
            matA1.at<float>(0, 0) = a11; matA1.at<float>(0, 1) = a12; matA1.at<float>(0, 2) = a13;
            matA1.at<float>(1, 0) = a12; matA1.at<float>(1, 1) = a22; matA1.at<float>(1, 2) = a23;
            matA1.at<float>(2, 0) = a13; matA1.at<float>(2, 1) = a23; matA1.at<float>(2, 2) = a33;

            // 对协方差矩阵进行特征值分解,提取特征值和特征向量
            cv::eigen(matA1, matD1, matV1);

            // 判断主方向特征值是否显著大于次方向特征值
            if (matD1.at<float>(0, 0) > 3 * matD1.at<float>(0, 1)) {
                // 定义点到直线的两端点坐标
                float x0 = pointSel.x;
                float y0 = pointSel.y;
                float z0 = pointSel.z;
                float x1 = cx + 0.1 * matV1.at<float>(0, 0);
                float y1 = cy + 0.1 * matV1.at<float>(0, 1);
                float z1 = cz + 0.1 * matV1.at<float>(0, 2);
                float x2 = cx - 0.1 * matV1.at<float>(0, 0);
                float y2 = cy - 0.1 * matV1.at<float>(0, 1);
                float z2 = cz - 0.1 * matV1.at<float>(0, 2);

                // 计算点到直线的垂直距离
                float a012 = sqrt(((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) * ((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) 
                                + ((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1)) * ((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1)) 
                                + ((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1)) * ((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1)));

                // 计算直线的长度
                float l12 = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2) + (z1 - z2)*(z1 - z2));

                // 计算法向量的分量
                float la = ((y1 - y2)*((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) 
                          + (z1 - z2)*((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1))) / a012 / l12;

                float lb = -((x1 - x2)*((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) 
                           - (z1 - z2)*((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1))) / a012 / l12;

                float lc = -((x1 - x2)*((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1)) 
                           + (y1 - y2)*((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1))) / a012 / l12;

                // 点到直线的距离平方
                float ld2 = a012 / l12;

                // 计算权重因子
                float s = 1 - 0.9 * fabs(ld2);

                // 设置优化系数
                coeff.x = s * la;
                coeff.y = s * lb;
                coeff.z = s * lc;
                coeff.intensity = s * ld2;

                // 如果权重因子足够大,保存该点用于优化
                if (s > 0.1) {
                    laserCloudOriCornerVec[i] = pointOri;
                    coeffSelCornerVec[i] = coeff;
                    laserCloudOriCornerFlag[i] = true;
                }
            }
        }
    }
}

1. 点集中心计算

从给定点的 5 个最近邻点中,计算点的中心位置:
c x = 1 5 ∑ j = 1 5 x j , c y = 1 5 ∑ j = 1 5 y j , c z = 1 5 ∑ j = 1 5 z j c_x = \frac{1}{5} \sum_{j=1}^{5} x_j, \quad c_y = \frac{1}{5} \sum_{j=1}^{5} y_j, \quad c_z = \frac{1}{5} \sum_{j=1}^{5} z_j cx​=51​j=1∑5​xj​,cy​=51​j=1∑5​yj​,cz​=51​j=1∑5​zj​

            float cx = 0, cy = 0, cz = 0; // 初始化质心坐标
            for (int j = 0; j < 5; j++) {
                // 计算5个最近邻点的质心
                cx += laserCloudCornerFromMapDS->points[pointSearchInd[j]].x;
                cy += laserCloudCornerFromMapDS->points[pointSearchInd[j]].y;
                cz += laserCloudCornerFromMapDS->points[pointSearchInd[j]].z;
            }
            cx /= 5; cy /= 5; cz /= 5; // 计算质心坐标

2. 协方差矩阵计算

对最近邻点的分布计算协方差矩阵 A \mathbf{A} A:
a 11 = 1 5 ∑ j = 1 5 ( x j − c x ) 2 , a 12 = 1 5 ∑ j = 1 5 ( x j − c x ) ( y j − c y ) , a 13 = 1 5 ∑ j = 1 5 ( x j − c x ) ( z j − c z ) a_{11} = \frac{1}{5} \sum_{j=1}^{5} (x_j - c_x)^2, \quad a_{12} = \frac{1}{5} \sum_{j=1}^{5} (x_j - c_x)(y_j - c_y), \quad a_{13} = \frac{1}{5} \sum_{j=1}^{5} (x_j - c_x)(z_j - c_z) a11​=51​j=1∑5​(xj​−cx​)2,a12​=51​j=1∑5​(xj​−cx​)(yj​−cy​),a13​=51​j=1∑5​(xj​−cx​)(zj​−cz​)
a 22 = 1 5 ∑ j = 1 5 ( y j − c y ) 2 , a 23 = 1 5 ∑ j = 1 5 ( y j − c y ) ( z j − c z ) , a 33 = 1 5 ∑ j = 1 5 ( z j − c z ) 2 a_{22} = \frac{1}{5} \sum_{j=1}^{5} (y_j - c_y)^2, \quad a_{23} = \frac{1}{5} \sum_{j=1}^{5} (y_j - c_y)(z_j - c_z), \quad a_{33} = \frac{1}{5} \sum_{j=1}^{5} (z_j - c_z)^2 a22​=51​j=1∑5​(yj​−cy​)2,a23​=51​j=1∑5​(yj​−cy​)(zj​−cz​),a33​=51​j=1∑5​(zj​−cz​)2

协方差矩阵表示为:
A = [ a 11 a 12 a 13 a 12 a 22 a 23 a 13 a 23 a 33 ] \mathbf{A} = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{12} & a_{22} & a_{23} \\ a_{13} & a_{23} & a_{33} \end{bmatrix} A= ​a11​a12​a13​​a12​a22​a23​​a13​a23​a33​​

            // 初始化协方差矩阵元素
            float a11 = 0, a12 = 0, a13 = 0, a22 = 0, a23 = 0, a33 = 0;
            for (int j = 0; j < 5; j++) {
                // 计算每个邻点相对于质心的偏移量
                float ax = laserCloudCornerFromMapDS->points[pointSearchInd[j]].x - cx;
                float ay = laserCloudCornerFromMapDS->points[pointSearchInd[j]].y - cy;
                float az = laserCloudCornerFromMapDS->points[pointSearchInd[j]].z - cz;

                // 计算协方差矩阵的各个元素
                a11 += ax * ax; a12 += ax * ay; a13 += ax * az;
                a22 += ay * ay; a23 += ay * az;
                a33 += az * az;
            }
            // 取平均值
            a11 /= 5; a12 /= 5; a13 /= 5; a22 /= 5; a23 /= 5; a33 /= 5;

            // 填充协方差矩阵
            matA1.at<float>(0, 0) = a11; matA1.at<float>(0, 1) = a12; matA1.at<float>(0, 2) = a13;
            matA1.at<float>(1, 0) = a12; matA1.at<float>(1, 1) = a22; matA1.at<float>(1, 2) = a23;
            matA1.at<float>(2, 0) = a13; matA1.at<float>(2, 1) = a23; matA1.at<float>(2, 2) = a33;

3. 特征值分解

对协方差矩阵 A \mathbf{A} A 进行特征值分解:
A = V D V ⊤ \mathbf{A} = \mathbf{V} \mathbf{D} \mathbf{V}^\top A=VDV⊤
其中, D \mathbf{D} D 是对角矩阵,其对角线上的值是特征值, V \mathbf{V} V 是对应的特征向量。


补充: (1) 特征值与特征向量的定义
对于一个对称矩阵 A ∈ R n × n \mathbf{A} \in \mathbb{R}^{n \times n} A∈Rn×n,特征值 λ \lambda λ 和对应特征向量 v \mathbf{v} v 满足以下关系:
A v = λ v \mathbf{A} \mathbf{v} = \lambda \mathbf{v} Av=λv
其中:

  • A \mathbf{A} A 是输入矩阵(如 matA1)。
  • λ \lambda λ 是特征值。
  • v \mathbf{v} v 是与 λ \lambda λ 对应的特征向量。

(2)特征值分解的公式
特征值分解可以表示为:
A = V Λ V ⊤ \mathbf{A} = \mathbf{V} \mathbf{\Lambda} \mathbf{V}^\top A=VΛV⊤
其中:

  • Λ \mathbf{\Lambda} Λ 是对角矩阵,对角线上元素为特征值:
    Λ = [ λ 1 0 ⋯ 0 0 λ 2 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ λ n ] \mathbf{\Lambda} = \begin{bmatrix} \lambda_1 & 0 & \cdots & 0 \\ 0 & \lambda_2 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & \lambda_n \end{bmatrix} Λ= ​λ1​0⋮0​0λ2​⋮0​⋯⋯⋱⋯​00⋮λn​​
  • V \mathbf{V} V 是正交矩阵,其列为特征向量:
    V = [ v 1 v 2 ⋯ v n ] \mathbf{V} = \begin{bmatrix} \mathbf{v}_1 & \mathbf{v}_2 & \cdots & \mathbf{v}_n \end{bmatrix} V=[v1​​v2​​⋯​vn​​]
    满足 V ⊤ V = I \mathbf{V}^\top \mathbf{V} = \mathbf{I} V⊤V=I,其中 I \mathbf{I} I 是单位矩阵。

(3) 数学推导
特征值分解的步骤:

  1. 特征值求解
    求解特征值 λ \lambda λ,需要解以下特征方程:
    det ⁡ ( A − λ I ) = 0 \det(\mathbf{A} - \lambda \mathbf{I}) = 0 det(A−λI)=0
    其中, det ⁡ ( ⋅ ) \det(\cdot) det(⋅) 表示行列式。

  2. 特征向量求解
    对于每个特征值 λ i \lambda_i λi​,通过解以下方程得到对应的特征向量 v i \mathbf{v}_i vi​:
    ( A − λ i I ) v i = 0 (\mathbf{A} - \lambda_i \mathbf{I}) \mathbf{v}_i = 0 (A−λi​I)vi​=0

(4)代码中分解结果的含义
在代码 cv::eigen(matA1, matD1, matV1) 中:

  • matA1: 输入矩阵 A \mathbf{A} A。
  • matD1: 输出的特征值矩阵 Λ \mathbf{\Lambda} Λ,表示为:
    Λ = [ λ 1 , λ 2 , λ 3 ] \mathbf{\Lambda} = [\lambda_1, \lambda_2, \lambda_3] Λ=[λ1​,λ2​,λ3​]
  • matV1: 输出的特征向量矩阵 V \mathbf{V} V,每列是一个特征向量:
    V = [ v 11 v 12 v 13 v 21 v 22 v 23 v 31 v 32 v 33 ] \mathbf{V} = \begin{bmatrix} v_{11} & v_{12} & v_{13} \\ v_{21} & v_{22} & v_{23} \\ v_{31} & v_{32} & v_{33} \end{bmatrix} V= ​v11​v21​v31​​v12​v22​v32​​v13​v23​v33​​
    其中, v 1 , v 2 , v 3 \mathbf{v}_1, \mathbf{v}_2, \mathbf{v}_3 v1​,v2​,v3​ 是 λ 1 , λ 2 , λ 3 \lambda_1, \lambda_2, \lambda_3 λ1​,λ2​,λ3​ 的特征向量。

对应代码:

            // 对协方差矩阵进行特征值分解,提取特征值和特征向量
            cv::eigen(matA1, matD1, matV1);

4. 主方向选择

主方向对应的特征值满足以下条件:
λ 1 > 3 λ 2 \lambda_1 > 3 \lambda_2 λ1​>3λ2​

原因分析:矩阵 A \mathbf{A} A 的特征值 λ 1 , λ 2 , λ 3 \lambda_1, \lambda_2, \lambda_3 λ1​,λ2​,λ3​ 表示局部点云分布在三个主方向上的方差大小。其中:

  • λ 1 \lambda_1 λ1​ 表示点云在主方向上的扩展程度。
  • λ 2 , λ 3 \lambda_2, \lambda_3 λ2​,λ3​ 表示点云在次方向上的扩展程度。

条件解释: 如果 λ 1 \lambda_1 λ1​ 显著大于 λ 2 \lambda_2 λ2​ 和 λ 3 \lambda_3 λ3​,例如满足 λ 1 > 3 λ 2 \lambda_1 > 3 \lambda_2 λ1​>3λ2​:

  • 说明点云在 v 1 \mathbf{v}_1 v1​ 主方向上延展显著,而在其他方向上分布紧凑。
  • 这种特征说明局部点云的形状更接近一条直线。在满足 λ 1 > 3 λ 2 \lambda_1 > 3 \lambda_2 λ1​>3λ2​ 的条件下,局部点云形状可以近似为一条直线:
  • 直线的方向:由主方向的特征向量 v 1 = ( v x 1 , v y 1 , v z 1 ) \mathbf{v}_1 = (v_{x1}, v_{y1}, v_{z1}) v1​=(vx1​,vy1​,vz1​) 确定。
  • 直线的位置:由局部点云的质心 ( c x , c y , c z ) (c_x, c_y, c_z) (cx​,cy​,cz​) 确定。

直线的表达形式为:
P ( t ) = ( c x , c y , c z ) + t ⋅ ( v x 1 , v y 1 , v z 1 ) \mathbf{P}(t) = (c_x, c_y, c_z) + t \cdot (v_{x1}, v_{y1}, v_{z1}) P(t)=(cx​,cy​,cz​)+t⋅(vx1​,vy1​,vz1​)
其中 t t t 是直线上点的参数,表示点沿着方向向量 v 1 \mathbf{v}_1 v1​ 偏移的程度。

端点的计算逻辑: 在实际计算中,为了定义有限长度的直线段:

  • 选择点云质心作为直线的中心点。

  • 通过在质心两侧沿 v 1 \mathbf{v}_1 v1​ 方向各偏移固定距离 0.1 0.1 0.1 来定义两个端点 P 1 P_1 P1​ 和 P 2 P_2 P2​。

  • 端点 P 1 P_1 P1​:在质心处沿 v 1 \mathbf{v}_1 v1​ 方向偏移正方向距离 0.1 0.1 0.1:
    P 1 = ( c x + 0.1 v x 1 , c y + 0.1 v y 1 , c z + 0.1 v z 1 ) P_1 = (c_x + 0.1 v_{x1}, c_y + 0.1 v_{y1}, c_z + 0.1 v_{z1}) P1​=(cx​+0.1vx1​,cy​+0.1vy1​,cz​+0.1vz1​)

  • 端点 P 2 P_2 P2​:在质心处沿 v 1 \mathbf{v}_1 v1​ 方向偏移负方向距离 0.1 0.1 0.1:
    P 2 = ( c x − 0.1 v x 1 , c y − 0.1 v y 1 , c z − 0.1 v z 1 ) P_2 = (c_x - 0.1 v_{x1}, c_y - 0.1 v_{y1}, c_z - 0.1 v_{z1}) P2​=(cx​−0.1vx1​,cy​−0.1vy1​,cz​−0.1vz1​)
    几何意义:

  • v 1 \mathbf{v}_1 v1​ 表示直线的方向向量。

  • 通过质心加减一定的偏移量( 0.1 ⋅ v 1 0.1 \cdot \mathbf{v}_1 0.1⋅v1​)即可定义直线的两个端点,从而表示局部点云拟合的直线。

  • λ 1 > 3 λ 2 \lambda_1 > 3 \lambda_2 λ1​>3λ2​ 表明点云的形状趋向于一维分布。

  • 使用主方向的特征向量 v 1 \mathbf{v}_1 v1​ 和质心 ( c x , c y , c z ) (c_x, c_y, c_z) (cx​,cy​,cz​),可以定义直线的方向和位置。


                    float x0 = pointSel.x;  //中心点
                    float y0 = pointSel.y;  //中心点
                    float z0 = pointSel.z;  //中心点
                    float x1 = cx + 0.1 * matV1.at<float>(0, 0);//端点P1
                    float y1 = cy + 0.1 * matV1.at<float>(0, 1);//端点P1
                    float z1 = cz + 0.1 * matV1.at<float>(0, 2);//端点P1
                    float x2 = cx - 0.1 * matV1.at<float>(0, 0);//端点P2
                    float y2 = cy - 0.1 * matV1.at<float>(0, 1);//端点P2
                    float z2 = cz - 0.1 * matV1.at<float>(0, 2);//端点P2

5. 距离与权重计算

这里将点线的距离转化为点到面的距离参考博客
平面由三个特定的点确定:

  • 中心点 p 0 \mathbf{p}_0 p0​:这是当前处理的角点,经过关联变换后的点,坐标为 ( x 0 , y 0 , z 0 ) (x_0, y_0, z_0) (x0​,y0​,z0​)。
  • 端点1 p 1 \mathbf{p}_1 p1​:这是基于协方差矩阵的主特征向量方向延伸一定距离得到的点,坐标为 ( x 1 , y 1 , z 1 ) (x_1, y_1, z_1) (x1​,y1​,z1​)。
  • 端点2 p 2 \mathbf{p}_2 p2​:这是与端点1 相反方向延伸得到的点,坐标为 ( x 2 , y 2 , z 2 ) (x_2, y_2, z_2) (x2​,y2​,z2​)。

这三个点共同确定了一个几何平面,该平面用于后续的优化过程。具体来说:

  • p 0 \mathbf{p}_0 p0​ 是当前处理的角点。
  • p 1 \mathbf{p}_1 p1​ 和 p 2 \mathbf{p}_2 p2​ 是沿着主方向(由协方差矩阵的主特征向量确定)的两个端点,用于定义该平面的方向和位置。

通过这三个点,可以唯一确定一个平面,该平面用于评估和优化角点在地图中的位置。代码计算了由三个点 p 0 \mathbf{p}_0 p0​, p 1 \mathbf{p}_1 p1​, p 2 \mathbf{p}_2 p2​ 构成的三角形的面积和端点之间的距离。

  1. 计算三角形的面积 a 012 a_{012} a012​:

    计算公式:

    a 012 = [ ( x 0 − x 1 ) ( y 0 − y 2 ) − ( x 0 − x 2 ) ( y 0 − y 1 ) ] 2 + [ ( x 0 − x 1 ) ( z 0 − z 2 ) − ( x 0 − x 2 ) ( z 0 − z 1 ) ] 2 + [ ( y 0 − y 1 ) ( z 0 − z 2 ) − ( y 0 − y 2 ) ( z 0 − z 1 ) ] 2 a_{012} = \sqrt{ \left[(x_0 - x_1)(y_0 - y_2) - (x_0 - x_2)(y_0 - y_1)\right]^2 + \left[(x_0 - x_1)(z_0 - z_2) - (x_0 - x_2)(z_0 - z_1)\right]^2 + \left[(y_0 - y_1)(z_0 - z_2) - (y_0 - y_2)(z_0 - z_1)\right]^2 } a012​=[(x0​−x1​)(y0​−y2​)−(x0​−x2​)(y0​−y1​)]2+[(x0​−x1​)(z0​−z2​)−(x0​−x2​)(z0​−z1​)]2+[(y0​−y1​)(z0​−z2​)−(y0​−y2​)(z0​−z1​)]2

    原因与用途:

    这个公式实际上计算的是三角形 p 0 \mathbf{p}_0 p0​, p 1 \mathbf{p}_1 p1​, p 2 \mathbf{p}_2 p2​ 的两倍面积。通过向量的叉积公式,可以得到三角形面积的两倍。这个面积用于后续计算平面参数时的归一化处理,确保平面参数的稳定性和准确性。

                    float a012 = sqrt(((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) * ((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) 
                                    + ((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1)) * ((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1)) 
                                    + ((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1)) * ((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1)));

  1. 计算两端点之间的距离 l 12 l_{12} l12​:

    计算公式:

    l 12 = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 + ( z 1 − z 2 ) 2 l_{12} = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2 + (z_1 - z_2)^2} l12​=(x1​−x2​)2+(y1​−y2​)2+(z1​−z2​)2

    原因与用途:

    这个距离表示端点1和端点2之间的欧几里得距离。它用于归一化平面参数,确保平面方程的标准化,使得优化过程中的权重计算更加合理。平面的法向量 ( a , b , c ) (a, b, c) (a,b,c) 可以通过两个向量的叉积得到,这两个向量分别是 p 1 − p 0 \mathbf{p}_1 - \mathbf{p}_0 p1​−p0​ 和 p 2 − p 0 \mathbf{p}_2 - \mathbf{p}_0 p2​−p0​。通过叉积,可以得到垂直于这两个向量的法向量。

    向量 u = p 1 − p 0 = ( x 1 − x 0 , y 1 − y 0 , z 1 − z 0 ) \mathbf{u} = \mathbf{p}_1 - \mathbf{p}_0 = (x_1 - x_0, y_1 - y_0, z_1 - z_0) u=p1​−p0​=(x1​−x0​,y1​−y0​,z1​−z0​)

    向量 v = p 2 − p 0 = ( x 2 − x 0 , y 2 − y 0 , z 2 − z 0 ) \mathbf{v} = \mathbf{p}_2 - \mathbf{p}_0 = (x_2 - x_0, y_2 - y_0, z_2 - z_0) v=p2​−p0​=(x2​−x0​,y2​−y0​,z2​−z0​)

    则法向量为:

    n = u × v = ∣ i j k x 1 − x 0 y 1 − y 0 z 1 − z 0 x 2 − x 0 y 2 − y 0 z 2 − z 0 ∣ \mathbf{n} = \mathbf{u} \times \mathbf{v} = \begin{vmatrix} \mathbf{i} & \mathbf{j} & \mathbf{k} \\ x_1 - x_0 & y_1 - y_0 & z_1 - z_0 \\ x_2 - x_0 & y_2 - y_0 & z_2 - z_0 \\ \end{vmatrix} n=u×v= ​ix1​−x0​x2​−x0​​jy1​−y0​y2​−y0​​kz1​−z0​z2​−z0​​

    展开后得到:

    n = [ ( y 1 − y 0 ) ( z 2 − z 0 ) − ( z 1 − z 0 ) ( y 2 − y 0 ) ] i − [ ( x 1 − x 0 ) ( z 2 − z 0 ) − ( z 1 − z 0 ) ( x 2 − x 0 ) ] j + [ ( x 1 − x 0 ) ( y 2 − y 0 ) − ( y 1 − y 0 ) ( x 2 − x 0 ) ] k \mathbf{n} = \left[(y_1 - y_0)(z_2 - z_0) - (z_1 - z_0)(y_2 - y_0)\right] \mathbf{i} - \left[(x_1 - x_0)(z_2 - z_0) - (z_1 - z_0)(x_2 - x_0)\right] \mathbf{j} + \left[(x_1 - x_0)(y_2 - y_0) - (y_1 - y_0)(x_2 - x_0)\right] \mathbf{k} n=[(y1​−y0​)(z2​−z0​)−(z1​−z0​)(y2​−y0​)]i−[(x1​−x0​)(z2​−z0​)−(z1​−z0​)(x2​−x0​)]j+[(x1​−x0​)(y2​−y0​)−(y1​−y0​)(x2​−x0​)]k

    由此可得:

    a = ( y 1 − y 0 ) ( z 2 − z 0 ) − ( z 1 − z 0 ) ( y 2 − y 0 ) b = − [ ( x 1 − x 0 ) ( z 2 − z 0 ) − ( z 1 − z 0 ) ( x 2 − x 0 ) ] c = ( x 1 − x 0 ) ( y 2 − y 0 ) − ( y 1 − y 0 ) ( x 2 − x 0 ) a = (y_1 - y_0)(z_2 - z_0) - (z_1 - z_0)(y_2 - y_0) \\ b = -[(x_1 - x_0)(z_2 - z_0) - (z_1 - z_0)(x_2 - x_0)] \\ c = (x_1 - x_0)(y_2 - y_0) - (y_1 - y_0)(x_2 - x_0) \\ a=(y1​−y0​)(z2​−z0​)−(z1​−z0​)(y2​−y0​)b=−[(x1​−x0​)(z2​−z0​)−(z1​−z0​)(x2​−x0​)]c=(x1​−x0​)(y2​−y0​)−(y1​−y0​)(x2​−x0​)

标准化平面参数: 为了确保平面方程的标准化,使得优化过程中不同参数的影响力一致,需要将平面参数进行归一化。这里使用了之前计算的面积 a 012 a_{012} a012​ 和距离 l 12 l_{12} l12​ 来进行归一化处理。

标准化后的平面参数为:

a ′ = a s , b ′ = b s , c ′ = c s , d ′ = d s a' = \frac{a}{s}, \quad b' = \frac{b}{s}, \quad c' = \frac{c}{s}, \quad d' = \frac{d}{s} a′=sa​,b′=sb​,c′=sc​,d′=sd​

其中, s s s 是归一化因子,用于确保法向量的单位长度或其他标准化需求。

                    float la = ((y1 - y2)*((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) 
                              + (z1 - z2)*((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1))) / a012 / l12;

                    float lb = -((x1 - x2)*((x0 - x1)*(y0 - y2) - (x0 - x2)*(y0 - y1)) 
                               - (z1 - z2)*((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1))) / a012 / l12;

                    float lc = -((x1 - x2)*((x0 - x1)*(z0 - z2) - (x0 - x2)*(z0 - z1)) 
                               + (y1 - y2)*((y0 - y1)*(z0 - z2) - (y0 - y2)*(z0 - z1))) / a012 / l12;

4 . 计算权重因子

计算权重因子 s s s,用于调整平面参数在优化过程中的影响力:

s = 1 − 0.9 ⋅ ∣ d ∣ x ori 2 + y ori 2 + z ori 2 4 s = 1 - 0.9 \cdot \frac{|d|}{\sqrt[4]{x_{\text{ori}}^2 + y_{\text{ori}}^2 + z_{\text{ori}}^2}} s=1−0.9⋅4xori2​+yori2​+zori2​ ​∣d∣​

6. 优化目标

如果 s > 0.1 s > 0.1 s>0.1,则更新点的优化目标系数:
coeff = ( s ⋅ l a , s ⋅ l b , s ⋅ l c , s ⋅ l d 2 ) \text{coeff} = (s \cdot l_a, s \cdot l_b, s \cdot l_c, s \cdot l_d^2) coeff=(s⋅la​,s⋅lb​,s⋅lc​,s⋅ld2​)

2. surfOptimization

void surfOptimization() 
{
    // 更新点的位姿变换参数,将点从当前坐标系映射到全局地图坐标系
    updatePointAssociateToMap();

    // 使用多线程并行优化每个平面点
    #pragma omp parallel for num_threads(numberOfCores)
    for (int i = 0; i < laserCloudSurfLastDSNum; i++) 
    {
        PointType pointOri, pointSel, coeff;         // 定义原始点、映射点和优化系数
        std::vector<int> pointSearchInd;            // 最近邻点的索引
        std::vector<float> pointSearchSqDis;        // 最近邻点的平方距离

        // 获取当前点并映射到全局坐标系
        pointOri = laserCloudSurfLastDS->points[i];
        pointAssociateToMap(&pointOri, &pointSel);

        // 查找当前点的 5 个最近邻点
        kdtreeSurfFromMap->nearestKSearch(pointSel, 5, pointSearchInd, pointSearchSqDis);

        // 初始化平面拟合矩阵
        Eigen::Matrix<float, 5, 3> matA0;           // 最近邻点的坐标矩阵
        Eigen::Matrix<float, 5, 1> matB0;           // 方程右侧常数项
        Eigen::Vector3f matX0;                      // 解向量,表示平面法向量

        matA0.setZero();
        matB0.fill(-1);                             // 初始化常数项为 -1
        matX0.setZero();

        // 如果第五个邻点的距离小于阈值,进行平面拟合
        if (pointSearchSqDis[4] < 1.0) 
        {
            // 构造邻点的坐标矩阵 matA0
            for (int j = 0; j < 5; j++) {
                matA0(j, 0) = laserCloudSurfFromMapDS->points[pointSearchInd[j]].x;
                matA0(j, 1) = laserCloudSurfFromMapDS->points[pointSearchInd[j]].y;
                matA0(j, 2) = laserCloudSurfFromMapDS->points[pointSearchInd[j]].z;
            }

            // 求解平面方程的系数 pa, pb, pc
            matX0 = matA0.colPivHouseholderQr().solve(matB0);

            float pa = matX0(0, 0);                 // 平面方程法向量的 x 分量
            float pb = matX0(1, 0);                 // 平面方程法向量的 y 分量
            float pc = matX0(2, 0);                 // 平面方程法向量的 z 分量
            float pd = 1;                           // 平面方程的常数项

            // 对平面方程系数进行归一化
            float ps = sqrt(pa * pa + pb * pb + pc * pc);
            pa /= ps; pb /= ps; pc /= ps; pd /= ps;

            // 验证邻点是否满足平面方程的误差阈值
            bool planeValid = true;
            for (int j = 0; j < 5; j++) {
                if (fabs(pa * laserCloudSurfFromMapDS->points[pointSearchInd[j]].x +
                         pb * laserCloudSurfFromMapDS->points[pointSearchInd[j]].y +
                         pc * laserCloudSurfFromMapDS->points[pointSearchInd[j]].z + pd) > 0.2) {
                    planeValid = false;
                    break;
                }
            }

            // 如果平面有效,计算当前点到平面的距离
            if (planeValid) {
                float pd2 = pa * pointSel.x + pb * pointSel.y + pc * pointSel.z + pd;

                // 计算点的权重因子
                float s = 1 - 0.9 * fabs(pd2) / sqrt(sqrt(pointOri.x * pointOri.x
                        + pointOri.y * pointOri.y + pointOri.z * pointOri.z));

                // 计算优化系数
                coeff.x = s * pa;
                coeff.y = s * pb;
                coeff.z = s * pc;
                coeff.intensity = s * pd2;

                // 如果权重因子大于阈值,记录该点的优化信息
                if (s > 0.1) {
                    laserCloudOriSurfVec[i] = pointOri;
                    coeffSelSurfVec[i] = coeff;
                    laserCloudOriSurfFlag[i] = true;
                }
            }
        }
    }
}

1. 平面方程拟合

通过邻点构造线性方程组:
[ x 1 y 1 z 1 x 2 y 2 z 2 x 3 y 3 z 3 x 4 y 4 z 4 x 5 y 5 z 5 ] [ a b c ] = [ − 1 − 1 − 1 − 1 − 1 ] \begin{bmatrix} x_1 & y_1 & z_1 \\ x_2 & y_2 & z_2 \\ x_3 & y_3 & z_3 \\ x_4 & y_4 & z_4 \\ x_5 & y_5 & z_5 \end{bmatrix} \begin{bmatrix} a \\ b \\ c \end{bmatrix} =\begin{bmatrix} -1 \\ -1 \\ -1 \\ -1 \\ -1 \end{bmatrix} ​x1​x2​x3​x4​x5​​y1​y2​y3​y4​y5​​z1​z2​z3​z4​z5​​ ​abc​ ​= ​−1−1−1−1−1​

记为 A X = B \mathbf{A} \mathbf{X} = \mathbf{B} AX=B,使用最小二乘法求解:
X = ( A ⊤ A ) − 1 A ⊤ B \mathbf{X} = (\mathbf{A}^\top \mathbf{A})^{-1} \mathbf{A}^\top \mathbf{B} X=(A⊤A)−1A⊤B


2. 平面方程归一化

求得的平面法向量 X = [ a , b , c ] ⊤ \mathbf{X} = [a, b, c]^\top X=[a,b,c]⊤,需进行归一化:
p a = a a 2 + b 2 + c 2 , p b = b a 2 + b 2 + c 2 , p c = c a 2 + b 2 + c 2 , p d = 1 a 2 + b 2 + c 2 p_a = \frac{a}{\sqrt{a^2 + b^2 + c^2}}, \quad p_b = \frac{b}{\sqrt{a^2 + b^2 + c^2}}, \quad p_c = \frac{c}{\sqrt{a^2 + b^2 + c^2}}, \quad p_d = \frac{1}{\sqrt{a^2 + b^2 + c^2}} pa​=a2+b2+c2 ​a​,pb​=a2+b2+c2 ​b​,pc​=a2+b2+c2 ​c​,pd​=a2+b2+c2 ​1​


3. 点到平面的距离

点 P ( x , y , z ) P(x, y, z) P(x,y,z) 到平面 p a x + p b y + p c z + p d = 0 p_a x + p_b y + p_c z + p_d = 0 pa​x+pb​y+pc​z+pd​=0 的距离:
d = p a x + p b y + p c z + p d d = p_a x + p_b y + p_c z + p_d d=pa​x+pb​y+pc​z+pd​


4. 权重因子计算

定义权重因子 s s s,根据点到平面的距离和点的深度调整权重:
s = 1 − 0.9 ⋅ ∣ d ∣ x 2 + y 2 + z 2 s = 1 - 0.9 \cdot \frac{|d|}{\sqrt{\sqrt{x^2 + y^2 + z^2}}} s=1−0.9⋅x2+y2+z2 ​∣d∣​


5. 优化系数

优化系数 ( coeff.x , coeff.y , coeff.z , coeff.intensity ) (\text{coeff.x}, \text{coeff.y}, \text{coeff.z}, \text{coeff.intensity}) (coeff.x,coeff.y,coeff.z,coeff.intensity):
coeff.x = s ⋅ p a , coeff.y = s ⋅ p b , coeff.z = s ⋅ p c , coeff.intensity = s ⋅ d \text{coeff.x} = s \cdot p_a, \quad \text{coeff.y} = s \cdot p_b, \quad \text{coeff.z} = s \cdot p_c, \quad \text{coeff.intensity} = s \cdot d coeff.x=s⋅pa​,coeff.y=s⋅pb​,coeff.z=s⋅pc​,coeff.intensity=s⋅d

标签:float,mathbf,SAM,0.1,LIO,mapOptmization,y0,x0,z0
From: https://blog.csdn.net/weixin_41331879/article/details/145081206

相关文章

  • Ubuntu20.04下修改samba用户密码
    引言Samba是一个用于Linux和Windows系统之间文件和打印共享的强大工具。在Ubuntu20.04上,管理Samba用户和密码是系统管理员的常见任务之一。本文将详细介绍在Ubuntu20.04上如何修改Samba用户密码。安装和配置Samba在修改Samba用户密码之前,确保已经安装并配置......
  • LIO-SAM代码解析:mapOptmization.cpp(一)
    文章目录主流程1.`loopInfoHandler`1.1`updateInitialGuess`1.2`extractSurroundingKeyFrames`1.3`downsampleCurrentScan`1.4`scan2MapOptimization`1.5`saveKeyFramesAndFactor`1.6`correctPoses`1.7`publishOdometry`1.8`publishFrames`主流程1.loo......
  • YOLOv11全网最新创新点改进系列:“将Lion自动优化与YOLOv11完美结合,智能优化算法驱动,赋
    YOLOv11全网最新创新点改进系列:“将Lion自动优化与YOLOv11完美结合,智能优化算法驱动,赋能精准检测与高效推理,让您的应用在复杂场景下表现更卓越!”视频讲解戳这里,文档可以不看,视频内容一定要看,干货满满!祝大家少走弯路!!!所有改进代码均经过实验测试跑通!截止发稿时YOLOv11已改进......
  • 【优选算法】Bit-Samurai:位运算的算法之道
    文章目录1.常见位运算总结1.1基础位运算符号1.2给一个数n,确定它的二进制表示中的第x位是0还是11.3将一个数n的二进制表示的第x位修改成11.4将一个数n的二进制表示的第x位修改成01.5位图的思想1.6提取一个n二进制表示中最右侧的11.7干掉一个数......
  • 第十六章、文件服务器之二: SAMBA 服务器
    16.1什么是SAMBA在这个章节中,我们要教大家跳的是热情有劲的巴西SAMBA舞蹈...喔不~搞错了~是要向大家介绍 SAMBA 这个好用的服务器啦!咦!怪了!怎么服务器的名称会使用SAMBA呢?还真是怪怪的呢!那么这个SAMBA服务器的功能是什么呢?另外,它最早是经由什么样的想法而开发出来......
  • MasterSAM downloadService存在任意文件下载漏洞(CVE-2024-55457)
    免责声明:本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在使用本......
  • 基于GA遗传优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
    1.算法运行效果图预览(完整程序运行后无水印) 2.算法运行软件版本matlab2022a 3.部分核心程序(完整版代码包含详细中文注释和操作步骤视频)figureplot(Error2,'linewidth',2);gridonxlabel('迭代次数');ylabel('遗传算法优化过程');legend('Averagefitness......
  • samba服务 使用
    SAMBA服务访问安装http://www.samba.org/#模拟window共享[root@vm~]#yum-yinstallsamba[root@vm~]#systemctlstartsmb[root@vm~]#ss-antlp|grepsmbLISTEN050*:445*:*users:(("smbd",pid=20155,fd=36))LISTEN0......
  • ubuntu 使用samba与windows共享文件[注意权限配置]
    在Ubuntu上使用Samba服务与Windows系统共享文件,需要正确配置Samba服务以及相应的权限。以下是详细的步骤:安装Samba首先,确保你的Ubuntu系统上安装了Samba服务。sudoaptupdatesudoaptinstallsamba配置Samba安装完成后,需要配置Samba共享。编辑Samba的配置文件。su......
  • WWW文献阅读分享:《Reinforced Negative Sampling over Knowledge Graph for Recommend
    ......