三维空间点拟合圆原理及C++实现

已知三维空间离散点坐标(xi, yi, zi),构建一个空间圆使得空间点尽可能靠近拟合的空间圆。

首先,所有离散点尽可能在一个平面上,平面方程可表示为

《三维空间点拟合圆原理及C++实现》                                                                                                             (1)

写成矩阵形式为,

《三维空间点拟合圆原理及C++实现》,式中

《三维空间点拟合圆原理及C++实现》《三维空间点拟合圆原理及C++实现》《三维空间点拟合圆原理及C++实现》                                              (2)

这是一个超定方程求解,根据最小二乘法,可以求出《三维空间点拟合圆原理及C++实现》,即平面的法向向量。

假设所有离散点都在圆上,那么任意两点的连线的中垂线必过圆心。设圆心C(x0,y0,z0),取两个点P1(x1,y1,z1)与P2(x2,y2,z2),则P1和P2连线的向量vector1表示为(x2-x1,y2-y1,z2-z1),P1和P2连线的中点坐标P12为《三维空间点拟合圆原理及C++实现》

圆心C与P12连线向量vector2为《三维空间点拟合圆原理及C++实现》。要想P1与P2在圆上,则满足vector1*vecotor2=0,即

《三维空间点拟合圆原理及C++实现》

整理一下,有

《三维空间点拟合圆原理及C++实现》,

式中  《三维空间点拟合圆原理及C++实现》,《三维空间点拟合圆原理及C++实现》

所有点都在圆上,则有

《三维空间点拟合圆原理及C++实现》                                         (3)

写成矩阵形式   《三维空间点拟合圆原理及C++实现》

式中《三维空间点拟合圆原理及C++实现》《三维空间点拟合圆原理及C++实现》《三维空间点拟合圆原理及C++实现》

上述方程亦为超定方程,变换成适定形式为

《三维空间点拟合圆原理及C++实现》                                                                                                    (4)

由于圆心C必在前述控制的平面内,因此满足《三维空间点拟合圆原理及C++实现》, 即 

《三维空间点拟合圆原理及C++实现》                                                                                                                  (5)

A为平面的法向量,通过 《三维空间点拟合圆原理及C++实现》  (6)已求出,因而可将式(4)和式(5)合并,写成一个扩展式进行求解

《三维空间点拟合圆原理及C++实现》,式中

《三维空间点拟合圆原理及C++实现》《三维空间点拟合圆原理及C++实现》

则求解圆心坐标《三维空间点拟合圆原理及C++实现》

圆的半径可由所有点到圆心的距离的平均值确定:

《三维空间点拟合圆原理及C++实现》

C++代码实现如下:

vector<float> FitCircle(std::vector<vector<float>> pts)
{
    vector<float> circle;

    int num = pts.size();
    int dim = 3;

    Eigen::MatrixXd M(num, dim);

    for (int i = 0; i < num; i++)
    {
        for (int j = 0; j < dim; j++)
        {
            M(i, j) = pts[i][j];
        }
    }

    Eigen::MatrixXd L1 = Eigen::MatrixXd::Ones(num, 1);

    //式(6)
    Eigen::MatrixXd A = (M.transpose()*M).inverse()*M.transpose()*L1;

    Eigen::MatrixXd B = Eigen::MatrixXd::Zero(num – 1, 3);

    for (int i = 0; i < num – 1; i++)
    {
        B.row(i) = M.row(i + 1) – M.row(i);
    }

    Eigen::MatrixXd L2 = Eigen::MatrixXd::Zero(num – 1, 1);
    for (int i = 0; i < num – 1; i++)
    {
        L2(i) = (M(i + 1, 0)*M(i + 1, 0) + M(i + 1, 1)*M(i + 1, 1) + M(i + 1, 2)*M(i + 1, 2)
            – (M(i, 0)*M(i, 0) + M(i, 1)*M(i, 1) + M(i, 2)*M(i, 2))) / 2.0;
    }

    Eigen::MatrixXd D;
    //!!!矩阵合并前需要将合并后的矩阵 resize
    D.resize(4, 3);
    D << B.transpose()*B,
        A.transpose();

    Eigen::MatrixXd L3;
    Eigen::MatrixXd One31 = Eigen::MatrixXd::Ones(3, 1);
    L3.resize(4, 1);
    L3 << B.transpose()*L2,
        One31;
    
    //式(7)
    Eigen::MatrixXd C = (D.transpose()*D).inverse()*D.transpose() * L3;

    //式(8)
    double radius = 0;
    for (int i = 0; i < num; i++)
    {
        Eigen::MatrixXd tmp = M.row(i) – C.transpose();
        radius = radius + sqrt(tmp(0)*tmp(0) + tmp(1)*tmp(1) + tmp(2)*tmp(2));
    }
    radius = radius / num;

    circle.push_back(C(0));
    circle.push_back(C(1));
    circle.push_back(C(2));
    circle.push_back(A(0));
    circle.push_back(A(1));
    circle.push_back(A(2));
    circle.push_back(radius);
    return circle;
}

    原文作者:GL_logn
    原文地址: https://blog.csdn.net/qq_18895311/article/details/108340276
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