news 2026/10/15 0:20:03

最小二乘法系统辨识:MATLAB实现、阶次选择与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
最小二乘法系统辨识:MATLAB实现、阶次选择与避坑指南

简介:这份MATLAB程序包面向自动化、控制工程及信号处理方向的学习者与研究人员,用于系统辨识课程的实验与算法验证。包内共7个文件,全部为.m脚本,压缩包约7KB,涵盖普通最小二乘、递归最小二乘、扩展递归最小二乘、广义最小二乘以及基于神经网络的参数辨识等多类实现,可对照比较批处理与在线学习在参数估计上的差异。其中递归类脚本侧重实时数据流下的参数更新与跟踪,广义最小二乘部分则涉及异方差或相关误差下的加权处理,神经网络脚本展示了用最小二乘优化权值以拟合非线性系统的思路。目前已有2948人学习下载,适合希望从基础公式推导过渡到代码落地、并借助现成脚本快速复现实验的读者参考。

1. 最小二乘法系统辨识:从一堆采样点到能用的模型,中间差了什么

如果你手上有两组数据——一组是输入信号,一组是系统响应——想反推出这个系统的传递函数或者差分方程,最小二乘法系统辨识就是最直接的路径。它不依赖复杂的优化器,不需要神经网络调参那套玄学,核心就是解一个矩阵方程。很多人第一次接触系统辨识,都是从最小二乘法 matlab 程序开始的:几行代码就能跑出一个模型,但跑出来的结果能不能用、参数有没有物理意义、阶次选得对不对,这些才是真正区分“跑通”和“能用”的地方。

这篇文章面向的是需要在 MATLAB 里完成系统辨识的工程师和学生。你可能有实测的输入输出数据,也可能是在做仿真验证,目标都是拿到一个可以预测、可以控制的模型。我会从最小二乘法的数学形式讲起,然后落到 MATLAB 的具体实现,包括数据预处理、阶次选择、参数求解、模型验证,最后讲几个我踩过的坑。读完你应该能自己写出一套可复用的辨识脚本,而不是每次都在网上找一段代码改改就用。

2. 最小二乘法辨识的数学形式与 MATLAB 落地路径

2.1 从差分方程到回归向量:最小二乘到底在解什么

系统辨识的最小二乘法,本质上是把一个差分方程整理成线性回归的形式。假设系统可以用 ARX 模型描述:

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

把未知参数移到一边,写成向量形式:

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

其中 θ = [-a₁, …, -aₙ, b₁, …, bₘ]ᵀ 是待估参数,φ(k) = [y(k-1), …, y(k-n), u(k-1), …, u(k-m)]ᵀ 是回归向量。对于 N 个采样点,把所有方程堆起来:

Y = Φ·θ + E

最小二乘的解就是让残差平方和最小的 θ:

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

这个公式看起来简单,但直接求逆在数值上不稳定。MATLAB 里实际用的是左除运算符或者 QR 分解,避免显式求逆。我一般会优先用\,因为 MATLAB 会根据矩阵结构自动选择最稳的解法。

理解了这个形式,你就知道为什么数据长度 N 必须大于参数个数 n+m,否则 ΦᵀΦ 奇异,解不出来。另外,输入信号必须持续激励,否则某些参数不可辨识——这是后面避坑章节要展开的。

2.2 用 MATLAB 构造回归矩阵:一个可复用的函数

下面这个函数把输入输出数据转成回归矩阵和目标向量。我把它写成独立函数,方便在不同项目里复用。

function [Phi, Y] = build_regressor(u, y, na, nb) % build_regressor 构造ARX模型的回归矩阵 % 输入: % u - 输入信号向量(列向量) % y - 输出信号向量(列向量) % na - 输出滞后阶数 % nb - 输入滞后阶数 % 输出: % Phi - 回归矩阵,每行对应一个采样时刻 % Y - 目标向量,对应Phi每行的输出值 u = u(:); y = y(:); N = length(y); start_idx = max(na, nb) + 1; % 保证回归向量完整的最早时刻 Phi = zeros(N - start_idx + 1, na + nb); Y = zeros(N - start_idx + 1, 1); for k = start_idx:N row = k - start_idx + 1; % 输出滞后项:y(k-1) ... y(k-na) for i = 1:na Phi(row, i) = -y(k - i); end % 输入滞后项:u(k-1) ... u(k-nb) for j = 1:nb Phi(row, na + j) = u(k - j); end Y(row) = y(k); end end

