我遇到过不止一个工程师问同样的问题:既然傅里叶变换已经能把信号拆成不同频率的正弦波,为什么教材还要塞进来拉普拉斯变换和 z 变换,而且三者长得还那么像。这个问题问得很好,它其实戳中了整个信号与系统课程里最容易被讲糊的那条主线。我在做振动信号分析、滤波器设计和嵌入式控制的时候,三个变换轮着用,慢慢发现它们不是三门并列的知识,而是同一套逻辑在三种场景下的三个出口:拉普拉斯变换管连续时间上的瞬态和稳定性,z 变换管离散时间上的递推和数字实现,傅里叶变换则是它们各自在"频率轴"这条特殊切片上的样子。三角脉冲的傅里叶变换之所以被反复拿出来讲,是因为它一次性把"时域平滑程度"和"频域衰减速度"这层关系说透了,记住它一条,一整类变换对都能顺着推出来。下面我按自己实际用的顺序,把三者的共同骨架、收敛域、映射关系、三角脉冲的推导与记忆方法,以及踩过的坑,完整捋一遍,适合正在学这门课的人,也适合工作几年后想回头把基础补扎实的人。
1. 三个变换共享的那套骨架是什么
1.1 从"用正弦波拆信号"升级到"用复指数拆信号"
很多人一开始把傅里叶变换理解成"拿一堆不同频率的正弦波去凑原信号",这个直觉没错,但不够用。真正好用的表述是:把信号投影到一组复指数基函数上,用内积算出每个频率分量的权重,再把权重拼成一个关于频率的函数,这就是频谱。连续时间用 e^{jωt},离散时间用 e^{jωn},形式上几乎一模一样。
问题出在复指数这个基函数本身。它只在虚轴上取值,也就是只有振荡没有衰减,所以它只承认那些"能量有限、永远不会跑偏"的信号。一个单位阶跃、一个线性增长的斜坡、一个不稳定系统的冲激响应,套到傅里叶积分里都不收敛。但工程上这些信号到处都是——电路上电瞬间的阶跃、控制系统里的积分环节、数字滤波器的边界情形。于是自然想到:给复指数加一个实部,让它可以衰减也可以增长,基函数从 e^{jωt} 变成 e^{(σ+jω)t},这就从傅里叶走进了拉普拉斯。离散侧同理,把 e^{jωn} 换成 z^n,其中 z 是复数,就得到 z 变换。
1.2 e^{st} 与 z^n 是同一件事的两种记法
拉普拉斯变换写作 X(s)=∫x(t)e^{-st}dt,其中 s=σ+jω。z 变换写作 X(z)=Σx[n]z^{-n},其中 z=re^{jω}。把两者摆在一起看,你会发现问题结构完全对称:都是"信号乘以一个复指数的共轭再累加",只不过一个用积分、一个用求和。
提示:如果一开始记不住 z 变换里为什么是 z^{-n} 而不是 z^{n},可以把它看成"时间索引往前推一格,就多乘一次 1/z",本质和拉普拉斯里 e^{-st} 的负号是同一个来源——都在做与基函数的共轭内积。
关于离散侧还有一个容易被忽略的点:z 变换是从采样信号的拉普拉斯变换里直接长出来的。如果对连续信号 x(t) 做理想采样得到冲激串,再求拉普拉斯变换,你会得到 Σx(nT)e^{-snT},令 z=e^{sT},它立刻变成 Σx(nT)z^{-n},就是 z 变换。所以 z 变换不是凭空发明的一套工具,它天生就带着"采样"的烙印,这一点在后面讲映射的时候非常关键。
1.3 收敛域才是那个被忽略的真正主角
判断一个变换能不能用,关键不在于你写下的那个代数表达式,而在于存在一个区域让积分或求和收敛。这个区域就是收敛域。收敛域有三条铁律:它一定是若干条平行线之间的条带(连续)或若干同心圆之间的圆环(离散);它的边界一定由极点决定;它内部不能包含任何极点。
真正决定一个信号长什么样的,是"表达式 + 收敛域"这个组合,而不是表达式本身。举个最典型的例子,1/(s+a) 这一个表达式,配不同的收敛域可以对应两个完全不同的时域信号:收敛域是 Re(s)>-a,反变换得到的是 e^{-at}u(t),因果的、右边长的;收敛域是 Re(s)<-a,反变换得到的是 -e^{-at}u(-t),反因果的、左边长的。你要是只记表达式不记收敛域,反变换的时候必然出错。我做滤波器设计时,凡是看到有人拿着 H(z) 直接说"这个系统是稳定的",我都会先问一句:单位圆在收敛域里吗?
2. 拉普拉斯变换与 z 变换的收敛域到底差在哪
2.1 拉普拉斯变换:条带怎么画,稳定性怎么判
拉普拉斯变换的收敛域由 s 平面上的竖条带构成,边界就是极点的实部。我习惯按下面这套流程快速判断:
- 先把 X(s) 求出来,找出所有极点,把它们的实部记下来。
- 看时域信号是右边长的、左边长的还是双边长的。右边长的信号,收敛域在最右侧极点的右边;左边长的,在最左侧极点的左边;双边的,夹在中间形成一条竖条带。
- 检查这条竖条带是否包含虚轴 s=jω。包含,说明傅里叶变换存在;不包含,说明该信号在普通意义下没有傅里叶变换,只能借助冲激这类广义函数来写。
几个常用对照,我整理成表格方便随手查:
| 时域信号 | X(s) | 极点 | 收敛域 | 是否含虚轴 |
|---|---|---|---|---|
| e^{-at}u(t),a>0 | 1/(s+a) | -a | Re(s) > -a | 是 |
| -e^{-at}u(-t),a>0 | 1/(s+a) | -a | Re(s) < -a | 否 |
| e^{-a|t|},a>0 | 2a/(a²-s²) | ±a | -a < Re(s) < a | 是 |
| u(t) | 1/s | 0 | Re(s) > 0 | 否(广义意义下存在) |
| δ(t) | 1 | 无 | 整个 s 平面 | 是 |
这张表里第 1 行和第 2 行用的是同一个代数表达式,状态却完全不同,这是收敛域重要性的最好注脚。最后一行也值得多看一眼:冲激信号的拉普拉斯变换是全平面常值,对应它在时域上是"最集中"的。
2.2 z 变换:单位圆为什么能当稳定性裁判
离散侧的结论几乎是把上面的竖条带换成了圆环。极点按模长排开,右边长的信号收敛域在最外侧极点的外面,左边长的在最内侧极点的里面,双边的夹成圆环。而"包含虚轴"这条判据,对应过来就是"包含单位圆"。
| 时域序列 | X(z) | 极点 | 收敛域 | 是否含单位圆 |
|---|---|---|---|---|
| a^n u[n],|a|<1 | z/(z-a) | a | |z| > |a| | 是 |
| -a^n u[-n-1],|a|>1 | z/(z-a) | a | |z| < |a| | 是 |
| δ[n] | 1 | 无 | 整个 z 平面 | 是 |
| u[n] | z/(z-1) | 1 | |z| > 1 | 否(广义意义下存在) |
因果系统的稳定性判据在这里变得极其简洁:所有极点都在单位圆内。我调试数字滤波器的时候经常只做一件事——把系数丢进去算极点,看有没有跑到单位圆外面或者贴上去,比看频响曲线还快。极点贴圆的时候,振荡会衰减得很慢,数值实现里稍微有点量化误差就能让它彻底唱起来。
顺带说一句,离散侧的频率响应就是 z 变换在单位圆上的取值,X(e^{jω})。所以"看频响"和"看 z 变换"本来就是一回事,只不过复数取值被限制在了一个圆上。这也解释了为什么离散频响天然是周期的:单位圆只有一圈,绕一圈回来就重复了,2π 周期性是从这里来的,不是人为规定的。
2.3 z=e^{sT} 这条映射链条丢掉了什么
从 s 平面到 z 平面有一个精确映射:z=e^{sT},反过来 s=(1/T)ln z。这条式子直接给出了几何对应关系。
实部对应模长:|z|=e^{σT}。所以 σ<0 的左半平面映射到单位圆内,σ>0 的右半平面映射到单位圆外,虚轴 σ=0 精确映射到单位圆本身。虚部对应幅角:arg z=ωT,所以 ω 从 0 走到 π/T,对应的 z 沿单位圆从 1 走到 -1。s 平面上的每一段频率间隔 2π/T,都会绕 z 平面一整圈。
麻烦就出在这个"绕圈"上。s 平面上宽度 2π/T 的无限多条平行条带,全部映射到同一个 z 平面。也就是说,从 z 反推 s 时,解并不唯一——每一条条带里都有一个解。这正是采样混叠在变换域的表述:高频信息被折叠回了低频。所以从离散域的 H(z) 反推连续原型 H(s) 时,一定得明确你是在哪一条条带里讨论,不然结论会飘。
我在实际项目里遇到过这个问题的经典版本:有人把 s 平面上离虚轴很近的一对极点,按 z=e^{sT} 直接搬到 z 平面,结果发现离散系统衰减得比原型快很多,怎么调都对不上。原因是他把极点搬过去了,却忘了采样保持环节本身还引入了额外的相位滞后。后面的第 5 节我会细讲这个坑。
3. 三角脉冲的傅里叶变换,为什么值得单独吃透
3.1 先把结论钉死,并且钉死一个约定
设三角脉冲为 tri(t/T),定义是
- |t| ≤ T 时,tri(t/T) = 1 - |t|/T
- |t| > T 时,tri(t/T) = 0
它的傅里叶变换是
X(jω) = T · [sin(ωT/2) / (ωT/2)]²
如果用频率 f 而不是角频率 ω 来写,并且采用信号处理里常见的 sinc 定义 sinc(x)=sin(πx)/(πx),那么结论可以写得非常干净:
X(f) = T · sinc²(fT)
这两个写法是同一个东西,但我必须先把约定说清楚,因为这是我见过翻车最多的地方。数学和物理教材里 sinc 常常定义成 sin(x)/x,信号处理里 sinc 常常定义成 sin(πx)/(πx)。两种约定下的表达式长得不一样,混用以后差一个 2π 或者 π 的因子,答案会对不上。所以我在笔记里固定一个规则:只要写 sinc,就默认 sinc(x)=sin(πx)/(πx),配套使用 X(f)=T·sinc²(fT);如果要写成 sin(ωT/2)/(ωT/2) 这种形式,就老老实实把系数写全,不偷懒用 sinc 这个词。
从结论里能读出四件事,这四件事才是真正有价值的部分:
- 幅度峰值等于 T,正好等于三角脉冲的面积,这是所有变换对都要满足的面积守恒。
- 零点出现在 f=k/T,k=±1,±2,…,也就是每个 T 分之一赫兹一个零点。
- 主瓣从 -1/T 到 1/T,宽度是 2/T。
- 高频按 1/f² 衰减,比矩形脉冲的 sinc 快整整一阶。
3.2 两条推导路线,我都建议走一遍
路线一:直接积分,硬推。因为 tri(t/T) 是偶函数,可以直接把积分化成余弦形式:
X(jω) = ∫_{-T}^{T} (1-|t|/T)e^{-jωt} dt = 2∫_{0}^{T} (1-t/T)cos(ωt) dt
先拆成两项,∫cos(ωt)dt 从 0 到 T,再减去 (1/T)∫t·cos(ωt)dt。第二部分用分部积分:
∫ t·cos(ωt) dt = t·sin(ωt)/ω + cos(ωt)/ω²
把上下限代进去整理,会得到 ∫_{0}^{T}(1-t/T)cos(ωt)dt = (1-cos(ωT))/(Tω²)。再乘上外面那个 2,用半角公式 1-cos(ωT)=2sin²(ωT/2) 化简,最终落到
X(jω) = 4sin²(ωT/2)/(Tω²) = T·[sin(ωT/2)/(ωT/2)]²
这条路线的好处是每一步都能验证,坏处是容易在分部积分的符号上写错,我第一次推的时候在 cos(ωt)/ω² 那一项上丢了个负号,结果整个相位特性全错了。
路线二:卷积定理,几乎不需要算积分。记 rect(t/T) 是支撑在 |t|<T/2、幅度为 1 的矩形脉冲。两个这样的矩形脉冲做卷积,得到的是一个底宽 2T、峰值 T 的三角形,也就是
rect(t/T) * rect(t/T) = T · tri(t/T)
把它两边做傅里叶变换。矩形脉冲的变换是 T·sinc(fT),卷积对应频域相乘,所以
T · FT{tri(t/T)} = T² · sinc²(fT)
两边除以 T,立刻得到 FT{tri(t/T)} = T·sinc²(fT),和直接积分完全一致。
我更推荐路线二,因为它不是"算出来"的,而是"看出来"的。它直接把三角脉冲和矩形脉冲的谱关系摆在你面前:时域卷积两个矩形,频域就乘两个 sinc,幂次从 1 变成 2。这条逻辑一旦成立,整个家族都能顺出来。
3.3 我用来记这一类变换对的"卷积链"记法
单独背一个 sinc 平方很容易忘,但把它挂到一条链上就忘不掉了。我在本子上画的是这样一条链:
| 时域操作 | 时域形状 | 频域形状 | 高频衰减 |
|---|---|---|---|
| 单个矩形 | 有跳变 | sinc¹ | 1/f |
| 矩形卷积一次 | 三角形,连续、一阶导跳变 | sinc² | 1/f² |
| 再卷积一次 | 分段抛物线,二阶导跳变 | sinc³ | 1/f³ |
| 继续卷积 k 次 | k 重卷积,k-1 阶导跳变 | sinc^k | 1/f^k |
配一条粗糙但极其好用的判据:数时域信号第几阶导数出现跳变,高频就按 1/f 的几次方再高一阶衰减,谱里 sinc 的幂次就跟着涨。矩形脉冲自己就有跳变(零阶不连续),所以是 sinc 一次方;三角脉冲本身连续但折点处一阶导数跳变,所以是 sinc 二次方。这条判据反过来也能用:如果某天你拿到一个信号的谱,发现高频按 1/f³ 掉,那你基本可以断定它的二阶导数是分段常数、三阶导有跳变。
再把三角脉冲的特殊性拎出来说:它的谱是实数而且恒非负,相位是零。这不是巧合,而是因为 tri(t/T) 是偶函数。零相位看起来很美,但它带来的副作用是时域上信号关于 t=0 对称,也就是非因果的。真实滤波器做不到零相位,只能做到线性相位,也就是整体延迟一个常数,谱上表现为乘一个 e^{-jωt₀},幅度不变、相位线性。这一点在 FIR 滤波器设计里天天用到。
最后补一个我自己踩过的记忆错误。三角脉冲的零点位置是 f=k/T,不是 f=k/(2T)。那个底宽 2T 容易让人误以为周期是 2T,其实参数 T 是半宽,整个支撑区间是 (-T, T),长度 2T,但零点间距仍然是 1/T。这个点我在考试和实测里各错过一次,后来我把参数 T 在笔记里直接标注成"半宽",才算彻底记住。
3.4 三角脉冲在工程里的实际出现场景
三角脉冲不只是个习题形状。最直接的用途是作为窗函数——Bartlett 窗就是三角窗,它的频谱就是 sinc 平方的形式,第一旁瓣大约比主瓣低 26 分贝,而矩形窗只有 13 分贝左右。代价是主瓣宽度是矩形窗的两倍。这个取舍很典型:你在做频谱分析时,如果信号里有个强分量和一个弱分量挨得很近,用矩形窗会把弱分量淹没在旁瓣里,换成三角窗或者汉宁窗就能救回来。
另一个场景是相关运算。一个矩形脉冲和自己的相关函数,形状恰好是三角形。所以你在脉冲压缩雷达、扩频通信里看到的那个三角形状的匹配滤波输出,本质就是自相关。这也解释了为什么匹配滤波器的输出旁瓣结构可以用 sinc 平方来描述——它和三角脉冲是同一套数学。
还有一处容易被忽略:对信号做一次滑动平均,就是在和矩形窗卷积。滑动平均两次,等效于和三角窗卷积。这就是为什么两级滑动平均滤波器的频率响应比单级衰减得快得多,高频抑制效果立竿见影。我在处理称重传感器的数据时用的就是这套:单级平均只能压掉 13 分贝左右的噪声,两级串联就直接跳到 26 分贝以上,成本为零,只是多了一行代码。
4. 三个变换各自的地盘与协作方式
4.1 稳态与瞬态:傅里叶和拉普拉斯的分界线
傅里叶变换描述的是稳态行为。它假定信号从负无穷一直存在到正无穷,没有任何"开始"和"结束"。当你在看稳态频率响应、计算功率谱密度、设计调制方案时,用它最合适。
拉普拉斯变换描述的是从某个起点开始的行为。它天生带着初值条件,做微分方程时,把初始条件直接代进去,齐次解和特解一起出来。电路里开关闭合的那一瞬间、机械系统受到冲击载荷的那一下、控制回路刚上电的过渡过程,都属于这一类。
所以我的判断标准很直白:如果我要看系统在正常工作状态下的频率选择性,用傅里叶;如果我要看它开机那几百毫秒会不会过冲、会不会振铃,用拉普拉斯。这两件事在数学上并不冲突,前者是后者在 σ=0 时的特例,前提是虚轴落在收敛域内。一旦信号本身在普通意义下不可变换(比如阶跃),你就只能在广义函数意义下谈傅里叶,或者干脆退回拉普拉斯。
4.2 差分方程与系统函数:z 变换的主场
离散侧的情况更清楚。任何能用线性常系数差分方程描述的系统,z 变换都能把它变成代数式。取 z 变换、移项、整理成 Y(z)/X(z),就是系统函数 H(z)。极点位置决定稳定性,零点位置决定频率选择性,单位圆上的取值就是频率响应,把这些点连起来的几何方法(向量法)甚至能在不算数值的情况下估出幅频曲线的形状。
我做数字滤波器时的固定流程是:先定 H(z) 的零极点位置,然后用向量法大致估一下频响形状,接着用工具算出精确曲线,最后跑一遍量化误差仿真。中间任何一个环节发现问题,回头调零极点,也还是在 z 域里操作。整个过程基本不需要回到时域,这就是 z 变换的价值。
顺带说一句,连续侧的传递函数 H(s) 和离散侧的 H(z) 在设计流程上是对位的,只不过一个用左半平面判稳定,一个用单位圆判稳定。如果你已经熟悉了 H(s) 那一套,学 H(z) 只需要把"左半平面"这个词替换成"单位圆内",其余直觉全部可以平移过来。
4.3 一张表看清三者怎么选
| 维度 | 傅里叶变换 X(jω) | 拉普拉斯变换 X(s) | z 变换 X(z) |
|---|---|---|---|
| 时间域 | 连续 | 连续 | 离散 |
| 变换变量 | jω(只在虚轴上) | s=σ+jω(整个平面) | z=re^{jω}(整个平面) |
| 存在条件 | 绝对可积或有限能量 | 存在非空收敛域 | 存在非空收敛域 |
| 与后者的关系 | — | 虚轴落在收敛域内时的特例 | 单位圆落在收敛域内时的特例 |
| 稳定判据 | 冲激响应绝对可积 | 极点全在左半平面 | 极点全在单位圆内 |
| 典型用途 | 频谱分析、调制、功率谱 | 微分方程、瞬态、电路、控制系统 | 差分方程、数字滤波、递推算法 |
| 常见陷阱 | 阶跃等信号不收敛 | 同一表达式对应不同信号 | 混叠、映射多值性 |
一个可以随身带的记忆方式:拉普拉斯和 z 变换都是傅里叶变换的"加压版本",多出来的那个实部 σ 或者模长 r,就是给信号加的一个指数权重,让它能被积分或求和吃下去。等你把 σ 或者 ln r 调成零,加压版本立刻退化成傅里叶版本。所以真正的基础只有一个——傅里叶变换的投影思想,另外两个只是加了收敛因子的推广。
5. 这些坑我都踩过,说一下排查思路
5.1 只看幅度谱,把相位扔了
我做过一个全通均衡器,幅频曲线是完美的水平线,看曲线报告的人都说没问题。实际跑起来波形完全变了样,一个方波进去,出来是一串抖动的振荡。原因就是相位。幅度谱只告诉你"每个频率分量被放大还是缩小了",相位谱告诉你要车"每个分量被延迟了多少"。全通系统幅度不动,但相位随频率剧烈变化,不同频率分量被延迟的时间不一样,叠起来当然变形。
排查这种问题的正确做法是把群延迟也画出来。群延迟是相位对 ω 的负导数,物理意义是"这一小段频率整体被延迟了多久"。理想的线性相位系统,群延迟是常数;群延迟有峰的地方,就是波形失真的位置。我现在的习惯是,只要涉及到波形保真,幅度和群延迟两张图一起看,一张都不省。顺带说,三角脉冲那种零相位的谱只有在时域关于原点对称时才能出现,那是非因果的,真实系统打不到,只能靠延迟逼近。
5.2 同一个表达式,两个完全不同的信号
前面已经提过 1/(s+a) 的例子,这里说具体排查过程。当时我在做一个衰减振荡的建模,拟合出来的 X(s) 有一对共轭极点,实部为负。我用工具做反变换,得到的时域曲线是一个右边长的衰减振荡,看起来挺合理。但把实测数据的开头和结尾比对一下发现,实测数据在时间轴负半轴是有值的、正的,而反变换结果在负半轴是零。
问题就在收敛域。我拟合的时候只对了幅度,没约束收敛域类型,工具默认取了因果解。实际上那个信号是双边的。改的方法是:把信号在时间轴上的支撑范围明确告诉求解流程,双边的就取条带型收敛域,然后重新做反变换。这件事给我的教训是,反变换前一定要问自己一句"这个信号在时间轴上是哪一段有值",答案直接决定收敛域的取法,取错了表达式再漂亮也是错的。
5.3 把 s 平面极点直接搬到 z 平面
这个坑在从模拟滤波器设计数字滤波器时最常出现。z=e^{sT} 是对的,但直接把模拟极点的模长和幅角按这条公式算出来,往往会发现数字滤波器的响应和原型对不上。原因有几个层次:
第一层,采样保持电路、零阶保持器的等效传递函数不是 1,它本身带一个 sinc 形状的衰减和相位滞后。第二层,数字频率 ω_d 和模拟频率 ω_a 的关系不是简单的 ω_a = ω_d/T,如果你用的是双线性变换,实际关系是 ω_a = (2/T)tan(ω_d T/2),这个正切函数会把高频压缩,也就是所谓的频率预畸变。第三层,冲激响应不变法本身有混叠,高频分量折叠回来,只能用于带限原型。
这三层加起来,结论就变成了:冲激响应不变法保留时域形状但会混叠,双线性变换没有混叠但频率轴被扭曲了,需要预先畸变。我在做音频滤波器的时候吃过这个亏,按双线性变换设的截止频率,实测比设计值高了一截,后来按 ω_a=(2/T)tan(ω_d T/2) 把预畸变加回去才对上。所以凡是看到设计值和实测值在高频端系统性偏离,先想预畸变,别急着怀疑代码。
5.4 用 FFT 验证连续谱时的归一化陷阱
最后一个坑是数值验证上的。连续谱的理论值是 X(f)=T·sinc²(fT),而你用 FFT 算出来的是离散和。要把两者对齐,有两点必须做对:求和要乘采样间隔 1/fs 来近似积分;时间序列的中心要对齐到索引 0,不然频谱里会多一个线性相位。
另外还有一个心理上的坑:不要用相对误差去判断高频段的吻合程度。sinc² 在高频会掉到很小,绝对值只有 1e-4 量级,相对误差看起来可能是百分之几十,但绝对误差小得可以忽略。我自己第一次做这个验证时,看到相对误差 0.3 就以为公式推错了,折腾了半天才发现是除法把一个微不足道的差值放大了。正确的做法是看绝对误差,并且和峰值 T 做比较。
6. 我平时用的手算加验证流程
6.1 先用符号计算把结果整理成标准形式
手推一遍是必须的,但推完以后我习惯用符号计算工具核对,并且要求它输出"整理后的闭式",而不是那种带一堆指数和多项式的原始结果。以三角脉冲为例,我会把积分拆成左右两段,避开绝对值符号带来的分支问题:
import sympy as sp t, w, T = sp.symbols('t omega T', positive=True, real=True) left = sp.integrate((1 + t / T) * sp.exp(-sp.I * w * t), (t, -T, 0)) right = sp.integrate((1 - t / T) * sp.exp(-sp.I * w * t), (t, 0, T)) X = sp.simplify(sp.expand_complex(left + right)) model = T * (sp.sin(w * T / 2) / (w * T / 2)) ** 2 print(sp.simplify(X - model)) # 期望输出 0,说明闭式与手算一致最后那行打印出 0,就说明我手推的那个 sinc 平方形式是对的。这个做法比直接对含绝对值的表达式积分要稳,我试过让符号工具直接处理 Abs,有时候它会卡很久或者给出带分段条件的结果,反而不好读。
6.2 再用数值做一次独立的交叉验证
符号计算和手推用的是同一套代数逻辑,万一逻辑本身错了,两边会一起错。所以我会再做一次纯数值验证,用 FFT 算一遍,和解析式逐点比:
import numpy as np T, fs = 1.0, 200.0 n = np.arange(-400, 401) # 覆盖 |t| <= 2,三角脉冲在此之外严格为零 t = n / fs x = np.where(np.abs(t) <= T, 1 - np.abs(t) / T, 0.0) N = 8192 X = np.fft.fftshift(np.fft.fft(np.fft.ifftshift(x), N)) / fs f = np.fft.fftshift(np.fft.fftfreq(N, d=1 / fs)) model = T * np.sinc(f * T) ** 2 # np.sinc(x) 的定义是 sin(pi*x)/(pi*x) print(np.max(np.abs(X - model))) # 绝对误差,期望在 1e-3 量级这里有两个细节值得强调。一是np.sinc用的就是 sin(πx)/(πx) 这个约定,所以配 T·sinc²(fT) 是自洽的,如果你换成别的语言的库,第一件事是去查它 sinc 的定义。二是ifftshift和fftshift这一对不能省,它们负责把数组的零点挪到正确位置,省掉的话频谱里会凭空多出一个随频率线性增长的相位,幅度看不出来,相位一看就露馅。
跑出来绝对误差落在 1e-3 量级就说明对了。如果误差大得多,我会按这个顺序排查:时间网格是不是覆盖到了脉冲之外并留了余量、采样率是不是够、归一化是不是漏了除以 fs、中心对齐是不是做错了。这四个检查点覆盖了我遇到过的绝大多数问题。这里还有一个值得说的小点:因为三角脉冲在 |t|>T 之后严格为零,只要时间窗取得够宽,截断误差就是零,剩下的误差全部来自离散化本身,所以把采样率再提高一档,误差应该明显下降。如果不下降,那说明问题不在离散化,而在公式或者归一化上——这个现象本身就是一个很好的诊断信号。
6.3 几个能省时间的小习惯
第一个习惯是维量纲检查。任何频谱结果的峰值都应该等于时域信号的面积。三角脉冲面积是 T,谱峰值也是 T,对上了;矩形脉冲面积是 2T(如果用 rect(t/T) 的定义面积是 T),谱峰值也应该是对应的面积。这个检查只要三秒钟,但能拦下大量系数错误。
第二个习惯是零点位置反查。拿到一个谱,先看零点,再看主瓣宽度。如果零点间距和时域支撑长度的倒数对不上,说明参数理解错了。三角脉冲的零点间距是 1/T,矩形脉冲的零点间距也是 1/T,后者更好记,可以当作基准去核对前者。
第三个习惯是衰减阶数反查。把谱画在对数坐标上,看高频段的斜率。掉 20 分贝每十倍频程对应 1/f,40 分贝对应 1/f²,60 分贝对应 1/f³。三角脉冲应该是 40 分贝每十倍频程。这个斜率和"时域第几阶导数跳变"是一一对应的,是我用来快速自检的最后一关。如果斜率不对,前面的推导一定有地方出了问题,而且问题大概率出在时域形状的理解上,而不是积分过程。
这三个习惯配合起来用,基本上不需要回头翻书,就能判断一次推导是否靠谱。等你把三角脉冲这一条通道走顺了,像梯形脉冲、升余弦脉冲、更一般的 B 样条形状,都能用同一套逻辑快速拿下——先看时域连续性阶数,再定频域衰减阶数,最后用卷积链把具体的 sinc 幂次对出来。