实践:曲线拟合问题

我们先通过高斯牛顿法来求解最小二乘问题,然后介绍使用优化库(Ceres/g2o)来求解此问题。
例题:
考虑一条满足如下方程的曲线:
y = exp(ax2x^2x2+ bx + c) + w,其中a,b,c为曲线的参数,w是高斯噪声,满足w~(0,σ2\sigma^2σ2)。 这是个非线性模型。假设我们有N个关于x,y的观测点,想根据这些数据点来求出曲线的参数。那么可以通过求解下面的最小二乘问题来估计曲线参数:
min⁡a,b,c\min\limits_{a,b,c}a,b,cmin​ 12\frac{1}{2}21​ ∑i=1N\sum\limits_{i=1}^{N}i=1∑N​ ∥yi−exp(axi2+bxi+c)∥2\lVert y_i - exp(ax_i^2+ bx_i + c) \rVert^2∥yi​−exp(axi2​+bxi​+c)∥2
在这个问题中待估计的变量是a,b,c。
求解思路:先根据模型生成x,y的真值,然后在真值中加入高斯分布的噪声。随后,使用高斯牛顿法从带噪声的数据拟合参数模型。
误差定义:eie_iei​ = yi−exp(axi2+bxi+c)y_i - exp(ax_i^2+ bx_i + c)yi​−exp(axi2​+bxi​+c)
可以求出每个误差项对于状态变量的导数:
∂ei∂a\frac{\partial e_i}{\partial a}∂a∂ei​​ = -xi2x_i^2xi2​ exp(axi2+bxi+c)exp(ax_i^2+ bx_i + c)exp(axi2​+bxi​+c)
∂ei∂b\frac{\partial e_i}{\partial b}∂b∂ei​​ = -xix_ixi​ exp(axi2+bxi+c)exp(ax_i^2+ bx_i + c)exp(axi2​+bxi​+c)
∂ei∂c\frac{\partial e_i}{\partial c}∂c∂ei​​ = -exp(axi2+bxi+c)exp(ax_i^2+ bx_i + c)exp(axi2​+bxi​+c)
于是JiJ_iJi​ = [∂ei∂a,∂ei∂b,∂ei∂c]T[ \frac{\partial e_i}{\partial a}, \frac{\partial e_i}{\partial b},\frac{\partial e_i}{\partial c}]^T[∂a∂ei​​,∂b∂ei​​,∂c∂ei​​]T,高斯牛顿的增量方程为:

