视觉SLAM十四讲 读书编程笔记 Chapter6 非线性优化

2023-11-01

实践:Ceres

1. 安装Ceres依赖库

sudo apt-get install liblapack-dev libsuitesparse-dev libcxsparse3.1.2 libgflags-dev libgoogle-glog-dev libgtest-dev

出现找不到package的错误:
在这里插入图片描述解决办法:
添加源,用下面命令打开source.list

sudo gedit /etc/apt/sources.list

在第一行添加:

deb http://cz.archive.ubuntu.com/ubuntu trusty main universe

然后更新一下:

sudo apt-get update

再执行Ceres依赖库的安装就不会报错啦

2. 编译安装Ceres

github上下载Ceres源码,下载地址为:https://github.com/ceres-solver
使用cmake编译它并安装到机器上,安装完成之后,在/usr/local/include/ceres/下可以找到Ceres的头文件,在/usr/local/lib/下可以找到名为libceres.a的库文件。

make过程出现错误:
在这里插入图片描述
error: no type named ‘Literal’ in ‘struct Eigen::NumTraits<ceres::Jet<double, 2> >’
这是因为eigen3与ceres发生了冲突。
解决办法:
下载与书中提供的ceres版本兼容的eigen,与书中提供的ceres版本兼容的eigen下载
编译并且安装它,然后再编译ceres工程就可以顺利通过啦

3. 曲线拟合问题描述

假设有一条满足以下方程的曲线:y=exp(ax2+bx+c)+w
其中,a,b,c为曲线的参数,w为高斯噪声。
为了模拟实际的曲线拟合问题,我们假设曲线参数a,b,c的真值,然后引入高斯噪声w生成观测x和y
我们在非线性优化问题中会首先给待估计变量a,b,c一个初始值,然后把观测(x,y)输入导ceres中用ceres拟合这条曲线。

4. ceres使用方法

(1)定义Cost Function模型。 方法是书写一个类。并在类中定义带模板的( )运算符,这样该类就成为了一个拟函数。这种定义方式使得ceres可以像调用函数一样,对该类的某个对象(比如 a)调用a( )方法,这使对象具有像函数那样的行为。

//代价函数的计算模型
struct CURVE_FITTING_COST
{
	CURVE_FITTING_COST(double x, double y):_x(x),_y(y) {}
	//残差的计算
	template <typename T>
	bool operator() (
		const T* const abc,	//模型参数,有3维
		T* residual) const	//残差
	{
		
		//y-exp(ax^2+bx+c)
		residual[0] = T(_y) -ceres::exp(abc[0] * T(_x) * T(_x) + abc[1] * T(_x) +abc[2]);
		return true;
	}

	const double _x,_y;//x,y数据
};

(2)构建最小二乘问题

定义一个问题对象:

ceres::Problem problem;

调用AddResidualBlock将误差项添加导目标函数中,残差block需要配置求导方式核函数以及待估计的参数
其中求导方式有a.使用ceres的自动求导(Auto Diff); b.使用数值求导(Numeric Diff); c.自行推导解析的导数形式,提供给ceres
自动求导需要设定误差项和优化变量的维度。这里误差项是我们定义的CURVE_FITTING_COST类;误差是标量,维度为1; 优化变量为abc,维度为3;不使用核函数;待估计参数为abc

	//构建最小二乘问题
	ceres::Problem problem;
	for(int i=0;i<N;i++)
	{
		problem.AddResidualBlock(	//向问题中添加误差项
			//使用自动求导,模板参数:误差类型,输出维度,输入维度
			new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3>(new CURVE_FITTING_COST(x_data[i],y_data[i])),
			nullptr,	//核函数,这里不使用,为空
			abc		//待估计参数
		);
	}

(3)配置求解器
求解选项配置:

	ceres::Solver::Options options;//这里有很多配置项可以填
	options.linear_solver_type = ceres::DENSE_QR;//	增量方程如何求解
	options.minimizer_progress_to_stdout = true;//输出到cout

优化信息对象的定义:

	ceres::Solver::Summary summary;

(4)对问题进行优化

	ceres::Solve (options,&problem,&summary);//开始优化

优化信息对象输出结果:

	cout<<summary.BriefReport()<<endl;

最后优化的结果放在abc变量中

5.完整代码

ceres_curve_fitting.cpp

//2019.08.08
#include <iostream>
#include <opencv2/core/core.hpp>
#include <ceres/ceres.h>
#include <chrono>

