简介:面向控制系统、信号处理与机器学习方向的学习者与工程人员,这份资源提供了一套可直接运行的最小二乘类参数辨识 MATLAB 程序,用于对稳定系统的参数进行在线估计与辨识。压缩包共 1 个 docx 文件,约 78KB,以文档形式完整收录程序代码与说明,便于对照阅读与复现。程序以 500 点 M 序列为输入、白噪声为扰动,构建极点位于单位圆内的稳定系统,分别实现递推最小二乘与渐消记忆最小二乘两种算法,并定义参数估计值 Theta、估计方差 P 与增益矩阵 K 完成递推更新,最后通过图形可视化展示参数估计过程。读者可从中掌握递推公式的编程实现、遗忘因子 0.998 的修改方式,以及两种算法辨识精度差异的对比思路,适合作为参数辨识入门练习与课程实验参考。目前已有 2162 人学习。
1. 递推最小二乘参数辨识:一份能直接跑通的 MATLAB 程序包
拿到一个二阶系统的差分方程,想在线把四个系数估出来,很多人第一反应是调arx或者tfest。工具箱当然能用,但黑匣子背后到底怎么递推、初值怎么影响收敛、为什么稳态总差那么一点,调包是看不出来的。这份最小二乘类参数辨识 MATLAB 程序包,走的是手写递推的路子:先用 M 序列做激励、白噪声做扰动,生成一组可复现的观测数据,再用递推最小二乘(RLS)逐点更新参数估计,最后把估计轨迹和真值画在一张图上对比。它适合正在学系统辨识、需要交课程设计、或者想给自研辨识模块找个干净参考实现的从业者。程序不长,但把数据生成、递推更新、协方差阵演化、渐消记忆改进这几件事都摆在了明面上,改参数、换模型都方便。
2. 系统建模与数据生成:M 序列激励怎么造
2.1 待辨识系统与差分方程
程序针对的是一个二阶线性差分方程,写成标准形式是:
Z(k+2) = 1.8*Z(k+1) - 0.9*Z(k) + 0.75*u(k+1) + 0.5*u(k) + v(k)这里Z是系统输出,u是输入,v是白噪声扰动。把它整理成最小二乘的标准回归形式,待估参数向量是Theta = [a1; a2; b1; b2],对应真值[1.8; -0.9; 0.75; 0.5](注意程序里画图对比线用的是-1.8和0.9,是因为回归向量里输出项带了负号,后面会讲清楚)。特征方程z^2 - 1.8z + 0.9 = 0的极点模长为sqrt(0.9) ≈ 0.949,落在单位圆内,系统稳定——这一点很关键,不稳定系统的输出会发散,递推过程根本收敛不了,做辨识仿真前先确认极点位置是基本习惯。
选这个系统做例子有两个好处:一是二阶、四参数,规模小到能手工验算,又能体现递推算法的完整结构;二是极点靠近单位圆但仍在圆内,收敛过程有肉眼可见的过渡段,方便观察初值影响和渐消记忆的效果差异。
2.2 M 序列的生成逻辑与寄存器初值
输入激励没有用阶跃或正弦,而是用了 M 序列(伪随机二进制序列)。原因很直接:阶跃信号激励频带窄,正弦一次只有一个频率,而 M 序列近似白噪声,能持续激励系统的所有模态,参数可辨识性才有保障。程序用 9 级移位寄存器加异或反馈生成:
%==========产生 500 点的 M 序列作为输入============== x=[0 1 0 1 1 0 1 1 1]; %寄存器初值 n=503; %脉冲数目 N=500; for i=1:n temp=xor(x(4),x(9)); M(i)=x(9);%M 序列 for j=9:-1:2 x(j)=x(j-1); end x(1)=temp; end逐行说明:x是 9 个寄存器的初值,不能全零,否则异或反馈永远是零,序列退化成常数;xor(x(4),x(9))取第 4 位和第 9 位做异或,这是 9 级 M 序列的一个常用反馈抽头组合,周期为2^9 - 1 = 511;M(i)=x(9)取末位作为输出;内层循环把寄存器整体右移一位,再把反馈值塞回第一位。n=503比N=500多跑 3 拍,是为了让后面生成观测数据时索引不越界,属于工程上的小冗余。
提示:换反馈抽头或寄存器级数时,要查对应的本原多项式,随便挑两位异或很可能得到短周期序列,激励不充分,辨识结果会莫名其妙地偏。
2.3 白噪声扰动与观测序列构造
有了输入,接着生成扰动和输出:
%===========产生方差为 1 的白噪声======== v=randn(1,N); %==============生成观测数列================= z=zeros(N+2,1); z(1)=-1; z(2)=0; for i=3:N+2 z(i)=1.8*z(i-1)-0.9*z(i-2)+0.75*M(i-1)+0.5*M(i-2)+v(i-2); endrandn(1,N)生成零均值、方差为 1 的高斯白噪声,这是最小二乘理论假设里最标准的扰动形式。z预分配N+2长度,前两点z(1)=-1、z(2)=0是人为给的初始条件,相当于系统从非零状态起步,避免全零初值让前几步回归向量退化。循环从i=3开始,按差分方程递推,注意输入项用的是M(i-1)和M(i-2),噪声项用v(i-2),索引整体错开两位,和方程里Z(k+2)的写法对齐。
这里有个容易翻车的点:M序列长度是 503,而循环里最大取到M(i-1)即M(N+1),不会越界;但如果有人把N改大而忘了同步改n,就会报索引超限。我一般会把n直接写成N+3,让两者绑定,省得改一处漏一处。
3. 递推最小二乘的实现:四个矩阵的更新节奏
3.1 回归向量与参数更新公式
进入核心递推段,先看初始化:
P=100*eye(4); %估计方差 Pstore=zeros(4,N+1); Pstore(:,1)=[P(1,1),P(2,2),P(3,3),P(4,4)]; Theta=zeros(4,N+1); %参数估计值 Theta(:,1)=[1;1;1;1];%参数估计初值 K=[1;1;1;1];%增益矩阵初值P是参数估计的协方差阵,初值取100*eye(4),意思是“我对初值完全没信心”,给一个大的不确定度,让算法前期大幅修正;如果取太小,前期更新步子会很小,收敛慢得像蜗牛。Theta初值全给 1,和真值[1.8; -0.9; 0.75; 0.5]差得不小,正好用来观察收敛过程。Pstore只存对角元,因为画图看方差收敛看对角线就够了,存全矩阵没必要。
递推主体:
for i=3:N+2 h=[-z(i-1);-z(i-2);M(i-1);M(i-2)]; K=P*h*inv(h'*P*h+1); Theta(:,i-1)=Theta(:,i-2)+K*(z(i)-h'*Theta(:,i-2)); P=(eye(4)-K*h')*P; Pstore(:,i-1)=[P(1,1),P(2,2),P(3,3),P(4,4)]; endh是回归向量,把-z(i-1)、-z(i-2)、M(i-1)、M(i-2)竖着摞起来。注意输出项前面带负号,所以估计出来的Theta(1)会收敛到-1.8、Theta(2)收敛到0.9,这就是第 2 章说画图对比线用-1.8和0.9的原因——符号约定要和回归向量一致,不然图上看半天以为算法错了。
K是增益向量,P*h*inv(h'*P*h+1)里的1对应噪声方差为 1 的情形,如果噪声方差已知是sigma2,这里应换成sigma2。Theta更新用的是新息z(i)-h'*Theta(:,i-2),即实测输出减去预测输出,乘增益修正。P的更新(eye(4)-K*h')*P是标准 RLS 协方差递推,随着数据增多,P逐渐收缩,增益变小,参数趋于稳定。
3.2 索引对齐与结果可视化
递推里Theta(:,i-1)对应第i-1步的估计,循环从i=3跑到N+2,最终填满Theta的2到N+1列,第 1 列是初值。画图段:
i=1:N+1; figure(1) plot(i,Theta(1,:),i,Theta(2,:),i,Theta(3,:),i,Theta(4,:),i,-1.8,i,0.9,i,0.75,i,0.5) grid on; title('参数估计过程');四条估计曲线加四条常数参考线,横轴是递推步数。跑一遍能看到:前几十步曲线剧烈摆动,随后逐渐贴向参考线,但稳态时和真值之间始终留着一条小缝。这条缝不是程序写错了,而是 RLS 在有色噪声或有限数据下的固有偏差——白噪声方差为 1、数据只有 500 点,估计方差还没完全压下去。很多人第一次跑到这里会怀疑自己公式抄错了,其实这就是原文说的“辨识结果会与其精确值始终存在偏差”。
注意:
inv(h'*P*h+1)在四阶系统里没问题,但如果模型阶次升高、h'*P*h接近奇异,直接求逆数值上不稳,常见做法是改用/(h'*P*h+1)的右除,或者对P做对称化处理P=(P+P')/2。
4. 渐消记忆最小二乘:两个除法的改动与效果
4.1 遗忘因子的作用机理
标准 RLS 有个特点:随步数增加,旧数据权重不衰减,P单调收缩,算法逐渐“失去学习能力”。如果系统参数缓慢漂移,或者想压低稳态偏差,就得让旧数据“忘掉一点”,这就是渐消记忆(遗忘因子)最小二乘。做法是在协方差更新里引入遗忘因子lambda(通常 0.95 到 0.999),让P每步被放大一点,保持持续修正能力。
原文给的改法非常精简,只动两条语句:
% 原语句 % K=P*h*inv(h'*P*h+1); % P=(eye(4)-K*h')*P; % 渐消记忆改法 K=P*h*inv(h'*P*h+0.998); P=(eye(4)-K*h')*P/0.998;第一处把分母的1换成0.998,第二处在P更新后除以0.998。0.998就是遗忘因子,等价于给协方差阵每步乘1/0.998 ≈ 1.002,抵消收缩趋势。数值上0.998偏保守,记忆长度约1/(1-0.998) = 500步,和 500 点数据量匹配;如果系统时变更快,可以降到0.98甚至0.95,但太小会让估计抖动加剧,稳态反而更差。
4.2 改前改后的对比与参数选择
把两版程序各跑一次,对比Theta稳态段(比如后 100 步)的均值,能明显看到渐消记忆版的四个参数更贴近真值,原文说“得到了较为准确的结果”就是这个意思。但要注意,遗忘因子不是越小越好,它本质是在“跟踪速度”和“稳态精度”之间做权衡:
| 遗忘因子 | 记忆长度(约) | 适用场景 | 风险 |
|---|---|---|---|
| 1.0(标准 RLS) | 无限 | 时不变系统、数据充足 | 稳态偏差消不掉 |
| 0.998 | 500 步 | 本程序 500 点数据 | 偏差改善有限 |
| 0.99 | 100 步 | 慢时变系统 | 估计抖动增大 |
| 0.95 | 20 步 | 快时变、强跟踪 | 噪声放大明显 |
实操建议:先跑标准 RLS 看偏差量级,再从中等遗忘因子试起,每次只改一个值,把Theta稳态均值和标准差都记下来,别只看曲线“看起来更直”。我一般会在脚本末尾加一段统计输出,把最后 100 步的均值和真值做差打印出来,比肉眼靠谱。
% 稳态段统计,放在画图之后 tail = 100; Theta_ss = mean(Theta(:, end-tail:end), 2); true_val = [-1.8; 0.9; 0.75; 0.5]; err = Theta_ss - true_val; fprintf('稳态估计: %.4f %.4f %.4f %.4f\n', Theta_ss); fprintf('稳态偏差: %.4f %.4f %.4f %.4f\n', err);这段不改变算法,只是把“偏差到底多大”量化出来,方便比较不同遗忘因子。end-tail:end取末尾窗口,窗口太短统计不稳,太长又会把过渡段混进来,100 步对 500 点数据是个折中。
5. 避坑与排查:跑不通时先看这五条
5.1 现象:参数曲线一路发散不收敛
原因通常是系统极点跑到单位圆外,或者P初值给得离谱、回归向量里混入了未初始化的大数。先检查特征方程根的模长,再确认z(1)、z(2)和M的前几个值都不是NaN。解决:把P初值降到1*eye(4)试跑,若仍发散,多半是差分方程符号写反,逐项对照h的构造。
5.2 现象:估计值收敛到错误符号
这是回归向量符号约定和画图参考线不一致的典型翻车。h里输出项带负号,Theta(1)就该收敛到-1.8;如果参考线画成1.8,看起来就像算法把符号搞反了。解决:统一符号约定,要么h不带负号、参考线用正值,要么保持现状、参考线用负值,别两头各写一套。
5.3 现象:索引越界报错
改N忘了改n,或者把M序列长度截短,循环里取M(i-1)就越界。解决:把n写成N+3与N绑定,或在循环前加assert(numel(M) >= N+2)做前置检查,报错信息比 MATLAB 默认的清楚得多。
5.4 现象:渐消记忆改了反而更抖
遗忘因子取得太小(比如0.9以下),协方差被过度放大,增益K长期偏大,噪声直接灌进参数估计。解决:把遗忘因子往0.995以上调,同时用 4.2 节的稳态统计确认偏差是否真的改善;如果标准 RLS 偏差本来就小,没必要上渐消记忆。
5.5 现象:inv警告矩阵接近奇异
h'*P*h+1在数据激励不足时会接近零,inv给出警告甚至Inf。解决:改用右除K = P*h/(h'*P*h+1),数值更稳;同时检查 M 序列周期是否够长、是否真的激励到了二阶模态,激励不足是根子上的问题,数值技巧只能缓解。
6. 从仿真到实测:把这份程序改成能用的辨识脚本
仿真跑通只是第一步,真正拿它去处理实测数据时,有几处必须动。第一是数据入口:把z和M的生成段整段替换成从文件或工作区读入,长度N由数据决定,z(1)、z(2)用实测前两点,别再用人为初值。第二是噪声方差:递推里那个+1是按单位方差写的,实测噪声方差未知时,常见做法是先跑一遍标准 RLS,用残差z(i)-h'*Theta的样本方差估计sigma2,再回填到增益公式里,迭代一两轮就稳了。第三是阶次:这份程序固定四参数二阶,换系统时h的长度、P和Theta的维度要同步改,我一般把阶次写成变量na、nb,用zeros(na+nb,1)统一分配,避免手改漏项。
验证方法上,别只看参数曲线贴不贴真值。更硬的标准是看残差:把最终Theta代回差分方程,算预测输出和实测输出的残差序列,白噪声假设下残差应接近零均值、无自相关。可以加一段:
% 残差白性检查 z_hat = zeros(N+2,1); for i=3:N+2 h = [-z(i-1);-z(i-2);M(i-1);M(i-2)]; z_hat(i) = h' * Theta(:,end); end resid = z(3:end) - z_hat(3:end); fprintf('残差均值 %.4f, 标准差 %.4f\n', mean(resid), std(resid));残差均值明显偏离零,说明模型结构或阶次不对;标准差远大于噪声真值,说明还有未建模动态。这一步比看曲线客观,也是我从一次翻车里学来的:当时参数曲线漂亮得不像话,结果残差自相关严重,换到实测数据上直接崩,后来每次辨识完都强制走一遍残差检查,再决定要不要调阶次或换遗忘因子。
最后提醒一句,渐消记忆的遗忘因子和噪声方差估计是耦合的,改一个要重看另一个,别一次改三处然后对着结果猜是哪处起了作用。把这份程序当骨架,数据入口、阶次、噪声参数三处按实际场景替换,剩下的递推逻辑基本不用动。希望帮到你。
本文还有配套的精品资源,点击获取