上一篇博客大致说明了下ceres-solver库的编译,然后形成了一个二次开发的库,下面就是用这个二次开发库来写一个简单(其实不太简单)的DEMO来演示ceres-solver库的强大。我们以求解一个非线性的方程(椭球方程)的系数为例子。下面是椭球方程的公式。

我们要求解的就是。为了演示,我通过程序生成了一个单位球上面的一系列坐标,也就是上面的abc均为1,偏移量均为0。为了验证ceres-solver是否可以完成这个工作。

首先我们编写下面的测试代码。

#include "glog/logging.h"
#include "ceres/ceres.h"
#include <vector> using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve; struct EllipsoidResidual
{
/*
* x, y, z 分别为观测值
*/
EllipsoidResidual(double x, double y, double z):_x(x), _y(y), _z(z){} /*
*pEllipsoidParameters:-2分别为a、b、c,3-5分别为x0、y0、z0
*/
template <typename T> bool operator () (const T * const pEllipsoidParameters, T * residual) const
{
residual[0] = T(1.0) - T(_x - pEllipsoidParameters[3])*T(_x - pEllipsoidParameters[3])/T(pEllipsoidParameters[0]*pEllipsoidParameters[0]) -
T(_y - pEllipsoidParameters[4])*T(_y - pEllipsoidParameters[4])/T(pEllipsoidParameters[1]*pEllipsoidParameters[1]) -
T(_z - pEllipsoidParameters[5])*T(_z - pEllipsoidParameters[5])/T(pEllipsoidParameters[2]*pEllipsoidParameters[2]);
return true;
}
private:
const double _x;
const double _y;
const double _z;
}; struct Point3D
{
double x;
double y;
double z;
}; /*
* 用于解算椭球参数的类
* m_EllipsoidParameters:椭球参数的初始值
* m_bInitParameters:椭球参数是否已经进行初始化
* m_data:观测值
* m_options:解算方式选项,可以进行设置
* m_summary:解算的报告信息
*
* 使用方式:
* ()设置椭球参数的初始值(m_EllipsoidParameters),并标记m_bInitParameters为true
* ()设置观测值m_data(个数大于)
* ()调用SolveParameters()函数
* ()得到结果m_EllipsoidParameters与解算报告信息m_summary
*/
struct EllipsoidFittingSolver
{
EllipsoidFittingSolver()
{
m_options;
m_options.max_num_iterations = 25;
m_options.linear_solver_type = ceres::DENSE_QR;
m_options.minimizer_progress_to_stdout = true;
m_bInitParameters = false;
} bool SolveParameters()
{
if (m_data.size() < 6 || !m_bInitParameters)
{
return false;
} Problem problem;
const int nObservations = m_data.size();
for (int i = 0; i < nObservations; ++i)
{
problem.AddResidualBlock(new AutoDiffCostFunction<EllipsoidResidual, 1, 6>(
new EllipsoidResidual(m_data[i].x, m_data[i].y, m_data[i].z)),
NULL,
m_EllipsoidParameters);
} Solve(m_options, &problem, &m_summary);
return true;
} bool m_bInitParameters;
double m_EllipsoidParameters[6];
std::vector<Point3D> m_data;
Solver::Options m_options;
Solver::Summary m_summary;
}; double* readFile(const char* szFilename,int& nCount)
{
if(!szFilename)return NULL;
FILE* fid=fopen(szFilename,"rt");
if(!fid)return NULL; nCount=0;
fscanf(fid,"%d",&nCount);//读第一行数据,是行数,赋给nCount int nSize=sizeof(double)*3*nCount;//根据行数分配大小,要取列数据所以是*nCount
double* dbData=(double*)malloc(nSize);
if(!dbData)return NULL;
memset(dbData,0,nSize);//将申请的内存空间初始化为 double dbInvalid=0.;
for(int i=0;i<nCount;i++)
{
double* dbTmp=dbData+3*i;
fscanf(fid,"%lf %lf %lf",dbTmp,dbTmp+1,dbTmp+2);
}
fclose(fid); return dbData;
} int main(int argc, char** argv)
{
google::InitGoogleLogging(argv[0]); EllipsoidFittingSolver efs;
double dInit[6] = {0.5,0.5,0.5,0.5,0.5,0.5};
memcpy(efs.m_EllipsoidParameters, dInit, sizeof(double)*6);
efs.m_bInitParameters = true; const char* pszFilename = "testdata2.txt";
fprintf(stderr,"ReadFile begins.\n");
int nCount=-1;
double* dbSrcData= readFile(pszFilename, nCount);
if(nCount<0)
{
fprintf(stderr,"ReadFile Error!\n");
return -1;
}
else
fprintf(stderr,"ReadFile Completed.!\n"); for (int i=0; i<nCount; i++)
{
Point3D pt;
pt.x = dbSrcData[i*3+0];
pt.y = dbSrcData[i*3+1];
pt.z = dbSrcData[i*3+2]; efs.m_data.push_back(pt);
} free(dbSrcData); if(efs.SolveParameters())
{
fprintf(stdout,"Solver Success!\n"); fprintf(stdout,"a=%lf b=%lf c=%lf\n",efs.m_EllipsoidParameters[0],efs.m_EllipsoidParameters[1],efs.m_EllipsoidParameters[2]);
fprintf(stdout,"x0=%lf y0=%lf z0=%lf\n",efs.m_EllipsoidParameters[3],efs.m_EllipsoidParameters[4],efs.m_EllipsoidParameters[5]);
fprintf(stdout,"%s\n",efs.m_summary.FullReport().c_str());
}
else
fprintf(stdout,"Solver Failed!\n"); return 0;
}

