第四节 最小二乘法求解:线性最小二乘原理、超定方程组求解、SVD分解在标定中的应用

各位,前面我们推导了九点标定的线性模型,也知道了理论上只需要三个点就能解出六个参数。但实际现场谁这么干?没人敢。为什么?因为传感器有噪声,机械有重复性误差,你取三个点算出来的结果,换个位置再测一次,可能就完全不一样了。

所以,我们实际做标定时,通常会采集九个、甚至十几个点。这就引出一个问题:方程个数比未知数多,方程组是“超定”的,没有精确解。怎么办?用最小二乘法,找一个“最不坏”的解。

4.1 线性最小二乘原理:到底在“最小”什么?

先别急着上公式。我用自己的理解给你讲清楚。

假设我们有一个线性系统 A · x = b。A 是 m×n 的矩阵(m > n),x 是 n 维未知向量,b 是 m 维观测值。因为 m > n,一般情况下,b 不在 A 的列空间里,所以 A · x = b 无解。

那怎么办?我们退一步,不要求等式成立,而是要求 A · x 尽可能接近 b。数学上,就是让残差向量 r = b - A · x 的 2-范数最小:

min || b - A·x ||²

这就是线性最小二乘。说白了,就是找一组参数 x,使得模型预测值 A·x 和实际观测值 b 之间的“总偏差平方和”最小。

核心理解:最小二乘不是“消除误差”,而是在误差不可避免的情况下,找到让所有点的综合误差最小的那个妥协方案。

我在一个汽车零部件检测项目里遇到过这种情况。当时用了12个标定点,但其中有一个点因为夹具松动,位置明显偏了。如果不用最小二乘,那个坏点会直接把整个标定结果带偏。最小二乘的好处是,它会把坏点的误差“分摊”到所有点上,虽然每个点都有点小误差,但整体效果最稳定。

4.2 超定方程组求解:正规方程法与QR分解

好,原理讲完了,怎么算?最直接的方法叫“正规方程法”。

对 min || b - A·x ||² 求导,令导数为零,得到:

Aᵀ·A·x = Aᵀ·b

这就是正规方程。如果 Aᵀ·A 可逆,解就是:

x = (Aᵀ·A)⁻¹ · Aᵀ · b

看起来很简单对吧?但我要提醒你,实际工程中我很少直接用正规方程。为什么?

方法 优点 缺点
正规方程法 公式简单,容易理解 Aᵀ·A 的条件数是原矩阵的平方,数值不稳定;矩阵接近奇异时误差极大
QR分解法 数值稳定性好,适合中等规模问题 计算量比正规方程稍大
SVD分解法 数值最稳定,能处理秩亏矩阵 计算量最大,但现代计算机完全能接受

正规方程法有个致命问题:它把 A 的条件数平方了。如果 A 本身条件数就大(比如标定点分布不好,几乎共线),Aᵀ·A 的条件数会更大,算出来的解可能完全不可信。

避坑指南:我曾经在一个项目中直接用正规方程解九点标定,结果标定出来的参数在某个区域误差特别大。后来一查,是因为我采集的9个点中有3个几乎在一条直线上,导致 Aᵀ·A 接近奇异。从那以后,我养成了一个习惯:不管用什么方法,先检查标定点的分布是否均匀。

QR分解比正规方程稳定。它把 A 分解成 Q·R,其中 Q 是正交矩阵,R 是上三角矩阵。然后原问题变成:

min || b - Q·R·x ||² = min || Qᵀ·b - R·x ||²

因为 Q 正交,范数不变。最后只需要解一个上三角方程组 R·x = Qᵀ·b,用回代法就能搞定。

4.3 SVD分解:标定中的“终极武器”

好了,重点来了。SVD(奇异值分解)是我个人最推荐的方法,没有之一。

任意一个 m×n 的矩阵 A,都可以分解为:

A = U · Σ · Vᵀ

