☰
最小二乘法系统辨识MATLAB程序:从数据到模型的落地路径
2026/10/13 18:08:03 网站建设 项目流程

简介:这份MATLAB程序包面向自动化、控制工程及信号处理方向的学习者与研究人员,用于系统辨识课程的实验复现与算法对比。包内共7个文件,全部为m脚本,压缩包约7KB,涵盖普通最小二乘、递归最小二乘、扩展递归最小二乘、广义最小二乘以及基于最小二乘的神经网络权值训练等实现,可分别对应批处理参数估计、在线实时跟踪、误差相关性与异方差处理、非线性系统建模等典型场景。已有2948人学习下载,说明其在教学与工程入门中具有较高参考价值。读者可借助这些脚本直接运行并观察不同算法在相同输入输出数据下的辨识效果,理解正规方程求解、协方差矩阵更新与权重矩阵构造等关键环节,也可将其作为课程设计或论文实验的算法基线,按需修改数据接口与模型阶次,快速搭建自己的系统辨识验证流程。

1. 最小二乘法系统辨识 MATLAB 程序:从数据到模型的落地路径

很多做控制、信号处理或者嵌入式算法的朋友,第一次接触系统辨识,都是被一个很朴素的需求逼出来的:手里有一组输入输出数据,想反推出一个能用的数学模型,但又不清楚该从哪套方法下手。最小二乘法系统辨识 MATLAB 程序,就是这条路上最稳的起点。它不依赖复杂的优化工具箱,也不需要你对系统内部机理了如指掌,只要数据质量过得去,几十行代码就能跑出一个可用的差分方程模型。我见过太多人一上来就冲子空间辨识或者非线性辨识,结果连数据对齐都没搞明白,最后模型发散还找不到原因。最小二乘法的好处在于,它的每一步都是显式的、可检查的,你能清楚看到模型参数是怎么从数据里“算”出来的,而不是被某个黑匣子算法一口吞掉。这篇文章面向的是需要快速把辨识流程跑通、并且希望理解每个参数含义的工程师,不管你是做电机控制、温度建模还是结构振动分析,这套流程都能直接迁移。

2. 最小二乘辨识的数学骨架与数据准备

2.1 从差分方程到回归形式:为什么最小二乘能解辨识问题

系统辨识的核心任务,是找到一个数学模型,让它在同样的输入下,输出尽可能接近真实系统的输出。对于线性时不变系统,最常见的描述方式是差分方程:

y(k) + a₁y(k-1) + … + aₙy(k-n) = b₁u(k-1) + … + bₘu(k-m)

这里 y 是输出,u 是输入,n 和 m 分别是输出和输入的阶数。这个式子看起来是“当前输出依赖过去输出和过去输入”,但直接用它做辨识并不方便,因为等式左边有未知的 y(k) 系数。我们把它改写一下:

y(k) = -a₁y(k-1) - … - aₙy(k-n) + b₁u(k-1) + … + bₘu(k-m)

现在右边全是已知的过去数据,左边是当前输出。这就变成了一个标准的线性回归问题:y = Φθ + ε,其中 θ 是待估参数向量 [a₁…aₙ b₁…bₘ]ᵀ,Φ 是由过去输入输出构成的回归矩阵,ε 是噪声或模型误差。最小二乘法的目标,就是找到 θ,使预测误差的平方和最小。这个解有闭式表达式:θ = (ΦᵀΦ)⁻¹ΦᵀY。MATLAB 里一行反斜杠就能算,但真正要落地,难点不在公式,而在数据怎么组织、阶数怎么选、噪声怎么处理。

2.2 数据采集与预处理:三个容易翻车的细节

在写辨识代码之前,数据本身的质量决定了结果的上限。我一般会先检查三件事。

第一,输入信号是否充分激励。如果输入是常数或者缓慢变化的直流信号,回归矩阵会严重病态,参数估计出来数值巨大且没有物理意义。常见做法是用伪随机二进制序列(PRBS)或者扫频信号作为激励,保证输入在感兴趣的频段内都有能量。MATLAB 里可以用idinput生成 PRBS,但如果你没有系统辨识工具箱,自己用randi加保持器也能凑一个。