using namespace std;

//代价函数的计算模型
struct CURVE_FITTING_COST
{
	CURVE_FITTING_COST(double x, double y):_x(x),_y(y) {}
	//残差的计算
	template <typename T>
	bool operator() (
		const T* const abc,	//模型参数,有3维
		T* residual) const	//残差
	{
		
		//y-exp(ax^2+bx+c)
		residual[0] = T(_y) -ceres::exp(abc[0] * T(_x) * T(_x) + abc[1] * T(_x) +abc[2]);
		return true;
	}

	const double _x,_y;//x,y数据
};


int main()
{
	double a=1, b=2.0, c=1.0;	//真实参数值
	int N = 100;			//数据点
	double w_sigma = 1.0;		//噪声Sigma(标准差)值
	cv::RNG rng;			//opencv随机数产生器
	double abc[3] = {0,0,0};	//abc参数的估计值,初始值为0,0,0
	
	vector<double> x_data,y_data;	//观测数据
	
	cout<<"generating data: "<<endl;
	
	//填充观测数据,实际应用时这个观测数据是由传感器读取的数据
	for(int i=0; i<N; i++)
	{
		double x = i/100.0;
		x_data.push_back(x);
		y_data.push_back(exp(a*x*x+b*x+c) + rng.gaussian(w_sigma));//加了高斯噪声的观测数据
		cout<<x_data[i]<<" "<<y_data[i]<<endl;
	}
	
	//构建最小二乘问题
	ceres::Problem problem;
	for(int i=0;i<N;i++)
	{
		problem.AddResidualBlock(	//向问题中添加误差项
			//使用自动求导,模板参数:误差类型,输出维度,输入维度
			new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3>(new CURVE_FITTING_COST(x_data[i],y_data[i])),
			nullptr,	//核函数,这里不使用,为空
			abc		//待估计参数
		);
	}

	//配置求解器
	ceres::Solver::Options options;//这里有很多配置项可以填
	options.linear_solver_type = ceres::DENSE_QR;//	增量方程如何求解
	options.minimizer_progress_to_stdout = true;//输出到cout

	ceres::Solver::Summary summary; //优化信息
	chrono::steady_clock::time_point t1 = chrono::steady_clock::now();//开始计时
	ceres::Solve (options,&problem,&summary);//开始优化
	chrono::steady_clock::time_point t2 = chrono::steady_clock::now();//结束计时
	chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2-t1);
	cout<<"solve time cost = "<<time_used.count()<<"seconds."<<endl;

	//输出结果
	cout<<summary.BriefReport()<<endl;
	cout<<"estimated a,b,c = ";
	for(auto a:abc)
		cout<<a<<" ";
	cout<<endl;


	return 0; 

}

CMakeLists.txt

#2019.08.08-08.09
# 添加C++标准文件
set(CMAKE_CXX_FLAGS "-std=c++11")

#寻找opencv库
find_package(OpenCV REQUIRED)
#寻找ceres库
find_package(Ceres REQUIRED)

#添加Eigen的头文件
include_directories("/usr/include/eigen3")
#添加Opencv的头文件
include_directories( ${OpenCV_INCLUDE_DIRS})
#添加ceres的头文件,下面两种方法等价
#include_directories(${CERES_INCLUDE_DIRS})
include_directories("/usr/local/include/ceres/")



add_executable(ceres_curve_fitting ceres_curve_fitting.cpp)

#链接OpenCV库
target_link_libraries(ceres_curve_fitting ${OpenCV_LIBS} )

#链接ceres库,以下两种方法不等价
target_link_libraries(ceres_curve_fitting ${CERES_LIBRARIES})
#target_link_libraries(ceres_curve_fitting "/usr/local/lib/")

运行结果:
在这里插入图片描述
在这里插入图片描述

实践:g2o

1.安装g2o依赖库

sudo apt-get install libqt4-dev qt4-qmake libqglviewer-dev libsuitesparse-dev libcxsparse3.1.2 libcholmod-dev

安装时出现:
在这里插入图片描述
E: Unable to locate package libcholmod-dev
解决办法:
先把其他依赖库安装好:

sudo apt-get install libqt4-dev qt4-qmake libqglviewer-dev libsuitesparse-dev libcxsparse3.1.2

然后单独安装libcholmod-dev,输入以下命令然后按下Tab键

sudo apt-get install libcholmod

在这里插入图片描述
我们安装libcholmod3.0.6

