校正算法入门:三种核心方法

好,我们正式开始聊校正算法。说实话,很多初学者一听到「算法」两个字就头大,觉得肯定要推一堆公式。其实没那么玄乎。校正算法的本质,就是找到一条曲线——一条能把「测出来的值」映射回「真实值」的曲线。

我个人习惯把校正算法分成三类:最小二乘法拟合、查找表方法、多项式逼近法。这三条路各有各的脾气,选对了事半功倍,选错了……嗯,我在项目里吃过亏,后面会细说。

核心观点:没有最好的算法,只有最合适的场景。你选哪种方法,取决于你的硬件资源、精度要求、实时性需求。

余弦响应校正算法 · 三种核心方法 校正算法 最小二乘法拟合 全局误差最小 · 离线计算 查找表方法 速度快 · 精度可控 · 占内存 多项式逼近法 计算量小 · 适合嵌入式 选型要点 • 有离线标定数据 → 最小二乘法 • 需要极快响应 → 查找表 • 资源受限的MCU → 多项式逼近

4.1 最小二乘法拟合

先讲我最常用的方法——最小二乘法。说白了,就是给你一堆散点,你找一条曲线,让所有点到这条曲线的「垂直距离的平方和」最小。为什么是平方和?因为平方能放大大误差,让算法优先照顾那些偏差大的点。

我记得第一次做传感器校准时,拿到一组余弦响应数据,直接拿线性拟合去套,结果误差大得离谱。后来才意识到,余弦响应是非线性的,得用多项式或指数模型去拟合。

我的经验:对于余弦响应校正,一般用 3~5 阶多项式做最小二乘拟合就够了。阶数太低欠拟合,阶数太高过拟合——我在一个项目里试过 8 阶,结果在校准点之间疯狂振荡,还不如 3 阶稳。

最小二乘法的实现步骤其实很固定:

  1. 采集标定数据:在已知角度下记录传感器输出值,至少 10~20 个点
  2. 选择模型:比如 y = a₀ + a₁x + a₂x² + a₃x³
  3. 构造正规方程:用矩阵形式 XᵀXa = Xᵀy
  4. 求解系数:用高斯消元或 Cholesky 分解
  5. 验证残差:检查拟合误差是否在容忍范围内

代码实现也不复杂。下面是一个 Python 示例,用 numpy 做 3 阶多项式拟合:

import numpy as np

# 标定数据:角度(度) -> 传感器输出
angles = np.array([0, 15, 30, 45, 60, 75, 90])
outputs = np.array([1.00, 0.97, 0.87, 0.71, 0.50, 0.26, 0.00])

# 构造 Vandermonde 矩阵(3阶)
A = np.vander(angles, 4, increasing=True)

# 最小二乘解:A^T A coeff = A^T y
coeff, _, _, _ = np.linalg.lstsq(A, outputs, rcond=None)

print("拟合系数:", coeff)
# 输出类似: [ 1.002, -0.003, -0.00015,  0.000002 ]

注意:当标定点数量少或者分布不均匀时,正规方程可能病态。我曾经遇到过矩阵条件数大到 10⁶,算出来的系数稍微一抖动,校正值就飞了。这时候可以用 SVD 分解或者加正则化项(岭回归)来稳定解。

4.2 查找表方法

查找表(LUT)是最直观的方法。你想想看,既然我们知道每个角度对应的真实值,为什么不直接存一张表呢?运行时查表加插值,又快又准。

查找表的精髓在于「空间换时间」。你预先算好一批离散点的校正值,运行时根据当前测量值找到最近的两个表项,做线性插值就完事了。

特性 说明
精度 取决于表密度和插值方式。256 点 + 线性插值通常能到 0.1°
速度 O(1) 查表 + 一次插值,适合实时系统
内存 每增加一倍点数,内存翻倍。嵌入式里要精打细算
灵活性 换传感器只需重新烧表,不用改算法代码

我建议你在做原型验证时先用查找表。为什么?因为调试方便。我在一个激光雷达项目里,前期用查找表快速验证了校正效果,后期才换成多项式逼近来节省内存。查找表就像你的「安全网」——先保证功能正确,再优化资源。

小技巧:表项不一定要均匀分布。在余弦响应变化剧烈的区域(比如 0° 附近)可以加密,在变化平缓的区域(比如 60°~90°)可以稀疏。这叫「非均匀量化」,能在同样表大小下提升精度。

4.3 多项式逼近法

多项式逼近和最小二乘法有点像,但思路不同。最小二乘法是「拟合已知数据点」,多项式逼近是「用多项式近似一个已知函数」。说白了,如果你已经知道传感器的理论响应公式(比如 cosθ),你可以直接用一个低阶多项式去近似它,而不需要做实验标定。

常用的逼近方法有:

  • 泰勒展开:在 0° 附近展开,适合小角度场景。但离展开点越远误差越大
  • 切比雪夫逼近:在整个区间内均匀分布误差,比泰勒更「公平」
  • 帕德逼近:用有理函数(分子分母都是多项式),对某些非线性曲线效果更好

举个例子,cosθ 在 0° 附近的 4 阶泰勒展开是:

cos(θ) ≈ 1 - θ²/2 + θ⁴/24   (θ 是弧度)

当 θ = 30°(0.524 rad)时:
真实值:cos(30°) = 0.8660
泰勒近似:1 - 0.1373 + 0.0031 = 0.8658
误差:0.0002,相当不错

曾经踩过的坑:我在一个项目中直接用泰勒展开做全量程(0°~90°)校正,结果到 80° 附近误差飙到 5% 以上。后来换成切比雪夫逼近,最大误差控制在 0.3% 以内。记住:泰勒只适合局部,切比雪夫才适合全局。

三种方法怎么选?

我整理了一个对比表,方便你快速决策:

维度 最小二乘法 查找表 多项式逼近
是否需要标定数据 是 是 否(已知理论模型即可)
计算复杂度 中(离线计算系数) 低(查表+插值) 低(几次乘加)
内存占用 低(只存几个系数) 高(存整个表) 低(只存几个系数)
精度可控性 高(增加阶数即可) 高(增加表密度即可) 中(受逼近函数限制)
适用场景 有标定数据、离线处理 实时系统、资源充足 嵌入式MCU、已知理论模型

我个人习惯的选型思路是这样的:如果手头有标定设备,优先用最小二乘法做 3~5 阶多项式拟合,系数存下来,运行时直接计算。如果 MCU 算力太弱(比如 8 位单片机),就换成查找表加线性插值。如果连标定设备都没有,只能用理论模型,那就用切比雪夫逼近。

一句话总结:最小二乘法是「数据驱动」,查找表是「空间换时间」,多项式逼近是「模型驱动」。三种方法不是互斥的——我在实际项目中经常混合使用,比如用最小二乘法生成查找表,或者用多项式逼近做查找表的压缩。