☰
递推最小二乘算法程序与实验报告:从原理到工程避坑完整指南
2026/10/16 18:24:04 网站建设 项目流程

简介:围绕递推最小二乘(RLS)算法,资料包含Python程序代码、配套说明文档和Word版详细实验报告,适合正在学习自适应信号处理、系统辨识或参数估计的本科生与工程师,也可作为课程实验与毕业设计的参考资料。压缩包共3个文件,包含可直接运行的.py源码、txt程序说明和.doc实验报告,整体仅316KB,便于快速下载与阅读。代码按初始化、预测、误差计算、遗忘因子更新等步骤编写,结构清晰,注释简明;Word报告则按算法背景、原理介绍、代码实现解析、实验结果与分析展开,对递推公式推导和遗忘因子λ的影响均有详细说明。已有468人学习使用,内容理论结合实践,帮助读者从原理到实现完整掌握RLS在线参数估计方法,并迁移到实时系统或工程场景中,如在线预测、自适应滤波等。

1. 递推最小二乘算法程序代码及word版详细实验报告:这套资源到底在解决什么问题

“递推最小二乘算法程序代码及word版详细实验报告.rar”——看到这个标题的人,基本是同一类情况:自动控制或信号处理课程的实验要求里写着“实现递推最小二乘系统辨识”,或者毕业设计里需要一个能在线更新参数的估计模块。你下载这份资源,不是为了把推导过程背下来,而是想拿到一段能跑的代码,和一份能照着填的报告。本文就把这两件事讲透:递推最小二乘的更新公式到底在干什么、程序怎么写能跑通、Word报告里哪些内容才算“详细”,以及调试时最常见的几个翻车点。

这份资源通常解压后就是一个工程文件夹加一份Word文档,但真正值钱的不是某个人的代码,而是代码背后的参数选择和报告里的分析套路。我会先讲清楚算法原理,再给完整可复现的Python实现,然后拆解实验报告每个章节该写什么,最后集中说五个换着花样翻车的典型案例。新手能按步骤抄作业,想深究的人也能看到遗忘因子、持续激励、数值稳定性这些边界问题。

2. 递推最小二乘的数学模型:为什么它是批量最小二乘的在线升级版

2.1 从批量最小二乘到递推:用一行标量除法替代高维矩阵求逆

先看批量最小二乘的经典公式。假设系统满足线性回归关系:

y(k) = φᵀ(k) · θ + v(k)

收集了N组数据后,把所有回归向量堆成矩阵Φ,所有观测值堆成向量Y,参数估计就是:

θ̂ = (ΦᵀΦ)⁻¹ · Φᵀ · Y

这个公式简单、好用,但工程上有个致命限制:每来一组新数据,都要把旧的N+1行数据重新拼矩阵,然后重新做一个高维矩阵求逆。数据量到几千行之后,单次求逆的时间就会让实时辨识变得不现实。

递推最小二乘的思路是换一种累积方式:保留上一次的估计结果θ̂(k-1),只拿新到的y(k)和φ(k)去修正它。整个递推过程只需要四个方程:

e(k) = y(k) - φᵀ(k) · θ̂(k-1)

K(k) = P(k-1) · φ(k) / [λ + φᵀ(k) · P(k-1) · φ(k)]

P(k) = [I - K(k) · φᵀ(k)] · P(k-1) / λ

θ̂(k) = θ̂(k-1) + K(k) · e(k)

这里的P(k)对应批量最小二乘中的(ΦᵀΦ)⁻¹,表示当前参数估计的不确定度。K(k)是增益向量,决定新数据以多大权重修正旧估计。仔细观察就会发现,分母λ + φᵀPφ是一个标量,更新P矩阵时做的也只是外积和矩阵加减,全程没有一个高维矩阵求逆。这就是递推意义所在:计算量从O(N³)降到O(dim²),dim是参数个数,和累积数据量无关。

