共计 1530 个字符,预计需要花费 4 分钟才能阅读完成。
非线性优化中的异常值难题
在 SLAM、三维重建等任务中,最小二乘法的目标函数对异常值 (outliers) 极其敏感。举个例子:当特征点匹配错误时,传统最小二乘会因平方项放大误差,导致整个优化过程偏离正确解。

数学上表示为:
$$\min_x \sum_i \rho(|f_i(x)|^2)$$
其中 $\rho$ 就是损失函数,它决定了残差如何影响整体代价。
常见损失函数对比
Ceres Solver 内置了多种损失函数实现,核心差异在于对大残差的处理方式:
- TrivialLoss(平方损失)
- 公式:$\rho(s) = s$
-
特点:直接线性放大误差,对异常值零容忍
-
HuberLoss
- 公式:
$$\rho(s) = \begin{cases}
s & s \leq \delta^2 \
2\delta\sqrt{s} – \delta^2 & s > \delta^2
\end{cases}$$ -
$\delta$ 为阈值参数,超过后转为线性增长
-
CauchyLoss
- 公式:$\rho(s) = c^2\log(1+s/c^2)$
- 对极端异常值有更好的鲁棒性
实现机制剖析
Ceres 通过虚基类 LossFunction 定义统一接口,关键方法是:
virtual void Evaluate(double s, double out[3]) const = 0;
其中 out 数组返回:
– out[0]: $\rho(s)$
– out[1]: $\rho'(s)$
– out[2]: $\rho”(s)$
与自动求导的结合示例:
ceres::AutoDiffCostFunction<CostFunctor, 1, 3>(new CostFunctor),
new ceres::HuberLoss(1.0));
实战:自定义 Tukey 损失
实现一个抑制极端异常值的损失函数:
class TukeyLoss : public ceres::LossFunction {
public:
explicit TukeyLoss(double c) : c_squared_(c * c) {}
void Evaluate(double s, double out[3]) const override {
const double ratio = s / c_squared_;
if (ratio <= 1.0) {
const double tmp = 1 - ratio;
out[0] = c_squared_ * (1 - tmp * tmp * tmp) / 3.0;
out[1] = tmp * tmp;
out[2] = -2 * tmp / c_squared_;
} else {out[0] = c_squared_ / 3.0;
out[1] = 0;
out[2] = 0;
}
}
private:
const double c_squared_;
};
工程选型指南
根据实际场景选择损失函数:
- 视觉 SLAM 前端
- 推荐:HuberLoss ($\delta$=5~10 pixels)
-
原因:特征点匹配存在少量误匹配
-
LiDAR 点云配准
- 推荐:CauchyLoss (c=0.5~1.0)
- 原因:点云中存在大量噪声点
性能注意事项:
– 线程安全:所有内置损失函数均无状态,可多线程共享
– 计算开销:HuberLoss 比 TrivialLoss 多约 15% 计算量
进阶思考方向
- 二阶导优化:当使用解析求导时,精确的二阶导能否改善收敛速度?
- 动态阈值:能否根据残差分布自动调整 $\delta$ 参数?
- 混合损失:不同残差项使用不同损失函数(如 IMU 用 TrivialLoss,视觉用 CauchyLoss)
推荐扩展阅读:
–《Robust Estimation in Computer Vision》
– Ceres 官方文档中的 loss_function.h 源码分析
通过合理选择损失函数,我们在实际项目中将 BA 的收敛成功率提升了 40%,异常值场景下的定位精度提高约 2 倍。希望这些实践经验对你有帮助!
正文完
发表至: 未分类
近一天内
