如何用ceres进行两帧之间的BA优化

本文介绍使用Ceres解决BA优化问题的过程,通过自定义残差结构实现3D点的重投影误差最小化,并对比了与g2o的不同之处。

学习高博的书已有很长一段时间了,一直看理论,看代码,而没有自己亲自上手,最近在做BA优化,大部分SLAM是用g2o进行的,而对于ceres用的很少,由于博主根本看不懂g2o的代码风格,个人觉得很无语(其实是博主zz),那咋办?,于是就想干脆用ceres实现BA优化吧。

而关于ceres,其实主要还是残差的定义了,然后求解过程都是大同小异,直接上我写的代码

struct cost_function_define
{
  cost_function_define(Point3d p1,Point3d p2):_p1(p1),_p2(p2){}
  template<typename T>
  bool operator()(const T* const cere_r,const T* const cere_t,T* residual)const
  {
    T p_1[3];
    T p_2[3];
    p_1[0]=T(_p1.x);
    p_1[1]=T(_p1.y);
    p_1[2]=T(_p1.z);
    ceres::AngleAxisRotatePoint(cere_r,p_1,p_2);
    p_2[0]=p_2[0]+cere_t[0];
    p_2[1]=p_2[1]+cere_t[1];
    p_2[2]=p_2[2]+cere_t[2];
    const T x=p_2[0]/p_2[2];
    const T y=p_2[1]/p_2[2];
    const T u=x*520.9+325.1;
    const T v=y*521.0+249.7;
    T p_3[3];
    p_3[0]=T(_p2.x);
    p_3[1]=T(_p2.y);
    p_3[2]=T(_p2.z);
    const T x1=p_3[0]/p_3[2];
    const T y1=p_3[1]/p_3[2];
    const T u1=x1*520.9+325.1;
    const T v1=y1*521.0+249.7;
    residual[0]=u-u1;
    residual[1]=v-v1;
    return true;
  }
   Point3d _p1,_p2;
};

主要是用模板类编写,博主一开始也很不习惯,但是没办法,用多了也就这样了,注意的是需要重载操作符(),然后就可以定义残差了,相信学过BA优化的同学应该对此不陌生,就是简单的重投影误差。需要说明的是,这里面用到了ceres提供的空间点的旋转向量,这就很愉快了,因为旋转向量和旋转矩阵其实就是我们所说的李代数和李群的关系,这就在迭代优化时,使得旋转向量在+是封闭的,可以进行求导。

然后就是配置求解器

ceres::Problem problem;
  for(int i=0;i<image1_3d.size();i++)
  {
    ceres::CostFunction* costfunction=new ceres::AutoDiffCostFunction<cost_function_define,2,3,3>(new cost_function_define(image2_3d[i],image1_3d[i]));
    problem.AddResidualBlock(costfunction,NULL,cere_r,cere_t);
  }
  ceres::Solver::Options option;
  option.linear_solver_type=ceres::DENSE_SCHUR;
  //输出迭代信息到屏幕
  option.minimizer_progress_to_stdout=true;
  //显示优化信息
  ceres::Solver::Summary summary;
  //开始求解
  ceres::Solve(option,&problem,&summary);
  //显示优化信息
  cout<<summary.BriefReport()<<endl;

中间关于空间点的求解博主就省略了,在这里,我们可以看到其实ceres给人的感觉还是很清晰的,至少不像g2o一样,而对于优化结果,可是说效果是比较好的,对应3D-3D的ICP问题,直接的一个代数解和用g2o做的优化它们实际上差别不是很大,而ceres的结果就给人感觉转角变了一些,位移也变了一些,一开始博主以为自己做错了,但是用反投影发现ceres优化的结果确实要比ICP要好很多,对应一个空间三维点,ceres似乎能更好的接近,博主其实也很奇怪为什么至少在这里g2o的效果会没有ceres好,毕竟一个是自动求导,一个是要自己写雅克比矩阵的。。。

想不出那就算了,博主继续用ceres做BA优化了。。。