sudo apt-get install libcholmod3.0.6

至此,依赖库安装完毕

2.编译安装g2o

github上下载g2o源码,下载地址为:https://github.com/RainerKuemmerle/g2o
使用cmake编译它并安装到机器上,安装完成之后,在/usr/local/include/g2o/下可以找到g2o的头文件,在/usr/local/lib/下可以找到它的库文件。

make出现错误:
在这里插入图片描述
moc: Cannot open options file specified with @
这是因为路径中包含了中文名称,把源码放在无中文名称的路径中,make就可以顺利通过。

3. g2o使用方法

(1)图优化基础
传统的非线性优化问题仅有一组优化变量和很多个误差项,我们并不知道它们之间的关联。图优化是把优化问题表现成图的一种方式,用顶点表示优化变量,用边表示误差项。
(2)SLAM中图优化问题举例
在这里插入图片描述
(3) 曲线拟合问题的图优化表示
在这里插入图片描述
节点为优化变量,边为误差项,这里的边是一元边

(4)使用g2o的基本步骤
定义顶点和边的类型
构建图
选择优化算法
调用g2o进行优化,返回结果
(5)g2o头文件

#include <g2o/core/base_vertex.h>
#include <g2o/core/base_unary_edge.h>
#include <g2o/core/block_solver.h>
#include <g2o/core/optimization_algorithm_levenberg.h>
#include <g2o/core/optimization_algorithm_gauss_newton.h>
#include <g2o/core/optimization_algorithm_dogleg.h>
#include <g2o/solvers/dense/linear_solver_dense.h>

(6)派生顶点CurveFittingVertex
在其中要重写两个重要的虚函数:
顶点的更新函数:oplusImpl(),该函数处理的是xk+1=xk+增量x
顶点的重置函数:setToOriginImpl(),把估计值置零,是平凡的

//定义曲线模型的顶点(优化变量),参数模板:优化变量维度和数据类型
class CurveFittingVertex: public g2o::BaseVertex<3,Eigen::Vector3d>
{
public:
	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
	virtual void setToOriginImpl()//重置
	{
		_estimate << 0,0,0;
	}

	virtual void oplusImpl(const double* update)//更新
	{
		_estimate += Eigen::Vector3d(update);
	}

	//存盘和读盘,留空
	virtual bool read(istream& in){}
	virtual bool write(ostream& out) const {}
};

(7)派生边CurveFittingEdge
其中要重写一个重要的虚函数:
边的误差计算函数:computeError(),这里是观测值-曲线系数拟合的值

//定义曲线模型的边(误差项) 模板参数:观测值维度,数据类型,连接顶点类型
class CurveFittingEdge: public g2o::BaseUnaryEdge<1,double,CurveFittingVertex>
{
public:
	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
	CurveFittingEdge(double x):BaseUnaryEdge(),_x(x){}
	//计算曲线模型误差
	void computeError()
	{
		const CurveFittingVertex* v = static_cast<const CurveFittingVertex*>(_vertices[0]);
		const Eigen::Vector3d abc = v->estimate();
		_error(0,0) = _measurement - std::exp(abc(0,0)*_x*_x + abc(1,0)*_x + abc(2,0));
	}

	virtual bool read(istream& in) {}
	virtual bool write(ostream& out) const {}

public:
	double _x;//x值,y值为_measurement
};

(8)配置图优化

	//构建图优化,先设定g2o
	//矩阵块,每个误差项优化变量维度为3,误差值维度为1
	typedef g2o::BlockSolver<g2o::BlockSolverTraits<3,1>> Block;
	//线性方程求解器,稠密的增量方程
	Block::LinearSolverType* linearSolver = new g2o::LinearSolverDense<Block::PoseMatrixType>();
	Block* solver_ptr = new Block(linearSolver);//矩阵块求解器
	//梯度下降方法,从GN,LM,DogLeg中选
	g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg(solver_ptr);
	//取消下面的注释,以使用GN或DogLeg
	//g2o::OptimizationAlgorithmGaussNewton* solver = new g2o::OptimizationAlgorithmGaussNewton(solver_ptr);
	//g2o::OptimizationAlgorithmDogleg* solver = new g2o::OptimizationAlgorithmDogleg(solver_ptr);

	g2o::SparseOptimizer optimizer;//图模型
	optimizer.setAlgorithm(solver);//设置求解器
	optimizer.setVerbose(true);//打开调试输出