代码实现时,四个方程的先后顺序也很关键。第一步必须先算预测误差e(k),此时用的是旧参数θ̂(k-1);然后算增益K(k),接着更新P(k),最后才更新参数θ̂(k)。如果写成先更新参数再算误差,数学上也能自洽,但和教材的标准递推公式不对应,调试时容易和文档里的符号打架。

2.2 遗忘因子λ:在线更新的“记忆灰度”和一张取值表

递推最小二乘里唯一一个需要你手动调的参数就是遗忘因子λ,取值范围是0到1。它的作用从P(k)更新公式里能直接看出来:λ出现在分母,同时P(k)整体除以λ。λ接近1,P矩阵衰减慢,历史数据的影响持续很久;λ变小,P矩阵在每次迭代时被放大更多,让后面的新数据获得更大话语权。

用等效记忆长度来理解更直观。遗忘因子λ对应的记忆长度近似为1/(1-λ)个采样点。也就是说λ=0.99时,大约最近100个点对参数估计起主要作用;λ=0.95时,等效记忆长度只剩20步。实际使用中有一个普遍权衡:λ越小,跟踪时变参数越快,但对噪声越敏感,稳态时参数波动大;λ太大,估计稳定但反应迟钝。

λ取值等效记忆长度典型适用场景
0.99约100步系统参数缓慢漂移、测量噪声很小
0.98约50步教材和大多数实验的默认选择
0.95约20步参数变化较快,但需要接受更大的估计波动
0.90约10步快速时变系统,噪声大时慎用

初次做实验,我建议直接从λ=0.98起步。跑通之后把λ改成0.95和0.99各跑一次,对比参数收敛曲线的波动幅度和跟随时滞,这张对比图放进实验报告里,比任何文字都说明问题。

2.3 初始化三件套:θ(0)、P(0)、持续激励三者怎么配

递推最小二乘有四个初始化设置,任何一个出错都会让程序要么不收敛、要么直接数值爆炸。

第一个是参数初值θ̂(0)。一般取零向量。如果你对系统已有先验认识,可以填补估计值,但实验场景下零向量最稳妥,让算法自己花几十步收敛过去。

第二个是协方差矩阵初值P(0)。通常取大对角矩阵P(0) = αI,α从1e2到1e6都可以。P(0)大的含义是“我对初始参数完全没信心”,这样头几步的增益K会比较大,参数能快速从零拉到真实值附近。但α太大会看到参数初期的明显过冲;太小则收敛极慢,跑完整个数据序列参数还没到位。实验里常用P(0) = 1e4·I,和λ=0.98、θ(0)=0搭配。

第三个输入信号必须满足持续激励条件。通俗说,输入u(k)要足够“丰富”,不能是常数或者单一频率的正弦波。因为递推最小二乘是靠回归向量φ(k)的各个方向来更新参数,如果输入只在一个方向上变化,P矩阵只有对应方向衰减,其他方向的参数永远得不到修正。实验中用均匀白噪声或者伪随机二值序列(PRBS)最保险。

第四个容易被忽略的就是初始段处理。由于θ(0)=0和P(0)=1e4,前20到50步的参数轨迹往往有明显跳变。这不算发散,但实验报告里画收敛曲线时,最好把热身段和数据采信段分开说明。

3. 递推最小二乘程序实现:仿真数据、核心函数、收敛对照全套代码

3.1 生成测试数据:二阶ARX加噪模型与持续激励输入

为了验证算法,先构造一个已知参数的仿真对象。这里用二阶ARX模型:

y(k) = a1·y(k-1) + a2·y(k-2) + b1·u(k-1) + b2·u(k-2) + v(k)

真实参数取a1=1.2,a2=-0.35,b1=0.6,b2=0.4。输入u用均匀白噪声,测量噪声v用零均值高斯分布。代码如下:

import numpy as np import matplotlib.pyplot as plt # 可复现随机种子,改数字观察不同噪声实现的差异 rng = np.random.default_rng(42) n = 500 u = rng.uniform(-1, 1, n) # 均匀白噪声,满足持续激励 v = rng.normal(0, 0.1, n) # 测量噪声 y = np.zeros(n) for k in range(2, n): y[k] = (1.2 * y[k-1] - 0.35 * y[k-2] + 0.6 * u[k-1] + 0.4 * u[k-2] + v[k])

