4. 信号处理与滤波:低通滤波、移动平均滤波、卡尔曼滤波入门,Python仿真实现
做风机控制这些年,我最大的体会就是:传感器数据从来都不干净。
你想想看,风速传感器测回来的信号,抖得像心电图。编码器反馈的转速,时不时蹦个毛刺出来。如果不做滤波,控制器就会跟着这些噪声一起抽风。今天我们就聊聊三种最常用的滤波方法,从简单到复杂,我会结合我在风机项目里的实际踩坑经历来讲。
4.1 为什么风机控制离不开滤波?
风机运行环境其实挺恶劣的。塔筒会振动,叶片会受湍流影响,传感器本身也有热噪声。我见过一个新手工程师,直接把原始风速信号送进PID控制器,结果电机在额定转速附近来回震荡,最后把IGBT模块烧了。
滤波的目的很简单:保留有用信号,抑制噪声。但难点在于——有用信号和噪声往往混在一起。比如风速的缓慢变化是我们要跟踪的,而叶片扫过塔筒时的振动干扰就是噪声。怎么把它们分开?这就是滤波算法要干的事。
核心原则:滤波不是把信号变“平滑”就完事了,关键是要在“平滑度”和“响应速度”之间找到平衡。滤得太狠,信号滞后,系统反应慢;滤得太轻,噪声还在,系统不稳定。
4.2 一阶低通滤波(LPF)——最经典的“惯性环节”
一阶低通滤波,说白了就是一个带遗忘因子的加权平均。它的数学形式很简单:
y[n] = α * x[n] + (1 - α) * y[n-1]
其中α是滤波系数,范围0到1。α越大,当前采样值权重越高,滤波效果越弱;α越小,历史值权重越高,滤波效果越强,但滞后也越大。
α怎么选? 我一般用这个公式:
α = 2π * fc * Ts
fc是截止频率,Ts是采样周期。比如你的采样频率是100Hz,想滤掉10Hz以上的噪声,那fc取5Hz,α ≈ 2 * 3.14 * 5 * 0.01 = 0.314。
我的经验:在风机变桨控制里,我习惯把α设在0.1~0.3之间。太小了(比如0.01),风速突变时桨叶响应慢半拍,发电量会损失;太大了(比如0.8),噪声滤不干净,变桨电机频繁动作,寿命缩短。
Python实现
import numpy as np
import matplotlib.pyplot as plt
def lowpass_filter(data, alpha):
filtered = np.zeros_like(data)
filtered[0] = data[0]
for i in range(1, len(data)):
filtered[i] = alpha * data[i] + (1 - alpha) * filtered[i-1]
return filtered
# 模拟带噪声的风速信号
t = np.linspace(0, 10, 1000)
true_wind = 5 + 2 * np.sin(0.5 * t) # 真实风速
noise = np.random.normal(0, 0.5, len(t)) # 高斯噪声
measured = true_wind + noise
# 应用低通滤波
alpha = 0.15
filtered = lowpass_filter(measured, alpha)
plt.plot(t, measured, label='测量值', alpha=0.5)
plt.plot(t, filtered, label='滤波后', linewidth=2)
plt.plot(t, true_wind, '--', label='真实值')
plt.legend()
plt.show()
4.3 移动平均滤波(MAF)——简单粗暴但有效
移动平均滤波的思路更直接:取最近N个采样点的平均值作为当前输出。
y[n] = (x[n] + x[n-1] + ... + x[n-N+1]) / N
N越大,平滑效果越好,但滞后也越严重。我做过一个对比测试:N=5时,滞后约2~3个采样周期;N=20时,滞后接近10个采样周期。
注意:移动平均滤波对脉冲噪声(比如传感器瞬间跳变)的抑制效果很好,但对缓慢漂移无能为力。我曾经在风机偏航控制里用MAF处理风向信号,结果发现风向缓慢变化时,滤波后的信号总是慢半拍,导致偏航对风不准。后来换成了卡尔曼滤波才解决。
Python实现
def moving_average(data, window_size):
filtered = np.convolve(data, np.ones(window_size)/window_size, mode='same')
# 处理边界
filtered[:window_size//2] = data[:window_size//2]
filtered[-window_size//2:] = data[-window_size//2:]
return filtered
# 应用移动平均滤波
window = 10
ma_filtered = moving_average(measured, window)
4.4 卡尔曼滤波入门——从“猜”到“信”
卡尔曼滤波听起来高大上,其实核心思想就一句话:用模型预测,用测量修正。
我刚开始学卡尔曼滤波时,被那一堆矩阵和协方差搞晕了。后来在风机塔筒振动监测项目里硬着头皮用了一次,才真正理解它的妙处。
核心思想
卡尔曼滤波假设系统是线性的,噪声是高斯分布。它分两步走:
- 预测步:根据上一时刻的状态,预测当前时刻的状态和误差协方差。
- 更新步:用当前测量值修正预测值,得到最优估计。
说白了,就是“模型说应该这样,传感器说应该那样,我取个加权平均”。这个权重就是卡尔曼增益K,它会根据预测和测量的可信度自动调整。
一维卡尔曼滤波的Python实现
class KalmanFilter1D:
def __init__(self, process_noise, measurement_noise, initial_value):
self.Q = process_noise # 过程噪声协方差
self.R = measurement_noise # 测量噪声协方差
self.x = initial_value # 状态估计
self.P = 1.0 # 误差协方差初始值
def predict(self):
# 预测步:假设状态不变(匀速模型可扩展)
self.P += self.Q
def update(self, measurement):
# 计算卡尔曼增益
K = self.P / (self.P + self.R)
# 更新状态估计
self.x += K * (measurement - self.x)
# 更新误差协方差
self.P = (1 - K) * self.P
return self.x
# 使用示例
kf = KalmanFilter1D(process_noise=0.01, measurement_noise=0.25, initial_value=5.0)
kf_results = []
for meas in measured:
kf.predict()
est = kf.update(meas)
kf_results.append(est)
参数调优经验:Q和R是卡尔曼滤波的灵魂。Q越大,说明模型不可靠,滤波器会更相信测量值;R越大,说明测量噪声大,滤波器会更依赖模型预测。在风机转速估计中,我通常把Q设在0.001~0.01之间,R根据传感器手册的噪声方差来定。
4.5 三种滤波方法的对比
| 特性 | 一阶低通滤波 | 移动平均滤波 | 卡尔曼滤波 |
|---|---|---|---|
| 计算复杂度 | 极低(1次乘加) | 低(N次加法) | 中等(矩阵运算) |
| 内存需求 | 2个变量 | N个缓存 | 几个矩阵 |
| 滞后特性 | 固定滞后 | N/2采样周期 | 自适应滞后 |
| 抗脉冲噪声 | 一般 | 好 | 好 |
| 适用场景 | 实时性要求高的场合 | 周期性噪声抑制 | 需要状态估计的场景 |
4.6 知识体系结构图
4.7 实际项目中的选择建议
说了这么多,到底该用哪个?我根据实际项目经验给几个建议:
- 单片机资源紧张(比如8位MCU):用一阶低通滤波。一个乘加指令就搞定,我曾在STM8上跑过,CPU占用不到1%。
- 需要抑制周期性干扰(比如50Hz工频噪声):用移动平均滤波,窗口大小取采样频率/干扰频率的整数倍。
- 需要估计不可直接测量的量(比如风机桨叶载荷):上卡尔曼滤波。虽然代码复杂点,但效果确实好。
避坑指南:我曾经在一个项目里,直接用低通滤波处理编码器转速信号,结果发现电机在低速时转速波动很大。后来分析发现,编码器在低速时脉冲间隔长,采样率不够,低通滤波的α值没跟着调整。解决方案是改用自适应滤波——根据转速动态调整α值。嗯,这个坑踩得值。
最后说一句:滤波不是万能的。如果传感器本身质量太差,再好的滤波算法也救不回来。我见过有人花三个月调卡尔曼滤波参数,最后发现是传感器接地不良导致的噪声。先检查硬件,再优化算法,这个顺序别搞反了。
公众号:蓝海资料掘金营,微信deep3321