(9)向图中填充顶点和边

	//向图中增加顶点
	CurveFittingVertex* v = new CurveFittingVertex();
	v->setEstimate(Eigen::Vector3d(0,0,0));
	v->setId(0);
	optimizer.addVertex(v);

	//向图中增加边
	for(int i=0;i<N;i++)
	{
		CurveFittingEdge* edge = new CurveFittingEdge(x_data[i]);
		edge->setId(i);
		edge->setVertex(0,v);//设置连接的顶点
		edge->setMeasurement(y_data[i]);//观测数值
		//信息矩阵:协防差矩阵的逆
		edge->setInformation(Eigen::Matrix<double,1,1>::Identity()*1/(w_sigma*w_sigma));
		optimizer.addEdge(edge);
	}

(10)执行优化

	optimizer.initializeOptimization();
	optimizer.optimize(100);

4. 完整代码

g2o_curve_fitting.cpp

//2019.08.09
#include <iostream>

#include <g2o/core/base_vertex.h>
#include <g2o/core/base_unary_edge.h>
#include <g2o/core/block_solver.h>
#include <g2o/core/optimization_algorithm_levenberg.h>
#include <g2o/core/optimization_algorithm_gauss_newton.h>
#include <g2o/core/optimization_algorithm_dogleg.h>
#include <g2o/solvers/dense/linear_solver_dense.h>

#include <Eigen/Core>

#include <opencv2/core/core.hpp>

#include <cmath>
#include <chrono>

using namespace std;




//定义曲线模型的顶点(优化变量),参数模板:优化变量维度和数据类型
class CurveFittingVertex: public g2o::BaseVertex<3,Eigen::Vector3d>
{
public:
	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
	virtual void setToOriginImpl()//重置
	{
		_estimate << 0,0,0;
	}

	virtual void oplusImpl(const double* update)//更新
	{
		_estimate += Eigen::Vector3d(update);
	}

	//存盘和读盘,留空
	virtual bool read(istream& in){}
	virtual bool write(ostream& out) const {}
};

//定义曲线模型的边(误差项) 模板参数:观测值维度,数据类型,连接顶点类型
class CurveFittingEdge: public g2o::BaseUnaryEdge<1,double,CurveFittingVertex>
{
public:
	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
	CurveFittingEdge(double x):BaseUnaryEdge(),_x(x){}
	//计算曲线模型误差
	void computeError()
	{
		const CurveFittingVertex* v = static_cast<const CurveFittingVertex*>(_vertices[0]);
		const Eigen::Vector3d abc = v->estimate();
		_error(0,0) = _measurement - std::exp(abc(0,0)*_x*_x + abc(1,0)*_x + abc(2,0));
	}

	virtual bool read(istream& in) {}
	virtual bool write(ostream& out) const {}

public:
	double _x;//x值,y值为_measurement
};