<think>嗯,用户问的是ORB-SLAM中的BA优化实现和改进方法。首先,我需要回忆一下ORB-SLAM的基本结构。ORB-SLAM主要分为跟踪、局部建图和闭环检测三个线程,而BA优化主要在局部建图和闭环检测中使用。 BA(Bundle Adjustment)在ORB-SLAM中应该是在局部地图优化和全局优化时进行的。记得ORB-SLAM使用的是g2o库作为优化后端,所以需要确认这一点。用户可能想了解具体的实现细节,比如如何构建图优化模型,使用的节点和边是什么类型的。 在实现部分,应该分步骤说明:数据结构准备、图模型构建、优化策略、参数设置。比如,关键和地图点作为顶点,观测关系作为边。需要提到使用的鲁棒核函数,比如Huber,以及优化后的外点处理机制。 改进方法方面,ORB-SLAM3可能对BA有改进,比如加入IMU或视觉惯性BA。此外,可能提到使用其他优化库如Ceres,或者增量式BA、滑动窗口法来提升效率。还有边缘化策略和自适应参数调整,比如根据运动状态调整迭代次数。 用户提供的引用中,引用[1]提到了阶段的全局-局部图优化,这可能和ORB-SLAM的全局BA与局部BA有关,可以引用这一点。引用[3]提到图形学中的算法,可能和BA中的优化算法或数值方法有关联,但不太确定是否需要引用。需要检查引用内容是否相关,再决定是否加入。 还要注意生成相关问题,可能涉及BA的作用、与其他SLAM的区别、实时性挑战、鲁棒性处理等。确保问题覆盖用户可能关心的其他方面。 需要验证提到的优化策略是否准确,比如ORB-SLAM是否真的使用Huber核函数,以及边缘化的具体应用。可能还需要确认ORB-SLAM3中的改进点,比如视觉惯性BA的具体实现方式。 最后,确保回答结构清晰,分点说明,并且引用正确。可能需要调整语言更口语化,但按照用户要求,使用中文回答,并符合格式要求。</think>### ORB-SLAM中的BA优化实现与改进方法 #### 一、ORB-SLAMBA优化实现 1. **BA的作用与层级** - **局部BA**:优化当前关键及其共视关键,以及这些关键观测到的地图点,减少局部累积误差[^1]。 - **全局BA**:在闭环检测后优化所有关键和地图点,确保全局一致性。 - 数学形式可表示为最小化重投影误差: $$ \min_{\mathbf{T}_i, \mathbf{P}_j} \sum_{i,j} \rho \left( \|\pi(\mathbf{T}_i \mathbf{P}_j) - \mathbf{u}_{ij} \|^2 \right) $$ 其中$\mathbf{T}_i$为位姿,$\mathbf{P}_j$为3D点,$\rho$为Huber鲁棒核函数。 2. **实现步骤** - **数据结构准备**:提取当前激活的关键集合及其观测的地图点。 - **图模型构建**: - 关键位姿作为SE(3)节点 - 地图点坐标作为3D点节点 - 观测关系构建一元边(单目)或二元边(双目) - **优化策略**: - 使用g2o库的LM(Levenberg-Marquardt)优化器 - 固定非活跃关键的位姿 - 采用优化:先优化位姿再联合优化位姿与地图点 3. **代码级实现(简化逻辑)** ```cpp void Optimizer::LocalBundleAdjustment(KeyFrame* pKF, bool* pbStopFlag) { // 1. 获取局部关键集合 vector<KeyFrame*> vLocalKeyFrames = pKF->GetVectorCovisibleKeyFrames(); // 2. 构建优化器 g2o::SparseOptimizer optimizer; g2o::BlockSolver_6_3::LinearSolverType* linearSolver = new g2o::LinearSolverEigen<g2o::BlockSolver_6_3::PoseMatrixType>(); g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg( new g2o::BlockSolver_6_3(linearSolver)); // 3. 添加顶点与边 for(KeyFrame* pKFi : vLocalKeyFrames) { g2o::VertexSE3Expmap* vSE3 = new g2o::VertexSE3Expmap(); vSE3->setEstimate(Converter::toSE3Quat(pKFi->GetPose())); optimizer.addVertex(vSE3); // 添加地图点观测边 for(MapPoint* pMP : pKFi->GetMapPoints()) { g2o::EdgeSE3ProjectXYZ* e = new g2o::EdgeSE3ProjectXYZ(); e->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(vSE3)); // ... 设置其他参数 optimizer.addEdge(e); } } // 4. 执行优化 optimizer.initializeOptimization(); optimizer.optimize(5); // 迭代5次 } ``` #### 二、改进方法 1. **算法层面的改进** - **视觉惯性BA**(ORB-SLAM3新增): $$ E = \sum \rho(\| \mathbf{r}_{IMU} \|_{\Sigma_{IMU}}^2 ) + \sum \rho(\| \mathbf{r}_{proj} \|_{\Sigma_{img}}^2 ) $$ 联合优化相机位姿、速度、IMU偏置和地图点[^1]。 - **滑动窗口BA**:限制优化关键数量,保持实时性。 - **边缘化策略**:使用Schur补消除旧关键的状态,保留其约束信息。 2. **工程优化** - **增量式BA**:仅优化受新观测影响的部分变量 - **多线程加速**:将雅可比矩阵计算与线性求解步骤并行化 - **自适应参数调整**:根据运动剧烈程度动态调整迭代次数 3. **鲁棒性增强** - **动态权重分配**:基于特征匹配质量调整信息矩阵权重 - **多假设BA**:维护多个优化假设,选择最一致的解 #### 三、典型改进案例 DiSCo-SLAM提出的阶段优化框架[^1]: 1. **第一阶段**:分布式局部BA优化 - 各机器人独立优化局部地图 - 采用scan context进行快速数据关联 2. **第二阶段**:全局一致性优化 - 整合各局部优化结果 - 构建全局位姿图进行轻量级优化
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值