使用点云拟合圆柱,并使用ceres对圆柱方程参数进行非线性优化

本文主要针对给定的圆柱点云拟合圆柱方程,在得到方程的参数之后,对圆柱方程参数使用ceres进行非线性优化,最终得出优化后的圆柱方程。
首先我们给出圆柱面方程的一般形式
在这里插入图片描述
因此我们可以根据圆柱面方程构建一个如下所示的最小化问题。

本文使用AT960激光跟踪仪和T-SCAN对机器人加工刀具主轴进行点云扫描,获得点云文件。
下面针对该解决方案的关键代码进行讲解。

//构建残差块
struct CostFun{
  //残差块构造函数 
    CostFun(double x,double y,double z):x_(x),y_(y),z_(z){}
//运算符重载函数,params为优化参数数组,residual为误差
    template<typename T>
    bool operator()(const T* const parm,T* residual) const{
        residual[0]=pow((x_-parm[0]),2)+pow((y_-parm[1]),2)+pow((z_-parm[2]),2)-pow(parm[3]*(x_-parm[0])+parm[4]*(y_-parm[1])+parm[5]*(z_-parm[2]),2)-pow(parm[6],2);
        return true;
    }
//圆柱点云数据
    const double x_,y_,z_;
};

构建好残差块之后,首先使用pcl库对点云进行分割,然后提取圆柱点云,获取圆柱方程初始参数。

//读取点云
  reader.read ("/home/dongfu/project/Cylinder_extract/210127_101328_height.pcd", *cloud);
  std::cerr << "PointCloud has: " << cloud->size () << " data points." << std::endl;
//滤波
pcl::PassThrough<PointT> pass;
pass.setInputCloud (cloud);
  pass.setFilterFieldName ("z");
  pass.setFilterLimits (-1000,1000);
  pass.filter (*cloud_filtered);
  std::cerr << "PointCloud after filtering has: " << cloud_filtered->size () << " data points." << std::endl;
//估计法线
  ne.setSearchMethod (tree);
  ne.setInputCloud (cloud_filtered);
  ne.setKSearch (50);
  ne.compute (*cloud_normals);
  std::cout<<"normals size="<<cloud_normals->size()<<endl;
 pcl::NormalEstimation<PointT, pcl::Normal> ne;
//分割点云,提取点云
 pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; 
 pcl::ExtractIndices<PointT> extract;
seg.setOptimizeCoefficients (true);
  seg.setModelType (pcl::SACMODEL_CYLINDER);
  seg.setMethodType (pcl::SAC_RANSAC);
  seg.setNormalDistanceWeight (0.1);
  seg.setMaxIterations (10000);
  seg.setDistanceThreshold (5);
  seg.setRadiusLimits (0, 600);
  seg.setInputCloud (cloud_filtered);
  seg.setInputNormals (cloud_normals);
 
  // Obtain the cylinder inliers and coefficients
  seg.segment (*inliers_cylinder, *coefficients_cylinder);
  std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;

  // Write the cylinder inliers to disk
  extract.setInputCloud (cloud_filtered);
  extract.setIndices (inliers_cylinder);
  extract.setNegative (false);
  pcl::PointCloud<PointT>::Ptr cloud_cylinder (new pcl::PointCloud<PointT> ());
  extract.filter (*cloud_cylinder);
  if (cloud_cylinder->points.empty ()) 
    std::cerr << "Can't find the cylindrical component." << std::endl;
  else
  {
	  std::cerr << "PointCloud representing the cylindrical component: " << cloud_cylinder->size () << " data points." << std::endl;
	  writer.write ("/home/dongfu/projects/Cylinder_extract/cylinder.pcd", *cloud_cylinder, false);
  }
//设置优化初值
  double x0=coefficients_cylinder->values[0];
  double y0=coefficients_cylinder->values[1];
  double z0=coefficients_cylinder->values[2];
  double a=coefficients_cylinder->values[3];
  double b=coefficients_cylinder->values[4];
  double c=coefficients_cylinder->values[5];
  double r0=coefficients_cylinder->values[6];