测试数据部分截图如下:

新建一个控制台应用程序,然后将第一篇的include和lib目录分别添加到工程中,同时配置引用的lib文件(RELEASE版本引用libglog.lib和ceres_r.lib,DEBUG版本引用libglog.lib和ceres_d.lib),然后编译即可。执行结果如下图所示。

红色区域为迭代过程,蓝色区域为计算的结果,最下面的为拟合报告。从蓝色的区域可以看出,拟合的abc的值为1,偏移量为0,和我们开始指定的球体方程一致。

从代码中可以看出,ceres-solver库不需要对非线性的方程进行求偏导数进行线性化然后再使用最小二乘求解系数。这大大方便了人们手动计算偏导数列系数矩阵,可以避免复杂的计算以及手工计算过程中出现的错误。

最后感谢@末末_happy提供的测试数据,感谢@AegeanSea87编写的测试代码。

ceres-solver库使用示例的更多相关文章

  1. Ceres Solver: 高效的非线性优化库(一)

    Ceres Solver: 高效的非线性优化库(一) 注:本文基于Ceres官方文档,大部分由英文翻译而来.可作为非官方参考文档. 简介 Ceres,原意是谷神星,是发现不久的一颗轨道在木星和火星之间 ...

  2. VINS(九)Ceres Solver优化(未完待续)

    使用Ceres Solver库处理后端优化问题,首先系统的优化函数为

  3. Ceres Solver: 高效的非线性优化库(二)实战篇

    Ceres Solver: 高效的非线性优化库(二)实战篇 接上篇: Ceres Solver: 高效的非线性优化库(一) 如何求导 Ceres Solver提供了一种自动求导的方案,上一篇我们已经看 ...

  4. Ceres Solver for android

        最近开发中,需要对图片做一些处理与线性技术,这时就用到了Ceres Solver.如何把Ceres Solver集成到Android里呢? 官网给了一个解决方案,简洁明了:   Downloa ...

  5. C/C++ 开源库及示例代码

    C/C++ 开源库及示例代码 Table of Contents 说明 1 综合性的库 2 数据结构 & 算法 2.1 容器 2.1.1 标准容器 2.1.2 Lockfree 的容器 2.1 ...

  6. RAC集群数据库连库代码示例(jdbc thin方式,非oci)

    1.RAC集群数据库连库代码示例(jdbc thin方式,非oci):jdbc.driverClassName=oracle.jdbc.driver.OracleDriverjdbc.url=jdbc ...

  7. Ceres Solver 入门稍微多一点

    其实ceres solver用了挺多的,可能是入门不精,有时候感觉感觉不理解代码上是怎么实现的,这次就通过ceres的官网仔细看了一些介绍,感觉对cpp了解更好了一些. 跟g2o的比较的话,感觉cer ...

  8. node.js的Promise库-bluebird示例

    前两天公司一哥们写了一段node.js代码发给我,后面特意提了一句“写的不太优雅”.我知道,他意思是回调嵌套回调,因为当时比较急也就没有再纠结.然而内心中总记得要解决这个问题.解决node.js的回调 ...

  9. Robot Framework - 4 - 创建和扩展测试库的示例

    创建和扩展Library的示例 示例:Check status on Linux OS 创建与使用library的基本步骤:           1--- library实现的内容和实现的方式     ...

随机推荐

  1. cassandra 3.x官方文档(6)---内部原理之存储引擎

    写在前面 cassandra3.x官方文档的非官方翻译.翻译内容水平全依赖本人英文水平和对cassandra的理解.所以强烈建议阅读英文版cassandra 3.x 官方文档.此文档一半是翻译,一半是 ...

  2. 临时关闭Mac SIP系统完整性保护机制

    # 修正更新 [2016-12-27] 晚上给我笔记本安装的时候,使用user权限安装成功,mac最后是关闭sip才安装成功. $ pip install -r requirements.txt -- ...

  3. Ajax原理学习

    一.AJAX 简介 AJAX即"Asynchronous Javascript And XML"(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术. A ...

  4. 关于 linux中TCP数据包(SKB)序列号的小笔记

    关于  SKB序列号的小笔记 为了修改TCP协议,现在遇到了要改动tcp分组的序列号,但是只是在tcp_sendmsg函数中找到了SKB的end_seq  一直没有找到seq 不清楚在那里初始化了,就 ...

  5. CountDownLatch使用

    分享牛原创,CountDownLatch类的使用,CountDownLatch是一个工具类,运行主线程开启子线程的时候,子线程还没有结束的时候,主线程可以一直等待,直到初始化的现成的计数器count为 ...

  6. 如何找出Xcode中不同版本Swift的路径

    我们知道Xcode中可能包含不知一个Swift的版本,那么我们如何找到它们对应的路径呢? 熟悉unix shell命令的童鞋都知道有一个find指令,在我们已知Xcode路径时,我们可以在其中找到Sw ...

  7. System startup files

    System startup files When you log in, the shell defines your user environment after reading the init ...

  8. Effective C++ ——模板和泛型编程

    条款41:了解隐式接口和编译器多态 以public继承的类,

  9. Xcode7.2中如何添加一个Empty Application模板

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) Xcode 6.0正式版之后已经没有所谓的Empty Appl ...

  10. UNIX网络编程——常用服务器模型总结

    下面有9种服务器模型分别是: 迭代服务器. 并发服务器,为每个客户fork一个进程. 预先派生子进程,每个子进程都调用accept,accept无上锁保护. 预先派生子进程,以文件锁的方式保护acce ...