第三章 辨识实验设计:输入信号设计、采样时间选择与数据预处理
做模型辨识这么多年,我最大的体会是:实验设计没做好,后面再牛的算法也白搭。说白了,输入信号就是给系统出的考题,你出的题太简单,系统真实特性就测不出来。今天咱们就聊聊怎么出好这道题。
3.1 输入信号设计——给系统出什么题?
辨识实验的核心,就是通过输入信号把系统的动态特性“激”出来。我见过太多人随便给个阶跃信号就开始辨识,结果模型精度惨不忍睹。为什么?因为阶跃信号的能量集中在低频,高频动态根本测不到。
3.1.1 白噪声——理论上的完美信号
白噪声在理论上是最理想的辨识信号。它的功率谱密度在所有频率上都是常数,能均匀地激励系统的所有模态。但现实中,纯白噪声很难实现。
白噪声的关键特性:
- 均值为零:E[u(t)] = 0
- 自相关函数为脉冲:R_u(τ) = σ²·δ(τ)
- 功率谱密度平坦:S_u(ω) = σ²
我在项目中用过硬件白噪声发生器,但说实话,效果并不理想。原因有二:一是实际系统的执行器(比如阀门、电机)响应速度有限,跟不上白噪声的高频变化;二是白噪声的幅值分布是高斯型,偶尔会出现很大的尖峰,可能把系统推到非线性区甚至损坏设备。
⚠ 注意:工业现场千万别直接用高斯白噪声做辨识!我曾经在化工厂试过一次,结果阀门执行器因为频繁大幅动作,不到半小时就过热保护了。后来我学乖了,改用限幅白噪声或PRBS。
3.1.2 PRBS——工程界的首选
伪随机二进制序列(PRBS)是我个人最常用的辨识信号。它只有两个幅值(±a),像方波一样在高低电平之间随机切换。你想想看,执行器只需要在两个位置之间切换,多省事!
PRBS的生成靠的是线性反馈移位寄存器(LFSR)。一个n阶的LFSR可以生成周期为N=2ⁿ-1的序列。比如n=5,周期就是31个时钟周期。
// PRBS生成示例(Python)
import numpy as np
def generate_prbs(n_bits, n_periods, amplitude=1.0):
"""
生成PRBS信号
n_bits: 移位寄存器阶数
n_periods: 周期数
amplitude: 信号幅值
"""
# 初始化寄存器(全1)
reg = [1] * n_bits
# 反馈多项式(常用本原多项式)
# 例如 n=5 时,反馈抽头为 [5, 3]
taps = {3: [3, 2], 4: [4, 3], 5: [5, 3], 6: [6, 5], 7: [7, 6]}
seq_len = (2**n_bits - 1) * n_periods
prbs = []
for _ in range(seq_len):
# 输出最低位
prbs.append(reg[-1])
# 计算反馈
feedback = 0
for tap in taps[n_bits]:
feedback ^= reg[tap-1]
# 移位
reg = [feedback] + reg[:-1]
# 映射到 ±amplitude
return amplitude * (2 * np.array(prbs) - 1)
💡 经验之谈:PRBS的时钟周期ΔT怎么选?我一般取系统上升时间的1/5到1/10。比如系统上升时间1秒,ΔT就取0.1~0.2秒。太大会丢失高频信息,太小则执行器跟不上。
3.2 采样时间选择——别让数据说谎
采样时间选错了,辨识出来的模型就是错的。这个坑我踩过不止一次。
3.2.1 采样定理与工程折衷
理论上,采样频率要大于系统最高频率的两倍(奈奎斯特采样定理)。但实际中,我建议采样频率取系统截止频率的5~10倍。为什么?因为真实系统总有高频噪声,留点余量好做滤波。
| 系统类型 | 典型上升时间 | 建议采样时间 |
|---|---|---|
| 温度系统 | 10~100秒 | 1~10秒 |
| 压力系统 | 1~10秒 | 0.1~1秒 |
| 电机转速 | 0.1~1秒 | 0.01~0.1秒 |
| 机械振动 | 0.001~0.01秒 | 0.0001~0.001秒 |
3.2.2 采样时间的经验法则
我个人总结了一套选采样时间的步骤:
- 先做阶跃响应测试——给系统一个阶跃输入,记录响应曲线
- 估算上升时间T_r——从10%到90%的时间
- 采样时间取T_r/10——这是起点,后续可以调整
- 检查数据质量——如果相邻采样点变化太大或太小,调整采样时间
⚠ 我曾经犯过的错:有一次辨识一个热风炉的温度模型,我按经验取了1秒采样。结果数据量巨大,模型阶数怎么都定不下来。后来发现,温度变化很慢,1秒采样根本就是浪费。改成10秒采样后,模型一下子就清晰了。记住:采样太快不一定好,关键是信息要够用。
3.3 数据预处理——把脏数据洗干净
现场采集的数据,十有八九是脏的。有噪声、有漂移、有异常值。直接拿这些数据去辨识,模型肯定不准。数据预处理,说白了就是给数据“洗澡”。
3.3.1 去趋势——消除低频漂移
传感器温漂、环境变化都会让数据带上缓慢的漂移。如果不处理,辨识出来的模型会多出一个虚假的积分环节。
# 去趋势处理(Python)
import numpy as np
from scipy import signal
def remove_trend(data, method='linear'):
"""
去除数据趋势
method: 'linear' 线性去趋势, 'mean' 减去均值
"""
if method == 'linear':
# 线性拟合后减去
x = np.arange(len(data))
coeffs = np.polyfit(x, data, 1)
trend = np.polyval(coeffs, x)
return data - trend
elif method == 'mean':
return data - np.mean(data)
3.3.2 滤波——别把噪声当信号
高频噪声会严重干扰辨识结果。我习惯用低通滤波,但要注意截止频率不能太低,否则会把系统的高频动态也滤掉。
💡 我的滤波原则:截止频率取系统带宽的2~3倍。比如系统带宽1Hz,滤波器截止频率就设2~3Hz。这样既能滤掉高频噪声,又不损失有用信息。
3.3.3 异常值处理——揪出“坏点”
传感器偶尔会跳变,或者通信丢包导致数据异常。这些“坏点”如果不处理,会让辨识算法跑偏。
我常用的方法是3σ准则:计算数据的均值和标准差,超出均值±3σ的点视为异常值,用前后点的平均值替代。
def remove_outliers(data, threshold=3):
"""
3σ准则去除异常值
"""
mean = np.mean(data)
std = np.std(data)
clean_data = data.copy()
for i in range(len(data)):
if abs(data[i] - mean) > threshold * std:
# 用前后点的均值替代
if i == 0:
clean_data[i] = data[i+1]
elif i == len(data)-1:
clean_data[i] = data[i-1]
else:
clean_data[i] = (data[i-1] + data[i+1]) / 2
return clean_data
3.4 本章知识体系
下面这张图总结了辨识实验设计的核心逻辑。你看,从输入信号设计到采样时间选择,再到数据预处理,每一步都环环相扣。
3.5 实操建议——从理论到现场
最后,我给大家几条实操建议:
- 先做预实验——正式辨识前,先给系统一个小的阶跃信号,看看响应速度、噪声水平,再确定采样时间和信号参数
- 多周期采集——PRBS至少采集3~5个完整周期,保证数据量足够
- 数据分段检查——把采集的数据分成几段,分别做预处理,看看一致性如何
- 保留原始数据——预处理后的数据用于辨识,但原始数据一定要保留,方便回溯
记住:辨识实验设计不是一次就能搞定的。我每次做新系统的辨识,至少会做三轮实验:第一轮摸清系统脾气,第二轮优化参数,第三轮正式采集。别嫌麻烦,前期的功夫都会在模型精度上体现出来。
好了,关于辨识实验设计就聊到这儿。下一节咱们进入模型结构选择,看看怎么给系统挑一个合适的“骨架”。