LM解非线性最小二乘--以圆锥面拟合为例

LM解非线性最小二乘--以圆锥面拟合为例

圆锥面方程

此处我们将模型简化为一个中心轴始终与z轴平行的圆锥面,该圆锥面包括4个参数,分别为锥点坐标(a, b, c)和半顶角α。
在这里插入图片描述

则圆锥面上一点对应的高程z’可表达为:
z = c − ( x − a ) 2 + ( y − b ) 2 × 1 tan ⁡ α . z = c -\sqrt{\left (x-a \right )^{2}+\left (y-b \right )^{2}} \times \frac{1}{\tan \alpha } . z=c(xa)2+(yb)2 ×tanα1.

LM解非线性最小二乘

根据圆锥面方程,我们可以定义误差方程为:
r i = z i − c + ( x i − a ) 2 + ( y i − b ) 2 × 1 tan ⁡ α r_{i} = z_{i}- c +\sqrt{\left (x_{i} -a \right )^{2}+\left (y_{i} -b \right )^{2}} \times \frac{1}{\tan \alpha } ri=zic+(xia)2+(yib)2 ×tanα1
显然这个误差方程是非线性的,要解这个方程有多种现成的方法,比如高斯牛顿法,Levenberg-Marquardt (LM)法等,这里我们采用最常用的LM法。

C++编程实现

第三方库:eigen

template<typename _Scalar, int NX = Eigen::Dynamic, int NY = Eigen::Dynamic>
struct Functor
{
	typedef _Scalar Scalar;
	enum {
		InputsAtCompileTime = NX,
		ValuesAtCompileTime = NY
	};
	typedef Eigen::Matrix<Scalar, InputsAtCompileTime, 1> InputType;
	typedef Eigen::Matrix<Scalar, ValuesAtCompileTime, 1> ValueType;
	typedef Eigen::Matrix<Scalar, ValuesAtCompileTime, InputsAtCompileTime> JacobianType;

	int m_inputs, m_values;

	Functor() : m_inputs(InputsAtCompileTime), m_values(ValuesAtCompileTime) {}
	Functor(int inputs, int values) : m_inputs(inputs), m_values(values) {}

	int inputs() const { return m_inputs; }
	int values() const { return m_values; }

};

//构建LM函数
struct LMFunctor : Functor<float> {
private:
	std::vector<Vec3>   mVec; // Data points to fit.
	int                 mSize; // Number of data points, i.e. values.

public:
	// Set the number of data points
	void setValues(std::vector<Vec3> vec)
	{
		mVec = vec;
		mSize = vec.size();
	}

	// Compute 'm' errors, one for each data point, for the given parameter values in 'x'
	int operator()(const Eigen::VectorXf& x, Eigen::VectorXf& fvec) const
	{
		// 'x' has dimensions n x 1
	    // It contains the current estimates for the parameters.

	    // 'fvec' has dimensions m x 1
	    // It will contain the error for each data point.

		float a = x(0);
		float b = x(1);
		float c = x(2);
		float d = x(3);

		for (int i = 0; i < values(); i++) {
			float x = mVec[i].f[0];
			float y = mVec[i].f[1];
			float z = mVec[i].f[2];

			fvec(i) = z - c + std::sqrt((x - a) * (x - a) + (y - b) * (y - b)) / std::tan(d);
		}

		return 0;
	}

	// Compute the jacobian of the errors
	int df(const Eigen::VectorXf& x, Eigen::MatrixXf& fjac) const
	{
		// 'x' has dimensions n x 1
        // It contains the current estimates for the parameters.

        // 'fjac' has dimensions m x n
        // It will contain the jacobian of the errors, calculated numerically in this case.

		float epsilon;
		epsilon = 1e-5f;

		for (int i = 0; i < x.size(); i++) {
			Eigen::VectorXf xPlus(x);
			xPlus(i) += epsilon;
			Eigen::VectorXf xMinus(x);
			xMinus(i) -= epsilon;

			Eigen::VectorXf fvecPlus(values());
			operator()(xPlus, fvecPlus);

			Eigen::VectorXf fvecMinus(values());
			operator()(xMinus, fvecMinus);

			Eigen::VectorXf fvecDiff(values());
			fvecDiff = (fvecPlus - fvecMinus) / (2.0f * epsilon);

			fjac.block(0, i, values(), 1) = fvecDiff;
		}

		return 0;
	}