其中:

  • U 是 m×m 正交矩阵(左奇异向量)
  • Σ 是 m×n 对角矩阵(对角线元素是奇异值 σ₁ ≥ σ₂ ≥ ... ≥ σᵣ > 0)
  • V 是 n×n 正交矩阵(右奇异向量)

用 SVD 解最小二乘问题,解可以写成:

x = V · Σ⁺ · Uᵀ · b

其中 Σ⁺ 是 Σ 的伪逆,就是把非零奇异值取倒数,零奇异值保持不变。

你想想看,SVD 为什么这么强?因为它能告诉我们矩阵的“真实秩”。如果某个奇异值非常小(接近零),说明对应的方向在数据中几乎没有贡献,我们可以直接把它忽略掉。这在标定中特别有用——比如你的标定点虽然采集了9个,但实际有效自由度可能只有5个(因为某些方向没有激励到)。

个人经验:我在做高精度视觉定位时,习惯先对设计矩阵 A 做一次 SVD,看看奇异值的分布。如果最大奇异值和最小奇异值的比值(条件数)超过 1000,我会重新采集标定点,或者调整点的分布。这个习惯帮我避免了很多次“标定通过但实际跑飞”的尴尬。

4.4 九点标定中的SVD应用流程

具体到九点标定,我们怎么用 SVD?我给你画个流程图:

九点标定 SVD 求解流程图 采集 N 个标定点(N ≥ 4,通常 N=9) 构造设计矩阵 A 和观测向量 b 对 A 进行 SVD 分解:A = U·Σ·Vᵀ 检查奇异值,截断小于阈值的奇异值(去噪) 计算伪逆解:x = V·Σ⁺·Uᵀ·b 得到仿射变换参数(6个自由度) 关键检查点 • 点分布是否均匀? • 条件数 < 100? • 残差是否在容忍范围内?

你看,整个流程其实不复杂。核心就三步:构造矩阵、SVD分解、取伪逆。但每一步都有坑。

4.5 HALCON中的实现与注意事项

在 HALCON 里,你不需要手写 SVD,直接用 vector_to_hom_mat2d 算子就行。但理解背后的原理,能帮你用好这个算子。

* 采集标定点坐标(像素坐标和世界坐标)
* 假设 PointsX, PointsY 是像素坐标
* WorldX, WorldY 是世界坐标

* 构造齐次矩阵
vector_to_hom_mat2d (PointsX, PointsY, WorldX, WorldY, HomMat2D)

* 验证标定精度
affine_trans_point_2d (HomMat2D, PointsX, PointsY, TransX, TransY)

* 计算每个点的残差
for i := 0 to |PointsX|-1 by 1
    ErrorX := TransX[i] - WorldX[i]
    ErrorY := TransY[i] - WorldY[i]
    Error := sqrt(ErrorX*ErrorX + ErrorY*ErrorY)
    * 检查误差是否在容忍范围内
endfor

我曾经踩过的坑:有一次我用 vector_to_hom_mat2d 算出来的矩阵,在大部分区域精度都很好,但图像边缘误差突然变大。后来发现,是因为我采集标定点时只覆盖了图像中心区域,边缘没有点约束。SVD 虽然能给出数学上最优的解,但它不能弥补数据本身的缺陷。标定点的覆盖范围,一定要大于你实际工作的区域。

4.6 小结:选对方法,事半功倍

最后,我总结一下我的建议:

  • 如果标定点质量好、分布均匀、数量适中(4-9个):用 QR 分解就足够了,速度快,稳定性也不错。
  • 如果标定点数量多(10个以上)、或者可能存在坏点:用 SVD,它能帮你识别并抑制噪声。
  • 如果标定点分布不好、接近共线:SVD 是唯一能给出稳定解的方法,但更根本的解决方案是重新采集数据。

记住,最小二乘不是万能的。它只能帮你找到“给定数据下的最优解”,但如果数据本身有问题,再好的算法也救不了。所以,我每次做标定前,都会花时间检查数据质量——这比纠结用哪种求解方法重要得多。