4、最小二乘法基础:离线最小二乘(LS)与递推最小二乘(RLS)原理
说到参数辨识,最小二乘法是绕不开的基础。我入行那会儿,第一次接触电机参数辨识,师傅丢给我一堆数据说:「用最小二乘算一下。」我当时一脸懵,心想这玩意儿到底怎么把电阻电感算出来的?后来啃了几本书,又亲手调过几版代码,才算真正搞明白。
说白了,最小二乘法的核心就一句话:找一组参数,让模型输出和实际测量值的误差平方和最小。你想想看,我们建电机模型,不就是想让模型算出来的电流、转速跟实际测到的尽量接近吗?
核心思想:误差平方和最小化。数学上就是求解一个优化问题,让残差向量的二范数最小。
4.1 离线最小二乘(LS)—— 一次算清
离线最小二乘,也叫批处理最小二乘。它的做法很简单:先把所有数据都采集完,然后一次性把参数算出来。
假设我们有一个线性模型:
y = θ₁·x₁ + θ₂·x₂ + ... + θₙ·xₙ + e
写成矩阵形式就是:
Y = X·θ + E
其中 Y 是测量值向量,X 是数据矩阵,θ 是待求参数向量,E 是误差向量。
那么最小二乘的解就是:
θ̂ = (Xᵀ·X)⁻¹·Xᵀ·Y
这个公式看着简单,但实际用起来有几个坑。我在项目中遇到过一个问题:采集了1000组数据,直接套公式算,结果参数完全不对。后来一查,发现 Xᵀ·X 这个矩阵接近奇异,根本求不了逆。
注意:Xᵀ·X 必须满秩才能求逆。如果数据不够丰富,或者某些输入高度相关,这个矩阵就可能奇异或病态。我建议在计算前先检查条件数(condition number),如果太大就要考虑加正则化或者重新设计实验。
离线LS的优点很明显:
- 精度高——用了全部数据,统计特性好
- 实现简单——一个公式搞定
- 适合离线分析——做仿真、标定的时候很实用
缺点也很突出:
- 计算量大——矩阵求逆是 O(n³) 的复杂度,数据量大了扛不住
- 不能在线更新——新数据来了必须全部重算
- 存储开销大——所有历史数据都得存着
所以,离线LS更适合做离线标定、实验室分析。要是想在电机运行过程中实时更新参数,就得用递推最小二乘了。
4.2 递推最小二乘(RLS)—— 边跑边学
递推最小二乘,说白了就是「在线版」的最小二乘。它的思路是:每来一个新数据,就用当前的结果加上一个修正项,得到新的参数估计。不用重新算全部数据,效率高很多。
RLS的递推公式长这样:
K(k) = P(k-1)·φ(k) / [λ + φᵀ(k)·P(k-1)·φ(k)]
θ̂(k) = θ̂(k-1) + K(k)·[y(k) - φᵀ(k)·θ̂(k-1)]
P(k) = [I - K(k)·φᵀ(k)]·P(k-1) / λ
其中:
- θ̂(k) 是第 k 步的参数估计
- φ(k) 是第 k 步的回归向量(输入数据)
- y(k) 是第 k 步的测量值
- K(k) 是增益向量,决定修正力度
- P(k) 是协方差矩阵,反映估计的不确定性
- λ 是遗忘因子,0 < λ ≤ 1
我的经验:遗忘因子 λ 很关键。λ=1 就是标准RLS,所有数据权重一样;λ<1 会逐渐「遗忘」旧数据,适合时变系统。我一般从 λ=0.98 开始调,如果参数变化快就降到 0.95,变化慢就升到 0.995。但注意,λ 太小会导致协方差矩阵 P 膨胀,参数会剧烈抖动。
RLS的流程可以概括为:
- 初始化——设置 θ̂(0) 和 P(0)。我习惯用 P(0)=α·I,α 取 100~1000,表示初始不确定性大
- 预测误差——计算 e(k) = y(k) - φᵀ(k)·θ̂(k-1)
- 计算增益——K(k) = P(k-1)·φ(k) / [λ + φᵀ(k)·P(k-1)·φ(k)]
- 更新参数——θ̂(k) = θ̂(k-1) + K(k)·e(k)
- 更新协方差——P(k) = [I - K(k)·φᵀ(k)]·P(k-1) / λ
- 循环——回到步骤2,等待下一个数据
下面这张图展示了RLS的核心逻辑:
4.3 LS 与 RLS 的对比
这两种方法各有各的用武之地。我整理了一个对比表,方便你快速把握:
| 对比项 | 离线最小二乘(LS) | 递推最小二乘(RLS) |
|---|---|---|
| 数据使用方式 | 一次性使用全部数据 | 逐点递推,边来边算 |
| 计算复杂度 | O(N·n² + n³),N为数据量 | O(n²) 每步,n为参数个数 |
| 存储需求 | 大,需存所有数据 | 小,只需存当前状态 |
| 在线更新能力 | 不支持,需重算 | 支持,实时更新 |
| 时变系统适应性 | 差,无法跟踪变化 | 好,遗忘因子可调 |
| 典型应用场景 | 离线标定、实验室分析 | 在线辨识、自适应控制 |
4.4 实际应用中的注意事项
我在实际项目中踩过不少坑,这里分享几个关键点:
数据激励问题:RLS虽然能在线更新,但如果输入信号不够丰富(比如电机恒速运行),参数估计会发散。我遇到过一台风机,转速基本不变,RLS跑着跑着参数就飘了。解决办法是注入持续激励信号,比如在电流环上加一个小幅的PRBS(伪随机二进制序列)。
数值稳定性:RLS的协方差矩阵 P(k) 理论上是对称正定的,但实际计算中由于舍入误差,可能会失去正定性。我建议每步更新后强制对称化:P = (P + Pᵀ)/2。另外,如果发现 P 矩阵对角线元素异常增大,说明数值不稳定了,需要重置或者改用平方根RLS。
还有一个容易被忽略的点:初始参数的选择。如果 θ̂(0) 离真实值太远,RLS初期会有很大的超调。我一般先用离线LS跑一小段数据得到一个初始值,再切换到RLS模式。这样既保证了初值合理,又能在线跟踪变化。
4.5 一个简单的RLS代码示例
下面是一个Python风格的RLS实现,用于辨识一个一阶系统:
import numpy as np
class RLS:
def __init__(self, n_params, lambda_=0.98, P0=1000):
self.n = n_params
self.lambda_ = lambda_
self.theta = np.zeros(n_params)
self.P = P0 * np.eye(n_params)
def update(self, phi, y):
# 预测误差
e = y - np.dot(phi, self.theta)
# 增益向量
denom = self.lambda_ + np.dot(phi, np.dot(self.P, phi))
K = np.dot(self.P, phi) / denom
# 更新参数
self.theta = self.theta + K * e
# 更新协方差
self.P = (np.eye(self.n) - np.outer(K, phi)) @ self.P / self.lambda_
# 强制对称
self.P = (self.P + self.P.T) / 2
return self.theta.copy()
# 使用示例
rls = RLS(n_params=2, lambda_=0.98)
for k in range(1000):
phi = np.array([data_x1[k], data_x2[k]])
y = data_y[k]
theta_est = rls.update(phi, y)
关键点:代码中的 np.outer(K, phi) 就是 K(k)·φᵀ(k) 的向量化实现。强制对称那一步虽然多了一点点计算,但能避免很多数值问题。我建议你在实际代码里都加上这一行。
好了,这一章的内容就到这里。最小二乘法是参数辨识的基石,离线LS和递推RLS各有各的适用场景。搞懂了这两个,后面再学扩展卡尔曼滤波、粒子滤波这些高级方法,你就能理解它们是怎么在最小二乘的基础上一步步演进的。