点云配准 -辅助知识 最小二乘法代码实现拟合曲线(C++)

Posted 行码阁119

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点云配准 -辅助知识 最小二乘法代码实现拟合曲线(C++)相关的知识,希望对你有一定的参考价值。

一、原理讲解

通过实验获得一些列的观测数值(假设为三个):

其每个样本观测值对应的精确值为:

这里假设其观测值对应的准确值为:

上面矩阵计算公式可以等价于:

其误差计算公式:

其平方误差计算公式:

        

由于这是误差公式关于的平方公式,所以根据要达到误差最小,既是极点,对其求导,令其等于0:

可知:

此时,系数就找到了,带入就可:

二、代码实现

2.1 最小二乘拟合正弦函数

代码复现的统计学第一章-最小二乘拟合正弦函数,正则化,其官方python代码为:

#coding:utf-8
import numpy as np
import scipy as sp
from scipy.optimize import leastsq
import matplotlib.pyplot as plt
# 目标函数
def real_func(x):
    return np.sin(2*np.pi*x)
 
# 多项式
def fit_func(p, x):
    f = np.poly1d(p)
    # print('f=',f)
    return f(x)
 
# 残差
def residuals_func(p, x, y):
    ret = fit_func(p, x) - y
    return ret
 
# 十个点
x = np.linspace(0, 1, 10)
x_points = np.linspace(0, 1, 1000)
# 加上正态分布噪音的目标函数的值
y_ = real_func(x)
y = [np.random.normal(0, 0.1) + y1 for y1 in y_]
 
 
def fitting(M=0):
    """
    M    为 多项式的次数
    """
    # 随机初始化多项式参数
    p_init = np.random.rand(M + 1)
    # 最小二乘法
    p_lsq = leastsq(residuals_func, p_init, args=(x, y))
    print('Fitting Parameters:', p_lsq[0])
    #
    # 可视化
    plt.plot(x_points, real_func(x_points), label='real')
    plt.plot(x_points, fit_func(p_lsq[0], x_points), label='fitted curve')
    plt.plot(x, y, 'bo', label='noise')
    plt.legend()
    plt.show()
    return p_lsq
# M=0
p_lsq_0 = fitting(M=0)
# M=1
p_lsq_1 = fitting(M=1)
# M=3
p_lsq_3 = fitting(M=3)
# M=9
p_lsq_9 = fitting(M=9)

其运行结果图:

 2.2 c++实现拟合正弦曲线(对应上诉代码M = 10)

头函数:

# include<iostream>
# include<Eigen/Dense>
# include<math.h>
# include<Eigen/Eigenvalues>
using namespace std;
using namespace Eigen;

生成原理所讲矩阵w,x,yt

//生成观测值
MatrixXd x(20, 10);
//目标值
MatrixXd y(20, 1);
//权重值
MatrixXd w(10, 1) ;

计算误差函数():

MatrixXd errCount(MatrixXd y_p, MatrixXd y_t)

	MatrixXd err = (y_p - y_t);
	MatrixXd myerr = err.transpose() * err;
	return myerr;

关键部分(原理)实现

MatrixXd leastsq(MatrixXd y_p, MatrixXd y_t, MatrixXd x, MatrixXd w)

	MatrixXd err = errCount(y_p, y_t);
	if (err.sum() > 0.1)
	
		w = (x.transpose() * x).inverse() * x.transpose() * y;
		y_p = x * w;
		err = errCount(y_p, y_t);
		cout << err.sum() << endl;
	
	return w;

由于官方函数,说明当M=10(多项式的系数)效果最好,固然复现构造函数(只有一个观测值x):

	for (int i = 0; i < 20; i++)
		
		x(i, 9) = 1;
		x(i, 8)=0.25 + sum;
		x(i, 7) = x(i, 8) * x(i, 8);
		x(i, 6) = x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 5) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 4) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 3) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 2) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 1) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8)*x(i, 8);
		x(i, 0) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8)*x(i, 8)*x(i, 8);
		y(i, 0) = sin(x(i,8)*3.14);
		sum = sum + 0.25;
	