逻辑说明:这个函数把每个采样时刻的滞后项拼成一行。注意输出滞后项前面加了负号,因为 ARX 模型的标准形式是 y(k) + a₁y(k-1) + … = b₁u(k-1) + …,移项后 y(k) = -a₁y(k-1) - … + b₁u(k-1) + …,所以回归向量里 y 的滞后项带负号,这样解出来的 θ 直接就是 [-a₁, …, b₁, …]。

参数说明:na和nb决定了模型阶次。na是输出自回归阶数,nb是输入延迟阶数。起始索引start_idx取max(na, nb) + 1,保证第一个回归向量的所有滞后项都有数据。如果你的系统有纯延迟 d,可以把输入项改成u(k-d-j+1),这个后面讲。

2.3 参数求解与模型验证:别只看拟合曲线

拿到 Phi 和 Y 之后,求解就一行:

theta = Phi \ Y;

但解出来只是开始。我一般会做三件事来验证模型质量。

第一,看残差。残差 e = Y - Phi*theta,如果残差有自相关,说明模型结构不对或者阶次不够。用autocorr(e)看一眼,残差应该在零附近随机波动。

第二,看参数置信区间。最小二乘的参数协方差估计是:

sigma2 = (e' * e) / (length(Y) - size(Phi,2)); cov_theta = sigma2 * inv(Phi' * Phi); std_theta = sqrt(diag(cov_theta));

如果某个参数的绝对值小于两倍标准差,这个参数可能不显著,考虑降阶。

第三,做交叉验证。把数据分成两段,前一段辨识,后一段预测。预测输出和实际输出的拟合度用fitpercent或者归一化均方误差衡量。我习惯算 NRMSE:

y_pred = Phi * theta; nrmse = norm(Y - y_pred) / norm(Y - mean(Y)) * 100;

如果 NRMSE 低于 80%,模型基本可用;低于 60% 就要检查数据质量或者模型结构了。

3. 阶次选择、数据预处理与递推最小二乘的实现细节

3.1 阶次选不对,后面全白费:AIC 与试凑的结合

阶次选择是系统辨识里最容易被忽视的一步。很多人直接设 na=2, nb=2 就跑,结果要么欠拟合要么过拟合。我一般用 AIC 准则先粗选,再结合物理意义微调。

AIC 的定义是:

AIC = N·ln(σ²) + 2·(na + nb)

其中 σ² 是残差方差,N 是样本数。阶次越高,σ² 越小,但第二项惩罚越大。把 na 和 nb 从 1 到 6 遍历,找 AIC 最小的组合。

