https://www.cnblogs.com/CaptainLL/p/10831072.html


//
// Created by 高翔 on 2017/12/15.
//
#include <iostream>
#include <opencv2/opencv.hpp>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <math.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; // 噪声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));
}
// 开始Gauss-Newton迭代
int iterations = 100; // 迭代次数
double cost = 0, lastCost = 0; // 本次迭代的cost和上一次迭代的cost
for (int iter = 0; iter < iterations; iter++) {
Matrix3d H = Matrix3d::Zero(); // Hessian = J^T J in Gauss-Newton
Vector3d b = Vector3d::Zero(); // bias
cost = 0;
for (int i = 0; i < N; i++) {
double xi = x_data[i], yi = y_data[i]; // 第i个数据点
// start your code here
double ex=exp(ae*xi*xi+be*xi+ce);
double error = 0; // 第i个数据点的计算误差
error = yi-ex; // 填写计算error的表达式
Vector3d J; // 雅可比矩阵
J[0] = -xi*xi*ex; // de/da
J[1] = -xi*ex; // de/db
J[2] = -ex; // de/dc
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);
// 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;
}
// 更新abc估计值
ae += dx[0];
be += dx[1];
ce += dx[2];
lastCost = cost;
cout<<"iterate: "<<iter<<endl;
cout<<"ae="<<ae<<" be="<<be<<" ce="<<ce<<endl;
cout<<"cost= "<<cost<<endl;
// end your code here
cout << "total cost: " << cost << endl;
}
cout << "estimated abc = " << ae << ", " << be << ", " << ce << endl;
return 0;
}
代码如上。
自写部分:
double ex=exp(ae*xi*xi+be*xi+ce);
double error = 0; // 第i个数据点的计算误差
error = yi-ex; // 填写计算error的表达式
Vector3d J; // 雅可比矩阵
J[0] = -xi*xi*ex; // de/da
J[1] = -xi*ex; // de/db
J[2] = -ex; // de/dc
H += J * J.transpose(); // GN近似的H
b += -error * J;
然后解个方程
自己开始推了一大堆后来发现按照提示写很简单就完了。
需要注意的地方:
每一个点引入一个error,在内层循环中只计算了单个error的J矩阵,并且不考虑外面的平方。内层就是f(x),所以形式都很简单,然后在外层把算出来的H和b加起来最后统一求解,这里的加法原理是讲得通的。
一开始我解得欲仙欲死就是因为想到这个函数是一百个点误差平方加起来,之后对这个大函数求一次J和H然后解方程直接得到迭代值,最后出了问题。
其实这么做理论上也是对的,一会再试试。(PS:没有试出来)
运行结果如下:

浙公网安备 33010602011771号