//开始创建优化问题进行优化
//开始优化
  //设置参数块
  double param[7]={x0,y0,z0,a,b,c,r0};
  //定义优化问题
  ceres::Problem problem;
  for(int i=0;i<cloud_cylinder->size();i++)
  {
     problem.AddResidualBlock(
     //添加残差块,主要参数包括误差函数、输入维度、输出维度
         new ceres::AutoDiffCostFunction<CostFun,7,1>(new CostFun(cloud_cylinder->points[i].x,cloud_cylinder->points[i].y,cloud_cylinder->points[i].z)),
        nullptr,
        param
     );
  }
  //配置求解器
  ceres::Solver::Options options;
  options.linear_solver_type=ceres::DENSE_NORMAL_CHOLESKY;//增量方程求解
  options.minimizer_progress_to_stdout=true;//输出到cout
  ceres::Solver::Summary summary;
  ceres::Solve(options,&problem,&summary);
  cout<<summary.BriefReport()<<endl;
  cout<<"优化之后的结果为:"<<endl;
  for(auto x:param)
  {
      cout<<x<<" ";
  }
  cout<<std::endl;
  cout<<"优化后半径为"<<param[6]<<endl;
//误差评价
 //计算优化后的参数误差
   double r_=param[6];
   double sum_error=0;
   vector<double> error;
   double ave_error=0;
  for(int i=0;i<cloud_cylinder->size();i++)
  {
     double temp1=pow(cloud_cylinder->points[i].x-param[0],2)+pow(cloud_cylinder->points[i].y-param[1],2)+pow(cloud_cylinder->points[i].z-param[2],2);
     double temp2=pow(param[3]*(cloud_cylinder->points[i].x-param[0])+param[4]*(cloud_cylinder->points[i].y-param[1])+param[5]*(cloud_cylinder->points[i].z-param[2]),2);
     double r_predict_2=temp1-temp2;
     double r_predict=sqrt(r_predict_2);
     double temp_error=r_-r_predict;
     double temp_error_2=pow(temp_error,2);
     sum_error+=temp_error_2;
     error.push_back(temp_error);
 }
 //标准差
 ave_error=sqrt(sum_error/cloud_cylinder->size());
 cout<<"平均误差为"<<ave_error<<endl;
 //cout<<"总误差"<<sum_error<<endl;

通过运行该优化程序,最终得出的优化结果为:
优化后预测半径和优化半径之间的标准差为0.009mm
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
本文源码见github:https://github.com/dongfufu/ceres_learn

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Cartographer是一种用于构建室内或室外三维地图的算法。它将传感器数据(如激光雷达、惯性测量单元和相机)与机器人的运动轨迹相结合,通过优化来估计环境的结构和机器人在其中的位置。 Cartographer的算法原理包括以下步骤: 1. 传感器数据处理:首先,激光雷达数据会通过滤波和去噪等预处理步骤进行处理,以减少数据噪声和异常点的影响。 2. 位姿估计:通过使用惯性测量单元(IMU)和里程计数据,估计机器人在每个时间步的位姿(位置和姿态)。这些位姿估计可以通过滤波器(如扩展卡尔曼滤波器)进行融合和优化。 3. 建图:使用激光雷达数据和位姿估计,将地图表示为一个稀疏或稠密的三维点云。这可以通过扫描匹配算法(例如,最近邻算法)来实现,将每个激光束与地图中的点进行匹配。 4. 后端优化:通过最小化误差函数来对地图和机器人位姿进行优化。这可以使用非线性优化方法,如ceres解算器进行求解。ceres是一个强大的C++库,用于求解非线性最小二乘问题。 Cartographer的后端优化流程大致如下: 1. 定义误差函数:将地图点和机器人位姿与实际测量数据进行比较,得到一个误差函数。 2. 优化问题建模:将误差函数转化为非线性最小二乘问题的形式,其中需要定义待优化的变量和约束条件。 3. 选择求解方法:使用ceres等非线性优化库选择适当的求解方法,如Levenberg-Marquardt算法。 4. 迭代求解:通过迭代优化过程,不断更新变量的估计值,直到达到收敛条件或达到最大迭代次数。 5. 结果评估:评估优化后的地图和机器人位姿与实际情况的吻合程度,可以使用重投影误差等指标进行评估。 通过这样的后端优化流程,Cartographer可以提高地图的准确性和机器人位姿的估计精度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_43257155

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值