ARTICLE DETAIL

建站实战干货

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

连续周期信号傅里叶级数详解:Python验证与工程实践

2026/9/5 12:34:05 拓冰建站 浏览量
连续周期信号傅里叶级数详解:Python验证与工程实践 连续周期信号的傅里叶级数是信号从时域进入频域的第一个门槛。很多教材会在这一章给出积分公式、正交条件和吉布斯现象但读者真正动手时最容易卡住的反而不是公式本身而是系数定义不统一、时间轴采样方式不对、三角形式与复指数形式换算错位这几个工程细节。这篇文章以周期方波为样例把连续周期信号傅里叶级数的概念、系数计算、信号合成、验证方法和排错路径完整走一遍重点说明每一步背后的原因以及如何在代码里确认结果是对的。1. 连续周期信号为什么需要傅里叶级数它到底分解出了什么1.1 从“函数表示”到“频率成分”连续周期信号是指满足[ x(tT_0)x(t) ]的信号其中 (T_0) 是最小正周期。常见的正弦波、方波、锯齿波、三角波都是周期信号的典型例子。时域描述能告诉我们信号在某个时刻的取值却很难告诉我们这个信号由哪些“基础分量”叠加而成。实际工程里滤波、去噪、调制解调、故障特征提取几乎都依赖频率信息。傅里叶级数的作用就是把一个连续周期信号拆分成的直流分量、基波分量和各次谐波分量。这里要区分一个概念傅里叶级数不是“把一个信号变短”也不是“压缩数据”而是把信号从“时间坐标系”切换到“频率坐标系”。同一个信号在时域是一条曲线在频域是一组谱线。连续周期信号的频谱是离散谱只在基波频率的整数倍处有值这是它和非周期信号最直观的区别。1.2 周期、基波与谐波三角展开的完整定义设周期信号 (x(t)) 的周期为 (T_0)则基波频率为[ f_0\frac{1}{T_0} ]角频率为[ \omega_02\pi f_0\frac{2\pi}{T_0} ]三角形式的傅里叶级数可以写成[ x(t)\frac{a_0}{2}\sum_{n1}^{\infty}\left[a_n\cos(n\omega_0t)b_n\sin(n\omega_0t)\right] ]其中系数为[ a_0\frac{2}{T_0}\int_{T_0}x(t)dt ][ a_n\frac{2}{T_0}\int_{T_0}x(t)\cos(n\omega_0t)dt ][ b_n\frac{2}{T_0}\int_{T_0}x(t)\sin(n\omega_0t)dt ]这里的积分区间可以取 ([0,T_0])也可以取 ([-T_0/2,T_0/2])。由于被积函数是周期信号只要积分区间长度正好是一个周期起点取在哪里不影响积分结果。工程里最容易混淆的是 (a_0) 的定义。有些教材把 (a_0) 定义为信号在一个周期内的平均值也就是直流分量本身上面的定义里直流分量是 (a_0/2)。这两种定义差一个系数。如果看书时没有注意公式上下文写代码时就可能出现结果偏大两倍或者偏小两倍。1.3 复指数形式、离散谱与吉布斯现象三角形式直观但涉及两个系数序列。做理论推导和数值计算时复指数形式更紧凑[ x(t)\sum_{n-\infty}^{\infty}c_ne^{jn\omega_0t} ][ c_n\frac{1}{T_0}\int_{T_0}x(t)e^{-jn\omega_0t}dt ]三角系数和复指数系数之间可以互相换算系数与三角系数关系(c_0)(a_0/2)即直流分量(c_n, n0)((a_n-jb_n)/2)(c_n, n0)((a_{复指数形式会同时出现正频率和负频率这是数学表达上的对称性不是真实存在“负频率”的物理信号。画单边频谱时需要把正频率对应的复幅度乘以 2才能得到对应正弦或余弦分量的实际幅度。还要理解吉布斯现象。连续周期信号在跳变点处的傅里叶级数不是均匀收敛的即使展开项数增加到很大间断点附近也会出现固定比例的过冲大约为跳变幅度的 9%。项数越大过冲越靠近间断点但过冲峰值不会消失。这不是计算错误而是傅里叶级数本身在间断点邻域的收敛特性。2. 用 Python 搭建最小验证环境先跑出一个周期方波2.1 实验思路与依赖环境验证傅里叶级数只用纸面推导不够因为系数积分、信号合成、能量校验都必须落到具体数值上。用 Python 做这个实验需要三个库NumPy 负责数值计算Matplotlib 负责绘图SciPy 在需要 FFT 交叉验证时使用。pip install numpy matplotlib scipy如果使用虚拟环境推荐先创建独立目录python -m venv fs_demo source fs_demo/bin/activate pip install numpy matplotlib scipyWindows 下激活命令是fs_demo\Scripts\activate。本文示例基于 Python 3.10 以上版本NumPy 1.21 以上、Matplotlib 3.5 以上均可运行。傅里叶级数实验对版本不敏感关键是采样点设置要正确。实验的主线可以拆成四个验证目标手写系数积分的结果是否接近解析解。用有限项合成信号后波形是否逼近原信号。时域功率和频域功率是否满足帕塞瓦尔定理。间断点附近是否出现吉布斯现象。这四个目标全部通过才能说明对这一部分的理解没有系统性偏差。2.2 生成周期方波信号为了后续能和解析系数对照选用占空比为 50% 的周期方波。设周期 (T2) 秒基波频率 (f_00.5) Hz角频率 (\omega_0\pi) rad/s。方波在一个周期前半段为 1后半段为 -1。import numpy as np import matplotlib.pyplot as plt # 周期 T 2.0 # 一个周期内的采样点数 M 4096 # 时间轴从 0 到 T但不包含 T t np.linspace(0.0, T, M, endpointFalse) def square_wave(t, T): 占空比 50% 的周期方波取值范围 -1 到 1。 phase t % T return np.where(phase T / 2.0, 1.0, -1.0) x square_wave(t, T)这段代码里最关键的是endpointFalse。傅里叶级数积分要求区间长度正好是一个周期。如果写成np.linspace(0, T, M)最后一个点会重复采样时刻 (T)而周期边界上 (x(T)x(0))相当于多计算了一次端点。虽然采样点数量很大时影响很小但从一开始就养成“周期采样不含终点”的习惯后续和 FFT 对照也会更顺畅。绘图时不要只画一个周期建议画两个周期观察周期性t_plot np.linspace(0.0, 2 * T, 2 * M, endpointFalse) fig, ax plt.subplots(figsize(8, 3)) ax.plot(t_plot, square_wave(t_plot, T), linewidth1) ax.set_xlabel(time / s) ax.set_ylabel(x(t)) ax.set_xlim(0, 2 * T) ax.set_title(Periodic Square Wave) plt.show()运行后应看到幅度在 -1 和 1 之间切换的方波一个完整周期内高低电平各占一半。2.3 采样时间与积分近似的对应关系把傅里叶系数积分改成数值计算时本质上是用离散采样点近似连续积分。因为这里处理的信号是连续周期信号数值积分只要求足够密即可。M 取 4096 是相对保守的选择既能保证积分精度速度也非常快。需要注意时间轴的单位和角频率单位。代码里周期 T 的单位是秒所以omega0 2.0 * np.pi / T的单位是 rad/s。如果直接写成omega0 2.0 * np.pi角频率就错了前几个谐波会对应错误频率位置。另一个常见误解是认为采样点越多傅里叶级数就能展开到越多项。实际上一个周期内采样 M 个点后可表示的最高谐波次数受奈奎斯特频率约束。M4096 对展开几十个谐波绰绰有余但展开到 2000 项以上时数值结果会退化。学习阶段的检查点是先看时域图是否正确再打印一次周期内的平均值。占空比 50% 的对称方波平均值为 0所以后续理论上的 (a_0) 应当接近 0。3. 手写傅里叶级数系数计算与信号合成3.1 系数积分的矩阵化写法用循环逐个计算 (a_n) 和 (b_n)思路直观但代码较长。这里采用 NumPy 的广播机制把一个周期内所有采样点上的正弦、余弦基函数一次性构造出来再和信号做逐元素相乘后求和。def fs_coeffs(signal, t, T, n_max): 计算三角形式傅里叶系数。 参数 ---- signal : 一个完整周期内的采样值 t : 对应时间轴 T : 信号周期 n_max : 最高谐波次数 返回 a0, a, b a0 对应三角公式中的 a0合成时直流分量为 a0/2 dt t[1] - t[0] omega0 2.0 * np.pi / T n np.arange(1, n_max 1) # cos_base.shape (n_max, M) cos_base np.cos(np.outer(n, omega0 * t)) sin_base np.sin(np.outer(n, omega0 * t)) # 对每个基函数做一整个周期的积分 a (2.0 / T) * np.sum(signal * cos_base, axis1) * dt b (2.0 / T) * np.sum(signal * sin_base, axis1) * dt # a0 (2/T) * integral(signal dt) a0 2.0 * np.mean(signal) return a0, a, b对于占空比 50% 的对称方波理论上只有奇数阶正弦项即[ b_n \begin{cases} \dfrac{4}{n\pi}, n\text{为奇数}\ 0, n\text{为偶数} \end{cases} ]而所有 (a_n) 都应该接近 0。拿解析解检查数值结果是判断代码是否正确的最快方式。a0, a, b fs_coeffs(x, t, T, 50) print(a0 , a0) for i in range(5): n i 1 exact 4.0 / (n * np.pi) if n %