视觉SLAM十四讲作业练习(4)非线性优化部分

  • Post author:
  • Post category:其他




.5非线性优化



(0)回顾 SLAM问题的数学表达

定义:x:表示自身位置,x1,x2 … xk

​ y: 表示地图的路标,y1, y2 … yn

运动:从 k-1 时刻到 k 时刻,位置 x 如何变化,因此,运动方程即 k 时刻的位置需要借助 k-1 时刻的位置与输入uk确定。即:





x

k

=

f

(

x

k

1

,

u

k

,

w

k

)

x_k=f(x_{k-1},u_k,w_k)







x










k




















=








f


(



x











k





1



















,





u










k


















,





w










k


















)







其中uk为输入,wk为该过程中加入的噪声。

观测:k 时刻在 xk 处探测到某个路标 yj,产生的观测数据为zkj。即:





z

k

,

j

=

h

(

y

j

,

x

k

,

v

k

,

j

)

z_{k,j}=h(y_j,x_k,v_{k,j})







z











k


,


j





















=








h


(



y










j


















,





x










k


















,





v











k


,


j



















)







最基本的SLAM问题:已知运动测量读数 u 以及传感器读数 z 时,如何求解定位问题(估计 x )和建图问题(估计 y )。此时便将SLAM问题建模成了一个状态估计问题。



(1)状态估计问题



1)最大似然估计

运动方程与观测方程亦可以写成:





{

x

k

=

f

(

x

k

1

,

u

k

)

+

w

k

z

k

,

j

=

h

(

y

j

,

x

k

)

+

v

k

,

j

\begin{cases} x_k=f(x_{k-1},u_k)+w_k\\ z_{k,j}=h(y_j,x_k)+v_{k,j}\\ \end{cases}








{















x










k




















=




f


(



x











k





1



















,





u










k


















)




+





w










k

























z











k


,


j





















=




h


(



y










j


















,





x










k


















)




+





v











k


,


j












































设两个噪声项均满足零均值的高斯分布(正态分布),即:





w

k

N

(

0

,

R

k

)

,

v

k

N

(

0

,

Q

k

,

j

)

w_k \sim N(0,R_k) ,v_k \sim N(0,Q_{k,j})







w










k





























N


(


0


,





R










k


















)


,





v










k





























N


(


0


,





Q











k


,


j



















)







其中,R,Q为协方差矩阵。



a)整体的分析

定义:所有时刻的机器人位姿和路标点坐标为





x

=

{

x

1

,

x

2

,

x

k

}

,

y

=

{

y

1

,

y

2

,

y

N

}

x=\left\{\begin{matrix}x_1,x_2,\cdots x_k\end{matrix}\right\},y=\left\{\begin{matrix}y_1,y_2,\cdots y_N\end{matrix}\right\}






x




=










{















x










1


















,





x










2


















,










x










k




































}






,




y




=










{















y










1


















,





y










2


















,










y










N




































}









u表示所有时刻的输入,z表示所有时刻的观测数据。在已知输入数据u和观测数据z的条件下,求x,y的条件概率分布:





P

(

x

,

y

z

,

u

)

P(x,y|z,u)






P


(


x


,




y





z


,




u


)







应用贝叶斯法则:





P

(

x

,

y

z

,

u

)

=

P

(

z

,

u

x

,

y

)

P

(

x

,

y

)

P

(

z

,

u

)

P(x,y|z,u)=\frac{P(z,u|x,y)P(x,y)}{P(z,u)}






P


(


x


,




y





z


,




u


)




=



















P


(


z


,




u


)














P


(


z


,




u





x


,




y


)


P


(


x


,




y


)

























首先明确,x与y均是假设的位置,并不一定就在那里

例:机器人从



x

i

x_i







x










i





















位置,想到



x

i

+

1

x_{i+1}







x











i


+


1






















位置

原因:机器人在



x

i

x_i







x










i





















这个位置,此处有



y

j

y_j







y










j





















路标。

结果:因为在



x

i

x_i







x










i





















这个位置,机器人通过传感器测得了观测数据



z

i

z_i







z










i





















,同时为接下来的运动输入了



u

u






u




(1)



P

(

x

,

y

z

,

u

)

P(x,y|z,u)






P


(


x


,




y





z


,




u


)





:后验概率,知道结果推原因。看到正确的观测结果,对位置的看法(是否在这个位置)。

(2)



P

(

x

,

y

)

P(x,y)






P


(


x


,




y


)





:先验概率,知道原因推结果,在这个位置(假设)的概率(没有新证据之前)。

(3)



P

(

z

,

u

x

,

y

)

P(z,u|x,y)






P


(


z


,




u





x


,




y


)





:似然概率,在这个位置(假设)符合观测结果(证据)的比例

因为无法求后验分布,所以选择求一个状态最优估计,使得在该状态下后验概率最大化:





(

x

,

y

)

M

A

P

=

a

r

g
  

m

a

x
  

P

(

x

,

y

z

,

u

)

=

a

r

g
  

m

a

x
  

P

(

z

,

u

x

,

y

)

P

(

x

,

y

)

(x,y)^*_{MAP}=arg\;max\;P(x,y|z,u) = arg\;max\;P(z,u|x,y)P(x,y)






(


x


,




y



)











M


A


P






























=








a


r


g




m


a


x




P


(


x


,




y





z


,




u


)




=








a


r


g




m


a


x




P


(


z


,




u





x


,




y


)


P


(


x


,




y


)







由贝叶斯法则,得求解最大后验概率等价于最大化似然和先验的乘积。但是我们不知道机器人位姿与路标,所以缺失先验,因此求解最大似然估计(MLE):





(

x

,

y

)

M

L

E

=

a

r

g
  

m

a

x
  

P

(

z

,

u

x

,

y

)

(x,y)^*_{MLE}= arg\;max\;P(z,u|x,y)






(


x


,




y



)











M


L


E






























=








a


r


g




m


a


x




P


(


z


,




u





x


,




y


)







至此,分析的都是所有时刻的x,y,u,z。



b)多维向量的正态分布模型