代码逻辑很简单:先初始化输入和输出序列,然后从第3个点开始递推生成输出。注意y[0]和y[1]保持为0,相当于系统从静止状态启动。均匀白噪声比正弦信号更能满足持续激励条件,这是ARX辨识实验的通用做法。噪声方差0.1会让参数收敛曲线带有可观察的波动,但不会掩盖收敛趋势;如果改成0.01,曲线会平滑很多,报告里可以两组都跑。

3.2 RLS核心递推函数:误差、增益、协方差、参数四步更新的顺序

核心算法封装成一个函数,方便在不同实验中复用。参数向量维度是4,对应[a1, a2, b1, b2]:

def rls(u, y, lam=0.98, p0=1e4): """递推最小二乘,4参数版本。 返回参数轨迹矩阵和最终估计参数。 """ n = len(u) theta = np.zeros(4) # [a1, a2, b1, b2] P = p0 * np.eye(4) # 大对角初值表示完全未知 theta_hist = np.zeros((n, 4)) for k in range(2, n): # 回归向量,顺序必须与theta一致 phi = np.array([y[k-1], y[k-2], u[k-1], u[k-2]]) e = y[k] - phi @ theta # 1. 预测误差 Pphi = P @ phi K = Pphi / (lam + phi @ Pphi) # 2. 增益向量,分母是标量 P = (P - np.outer(K, Pphi)) / lam # 3. 协方差更新 theta = theta + K * e # 4. 参数更新 theta_hist[k] = theta return theta_hist, theta

四步更新顺序是标准递推公式的顺序,错一步结果对不上教材。增益K的分母是一个标量,直接做除法就行,不需要调用np.linalg.inv。P矩阵更新里的np.outer(K, Pphi)等价于K乘以Pphi的转置,但写法更直观,也避免了构造中间矩阵。如果忘记除以λ,P矩阵会不断膨胀,到达一定步数后参数开始剧烈震荡,后面避坑章节会专门说这个问题。

调用方式很简单:

theta_hist, theta = rls(u, y) # 打印最终估计结果 print("RLS最终估计:", theta)

运行这个函数,theta_hist每一行对应每一步的参数估计值。前期会有明显波动,中后期逐渐平稳,这就是收敛过程的可视化素材。

3.3 跑通后的验证:参数收敛曲线与批量最小二乘对照

有仿真对象时,批量最小二乘的结果可以作为“标准答案”来对照。批量版本直接套公式:

# 批量最小二乘,结果作为参考标准 Phi = np.array([ [y[k-1], y[k-2], u[k-1], u[k-2]] for k in range(2, n) ]) Y = y[2:] theta_batch = np.linalg.inv(Phi.T @ Phi) @ Phi.T @ Y

这里直接把数据矩阵堆出来做一次整体求逆,在仿真数据量不大时算得很快,也和递推结果形成直接对比。你会看到RLS运行到最后,估计值应该和批量最小二乘非常接近,差在第三位小数以后,这正是算法正确性的有力证据。

画收敛曲线的代码:

true_theta = np.array([1.2, -0.35, 0.6, 0.4]) fig, axes = plt.subplots(2, 2, figsize=(10, 6)) names = ['a1', 'a2', 'b1', 'b2'] for ax, name, est, true in zip(axes.flat, names, [theta_hist[:, j] for j in range(4)], true_theta): ax.plot(est, lw=1.8, label=name) ax.axhline(true, color='r', ls='--', lw=1, label='真实值') ax.set_title(name) ax.grid(True) ax.legend() plt.tight_layout() plt.show()

这段绘图逻辑把四个参数分别画在2×2的子图里,每个子图加一条真实值的红色虚线。曲线在开始阶段会有一个较大的摆尾,这是P(0)取大对角阵造成的,不是代码错误。要得到更平滑的展示,可以跳过前50个点的轨迹不画,或者直接把收敛段单独截出来放大。

