尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

离散信号与序列:核心概念、运算及工程实现陷阱

离散信号与序列:核心概念、运算及工程实现陷阱 1. 先纠正一个直觉离散信号不是连续信号打了几个点我见过太多人第一次接触离散信号的判断失误把x[n]直接理解成对x(t)每隔 T 采一次样得到的那串数。这个理解在工程里有一半是对的但作为定义是错的而且会一路错下去——错到后面做周期判断、做频谱分析时你会莫名其妙地和答案差一个倍数还找不到原因。根子在于连续信号的横轴是时间是物理量有单位、有连续性离散信号的横轴是序号是一个整数下标本身没有量纲。x[3]的意思是第 3 号位置上的那个值这个3不是 3 秒也不是 3 毫秒它只是排队时的第 3 个。你采样得到的序列是离散信号的一个特例但离散信号本身更宽——每天收盘价、每小时队列长度、每个数据包到达间隔、每次迭代的误差值这些天然就是离散的压根不存在采样的连续母体。搞清楚这一点后面所有记号才站得住脚。1.1 x[n] 的括号、下标和它的身份约定上连续信号写圆括号x(t)离散信号写方括号x[n]这个方括号不是为了好看——它是在提醒你n 只能取整数。x[2.5]是没有定义的不是取中间值的问题是根本不存在这个样本。所以凡是涉及离散域的表达式你都要在心里过一遍这个下标一定是整数吗很多推导出错就出在这一步。在中文教材里离散时间信号和序列基本是同义词我后面也会混着用。你看到序列两个字默认就是一串按整数序号排列的数不必纠结名字。一个完整的离散信号描述包含两样东西样值和序号轴。缺了任何一个这个信号就说不清楚。这一点在写代码的时候尤其致命后面第 7 章会专门讲。1.2 三条容易漏掉的约定第一n 可以是负数。连续信号你在物理上很难有负时间但离散序列允许存在负半轴尤其是在做相关、卷积、滤波器分析的时候双边序列是家常便饭。所以别一上手就默认n从 0 开始。第二零点必须有基准。x[n]里n0对应的那个样本是整个坐标系的锚点。你把它平移到别处整个序列的相位、周期起算点全变了。我见过有人做仿真时忘记记录n0结果画出来的图和教科书一模一样但错开两格排查半天。第三序列在概念上定义在整个整数集上只不过大部分位置是 0。一个只有 5 个非零值的序列严格说是在-∞到∞上都有定义、绝大多数位置取零的双边序列。这不是咬文嚼字——当你写Σ_{n-∞}^{∞}这种累加式的时候这个视角是必需的。1.3 三个描述范围的术语先把话说准按非零值所在的范围序列分几类术语用准了交流效率会高很多有限长序列只在n的某个有限区间内非零比如R_N[n]只在[0, N-1]上为 1。右边序列因果序列n 0时全为零从某个起点往右才有值a^n u[n]就是典型。工程上因果这个词比右边用得更多。左边序列往左才有值通常出现在反因果系统里。双边序列两边都无限延伸做双边变换时会遇到收敛域的问题。你可能会觉得这套分类有点啰嗦但等你做 Z 变换时会发现收敛域是什么形状完全由这四类决定——右边序列收敛域是圆外左边是圆内双边是个环。现在把范围说清楚后面能省一堆事。2. 把一个序列说清楚四种描述方式的取舍描述一个离散信号常用的有四条路闭式表达式、列表、图形、以及程序生成。这四种方式互为补充但没有一种能通吃你得知道每种在什么场合会掉链子。2.1 闭式表达能省掉多少字又在什么时候失效闭式表达就是用一个公式把整个序列写完例如x[n] 0.8^n u[n]。它的优势是紧凑、可解析、能做符号推导写论文和教材必须用它。但它有个硬伤大量真实序列根本没有闭式。比如某只股票 500 天的收盘价、某段语音的采样值、一段实测信道的冲激响应——这些只能列表或者存成文件。更麻烦的是有些序列虽然有规则但用初等函数写不出来只能用分段加条件的方式硬凑。还有一个非常常见的坑闭式表达里的约束条件别漏。x[n] 0.8^n和x[n] 0.8^n u[n]是两个完全不同的序列前者是双边序列n取负值时0.8^n会发散到无穷大后者才是我们想要的因果衰减序列。写公式时省掉那个u[n]性质就全变了。分段表达也容易出错。比如矩形窗写成x[n] 1, 0 ≤ n ≤ N-1 0, 其他这里的其他必须写全不能只写n N-1而漏掉n 0否则定义域就不完整。2.2 列表法与图形法棒图和折线图的选择标准列表法就是老老实实把n和x[n]两个数并排写出来格式像这样n-2-10123x[n]00.510.50.250我强烈建议你在做题和记录数据时都用这个格式因为它把序号轴显式写出来了不会出现起点记错的事故。图形法要特别注意离散信号的标准画法是棒图stem plot每根竖直的线段顶端有一个点点之间不连线。为什么不连线因为n1和n2之间没有定义连线会暗示存在中间值这是误导。有些场合为了看清包络会用细线把顶点连起来做辅助这没问题但你要明确那条线只是视觉辅助不是信号本身。我在报告里用过这个技巧但会在图注里写清楚虚线为包络示意。2.3 描述方式互转时最容易错的三处第一处从闭式到列表时的边界。u[n]在n0处取 1 而不是 0R_N[n]的长度是N而不是N1区间0到N-1一共 N 个点。这两个边界我批改过无数次。第二处从列表到闭式时丢掉起始位置。一堆数字[1, 2, 3, 4]如果不说清楚它对应n 0..3还是n -1..2就是两个不同的信号。程序里这个错误尤其隐蔽因为数组本身不携带序号信息。第三处图形读数的横轴刻度。用linspace之类均匀取点画出来的横轴往往不是整数画出来看着挺像其实每个棒的位置都不对。这个坑在第 7 章会详细说。3. 序列运算离散域最好用的五个操作离散域的运算有一个让连续域羡慕的优点它全是逐点操作没有微积分没有测度问题。你只要对齐序号轴剩下的就是加减乘除。3.1 逐点加乘与数乘为什么它比连续域省心序列相加z[n] x[n] y[n]逐点相加。序列相乘z[n] x[n] · y[n]逐点相乘。数乘z[n] c · x[n]每个点乘同一个常数。但前提是序号轴要对齐。x[n]定义在n 0..9y[n]定义在n -5..4你直接把它们两个数组相加程序不会报错但结果是垃圾——因为数组下标第 0 位对应的n不一样。我做仿真时的固定习惯是所有序列都统一挂在同一个n轴上用np.arange(-20, 21)这种显式轴然后按n的值去构造每个序列而不是分别构造再对齐。这个习惯能挡掉 90% 的对齐类 bug。3.2 移位与翻转x[n-k] 和 x[-n] 的坐标系动手画法移位有个反直觉的地方x[n-k]是右移 k不是左移。原因很直白你想让x[n-k]在n 5处取到x[0]那k就得等于 5整个波形往右挪了 5 格。我刚学的时候也搞反过后来找到个不用背的口诀减号在后波形往右减号在前波形往左。翻转是y[n] x[-n]整个波形关于n 0这条竖线镜像。复合操作要注意顺序x[-nk]是把x[n]先右移k再翻转还是先翻转再左移k结果是一样的但中间过程不同。你如果容易乱就老老实实列一个三行表一行写n的取值一行写x[-n]的值再一行写最终结果慢慢推一遍比心算稳。3.3 抽取与内插离散尺度变换必须落在整数格点上连续域可以随便做时间压缩x(2t)离散域不行。x[2n]会发生什么它只保留了原来偶数序号上的点奇数点全丢了——这叫抽取decimation。后果很严重如果原序列里刚好藏着高频成分x[2n]之后它可能伪装成低频这就是混叠。处理办法是在抽取前先做低通滤波这就是经典的抗混叠 抽取两步走。反过来x[n/2]是内插问题更麻烦n取奇数时n/2不是整数x[n/2]没有定义。工程上的实现是先在相邻点之间插零再过一个低通滤波器把零填平这套操作在采样率转换里天天用。3.4 累加、差分与奇偶分解一套能互相验证的运算累加是离散域的积分y[n] Σ_{k-∞}^{n} x[k]差分是离散域的微分最常用的是后向差分y[n] x[n] - x[n-1]。这两个操作互为逆运算差掉累加结果就回到原序列你可以用这个关系做自检。奇偶分解把任意序列拆成对称和反对称两部分偶部x_e[n] (x[n] x[-n]) / 2 奇部x_o[n] (x[n] - x[-n]) / 2这个分解在频域里会变成实部对偶、虚部对偶的结论是后面理解实序列频谱对称性的钥匙。还有相关运算互相关定义为r_xy[m] Σ_n x[n] y[n-m]。它的物理含义是把 y 左移 m 格之后和 x 有多像。自相关r_xx[m]一定是关于m 0对称的偶序列而且|r_xx[m]| ≤ r_xx[0]这两条性质可以拿来验证你算得对不对。4. 基元序列δ[n]、u[n] 与矩形序列 R_N[n]典型序列里最重要的三个是单位脉冲、单位阶跃和矩形序列它们之间的关系不是并列的三个定义而是可以互相推导的。4.1 δ[n] 不是无穷大它就是1δ[n]的定义极简δ[n] 1, n 0 0, n ≠ 0这里必须强调一个和连续域完全不同的地方连续域的冲激函数δ(t)是个广义函数它在t0处取值无穷大积分为 1处理起来要小心。离散域的δ[n]是个普通序列n0处的值就是实打实的 1没有任何无穷的玄学。谁要是跟你说δ[n] 在 0 点无穷大那是在把连续的概念硬套过来会让你后面算卷积时彻底迷路。正因为它是普通序列面积求和就等于 1 也是自然结论不需要额外的广义函数理论撑着。4.2 从 δ 累加出 u从 u 相减出 R_N单位阶跃序列u[n] 1, n ≥ 0 0, n 0它们的关系有两组建议你亲手推一遍δ 和 u 的差分关系δ[n] u[n] - u[n-1]。代入检验n ≥ 1时两边都是 1-10n 0时左边 1右边 1-01n 0两边都是 0。成立。δ 和 u 的累加关系u[n] Σ_{k-∞}^{n} δ[k]。因为只有k0这一项非零所以当n ≥ 0时求和结果为 1n 0时为 0。成立。换个写法u[n] Σ_{m0}^{∞} δ[n-m]这个形式在后面写卷积和时更好用。矩形序列 R_N[n]定义在0 ≤ n ≤ N-1上取 1其余为 0长度恰好N个点。它由两个阶跃相减得到R_N[n] u[n] - u[n-N]这个式子的直观解释是一个从 0 起跳的阶跃减去一个从N起跳的阶跃剩下的就是一段宽N的平台。这也是所有加窗操作的核心思想——窗本质上就是矩形序列的一个加权版本。R_N[n]同时也可以写成 N 个移位脉冲的和Σ_{k0}^{N-1} δ[n-k]N 个 1 叠加起来正是平台。4.3 任意序列的脉冲表示后面所有卷积的地基这一条是整个课程里我最看重的公式x[n] Σ_{k-∞}^{∞} x[k] · δ[n-k]看起来是同义反复其实含义很深任何一个序列都可以表示成无穷多个加权移位的单位脉冲之和权重就是它自己的样值。为什么它重要因为系统是线性的、时不变的。输入一个移位脉冲δ[n-k]输出就是移位的冲激响应h[n-k]再乘上权重x[k]并全部叠加就得到y[n] Σ_k x[k] · h[n-k] x[n] * h[n]这就是卷积和的全部来历。你现在把这条线走通后面学卷积就不用死记公式了。5. 实指数与复指数a^n u[n] 的形态分类指数序列是描述一阶系统行为的通用语言很多物理过程的离散模型最后都落到a^n上。5.1 a 取不同范围时的六种形态对照x[n] a^n u[n]的形态完全由a决定我把它们整理成一张表这张表值得记住a 的取值序列形态有界性典型场景a 1单调递增无界发散不稳定系统、指数增长模型a 1恒为 1即 u[n]有界累加器、积分器0 a 1单调衰减有界能量有限RC 放电、衰减记忆a 0退化为 δ[n]有界纯比例环节-1 a 0衰减振荡符号交替有界能量有限欠阻尼数字系统a -11/-1 交替有界功率有限频率 ω π 的振荡a -1增长振荡无界不稳定振荡这里有个细节值得单独说a -1和a -1虽然都是符号交替但一个不衰减、一个发散性质完全不同。而a -1这个序列恰好对应数字角频率ω π的振荡也就是每个样本都变号的最快振荡——这个联系在第 6 章会讲清楚。5.2 用几何级数算能量|a|1 才有界离散信号的能量定义为E Σ_n |x[n]|²。对x[n] a^n u[n]E Σ_{n0}^{∞} |a|^{2n} 1 / (1 - |a|²)当 |a| 1推导很简单把|a|²当成公比q几何级数求和即可。举个具体的数a 0.8则q 0.64E 1 / 0.36 ≈ 2.778。这个结果有两层用处。第一它给出稳定的判据一阶系统的极点在这里就是a落在单位圆内能量才有限。第二它给了你一个可用的自检手段——画完a^n u[n]的图把前若干项平方求和看是否收敛到1/(1-|a|²)附近对不上说明你截断的项数太少或者乘错了u[n]。顺带提一句能量的对数关系|a|越接近 1能量越大|a| 0.99时E ≈ 50.25这就是为什么接近临界稳定的系统对扰动极其敏感。5.3 复指数 e^{jωn} 的实部虚部与共轭对称复指数序列x[n] e^{jωn}按欧拉公式展开e^{jωn} cos(ωn) j·sin(ωn)也就是说实部是余弦序列虚部是正弦序列两者在相位上相差π/2。它是复数序列在复平面上表现为单位圆上的匀速旋转点每步转过ω弧度转满一周需要2π/ω步。复指数之所以是线性时不变系统分析的基石是因为它是特征函数把e^{jωn}输入任意线性时不变系统输出仍然是同频率的复指数只是幅度和相位变了——频率不动。这个性质让频域分析成为可能也是后面离散时间傅里叶变换的出发点。还有个常考性质共轭对称性。e^{jωn}的共轭是e^{-jωn}所以cos(ωn) (e^{jωn} e^{-jωn})/2实序列的频域表示一定满足共轭对称。这个结论在做频谱图时非常有用——你只需要画一半频率轴就够了。6. 正弦序列的周期判断与高频像低频陷阱这一章是我认为离散信号里最反直觉、也最容易考、最容易出错的地方。它的很多结论在连续域里根本没有对应物。6.1 ω 的 2π 周期性为什么 ω1.9π 和 ω-0.1π 是同一个序列在连续域cos(Ωt)的频率越高振荡越快Ω可以取到无穷大。离散域不是这样。看这个推导cos((ω 2πk)n φ) cos(ωn 2πkn φ)因为n一定是整数所以2πkn一定是2π的整数倍对余弦函数来说等于没加。于是cos((ω 2πk)n φ) cos(ωn φ)结论就是数字角频率ω以2π为周期ω和ω 2πk描述的是同一个序列。因此我们只需要关心一个区间通常取[-π, π]或者[0, 2π]。这条结论带来的第一个反直觉现象ω 1.9π和ω -0.1π是同一个序列前者听起来频率很高实际表现和很慢的负频率一模一样。第二个反直觉现象最高的振荡频率在ω π不是无穷大。ω π时每个样本都变号这是离散序列能达到的最快振荡。超过 π 之后频率涨得越多实际振荡反而越慢因为ω 1.1π等价于-0.9π。这个现象和采样中的高频折叠是同一件事的两面理解它对后面处理混叠至关重要。6.2 周期条件的完整推导2π/ω 化最简分数分子即周期正弦序列一定是振荡的但振荡不等于周期。判断条件是存在正整数N使得对一切n有x[nN] x[n]。代入正弦表达式要求cos(ω(nN) φ) cos(ωn φ)也就是ωN必须是2π的整数倍ωN 2πk k 为整数整理得N/k 2π/ω。由于N和k都是整数这就要求2π/ω必须是有理数。如果它是个无理数无论 N 取多大都凑不出整数关系序列永远不周期。把比值写成最简分数2π/ω N₀/k₀分子分母互质那么基本周期就是N N₀。注意是分子不是分母这里特别容易记反。看几个例子ωrad/sample2π/ω是否周期基本周期 Nπ2/1是2π/24/1是43π/510/3是102π/33/1是312π无理否—0.210π无理否—0无穷是常数1拿ω 3π/5验算一下ωN 3π/5 × 10 6π正好是2π的 3 倍成立。而N 5时ωN 3π不是2π的整数倍不成立。ω 1这个例子很值得留意——它明明在振荡但就是不周期。很多人在实验里画出一段cos(n)觉得看着挺规律的其实那是短时间窗口内的伪规律拉长到几百个点就会发现相位一直在飘。6.3 数字频率与采样ω ΩT 的来历和混叠的直观如果序列确实来自对连续正弦cos(Ωt)的采样采样间隔T采样点t nT代入cos(Ω·nT) cos(ωn)其中 ω ΩTω就是数字角频率单位是弧度每样本。如果用采样频率f_s 1/T表示则ω 2πf/f_s。这条关系解释了混叠连续频率f和f m·f_s会给出同一个ω模2π意义下相等因此采样后无法区分。比如f_s 1000Hz一个 900 Hz 的信号和一个900 - 1000 -100Hz 的信号采样后完全一样——表现出来就是一个 100 Hz 的慢振荡。这就是为什么抗混叠滤波器必须放在采样之前而不是采样之后想办法补救因为采样那一刻信息就已经塌掉了。另一个实用的推论为了让数字角频率落在主值区间[0, π]内采样率至少要取信号最高频率的两倍。这句话就是采样定理但你现在是从ω的2π周期性推出来的而不是背下来的。7. 把公式落到代码里绘图与验证环节的坑上面这一堆公式写出来容易画出来才是真考验。我自己在这上面栽过的次数比推公式多得多。7.1 横轴对齐n 数组必须显式声明看这段代码这是画δ[n]的标准姿势import numpy as np import matplotlib.pyplot as plt n np.arange(-5, 11) # 显式声明序号轴不要用 linspace delta np.where(n 0, 1, 0) fig, ax plt.subplots(figsize(8, 3)) ax.stem(n, delta) ax.set_xlabel(n) ax.set_ylabel(delta[n]) ax.set_xticks(np.arange(-5, 11, 1)) ax.grid(alpha0.3) plt.show()几个关键点n用np.arange而不是np.linspace因为arange给的是整数步长delta用np.where按n的值构造保证它和n一一对应set_xticks把刻度拨到整数上读图不会错位。对比一下常见的错误写法delta np.zeros(16) delta[5] 1 # 想表达 n0 处取 1 plt.stem(delta) # 忘了传 n横轴变成了 0..15这段代码画出来的图脉冲出现在横轴 5 的位置而实际对应的是n 0。图和对不上号但编译运行一点问题都没有这就是它危险的地方。MATLAB 里同理n -5:10; delta double(n 0); stem(n, delta, filled); xlabel(n); ylabel(\delta[n]);n 0产生的是逻辑数组double转成 0/1这样写既短又不容易错。7.2 一个真实的踩坑浮点转整型把 a^n 全变成 0这个坑我印象太深了。当时想画0.8^n u[n]觉得值都是小数画出来不好看顺手加了个取整n np.arange(0, 20) x (0.8 ** n).astype(int) # 灾难现场结果整条曲线几乎全是 0只有第一个点有个孤零零的 1。原因很直白0.8^0 1取整得 10.8^1 0.8取整得 0之后全是 0。一个漂亮的衰减序列被astype(int)直接抹平了。正确的做法是保持浮点n np.arange(0, 20) a 0.8 x a ** n真要显示成整数比如画脉冲位置就用np.floor或者干脆不转。这个教训的通用版本是任何把信号值做类型转换的操作都要先问一句这个转换会不会改变信号的形状。7.3 周期性的数值判断与显示范围的标注程序里判断正弦序列是否周期本质上是判断2π/ω是不是有理数。浮点数做不到严格判断无理数在机器里也是有理数实践中的做法是带容忍度的近似from fractions import Fraction w 0.6 * np.pi r Fraction(2 * np.pi / w).limit_denominator(1000) print(r) # 10/3 print(N , r.numerator) # 10limit_denominator(1000)的意思是找一个分母不超过 1000 的最接近分数对10/3这种简单比值它能精准命中。但你要清楚这只是工程近似给定一个看似无理的ω程序总会还你一个分数所以还得结合物理背景判断。ω 1这种明显带有整数弧度含义的情况你应该直接判定为非周期而不是让程序找个355/113之类的近似糊弄过去。画图时还有两个习惯我强烈建议保留第一无限长序列一定要标注截断范围比如a^n u[n]只画n 0..30就在图注里写明第二画多周期的正弦时让窗口长度尽量取基本周期的整数倍否则你会看到首尾接不上那是窗口截断造成的假象不是信号本身的问题。最后再分享一个自查小技巧画完任何序列先手算三个点——起点、n0、终点——和图上对一遍。这三个点对了中间基本不会错对不上多半是移位方向或者u[n]漏了。我到现在做滤波器仿真还保留这个习惯一次手算三秒钟能省掉半小时的调试。
返回列表