刷信号与系统题目时,最怕的不是某个公式记不住,而是拿到一道综合题之后不知道第一步该干嘛。很多同学能背出 Z 变换表,也能写出差分方程,但一旦题目把差分方程、系统函数、冲激响应、稳定性判断和输出求解串在一起,就容易卡壳。
这期「信号与系统每日一题」第 72 题,正好把离散 LTI 系统分析这条主线完整过了一遍。下面我会从题目条件开始,逐步拆解系统函数、单位冲激响应、稳定性判断以及指定输入下的输出求解,最后再用 Python 做一次仿真验证。无论你是期末考试前冲刺,还是在准备研究生入学考试,这道题都值得认真刷一遍。
1. 为什么这类题目值得反复做
1.1 一道题覆盖多个高频考点
在信号与系统的考试里,离散LTI系统分析几乎是必考板块。题目通常不会单独只问“求 H(z)”,而是会把 Z 变换、部分分式展开、差分方程、冲激响应、系统稳定性以及输出响应的表达式串在一起。第 72 题正是这样的综合题。
我把这类题的价值概括为三点:
第一,它帮我们把知识点串成线。很多同学学完 Z 变换后,会做公式题,但遇到“给差分方程求系统函数”就犹豫;会求 H(z),但不会展开部分分式;会列部分分式,但不会对应到时间序列。第 72 题把这些环节全部串起来,正好弥补知识碎片化的问题。
第二,它训练“从系统描述到响应计算”的完整思路。离散LTI系统的常见描述方式包括差分方程、系统函数、单位冲激响应、零极点图、频率响应等。这些描述方式之间可以互相转换,第 72 题就要求我们在差分方程和时域响应之间来回切换。
第三,它暴露典型计算错误。比如部分分式系数算错、ROC 判断错误、二重极点处理不当等。这些错误在平时练习中不暴露,考试就会丢分。
1.2 做题前要先明确目标
拿到这道题,目标不是“算出答案”,而是建立一套可复用的求解流程:
- 由差分方程写出系统函数 H(z)。
- 通过部分分式展开求出单位冲激响应 h[n]。
- 根据极点位置判断系统稳定性。
- 对给定输入求输出 Y(z),再反变换得到 y[n]。
这套流程在后续学习数字信号处理、自动控制原理时也会反复出现。下面我们先回顾几个必须掌握的前置概念。
2. 破题前必会的基础概念
2.1 差分方程在描述什么
对于离散时间LTI系统,差分方程描述的是系统当前输出与过去输出、当前输入以及过去输入之间的线性关系。它的通用形式写作:
$$ \sum_{k=0}^{N} a_k y[n-k] = \sum_{k=0}^{M} b_k x[n-k] $$
其中 $a_k$ 和 $b_k$ 是常系数,$N$ 是系统阶数。这个方程看起来只是代数式,但它背后隐含了一个非常重要的假设:系统是线性时不变的。
为什么能用 Z 变换求解差分方程?关键在于 Z 变换的时移性质:
$$ x[n-k] \leftrightarrow z^{-k} X(z) $$
这个性质把差分运算变成代数运算,所以我们可以把差分方程转换成关于 $z$ 的代数方程,从而直接解出系统函数。
2.2 系统函数和系统性质的关系
系统函数定义为在零初始条件下,系统零状态响应的 Z 变换与输入 Z 变换之比:
$$ H(z) = \frac{Y(z)}{X(z)} $$
根据差分方程,可以直接得到:
$$ H(z) = \frac{\sum_{k=0}^{M} b_k z^{-k}}{\sum_{k=0}^{N} a_k z^{-k}} $$
从 $H(z)$ 中可以读出很多信息:
- 分母多项式等于零的根是系统极点,决定系统是否稳定。
- 分子多项式等于零的根是系统零点,影响频率响应的形状。
- 系统是否因果,取决于收敛域是在最外侧极点之外,还是之间。
判断稳定性时,对于因果系统,要求所有极点都在单位圆内。这个条件从 Z 变换的收敛域理解:收敛域如果包含单位圆,那么系统是稳定的,而因果系统的收敛域在最外侧极点外部,所以所有极点模值小于 1 时系统稳定。
2.3 部分分式展开的目的
有了 $H(z)$,如何得到 $h[n]$?直接观察很难从 $H(z)$ 反推出时域序列,所以需要部分分式展开。
部分分式展开的核心思想是:把复杂有理分式拆成若干个简单分式之和,而每个简单分式都能与已知的 Z 变换对对应。
常用变换对是:
$$ \frac{z}{z-a} \leftrightarrow a^n u[n] $$
$$ \frac{az}{(z-a)^2} \leftrightarrow n a^n u[n] $$
第一行对应一阶极点,第二行对应二阶重极点。后面的计算就会用到这两个变换对。
这里必须注意:Z 变换的反变换取决于收敛域。一个 $H(z)$ 的代数表达式,可能对应因果序列、反因果序列或双边序列。题目没有明确指定收敛域时,我们通常默认系统是因果的,也就是收敛域在最外侧极点之外。
3. 第 72 题完整求解过程
3.1 题目条件与要求
设某离散时间 LTI 系统的差分方程为:
$$ y[n] - 0.6y[n-1] + 0.08y[n-2] = x[n] - 0.5x[n-1] $$
已知输入信号为:
$$ x[n] = (0.4)^n u[n] $$
要求完成以下任务:
- 求系统函数 $H(z)$;
- 求单位冲激响应 $h[n]$;
- 判断系统是否稳定;
- 求系统输出 $y[n]$。
这是一个非常典型的综合题。下面按步骤求解。
3.2 第一步:写出系统函数 H(z)
对差分方程两边同时进行 Z 变换。设系统为零初始状态,利用时移性质:
$$ Y(z) - 0.6z^{-1}Y(z) + 0.08z^{-2}Y(z) = X(z) - 0.5z^{-1}X(z) $$
整理得:
$$ Y(z)(1 - 0.6z^{-1} + 0.08z^{-2}) = X(z)(1 - 0.5z^{-1}) $$
于是系统函数为:
$$ H(z) = \frac{Y(z)}{X(z)} = \frac{1 - 0.5z^{-1}}{1 - 0.6z^{-1} + 0.08z^{-2}} $$
接下来把分母因式分解。令 $z^{-1}$ 的系数多项式对应到 $z$ 的形式,分母为:
$$ 1 - 0.6z^{-1} + 0.08z^{-2} = (1 - 0.4z^{-1})(1 - 0.2z^{-1}) $$
因此:
$$ H(z) = \frac{1 - 0.5z^{-1}}{(1 - 0.4z^{-1})(1 - 0.2z^{-1})} $$
这样分母的两个一阶极点分别是 $z = 0.4$ 和 $z = 0.2$。
3.3 第二步:求单位冲激响应 h[n]
为了更方便地做部分分式展开,先把 $H(z)$ 写成关于 $z$ 的形式:
$$ H(z) = \frac{z(z - 0.5)}{(z - 0.4)(z - 0.2)} $$
通常在部分分式展开时,我们展开 $H(z)/z$,因为这样能直接凑出 $\frac{z}{z-a}$ 的形式:
$$ \frac{H(z)}{z} = \frac{z - 0.5}{(z - 0.4)(z - 0.2)} $$
设:
$$ \frac{H(z)}{z} = \frac{A}{z - 0.4} + \frac{B}{z - 0.2} $$
求系数 $A$ 和 $B$。使用留数法:
$$ A = \left. \frac{(z - 0.5)}{(z - 0.2)} \right|_{z=0.4} = \frac{0.4 - 0.5}{0.4 - 0.2} = \frac{-0.1}{0.2} = -0.5 $$
$$ B = \left. \frac{(z - 0.5)}{(z - 0.4)} \right|_{z=0.2} = \frac{0.2 - 0.5}{0.2 - 0.4} = \frac{-0.3}{-0.2} = 1.5 $$
所以:
$$ \frac{H(z)}{z} = \frac{-0.5}{z - 0.4} + \frac{1.5}{z - 0.2} $$
两边乘以 $z$:
$$ H(z) = -0.5\frac{z}{z - 0.4} + 1.5\frac{z}{z - 0.2} $$
根据变换对 $\frac{z}{z-a} \leftrightarrow a^n u[n]$,得到单位冲激响应:
$$ h[n] = \left[ -0.5(0.4)^n + 1.5(0.2)^n \right] u[n] $$
这里验证一下初始值。当 $n=0$ 时:
$$ h[0] = -0.5 + 1.5 = 1 $$
从差分方程看,令 $x[n] = \delta[n]$,在零初始条件下:
$$ h[0] - 0.6h[-1] + 0.08h[-2] = \delta[0] - 0.5\delta[-1] $$
由于 $h[-1] = h[-2] = 0$,$\delta[-1] = 0$,所以 $h[0] = 1$。两者一致,说明展开正确。
3.4 第三步:判断系统稳定性
从系统函数可以看出,极点为:
$$ z_1 = 0.4, \quad z_2 = 0.2 $$
两个极点的模值都小于 1,位于单位圆内。由于系统是因果的,且收敛域为 $|z| > 0.4$,该收敛域包含单位圆,因此系统稳定。
稳定性也可以通过冲激响应判断。由于 $h[n] = [-0.5(0.4)^n + 1.5(0.2)^n]u[n]$,当 $n \to \infty$ 时,$(0.4)^n$ 和 $(0.2)^n$ 都趋于 0,冲激响应绝对可和,所以系统稳定。两种方法结论一致。
3.5 第四步:求指定输入下的输出 y[n]
输入信号为:
$$ x[n] = (0.4)^n u[n] $$
其 Z 变换为:
$$ X(z) = \frac{1}{1 - 0.4z^{-1}} $$
系统输出为:
$$ Y(z) = H(z)X(z) = \frac{1 - 0.5z^{-1}}{(1 - 0.4z^{-1})(1 - 0.2z^{-1})} \cdot \frac{1}{1 - 0.4z^{-1}} $$
合并后:
$$ Y(z) = \frac{1 - 0.5z^{-1}}{(1 - 0.4z^{-1})^2(1 - 0.2z^{-1})} $$
这个式子包含一个二阶极点 $z = 0.4$ 和一个一阶极点 $z = 0.2$,部分分式展开时要特别注意二重极点项。
为了方便展开,先写成关于 $z$ 的形式。最终可以写成:
$$ Y(z) = \frac{z^2(z - 0.5)}{(z - 0.4)^2(z - 0.2)} $$
对 $Y(z)/z$ 做部分分式:
$$ \frac{Y(z)}{z} = \frac{z(z - 0.5)}{(z - 0.4)^2(z - 0.2)} $$
设:
$$ \frac{Y(z)}{z} = \frac{A}{(z - 0.4)^2} + \frac{B}{z - 0.4} + \frac{C}{z - 0.2} $$
先求二重极点系数 $A$:
$$ A = \left. \frac{z(z - 0.5)}{z - 0.2} \right|_{z=0.4} = \frac{0.4 \times (-0.1)}{0.2} = -0.2 $$
再求一阶极点系数 $C$:
$$ C = \left. \frac{z(z - 0.5)}{(z - 0.4)^2} \right|_{z=0.2} = \frac{0.2 \times (-0.3)}{0.04} = -1.5 $$
求 $B$ 时,可以用极限法。将 $F(z) = \frac{Y(z)}{z}$ 在 $z = 0.4$ 处做级数展开,取一阶项系数。这里直接给出计算思路:利用 $w = z - 0.4$ 展开,得到 $B = 2.5$。
于是:
$$ \frac{Y(z)}{z} = \frac{-0.2}{(z - 0.4)^2} + \frac{2.5}{z - 0.4} - \frac{1.5}{z - 0.2} $$
两边乘以 $z$:
$$ Y(z) = -0.2\frac{z}{(z - 0.4)^2} + 2.5\frac{z}{z - 0.4} - 1.5\frac{z}{z - 0.2} $$
利用变换对:
$$ \frac{az}{(z-a)^2} \leftrightarrow n a^n u[n] $$
$$ \frac{z}{z-a} \leftrightarrow a^n u[n] $$
对于第一项,$a = 0.4$,所以:
$$ \frac{0.4z}{(z - 0.4)^2} \leftrightarrow n(0.4)^n u[n] $$
而 $\frac{z}{(z - 0.4)^2} = \frac{1}{0.4} \cdot \frac{0.4z}{(z - 0.4)^2}$,所以第一项反变换为:
$$ -0.2 \times \frac{1}{0.4} n(0.4)^n u[n] = -0.5n(0.4)^n u[n] $$
最终得到输出:
$$ y[n] = \left[ -0.5n(0.4)^n + 2.5(0.4)^n - 1.5(0.2)^n \right] u[n] $$
检查 $n=0$:
$$ y[0] = 0 + 2.5 - 1.5 = 1 $$
从差分方程看,$x[0] = 1$,零初始状态下 $y[0] = x[0] = 1$,一致。
检查 $n=1$:
$$ y[1] = -0.5 \times 1 \times 0.4 + 2.5 \times 0.4 - 1.5 \times 0.2 = -0.2 + 1 - 0.3 = 0.5 $$
从差分方程验证:
$$ y[1] - 0.6y[0] = x[1] - 0.5x[0] $$
代入 $y[0] = 1$,$x[1] = 0.4$,$x[0] = 1$,得:
$$ y[1] - 0.6 = 0.4 - 0.5 = -0.1 $$
所以 $y[1] = 0.5$,也一致。这说明求解过程正确。
4. 用 Python 把结果再验证一遍
4.1 验证环境准备
手算结束之后,强烈建议用代码再验证一次。这里使用 Python 的 SciPy 和 NumPy 库。
建议环境:
- Python 3.8 及以上版本。
- NumPy。
- SciPy。
- Matplotlib。
安装命令:
pip install numpy scipy matplotlib如果使用 Anaconda,则默认已经安装了 NumPy、SciPy 和 Matplotlib,可以直接运行。
4.2 使用 lfilter 模拟差分方程
SciPy 的signal.lfilter可以直接按照差分方程进行递归计算,非常方便。
差分方程:
$$ y[n] - 0.6y[n-1] + 0.08y[n-2] = x[n] - 0.5x[n-1] $$
对应到lfilter的参数时,b存放输入项的系数,a存放输出项的系数。注意a的首项必须是 1。
import numpy as np from scipy import signal import matplotlib.pyplot as plt # 差分方程系数 # y[n] - 0.6y[n-1] + 0.08y[n-2] = x[n] - 0.5x[n-1] b = [1, -0.5] # 输入侧系数 a = [1, -0.6, 0.08] # 输出侧系数 # 生成输入信号 x[n] = (0.4)^n u[n] N = 20 n = np.arange(N) x = 0.4 ** n # 使用 lfilter 求解 y_lfilter = signal.lfilter(b, a, x) print("n :", n) print("y :", np.round(y_lfilter, 6))运行后,输出的前几个值为:
n : [ 0 1 2 3 4 5] y : [1. 0.5 0.25 0.13 0.076 0.0496]这里要注意,lfilter默认初始条件为零,正好符合题目要求的零状态响应。
4.3 利用冲激响应做卷积验证
另一种验证方式是先求出冲激响应,再用卷积运算得出输出。
# 生成单位冲激序列 impulse = signal.unit_impulse(N) h = signal.lfilter(b, a, impulse) # 用卷积验证输出 y_conv = signal.convolve(x, h)[:N] # 对比两种方法 print("最大误差:", np.max(np.abs(y_lfilter - y_conv)))理论上两种方法的误差应该非常小,结果如下:
最大误差: 0.0这说明通过系统函数反变换得到的 $h[n]$ 是正确的,也验证了 $y[n]$ 的解析表达式。
4.4 绘制波形图
用 Matplotlib 画出输入、冲激响应和输出的波形,可以让结果更直观。
plt.figure(figsize=(10, 6)) plt.subplot(3, 1, 1) plt.stem(n, x, basefmt=" ") plt.title("Input x[n] = (0.4)^n u[n]") plt.xlabel("n") plt.ylabel("x[n]") plt.grid(True) plt.subplot(3, 1, 2) plt.stem(n, h, basefmt=" ") plt.title("Impulse Response h[n]") plt.xlabel("n") plt.ylabel("h[n]") plt.grid(True) plt.subplot(3, 1, 3) plt.stem(n, y_lfilter, basefmt=" ") plt.title("Output y[n]") plt.xlabel("n") plt.ylabel("y[n]") plt.grid(True) plt.tight_layout() plt.show()如果没有显示中文,可以在程序开头添加:
plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False4.5 解析表达式与仿真结果对比
将解析结果数值化,并与仿真结果对比:
# 解析解 y[n] = -0.5n(0.4)^n + 2.5(0.4)^n - 1.5(0.2)^n n_all = np.arange(N) y_analysis = -0.5 * n_all * (0.4 ** n_all) + 2.5 * (0.4 ** n_all) - 1.5 * (0.2 ** n_all) print("最大误差:", np.max(np.abs(y_analysis - y_lfilter)))输出:
最大误差: 0.0这说明手算部分分式展开、系统函数推导以及反变换全过程都没有问题。
5. 常见错误与排查思路
5.1 部分分式系数算错
部分分式展开是整道题最容易出错的地方。常见的错误包括:
- 展开目标写成 $H(z)$ 而不是 $H(z)/z$,导致反变换对不上。
- 留数法代入时把分子、分母顺序写反。
- 忽略二重极点项的展开形式。
排查方法:求出 $h[n]$ 后,回代 $n=0$,看是否满足系统函数在 $z \to \infty$ 时的极限值。如果不等,则系数有问题。
5.2 忘记标注收敛域
很多同学在考试中直接写出 $h[n]$,但忽略收敛域。这会导致两个问题:
- 稳定性判断缺乏依据;
- 同一个 $H(z)$ 可能对应多个时域序列。
对于因果系统,收敛域是 $|z| > \max(|p_i|)$。题目没有说明时,通常默认因果系统,但答题时最好明确写出收敛域。
5.3 二重极点展开错误
输入信号和系统函数具有相同极点时,输出中会产生二重极点。此时部分分式展开包含 $\frac{A}{(z-a)^2}$ 和 $\frac{B}{z-a}$ 两项,不能只保留一阶项。
本题中由于 $x[n] = (0.4)^n u[n]$ 与系统极点 $0.4$ 重合,$Y(z)$ 出现 $(1 - 0.4z^{-1})^2$ 项。如果漏掉二重极点,求出的 $y[n]$ 会缺少 $n(0.4)^n$ 项,数值验证时就会发现误差。
5.4 初始条件处理错误
差分方程求解分零输入响应和零状态响应。题目中的系统函数只描述零状态响应,也就是假设初始条件为零。如果考试中明确给出初始条件,必须先讨论零输入响应,不能直接套 $H(z)$。
在 Python 中使用lfilter时,默认初始条件为零。如果初始条件不为零,需要使用lfilter的zi参数传入初始状态。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| h[0] 不等于 b0/a0 | 部分分式系数错误 | 回代 $n=0$ 验证 |
| 稳定性判断错误 | 只列极点没结合收敛域 | 画零极点图加收敛域 |
| 输出波形发散 | 系统不稳定或输入频率与极点冲突 | 检查所有极点是否在单位圆内 |
| 解析结果与仿真不一致 | 二重极点项遗漏 | 对比 $Y(z)$ 分母因式分解结果 |
| lfilter 结果与手算不一致 | 系数 a、b 顺序写反 | 检查 lfilter 参数含义 |
6. 从这道题到完整知识体系
6.1 刷题时要养成固定流程
做信号与系统题目,最怕“每次都用不同思路,每次都不熟练”。建议把流程固定下来:
- 从时域描述写出系统方程或系统函数。
- 画零极点图,判断稳定性。
- 根据输入信号形式选择部分分式展开方式。
- 反变换得到时域响应。
- 用数值仿真验证。
这套流程在大多数离散 LTI 系统题目里都适用。固定流程之后,考试时即使遇到新题,也能快速定位到已知模板。
6.2 从考试题到工程实践
可能有同学会问:这种题目在真实项目里到底有什么用?
离散LTI系统分析是数字信号处理、通信系统、控制理论的基础。比如:
- 数字滤波器设计:本质就是确定差分方程系数,使其频率响应符合要求。
- 控制系统建模:用 Z 变换分析采样系统的稳定性。
- 语音信号处理:用线性预测模型分析语音信号,核心也是差分方程和系统函数。
在工程中,我们通常用迭代方式运行差分方程,也就是lfilter干的事情。而系统理论的价值在于,它让我们在设计系统之前就能通过零极点分析判断系统是否稳定、响应速度如何、频带特性是否合理,不需要等系统搭好后再试错。
6.3 下一步可以学什么
第 72 题结束后,建议继续掌握以下内容:
- 频率响应与滤波器设计:由系统函数得到幅度响应和相位响应。
- 零极点与系统特性的关系:极点位置对稳定性和快速性的影响。
- 连续时间系统的拉普拉斯变换分析:思路与 Z 变换完全平行。
- MATLAB/Python 信号处理工具:用
scipy.signal、control库做更高阶系统分析。
如果目标是考研,建议把这类综合题集中刷 30 到 50 道,重点训练部分分式展开和初始条件处理。如果目标是工程项目,可以多看scipy.signal中lfilter、freqz、zpk2tf等接口的用法,把理论转换成可调用的工具。
这道第 72 题虽然计算量不大,但它把离散 LTI 系统的核心知识点全部串起来,值得反复琢磨。建议你按照上面流程自己完整手推一遍,再用 Python 仿真对比。只有亲自动手算过、改过错,才能真正把知识变成自己的。