Ceres Solver 入门稍微多一点
其实ceres solver用了挺多的,可能是入门不精,有时候感觉感觉不理解代码上是怎么实现的,这次就通过ceres的官网仔细看了一些介绍,感觉对cpp了解更好了一些。
跟g2o的比较的话,感觉ceres solver是一个更通用的非线性优化器,g2o是更加针对SLAM的开发。比如g2o对一个outlier有函数借口,我了解的ceres里就只能在计算error搞一搞了。
本来以为只有ceres提供了autodiff,后来被告之g2o也有了,那感觉ceres也没这么有优势了。不过真的要落地的肯定都要自己写的,前期开发的话,大家哪个熟用哪个呗。
Ceres
Ceres solver consists of two parts:
- a modeling API construct an optimization problem one term at a time.
- a solver API that controls the minimization algorithm.
Cost Function
which is responsible for computing a vector of residuals and Jacobian matrices.
class CostFunction {
public:
virtual bool Evaluate(double const* const parameters, double* residuals, double** jacobians) = 0;
const vector<int32>& parameter_block_sizes();
int num_residuals() const;
protected:
vector<int32>* mutable_parameter_block_sizes();
void set_num_residuals(int num_residuals);
};
CostFunction::parameter_block_sizes and CostFunction::num_residuals_ would be set by Problem when using Problem::AddResidualBlock().
bool CostFunction::Evaluate
parameters is an array of arrays of size x and parameters[i] is an array of size xx.
parameters is never NULL.
residuals is an array of size xxx.
residuals is never NULL.
jacobian is an array of arrays of size xxxx.
if jacobian is NULL, the user is only expected to compute the residuals.
jacobian is a row-major array. of residuam_num * parameter_block_size
If jacobian[i] is NULL, then this computation can be skipped. This is the case when the corresponding parameter block is markded constant.
SizedCostFunction
If the size of the parameter block and the size of the residual vector is known at compile time(常规场景).
The user only needs to implement CostFunction::Evaluate().
// 模板参数
template<int kNumResiduals, int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0,
int N5 = 0, int N6 = 0, int N7 = 0, int N8 = 0, int N9 = 0>
class SizedCostFUnction: public CostFunction {
public:
virtual bool Evalute(double const* const* parameters,
double* residuals,
double** jacobians) const = 0;
};
AutoDiffCostFunction
template <typename CostFunctor,
int kNumResiduals, int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0,
int N5 = 0, int N6 = 0, int N7 = 0, int N8 = 0, int N9 = 0>
class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9> {
public:
explicit AutoDiffCostFunction(CostFunctor* functor);
AutoDiffCostFunction(CostFunctor* functor, int num_residuals);
};
To get an auto differentiated cost function, you must define a class(functor) with a templated operator() (a functor) that computes the cost function in terms of the template parameter T.
The function must write the computed value in the last argument( the only non-const one) and return true to indicate success. (咋搞的??)
For example $ e=k - x^Ty $, the actual cost added to the total problem is e^2 while the squaring is implicitly done by the optimization framework.
class MyScalarCostFunctor {
public:
MyScalarCostFunctor(double k) : k_(k) {}
template <typename T>
bool operator()(const T* const x, const T* const y, T* e) const {
e[0] = k_ - x[0] * y[0] - x[1] * y[1];
return true;
}
private:
double k_;
};
class definition is shown as below:
CostFunction* cost_function = new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(new MyScalarCostFUnctor(1.0));
// the sequence is: Dimension of residuals, of x and of y.
AutoDiffCostFunction also supports cost functions with a runtime-determined number of residuals. 这里用了第二个AutoDiffCostFunction的第二个构造函数AutoDiffCostFunction(CostFunctor*, int).
e.g.
CostFunction* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, DYNAMIC, 2, 2>(
new CostFunctiorWithDynamicNumResiduals(1.0),
runtime_number_of_residuals);
The framework can currently accommodate cost functions of up to 10 independent variables, and there is no limit on the dimensionality of each of them.
DynamicAutoDiffCostFunction
It requires that the number of parameter blocks and their sizes be known at compile time. It also has an upper limit of 10 parameter blocks.
template <typename CostFunctor, int Stride = 4>
class DynamicAutoDiffCostFunction : public CostFunction {};
The functor signature is a bit different.
struct MyCostFunctor {
template<typename T>
bool operator() (T const* const * parameters, T* residuals) const {
}
}
这玩意反正不太好用。
NumericDiffCostFunction
有时候不能定义一个cost functor, 用这玩意。
template <typename CostFunctor, NumericDiffMethodType method = CENTRAL, int kNumResiduals, int N0, int N1 = 0> // 当然后还有8个
class NumericDIffCostFunction : public SizedCostFunction<kNumResiduals, N0, N1> {}
在ceres中有三种numeric differentiation的方式,FORWARD, CENTRAL, RIDDERS.
如果你的parameter block是在一个流行上的,numerical differentiation可能会有问题,因为这个方式只是扰动parameter block中单个coordinate,这也就表示我们认为parameter block在欧式空间里而忽视了流行的structure。
e.g. Pertubing the coordiantes of a unit quaternion will vilate the unit norm property of the parameter block.
解决这个问题需要让NumericDiffCostFunction意识到LocalParameterization.
一般来说,我们推荐使用AutoDiffCostFunction而非NumericDiffCostFunction,因为在cpp模板类使得自动微分非常有效,而采用数值微分却划不来,由于其数值误差会使得收敛变慢。(反正auto就是用了很吊的扫操作牛逼就行了)
DynamicNumericDiffCostFunction
template <typename CostFunctor, NumericDiffMethodType method = CENTRAL>
class DynamicNumericDiffCostFunction : public CostFunction {
};
NormalPrior
class NormalPrior : public CostFunction {
public:
// 保证b中的行数和A的列数一样
NormalPrior(const Matrix& A, const Vector& b);
virtual bool Evalute(double const* const parameters, double* residuals, double** jacobians) const;
};
LossFunction
class LossFunction {
public:
virtual void Evaluete(double s, double out[3]) const = 0;
};
LocalParameterization
class LocalParameterization {
public:
virtual ~LocalParameterization() {}
virtual bool Plus(const double* x, const double* delta, double* x_plus_delta) const = 0;
virtual bool ComputeJacobian(const double* x, double* jacobian) const = 0;
//...
}
class IdentityParameterization
加法定义和一般的一样 Plus(x, delta x) = x + delta x
class QuaternionParameterization
class EigenQuaternionParameterization
Eigen存储quaternion的方式不同,是[x, y, z, w].
ProductParameterization可以构建一个Cartesian product of localparameterization.
ProductParameterization se3_param(new QuaternionParameterization(), new IdentityTransformation(3));
AutoDiffLocalParameterization
struct QuaternionPlus {
template<typename T>
bool operator() (const T* x, const T* delta, T* x_plus_delta) const {
const T squared_norm_delta = delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2];
T q_delta[4];
if (squared_norm_delta > 0.0) {
T norm_delta = sqrt(squared_norm_delta);
const T sin_delta_by_delta = sin(norm_delta) / norm_delta;
q_delta[0] = cos(norm_delta);
q_delta[1] = sin_delta_by_delta * delta[0];
q_delta[2] = sin_delta_by_delta * delta[1];
q_delta[3] = sin_delta_by_delta * delta[2];
} else {
q_delta[0] = T(1.0);
q_delta[1] = delta[0];
q_delta[2] = delta[1];
q_delta[3] = delta[2];
}
Quaternionproduct(q_delta, x, x_plus_delta);
return true;
}
};
Given this struct, the auto differentiated lcoal parameterization can now be constructed as
LocalParameterization* local_parameterization =
new AutoDiffLocalParameterization<QuaternionPlus, 4, 3>; // global size and local size
Problem
Use Problem::AddResidualBlock() and Problem::AddParameterBlock() methods.
Problem::AddResidualBlock()加入一个CostFunction,一个可选项的LossFunction,然后链接CostFunction和一系列的parameter block。
AddResidualBlock
ResidualBlockId Problem::AddResidualBlock(CostFunction*, LossFunction*, const vector<double*> parameter_blocks);
ResidualBlockId Problem::AddResidualBlock(CostFunction*, LossFunction*, double* x0, double* x1...);
CostFunction有parameter block的信息,这个函数会检查这些信息是否匹配。
LossFunction可以是NULL。
用户可以选择explicitly加入parameter block用``AddParameterBlock``,这会加入一些额外的检查。但是``AddResidualBlock``会implicitly加入不存在的parameter block。
Problem对象会默认拥有cost_function和loss_function的指针,他们会在problem对象存在的受活着。如果用户想自己销毁的话,可以在option里面设置。
尽管problem拥有cost_function和loss_function,它不会妨碍用户在其他residual block中重复使用。销毁器只会销毁cost_function和loss_function一次,不管多少residual block引用了他们。
???如果两个problem用了同一个parameter block,会有double free的问题么?
### AddParameterBlock
```cpp
void Problem::AddParameterBlock(double8 values, int size, LocalParameterization *);
加入合适的size的parameter block给problem。
重复的参数被加入会被ignore。
重复加入一个参数块但是定义不一样的size的结果是undefined。
Problem::RemoveResidualBlock
void Problem::RemoveResidualBlock(ResidualBlockId);
移除一个residual block,但是任何依赖的parameter不会被移除。
对应的cost function和loss function不会被立马删除,会在problem被销毁后。
如果Problem::Options::enable_fast_removal为真,会移除会快(constant time),不然会扫描整个problem来看是否是valid residual。
WARNING: 会改变内部的顺序,导致雅克比和参差没法解析,如果是用evaluated jacobian的话,最好别这么做。
Problem::RemoveParameterBlock
void Problem::RemoveParameterBlock(double *values)
以来这个parameter block的residual block也会被移除。
WARNING和上述一样。
Others
void Problem::SetParameterBlockConstant(double* values);
void Problem::SetParameterBlockVaribale(double* values);
void Problem::SetParameterization(double* values, LocalParameterization*);
LocalParameterization* Problem::GetParameterization(double* values);
void Problem::SetParameterLowerBound(double* value, int index, double lower_bound);
void Problem::SetParameterUpperBound(double* value, int index, double upper_bound);
rotation.h
template <typename T>
void AngleAxisToQuaternion(T const *angle_axis, T* quaternion);
template <typename T>
void QuaternionToAngleAxis(T const *quaternion, T* angle_axis);
template <typename T, int row_stride, int col_stride>
void RotationMatrixToAngleAxis(const MatrixAdapter<const T, row_stride, col_stride>&R, T* angle_axis);
template <typename T, int row_stride, int col_stride>
void AngleAxisToRotationMatrix(T const* angle_axis, const MatrixAdapter<T, row_stride, col_stride> &R)
template <typename T>
void RotationMatrixToAngleAxis(T const* R, T* angle_axis);
tempalte <typename T>
void AngleAxisToRotationMatrix(T const* angle_axis, T* R);
Ceres Solver 入门稍微多一点的更多相关文章
- Ceres Solver: 高效的非线性优化库(二)实战篇
Ceres Solver: 高效的非线性优化库(二)实战篇 接上篇: Ceres Solver: 高效的非线性优化库(一) 如何求导 Ceres Solver提供了一种自动求导的方案,上一篇我们已经看 ...
- Ceres Solver for android
最近开发中,需要对图片做一些处理与线性技术,这时就用到了Ceres Solver.如何把Ceres Solver集成到Android里呢? 官网给了一个解决方案,简洁明了: Downloa ...
- VINS(九)Ceres Solver优化(未完待续)
使用Ceres Solver库处理后端优化问题,首先系统的优化函数为
- Ceres Solver: 高效的非线性优化库(一)
Ceres Solver: 高效的非线性优化库(一) 注:本文基于Ceres官方文档,大部分由英文翻译而来.可作为非官方参考文档. 简介 Ceres,原意是谷神星,是发现不久的一颗轨道在木星和火星之间 ...
- [HeadFist-HTMLCSS学习笔记][第七章CSS入门:加一点样式]
CSS入门 style元素设置CSS 基本格式 <style type="text/css"> body { background-color: #eaf3da; } ...
- LeetCode 笔记系列15 Set Matrix Zeroes [稍微有一点hack]
题目:Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. Fol ...
- Android平台使用Ceres Solver
在Android平台上使用Ceres求解器,官方教程不明确,且编译过程遇到了很多问题. 环境 Ubuntu 18.04 源代码 https://github.com/Great-Keith/ceres ...
- 入门 uCOS 操作系统的一点建议
原创: 鱼鹰Osprey 鱼鹰谈单片机 3月2日 预计阅读时间: 4 分钟 对于想入门操作系统的读者,我的建议是先学 uCOS II.原因有以下几点: 1.最为重要的原因是网上相关资源非常丰富,这对 ...
- Ceres Solver 在win8+vs2013环境下的安装
参考博文:https://blog.csdn.net/wzheng92/article/details/79504709
随机推荐
- docker tmpfs 的测试结果
创建 2G 内存的 Container 使用tmpfs挂载到 /tmp docker run --rm -it --memory 2g --mount type=tmpfs,destination=/ ...
- react --- 搭建环境
搭建react开发环境的准备工作 1. node.js 稳定版本 2. 安装cnpm,用cnpm代替npm 3. 用yarn替代npm yarn的安装:npm install -g yarn 搭建re ...
- WIN10下微信崩溃(已经是最新版)的解决方法
微信运行错误---------------------------你的微信崩溃次数较多,建议使用最新版本,点击"确定"到官网(http://pc.weixin.qq.com/)下载 ...
- Tomcat在Window控制台下启动时乱码的两种解决办法
在命令提示符中启动Tomcat时,日志窗口出现乱码: 乱码的原因肯定是日志解码错误引起的,因此就有一系列问题: 1.这个窗口的文本编码是什么? 窗口的文本编码查看:右击窗口>选项 可以看到窗口的 ...
- 网站搭建 (第06天) Ckeditor编辑器
一.前言 富文本编辑器,Rich Text Editor, 简称 RTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器,这是百度百科的对富文本编辑器的解释.我们可以借助富文本编辑器,编辑出来一个包含 ...
- 用Xshell在centos7下安装lnmp服务
虚拟机已创建好,本机已安装Xshell 一.准备工作:安装常用工具 1.1 yum install -y vim 备注:-y是同意安装过程中的询问,不被询问打断安装 vim:vim是一个类似于Vi的 ...
- 必会SQL练习题
()表名:购物信息 购物人 商品名称 数量 A 甲 B 乙 C 丙 A 丁 B 丙 …… 给出所有购入商品为两种或两种以上的购物人记录 答:); ()表名:成绩表 姓名 课程 分数 张三 语文 张三 ...
- OO随笔
第一次作业——多项式计算 1.自我程序分析 第一次作业是多项式计算,只使用了一个多项式类.第一次接触面向对象的程序,还比较生疏,不是很能理解面向对象的思想.将读入,处理,计算,都放到了main函数中, ...
- Spring定时任务@Scheduled注解使用方式
1.开篇 spring的@Scheduled定时任务相信大家都是十分熟悉.最近在使用过程中发现了一些问题,写篇文章,和大家分享一下.结论在最后,不想看冗长过程的小伙伴可以直接拉到最后看结论. 2.简单 ...
- Kinect外包-就找北京动点飞扬软件(长年承接微软Kinect体感项目外包,有大型Kinect案例)
承接Kinect体感企业项目.游戏项目外包 有丰富案例提供演示,可公对公签正规合同,开发票. 我们是北京的公司.专业团队,成员为专业WPF产品公司一线开发人员,有大型产品开发经验: 提供优质的售后服务 ...