做嵌入式开发或信号处理的同学,大概率有过这种困惑:看到"一阶低通滤波""二阶陷波滤波"的资料时,满屏的拉普拉斯变换、Z变换公式让人头大;明明知道这些滤波算法能解决信号噪声问题,却搞不懂从数学公式到C语言代码的转化逻辑,只能生硬套用现成代码,遇到参数调整、场景适配的问题就束手无策。其实,传递函数就是连接数学理论与工程实现的"桥梁"------它能清晰描述系统的输入输出关系,是我们理解滤波特性、推导运算代码的核心工具。今天就从编程实用角度,把连续/离散传递函数的核心概念讲通俗,结合常见滤波器案例,手把手教你从传递函数推导差分方程,最终落地成可直接移植的C语言代码!
一、原理拆解:传递函数的核心逻辑(简化版)
先抛开复杂的数学推导,用最直白的话解释传递函数:传递函数是描述线性系统输入与输出之间数学关系的表达式,核心作用是"预测"系统对不同输入信号的响应。比如我们常用的低通滤波器,输入是含高频噪声的信号,输出是平滑后的信号,传递函数就定量描述了"哪些频率的信号能通过、哪些会被衰减"。
从工程实用角度,我们只需掌握两种核心传递函数:
-
连续系统传递函数(基于拉普拉斯变换):描述连续时间系统的特性,比如模拟电路实现的滤波器。核心价值是帮我们理解滤波的基本原理,很多离散滤波算法都是从连续传递函数转化而来的。它的基本形式很简单: G(s)=输出信号拉普拉斯变换/输入信号拉普拉斯变换G(s) = 输出信号拉普拉斯变换 / 输入信号拉普拉斯变换G(s)=输出信号拉普拉斯变换/输入信号拉普拉斯变换 ,其中s是复频率变量,我们不用深究其物理意义,重点关注分子分母的多项式形式即可。
-
离散系统传递函数(基于Z变换):描述离散时间系统的特性,也就是我们嵌入式程序中实现的数字滤波。它是连续传递函数通过"离散化"得到的,基本形式是 G(z)=输出序列Z变换/输入序列Z变换G(z) = 输出序列Z变换 / 输入序列Z变换G(z)=输出序列Z变换/输入序列Z变换 ,z是离散域的复变量。对编程而言,离散传递函数是推导差分方程的直接依据,是实现数字滤波的核心。
核心结论:我们不用精通拉普拉斯和Z变换的推导,只需能看懂常见滤波器的传递函数,掌握"从传递函数→差分方程"的转化方法,就能自主实现滤波算法的C语言编程,还能根据场景调整参数。
二、工程化分析:传递函数的实用价值与转化逻辑
在嵌入式信号处理中,传递函数的核心价值是"指导算法实现",而非理论研究。工程化应用的核心逻辑是:先根据需求选择合适的滤波器类型(如低通、高通、陷波),确定其传递函数;再将传递函数转化为差分方程;最后把差分方程用C语言实现。
工程实现中需重点关注三个关键点:
-
滤波器阶数与复杂度:传递函数分母的最高次数就是系统阶数,阶数越高,滤波效果越好,但对应的差分方程运算越复杂,占用MCU的算力和内存越多。嵌入式场景中,一阶、二阶滤波器因实现简单、资源占用少,应用最广泛。
-
离散化方法选择:将连续传递函数转化为离散传递函数的过程叫"离散化",常用方法有冲激响应不变法、零极点匹配法、双线性变换法。其中双线性变换法能避免频率混叠,是数字滤波中最常用的离散化方法,后续案例均采用此方法。
-
数值稳定性:转化后的差分方程在C语言实现时,需注意数值溢出和精度问题。建议采用定点数运算(如Q15格式),或合理选择数据类型(如float32_t),确保滤波过程中数值不会超出范围,保证系统稳定。
三、核心转化:从传递函数到差分方程(实战案例)
这是本文的核心部分------从常见滤波器的传递函数出发,推导嵌入式编程可用的差分方程。我们选取三个高频应用场景:一阶低通、一阶高通、二阶陷波滤波器,全程聚焦实用转化步骤,弱化数学推导。
1. 案例1:一阶低通滤波器(最常用平滑滤波)
一阶低通滤波器的核心作用是平滑高频噪声,比如温度、压力等慢变信号的滤波。
第一步:连续传递函数(模拟低通)。一阶低通的连续传递函数为: G(s)=1/(τs+1)G(s) = 1 / (\tau s + 1)G(s)=1/(τs+1) ,其中τ是时间常数,τ越大,滤波越平滑(高频衰减越强)。
第二步:离散化得到离散传递函数。采用双线性变换法,将 s=2(1−z−1)/(T(1+z−1))s = 2(1 - z^{-1})/(T(1 + z^{-1}))s=2(1−z−1)/(T(1+z−1)) (T为采样周期)代入连续传递函数,化简后得到离散传递函数: G(z)=T/(τ×2+T)×(1+z−1)/(1−(τ×2−T)/(τ×2+T)z−1)G(z) = T / (\tau \times 2 + T) \times (1 + z^{-1}) / (1 - (\tau \times 2 - T)/(\tau \times 2 + T) z^{-1})G(z)=T/(τ×2+T)×(1+z−1)/(1−(τ×2−T)/(τ×2+T)z−1) 。为简化计算,定义系数: a0=T/(2τ+T)a0 = T/(2\tau + T)a0=T/(2τ+T) , a1=a0a1 = a0a1=a0 , b1=(2τ−T)/(2τ+T)b1 = (2\tau - T)/(2\tau + T)b1=(2τ−T)/(2τ+T) ,则传递函数可简化为: G(z)=(a0+a1z−1)/(1−b1z−1)G(z) = (a0 + a1 z^{-1}) / (1 - b1 z^{-1})G(z)=(a0+a1z−1)/(1−b1z−1) 。
第三步:推导差分方程。根据离散传递函数的定义 G(z)=Y(z)/X(z)G(z) = Y(z)/X(z)G(z)=Y(z)/X(z) (Y(z)是输出Z变换,X(z)是输入Z变换),代入简化后的传递函数可得: Y(z)(1−b1z−1)=(a0+a1z−1)X(z)Y(z)(1 - b1 z^{-1}) = (a0 + a1 z^{-1})X(z)Y(z)(1−b1z−1)=(a0+a1z−1)X(z) 。将Z变换的延迟特性( z−1z^{-1}z−1 表示延迟一个采样周期)转化为时间域,就得到差分方程: y(n)=a0×x(n)+a1×x(n−1)+b1×y(n−1)y(n) = a0 \times x(n) + a1 \times x(n-1) + b1 \times y(n-1)y(n)=a0×x(n)+a1×x(n−1)+b1×y(n−1) 。其中x(n)是当前输入采样值,x(n-1)是上一时刻输入值,y(n)是当前输出值,y(n-1)是上一时刻输出值------这个方程就是我们C语言实现的核心!
2. 案例2:一阶高通滤波器(抑制直流漂移)
一阶高通滤波器的核心作用是抑制直流分量或低频漂移,比如传感器零漂的消除。
简化转化过程:连续传递函数 G(s)=τs/(τs+1)G(s) = \tau s / (\tau s + 1)G(s)=τs/(τs+1) ,经双线性变换离散化后,定义系数 a0=2τ/(2τ+T)a0 = 2\tau/(2\tau + T)a0=2τ/(2τ+T) , a1=−a0a1 = -a0a1=−a0 , b1=(2τ−T)/(2τ+T)b1 = (2\tau - T)/(2\tau + T)b1=(2τ−T)/(2τ+T) ,对应的差分方程为: y(n)=a0×x(n)+a1×x(n−1)+b1×y(n−1)y(n) = a0 \times x(n) + a1 \times x(n-1) + b1 \times y(n-1)y(n)=a0×x(n)+a1×x(n−1)+b1×y(n−1) 。可见其差分方程形式与一阶低通一致,仅系数不同,这也是嵌入式实现中可复用代码框架的关键。
3. 案例3:二阶陷波滤波器(抑制特定频率干扰)
二阶陷波滤波器的核心作用是精准衰减单一特定频率的噪声(如50Hz工频干扰),是嵌入式信号处理中解决工频干扰的首选算法。
简化转化过程:二阶陷波的离散传递函数经双线性变换后,简化形式为 G(z)=(a0+a1z−1+a2z−2)/(1−b1z−1−b2z−2)G(z) = (a0 + a1 z^{-1} + a2 z^{-2}) / (1 - b1 z^{-1} - b2 z^{-2})G(z)=(a0+a1z−1+a2z−2)/(1−b1z−1−b2z−2) 。对应的差分方程为: y(n)=a0×x(n)+a1×x(n−1)+a2×x(n−2)+b1×y(n−1)+b2×y(n−2)y(n) = a0 \times x(n) + a1 \times x(n-1) + a2 \times x(n-2) + b1 \times y(n-1) + b2 \times y(n-2)y(n)=a0×x(n)+a1×x(n−1)+a2×x(n−2)+b1×y(n−1)+b2×y(n−2) 。其中x(n-2)、y(n-2)是前两个时刻的输入、输出值,相比一阶滤波器多了一个延迟项,运算复杂度略有增加,但抑制特定频率的效果更精准。
四、Python仿真实现:滤波算法验证与效果可视化
基于前面推导的差分方程,我们用Python实现滤波算法的仿真验证。Python的numpy库可快速生成含噪声的测试信号,matplotlib库能直观可视化滤波效果,非常适合算法调试和参数优化。以下实现通用滤波类,支持一阶低通、高通和二阶陷波滤波,仿真流程与嵌入式实际运行逻辑一致。
1. 通用滤波类定义(封装系数与状态管理)
python
import numpy as np
import matplotlib.pyplot as plt
class DigitalFilter:
def __init__(self):
self.type = None # 滤波类型:'lowpass'/'highpass'/'notch'
self.a0, self.a1, self.a2 = 0.0, 0.0, 0.0 # 输入系数
self.b1, self.b2 = 0.0, 0.0 # 输出系数
# 历史状态缓存(输入x(n-1)、x(n-2);输出y(n-1)、y(n-2))
self.x_prev1, self.x_prev2 = 0.0, 0.0
self.y_prev1, self.y_prev2 = 0.0, 0.0
def init(self, filter_type, T, param1, param2=0.0):
"""
滤波器初始化:计算系数并重置状态
:param filter_type: 滤波类型 'lowpass'/'highpass'/'notch'
:param T: 采样周期(s)
:param param1: 一阶滤波:时间常数τ;二阶陷波:中心频率f0(Hz)
:param param2: 二阶陷波:品质因数Q(默认3~5)
:return: True-成功,False-失败
"""
if T <= 0:
return False
self.type = filter_type
# 重置历史状态
self.x_prev1 = self.x_prev2 = 0.0
self.y_prev1 = self.y_prev2 = 0.0
if filter_type == 'lowpass':
# 一阶低通系数计算
tau = param1
denominator = 2 * tau + T
self.a0 = T / denominator
self.a1 = self.a0
self.a2 = 0.0
self.b1 = (2 * tau - T) / denominator
self.b2 = 0.0
elif filter_type == 'highpass':
# 一阶高通系数计算
tau = param1
denominator = 2 * tau + T
self.a0 = 2 * tau / denominator
self.a1 = -self.a0
self.a2 = 0.0
self.b1 = (2 * tau - T) / denominator
self.b2 = 0.0
elif filter_type == 'notch':
# 二阶陷波系数计算
f0 = param1
Q = param2
if f0 <= 0 or Q <= 0:
return False
w0 = 2 * np.pi * f0 # 中心角频率
alpha = np.sin(w0 * T / 2) / (2 * Q)
denominator = 1 + alpha
self.a0 = 1 / denominator
self.a1 = -2 * np.cos(w0 * T) / denominator
self.a2 = self.a0
self.b1 = 2 * (np.cos(w0 * T) * (1 - alpha)) / denominator
self.b2 = (alpha - 1) / denominator
else:
return False
return True
def process(self, x):
"""
滤波核心运算(单次采样)
:param x: 当前输入采样值
:return: 滤波后输出值
"""
y = 0.0
if self.type in ['lowpass', 'highpass']:
# 一阶滤波差分方程
y = self.a0 * x + self.a1 * self.x_prev1 + self.b1 * self.y_prev1
# 更新历史状态
self.x_prev1 = x
self.y_prev1 = y
elif self.type == 'notch':
# 二阶滤波差分方程
y = (self.a0 * x + self.a1 * self.x_prev1 + self.a2 * self.x_prev2 +
self.b1 * self.y_prev1 + self.b2 * self.y_prev2)
# 更新历史状态(注意顺序)
self.x_prev2 = self.x_prev1
self.x_prev1 = x
self.y_prev2 = self.y_prev1
self.y_prev1 = y
return y
def batch_process(self, x_list):
"""
批量处理信号(用于仿真测试)
:param x_list: 输入信号列表
:return: 滤波后输出信号列表
"""
y_list = []
for x in x_list:
y_list.append(self.process(x))
return y_list
2. 仿真测试框架(生成测试信号+滤波验证)
python
def generate_test_signal(T, duration, signal_type='temp_noise'):
"""
生成测试信号(模拟嵌入式场景中的传感器信号)
:param T: 采样周期(s)
:param duration: 信号时长(s)
:param signal_type: 信号类型 'temp_noise'/'acc_drift'/'ecg_50hz'
:return: 时间序列t,原始信号x
"""
t = np.arange(0, duration, T) # 时间序列
N = len(t)
if signal_type == 'temp_noise':
# 模拟温度信号:直流分量+缓慢变化+高频噪声
x = 25 + 2 * np.sin(2 * np.pi * 0.5 * t) + 0.5 * np.random.randn(N)
elif signal_type == 'acc_drift':
# 模拟加速度信号:动态信号+直流漂移
x = 0.5 * np.sin(2 * np.pi * 5 * t) + 0.2 * (t / duration) + 0.1 * np.random.randn(N)
elif signal_type == 'ecg_50hz':
# 模拟心电信号:QRS波+50Hz工频干扰
x = np.zeros(N)
# 生成QRS波(模拟心电特征)
qrs_pos = np.arange(0.5, duration, 1.0) # 每1秒一个QRS波
for pos in qrs_pos:
idx = np.where(np.abs(t - pos) < 0.05)[0]
x[idx] = 2 * np.exp(-((t[idx] - pos)/0.02)**2)
# 添加50Hz干扰和噪声
x += 0.8 * np.sin(2 * np.pi * 50 * t) + 0.1 * np.random.randn(N)
else:
x = np.zeros(N)
return t, x
# 通用仿真函数
def filter_simulation(filter_type, T, duration, param1, param2=0.0, signal_type='temp_noise'):
"""
滤波仿真与效果可视化
:param filter_type: 滤波类型 'lowpass'/'highpass'/'notch'
:param T: 采样周期(s)
:param duration: 信号时长(s)
:param param1: 一阶滤波:τ;二阶陷波:f0
:param param2: 二阶陷波:Q
:param signal_type: 测试信号类型
"""
# 1. 生成测试信号
t, x_raw = generate_test_signal(T, duration, signal_type)
# 2. 初始化滤波器
filter = DigitalFilter()
if not filter.init(filter_type, T, param1, param2):
print("滤波器初始化失败!")
return
# 3. 执行滤波
x_filtered = filter.batch_process(x_raw)
# 4. 可视化结果
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(t, x_raw, label='原始信号', color='lightcoral', linewidth=1)
plt.xlabel('时间 (s)')
plt.ylabel('信号幅度')
plt.title(f'{filter_type}滤波 - 原始信号')
plt.legend()
plt.grid(True, alpha=0.3)
plt.subplot(2, 1, 2)
plt.plot(t, x_filtered, label='滤波后信号', color='royalblue', linewidth=1.5)
plt.xlabel('时间 (s)')
plt.ylabel('信号幅度')
plt.title(f'{filter_type}滤波 - 滤波后信号')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
3. 快速运行示例(对应三大实战场景)
python
# 示例1:温度信号平滑(一阶低通)
# 参数:采样周期T=0.1s(10Hz),时间常数τ=0.5s
filter_simulation(
filter_type='lowpass',
T=0.1,
duration=10, # 10秒信号
param1=0.5,
signal_type='temp_noise'
)
# 示例2:传感器零漂抑制(一阶高通)
# 参数:采样周期T=0.01s(100Hz),时间常数τ=0.1s
filter_simulation(
filter_type='highpass',
T=0.01,
duration=5, # 5秒信号
param1=0.1,
signal_type='acc_drift'
)
# 示例3:工频干扰抑制(二阶陷波)
# 参数:采样周期T=0.005s(200Hz),中心频率f0=50Hz,品质因数Q=5
filter_simulation(
filter_type='notch',
T=0.005,
duration=5, # 5秒信号
param1=50.0,
param2=5.0,
signal_type='ecg_50hz'
)
# 运行说明:
# 1. 需安装依赖库:pip install numpy matplotlib
# 2. 直接运行代码即可生成滤波前后的对比图
# 3. 可通过调整param1/param2参数,观察滤波效果变化,快速优化参数
五、问题解决:滤波实现中的常见坑与解决方案
在传递函数转化和C语言实现过程中,容易遇到以下问题,针对性解决方案如下:
-
滤波效果差(噪声未有效抑制):原因可能是滤波器参数选择不当(如一阶低通τ太小、陷波频率不准)。解决方案:根据采样频率和噪声特性调整参数,比如增大低通τ、校准陷波中心频率;若一阶滤波效果不足,可改用二阶滤波。
-
信号延迟过大:原因是滤波器阶数过高或时间常数太大。解决方案:在滤波效果和延迟之间权衡,比如降低一阶低通的τ、选择合适的Q值(陷波滤波器);对实时性要求高的场景,优先用一阶滤波。
-
数值溢出/精度失真:原因是数据类型选择不当,尤其是定点数运算时。解决方案:优先用float32_t类型;若需用定点数(如Q15),需对系数和采样值进行缩放,确保运算过程中数值在[-32768, 32767]范围内。
-
初始化后前几帧数据异常:原因是历史状态未初始化或初始化不当。解决方案:将x_prev1、y_prev1等历史值初始化为0,或用第一个采样值初始化,避免前几帧输出突变。
总结
传递函数并非遥不可及的数学理论,而是嵌入式信号处理的实用工具。核心逻辑是"传递函数→差分方程→C语言实现",我们无需深钻复杂推导,只需掌握常见滤波器的转化方法和通用实现框架,就能解决大部分噪声抑制问题。本文搭建的通用滤波框架,支持一阶低通、高通和二阶陷波,可直接移植到实际项目中。
如果这篇内容帮你打通了"数学理论→编程实现"的任督二脉,别忘了点赞、收藏 备用!后续还会更新卡尔曼滤波、滑动平均滤波等算法的实战教程,关注我,获取更多嵌入式信号处理干货!如果在实际项目中遇到滤波参数调整、算法优化的问题,欢迎在评论区留言讨论,一起攻克技术难点~