w:
          0           0           0           0           0           0           0           0           0           1
 3.8147e-06 1.52588e-05 6.10352e-05 0.000244141 0.000976562  0.00390625    0.015625      0.0625        0.25           1
 0.00195312  0.00390625   0.0078125    0.015625     0.03125      0.0625       0.125        0.25         0.5           1
  0.0750847    0.100113    0.133484    0.177979    0.237305    0.316406    0.421875      0.5625        0.75           1
          1           1           1           1           1           1           1           1           1           1
    7.45058     5.96046     4.76837      3.8147     3.05176     2.44141     1.95312      1.5625        1.25           1
    38.4434     25.6289     17.0859     11.3906     7.59375      5.0625       3.375        2.25         1.5           1
    153.937     87.9639     50.2651     28.7229     16.4131     9.37891     5.35938      3.0625        1.75           1
        512         256         128          64          32          16           8           4           2           1
    1477.89     656.841     291.929     129.746      57.665     25.6289     11.3906      5.0625        2.25           1
     3814.7     1525.88     610.352     244.141     97.6562     39.0625      15.625        6.25         2.5           1
    8994.86     3270.86      1189.4      432.51     157.276     57.1914     20.7969      7.5625        2.75           1
      19683        6561        2187         729         243          81          27           9           3           1
      40453     12447.1     3829.87     1178.42     362.591     111.566     34.3281     10.5625        3.25           1
    78815.6     22518.8     6433.93     1838.27     525.219     150.062      42.875       12.25         3.5           1
     146650     39106.6     10428.4     2780.91     741.577     197.754     52.7344     14.0625        3.75           1
     262144       65536       16384        4096        1024         256          64          16           4           1
     452377      106442     25045.1     5892.96     1386.58     326.254     76.7656     18.0625        4.25           1
     756681      168151     37366.9     8303.77     1845.28     410.062      91.125       20.25         4.5           1
1.23096e+06      259149     54557.6     11485.8     2418.07     509.066     107.172     22.5625        4.75           1
yt:
          0    0.706825           1    0.707951  0.00159265   -0.705698   -0.999997   -0.709075  -0.0031853    0.704568    0.999992    0.710197  0.00477794   -0.703437   -0.999984   -0.711317 -0.00637057    0.702304    0.999974    0.712436
原始y_p:
          1     1.33333     1.99805     3.77475          10     33.2529      113.33     357.853        1023     2659.41     6357.16     14134.2       29524     58431.6      110341      199977      349525      591569      972875 1.55921e+06

变换过后:

精确值
          0    0.706825           1    0.707951  0.00159265   -0.705698   -0.999997   -0.709075  -0.0031853    0.704568    0.999992    0.710197  0.00477794   -0.703437   -0.999984   -0.711317 -0.00637057    0.702304    0.999974    0.712436
变换后的y_p:
   0.0103961     0.658876      1.06156     0.724083   -0.0521667    -0.743825    -0.969841    -0.649122    0.0135521     0.657116     0.942181     0.709952    0.0648353    -0.655788     -1.02993    -0.781023 -0.000910797     0.785843      0.94112     0.724696

2.3 完整代码

# include<iostream>
# include<Eigen/Dense>
# include<math.h>
# include<Eigen/Eigenvalues>
using namespace std;
using namespace Eigen;
//生成观测值
MatrixXd x(20, 10);
//目标值
MatrixXd y(20, 1);
//权重值
MatrixXd w(10, 1) ;

MatrixXd errCount(MatrixXd y_p, MatrixXd y_t)

	MatrixXd err = (y_p - y_t);
	MatrixXd myerr = err.transpose() * err;
	return myerr;


MatrixXd leastsq(MatrixXd y_p, MatrixXd y_t, MatrixXd x, MatrixXd w)

	MatrixXd err = errCount(y_p, y_t);
	if (err.sum() > 0.1)
	
		w = (x.transpose() * x).inverse() * x.transpose() * y;
		y_p = x * w;
		err = errCount(y_p, y_t);
		cout << err.sum() << endl;
	
	return w;


int main()

	float sum=-0.25;
	//给观测值和准确值赋值
	for (int i = 0; i < 20; i++)
		
		x(i, 9) = 1;
		x(i, 8)=0.25 + sum;
		x(i, 7) = x(i, 8) * x(i, 8);
		x(i, 6) = x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 5) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 4) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 3) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 2) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8);
		x(i, 1) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8)*x(i, 8);
		x(i, 0) = x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8) * x(i, 8)*x(i, 8)*x(i, 8);
		y(i, 0) = sin(x(i,8)*3.14);
		sum = sum + 0.25;
	
	cout << "w:" << endl;
	cout << x << endl;
	cout << "yt:" << endl;
	cout << y.transpose() << endl;
	w << 1, 1, 1, 1, 1, 1, 1, 1, 1, 1;
	cout << "原始y_p:" << endl;
	cout << (x * w).transpose() << endl;
	MatrixXd y_p = x * w;
	w = leastsq(y_p, y, x, w);
	cout << "精确值" << endl;
	cout << y.transpose() << endl;
	cout << "变换后的y_p:" << endl;
	cout << (x * w).transpose() << endl;


3 、总结

根据上诉公式,好像只需要变换一次,有点奇怪。实验证明,也只变了一次,误差就很小了。

以上是关于点云配准 -辅助知识 最小二乘法代码实现拟合曲线(C++)的主要内容,如果未能解决你的问题,请参考以下文章

MATLAB怎么实现两个点云的配准

自适应点云配准(RANSACICP)

自适应点云配准(RANSACICP)

点云配准文献阅读与简单实现

MATLAB点云处理(十七):最小二乘多项式曲线拟合

关于VC的最小二乘法曲线拟合算法问题