ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

数字信号处理核心:z变换与DTFT的工程实战解析

2026/9/10 9:01:34 拓冰建站 浏览量
数字信号处理核心:z变换与DTFT的工程实战解析 学数字信号处理最怕什么我当年最怕的就是第2章z变换和DTFT这一页翻过去后面全是公式每个字母都认识放在一起就不知道在算什么。后来在工程里做了几年再回头看这一章才发现它其实是整本书的枢纽前面连着信号与系统后面接着滤波器设计、频谱分析、通信系统。z变换解决的是“这个离散系统到底稳不稳定、怎么从差分方程得到系统函数”DTFT解决的是“这个序列的频谱长什么样”。这篇文章不打算把教材再抄一遍而是按我自己的理解把这一章重新拆给你看。我会先讲清楚z变换、拉普拉斯变换、傅里叶变换的关系再深入收敛域、逆变换这些核心细节然后直接上Python代码从DTFT计算到频率响应再到调制信号包络谱提取最后整理几个特别容易踩的坑。无论你是正在学这门课的学生还是工作中突然要补这块底的工程师按这个顺序走一遍会比孤立地啃定义有用得多。1. 这章到底在讲什么z变换和DTFT各自解决什么问题1.1 从连续到离散傅里叶变换、拉普拉斯变换和z变换的家族关系先把坐标系摆正。连续时间信号有一套分析工具傅里叶变换看频率成分拉普拉斯变换看复平面上的极点分布。到了离散时间原来的积分变成求和原来的变量s变成了z。三个变换不是三座孤岛而是同一条“复频域分析”思路在不同信号形态下的变形。连续时间信号的傅里叶变换是X(jΩ) ∫ x(t) e^{-jΩt} dt拉普拉斯变换把实频域推广到复频域X(s) ∫ x(t) e^{-st} dt其中 s σ jΩ对离散序列 x[n]最直接的思路是把连续信号采样后得到的序列用求和代替积分于是得到z变换X(z) Σ_{n-∞}^{∞} x[n] z^{-n}这个概念桥最关键的地方在于 z e^{sT}T 是采样周期。也就是说s域里的系统一旦被采样它的一切特性都会映射到z平面上。s平面的虚轴σ0映射到z平面的单位圆s平面左半平面对应单位圆内部右半平面对应单位圆外部。这个几何映射比背十遍定义都有用。为什么这条映射关系这么重要因为DTFT的定义是 X(e^{jω}) Σ x[n] e^{-jωn}它其实就是z变换在 z e^{jω} 条件下的特例而 z e^{jω} 正好落在单位圆上。所以分析系统稳定性时只要看单位圆在不在收敛域里分析频谱时只要在单位圆上取值。后面很多结论都是从这一句话推导出来的。三个变换放到一起总结就是这样变换类型适用信号变量逆变换工具主要用途傅里叶变换连续时间信号Ω实数积分频谱分析拉普拉斯变换连续时间系统s σ jΩ围线积分系统稳定性、暂态分析z变换离散时间序列z复数围线积分、部分分式离散系统建模、稳定性判断DTFT离散时间序列ω实数积分频谱分析表格里DTFT和z变换的“变量”一栏不同这提醒我们DTFT是z变换在单位圆上的取值但z变换能提供更多复平面信息。很多教材把DTFT放在z变换之后讲就是这个逻辑——先给你整个地图再告诉你“沿着单位圆走一圈看到的就是频谱”。1.2 z变换和DTFT的分工一个管系统一个管频谱经常有学弟问我既然DTFT已经能画出频谱曲线为什么还要搞一个z变换出来这不只是理论上的炫技而是两件事的深度不一样。DTFT描述的是信号或者系统在频率轴上的表现它是一个直观的横切面而z变换把整个复平面上零极点分布、收敛域边界都摊在你面前让你能判断系统内部结构是否稳定、因果、可实现。用一个不太严谨的类比DTFT像是站在楼外面看这栋楼有几层窗户z变换则是拿到建筑图纸能看到承重墙和地基在哪儿。滤波器的零点极点一旦放错位置时域波形再好看也会在关键频率上出问题光看频谱曲线是不够的。从差分方程写系统函数再在单位圆上取模得频响是这一章最核心的操作。任意一个LTI离散系统都可以写成H(z) Y(z) / X(z) (Σ b_k z^{-k}) / (1 Σ a_k z^{-k})然后频率响应 H(e^{jω}) 就是 H(z) 在 z e^{jω} 处的值。严格地说只有在收敛域包含单位圆时H(e^{jω}) 才有意义这也就是稳定系统的必然要求。这个“先建模、后取频响”的顺序决定了我们学习这一章时要时刻保持两套视角一套是复平面视角零极点、收敛域一套是频率轴视角幅频、相频。2. 核心定义、性质和收敛域公式背后到底在说什么2.1 定义里z^{-1}的物理意义以及单边/双边的选择z变换定义式里的 z^{-n}直接对应一个单位延时。在数字系统里寄存器每拍把数据往后挪一位就是乘一个 z^{-1}。所以差分方程里 y[n-1] 一项到z域就变成 z^{-1}Y(z)。把整个差分方程做z变换你就能得到一个代数方程这就是系统函数快速推导的本质。我见过很多初学者把 z^{-n} 当成普通的幂忽略它的延时含义结果在写系统函数时把分子分母全弄反。举个例子一阶差分方程y[n] a y[n-1] x[n]两边做z变换得到 Y(z) a z^{-1} Y(z) X(z)整理后 H(z) 1 / (1 - a z^{-1})极点在 z a。如果 a 的绝对值大于1极点就落在单位圆外系统不稳定。这个过程只要把 z^{-1} 理解成“延迟一拍”每一步都顺理成章。单边z变换与双边z变换的最大差别体现在处理因果序列和系统初始条件上。单边变换只考虑 n≥0 的部分所以可以用来解差分方程并带入初始状态双边变换则天然适合描述各种无限长序列但要特别小心收敛域。教材里讲z变换的性质比如移位、卷积、初值定理、终值定理都是在单边/双边不同语境下有微妙差别的做题前先明确题目到底用的是哪一种这一点比背一百条性质都重要。常用因果序列的z变换对可以直接查表序列 x[n]X(z)ROCδ[n]1整个z平面u[n]1 / (1 - z^{-1})a^n u[n]1 / (1 - a z^{-1})n a^n u[n]a z^{-1} / (1 - a z^{-1})^2cos(ω0 n) u[n](1 - cos(ω0) z^{-1}) / (1 - 2 cos(ω0) z^{-1} z^{-2})sin(ω0 n) u[n]sin(ω0) z^{-1} / (1 - 2 cos(ω0) z^{-1} z^{-2})这张表我建议自己手推一遍尤其 cos 和 sin 的表达式是用欧拉公式把指数序列组合出来的推一次就能记住。2.2 收敛域ROC为什么是z变换的灵魂很多同学背下了 a^n u[n] 对应 X(z) 1 / (1 - a z^{-1})但一遇到“X(z) 1 / (1 - a z^{-1}) 对应的 x[n] 是什么”就懵了。因为同一个表达式可以对应两个完全不同的序列ROC 为 |z| |a| 时对应右边序列 a^n u[n]ROC 为 |z| |a| 时对应左边序列 -a^n u[-n-1]能区分它们的唯一信息就是 ROC。ROC 不是可有可无的附加条件它本身就是答案的一部分。很多考研题就是故意不告诉你收敛域让你意识到同一个代数式可能对应多个序列必须靠收敛域才能唯一确定。ROC的基本规律也不难记有限长序列ROC是整个z平面某些点除外右边序列ROC在某个半径之外左边序列ROC在某个半径之内双边序列ROC是环状区域判断一个LTI系统是否稳定直接看ROC是否包含单位圆判断是否因果看ROC是否在某圆外部并且包括无穷远点。比如 H(z) 1 / (1 - 0.5 z^{-1})ROC 如果写成 |z| 0.5系统因果稳定如果写成 |z| 0.5系统反因果且不稳定。同样的代数式一个题目可以出现两个完全相反的结论。我上课时总喜欢再追问一句为什么稳定条件不是“所有极点都在单位圆内”这个说法其实只在因果系统里成立。若系统非因果即使所有极点都在单位圆内ROC也可能不包含单位圆。真正普适的判据始终是“ROC包含单位圆”。想理解这一点就回到“DTFT是z变换在单位圆上的取值”这句话单位圆上有取值频响才有定义系统才BIBO稳定。2.3 逆z变换的三种常用路径长除法、部分分式与留数法逆z变换的围线积分公式考试很少直接积实际都是转成查表问题。三种主流方法各有适用场景但多数题目都能用部分分式解决。幂级数法长除法适合只需要序列前几项的情况。把 X(z) 按 z^{-1} 升幂排列做除法商的系数就是 x[n] 的前几项。如果ROC是 |z| 某个半径则要按 z 的正幂展开因为此时对应的是左边序列。这个方法最大的优点是快缺点是一般只能得到有限项看不到闭式表达式。部分分式法是默认路线。步骤是先将 X(z) 除以 z做部分分式再乘回 z让每一项都能对应到标准 z/(z-p) 的形式最后用查表得到 a^n u[n] 或相关形式。这里最容易错的是忘记“先除以z再乘回z”直接照搬连续域的部分分式形式结果每一项都差一个移位。留数法适合系统阶数较高、极点有重根的情况。计算围线内所有极点的留数和注意ROC方向决定围线方向。工程上这些求逆变换的事情已经很少手算了Python 的 scipy.signal.residuez 可以直接做部分分式展开得到留数 r、极点 p 和直接项 k查表基本一步到位。但考试和面试还是经常考手算建议还是把部分分式练熟。3. 实操用Python把DTFT、频谱和包络波形一步步算出来3.1 手写DTFT函数画出任意序列的频谱理解DTFT最好的方式是自己写一个数值计算函数。DTFT的公式是 X(e^{jω}) Σ x[n] e^{-jωn}ω 是连续变量所以我们在数值计算时对它做稠密采样在每个采样频率点上做一次加权求和。import numpy as np import matplotlib.pyplot as plt def dtft(x, w): 计算序列x的DTFTw是一组频率点弧度/采样 n np.arange(len(x)) X np.zeros_like(w, dtypecomplex) for i, ww in enumerate(w): X[i] np.sum(x * np.exp(-1j * ww * n)) return X # 一个简单序列两个正弦叠加 N 64 n np.arange(N) x np.cos(0.2 * np.pi * n) 0.5 * np.cos(0.45 * np.pi * n) w np.linspace(0, 2 * np.pi, 1024, endpointFalse) X dtft(x, w) plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.plot(w / np.pi, np.abs(X)) plt.title(DTFT magnitude) plt.xlabel(frequency (x pi rad/sample)) plt.ylabel(|X(e^{jw})|) plt.grid(True) plt.subplot(1, 2, 2) plt.plot(w / np.pi, np.angle(X)) plt.title(DTFT phase) plt.xlabel(frequency (x pi rad/sample)) plt.grid(True) plt.tight_layout() plt.show()这段代码输出的频谱会在 ω 0.2π 和 0.45π 附近出现两个明显的峰。这里有一个关键概念DTFT 输出的是连续频率函数而FFT输出的是频域采样点。这个函数对任意长度序列都能直接用代价是循环计算速度慢实际工程中一般用 FFT 来获取频域采样值。3.2 从系统函数到频率响应一个IIR低通滤波器的完整流程系统函数写出来后最高频的操作用 scipy.signal.freqz 计算频率响应。下面用三阶巴特沃斯低通滤波器演示截止频率设为 0.2π rad/sample这段代码可以直接跑。import numpy as np import matplotlib.pyplot as plt from scipy import signal b, a signal.butter(3, 0.2, low) w, h signal.freqz(b, a, worN2048) plt.figure(figsize(10, 6)) plt.subplot(2, 1, 1) plt.plot(w / np.pi, 20 * np.log10(np.abs(h) 1e-12)) plt.title(Butterworth lowpass frequency response) plt.xlabel(Normalized frequency (x pi rad/sample)) plt.ylabel(Magnitude (dB)) plt.grid(True) plt.subplot(2, 1, 2) plt.plot(w / np.pi, np.angle(h)) plt.xlabel(Normalized frequency (x pi rad/sample)) plt.ylabel(Phase (rad)) plt.grid(True) plt.tight_layout() plt.show() # 用系统函数对信号滤波 t np.arange(256) x np.sin(0.05 * np.pi * t) 0.5 * np.sin(0.7 * np.pi * t) y signal.lfilter(b, a, x) plt.figure(figsize(10, 3)) plt.plot(t, x, labelinput) plt.plot(t, y, labelfiltered) plt.legend() plt.show()运行后能看到幅度响应从 0.2π 开始明显下降0.7π 附近的高频分量被大幅衰减。这里提示一点freqz 函数默认处理的是 z^{-1} 升幂排列的系数b[0] 是分子常数项和差分方程的 y[n] 对应别因为顺序问题把滤波器系数写反。3.3 结合包络谱用希尔伯特变换提取调制信号的包络波形这一章节的热词里常出现包络波形、包络谱、希尔伯特变换。希尔伯特变换在数字信号处理里最经典的应用就是提取调制信号的包络。原理是这样的对一个实信号 x[n] 构造解析信号 x_a[n] x[n] j * H{x[n]}其中 H{x[n]} 是希尔伯特变换后的序列解析信号的模 |x_a[n]| 就是瞬时包络。from scipy.signal import hilbert fs 1000 t np.arange(0, 1, 1 / fs) fm 5 fc 50 m 1 0.8 * np.sin(2 * np.pi * fm * t) x m * np.cos(2 * np.pi * fc * t) analytic hilbert(x) envelope np.abs(analytic) plt.figure(figsize(10, 4)) plt.plot(t, x, alpha0.7, labelAM signal) plt.plot(t, envelope, r--, labelenvelope) plt.legend() plt.xlabel(time (s)) plt.ylabel(amplitude) plt.title(AM demodulation with Hilbert transform) plt.show()这段代码会看到红色虚线包络正好套在调制信号上下边沿上。对包络再做 FFT得到的就是包络谱可以观察调制频率分量。这个流程是通信解调、机械故障诊断里提取包络波形的标准做法。理解它可以联系前面DTFT的知识x[n] 乘上载波相当于频谱搬移希尔伯特变换把双边谱变成单边谱取模就是基带包络。4. 常见问题与避坑指南4.1 收敛域和稳定性判断题里的高频失分点这一章的题目陷阱其实很固定先看常见错误表常见错误错误表现正确做法只写X(z)不写ROC无法唯一确定序列任何z变换结果都要标注ROC把单位圆当成s平面的虚轴误判稳定条件稳定条件是ROC包含单位圆部分分式不除以z逆变换结果整体移位先X(z)/z后查表用单边z变换判断双边系统输出缺了一半先用双边定义确定收敛域我记得有个很典型的题目H(z) 1 / (1 - 1.5 z^{-1})收敛域给的是 |z| 1.5。这时结论是“系统因果但不稳定”因为极点 z 1.5 在单位圆外。收敛域 |z| 1.5 的情况下系统虽然稳定但不是因果的。很多同学一看到“稳定”就默认因果一看到“因果”就默认稳定这两个属性必须分开判断。4.2 numpy/scipy计算时的三个“坑”第一个坑是 freqz 系数顺序。scipy.signal 里的 b 和 a 都是按 z^{-1} 升幂排列的b[0] 是分子常数项a[0] 通常归一化为1。如果拿系统的零极点自己构造多项式一定要记得多项式系数顺序和 freqz 的输入顺序完全一致否则结果会翻转。第二个坑是 FFT 和 DTFT 的关系。FFT 只是对 DTFT 在频域上做等间隔采样。补零可以让频谱曲线看起来更平滑但不能提高频率分辨率真正的分辨率由参与计算的数据长度决定。这个坑在写频谱分析代码时特别常见尤其在包络谱分析里为了“让谱线更细”而拼命补零是没有意义的。第三个坑是 scipy.signal.residuez 的返回值。它返回的是留数 r、极点 p 和直接项 k对应的是一阶部分分式展开。用查表法往回推序列时要把极点 p 与 z/(z-p) 的标准形式对应起来而不是直接套连续时间拉普拉斯变换的公式。每次写完逆z变换建议用信号的前几个采样值验证一下避免低级错误。4.3 从零极点图直接看图说话的经验法则零极点图一旦会看很多结论直接口算。极点半径越接近1频响峰值越尖锐峰值位置由极点角度决定零点在单位圆上则表示这个频率分量完全被滤掉零点与极点如果在某个频率上靠得很近说明该频率的响应受到牵制。全通滤波器的极点和零点关于单位圆镜像对应线性相位FIR滤波器的零点在单位圆上成对出现并且关于实轴共轭对称。我自己的习惯是拿到系统函数先画零极点图扫描一遍单位圆外有没有极点然后用 freqz 看频响曲线对照零极点位置理解为什么在这个地方凸起、那个地方凹下。多做几次比背“巴特沃斯、切比雪夫”的指标有用得多。特别是做滤波器设计时零点放哪里能压住某个干扰频率极点放哪里能形成谐振峰这些直觉全是从这一章培养出来的。5. 我的实操心得与后续扩展建议复习或初学这一章时我强烈建议做三件事。第一把 s 平面和 z 平面的映射画一遍标出左半平面、虚轴、右半平面对应圆内、圆上、圆外第二把常见序列的 z 变换对手写成一页纸包括 δ[n]、u[n]、a^n u[n]、n a^n u[n]、sin 和 cos 序列做题目时直接查这页第三不满足于只会手算把第3部分的三段代码跑通用鼠标在频响曲线上点点看零极点移动时曲线怎么变这才是把书读进脑子里。接下来要接的内容我也可以预告一下第3章的DFT本质就是频域采样第4章的FFT是它的快速算法第5章滤波器设计就是反复在 z 平面上摆弄零点和极点后面学功率谱估计、自适应滤波很多结论都可以追溯到这一章的z变换性质。如果你还要做工程希尔伯特变换、包络谱分析、数字锁相环这些模块也都离不开这一章的基础。至少我发现当年把z变换和DTFT想透彻之后整个数字信号处理后面几章基本是顺着走的没有太多突然跳出来的难点。