(∑i=1100Ji(σ2)−1( \sum\limits_{i=1}^{100}J_i(\sigma^2)^{-1}(i=1∑100​Ji​(σ2)−1JiTJ_i ^TJiT​) △\triangle△xkx_kxk​ = ∑i=1100−Ji(σ2)−1ei\sum\limits_{i=1}^{100}-J_i(\sigma^2)^{-1}e_ii=1∑100​−Ji​(σ2)−1ei​, 可以将所有的JiJ_iJi​排成一列,将这个方程写成矩阵的形式,不过它的含义与求和是一致的。

1.1高斯牛顿法求解最小二乘问题

#include <iostream>
#include <chrono>
#include <opencv2/opencv.hpp>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <stdio.h>using namespace std;
using namespace Eigen;int main(int argc, char **argv) {double ar = 1.0, br = 2.0, cr = 1.0;   //真实参数值double ae = 2.0, be = -1.0, ce = 5.0;  //估计参数值int N = 100; //数据点double w_sigma = 1.0;   //噪声Sigma值double inv_sigma = 1.0 / w_sigma; cv::RNG  rng;  //OpenCV随机数生成器vector<double> x_data, y_data;   //数据for(int i = 0; i < N; i++) {double x = i / 100.0;x_data.push_back(x);//rng.gaussian(a),产生均值为0,标准差为a的高斯分布的随机数y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma * w_sigma));}int iterations = 100;double cost = 0, lastcost = 0;chrono::steady_clock::time_point t1 = chrono::steady_clock::now();for(int iter = 0; iter < iterations; iter++) {Matrix3d H = Matrix3d::Zero();Vector3d b = Vector3d::Zero();cost = 0;for(int i = 0; i < N; i++) {double xi = x_data[i], yi = y_data[i];double error = yi - exp(ae * xi * xi + be * xi + ce);Vector3d J;J[0] = J[0] = -xi * xi * exp(ae * xi * xi + be * xi + ce);  // de/daJ[1] = -xi * exp(ae * xi * xi + be * xi + ce);  // de/dbJ[2] = -exp(ae * xi * xi + be * xi + ce);  // de/dcH += inv_sigma * inv_sigma * J * J.transpose();  //J和J的转置中间乘的是协方差矩阵的倒数,所以需要上式的double inv_sigma = 1.0 / w_sigma;b += -inv_sigma * inv_sigma * error * J;cost += error * error;}Vector3d dx = H.ldlt().solve(b);//isnan()函数判断输入是否为非数字,是非数字为真is not a numberif(isnan(dx[0])) {cout << "reuslt is nan!" << endl;break;}if(iter > 0 && cost >= lastcost) {cout << "cost " << cost << ">= last cost :" << lastcost << "break;" << endl;break;}ae += dx[0];be += dx[1];ce += dx[2];lastcost = cost;cout << "\033[1;32m total cost :" << cost << "\t\tupdate:" << dx.transpose() << "\t\testimated parmas:" << ae << "," << be << "," << ce << "\033[0m" << endl;}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 << "estimated abd=" << ae << "," << be << "," << ce << endl;return 0;}

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(ch6_1)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++14 -O3")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# OpenCV
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# Eigen
include_directories("/usr/include/eigen3")add_executable(gaussNewton gaussNewton.cpp)
target_link_libraries(gaussNewton ${OpenCV_LIBS})

运行结果:


由此结果可见:目标函数接近9此迭代后残差的结果不再发生变化趋近于收敛,更新量趋近于0,最终真实值1,2,3,与估计置0.890912,2.1719,0.943629接近。优化用时大约0.3毫秒。
补充: sudo apt-get install libpcl-dev pcl-tools 用来查看点云文件,通过一个窗口将其画出。

1.2 使用Ceres进行曲线拟合
1.2.1 Ceres简介
来自谷歌的Ceres库是一个广泛使用的用于求解最小二乘问题的求解库。在Ceres中,我们只需按照一定的步骤定义待解的优化问题,然后交给求解器来计算。
Ceres求解最小二乘问题最一般的形式如下:
min⁡x\min\limits_{x}xmin​ 12\frac{1}{2}21​ ∑i\sum\limits_{i}i∑​ ρi\rho_iρi​ (∥fi(xi,1......xi,n∥2\lVert f_i(x_{i,1}......x_{i,n}\rVert^2∥fi​(xi,1​......xi,n​∥2)
s.t ljl_jlj​ ≤\leq≤ xjx_jxj​ ≤\leq≤ uju_juj​
在这个形式中,x1x_1x1​, …,xnx_nxn​为优化变量,又称为参数块(Parameter blocks),fif_ifi​称为代价函数(Cost function), 也称为残差(Residual blocks),在SLAM中可以理解为残差项。ljl_jlj​,uju_juj​为第j个待优化变量的上限和下限。取ljl_jlj​ = -∞\infty∞,uju_juj​ = ∞\infty∞(不限制优化变量的边界),此时,目标函数由许多个项的平方项经过一个核函数ρ\rhoρ(.)之后求和组成。同样,可以取ρ\rhoρ为恒等函数,那么目标函数就是由许多个平方项的和组成,就得到了一个无约束的最小二乘问题。
Cereos求解此类问题时,我们需要做的有如下任务:

  • 定义每个参数块。参数块通常为向量,但是在SLAM里也可以定义为四元数,李代数这种特殊的结构。如果是向量,我们需要为每个参数块分配一个double数组来存储变量的值。
  • 定义残差块的计算方式。残差块通常关联若干个参数块,对他们进行一些自定义的计算,然后返回残差值。Ceres对他们求平方和之后,作为目标函数的值
  • 残差块也需要定义雅可比的计算方式。在Ceres中可以用这个库提供的自动求导功能;也可以手动指定雅可比的计算过程。如果使用自动求导,那么残差块需按照特定的书写;残差的计算应该是一个带模板的括号运算符。
  • f把所有的参数块和残差加入到Ceres定义的Problem对象中,调用Solve函数求解。求解之前,可以传入一些配置信息,例如:迭代次数,终止条件等,也可以使用默认的配置。

首先安装Cereos库,先安装相关的依赖,然后编译安装即可(略)

1.2.2 上例,用Ceres拟合曲线

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <ceres/ceres.h>
#include <chrono>using namespace std;
using namespace Eigen;//第一部分:构建代价函数,重载()符号
struct CURVE_FITTING_COST  {CURVE_FITTING_COST(double x, double y) : _x(x), _y(y) {}// 残差的计算template<typename T>bool operator()(const T *const abc, T *residual) const {residual[0] = T(_y) - ceres::exp(abc[0] * T(_x) * T(_x) + abc[1] * T(_x) + abc[2]); // y-exp(ax^2+bx+c)return true;}const double _x, _y;
};int main(int argc, char **argv)
{double ar = 1.0, br = 2.0, cr = 1.0;         // 真实参数值double ae = 2.0, be = -1.0, ce = 5.0;        // 估计参数值int N = 100;                                 // 数据点double w_sigma = 1.0;                        // 噪声Sigma值double inv_sigma = 1.0 / w_sigma;cv::RNG rng;                                 // OpenCV随机数产生器vector<double> x_data, y_data;      // 数据for (int i = 0; i < N; i++) {double x = i / 100.0;x_data.push_back(x);y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma * w_sigma));}//寻找待优化参数的初始值double abc[3] = {ae, be, ce};//第二步:构建优化问题ceres::Problem problem;for(int i = 0; i < N; i++){//使用自动求导,将之前的代价函数结构体传入,第一个是输出的维度,即残差的维度,第二个是输入的维度,即待优化变量的维度。//double abc[3] = {ae, be, ce};参数块//new CURVE_FITTING_COST(x_data[i], y_data[i]);对于残差块,我们对每一个数据构造CURVE_FITTING_COST 对象,然后调用AddResidualBlock()将误差项添加到目标函数中//优化需要梯度,方式:1,Ceres自动求导  2,数值求导  3,自行推导导数形式//自动求导需要指定误差项和优化变量的维度。于是在自动求道的模板参数中指定了维度。ceres::CostFunction * cost_function = new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3>(new CURVE_FITTING_COST(x_data[i], y_data[i]));//设定问题,向问题中添加误差项problem.AddResidualBlock(cost_function,nullptr,  //核函数,不使用abc);  //待估计的参数}//第四步:配置并运行求解器(可以选择使用Line Search还是Trust Region,迭代次数,步长等)ceres::Solver::Options options;options.linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY; //配置增量方程的解法options.minimizer_progress_to_stdout = true;  //输出到coutceres::Solver::Summary summary;//优化信息chrono::steady_clock::time_point t1 = chrono::steady_clock::now();//设置好问题后,调用Solve求解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 << "estimatde a, b, c = ";for(auto a:abc) cout << a << "\t" << "";  //最终结果cout << endl;return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(ch6_1)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++14 -O3")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# OpenCV
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# Ceres
find_package(Ceres REQUIRED)
include_directories(${CERES_INCLUDE_DIRS})
# Eigen
include_directories("/usr/include/eigen3")
add_executable(ceresCurveFitting ceresCurveFitting.cpp)
target_link_libraries(ceresCurveFitting ${OpenCV_LIBS} ${CERES_LIBRARIES})

编译过程中出现的问题:

代价函数模型中的括号运算符重载中形参缺少const 。
实现结果:

最终的优化结果与高斯牛顿法的结果基本相同。运行速度上比高斯牛顿法慢一点点,与书本上的慢六倍有出入。

1.2.3 代码部分难点剖析:
(1)构造代价函数结构体

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

使用了仿函数(拟函数)技巧,在CURVE_FITTING_COST结构体内,对()进行了重载,这样,该结构体的一个实例就能具有类似一个函数的性质。
(2)通过代价函数构建待优化求解的优化问题

ceres::CostFunction * cost_function = new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3>(new CURVE_FITTING_COST(x_data[i], y_data[i]));//设定问题,向问题中添加误差项
problem.AddResidualBlock(cost_function,nullptr,  //核函数,不使用abc);  //待估计的参数

通过new了一个costFunction结构体,使用自动求导,将之前的代价函数结构体传入。然后,向问题中添加误差项。
(3)数值求导
使用NumericDiffCostFunction,模板参数设置输入输出维数前面加一个模板参数ceres::CENTRAL,表明使用的是数值求导。
(4)鲁棒曲线拟合
在解优化问题中(拟合曲线),数据中往往会有离群点,错误值。最终得到的寻优结果很受影响,此时可以使用一些损失函数来对离群点的影响消除。要使用核函数,只需要把NULL/nullptr环城损失函数结构体的实例。
ceres库提供的核函数有TrivialLoss 、HuberLoss、 SoftLOneLoss 、 CauchyLoss。
比如此时要使用CauchyLoss,只需要将nullptr换成new CauchyLoss()即可。 参考:http://www.ceres-solver.org/installation.html

1.3 使用g2o进行曲线拟合
g2o(General Graphic Optimization, G2G^2G2O是在SLAM广为使用的优化库,它是一个基于图优化的库,也是一个通用的图优化库,通用意味着只要是能表示为图优化的最求二乘问题在g2o里都可以求解。图优化是一种将非线性优化和图论结合起来的理论。因此,我们先来介绍图优化。
1.3.1 图优化
求解非线性问题中,目标函数仅描述了优化变量和许多个误差项,但我们不清楚他们之间的关联。我们希望直观的看到它们之间的关系,引入图优化
图优化:就是优化问题变成图的一种方式。一个图由若干个顶点(Vertex),以及连接这些顶点的连边组成(Edge)。顶点表示优化变量,边表示误差项。所以对任意一个上述形式的非线性最小二乘问题,我们构建与之对应的图(贝叶斯图\因子图)。

1.3.2 使用g2o拟合曲线
编译和安装g2o库。
为了使用g2o,首先要将曲线拟合问题抽象为图优化问题。节点为优化变量,边为误差项。


图中,我们只有一元边(超边Hyper Edge),一个顶点的图(超图),事实上,图优化中,一条边可以连一个,多个顶点,取决与每个误差与多少个优化变量有关。
有了图模型后,接下来就是在g2o中建立该模型进行优化。
步骤如下:
(1)定义顶点和边的类型;
(2)构建图;
(3)选择优化算法;
(4)调用g2o进行优化,返回结果;

#include <iostream>
#include <g2o/core/g2o_core_api.h>
#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() override {_estimate << 0, 0, 0;}// 顶点更新函数virtual void oplusImpl(const double *update) override {_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_NEWCurveFittingEdge(double x) : BaseUnaryEdge(), _x(x) {}// 计算曲线模型误差virtual void computeError() override {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 void linearizeOplus() override {const CurveFittingVertex *v = static_cast<const CurveFittingVertex *> (_vertices[0]);const Eigen::Vector3d abc = v->estimate();double y = exp(abc[0] * _x * _x + abc[1] * _x + abc[2]);_jacobianOplusXi[0] = -_x * _x * y;_jacobianOplusXi[1] = -_x * y;_jacobianOplusXi[2] = -y;}virtual bool read(istream &in) {}virtual bool write(ostream &out) const {}public:double _x;  // x 值, y 值为 _measurement
};int main(int argc, char **argv) {double ar = 1.0, br = 2.0, cr = 1.0;         // 真实参数值double ae = 2.0, be = -1.0, ce = 5.0;        // 估计参数值int N = 100;                                 // 数据点double w_sigma = 1.0;                        // 噪声Sigma值double inv_sigma = 1.0 / w_sigma;cv::RNG rng;                                 // OpenCV随机数产生器vector<double> x_data, y_data;      // 数据for (int i = 0; i < N; i++) {double x = i / 100.0;x_data.push_back(x);y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma * w_sigma));}// 构建图优化,先设定g2otypedef g2o::BlockSolver<g2o::BlockSolverTraits<3, 1>> BlockSolverType;  // 每个误差项优化变量维度为3,误差值维度为1typedef g2o::LinearSolverDense<BlockSolverType::PoseMatrixType> LinearSolverType; // 线性求解器类型// 梯度下降方法,可以从GN, LM, DogLeg 中选auto solver = new g2o::OptimizationAlgorithmGaussNewton(g2o::make_unique<BlockSolverType>(g2o::make_unique<LinearSolverType>()));g2o::SparseOptimizer optimizer;     // 优化器optimizer.setAlgorithm(solver);   // 需要传递一个优化算法求解器optimizer.setVerbose(true);       // 打开调试输出// 往图中增加顶点CurveFittingVertex *v = new CurveFittingVertex();v->setEstimate(Eigen::Vector3d(ae, be, ce));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(10);  //开始优化,需要迭代次数chrono::steady_clock::time_point t2 = chrono::steady_clock::now();chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);cout << "\033[1;49m solve time cost = " << time_used.count() << " seconds. \033[0m" << endl;// 输出优化值Eigen::Vector3d abc_estimate = v->estimate();cout << "\033[1;32m estimated model: " << abc_estimate.transpose() << "\033[0m" << endl;return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(ch6_1)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++14 -O3")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# OpenCV
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# g2o
find_package(G2O REQUIRED)
include_directories(${G2O_INCLUDE_DIRS})
# Eigen
include_directories("/usr/include/eigen3")
add_executable(g2oCurveFitting g2oCurveFitting.cpp)
target_link_libraries(g2oCurveFitting ${OpenCV_LIBS} g2o_core g2o_stuff)

我们从g2o派生出了用于曲线拟合的顶点和边,在派生类中我们重写了重要的虚函数:
(1)顶点的更新函数:oplusImpl(),优化过程中最重要的是增量,而该函数使用了xk+1x_{k+1}xk+1​ = xkx_kxk​ + △\triangle△xxx的过程。在曲线拟合过程中,曲线参数本就位于向量空间中,这个更新确实要用加法。但是,当优化变量不在向量空间中时,如xxx是相机的位姿,据需要重新定义增量如何加到现有的估计上的行为了。(用左乘或右乘更新量进行更新)。
(2)顶点的重置函数:setToOriginImpl().把估计值置0
(3)边的误差计算函数:computeError .该函数需要取出边所连接的顶点的当前估计值,根据曲线模型,与它的观测值比较。
(4)边的雅可比计算函数:linearizeOplus() 这个函数我们计算了每条边相对于雅可比。
(5)存盘和读盘:read, write.

从结果来看:与Ceres和高斯牛顿法时间相差无几,运行速度看:手写快于g2o块于Ceres,往往通用性和高效性矛盾。

1.4 g2o图优化框架

粗浅的理解:OptimizationWithHessian内部包含一个求解器(Solver), 这个Solver是由一个BlockSolver组成的。BlockSolver由两部分组成,一是SparseBlockMatrix用于计算稀疏J和H矩阵;一是线性方程的求解器(LinearSolver),它用于计算迭代过程中最关键的一步HΔ\DeltaΔx = -b.

曲线拟合(高斯牛顿法,Ceres,g2o)相关推荐

  1. Ceres 库:基础使用,以手写高斯-牛顿法为例

    Ceres 库 简介 Ceres库为Google开发的开源C++非线性优化库,被广泛使用于求解最小二乘问题. Ceres库的Github主页如下: 安装 首先,下载Cere的源码: git clone ...

  2. C++手写高斯牛顿法

    因为毕设要用Bundle Adjustment 进行数据优化,涉及到优化方法的实现,所以想训练一下编程实现的能力.这篇文章也是参考SLAM14讲,通过高斯牛顿法来完成曲线拟合,从而熟悉高斯牛顿法流程和 ...

  3. 牛顿法和高斯牛顿法对比

    文章目录 一.非线性最小二乘 一.牛顿法 二.高斯牛顿法 三.列文伯格-马夸尔特法(LM) 四.ceres求解优化问题 一.非线性最小二乘 考虑最小二乘函数F(x), 其等于: 通过求F(x)导数为零 ...

  4. 【slam十四讲第二版】【课本例题代码向】【第七讲~视觉里程计Ⅱ】【使用LK光流(cv)】【高斯牛顿法实现单层光流和多层光流】【实现单层直接法和多层直接法】

    [slam十四讲第二版][课本例题代码向][第七讲~视觉里程计Ⅱ][使用LK光流(cv)][高斯牛顿法实现单层光流和多层光流][实现单层直接法和多层直接法] 0 前言 1 使用LK光流(cv) 1.1 ...

  5. 最小均方误差的推导+最小二乘法、梯度下降法、牛顿法、高斯牛顿法

    最小二乘法(又称最小平方法)是一种数学优化技术.它通过最小化误差的平方和寻找数据的最佳函数匹配.利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小.最小二乘法 ...

  6. SLAM从0到1——状态估计之最小二乘问题解法:最速下降法、牛顿法、高斯牛顿法、LM法...

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 学习3D视觉核心技术,扫描查看介绍,3天内无条件退款 圈里有高质量教程资料.可答疑解惑.助你高效解决问 ...

  7. 高斯牛顿法在具体工程中的应用——C++版

    高斯牛顿法在具体工程中的应用--C++版 说明:本文章没有用大量篇幅来讲述高斯牛顿的原理和数学中的应用,而是用具体的代码来说明,具体是怎么应用的. 如果对高斯牛顿法的原理比较感兴趣,可以阅读以下链接中 ...

  8. 牛顿法、梯度下降法、高斯牛顿法、Levenberg-Marquardt算法

    何为梯度? 一般解释: f(x)在x0的梯度:就是f(x)变化最快的方向 举个例子,f()是一座山,站在半山腰, 往x方向走1米,高度上升0.4米,也就是说x方向上的偏导是 0.4 往y方向走1米,高 ...

  9. 漫步最优化三十四——高斯-牛顿法

    你的温柔像羽毛,\textbf{你的温柔像羽毛,} 秘密躺在我怀抱.\textbf{秘密躺在我怀抱.} 你的微笑像拥抱,\textbf{你的微笑像拥抱,} 只有我能看到.\textbf{只有我能看到. ...

最新文章

  1. 【Vue】ElementUI el-select 下拉分页加载数据,并支持搜索功能(此处不支持分页)
  2. freemark静态页面中文乱码
  3. tensorflow gpu python3.5_Win10+Anaconda3下tensorflow-gpu环境配置
  4. UiPickerView基本使用方法
  5. php申请系统,PHP+MYSQL的文章管理系统(一)_php
  6. Python序列化的使用(有少量修改)
  7. [LeetCode] Convert Sorted Array to Binary Search Tree
  8. saltstack之混合匹配
  9. python列表转集合_Python数据类型 列表、元组、集合、字典的区别和相互转换
  10. 局域网管理工具_局域网vnc远程控制软件,七款免费又好用的局域网vnc远程控制...
  11. Pytorch 微积分
  12. oracle四大索引类型,各种Oracle索引类型介绍
  13. MCSA Windows Server 2016 Complete Study Guide, 2nd Edition 免积分下载
  14. #读源码+论文# 三维点云分割Deep Learning Based Semantic Labelling of 3D Point Cloud in Visual SLAM
  15. 经典圣诞老人题----同步与互斥
  16. String intern方法
  17. 认识电信产品生命周期管理PLM及其PLM服务
  18. 迭代式开发使用方法总结
  19. C语言入门之【C语言 “ 函数 “】
  20. ARMv8-AArch64简述

热门文章

  1. 手绘计算机比赛海报,手绘海报大赛专题计划.doc
  2. html抽奖怎么重置,js实现抽奖的两种方法
  3. SourceInsight只添加特定类型文件的方法
  4. java毕业设计校园新闻网站mybatis+源码+调试部署+系统+数据库+lw
  5. 用PE修复NTLDR is missing
  6. 云计算DNS的优缺点
  7. VNCTF2023-misc方向wp
  8. android像360一样跳转到系统菜单,Android开源库-仿360手机助手底部动画菜单布局
  9. QB64 -- C++编写的语言
  10. 互联网里的人生百态:四大互联网人群五大人生阶段