# 泰勒展开的极值判别法:从数学公式到Python实现,附SLAM中的优化案例
在机器学习和机器人技术的核心地带,我们常常需要让一个系统“找到最佳状态”。无论是调整神经网络的数百万个参数以最小化损失,还是让机器人精确地估计自身在三维空间中的位置与姿态,本质上都是在求解一个优化问题:寻找某个复杂函数的最小值(或最大值)。面对这些高度非线性的函数,直接求解往往无从下手。这时,微积分中一个古老而强大的工具——泰勒展开,便成为了照亮这片复杂地形的一束光。它允许我们将一个在局部“弯弯曲曲”的函数,用一个简单的多项式来近似,从而将复杂的非线性问题转化为我们更擅长处理的线性或二次问题。
这篇文章正是要带你深入这个迷人的数学世界,并亲手将其转化为代码。我们将从多元函数极值判别的数学原理出发,详细拆解如何利用泰勒展开和海森矩阵(Hessian Matrix)来精确判断一个点是“山峰”、“山谷”还是“马鞍”。更重要的是,我们将超越课本例题,直接切入**即时定位与地图构建(SLAM)** 这一前沿领域,看看这些抽象的数学概念是如何在机器人感知世界的复杂优化问题中扮演关键角色的。无论你是希望夯实数学基础的算法工程师,还是渴望理解SLAM背后优化原理的开发者,这篇文章都将为你提供从理论到实战的完整路径。
## 1. 数学基石:多元泰勒展开与极值判别的本质
要理解如何用泰勒展开判断极值,我们首先得回到它的核心思想:**局部近似**。对于一个行为良好的函数,如果我们只关心它在某一点附近很小范围内的表现,那么完全可以用一个简单的多项式来“模仿”它,并且模仿得相当逼真。
### 1.1 从一元到多元:泰勒展开的升维思考
对于一元函数 `f(x)`,在点 `a` 处的二阶泰勒展开我们很熟悉:
```
f(x) ≈ f(a) + f'(a)(x-a) + (1/2!) f''(a)(x-a)²
```
它用常数项(函数值)、一次项(斜率)和二次项(曲率)来构建一个抛物线,在 `a` 点附近拟合原函数。
当函数上升到多元,例如 `f(x, y)`,情况变得复杂,但思想一脉相承。在点 `(a, b)` 处的二阶泰勒展开为:
```
f(a+h, b+k) ≈ f(a,b) + [f_x(a,b) * h + f_y(a,b) * k] + (1/2) * [f_xx(a,b)*h² + 2f_xy(a,b)*h*k + f_yy(a,b)*k²]
```
其中,`h = x-a`, `k = y-b`。这个式子看似冗长,但结构清晰:
- **零阶项**:`f(a,b)`,基准点的高度。
- **一阶项**:`f_x*h + f_y*k`,这是**梯度**向量 `∇f` 与位移向量 `[h, k]` 的点积,代表了函数在基准点切平面的斜率信息。
- **二阶项**:一个关于 `h` 和 `k` 的二次型,其系数由函数的二阶偏导数(曲率信息)决定。
> **提示**:这里的 `f_xy` 是混合偏导数,在函数连续可微的条件下,`f_xy = f_yx`。这个对称性是后续海森矩阵正定判断的基础。
为了更紧凑、更易于推广到n维,我们引入向量和矩阵记号。令 `x = [x1, x2, ..., xn]^T`,在点 `x0` 处的二阶泰勒展开的向量形式堪称优美:
```
f(x0 + Δx) ≈ f(x0) + ∇f(x0)^T Δx + (1/2) Δx^T H(x0) Δx
```
- `∇f(x0)`:梯度向量(所有一阶偏导数组成的列向量)。
- `H(x0)`:**海森矩阵**,一个 `n×n` 的对称矩阵,其第 `(i, j)` 元素是二阶偏导数 `∂²f/(∂xi∂xj)`。
这个形式将函数在 `x0` 附近的局部信息封装在了三个核心量里:函数值、梯度(一阶信息)和海森矩阵(二阶信息)。
### 1.2 极值点的数学侦探:梯度与海森矩阵的联袂演出
如何判断 `x0` 是否是极值点?我们来做一场逻辑推演。
首先,如果 `x0` 是一个局部极值点(无论是极大还是极小),那么函数在这一点各个方向上的瞬时变化率都应该为零,否则我们就可以沿着某个方向移动一点点来使函数值变得更小或更大。这意味着**梯度必须为零向量**:
```
∇f(x0) = 0
```
满足这个条件的点称为**驻点**或**临界点**。但驻点不一定是极值点,它也可能是“鞍点”——一个在某个方向上是极小值,在另一个方向上是极大值的点。
那么,如何区分驻点的类型?这时,泰勒展开中的二阶项——海森矩阵 `H(x0)`——就成为了关键侦探。在梯度为零的前提下,泰勒展开简化为:
```
f(x0 + Δx) ≈ f(x0) + (1/2) Δx^T H(x0) Δx
```
函数在 `x0` 附近的变化完全由二次型 `Δx^T H Δx` 主导。这个二次型的符号决定了 `x0` 的性质:
- 如果对于**所有**非零的小位移 `Δx`,`Δx^T H Δx` **恒大于0**,那么 `f(x0+Δx) > f(x0)`,`x0` 是**局部极小值点**。此时我们称海森矩阵 `H` 是**正定**的。
- 如果对于**所有**非零的小位移 `Δx`,`Δx^T H Δx` **恒小于0**,那么 `f(x0+Δx) < f(x0)`,`x0` 是**局部极大值点**。此时海森矩阵 `H` 是**负定**的。
- 如果对于某些 `Δx` 它大于0,对于另一些 `Δx` 它小于0,那么 `x0` 就是一个**鞍点**。此时海森矩阵 `H` 是**不定**的。
- 如果存在非零 `Δx` 使得它等于0,而其他情况符号一致,则无法仅凭二阶信息判断,需要更高阶展开。此时海森矩阵是**半定**的。
对于二元函数 `f(x, y)`,这个判别法可以具体化为一个易于操作的口诀。记海森矩阵的行列式 `D = f_xx * f_yy - (f_xy)²`,以及 `A = f_xx`:
1. **`D > 0 且 A > 0`**:局部极小值。
2. **`D > 0 且 A < 0`**:局部极大值。
3. **`D < 0`**:鞍点。
4. **`D = 0`**:二阶判别法失效,需进一步分析。
这个判别法的本质,就是判断海森矩阵的正定性。`D>0` 且 `A>0` 等价于矩阵正定;`D>0` 且 `A<0` 等价于矩阵负定;`D<0` 等价于矩阵不定。
## 2. Python实战:实现极值判别与可视化
理解了数学原理,最好的巩固方式就是动手实现。我们将用Python的NumPy和Matplotlib库,从零开始编写一个多元函数的极值分析工具,并直观地看到“山峰”、“山谷”和“马鞍”的形状。
### 2.1 核心函数实现:梯度与海森矩阵的计算
首先,我们实现一个通用的函数,用于数值计算梯度和海森矩阵。虽然对于复杂函数可以手动求导,但数值方法更具通用性。
```python
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def numerical_gradient(f, x, h=1e-5):
"""计算函数f在点x处的数值梯度"""
grad = np.zeros_like(x)
for i in range(len(x)):
x_plus = x.copy()
x_minus = x.copy()
x_plus[i] += h
x_minus[i] -= h
grad[i] = (f(x_plus) - f(x_minus)) / (2 * h)
return grad
def numerical_hessian(f, x, h=1e-5):
"""计算函数f在点x处的数值海森矩阵"""
n = len(x)
hess = np.zeros((n, n))
# 对角线元素 (二阶纯偏导)
for i in range(n):
x_pp = x.copy(); x_pp[i] += h
x_pm = x.copy(); x_pm[i] += h/2 # 中心差分需要对称点,这里用另一种方式
x_mp = x.copy(); x_mp[i] -= h/2
x_mm = x.copy(); x_mm[i] -= h
# 使用中心差分公式计算二阶导
hess[i, i] = (f(x_pp) - 2*f(x) + f(x_mm)) / (h**2)
# 非对角线元素 (混合偏导)
for i in range(n):
for j in range(i+1, n):
# 四点公式计算混合偏导,精度更高
x_pp = x.copy(); x_pp[i] += h; x_pp[j] += h
x_pm = x.copy(); x_pm[i] += h; x_pm[j] -= h
x_mp = x.copy(); x_mp[i] -= h; x_mp[j] += h
x_mm = x.copy(); x_mm[i] -= h; x_mm[j] -= h
hess[i, j] = (f(x_pp) - f(x_pm) - f(x_mp) + f(x_mm)) / (4 * h**2)
hess[j, i] = hess[i, j] # 对称性
return hess
def classify_critical_point(f, x_candidate, tol=1e-6):
"""
判别临界点类型
参数:
f: 目标函数
x_candidate: 候选点 (numpy数组)
tol: 梯度为零的容差
返回:
classification: 字符串,描述点类型 ('local min', 'local max', 'saddle', 'inconclusive')
gradient: 梯度值
hessian: 海森矩阵
eigenvalues: 海森矩阵的特征值
"""
grad = numerical_gradient(f, x_candidate)
# 检查梯度是否接近零
if np.linalg.norm(grad) > tol:
return "not a critical point (gradient non-zero)", grad, None, None
H = numerical_hessian(f, x_candidate)
eigvals = np.linalg.eigvals(H)
# 根据特征值判断海森矩阵的正定性
if np.all(eigvals > tol):
return "local minimum", grad, H, eigvals
elif np.all(eigvals < -tol):
return "local maximum", grad, H, eigvals
elif np.any(eigvals > tol) and np.any(eigvals < -tol):
return "saddle point", grad, H, eigvals
else:
return "inconclusive (possibly degenerate)", grad, H, eigvals
```
### 2.2 案例研究:三个经典曲面
让我们用上面的工具分析三个经典的二元函数,它们分别对应极小值、极大值和鞍点。
**案例一:碗状函数(极小值)**
`f(x, y) = x² + y² + 0.5*x*y`
这个函数在原点附近像一个倾斜的碗,理论上在(0,0)处有极小值。
```python
def func_bowl(v):
x, y = v
return x**2 + y**2 + 0.5*x*y
point = np.array([0.0, 0.0])
classification, grad, H, eigvals = classify_critical_point(func_bowl, point)
print(f"函数: f(x,y) = x² + y² + 0.5xy")
print(f"分析点: {point}")
print(f"分类结果: {classification}")
print(f"海森矩阵:\n{H}")
print(f"特征值: {eigvals}")
```
运行这段代码,你会看到输出明确指示这是一个局部极小值点,海森矩阵的特征值均为正。
**案例二:倒置的碗(极大值)**
`f(x, y) = -x² - 2y² + x*y`
我们在原点附近分析。
**案例三:马鞍面**
`f(x, y) = x² - y²`
这是最经典的鞍面,形状像马鞍。在原点,沿x轴方向是极小值,沿y轴方向是极大值。
```python
def func_saddle(v):
x, y = v
return x**2 - y**2
point = np.array([0.0, 0.0])
classification, grad, H, eigvals = classify_critical_point(func_saddle, point)
print(f"\n函数: f(x,y) = x² - y²")
print(f"分析点: {point}")
print(f"分类结果: {classification}")
print(f"海森矩阵特征值: {eigvals} (一正一负,典型鞍点)")
```
### 2.3 三维可视化:让数学变得可见
为了获得更直观的理解,可视化至关重要。下面的代码生成案例一(碗状函数)的三维曲面和等高线图,并在极值点处进行标记。
```python
def plot_function_and_point(f, point, title, range_=(-2, 2), resolution=50):
"""绘制函数曲面、等高线并标记关键点"""
x = np.linspace(range_[0], range_[1], resolution)
y = np.linspace(range_[0], range_[1], resolution)
X, Y = np.meshgrid(x, y)
Z = f([X, Y]) # 注意:这里要求f能接受网格输入
fig = plt.figure(figsize=(14, 5))
# 1. 3D曲面图
ax1 = fig.add_subplot(121, projection='3d')
surf = ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8, linewidth=0, antialiased=True)
ax1.scatter(point[0], point[1], f(point), color='red', s=100, depthshade=True, label='Critical Point')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.set_zlabel('f(X,Y)')
ax1.set_title(f'{title} - 3D Surface')
ax1.legend()
fig.colorbar(surf, ax=ax1, shrink=0.5, aspect=5)
# 2. 等高线图
ax2 = fig.add_subplot(122)
contour = ax2.contour(X, Y, Z, levels=20, cmap='viridis')
ax2.scatter(point[0], point[1], color='red', s=100, label='Critical Point')
ax2.set_xlabel('X')
ax2.set_ylabel('Y')
ax2.set_title(f'{title} - Contour')
ax2.legend()
fig.colorbar(contour, ax=ax2, shrink=0.5, aspect=5)
plt.suptitle(f'Critical Point Analysis: {title}', fontsize=14)
plt.tight_layout()
plt.show()
# 绘制碗状函数
plot_function_and_point(func_bowl, np.array([0., 0.]), 'Bowl Function (Local Minimum)')
```
通过运行这段代码,你将在红色标记点看到,等高线呈闭合的椭圆状,并且从中心向外,函数值逐渐增大,这正是局部极小点的特征。对于鞍点函数,你会看到等高线是双曲线,标记点位于两条渐近线的交叉中心。
## 3. 从理论到引擎:SLAM优化中的泰勒展开
SLAM(即时定位与地图构建)是机器人、自动驾驶和增强现实领域的核心问题。它的目标是在未知环境中,同步估计机器人自身的运动轨迹(定位)并构建环境地图。这本质上是一个持续进行的、大规模的非线性优化问题。而泰勒展开,特别是其一阶形式,构成了大多数SLAM优化算法的“引擎”。
### 3.1 SLAM优化问题的数学建模
一个典型的基于图优化的SLAM问题,可以被建模为一个巨大的非线性最小二乘问题。机器人位姿(位置和姿态)和地图中的路标点(Landmarks)是待优化的变量。传感器(如激光雷达、相机、IMU)的观测数据构成了变量之间的约束。例如:
- 里程计约束:连接相邻两个机器人位姿。
- 观测约束:连接一个机器人位姿和一个它观测到的路标点。
这些约束通常不是完美的,存在噪声。优化目标就是调整所有变量(位姿和路标点),使得所有约束的误差(或称为“残差”)的平方和最小。用数学公式表达:
```
X* = argmin Σ || e_i(X) ||²
```
其中,`X` 是所有待优化变量组成的向量,`e_i(X)` 是第 `i` 个约束的误差函数,它依赖于相关的变量,并且是高度非线性的(尤其是涉及三维旋转时)。
### 3.2 线性化:泰勒展开的一阶魔法
直接最小化这个非线性平方和极其困难。这时,泰勒展开登场了。我们对每个非线性误差函数 `e_i(X)` 在当前估计值 `X0` 附近进行**一阶泰勒展开**:
```
e_i(X0 + ΔX) ≈ e_i(X0) + J_i(X0) * ΔX
```
这里 `J_i(X0) = ∂e_i / ∂X |_{X0}` 是误差函数 `e_i` 在 `X0` 处的**雅可比矩阵**,它编码了误差相对于每个优化变量的局部变化率。
将这个线性近似代入原目标函数,我们得到一个关于增量 `ΔX` 的**二次函数**:
```
Σ || e_i(X0) + J_i ΔX ||²
```
这是一个标准的线性最小二乘问题 `||AΔX - b||²` 的形式,其中 `A` 是所有雅可比矩阵堆叠而成的大矩阵,`b` 是所有负的当前误差堆叠而成的向量。这个问题的解可以通过求解**正规方程**得到:
```
(A^T A) ΔX = A^T b
```
这里的 `A^T A` 就是著名的**海森矩阵的近似**(高斯-牛顿法中的海森矩阵,忽略二阶项)。解出 `ΔX` 后,我们更新估计值:`X1 = X0 + ΔX`。由于泰勒展开只在局部有效,我们需要在新的点 `X1` 处重新进行线性化,并重复这个过程,直到收敛。这就是**高斯-牛顿法**的核心流程。
> **注意**:在SLAM中,由于问题的特殊结构(图结构),`A^T A` 矩阵是稀疏的。利用这种稀疏性进行高效求解,是SLAM算法(如g2o, GTSAM, Ceres Solver)能够实时运行的关键。这被称为**稀疏线性代数**的应用。
### 3.3 一个简化的视觉SLAM重投影误差案例
让我们用一个极度简化的例子来感受一下。假设一个单目相机观测到一个已知三维地图点 `P=[X, Y, Z]^T`(在世界坐标系)。相机有一个估计的位姿,包括旋转 `R`(旋转矩阵)和平移 `t`。它将世界点投影到图像平面上,得到一个预测的像素坐标 `u_pred = π(R*P + t)`,其中 `π` 是相机投影模型(包含内参)。
实际在图像中检测到的该点像素坐标为 `u_meas`。那么,重投影误差就是:
```
e = u_meas - u_pred
```
这是一个二维向量。我们的优化变量是相机位姿(`R` 和 `t`,通常用李代数 `ξ` 的6维向量表示)。误差 `e` 是关于 `ξ` 的非线性函数。
**线性化过程**:
1. 给定当前位姿估计 `ξ0`,计算当前误差 `e(ξ0)` 和雅可比矩阵 `J = ∂e/∂ξ |_{ξ0}`。这个雅可比矩阵描述了像素误差如何随相机位姿的微小变化(平移和旋转)而变化。
2. 进行一阶泰勒展开:`e(ξ0 + Δξ) ≈ e(ξ0) + J * Δξ`。
3. 构建线性最小二乘问题:`min ||e(ξ0) + J Δξ||²`。
4. 求解 `Δξ`,更新位姿:`ξ1 = ξ0 ⊕ Δξ`(`⊕` 是李代数上的更新操作)。
5. 回到步骤1,直到 `Δξ` 足够小。
下面的伪代码勾勒了这个过程的核心循环:
```python
# 伪代码:高斯-牛顿法优化相机位姿
def optimize_pose(initial_pose, landmark_3d, measured_pixel, camera_intrinsics, max_iterations=10):
current_pose = initial_pose
for i in range(max_iterations):
total_error = 0
H = np.zeros((6, 6)) # 海森近似 (J^T * J)
b = np.zeros(6) # -J^T * e
# 对于当前位姿,计算所有误差项的贡献(这里简化,假设只有一个点)
pixel_predicted = project(landmark_3d, current_pose, camera_intrinsics)
e = measured_pixel - pixel_predicted
J = compute_jacobian(landmark_3d, current_pose, camera_intrinsics) # 2x6 雅可比矩阵
# 累加海森近似和b向量
H += J.T @ J
b += J.T @ e
total_error += e.T @ e
# 求解增量 Δξ = H^{-1} * (-b)
delta_xi = -np.linalg.solve(H, b)
# 更新位姿 (使用李代数更新)
current_pose = update_pose(current_pose, delta_xi)
if np.linalg.norm(delta_xi) < 1e-6:
print(f"Converged after {i+1} iterations.")
break
return current_pose, total_error
```
在实际的SLAM系统中,这个循环会同时处理成百上千个地图点和位姿,构建起一个巨大的稀疏优化问题。每一次迭代,都是泰勒展开将复杂的非线性世界,在局部“拉直”成线性问题的一次努力。
## 4. 进阶探讨:海森矩阵的深层意义与优化算法选择
在SLAM和更广泛的优化领域,海森矩阵不仅仅用于极值判别。它的性质直接决定了优化算法的选择和收敛行为。
### 4.1 海森矩阵:曲率信息与优化地形
海森矩阵 `H` 是函数局部曲率的完整描述。它的特征向量指示了函数变化最快的方向(主曲率方向),对应的特征值则表示在该方向上的曲率大小。
- **大特征值**:表示在该方向上函数非常“陡峭”,梯度下降一小步,函数值变化很大。
- **小特征值**:表示在该方向上函数非常“平坦”,梯度下降很多步,函数值变化也不大。
这种曲率分布的不均匀性,是优化中“病态条件”问题的根源。想象一个又长又窄的山谷,沿着山谷方向(小特征值)很平坦,垂直山谷方向(大特征值)很陡峭。普通的梯度下降法在这里会剧烈震荡,收敛缓慢。
### 4.2 从梯度下降到牛顿法:利用二阶信息
为了克服病态问题,我们需要考虑曲率信息。这就引出了基于二阶泰勒展开的经典算法——**牛顿法**。
回顾二阶泰勒展开:`f(x+Δx) ≈ f(x) + g^T Δx + (1/2) Δx^T H Δx`,其中 `g` 是梯度。
牛顿法的思想是:直接找到这个二次近似的极小点。对近似函数求导并令其为零:
```
g + H Δx = 0 => Δx = -H^{-1} g
```
牛顿法的更新步长是 `-H^{-1} g`,而梯度下降法是 `-λ g`(`λ` 是学习率)。`H^{-1}` 的作用可以理解为:
1. **重新缩放梯度**:在陡峭方向(大特征值),`H^{-1}` 会减小步长,防止震荡;在平坦方向(小特征值),`H^{-1}` 会增大步长,加速前进。
2. **旋转梯度方向**:`H^{-1}` 将梯度方向 `g` 旋转,使其直接指向二次近似的极小点。
因此,牛顿法在局部二次性强的区域收敛速度极快(二次收敛)。但它有两个致命缺点:
1. **计算成本高**:需要计算并求逆海森矩阵 `O(n³)`,对于SLAM这种n很大的问题不可行。
2. **要求海森矩阵正定**:如果 `H` 不是正定的(例如在鞍点附近),`-H^{-1}g` 可能不是下降方向。
### 4.3 SLAM中的实用算法:高斯-牛顿与列文伯格-马夸尔特
为了在SLAM中实用,我们需要牛顿法的思想,但避免其缺点。这催生了两种主导算法:
**1. 高斯-牛顿法 (Gauss-Newton)**
专门用于非线性最小二乘问题 `min Σ ||e_i(x)||²`。它利用误差函数 `e_i(x)` 的特殊结构,用一阶雅可比矩阵 `J` 来近似海森矩阵:
```
H ≈ J^T J
```
这个近似忽略了误差函数 `e_i` 本身的二阶导数(即 `Σ e_i * ∇²e_i` 项)。在残差 `e_i` 很小,或者 `e_i` 接近线性时,这个近似是很好的。高斯-牛顿法的更新方程为:
```
(J^T J) Δx = -J^T e
```
它避免了计算二阶导数,且 `J^T J` 总是半正定的(如果 `J` 列满秩,则是正定的)。这就是我们在SLAM线性化部分看到的方法。
**2. 列文伯格-马夸尔特法 (Levenberg-Marquardt)**
这是高斯-牛顿法的鲁棒性增强版。它意识到 `J^T J` 可能病态或奇异,导致更新步长 `Δx` 过大或不稳定。LM算法引入了一个阻尼因子 `λ`,将更新方程修改为:
```
(J^T J + λ I) Δx = -J^T e
```
- 当 `λ` 很大时,方程近似为 `λ I Δx = -J^T e`,即 `Δx ≈ -(1/λ) J^T e`,这接近于梯度下降,步长小但稳定,适合在远离解时使用。
- 当 `λ` 很小时,方程退化为高斯-牛顿方程,收敛速度快,适合在接近解时使用。
LM算法在每次迭代中动态调整 `λ`:如果当前步长成功降低了误差,则减小 `λ`,更信任高斯-牛顿方向;如果失败,则增大 `λ`,退回到更保守的梯度下降方向。这种自适应机制使其成为SLAM和计算机视觉中非线性优化事实上的标准算法。
下面的表格对比了这几种核心优化算法的特点:
| 算法 | 核心思想 | 是否需要海森矩阵 `H`? | 计算成本 | 收敛速度 | 主要应用场景 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **梯度下降** | 沿负梯度方向移动 | 否 | 低 (`O(n)`) | 线性(慢) | 深度学习(配合自适应学习率)、简单问题 |
| **牛顿法** | 在局部二次模型上直接求极值 | 是(需精确`H`) | 很高 (`O(n³)`) | 二次(快) | 小规模、海森矩阵易求的优化问题 |
| **高斯-牛顿法** | 针对最小二乘,用 `J^T J` 近似 `H` | 否(需雅可比 `J`) | 中等 (`O(mn²)`, m为残差数) | 超线性 | **SLAM、Bundle Adjustment**等非线性最小二乘问题 |
| **列文伯格-马夸尔特** | 高斯-牛顿 + 阻尼因子自适应调整 | 否(需雅可比 `J`) | 中等(比GN略高) | 超线性 | **SLAM、计算机视觉优化**(鲁棒性要求高) |
在实际的SLAM系统(如ORB-SLAM, VINS-Mono)或通用优化库(如Ceres Solver, g2o)中,LM算法是默认或最常用的优化器。它巧妙地平衡了收敛速度和稳定性,而这一切的数学基础,都始于对非线性函数那一瞬间的线性化凝视——泰勒展开。