第二,采样周期是否匹配系统动态。采样太快,相邻数据点几乎一样,回归矩阵列之间高度相关,导致参数方差爆炸;采样太慢,高频动态被混叠,模型阶数再高也补不回来。经验法则是每个时间常数至少采 5 到 10 个点。如果你不知道时间常数,先做一次阶跃响应,看输出达到稳态 63% 的时间。

第三,去均值和去趋势。很多实际系统有恒定偏置或缓慢漂移,如果不处理,这些成分会被强行塞进模型参数里,导致模型在别的工况下完全不能用。我通常会对输入输出分别减去均值,如果存在明显趋势,再用detrend处理。注意,去均值的操作必须在构造回归矩阵之前做,否则回归矩阵里的过去输出和当前输出不在同一个基准上,参数会偏。

% 假设 u 和 y 是列向量,已经导入 u = u(:); y = y(:); % 去均值 u = u - mean(u); y = y - mean(y); % 如果需要去趋势,用 detrend,但注意 detrend 会改变信号长度 % y = detrend(y, 'linear'); % u = detrend(u, 'linear');

这段代码看起来简单,但u(:)强制转列向量这一步很关键。很多从 Excel 或 CSV 导入的数据默认是行向量,后面构造回归矩阵时如果维度不对,MATLAB 会报错或者更糟——广播成一个大矩阵,算出来的参数完全不对。去均值之后,模型的偏置项就不需要单独估计了,参数向量长度减少一维,对数据量少的场景很有帮助。

2.3 构造回归矩阵:从数据向量到 Φ 和 Y

有了预处理后的数据,下一步是构造回归矩阵。假设我们选定的输出阶数是 na,输入阶数是 nb,并且输入从 u(k-1) 开始影响输出(即没有直通项)。那么对于每个时刻 k,回归向量是:

φ(k) = [-y(k-1), …, -y(k-na), u(k-1), …, u(k-nb)]

把所有有效时刻的 φ(k) 按行堆叠,就得到回归矩阵 Φ。有效时刻从 max(na, nb)+1 开始,到数据长度 N 结束。对应的输出向量 Y 是 y(k) 在这些时刻的值。

function [Phi, Y] = build_regressor(u, y, na, nb) N = length(y); start_idx = max(na, nb) + 1; num_samples = N - start_idx + 1; Phi = zeros(num_samples, na + nb); Y = zeros(num_samples, 1); for i = 1:num_samples k = start_idx + i - 1; % 过去输出,取负号 for j = 1:na Phi(i, j) = -y(k - j); end % 过去输入 for j = 1:nb Phi(i, na + j) = u(k - j); end Y(i) = y(k); end end

这个函数里,start_idx的选取保证了每个回归向量里的过去数据都真实存在,不会出现负索引。Phi的列顺序是前 na 列对应输出系数,后 nb 列对应输入系数,和参数向量 θ 的顺序一致。注意这里没有把 u(k) 放进去,因为大多数物理系统都有至少一个采样周期的延迟,直通项在离散模型里很少出现。如果你的系统确实有直通,需要在回归向量里加上 u(k),但那样最小二乘的解会有偏,因为 u(k) 和噪声相关,这时候得用 instrumental variable 方法,那是另一个话题了。

3. 用 MATLAB 跑通最小二乘辨识:从脚本到验证

3.1 核心求解:反斜杠、正规方程与数值稳定性

构造好 Φ 和 Y 之后,参数估计就是一行代码的事:

theta = Phi \ Y;