针对向量的正态分布称为多变量正态分布



x

N

(

μ

,

Σ

)

x\sim N(\mu,\Sigma)






x













N


(


μ


,




Σ


)





, 其概率密度函数为:





p

(

x

)

=

d

e

t

(

2

π

Σ

)

1

2

e

1

2

(

x

μ

)

T

Σ

1

(

x

μ

)

p(x)=det(2\pi\Sigma)^{-\frac{1}{2}}e^{-\frac{1}{2}(x-\mu)^T\Sigma^{-1}(x-\mu)}






p


(


x


)




=








d


e


t


(


2


π


Σ



)


























2
















1






























e


























2
















1





















(


x





μ



)










T










Σ














1










(


x





μ


)















其中,μ为均值向量,Σ是一个半正定的对称矩阵称为协方差矩阵

covariance matrix

取负对数,得:





ln

(

p

(

x

)

)

=

1

2

ln

(

2

π
  

d

e

t

(

Σ

)

)

+

1

2

(

x

μ

)

T

Σ

1

(

x

μ

)

-\ln(p(x)) = \frac{1}{2}\ln(2\pi\;det(\Sigma))+\frac{1}{2}(x-\mu)^T\Sigma^{-1}(x-\mu)











ln


(


p


(


x


)


)




=



















2














1






















ln


(


2


π




d


e


t


(


Σ


)


)




+



















2














1




















(


x













μ



)










T










Σ














1










(


x













μ


)







即将求最大似然转化为求最小化后面的二次型。



2)最小二乘问题



c)局部分析

整体的x,y是由无数个独立的xi,yk构成的





P

(

z

,

u

x

,

y

)

=

P

(

u

k

x

k

1

,

x

k

)

P

(

z

k

,

j

x

k

,

y

i

)

P(z,u|x,y)=\prod P(u_k|x_{k-1},x_k)\prod P(z_{k,j}|x_k,y_i)






P


(


z


,




u





x


,




y


)