best_aic = inf; best_na = 1; best_nb = 1; for na = 1:6 for nb = 1:6 [Phi, Y] = build_regressor(u, y, na, nb); theta = Phi \ Y; e = Y - Phi * theta; sigma2 = (e' * e) / length(Y); aic = length(Y) * log(sigma2) + 2 * (na + nb); if aic < best_aic best_aic = aic; best_na = na; best_nb = nb; end end end

但 AIC 只是参考。如果被辨识对象是一个二阶机械系统,na=2 就有物理意义,即使 AIC 建议 na=3,我也会优先选 na=2。过拟合的模型在控制里会引入不必要的振荡。

3.2 数据预处理:去均值、去趋势、滤波的先后顺序

实测数据往往有直流偏置和低频漂移。不处理直接辨识,会得到一个带积分特性的模型,参数物理意义全乱。我一般按这个顺序处理:

第一步,去均值。输入输出都减去各自的均值,消除直流分量。

u = u - mean(u); y = y - mean(y);

第二步,去趋势。如果数据有明显的线性漂移,用detrend去掉。

y = detrend(y);

第三步,滤波。如果噪声带宽远高于系统带宽,可以低通滤波。但要注意滤波会引入相位延迟,如果输入输出用同一个滤波器,延迟可以抵消。我一般用零相位滤波filtfilt。

[b_f, a_f] = butter(2, 0.1); % 截止频率根据实际调整 u = filtfilt(b_f, a_f, u); y = filtfilt(b_f, a_f, y);

顺序很重要:先去均值再去趋势,最后滤波。如果先滤波,直流分量会影响滤波器初始状态,引入额外瞬态。

3.3 递推最小二乘:在线辨识的 MATLAB 实现

离线最小二乘适合批量数据,但如果要做自适应控制或者在线监测,就需要递推形式。递推最小二乘(RLS)的公式如下:

θ(k) = θ(k-1) + K(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) / λ

其中 λ 是遗忘因子,通常取 0.95 到 1.0。λ=1 时所有历史数据等权,适合时不变系统;λ<1 时旧数据权重衰减,适合时变系统。

function [theta_hist, y_pred] = rls_identify(u, y, na, nb, lambda, P0) % rls_identify 递推最小二乘辨识 % 输入: % u, y - 输入输出数据 % na, nb - 阶次 % lambda - 遗忘因子,0.95~1.0 % P0 - 初始协方差矩阵,通常取 1e4*eye(na+nb) % 输出: % theta_hist - 参数历史,每行一个时刻 % y_pred - 预测输出 u = u(:); y = y(:); N = length(y); n_params = na + nb; theta = zeros(n_params, 1); P = P0; theta_hist = zeros(N, n_params); y_pred = zeros(N, 1); start_idx = max(na, nb) + 1; for k = start_idx:N phi = zeros(n_params, 1); for i = 1:na phi(i) = -y(k - i); end for j = 1:nb phi(na + j) = u(k - j); end 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; theta_hist(k, :) = theta'; y_pred(k) = y_hat; end end

参数说明:lambda越接近 1,参数收敛越稳但跟踪时变能力越弱;越接近 0.9,跟踪快但参数波动大。我一般先用 1.0 跑一遍看收敛,如果系统有时变特性再降到 0.98 左右。P0取大一点表示初始参数不确定性大,让算法快速调整;取小一点则初始收敛慢但更稳。

4. 避坑与排查:最小二乘辨识里那些让人翻车的细节

4.1 输入信号持续激励不足,参数解出来是“假”的

现象:辨识出来的模型拟合训练数据很好,但换一组数据预测就完全不对。参数值看起来也合理,但物理意义对不上。

原因:输入信号频谱太窄,比如只用一个频率的正弦波做输入,只能激励系统在该频率附近的动态。其他频段的参数没有被数据约束,最小二乘会给出一个在训练集上拟合好但泛化差的解。

解决:用伪随机二进制序列(PRBS)或者扫频信号作为输入。PRBS 的频谱覆盖宽,实现也简单。MATLAB 里可以用idinput生成:

u = idinput(1023, 'prbs', [0 0.5], [-1 1]);

如果只能用实际运行数据,检查输入的自功率谱,确保在系统带宽范围内都有能量。

4.2 数据长度不够或者矩阵病态,解出来的参数大得离谱

现象:theta里某些参数是 10⁶ 量级,或者 MATLAB 警告“矩阵接近奇异”。

原因:数据长度 N 小于参数个数,或者输入输出高度相关导致 ΦᵀΦ 条件数极大。另一个常见原因是数据没有去均值,直流分量让矩阵第一列和常数项共线。

解决:先检查cond(Phi'*Phi),如果大于 10⁸ 就要警惕。增加数据长度、去均值、降低阶次都是办法。如果实在无法增加数据,可以用正则化最小二乘(岭回归):

lambda_reg = 1e-6; theta = (Phi' * Phi + lambda_reg * eye(size(Phi,2))) \ (Phi' * Y);

正则化会引入偏差,但能换来数值稳定。lambda_reg从小往大试,看参数是否稳定。

4.3 纯延迟没处理,模型阶次虚高

现象:AIC 建议的 nb 比实际物理延迟大很多,辨识出来的 b 参数前面几个接近零。

原因:系统有纯延迟 d,但回归向量里输入项从 u(k-1) 开始,模型需要用额外的 b 参数来“凑”出这个延迟。这会导致阶次虚高,参数冗余。

解决:先估计延迟。可以用互相关函数找输入输出的最大相关滞后:

[c, lags] = xcorr(y, u, 'normalized'); [~, idx] = max(abs(c)); delay_est = lags(idx);

然后在构造回归矩阵时,输入项从u(k-delay_est)开始。这样 nb 可以设得小一些,参数更简洁。

4.4 残差自相关严重,说明模型结构不对

现象:残差e的autocorr图在滞后几阶处有明显尖峰,超出置信区间。

原因:ARX 模型假设残差是白噪声。如果实际系统有未建模的动态,或者噪声是有色的,残差就会自相关。这时候最小二乘估计是有偏的。

解决:换模型结构。如果残差自相关,可以试试 ARMAX 或者输出误差(OE)模型。MATLAB 的 System Identification Toolbox 里有armax和oe函数。如果不想用工具箱,可以把残差也作为回归项加入,变成 ARARX 结构,但实现复杂度会上升。

4.5 训练集和验证集划分不当,过拟合被掩盖

现象:训练集 NRMSE 95%,验证集 NRMSE 只有 50%。

原因:数据划分时没有考虑系统的工作点变化。如果训练集只覆盖低负载工况,验证集包含高负载工况,模型外推能力差是必然的。

解决:按工况分层划分,确保训练集和验证集覆盖相似的工作范围。如果数据本身就不覆盖全工况,那模型只在该工况附近有效,不要指望外推。我一般会留出最后 20% 的数据做验证,并且检查两段数据的统计特性是否一致。

5. 从离线到在线:最小二乘辨识的进阶用法与验证习惯

5.1 用频域验证代替只看时域拟合

时域拟合好不代表模型频域特性对。我习惯在辨识完成后,用etfe或者spa估计非参数频率响应,再和辨识模型的bode图对比。如果在中频段有超过 3dB 的偏差,模型用于控制器设计时可能出问题。

% 非参数频率响应估计 [G_etfe, w] = etfe([y u], [], 1024); % 辨识模型的频率响应 sys_arx = tf(theta(nb+1:end)', [1 theta(1:na)'], 1); [G_arx, ~] = freqresp(sys_arx, w); % 对比幅频特性 semilogx(w, 20*log10(abs(G_etfe)), 'b', w, 20*log10(abs(squeeze(G_arx))), 'r--'); legend('ETFE', 'ARX模型'); xlabel('频率 (rad/s)'); ylabel('幅值 (dB)');

这个对比能暴露很多时域看不出来的问题。比如高频段模型增益偏高,可能是噪声建模不当;低频段偏差大,可能是去趋势不彻底。

5.2 参数不确定性与蒙特卡洛验证

最小二乘给出的参数是一个点估计,但实际参数是有不确定性的。我一般会做蒙特卡洛验证:在残差里加不同随机种子,重新辨识 100 次,看参数分布。

n_runs = 100; theta_samples = zeros(n_runs, length(theta)); for run = 1:n_runs e_sim = e .* randn(size(e)); % 用残差尺度生成噪声 y_sim = Phi * theta + e_sim; theta_samples(run, :) = Phi \ y_sim; end % 看参数标准差 std_theta_mc = std(theta_samples);

如果某个参数的蒙特卡洛标准差和解析协方差算出来的差很多,说明残差不是白噪声,解析公式不可靠。

5.3 一个我坚持了多年的习惯:先仿真验证再上实测数据

每次写新的辨识脚本,我一定先用仿真数据跑通。构造一个已知传递函数的系统,生成输入输出,加噪声,然后看辨识结果能不能还原参数。这个习惯帮我省了很多时间——如果仿真数据都辨识不准,实测数据更不可能。

% 仿真验证:已知系统 sys_true = tf([1 2], [1 3 2]); % 传递函数 u_sim = idinput(2000, 'prbs', [0 0.5], [-1 1]); y_sim = lsim(sys_true, u_sim, (0:length(u_sim)-1)'); y_sim = y_sim + 0.01 * randn(size(y_sim)); % 加噪声 % 辨识 [Phi_sim, Y_sim] = build_regressor(u_sim, y_sim, 2, 2); theta_sim = Phi_sim \ Y_sim; % 对比真实参数 % 离散化后的真实参数需要根据采样时间计算

仿真验证能确认脚本逻辑正确、阶次选择合理、预处理没有引入偏差。这一步花十分钟,能避免后面几小时的排查。

5.4 模型交付前必做的三个检查

第一,检查参数是否在物理合理范围内。比如时间常数应该是正的,增益符号应该和实际系统一致。如果出现负的时间常数,模型肯定有问题。

第二,检查模型在训练数据之外的预测能力。用验证集算 NRMSE,低于 70% 就要谨慎。

第三,检查模型是否可稳定用于控制。如果辨识出的离散模型有单位圆外的极点,闭环控制可能不稳定。用abs(roots([1 theta(1:na)']))检查极点模值。

这三个检查做完,模型才能交给控制组或者用于后续设计。我见过太多人跳过这些步骤,直接把辨识结果拿去调 PID,最后振荡得没法收拾。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 21:48:54

CNN卷积层与池化层原理详解:从参数计算到PyTorch实战

1. 卷积层与池化层的核心工作原理解读1.1 卷积层到底在做什么&#xff1a;一个生活视角在正式敲代码之前&#xff0c;我想先和你一起把卷积层这件事彻底想明白。回到系列前几篇&#xff0c;我们已经解决了"怎么用线性层激活函数搭出一个能跑的网络"的问题&#xff0c…

作者头像 李华
网站建设 2026/10/14 23:17:20

基于深度学习的恶意加密流量检测系统设计与实现

简介&#xff1a;基于深度学习的恶意加密流量检测系统毕业设计项目&#xff0c;以Python实现完整源码与配套文档&#xff0c;面向正在筹备毕业设计、课程设计或期末大作业的高校学生&#xff0c;提供一套可直接运行的检测原型。压缩包共217个文件&#xff0c;核心部分包括4个Py…

作者头像 李华
网站建设 2026/10/13 21:42:36

弗洛伊德算法详解:多源最短路径的Java实现与避坑指南

1. 核心思想与适用场景1.1 多源最短路径问题到底在解决什么先说个直白的场景&#xff1a;你手里有一张城市交通图&#xff0c;上面有若干个站点&#xff0c;站点之间有一些直达线路&#xff0c;每条线路有一个耗时。现在你想知道任意两个站点之间、经过中转后最短的耗时分别是多…

作者头像 李华
网站建设 2026/10/13 21:41:13

纯Java构建企业级AI Agent Harness平台:从架构设计到踩坑实战

做企业级 AI Agent 的人&#xff0c;大概都有过这种感觉&#xff1a;模型选型一堆、工具调用一堆、会话状态一团乱麻&#xff0c;最后发现真正难的不是让大模型“开口说话”&#xff0c;而是给这群 Agent 搭一个能稳定、可控、可追溯的“窝”。这个窝&#xff0c;就是 Harness—…

作者头像 李华
网站建设 2026/10/13 21:41:09

卷积编解码MATLAB代码从零手写:前向反向传播与调参避坑指南

简介&#xff1a;这份资源是一套自编的卷积码编解码MATLAB实现&#xff0c;面向通信原理、信息论与编码课程的学习者&#xff0c;以及需要完成相关课程设计或实验作业的同学。内容围绕卷积码的编码与Viterbi译码展开&#xff0c;涵盖移存器与生成多项式建模、网格图路径搜索、不…

作者头像 李华
网站建设 2026/10/14 22:37:07

风速均一化订正实战:基于SNHTT与Python的年最大风速处理

简介&#xff1a;在气象与水文学研究中&#xff0c;风速数据的连续性和可比性直接影响天气预报、气候分析与环境影响评估的可靠性。这份以最大风速为例的均一化订正资源包&#xff0c;面向需要处理历史观测数据的气象学者、环境评估人员及数据分析学习者&#xff0c;提供一套从…

作者头像 李华