MATLAB 的反斜杠运算符会根据矩阵形状自动选择求解策略。对于超定方程(行数远大于列数),它默认用 QR 分解,数值稳定性比直接求逆好得多。很多人喜欢写theta = inv(Phi'*Phi)*Phi'*Y,这在教学里常见,但在实际数据上很容易翻车。原因很简单:Phi'*Phi的条件数是Phi条件数的平方,如果Phi本身条件数在 10⁴ 量级,平方之后就是 10⁸,双精度浮点数的有效位数大概 15 到 16 位,算出来的参数可能只剩几位有效数字,甚至出现负的方差估计。我一般会先检查cond(Phi),如果超过 10⁶,就要考虑换激励信号或者降低阶数。

% 检查条件数 c = cond(Phi); if c > 1e6 warning('回归矩阵条件数 %.2e,参数估计可能不可靠', c); end % 用 QR 分解求解,和反斜杠等价但更显式 [Q, R] = qr(Phi, 0); theta = R \ (Q' * Y);

这段代码里,qr(Phi, 0)是经济型 QR 分解,只保留前 n 列,节省内存。R是上三角矩阵,R \ (Q'*Y)是回代求解,比直接反斜杠多了一步显式分解,但好处是你可以检查R的对角线元素。如果某个对角元素接近零,说明对应的回归变量几乎和其他变量线性相关,那个参数就不该出现在模型里。

3.2 模型阶数选择:AIC 准则与残差白噪声检验

阶数 na 和 nb 选多少,是辨识里最玄学的一步。选低了,模型欠拟合,残差里还有规律;选高了,模型过拟合,参数方差大,在新数据上表现差。我一般用两个手段交叉验证。

第一个是 AIC 准则。AIC 在残差平方和的基础上加了一个和参数个数成正比的惩罚项:

AIC = N * ln(残差方差) + 2 * (na + nb)

残差方差用sum((Y - Phi*theta).^2) / N估计。对不同的 (na, nb) 组合分别算 AIC,取最小值对应的阶数。注意 AIC 只给一个参考,不能全信,尤其是数据量少的时候。

第二个是残差白噪声检验。如果模型结构正确,残差应该接近白噪声,自相关函数除了零延迟外都应该落在置信区间内。MATLAB 里可以用xcorr算自相关,然后画出来看。

residual = Y - Phi * theta; % 残差自相关 [max_lag, ~] = size(Phi); [acf, lags] = xcorr(residual, max_lag, 'biased'); acf = acf(lags >= 0); lags = lags(lags >= 0); % 95% 置信区间,约 1.96/sqrt(N) N = length(residual); conf = 1.96 / sqrt(N); stem(lags, acf, 'filled'); hold on; plot([0, max(lags)], [conf, conf], 'r--'); plot([0, max(lags)], [-conf, -conf], 'r--'); hold off;

如果自相关图里除了 lag=0 之外还有明显超出红线的尖峰,说明模型没有把系统动态全部抓住,要么阶数不够,要么输入输出之间有非线性。这时候别急着加阶数,先检查数据里有没有异常点或者未记录的扰动。

3.3 模型验证:交叉验证与一步预测对比

参数算出来只是第一步,模型能不能用,得看它在没参与训练的数据上的表现。我通常把数据分成两段:前 70% 用来辨识,后 30% 用来验证。验证的时候,用辨识得到的模型对验证段做一步预测,然后比较预测输出和真实输出。

% 假设 theta 是用前 70% 数据辨识的 split = floor(0.7 * N); u_train = u(1:split); y_train = y(1:split); u_val = u(split+1:end); y_val = y(split+1:end); % 用训练数据辨识 [Phi_train, Y_train] = build_regressor(u_train, y_train, na, nb); theta = Phi_train \ Y_train; % 在验证数据上做一步预测 [Phi_val, Y_val] = build_regressor(u_val, y_val, na, nb); y_pred = Phi_val * theta; % 计算拟合度 fit = 1 - norm(y_val(max(na,nb)+1:end) - y_pred) / norm(y_val(max(na,nb)+1:end) - mean(y_val)); fprintf('验证集拟合度: %.2f%%\n', fit * 100);

拟合度超过 80% 通常说明模型抓住了主要动态。如果训练集拟合度很高但验证集很差,那就是过拟合,得降阶或者加正则化。如果两边都差,可能是激励信号不够丰富,或者系统本质非线性,最小二乘线性模型无能为力。

4. 避坑与排查:最小二乘辨识的五个血泪教训

4.1 现象:参数数值巨大且符号反常

原因:回归矩阵病态,通常是因为输入信号没有充分激励,或者 na 和 nb 选得太大,导致列之间高度相关。解决:先检查cond(Phi),如果超过 10⁶,换 PRBS 激励重新采数据,或者用逐步回归剔除冗余项。我一般会从 na=2, nb=2 开始试,不要一上来就设到 5 以上。

4.2 现象:残差自相关图有明显周期尖峰

原因:系统存在未建模的周期性扰动,比如工频干扰或者机械振动。解决:在预处理阶段加一个陷波滤波器,把已知频率的干扰去掉,再重新辨识。注意滤波会引入相位延迟,如果对相位敏感,要用零相位滤波filtfilt。

4.3 现象:训练集拟合很好,验证集一塌糊涂

原因:过拟合,模型把噪声也学进去了。解决:降低阶数,或者用正则化最小二乘(岭回归)。MATLAB 里可以手动加正则项:theta = (Phi'*Phi + lambda*eye(size(Phi,2))) \ (Phi'*Y),lambda 取 1e-6 到 1e-3 之间,根据条件数调整。

4.4 现象:去均值后模型没有偏置,但实际系统有恒定输出

原因:去均值把系统的直流增益也去掉了,辨识出的模型只描述动态部分。解决:如果需要在模型里保留偏置,就不要对输出去均值,而是在回归矩阵里加一列全 1,对应的参数就是偏置项。但要注意,加偏置项后输入也不能去均值,否则偏置的物理意义会混淆。

4.5 现象:换一组数据重新辨识,参数变化很大

原因:数据量太少,或者系统时变。解决:增加数据长度,至少保证每个待估参数有 10 到 20 个独立样本。如果系统确实时变,最小二乘就不适用了,得用递推最小二乘加遗忘因子,那是另一个层面的方法。

5. 递推最小二乘与在线辨识的衔接技巧

当你把批处理最小二乘跑通之后,很自然会遇到在线辨识的需求:数据源源不断来,不可能每次都重新构造整个回归矩阵再求逆。递推最小二乘(RLS)就是干这个的。它的核心思想是,新来一个数据点,就在旧参数的基础上做一次修正,修正量取决于预测误差和增益向量。

% 递推最小二乘初始化 theta = zeros(na + nb, 1); P = 1e4 * eye(na + nb); % 协方差矩阵,初始值大表示对初始参数不信任 lambda = 0.98; % 遗忘因子,1 表示不遗忘,越小越强调新数据 % 对每个新样本 for k = max(na, nb) + 1:N phi = [-y(k-1:-1:k-na); u(k-1:-1:k-nb)]; y_hat = phi' * theta; e = y(k) - y_hat; K = P * phi / (lambda + phi' * P * phi); theta = theta + K * e; P = (P - K * phi' * P) / lambda; end

这段代码里,lambda是最关键的参数。取 1 就是标准 RLS,适合时不变系统;取 0.95 到 0.99 之间,可以让算法慢慢忘掉旧数据,跟踪缓慢时变的系统。但 lambda 太小会导致参数波动大,甚至发散。我一般先用 1 跑一遍,看残差是否稳定,如果残差有缓慢上升趋势,再降到 0.98 试试。P矩阵的初始值也有讲究,1e4 是个常用起点,太小会让算法对新数据反应迟钝,太大则前几个点的修正量会非常剧烈。如果你大概知道参数的量级,可以把初始 theta 设成那个量级,P 设成 1e2 到 1e3,收敛会平稳很多。

还有一个容易忽略的点:递推最小二乘的数值稳定性不如批处理 QR 分解。长时间运行后,P 矩阵可能失去正定性,导致算法崩溃。工程上常见的做法是每隔几千步用批处理最小二乘重新算一次,给 RLS “重启”一下。或者用 UD 分解形式的 RLS,但那实现起来复杂得多,除非你的采样率极高、数据量极大,否则没必要。

最后说一个我自己的习惯:不管批处理还是递推,辨识完的模型我一定会拿阶跃响应再验一次。把辨识出的差分方程转成传递函数,用step看它的阶跃响应和实际系统录制的阶跃响应是否吻合。这一步能抓住很多纯数据指标看不出的问题,比如模型在高频段增益异常、或者稳态增益符号反了。希望帮到你。

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

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

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

立即咨询