int main()
{
	double a=1, b=2.0, c=1.0;	//真实参数值
	int N = 100;			//数据点
	double w_sigma = 1.0;		//噪声Sigma(标准差)值
	cv::RNG rng;			//opencv随机数产生器
	double abc[3] = {0,0,0};	//abc参数的估计值,初始值为0,0,0
	
	vector<double> x_data,y_data;	//观测数据
	
	cout<<"generating data: "<<endl;
	
	//填充观测数据,实际应用时这个观测数据是由传感器读取的数据
	for(int i=0; i<N; i++)
	{
		double x = i/100.0;
		x_data.push_back(x);
		y_data.push_back(exp(a*x*x+b*x+c) + rng.gaussian(w_sigma));//加了高斯噪声的观测数据
		cout<<x_data[i]<<" "<<y_data[i]<<endl;
	}



	//构建图优化,先设定g2o
	//矩阵块,每个误差项优化变量维度为3,误差值维度为1
	typedef g2o::BlockSolver<g2o::BlockSolverTraits<3,1>> Block;
	//线性方程求解器,稠密的增量方程
	Block::LinearSolverType* linearSolver = new g2o::LinearSolverDense<Block::PoseMatrixType>();
	Block* solver_ptr = new Block(linearSolver);//矩阵块求解器
	//梯度下降方法,从GN,LM,DogLeg中选
	g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg(solver_ptr);
	//取消下面的注释,以使用GN或DogLeg
	//g2o::OptimizationAlgorithmGaussNewton* solver = new g2o::OptimizationAlgorithmGaussNewton(solver_ptr);
	//g2o::OptimizationAlgorithmDogleg* solver = new g2o::OptimizationAlgorithmDogleg(solver_ptr);

	g2o::SparseOptimizer optimizer;//图模型
	optimizer.setAlgorithm(solver);//设置求解器
	optimizer.setVerbose(true);//打开调试输出

	//向图中增加顶点
	CurveFittingVertex* v = new CurveFittingVertex();
	v->setEstimate(Eigen::Vector3d(0,0,0));
	v->setId(0);
	optimizer.addVertex(v);

	//向图中增加边
	for(int i=0;i<N;i++)
	{
		CurveFittingEdge* edge = new CurveFittingEdge(x_data[i]);
		edge->setId(i);
		edge->setVertex(0,v);//设置连接的顶点
		edge->setMeasurement(y_data[i]);//观测数值
		//信息矩阵:协防差矩阵的逆
		edge->setInformation(Eigen::Matrix<double,1,1>::Identity()*1/(w_sigma*w_sigma));
		optimizer.addEdge(edge);
		
	}


	//执行优化
	cout<<"start optimization"<<endl;
	chrono::steady_clock::time_point t1 = chrono::steady_clock::now();//开始计时
	optimizer.initializeOptimization();
	optimizer.optimize(100);
	chrono::steady_clock::time_point t2 = chrono::steady_clock::now();//结束计时
	chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2-t1);
	cout<<"solve time cost = "<<time_used.count()<<"seconds."<<endl;

	//输出结果
	Eigen::Vector3d abc_estimate = v->estimate();
	cout<<"estimated model:"<<abc_estimate.transpose()<<endl;


	return 0; 

}

CMakeLists.txt

cmake_minimum_required( VERSION 2.8 )
project( g2o_curve_fitting )

set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )

# 添加cmake模块以使用g20库
list( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules )

# 寻找G2O
find_package( G2O REQUIRED )
include_directories( 
    ${G2O_INCLUDE_DIRS}
    "/usr/include/eigen3"
)

# OpenCV
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_DIRS} )

add_executable( curve_fitting g2o_curve_fitting.cpp )
# 与G2O和OpenCV链接
target_link_libraries( curve_fitting 
    ${OpenCV_LIBS}
    g2o_core g2o_stuff
)

运行结果:
在这里插入图片描述在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

视觉SLAM十四讲 读书编程笔记 Chapter6 非线性优化 的相关文章

