
模型结构摆在那儿参数全是未知数——这种局面我在建模里碰到的次数比想象中多得多。写方程容易把方程里那堆系数定下来才是真正见功力的地方。经典辨识法就是干这件事的一套老办法模型形式已经给定输入输出数据也测到了剩下的任务就是从数据里把参数反推出来。它不追求花哨但胜在可解释、可复现、可验证特别适合做数学建模竞赛题、做控制系统设计、做工程数据分析的人。这篇笔记是我把自己这几年在辨识上踩过的坑、走过的弯路、以及最后沉淀下来的操作流程整理出来的一次复盘从数据采集一直讲到参数验证尽量把每一步为什么这么做都说清楚。1. 经典辨识法到底解决什么问题先把问题边界划清楚刚接触辨识的人容易把所有求参数的问题都归到辨识里结果方法选错、数据采错后面怎么调都白搭。所以第一步不是打开编辑器写代码而是判断你手头这个问题究竟属于哪一类。1.1 三类建模问题的界线白箱、灰箱、黑箱工程和科研里的建模问题我习惯按结构已知程度分成三档。白箱是机理完全清楚、参数也有物理意义的情况比如牛顿第二定律里质量、阻尼系数、刚度系数都能量出来剩下的只是把它们填进方程求解。黑箱是机理完全不清楚只能靠输入输出关系去拟合神经网络、随机森林这类方法就是干这个的。而经典辨识法真正的主战场在中间的灰箱模型结构有理论依据比如一阶惯性加纯滞后、二阶传递函数、离散差分方程但参数没法直接测量或者测量成本极高。举个建模比赛里常见的例子传染病模型里传播率、恢复率、潜伏期转化率这些参数没有仪器能直接读出来只能靠每日新增病例数据去反推。再比如机械臂的关节模型惯量、摩擦系数、电机时间常数理论上可以查手册但装配之后的有效值跟手册值差得远只能通过激励实验辨识。这类问题的共同特征是方程的形式你有信心缺的是系数。区分这三类很关键因为它直接决定了你的方法边界。灰箱问题硬上黑箱方法会丢掉物理约束得到一堆没有意义的系数白箱问题去做辨识是浪费时间。我见过不少同学明明机理清楚却非要用数据拟合最后拟合出来的参数和物理量纲都对不上。1.2 辨识的三要素数据、模型集、准则所有辨识方法本质上都是同一个套路给定数据、给定候选模型集合、给定一个衡量拟合好坏的准则然后在模型集合里挑出让准则最优的那个。这三要素缺一不可而且顺序不能乱。数据决定了你能辨识出多少信息这是物理层面的限制。模型集决定了你的搜索空间选太小会欠拟合真模型不在集合里选太大又会过拟合噪声被当成规律。准则决定了你怎么定义好最常用的就是预测误差平方和但也可以是最小二乘、极大似然、最小预测误差熵等等。这三者里我个人的经验是数据的重要性被严重低估。很多人卡在参数估不准其实问题压根不在算法而在于激励信号太温和输入变化幅度不够导致某些参数方向上的信息几乎为零。这种时候换再高级的算法也没用因为信息根本不在数据里。1.3 为什么先做经典辨识而不是直接上神经网络现在工具太方便了遇到参数问题很多人第一反应是丢给神经网络或者交给大模型去猜。我不反对用新工具但顺序上建议先做经典辨识理由有三个。一是可解释性。经典辨识给出的参数有明确物理意义能直接写进论文、拿去和文献值对比。二是样本效率。辨识二阶系统几百个采样点就够神经网络往往需要几千上万条。在实验成本高的场景下这是个硬约束。三是可诊断性。经典方法出问题的时候你能定位到是激励不足、噪声有色、还是模型阶次不对神经网络出问题通常只能看到损失不下降原因全靠猜。我自己的做法是先用经典辨识拿到一组有物理意义的参数作为基准再看神经网络能不能在预测精度上明显超过它。如果超不过就说明经典方法已经够用没必要引入黑箱的复杂度如果超过了也至少有个可解释的参照系不至于完全失控。2. 数据采集与实验设计辨识的成败在动手之前就定了辨识圈里有句老话给你一组烂数据再好的算法也救不回来。数据环节做扎实后面的事半功倍数据环节敷衍后面就是在给噪声调参。这一章讲的是采集阶段最容易被忽略、但影响最大的几件事。2.1 激励信号的选型阶跃、脉冲、M序列、扫频激励信号的本质任务是把系统各个频段、各个参数方向都戳一遍。信号太温和系统没被充分激发某些参数就辨识不出来。下面这张表是我总结的常见激励信号适用场景实际选型时可以直接对照。激励信号频带覆盖适用场景主要缺点阶跃信号低频为主一阶、二阶系统快速粗辨高频信息少有色噪声下偏差大脉冲信号宽带但能量低冲击响应试验、快速测试信噪比低对采样率要求高M序列 / PRBS宽带可调离散系统、在线辨识需要设计周期和幅值易被非线性干扰正弦扫频逐点精确频域辨识、传递函数拟合耗时一次实验覆盖频点有限白噪声全频带理论分析、仿真验证实际很难生成幅值受限选型的核心判断依据是你要辨识的参数主要受哪个频段影响。比如辨识系统增益低频信息就够一个阶跃信号足矣辨识时间常数或者高频极点就得靠 M 序列这类宽带信号。我早期做过一次实验想辨识一个含高频动态的二阶系统结果只用了一个缓慢的阶跃测出来的高频极点完全是噪声里抠出来的跟真值差了百分之三十。后来换成时钟周期合适的 PRBS同一套算法误差直接降到百分之三以内。信号选错算法再对也白搭。还有一点常被忽略激励幅值。幅值太小输出变化淹在噪声里幅值太大系统进入非线性区比如电机饱和、阀门死区线性模型的假设就破了。我的经验是先在允许范围内做一次小幅值试探看看输出是否线性变化再逐步加大到最大允许幅值的三分之二左右。2.2 采样周期与数据长度怎么算采样周期选得太粗会丢掉动态信息选得太细会造成数据冗余、矩阵条件数变差两个极端都难受。工程上常用的经验判据是采样频率取系统期望带宽的 5 到 10 倍或者说采样周期取系统上升时间的十分之一到二十分之一。举个具体的算例。假设你辨识的是一个时间常数约为 0.5 秒的一阶系统那么它的带宽大致在 (1/(2\pi \times 0.5)) 附近也就是 0.3 赫兹量级。按 10 倍带宽取采样频率也就是 3 赫兹对应采样周期约 0.33 秒。要是系统上升时间约 1.5 秒按十分之一取采样周期约 0.15 秒两者取个中间值采样周期定在 0.1 到 0.2 秒之间比较稳妥。数据长度方面有个粗略的经验法则每个待辨识参数至少对应 10 到 50 个有效数据点。二阶离散差分方程通常有 4 到 5 个参数那至少需要 50 到 250 个采样点我一般会准备 500 到 1000 个点留出余量给预处理和验证集切分。数据太短参数估计的方差大数据太长又可能引入系统工况漂移尤其是做在线辨识时要权衡。2.3 数据预处理去趋势、去野值、滤波的先后顺序预处理顺序错了会引入假信息。我的固定流程是这样的先去野值再去趋势最后才滤波。去野值放在最前面是因为野值会影响后面所有步骤的统计量尤其是去趋势用的均值和中位数。常用的野值判据是 3σ 准则或者中位数绝对偏差MAD准则后者对野值本身更鲁棒。去趋势是为了处理传感器漂移或者工况缓慢变化常用方法是一阶差分或者减去滑动平均。滤波放最后是因为滤波本身会改变信号的相位和幅值如果在去趋势之前滤波滤波器的滞后可能被误判成趋势。这里必须提醒一个坑滤波会引入相位延迟如果输入输出两路信号用了相同的滤波器延迟基本相同影响不大但如果只滤了输出没滤输入两路信号就对不齐了辨识出来的模型会出现虚假的纯滞后甚至把时间常数估错。我做实验时固定用同一个滤波器参数对两路信号处理这一点从来不敢省。注意低通滤波的截止频率不要低于系统带宽否则会把有用的动态信息一起滤掉模型阶次会被低估。3. 核心算法逐个拆解从最小二乘到极大似然前面把数据和问题理清楚了这一章进入算法本身。我按上手难度和抗噪能力两条线把经典方法串一遍。每个方法我都会说清楚它假设了什么、在什么情况下失效、以及实际用的时候要注意什么。3.1 最小二乘一切参数辨识的原点最小二乘是所有辨识方法的祖宗理解它后面那些变体就都能看懂了。它的核心假设非常朴素模型输出可以写成回归向量乘以参数向量加上一个残差项。比如一个二阶离散差分方程y(k) -a1*y(k-1) - a2*y(k-2) b1*u(k-1) b2*u(k-2) e(k)把[-y(k-1), -y(k-2), u(k-1), u(k-2)]组成回归向量 φ(k)把[a1, a2, b1, b2]组成参数向量 θ那么y(k) φ(k)^T θ e(k)。把所有时刻的方程叠起来写成矩阵形式Y Φθ E最小二乘解就是θ_hat (Φ^T Φ)^{-1} Φ^T Y这个公式要背下来因为它出现的频率太高了。它的几何意义是把观测向量 Y 投影到回归矩阵 Φ 张成的空间里投影残差和空间正交。这个正交性就是最小的来源。最小二乘的致命弱点在于它假设残差 e(k) 和回归向量 φ(k) 不相关。如果噪声是白噪声这个假设成立估计是无偏的但如果噪声是有色噪声实际系统里几乎都是噪声通过-a1*y(k-1)这类项反馈进了回归向量正交性被破坏估计就偏了。而且这个偏差不是随机误差是系统性偏差数据再多也消不掉。注意很多人以为数据量够大最小二乘就一定准这是误解。有色噪声下最小二乘是一致但有偏的估计偏差大小取决于噪声和系统的耦合程度。3.2 递推最小二乘与遗忘因子在线辨识的主力实际工程中数据往往是一个一个来的或者系统参数会缓慢漂移这时候一次性求逆的批处理最小二乘就不合适了得用递推形式。递推最小二乘的核心思想是新来一个数据点就在旧估计的基础上做一次修正不需要重新算矩阵求逆。它的更新公式大致是这样先算增益向量K(k) P(k-1)φ(k) / (λ φ(k)^T P(k-1)φ(k))然后更新参数θ(k) θ(k-1) K(k)(y(k) - φ(k)^T θ(k-1))最后更新协方差矩阵P(k) (P(k-1) - K(k)φ(k)^T P(k-1)) / λ。这里的 λ 就是遗忘因子取值通常在 0.95 到 0.99 之间。遗忘因子的作用是给旧数据降权让算法能跟上参数变化。λ 等于 1 就是标准递推最小二乘所有历史数据等权λ 小于 1 时大约1/(1-λ)个采样点之前的旧数据权重衰减到很低。比如 λ 取 0.98有效记忆长度约 50 个点。遗忘因子太小估计会跟着噪声剧烈抖动太大又跟不上参数变化跟踪滞后。我实际调这块的体会是先别急着定 λ而是把 λ 从 1 开始逐步往下调观察参数估计曲线。如果曲线收敛后是平的λ 就取 1如果曲线收敛后有缓慢漂移说明参数在变再往下调 λ调到曲线既不抖又能跟上为止。这个过程比拍脑袋定一个数靠谱得多。3.3 辅助变量法对付有色噪声的一把好手辅助变量法的思路很巧妙既然最小二乘失效是因为回归向量和噪声相关那我就构造一个新的向量它跟回归向量高度相关但跟噪声不相关用它去做投影。这个新向量就是辅助变量。构造辅助变量最常用的办法是用一个预滤波器对输入输出信号再走一遍生成一组干净的工具信号。常用的辅助变量有延迟的输入信号、用高估阶次模型产生的预测输出、以及通过相关分析得到的中间变量。延迟输入法实现最简单因为输入 u(k) 通常和噪声不相关用u(k-1)这类延迟项去替代回归向量里的y(k-1)就能打破噪声的反馈回路。辅助变量法的优点是计算量小、不需要迭代、在有色噪声下能得到一致估计。缺点是辅助变量的选择有点技巧性选得不好虽然一致但方差可能很大。我的经验是先用最小二乘跑一遍拿到一个粗略参数再用这个粗略模型生成预测输出作为辅助变量迭代一两轮效果通常能明显改善。3.4 广义最小二乘与增广最小二乘把噪声模型也辨识出来有色噪声下另一个思路是不回避噪声干脆把噪声的模型也一起辨识出来。增广最小二乘RELS就是这个思路的简化版它在回归向量里加入残差的延迟项形式上是把噪声建模成移动平均过程。广义最小二乘GLS更彻底它把噪声建模成自回归过程通过迭代的方式交替估计系统参数和噪声参数。这两个方法的共同点是都引入了噪声模型好处是估计精度高代价是计算复杂、需要迭代、初值敏感。我在实际项目里的选择标准是如果只是为了预测输出用增广最小二乘就够实现简单如果要精确分离信号和噪声比如做故障诊断、要拿噪声特征做判断那就上广义最小二乘。需要提醒的是这类方法对模型阶次很敏感。噪声模型阶次给低了滤不干净给高了容易过拟合把系统动态当成噪声。一般噪声模型阶次不超过系统阶次加一我通常从一阶噪声模型开始试。3.5 相关分析两步法与极大似然、梯度校正相关分析两步法是先做非参数辨识再做参数辨识的经典组合。第一步用输入输出的互相关函数估计系统的脉冲响应第二步把脉冲响应当成数据用最小二乘去拟合一个参数模型。它的好处是对噪声不敏感因为相关运算本身就是一种平均白噪声经过相关运算后会衰减。缺点是要求输入是白噪声或者至少是持续激励信号否则相关函数估计不准。对于白噪声激励下的线性系统相关分析两步法能给出非常好的结果我在做仿真验证时常用它作为金标准去对比其他算法。但真实系统里很少能施加理想白噪声所以它更多是理论分析工具。极大似然法是理论上最优的方法它能达到克拉美-罗下界也就是方差理论最小值。代价是它需要假设噪声的概率分布还要做非线性优化计算量大而且有局部极值的问题。实际用的时候我通常把它作为最后一步精细化的工具用前面某个方法拿到初值再交给极大似然去微调。梯度校正法又叫随机逼近法是最简单的一类它沿着负梯度方向一点点挪参数优点是计算量极小、容易实现缺点是收敛慢。它适合嵌入式设备这种算力受限的场景或者作为其他算法的初始化手段。4. 亲手跑一遍Python 实现递推最小二乘辨识二阶系统光看公式容易飘这一章我带你从头到尾跑一遍完整流程包括造数据、写算法、看结果、做验证。代码用的是 numpy不依赖任何专用工具箱复制下来就能跑。4.1 数据生成与差分方程构造先造一个已知参数的系统这样我们才知道辨识结果准不准。假设真实系统是y(k) 1.5*y(k-1) - 0.7*y(k-2) 0.8*u(k-1) 0.5*u(k-2) e(k)参数真值是 a1-1.5, a20.7, b10.8, b20.5。输入用幅值在 -1 到 1 之间的随机信号噪声用高斯白噪声标准差取 0.05模拟一个信噪比合理的场景。import numpy as np np.random.seed(42) N 1000 u np.random.uniform(-1, 1, N) e np.random.normal(0, 0.05, N) a1_true, a2_true, b1_true, b2_true -1.5, 0.7, 0.8, 0.5 y np.zeros(N) for k in range(2, N): y[k] (-a1_true * y[k-1] - a2_true * y[k-2] b1_true * u[k-1] b2_true * u[k-2] e[k])这段代码里有个细节要注意循环从 k2 开始因为差分方程需要两个历史值前两个点作为初始条件保留为零。实际做辨识时前几十个点因为初始条件不准通常不参与统计或者用零初始化的方式让算法自己收敛。4.2 递推最小二乘代码实现与逐行说明接下来实现递推最小二乘。参数向量按[a1, a2, b1, b2]排列回归向量按[-y(k-1), -y(k-2), u(k-1), u(k-2)]构造。theta np.zeros(4) P np.eye(4) * 1000 # 初始协方差给大值表示对初值不信任 lam 1.0 # 遗忘因子先取1做标准RLS theta_history [] for k in range(2, N): phi np.array([-y[k-1], -y[k-2], u[k-1], u[k-2]]) y_pred phi theta K P phi / (lam phi P phi) theta theta K * (y[k] - y_pred) P (P - np.outer(K, phi) P) / lam theta_history.append(theta.copy()) print(辨识结果:, theta) print(真实参数: [-1.5 0.7 0.8 0.5])跑出来的结果通常在真值附近浮动百分之一到百分之几。初始协方差 P 给大值是因为我们对初始参数完全没信心让算法在前几个点快速调整如果初值有把握比如上一轮辨识的结果P 可以给小一点收敛会更快。4.3 结果验证残差白度、交叉验证与参数置信区间参数估计出来还不算完必须验证。我固定做三件事。第一件是看残差。计算e_hat(k) y(k) - φ(k)^T θ然后检查残差的自相关函数。如果模型结构正确、噪声是白噪声残差应该接近白噪声自相关在非零延迟处应该很小。如果残差自相关有明显峰值说明模型阶次低了或者残差里还有没被解释的动态得回去加阶次。第二件是交叉验证。把数据分成两段前 70% 用于辨识后 30% 用于验证看模型在验证集上的预测误差。如果训练集误差小、验证集误差大就是过拟合得降阶次或者加正则化。这个步骤在建模竞赛里尤其重要评委很看重模型的泛化能力。第三件是看参数置信区间。递推最小二乘的协方差矩阵 P 直接给出了参数的方差估计参数标准差就是 P 对角线元素的平方根。如果某个参数的标准差跟它的估计值一个量级说明这个参数没被辨识出来多半是激励不够。这时候要回去检查输入信号在那个参数方向上的激励是否充分。提示参数置信区间宽先别怀疑算法先怀疑数据。九成情况是激励信号设计的问题。5. 常见问题与排查技巧实录辨识这件事算法只是其中一环真正消耗时间的是排查各种异常。这一章我把这些年遇到的问题整理成速查表配上我自己的排查思路。5.1 参数估不准的典型症状与对策症状可能原因排查方法对策参数稳定偏离真值有色噪声导致最小二乘有偏检查残差自相关换成辅助变量或增广最小二乘参数剧烈抖动遗忘因子过小或数据噪声大看参数随时间曲线增大遗忘因子、增加滤波参数收敛极慢初值差、激励不足检查 P 矩阵收敛情况加大初始 P、增强激励某些参数估不出来该参数方向无激励看参数置信区间换激励信号或增加幅值训练好验证差过拟合、阶次过高交叉验证对比降低模型阶次、加正则出现虚假纯滞后输入输出滤波不一致对比两路信号延迟用相同滤波器处理两路这张表基本覆盖了我遇到的八成问题。剩下两成通常是数值问题下一节专门讲。5.2 数值稳定性与病态矩阵的处理最小二乘要求解(Φ^T Φ)^{-1}当数据存在共线性或者量纲差异大时这个矩阵会接近奇异求逆结果被放大误差污染。几个实用的处理办法一是做归一化。把输入输出都减均值、除标准差让各列量级接近能显著改善条件数。这个方法我每回都用几乎不花钱。二是用 QR 分解或者奇异值分解代替直接求逆。numpy 里的np.linalg.lstsq内部就是用这种方法数值稳定性远好于显式求逆。批处理辨识时直接用这个函数别自己写求逆。三是检查条件数。np.linalg.cond(Φ)如果超过 10 的 6 次方基本可以判定矩阵病态这时候估计结果不可信得回数据层面找原因比如是不是某段数据几乎没变化。递推最小二乘也有对应的稳定性问题主要来自 P 矩阵的对称性和正定性在数值迭代中可能被破坏。工程上常用 UD 分解或者平方根滤波来保持正定性如果嫌复杂至少每迭代若干步重新对称化一次 P 矩阵能缓解不少。5.3 几个踩过的坑第一个坑是数据对齐。输入输出采集如果用不同设备时钟可能有微小偏差这个偏差会在辨识中表现为虚假滞后让模型阶次判断出错。解决办法是做一次互相关找到互相关峰值位置据此对齐两路信号。第二个坑是量纲。曾经有个项目输入是电压信号在毫伏量级输出是温度在摄氏度量级量纲差了几个数量级。最小二乘直接跑出来的参数极小看着像是估不出来其实是数值精度问题。归一化之后一切正常。第三个坑是验证集泄漏。有段时间我把数据随机打乱后切分训练验证集结果验证误差异常地好后来才发现系统的动态是连续相关的随机切分会让相邻点落到不同的集合里造成信息泄漏。正确做法是按时间顺序切分前一段训练后一段验证。6. 方法选型的一点个人经验走到这里方法也讲完了实现也跑过了问题也排查过了。最后分享几条我在实际项目里形成的选型直觉。如果模型阶次不超过三阶、噪声水平不高、又有条件设计实验我一般先用最小二乘加归一化快速跑一遍看看参数量级和置信区间。这一步花不了十分钟但能帮你判断问题难度。如果置信区间很宽别急着换算法先回去改实验设计。这是我见过最多的误判把数据问题当成算法问题反复折腾算法效率极低。如果噪声有色明显我习惯直接上辅助变量法实现成本比广义最小二乘低效果通常也够用。只有在需要精确分离噪声特征时才上广义最小二乘或极大似然。递推形式我只在系统参数会漂移、或者需要在线更新的时候才用静态问题用批处理更省事也更容易复现。在建模竞赛的语境下我还会额外做一件事把辨识出来的参数代回原方程的物理约束里检查一遍。比如传染病模型的传播率必须是非负的如果辨识结果出现负值那一定是模型或者数据出了问题。这种物理约束的检查往往是发现错误最快的手段比看残差曲线还直接。辨识这个事说到底是和数据打交道的过程。算法是工具数据是原料验证是质检。三样都到位参数自然就准了。