验证时还可以做一个蒙特卡洛实验:换不同的随机种子,运行50次,记录最终估计值的均值和方差。均值应接近真实参数,方差反映噪声和遗忘因子共同决定的稳态波动水平。这部分数据放在实验报告里,可以明显提升“详细”程度。

4. Word实验报告怎么搭:六段式结构、图表规范和三张必写的表

4.1 报告骨架:实验目的到结论的六个章节与每处该写什么

Word版详细实验报告通常不是要求你写成长篇论文,而是按“实验目的、实验原理、实验设备、实验步骤、结果与分析、结论”六个模块填满。每个模块的篇幅和侧重点完全不同,一张表说清楚:

章节核心内容篇幅建议
实验目的用2到3句话说明RLS能干什么半页以内
实验原理从批量最小二乘引出递推公式,标出每个符号含义1到2页,公式占一半
实验设备操作系统、Python版本、NumPy版本、运行环境5到8行
实验步骤数据生成、RLS递推、绘图验证的执行顺序1页以内
结果与分析收敛曲线、残差分析、参数对比表2到3页,重点是分析
结论算法是否收敛、参数是否准确、局限和可改进点半页以内

实验目的不要写成套话。更合适的写法是直接给具体能力:“通过本实验理解递推最小二乘的在线更新机制,掌握遗忘因子对跟踪性能的影响,能够用RLS对含噪声ARX模型进行参数辨识。”这样读起来像一份技术备忘,而不是从教材里摘出的定义。

实验原理部分要用Word公式编辑器插入递推四式,而不是截图。这里需要逐一解释e(k)、K(k)、P(k)、θ̂(k)的物理含义,特别强调P(k)对应批量最小二乘里的(ΦᵀΦ)⁻¹。实验设备里不要出现模糊的“某版本Linux”,直接写实际运行环境,比如Windows 11 + Python 3.11 + NumPy 1.26。

4.2 结果分析的三个固定动作:收敛曲线、残差自相关、参数表格

结果与分析的写法决定了报告是“详细”还是“流水账”。这三个动作每一个都对应一个具体产出。

第一个动作是收敛曲线图。用上一章的2×2子图,图题写“图1 RLS参数收敛过程”,四个子图分别对应a1、a2、b1、b2。每张子图都要有红色真实值虚线作为参照,否则读者只能看到曲线无法判断误差大小。如果λ改过多个值,应把λ=0.98、0.95、0.99三组收敛曲线放在同一张图里,用不同颜色区分,说明遗忘因子对收敛速度和波动的影响。

第二个动作是残差自相关分析。计算每个采样点的残差:

e = np.zeros(n) for k in range(2, n): phi = np.array([y[k-1], y[k-2], u[k-1], u[k-2]]) e[k] = y[k] - phi @ theta

然后用matplotlib的acorr函数画残差自相关图,或者直接观察残差序列是否像白噪声。残差如果仍有明显周期成分,说明模型阶次不够或遗漏了常数项,这是提升报告深度的重要切入点。

第三个动作是参数对比表,这是整个报告中信息密度最高的一张三线表。表格至少五列:

参数真实值RLS估计批量LS估计相对误差(%)
a11.2填入运行结果填入运行结果计算后填入
a2-0.35填入运行结果填入运行结果计算后填入
b10.6填入运行结果填入运行结果计算后填入
b20.4填入运行结果填入运行结果计算后填入

相对误差的计算用|估计值-真实值|/|真实值|×100%。RLS和批量LS的结果应当基本一致,如果差距明显,说明递推还没收敛到稳态,需要增加数据点数或检查初始化参数。

4.3 代码怎么摆进Word:正文只放核心段,完整代码放附录

报告里最容易出现的问题是贴一大堆代码,结果正文被代码块占掉大半。正确的排版方式是:正文只放RLS核心函数这段大约15行的代码,数据生成和绘图细节放到附录。