随机推荐

  • linux shell 等待输入_shell中获得用户的输入

    有时我们需要shell脚本有更多的交互性 比如我们安装某个linux命令的时候 会提示 N Y 选择安装或者放弃 这时就需要用到shell的read命令 read命令的基本使用 演示代码如下 其中 n表示字符串输出尾端不换行 还可以直接在r
  • R语言 回归诊断几种方法

    回归诊断技术提供了评价回归模型使用性的必要工具 能帮助发现并且纠正问题 有几种方法进行回归诊断 分别是标准方法 car包中的函数 gvlma函数 建议先通过gvlma函数进行验证 如果违反假设条件 再使用其他方法来判断哪些假设没有满足并进行
  • Cloud Run min实例:最小化无服务器的冷启动

    作者 Kelsey Hightower Vinod Ramachandran 无服务器最为优异的一点在于其按需付费的操作模型 可以让用户将服务规模缩减为零 但是对于某些应用程序来说 无服务器对于他们来说没那么重要的原因也在于其模型可以按需缩
  • 春秋云镜 CVE-2022-24112

    春秋云镜 CVE 2022 24112 Apache APISIX batch requests SSRF RCE 漏洞 靶标介绍 Apache Apisix是美国阿帕奇 Apache 基金会的一个云原生的微服务API网关服务 该软件基于O
  • opencv 轮廓放大_使用OpenCV和Python构建自己的车辆检测模型

    概述 你对智慧城市领域有兴趣吗 如果是的话 你会喜欢这个关于实现你自己的车辆检测系统的教程 在深入实现部分之前 我们将首先了解如何检测视频中的移动目标 我们将使用OpenCV和Python构建自动车辆检测器 介绍 关于智慧城市的理念 以及自
  • 三分钟了解腾讯云DDOS基础防护

    三分钟了解腾讯云DDOS基础防护 DDoS 基础防护应用场景包括哪些 腾讯云 DDoS 基础防护应用于攻击频率不高且攻击峰值不超过基础防护阈值的 DDoS 攻击防护场景 当攻击流量超过一定流量时 将自动启动 DDoS 清洗设备进行流量清洗
  • nettty

    https www jianshu com p e58674eb4c7a 1 Netty 异步和事件驱动 1 Netty 能够帮助搭建允许系统能够扩展到支持150000名并发用户 2 Netty 设计关键 异步 事件驱动 1 1 Java网
  • Linux下JDK1.8安装及配置

    进入Linux系统Ubuntu debian或centos安装JDK1 8并配置环境变量 通过终端在 opt目录下新建base文件夹 命令行 sudo mkdir opt base 然后进入java目录 命令行 cd opt base 到官
  • Ubuntu安装Nvidia显卡驱动

    原来没那么复杂 https linuxconfig org how to install the nvidia drivers on ubuntu 18 04 bionic beaver linux 我用的第一种方法 安装途中遇到了一个 v
  • php ajax右边悬浮购物车,一个简单的php+Ajax购物车程序代码(1/2)_PHP教程

    cart name name this gt items SESSION this gt cart name setItemQuantity Set the quantity of an item param string order co
  • 远程服务器docker配置并启动jupyter lab

    前提摘要 在远程服务器docker中存在镜像 但想通过jupyter lab来更好的可视化代码结果 首先登录远程服务器ssh 并拉取镜像 p是端口映射 22给ssh连接 jupyter lab的默认端口是8889 8888是自己的端口号 n
  • Ubuntu下OpenResty 搭建高性能服务端

    Socke 介绍 Linux Socket 编程领域为了处理大量连接请求场景 需要使用非阻塞 I O 和复用 select poll epoll 是 Linux API 提供的 I O 复用方式 自从 Linux2 6 中加入了 epoll
  • 函数式,F#都做了哪些优化?

    非函数式语言中使用函数式风格的缺点 函数式的优点 想必大家都已经非常了解了 我们来看看 一般语言使用函数式风格可能带来的问题 变量默认是可变的 为了实现不可变性 开发者只能人为的规范不去改变变量的值 没有明确的变量修改提示 容易因失误改变变
  • 手撸软件测试框架——lua版(四)

    本文简单介绍一下开发测试用例用到的几个关键接口 1 断言 在测试用例中少不了对结果进行校验 校验的方法一般称为断言 assert 也就是说 在进行一系列的操作之后 断定会出现某个确定性的结果 如果这个确定性的结果如期出现 则断言成功 被测试
  • spring读取resources文件夹下的文件报错:cannot be resolved to absolute file path because it dose not reside in th

    spring springboot读取resources文件夹下的文件报错 cannot be resolved to absolute file path because it dose not reside in the file sy
  • k8s之存储、SC、STS、DS篇(一些常见的存储方案)

    为什么要做持久化存储 在k8s中部署的应用都是以pod容器的形式运行的 假如我们部署MySQL Redis等数据库 需要对这些数据库产生的数据做备份 因为Pod是有生命周期的 如果pod不挂载数据卷 那pod被删除或重启后这些数据会随之消失
  • 关于 最新版idea(2021.3.3) 在配置XML文件时候,因为没有加上 .xml变成了文档模式的解决方式

    关于 最新版idea 2021 3 3 在配置XML文件时候 因为没有加上 xml变成了文档模式的解决方式 首先就是关于自己的这个问题 自己到各大论坛上面找了半天 发现更多的都是旧版的 而且上面解决方式根本在新版的idea中根本不能用 最后
  • Hbase分布式集群搭建

    环境准备 hbase软件包 http mirror bit edu cn apache hbase 1 3 1 hbase 1 3 1 bin tar gz 完成hadoop集群环境搭建 安装hbase 首先在hadoop master安装
  • 门函数卷积_卷积及其应用

    卷积公式的由来 卷积公式最开始来自于古典概型 如题 掷两次公平的骰子 点数之和等于8的概率 设随机变量 为第一次掷得的点数 随机变量 为第二次掷得的点数 因此不考虑点数之和等于8的条件 则有 种样本空间 根据条件 相当于给随机变量的取值限定
  • 视觉SLAM十四讲 读书编程笔记 Chapter6 非线性优化

    Chapter6 非线性优化 实践 Ceres 1 安装Ceres依赖库 2 编译安装Ceres 3 曲线拟合问题描述 4 ceres使用方法 5 完整代码 实践 g2o 1 安装g2o依赖库 2 编译安装g2o 3 g2o使用方法 4 完