Ceres
使用Ceres求解非线性优化问题,主要分为三个部分:
第一部分:构建代价函数
Cost_Functor
1
2
3
4
5
6
7
8// 定义一个实例化时才知道的类型T
template <typename T>
// 运算符()的重载,用来得到残差fi
bool operator()(const T* const x, T* residual) const {
residual[0] = T(10.0) - x[0];
return true;
}第二部分:构建最小二乘问题
problem
1
2
3
4
5Problem problem;
// 使用自动求导,第一个1是输出维度(残差项),第二个1是输入维度(优化项)
CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
// 添加误差项,NULL表示不使用核函数,x是优化项
problem.AddResidualBlock(cost_function, NULL, &x);第三部分:求解器参数配置
Solver
1
2
3
4
5
6
7Solver::Options options;
options.linear_solver_type = ceres::DENSE_QR; //配置增量方程的解法,稠密的QR分解
options.minimizer_progress_to_stdout = true;//输出到cout
Solver::Summary summary;//优化信息
Solve(options, &problem, &summary);//求解
cout << summary.BriefReport() << "\n";//输出优化的简要信息
使用核函数:数据中往往存在离群点,离群点会对寻优结果造成影响,这时可以使用一些损失核函数来对离群点的影响加以消除,Ceres库中提供的核函数主要有:TrivialLoss 、HuberLoss、 SoftLOneLoss 、 CauchyLoss。
1 | // 使用核函数 |
g2o
用g2o优化库来进行优化的步骤如下:
定义节点和边的类型,通常在默认的基础类型上做修改
定义顶点,顶点的基类为
g2o::BaseVertex<优化变量维度,数据类型>
1
class CurveFittingVertex: public g2o::BaseVertex<3, Eigen::Vector3d>
顶点的更新函数
oplusImpl
:定义增量加法,因为优化变量和增量之间并不一定是线性叠加的关系,如位姿变换。定义边, 本例中的边为一元边,基类为
g2o::BaseUnaryEdge<观测值维度,数据类型,连接顶点类型>
1
class CurveFittingEdge: public g2o::BaseUnaryEdge<1, double , CurveFittingVertex>
误差项计算函数
computeError
:计算预测值和观测值的误差。估计值是基于当前对优化变量的estimate计算出的,观测值是直接获取的,如本例中的y值。
构建图模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// vertex
g2o::VertexSE3Expmap* pose = new g2o::VertexSE3Expmap(); // camera pose
pose->setId( index );
pose->setEstimate( expression );
optimizer.addVertex ( pose );
// edge
g2o::EdgeProjectXYZ2UV* edge = new g2o::EdgeProjectXYZ2UV();
edge->setId ( index );
edge->setVertex ( 0, point );
edge->setVertex ( 1, pose );
edge->setMeasurement ( Eigen::Vector2d ( p.x, p.y ) ); // 导入观测值
edge->setParameterId ( 0,0 );
edge->setInformation ( Eigen::Matrix2d::Identity() ); // 设置信息矩阵
optimizer.addEdge ( edge );信息矩阵
edge->setInformation(信息矩阵)
:因为最终的优化函数是$\sum e_i^T \Sigma^{-1}e_i$,是误差项和信息矩阵乘积的形式。优化器配置
- 矩阵块Block
- 优化算法solver
- 图模型optimizer
执行优化