	// Returns 'mSize', the number of values.
	int values() const { return mSize; }
};

其中jacobian矩阵的计算可以使用手动求导方法,也可以采用我给出的有限差分方法,如果对自己算的对不对没信心的话还是老实拷贝代码吧。
最后就是采用LM解方程了:

	// 圆锥曲面的方程参数,表示为 (x0,y0,z0,alpha)
	Eigen::VectorXf params = Eigen::VectorXf(4, 1);

	LMFunctor functor;
	functor.setValues(cloud); //cloud为待拟合数据
	Eigen::NumericalDiff<LMFunctor> numDiff(functor);
	Eigen::LevenbergMarquardt<Eigen::NumericalDiff<LMFunctor>, float> lm(numDiff);
	lm.parameters.maxfev = 1000;
	lm.parameters.xtol = 1.0e-10;
	int ret = lm.minimize(params);
	
	// 输出最优拟合参数
	std::cout << "The optimal parameters are: " << params.transpose() << std::endl;

至此圆锥面拟合完成:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
设直圆锥的顶点为V(2,-3,5),轴为直线L:{(x,y,z)=(1,1,1)+t(1,1,1)},其中t为实数。 以L为坐标轴,我们可以将空间点(x,y,z)表示为L上的向量a加上L上的向量b,即(x,y,z)=(1,1,1)+t(1,1,1)+s(1,-1,0),其中t,s均为实数。 现在我们只需寻找合适的t和s,使得向量OV和向量OM的夹角为π/6,其中O是坐标系的原点,M是圆锥上的一点。 向量OV即为V-O=(2,-3,5),向量OM即为(x,y,z)-O=(1-t-s,1-t+s,1+s)。 由内积公式,得到OM·OV=|OM|·|OV|·cos(π/6),带入上式并化简可得: (1-t-s)×2+(1-t+s)×(-3)+(1+s)×5=3√3∣∣(1-t-s,1-t+s,1+s)∣∣ 展开后可得: -2t+4s+2√3=∣∣(1-t-s,1-t+s,1+s)∣∣ 将右边的模长平方展开并整理,得到: ∣∣(1-t-s,1-t+s,1+s)∣∣^2=-2t+4s+23/3 另外还有一个条件,就是圆锥的顶角为π/6,即圆锥上任意一点与V的向量与圆锥的法向量的夹角都小于π/6。 所以我们还需满足以下两个条件: 1. 圆锥上任意一点与V的向量与L的方向向量的夹角小于π/6,即: ((1-t-s)-1,(1-t+s)-1,(1+s)-1)·(1,1,1)>|((1-t-s)-1,(1-t+s)-1,(1+s)-1)×(1,1,1)|·√3 展开可得: 8t-5s-7√3<0 2. 圆锥上任意一点与V的向量与圆锥的法向量的夹角小于π/6,即: ((1-t-s)-2,(1-t+s)+3,(1+s)-5)·(√2,-√2,0)>|((1-t-s)-2,(1-t+s)+3,(1+s)-5)×√2,-√2,0)|·√3 展开可得: 4t-4s-7√3<0 综上所述,这是一个带约束的优化问题,我们的目标是寻求符合以上三个条件的最小的t和s,然后将其代入(x,y,z)=(1,1,1)+t(1,1,1)+s(1,-1,0),即可得到圆锥方程。 不过,这是一个比较繁琐的问题,需要通过数值方法逐步逼近最优。如果需要一个精确的计算结果,建议采用数值计算软件,如MATLAB等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木得感情小铅笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值