ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Z变换核心指南:收敛域、零极点图与数字滤波器稳定性

Z变换核心指南:收敛域、零极点图与数字滤波器稳定性 Z变换这个词第一次出现在课本里的时候大多数人的反应是又来一个变换。前面刚啃完傅里叶级数、傅里叶变换、拉普拉斯变换现在又冒出来一个带 z 的求和式看着像幂级数又像拉普拉斯变换的亲戚。但真正做过离散系统设计、写过数字滤波器代码的人心里都清楚Z变换不是多学一个变换它是把离散时间问题彻底代数化的那套工具是把差分方程变成多项式除法的桥梁也是判断一个系统到底稳不稳、能不能跑、跑起来会不会发散的最短路径。这篇梳理不是教材的复刻而是把我自己从学生阶段到工程阶段反复折腾 Z变换 的经验摊开讲一遍它为什么长这样、收敛域为什么比表达式本身还重要、逆变换三种解法在什么场景下该选哪个、零极点图和稳定性之间的那根线到底怎么连。内容适合三类人看——正在学信号与系统的在校生、准备考试需要快速建立框架的人、以及已经工作但需要回头补一遍离散系统理论的嵌入式或算法工程师。零基础也能看因为我会把每个为什么都补上有基础的可以直接跳到第 4、5、6 章那几章是实操密度最高的部分。1. 从连续到离散Z变换到底解决什么问题1.1 拉普拉斯变换在离散域的水土不服连续时间系统有一套非常成熟的工具链微分方程描述系统拉普拉斯变换把微分方程变成代数方程系统函数 H(s) 的极点位置直接告诉你稳不稳。这套逻辑之所以成立是因为 e^{st} 是线性时不变系统的特征函数微分算子作用在它上面只是乘一个 s。离散时间系统也有对应的特征函数就是 z^n因为移位算子作用在 z^n 上只是乘一个 z^{-1} 或者 z。问题在于连续域的拉普拉斯变换是积分离散域的对应物自然就是求和而求和的对象 x[n]z^{-n} 恰好是一个幂级数。所以 Z变换 的诞生不是谁拍脑袋发明的它是把指数特征函数这套思路搬到离散域的必然结果。你如果接受拉普拉斯变换是给信号乘一个衰减因子再求傅里叶变换那 Z变换 就是给离散序列乘一个实指数权重再求离散时间傅里叶变换DTFT。理解了这一层后面所有性质、所有收敛域的讨论都会顺很多因为它们的根源全部在幂级数的收敛性上。1.2 定义式里的每一个符号都有来历双边 Z变换 的定义是 X(z) Σ_{n-∞}^{∞} x[n] z^{-n}其中 z 是复变量。单边 Z变换 把求和下限改成 0写成 Σ_{n0}^{∞} x[n] z^{-n}。初学者容易把这两个混用觉得差别只是少了几项实际上单边变换默认序列在 n0 时为 0也就是只处理因果序列这在解差分方程时是有意为之的选择不是偷懒。z^{-n} 这个负指数也不是随便写的。你可以把 z 看成 e^{sT}T 是采样周期那么 z^{-n} e^{-snT}跟拉普拉斯变换里的 e^{-st} 在采样点上的取值完全对得上。换句话说Z变换 就是拉普拉斯变换在采样序列上的离散版本只不过它把整个周期带折叠了。这个映射关系 z e^{sT} 是后面理解 s 平面到 z 平面映射、理解混叠、理解频率响应周期性的一把钥匙。1.3 什么样的工程问题真正需要它判断一个差分方程描述的滤波器会不会发散、求解带初始条件的差分方程、设计一个指定零极点位置的数字滤波器、把连续域的控制器离散化后再分析——这些全是 Z变换 的主场。举个具体的你写了一段 IIR 滤波器的系数跑起来发现输出越来越大最后爆成 Inf这时候最有效的排查手段不是盯着代码看而是把系数代进 H(z) B(z)/A(z)算出所有极点看有没有极点的模大于或等于 1。模大于 1 一定发散等于 1 会出现等幅振荡或者临界不稳。反过来如果你只会写代码不会看极点那排查这类问题基本靠猜。我在带新人时反复强调的一点是Z变换 的价值不在考试里而在于它给了你一个不用跑仿真就能预判系统行为的分析通道。这个能力在嵌入式场景下尤其值钱因为很多设备烧一次固件成本很高能在纸面上算清楚的东西就别拖到板子上去试。2. 收敛域Z变换最容易被忽略的核心2.1 同一个表达式为什么对应不同的序列这是 Z变换 里最反直觉、也最容易在考试和工程里翻车的一点X(z) z/(z-0.5) 这个表达式可以对应右边序列 0.5^n u[n]也可以对应左边序列 -0.5^n u[-n-1]具体是哪一个完全取决于收敛域。如果你的答案里只写了表达式没写收敛域那这个答案在数学上是不完整的在工程上就是错的。因为右边序列是因果的、稳定可能成立左边序列在 n→-∞ 时不收敛两者的系统行为天差地别。我的习惯是每次写出 X(z) 之后立刻在旁边标一行 ROC就像写身份证号一样不加这一行就不往下走。这个习惯救过我好几次特别是在做滤波器级联的时候两个子系统各自收敛域的交集不一定包含单位圆一旦交集不含单位圆即使每个子系统单独看都是稳定的级联之后也可能不稳。这个问题在只盯表达式不盯 ROC 的人手里几乎必然会踩。2.2 有限长序列、右边序列、左边序列的收敛域对照把常见序列类型和收敛域对应关系背下来比推导一遍更高效。下面这张表是我自己整理的速查版本覆盖了绝大多数场景。序列类型时域特征收敛域形状典型例子有限长序列只在有限区间非零整个 z 平面可能挖掉 z0 或 z∞δ[n]、矩形窗右边序列n N1 时为零某个圆的外部z左边序列n N2 时为零某个圆的内部z双边序列两端都无限圆环R1 z这张表里最需要注意的是有限长序列那一行。很多人以为有限长序列的收敛域永远是全平面其实要分情况因果有限长序列在 z0 处可能有问题非因果有限长序列在 z∞ 处可能有问题。比如 δ[n-1] 的变换是 z^{-1}它在 z0 处有极点收敛域是 z≠0。这种细节在纯计算题里分值不高但在推导系统函数、做级联分析时会直接影响你能不能约分。2.3 收敛域和序列因果性、稳定性的三角关系收敛域真正的作用是把三个概念串起来因果性、稳定性、极点位置。这三者的关系可以总结成一句话——收敛域在单位圆外的系统是因果的收敛域包含单位圆的系统是稳定的极点在单位圆内的因果系统是稳定的。注意最后一句多了因果这个前提如果去掉因果极点在单位圆内并不能推出稳定因为你可能取的是那个把单位圆排除在外的收敛域。实际工程里我们关心的几乎总是因果稳定系统因为实时处理要求当前输出只能依赖当前和过去的输入同时输出不能发散。所以在设计滤波器时我通常直接按所有极点模小于 1来约束这是最省心的路径。但如果你在做离线处理比如音频后处理、图像滤波非因果系统是可以用的这时候就真的要看收敛域是否包含单位圆而不是简单地看极点位置。这个区分在做零相位滤波的时候特别重要因为零相位滤波本质上就是非因果的。注意算出一个 H(z) 之后先找极点再判因果性最后才判稳定性。顺序反了容易得出错误结论。3. 性质梳理怎么用性质把复杂问题化简3.1 线性、时移与 z 域尺度变换线性是最基本的a·x1[n] b·x2[n] 的变换是 a·X1(z) b·X2(z)收敛域至少是两者交集如果存在零点极点相消交集可能扩大。这条性质在求复杂序列变换时是主力工具比如一个序列是多个已知序列的线性组合直接拆开查表就行不用从头推求和。时移性质 x[n-n0] ↔ z^{-n0}X(z) 是解差分方程的命脉。它的收敛域基本不变但在 z0 或者 z∞ 处可能因为引入了 z 的负幂或者正幂而新增或去掉极点这个细节在做严谨推导时不能忽略。z 域尺度变换对应时域的指数加权a^n x[n] ↔ X(z/a)收敛域从 R 变成 |a|R这条性质在分析带衰减因子的振荡序列时特别顺手比如阻尼正弦的变换就是用这条性质从纯正弦推出来的。3.2 卷积定理与 Z 域微分卷积定理 x[n] * h[n] ↔ X(z)H(z) 是 Z变换 在工程上最有价值的一条性质。它把时域的卷积运算变成了 z 域的乘法意味着级联系统的总系统函数就是各个子系统函数的乘积总极点就是各个子系统极点的并集。这条性质直接推导出了级联系统的稳定性取决于所有子系统也解释了为什么级联时会因为极点重合而出现双重极点从而让频率响应在某个频点附近变得很陡。Z 域微分性质 n·x[n] ↔ -z·dX(z)/dz 处理的是时域乘以 n 的情况。它的推导思路是把定义式对 z 求导再整理得到一个含 n 的求和式。这条性质在求斜坡序列、n 倍指数序列的变换时能省很多力气比如 n·a^n u[n] 的变换可以直接从 a^n u[n] 的变换求导得到结果是 a·z^{-1}/(1-a·z^{-1})^2不用自己重新做级数求和。3.3 初值定理和终值定理的适用边界初值定理说对于因果序列x[0] lim_{z→∞} X(z)。这个用法很直接如果你想快速验证一个变换结果对不对把 z 趋向无穷代进去看结果是不是等于序列的第一个值对不上就说明中间算错了。我做完每一道逆变换题都会用这个做一次自检十次里有三次能抓出符号错误或者系数错误。终值定理是 lim_{n→∞} x[n] lim_{z→1}(z-1)X(z)但它有严格的使用前提(1-z^{-1})X(z) 的所有极点必须都在单位圆内。很多人算稳态误差时直接套这个公式结果序列明明在振荡公式给出的却是一个有限值这就是因为前提不满足。判断方法很简单先看 X(z) 的极点如果存在模等于 1 的极点除了 z1 这个允许的极点之外终值定理就失效了这时候只能老老实实做逆变换看渐近行为。4. 逆Z变换的三种解法与实战选择4.1 部分分式展开工程上用得最多的一种部分分式法的核心思路是把有理函数拆成若干个一阶或二阶分式之和每一项都能查表反变换。这里有一个关键技巧必须强调不要直接对 X(z) 做部分分式而是先对 X(z)/z 做展开完再乘以 z。原因是 X(z) 通常是 z^{-1} 的有理式直接展开得到的分式形式不是标准查表形式而先除 z 再乘回来每一项会变成 A_k·z/(z-p_k) A_k/(1-p_k z^{-1})这个形式对应的逆变换就是 A_k·p_k^n u[n]干净利落。我见过太多人在这里翻车不是因为不会拆项而是因为拆完的形式和表里的对不上然后开始怀疑表抄错了。其实表没错是展开顺序错了。这个技巧值得单独记一笔因为它在考试和工程里都是高频操作。4.2 长除法需要前几项数值时的快捷路径长除法幂级数展开适合两种场景一是你只需要序列的前几项比如做验证二是 X(z) 的分母次数很高部分分式拆起来很痛苦。做法是把 X(z) 按 z 的降幂排列做多项式除法得到的商按 z^{-1} 的幂次排列系数就是序列的各个值。对因果序列商就是 z^{-1} 的幂级数对非因果序列要按 z 的正幂方向展开这时候需要把 X(z) 写成 z 的多项式再做除法。长除法的局限在于它给不出闭式解只能给你一串系数。所以它更适合做数值验证而不是理论推导。我的习惯是用部分分式求出闭式解再用长除法算前五项对一遍两者一致才敢下结论。这个交叉验证的成本很低但能挡住绝大多数计算失误。4.3 留数法理论推导和考试中的通用武器留数法基于逆变换的围线积分定义 x[n] (1/2πj)∮ X(z)z^{n-1}dz计算方式是求被积函数在围线内所有极点处的留数之和。对于因果序列围线取在收敛域内并包围所有极点x[n] Σ Res[X(z)z^{n-1}, p_k]。对于非因果序列围线取法不同需要考虑无穷远点的留数这部分是留数法最容易出错的地方。留数法的优势是通用性强遇到部分分式拆不动的、遇到多重极点的、遇到需要推导一般项公式的它都能处理。缺点是计算量大尤其是多重极点需要求导。实际操作中我一般用留数法处理二阶以内的极点超过二阶就转去用部分分式配合软件计算。这不是偷懒而是投入产出比的理性选择。4.4 三种方法的选用建议与交叉验证方法适用场景优势风险点部分分式有理函数、需要闭式解快、结果形式标准拆项顺序、重根处理长除法只需前几项、高次分母无需因式分解无闭式解、方向易错留数法理论推导、多重极点通用、适合复杂情形计算量大、围线选择我的实操原则是先看能不能查表能查表就不算不能查表就用部分分式同时用长除法验证前几项只有在部分分式明显复杂的情况下才上留数法。三种方法不是互相替代的关系而是互相验证的关系交叉验证的习惯能把你出错的概率压到很低。5. 从差分方程到系统函数Z变换的工程主战场5.1 差分方程求解的标准流程给定一个线性常系数差分方程比如 y[n] - 0.5y[n-1] x[n]求解流程是这样的第一对等式两边做单边 Z变换利用时移性质把 y[n-1] 变成 z^{-1}Y(z) y[-1]注意初始条件要带进来第二把 Y(z) 相关的项移到一边整理成 Y(z) H(z)X(z) 初始条件项第三分别求零状态响应初始条件为 0和零输入响应输入为 0两者相加就是全响应。这个流程里最容易出问题的是时移性质在单边变换下的形式。因为单边变换的求和从 0 开始x[n-1] 的变换不是简单乘 z^{-1}而是 z^{-1}X(z) x[-1]。这个 x[-1] 就是初始条件漏掉它会导致零输入响应整段丢失。我在做题时就吃过这个亏答案只对了零状态那部分零输入部分全错扣分扣得很冤。所以记住一句话单边变换的时移必然带初始条件双边变换的时移不带。5.2 零极点分布怎么读出一个系统H(z) B(z)/A(z)分子多项式的根是零点分母多项式的根是极点。极点决定系统的自然响应形式零点决定各个频率成分的加权。极点在单位圆内系统稳定极点在单位圆上临界振荡极点在单位圆外发散。极点越靠近单位圆对应的频率响应峰值越高时域衰减越慢。零点的作用相对隐蔽但同样重要。单位圆上的零点会让对应频率的响应完全为零这就是陷波滤波器的设计原理——把零点放在干扰频率对应的单位圆位置上那个频率就被彻底吃掉了。我自己做 50Hz 工频干扰抑制时就是直接把一对共轭零点放在对应角度上效果立竿见影。但要注意零点在单位圆上意味着分子多项式有一个不稳定的逆如果你后面要做反滤波这个零点会变成不稳定极点这是一个容易被忽略的连环坑。5.3 频率响应与离散系统设计频率响应就是把 z 取成 e^{jω} 代入 H(z)条件是单位圆必须在收敛域内。H(e^{jω}) 是周期为 2π 的复函数模是幅频响应相角是相频响应。因为 z e^{jω} 在单位圆上跑一圈对应的物理意义是采样频率归一化后的 0 到 fs/2 区间。设计滤波器本质上就是在 z 平面上摆零极点。低通滤波器把极点放低频区、零点放高频区高通反过来带通用两对共轭极点夹住通带两对共轭零点压在阻带。摆完之后用频率响应验证看通带是否平坦、阻带衰减够不够、过渡带陡不陡。这个过程听起来玄其实用软件画一张零极点图加一张幅频曲线迭代几轮就能出结果。后面第 6 章我会给一段可以直接跑的代码把整个流程串起来。6. 工程实操用 Python 把Z变换算一遍6.1 符号推导让计算机替你拆项手工拆部分分式容易出错用 sympy 做符号推导是个稳妥选择。下面这段代码求 a^n u[n] 的 Z变换并做化简。import sympy as sp n, z, a sp.symbols(n z a) # 注意sympy 的 summation 对收敛条件敏感可能返回 Piecewise X sp.summation(a**n * z**(-n), (n, 0, sp.oo)) X_simple sp.simplify(X) print(X_simple) # 整理后应为 z/(z - a) print(sp.apart(X_simple / z) * z) # 部分分式形式这里有两个实操细节。第一sympy 对无限求和会自动加收敛条件输出的可能是一个 Piecewise 对象你需要用 simplify 或者手动取第一个分支。第二部分分式一定要先除以 z 再乘回来代码里那一行sp.apart(X_simple / z) * z就是在演示这个技巧跟第 4 章讲的手工做法完全一致。跑完之后你可以把结果跟课本上的标准变换对对照一般来说就是 z/(z-a)验证一下。如果输出的形式是 -1/(a/z - 1)/z 之类的用sp.simplify或者sp.cancel再整理一次就行。6.2 数值验证与零极点图绘制理论算完之后用 scipy 做数值验证。下面这段代码给定 H(z) 的系数算出零极点并画图。import numpy as np import matplotlib.pyplot as plt import scipy.signal as sig # H(z) 1 / (1 - 0.5 z^-1)对应 a [1, -0.5] b [1.0] a [1.0, -0.5] zeros, poles, gain sig.tf2zpk(b, a) print(零极点:, zeros, poles, 增益:, gain) theta np.linspace(0, 2 * np.pi, 400) plt.figure(figsize(5, 5)) plt.plot(np.cos(theta), np.sin(theta), k--, lw1, label单位圆) plt.plot(zeros.real, zeros.imag, o, mfcnone, ms10, label零点) plt.plot(poles.real, poles.imag, x, ms10, mew2, label极点) plt.axhline(0, colorgray, lw0.5) plt.axvline(0, colorgray, lw0.5) plt.gca().set_aspect(equal) plt.grid(alpha0.3) plt.legend() plt.show() w, h sig.freqz(b, a, worN1024) plt.plot(w / np.pi, 20 * np.log10(np.abs(h))) plt.xlabel(归一化频率 (×π rad/sample)) plt.ylabel(幅度 (dB)) plt.grid(alpha0.3) plt.show()极点落在 z0.5在单位圆内系统稳定。幅频曲线是典型的一阶低通形状低频增益接近 0dB高频逐渐衰减。你可以把 a 改成 [1, -1.05]也就是极点移到单位圆外再跑一次会看到幅频曲线在高频处翘起来同时用 lfilter 跑一遍时域输出会直接发散。这种改一个数看行为怎么变的实验比看十页推导更能建立直觉。6.3 时域仿真验证与交叉检查最后一步是时域验证。给定输入信号用 lfilter 跑一遍跟理论上的自然响应对照。import numpy as np import scipy.signal as sig b [1.0] a [1.0, -0.5] x np.zeros(20) x[0] 1.0 # 单位脉冲输入 y sig.lfilter(b, a, x) print(y[:8]) # 应接近 1, 0.5, 0.25, 0.125 ... # 理论值对照 n np.arange(20) y_ref 0.5 ** n print(np.allclose(y, y_ref, atol1e-10))如果输出是 1, 0.5, 0.25, 0.125跟 0.5^n 完全对上说明整个变换链条从符号推导到数值验证都通了。这个三件套——符号推导、零极点图、时域仿真——是我做任何离散系统分析的固定流程缺一步都会让我不放心。尤其是时域仿真那一步它是最贴近硬件的验证因为真实设备上跑的就是这个差分方程。7. 常见坑与排查清单7.1 收敛域相关的四类典型错误第一类是只写表达式不写收敛域导致同一表达式对应两个序列时无法区分。第二类是误以为收敛域是所有式子共有的那个区域实际上每个分式的收敛域不同总和是交集。第三类是做级联时忘了取交集把两个子系统各自稳定错误地推导为级联稳定。第四类是忽略了有限长序列在 z0 或 z∞ 处的特殊性导致约分时把有效的极点约掉了。这四类错误的共同根源是把 Z变换 当成纯代数运算忘记了它本质是一个幂级数幂级数的一切结论都依赖收敛半径。我的排查方法是拿到任何 X(z)先问三个问题——有几个极点、极点的模是多少、哪个区域是收敛域。这三个问题答不上来后面的计算都是空中楼阁。7.2 部分分式与留数计算的三个高频错误第一个错误是重根处理不当。遇到二重极点部分分式的形式应该是 A/(z-p) B/(z-p)^2少写一项会导致结果完全错。计算 B 时用 (z-p)^2 乘以原式取 zp 的极限计算 A 时求导这两步很容易在求导那次算错符号。第二个错误是部分分式展开对象搞错前面强调过两次先除 z 再乘 z这个顺序不能变。第三个错误是留数法围线方向搞反导致结果差一个符号。留数法里围线是逆时针方向取的是被积函数在围线内的极点如果收敛域方向判断错了围线会取到外侧得到的是完全不同的序列。这三个错误我全犯过现在每次算完都会用长除法验证前几项基本上能在两分钟内发现异常。7.3 初值定理和终值定理的误用清单初值定理只对因果序列成立对非因果序列用它得到的不是 x[0]。终值定理要求 (1-z^{-1})X(z) 的极点全在单位圆内如果有单位圆上的极点或者有单位圆外的极点结论都不成立。这两个定理的误用率非常高因为它们看起来太方便了从 X(z) 一眼就能读出时域的初值和终值这种便利性让人容易忽略前提条件。我的做法是把前提条件写在题目旁边算之前先检查。终值定理检查这一条我通常用代码辅助把 X(z) 的极点和零点列出来逐个看模是否小于 1只有全部满足才用定理否则老老实实做逆变换然后取极限。多花五分钟避免一个错误的稳态值进入后续设计这笔账是划算的。7.4 问题排查速查表症状可能原因排查动作逆变换结果与查表对不上未先除 z 做部分分式改成对 X(z)/z 展开再乘 z级联系统莫名发散收敛域交集不含单位圆逐个算 ROC 并求交集时域仿真很快爆成 Inf有极点模大于等于 1用 tf2zpk 列出所有极点稳态值算出来是有限值但输出在振荡终值定理前提不满足检查是否有单位圆极点初值对不上序列非因果或变换算错先确认因果性再用长除法验证前几项频率响应峰值异常高极点过于靠近单位圆计算极点到单位圆的距离适当内缩陷波点位置不在目标频率零点角度与频率换算错误用 ω 2πf/fs 反查零点角度这张表里的每一条都是我自己或者同事真实踩过的不是从书上抄的。特别是第二行那条级联系统的收敛域问题在概念题里看起来简单但落到代码上非常隐蔽因为每个子系统单独跑都没问题只有连起来跑才出问题如果不做 ROC 分析很可能要花一整天去定位。最后分享一个我自己的习惯每做完一个离散系统的分析我都会画三张图——零极点图、幅频响应、单位脉冲响应。三张图放在一起看零极点位置和时域衰减速度的对应关系、和频域峰值的对应关系会变得非常直观。做了几十次之后你看一个系统函数脑子里基本就能浮现出那三张图。这个直觉的建立过程没有捷径但一旦建立起来后面做滤波器设计、做稳定性判断、做参数整定都会快得离谱。如果这套内容你还想往深处走下一步可以去看多速率信号处理里的抽取和插值那部分本质上是在 z 域做尺度变换用现在这套框架去理解会顺畅很多。
返回列表