=













P


(



u










k






















x











k





1



















,





x










k


















)









P


(



z











k


,


j























x










k


















,





y










i


















)







此处的公式不知如何得到。





w

k

N

(

0

,

R

k

)

,

v

k

N

(

0

,

Q

k

,

j

)

w_k \sim N(0,R_k) ,v_k \sim N(0,Q_{k,j})







w










k





























N


(


0


,





R










k


















)


,





v










k





























N


(


0


,





Q











k


,


j



















)





,得



x

k

N

(

f

(

x

k

1

,

u

k

)

,

R

k

)

x_k \sim N(f(x_{k-1},u_k),R_k)







x










k





























N


(


f


(



x











k





1



















,





u










k


















)


,





R










k


















)









z

j

,

k

N

(

h

(

y

j

,

x

k

)

,

Q

k

,

j

)

z_{j,k} \sim N(h(y_j,x_k),Q_{k,j})







z











j


,


k






























N


(


h


(



y










j


















,





x










k


















)


,





Q











k


,


j



















)




且令:





e

u

,

k

=

x

k

f

(

x

k

1

,

u

k

)

e_{u,k}=x_k-f(x_{k-1},u_k)







e











u


,


k





















=









x










k





























f


(



x











k





1



















,





u










k


















)









e

z

,

j

,

k

=

z

k

,

j

h

(

x

k

,

y

i

)

e_{z,j,k}=z_{k,j}-h(x_k,y_i)







e











z


,


j


,


k





















=









z











k


,


j






























h


(



x










k


















,





y










i


















)





可以求得:





m

i

n
  

J

(

x

,

y

)

=

e

u

,

k

T

R

k

1

e

u

,

k

+

e

z

,

j

,

k

T

Q

k

,

j

1

e

z

,

j

,

k

min \;J(x,y)= \sum e_{u,k}^TR_k^{-1}e_{u,k}+\sum \sum e_{z,j,k}^TQ_{k,j}^{-1}e_{z,j,k}






m


i


n




J


(


x


,




y


)




=














e











u


,


k









T



















R










k












1




















e











u


,


k





















+



















e











z


,


j


,


k









T



















Q











k


,


j













1




















e











z


,


j


,


k
























(2)非线性最小二乘



牛顿法:

增量方程:





Δ

x

=

a

r

g
  

m

i

n

(

F

(

x

)

+

J

(

x

)

T

Δ

x

+

1

2

Δ

x

T

H

Δ

x

)

\Delta x^*=arg \; min(F(x)+J(x)^T\Delta x+\frac{1}{2}\Delta x^TH\Delta x)






Δ



x






















=








a


r


g




m


i


n


(


F


(


x


)




+








J


(


x



)










T









Δ


x




+



















2














1




















Δ



x










T









H


Δ


x


)







求导:





J

+

H

Δ

x

=

0

J+H\Delta x = 0






J




+








H


Δ


x




=








0







高斯牛顿法:





J

(

x

)

J

T

(

x

)

Δ

x

=

J

(

x

)

f

(

x

)

J(x)J^T(x)\Delta x=-J(x)f(x)






J


(


x


)



J










T









(


x


)


Δ


x




=











J


(


x


)


f


(


x


)





高斯牛顿法的增量方程(程序中应用的):





(

i

=

1

100

J

i

1

J

i

T

)

Δ

x

k

=

i

=

1

100

J

i

1

e

i

(\sum_{i=1}^{100}J_i^{-1}J_i^T)\Delta x_k = \sum_{i=1}^{100}-J_i^{-1}e_i






(











i


=


1



















1


0


0





















J










i












1




















J










i








T


















)


Δ



x










k




















=

















i


=


1



















1


0


0
























J










i












1




















e










i























6.曲线拟合实验



(1)手写高斯牛顿

#include <iostream>
#include <opencv2/opencv.hpp>
#include <Eigen/Core>
#include <Eigen/Dense>

using namespace std;
using namespace Eigen;