放正文的代码要满足一个条件——读者不运行任何脚本,光看代码就能理解递推四步的顺序。所以核心函数里要保留中文注释,变量名和报告公式里用到的符号尽量一致。批量最小二乘公式和蒙特卡洛验证代码放附录,结果与分析里通过“详见附录B”引用。

Matplotlib画图时还要处理一个很实际的中文乱码问题。默认字体不识别中文时,图题和标签全是方框,报告观感大打折扣。在绘图脚本开头加三行配置:

import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False

SimHei在大多数Windows环境可用。如果字体设置后仍然乱码,检查系统是否有该字体,或者换成微软雅黑。图的编号、表的编号要手动维护,每一张图必须有图题,每一张表必须有表题,引用的时候写“图1”而不是“下图”。这些细节凑在一起,才是“word版详细实验报告”该有的完成度。

5. 递推最小二乘避坑排查:五个换着花样翻车的典型案例

5.1 P(0)设成0:程序跑通了,参数却一行不动的“假收敛”

现象:代码不报错,循环正常执行,但theta_hist整条曲线是一条水平直线,参数始终是初始值。

原因:增益K(k)的计算公式里分子是P(k-1)·φ(k),P(0)=0时分子永远是0,K恒为零,θ̂(k)永远等于θ̂(k-1)。整个递推过程退化成“空转”。

解决:P(0)必须用大对角矩阵初始化,实验里P(0)=1e4·I已经足够。如果你对系统有把握,可以把α调小,但千万不要设0或者极小值。遇到参数不更新的问题,第一反应就检查P矩阵初值,这个坑非常经典。

5.2 λ取1:时变系统面前,算法变成一条反应迟钝的鲸鱼

现象:仿真系统原本参数不变,某一步突然把a1从1.2改成0.8,RLS估计值需要几百步才缓慢爬向新值,期间预测误差显著增大。

原因:λ=1时等效记忆长度无穷大,算法把历史所有数据等权看待。系统参数突变了,但旧数据还占主导,参数自然追不上。

解决:改为λ=0.95到0.99。想要跟随时变参数,λ就必须小于1,具体数值根据时变速度和噪声水平折中。判断λ是否合适的简单方法是看“参数突变后50步内是否明显接近新值”:是则合理,否则继续调小λ。

5.3 输入信号不满足持续激励:部分参数锁死、部分参数乱跳

现象:输入u(k)用常数或单一频率正弦波,最终估计结果中b1、b2看起来正常,a1、a2却明显偏离真实值,有时还漂移不定。

原因:递推最小二乘靠回归向量的信息更新P矩阵,输入激励不足时,P矩阵某些特征方向衰减极慢,对应的参数得不到有效修正。本质是持续激励条件不满足,信息矩阵接近奇异。

解决:把输入换成白噪声或PRBS序列,保证φ(k)各个方向都有激励。更规范的做法是算一下回归向量的自相关矩阵条件数,条件数过大就先处理输入序列再辨识。

5.4 P更新漏掉除以λ:前100步正常,第500步开始数值爆炸

现象:前期参数收敛得很好,到一定步数之后参数曲线开始剧烈震荡,甚至出现NaN。回头看代码,P更新那行只写了P = P - np.outer(K, Pphi),漏掉了除以λ。

原因:递推公式里P(k) = [I - K(k)φᵀ(k)]·P(k-1)/λ,漏掉λ后P矩阵只减不增,在持续激励不足的方向上会被反复累积数值误差,最终导致协方差矩阵失去正定性,参数发散。这是我见过频率最高的血泪经验。

解决:逐行对照公式检查P更新行,确认分母上的λ还在。调试时可以打印P矩阵的特征值,如果出现负特征值,多半就是这行写错。

5.5 数据含直流分量但模型没有常数项:参数整体偏移,看上去还挺合理

现象:仿真时给u或者y叠加了一个常数偏置,但RLS模型仍然是φ = [y(k-1), y(k-2), u(k-1), u(k-2)],最后四个参数全部收敛,残差也不大,但对照真实值整体偏移。

