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

文章介绍了如何使用Levenberg-Marquardt(LM)算法解决非线性最小二乘问题,以圆锥面拟合为例。通过定义误差方程和利用C++的Eigen库,实现了LM法的编程实现,包括计算误差和雅可比矩阵。最后展示了如何设置参数并执行优化过程来找到最佳拟合参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

木得感情小铅笔

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

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

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

打赏作者

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

抵扣说明:

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

余额充值