int main(int argc, char **argv) {
    // 1.定义真实的参数值与估计值
    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值
    cv::RNG rng;                                 // OpenCV随机数产生器

    // 2.生成一系列的x与y,并将其放入容器中
    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));
    }

    // 3.开始Gauss-Newton迭代
    int iterations = 100;    // 迭代次数
    double cost = 0, lastCost = 0;  // 本次迭代的cost和上一次迭代的cost

    for (int iter = 0; iter < iterations; iter++) {
        //3.1明确目标,求出H与b
        Matrix3d H = Matrix3d::Zero();             // Hessian = J^T J in Gauss-Newton
        Vector3d b = Vector3d::Zero();             // bias
        cost = 0;
        //3.2根据高斯牛顿算法求
        //每一次迭代的x,y都不同因为ae,be,ce不同
        for (int i = 0; i < N; i++) {
            double xi = x_data[i], yi = y_data[i];  // 第i个数据点
            // start your code here
            double error = 0;   // 第i个数据点的计算误差
            double ye = (exp(ae*xi*xi + be*xi +ce));
            error = yi - ye; // 填写计算error的表达式 真实值的减去估计值
            Vector3d J; // 雅可比矩阵
            J[0] = -xi*xi*ye;  // de/da
            J[1] = -xi*ye;  // de/db
            J[2] = -ye;  // de/dc

            //H与b都是一个整体的概念
            H += J * J.transpose(); // GN近似的H
            b += -error * J;
            // end your code here

            cost += error * error;
        }

        // 求解线性方程 Hx=b,建议用ldlt
 	// start your code here
        Vector3d dx;
        dx = H.ldlt().solve(b);
		if (dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2] < 0.00001) {
			cout << "Not much improvement. Stop here." << endl;
			break;
		}
	// end your code here

        if (isnan(dx[0])) {
            cout << "result is nan!" << endl;
            break;
        }

        if (iter > 0 && cost > lastCost) {
            // 误差增长了,说明近似的不够好
            cout << "cost: " << cost << ", last cost: " << lastCost << endl;
            break;
        }

        // 3.3迭代的目的是更新abc估计值
        ae += dx[0];
        be += dx[1];
        ce += dx[2];

        lastCost = cost;

        cout << "total cost: " << cost << endl;
    }

    cout << "estimated abc = " << ae << ", " << be << ", " << ce << endl;
    return 0;
}



(2)使用Ceres库

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

using namespace std;

// 1.构建代价函数
struct CURVE_FITTING_COST {
    CURVE_FITTING_COST(double x, double y) : _x(x), _y(y) {}
    // 残差的计算 --- abc: 模型参数, 有3维   residual: 残差
    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;    // x,y数据
};

int main(int argc, char **argv) {
    double a = 1.0, 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参数的估计值

    vector<double> x_data, y_data;        // 数据

    cout << "generating data: " << endl;
    for (int i = 0; i < N; i++) {
        double x = i / 100.0;             // 其实可以改为double x=i/double(N)
        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;
    }

    // 2.构建最小二乘问题
    ceres::Problem problem;
    for (int i = 0; i < N; i++) {
        problem.AddResidualBlock(     // 向问题中添加误差项
            // 使用自动求导, 模板参数:误差类型, 输出维度, 输入维度(待估计参数维度), 维数要与前面struct中一致
            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.max_num_iterations = 100;
    options.linear_solver_type = ceres::DENSE_QR;  // 增量方程如何求解
    options.minimizer_progress_to_stdout = true;   // 输出到cout

    ceres::Solver::Summary summary;                // 优化信息
    ceres::Solve(options, &problem, &summary);     // 开始优化 求解

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

    return 0;
}

总结:

​ (1)进一步理解了slam问题的数学表达,引入状态估计,进而引出最小二乘法。

​ (2)主要了解求解最小二乘问题的方法:高斯牛顿法

​ (3)通过手动编写高斯牛顿法的求解程序简单了解运行步骤,简单了解优化库Ceres的使用。

​ (4)不足之处:需要补充一些C++的知识,主要是STL。



版权声明:本文为ASUNAchan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。