原因:模型缺少常数项,直流分量无法被参数线性组合解释,于是被强行分摊到a1、a2、b1、b2上,导致参数整体偏置。这种错误看起来“合理”,因为残差没有明显异常,极易漏检。

解决:处理办法有两种——对u和y先做去均值预处理,或者在回归向量里加一列常数1,同时把参数维度扩大到5。第二种更通用,毕竟工程数据里直流分量很难完全避免。

6. 递推最小二乘的工程进阶:自适应遗忘因子和平方根滤波怎么选

6.1 普通RLS在工程里的两个软肋:固定记忆长度和数值正定性损耗

固定遗忘因子意味着无论系统变化快慢,记忆长度始终一样。系统长期平稳时,较小的λ带来不必要的波动;系统突然变化时,较大的λ又反应不过来。自适应遗忘因子的思路是用残差能量来判断系统是否在变化,从而动态调整λ。

数值正定性损耗是普通RLS在长序列或持续激励不足时的隐患。P矩阵理论上是正定对称阵,但浮点运算中反复乘除法会逐渐破坏这一性质。对于几千步的课程实验,影响几乎看不到;但嵌入式设备上跑几万步,或者信号本身病态时,就可能出问题。

6.2 自适应遗忘因子:用残差窗口让λ自己动

一种工程上常用的做法是维护一个残差滚动窗口,窗口内残差的RMS值越大,说明当前模型对系统变化的跟踪越差,就把λ调小,让算法更快地“忘记”旧数据,加速追赶。下面这段代码演示了这个思路,模型带常数项,维度5:

def rls_adaptive(u, y, p0=1e4, win=30, lam_base=0.98): n = len(u) dim = 5 theta = np.zeros(dim) P = p0 * np.eye(dim) hist = np.zeros((n, dim)) lam = lam_base e_win = [] for k in range(2, n): phi = np.array([y[k-1], y[k-2], u[k-1], u[k-2], 1.0]) e = y[k] - phi @ theta # 滚动残差窗口 e_win.append(e) if len(e_win) > win: e_win.pop(0) rms_e = np.sqrt(np.mean(np.square(e_win))) # 残差越大,λ越小;0.1是归一化基准,按噪声量级调整 lam = max(0.90, lam_base - 0.05 * (rms_e / 0.1)) Pphi = P @ phi K = Pphi / (lam + phi @ Pphi) P = (P - np.outer(K, Pphi)) / lam theta = theta + K * e hist[k] = theta return hist, theta

这段代码里的自适应规则是线性的,不是严格推导出来的最优策略,但在很多实际场景下够用。关键是归一化基准0.1必须根据真实噪声方差来设定:噪声本身RMS就是0.3,那这个阈值会让λ一直被压到最低,反而丢失了自适应能力。第一步先测量系统稳定时的残差RMS,再设定基准值,这是调参的第一原则,不算玄学。

6.3 平方根滤波:数值稳定性优先的场景才值得换

平方根滤波的原理不复杂:不直接递推P矩阵,而是递推P的平方根因子,每一步保证P正定。实现上比普通RLS多几行矩阵分解运算,比如LDL分解或Cholesky因子更新。普通RLS和平方根滤波的取舍可以这样判断:

对比项普通RLS平方根滤波
实现难度低,四行公式搞定中高,因子更新逻辑复杂
数值稳定性长序列下可能丢失正定性稳定,适合单精度环境
适用场景课程实验、万步以内嵌入式实时系统、超长序列

课程实验和论文仿真里,普通RLS足够;如果做的是嵌入式实时辨识,数据量动辄十万步级别,或者只能用单精度浮点,平方根滤波才值得投入时间改造。

我现在的习惯是任何新系统上先跑普通RLS,固定λ=0.98把整体流程走通,确认模型结构和数据质量没问题之后,再考虑是否升级成自适应遗忘因子或平方根滤波。直接上复杂算法会引入额外变量,出问题时根本分不清是模型问题还是数值问题。这个顺序能省掉大量排查时间,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询