第四节 最小二乘法求解:线性最小二乘原理、超定方程组求解、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分解、取伪逆。但每一步都有坑。
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 是唯一能给出稳定解的方法,但更根本的解决方案是重新采集数据。
记住,最小二乘不是万能的。它只能帮你找到“给定数据下的最优解”,但如果数据本身有问题,再好的算法也救不了。所以,我每次做标定前,都会花时间检查数据质量——这比纠结用哪种求解方法重要得多。