
1. 连续周期信号与傅里叶级数的核心问题做信号处理、通信系统或者嵌入式算法开发的人迟早会遇到这样一个问题拿到一段连续周期信号比如一个方波、一个锯齿波或者调幅后的正弦波你在示波器上看到的只是随时间起伏的波形。但实际工程里我们经常想知道另一个维度的问题——这个信号里面到底包含了哪些频率成分每个频率成分的幅度有多大相位关系是怎样的傅里叶级数Fourier Series就是专门回答这个问题的一套数学工具。它把一个满足条件的连续周期信号分解成一系列频率为基波整数倍的正弦波和余弦波的加权和。换句话说时域里看似复杂的周期波形在频域里可能只是几条离散的谱线。这个思想贯穿了从模拟信号分析到数字信号处理再到通信系统设计的整条技术链路。但很多人学傅里叶级数时容易陷入一个误区把精力全花在背诵公式和计算积分上算完系数就结束完全不知道这个结果在工程里意味着什么。还有人混淆了周期信号傅里叶级数与傅里叶变换的关系在论文或项目汇报里写错公式结果在频谱分析、功率估计、谐波抑制等问题上绕了很大弯路。这篇文章不打算把傅里叶级数的所有数学细节铺开而是想从“为什么要引入它”“它的数学结构是什么”“工程里怎么计算和验证”“哪些地方最容易出错”四个角度把连续周期信号的傅里叶级数讲成一条完整的链路。读完你至少能解决三个问题第一理解傅里叶级数展开式的每一项到底代表什么三角形式与指数形式如何等价互换第二能够用代码对一个具体周期信号完成从系数计算、频谱绘制到时域重建的完整流程第三遇到频谱结果异常时知道从哪些方向排查。2. 傅里叶级数的基本概念与直觉理解2.1 用“坐标分解”理解频域思想先看一个直观的例子。假设你是一个音频工程师听到一段由小提琴和长笛同时演奏的和弦声人耳之所以能分辨出两种乐器根本原因在于不同乐器的音色差异体现在谐波成分的分布上。如果把整个和弦的声音看成一个复合信号那么它内部其实是多个频率成分叠加的结果。傅里叶级数做的事情本质上就是把这个复合信号“拆开”。它告诉你任意一个满足条件的周期信号都可以表示成一系列正交的三角函数之和。这个“正交”概念是理解整个傅里叶理论的关键。两个函数在区间 ([t_0, t_0T]) 上正交通俗地讲就是它们相乘后在周期内积分等于零。三角函数族恰好满足这个性质(\cos(n\omega_0 t)) 与 (\cos(m\omega_0 t))(m \neq n)正交。(\sin(n\omega_0 t)) 与 (\sin(m\omega_0 t))(m \neq n)正交。正弦函数与余弦函数任意阶之间正交。这意味着三角函数族可以构成一个函数空间里的“正交坐标系”而傅里叶系数就是这个坐标系下的“坐标”。求系数的过程本质上就是求信号在每一个基函数方向上的投影。2.2 三角形式的傅里叶级数设 (x(t)) 是周期为 (T) 的连续周期信号基波角频率为 (\omega_0 2\pi/T)。在满足一定条件时它可以展开为三角形式的傅里叶级数[ x(t) a_0 \sum_{n1}^{\infty} \left[ a_n \cos(n\omega_0 t) b_n \sin(n\omega_0 t) \right] ]其中直流分量 (a_0) 是信号在一个周期内的平均值。因为三角函数的积分特性所有交流项在一个完整周期内积分为零所以对等式两边直接在一个周期内积分就能得到[ a_0 \frac{1}{T} \int_{t_0}^{t_0T} x(t) dt ]利用正交性对等式两边分别乘以 (\cos(n\omega_0 t)) 或 (\sin(n\omega_0 t)) 再做积分可以得到[ a_n \frac{2}{T} \int_{t_0}^{t_0T} x(t) \cos(n\omega_0 t) dt ][ b_n \frac{2}{T} \int_{t_0}^{t_0T} x(t) \sin(n\omega_0 t) dt ]这里需要注意三点(a_0) 的系数是 (1/T)而 (a_n) 和 (b_n) 的系数是 (2/T)这个差异常有人忘记导致直流分量算错。公式中用到的 (\omega_0) 是基波角频率单位是弧度每秒和频率 (f_0) 的关系是 (\omega_0 2\pi f_0)。展开式中经常把直流分量单独写成 (\frac{a_0}{2}) 的形式这在不同的教材里约定不一致阅读和使用时必须看清上下文。2.3 指数形式的傅里叶级数工程计算更喜欢它三角形式意义直观每一项都对应一个真实的余弦或正弦分量。但在工程推导和计算里指数形式往往更方便因为指数运算的乘法规则远比三角函数积化和差简单。借助欧拉公式[ \cos(n\omega_0 t) \frac{e^{jn\omega_0 t} e^{-jn\omega_0 t}}{2}, \qquad \sin(n\omega_0 t) \frac{e^{jn\omega_0 t} - e^{-jn\omega_0 t}}{2j} ]可以把三角形式改写为指数形式[ x(t) \sum_{n-\infty}^{\infty} C_n e^{jn\omega_0 t} ]其中系数为[ C_n \frac{1}{T} \int_{t_0}^{t_0T} x(t) e^{-jn\omega_0 t} dt ]这里能看到为什么工程上更喜欢指数形式不需要分别计算正弦和余弦两组系数而且从 (n-\infty) 到 (n\infty) 的求和符号非常简洁推导傅里叶变换性质时也更统一。2.4 两组系数的对应关系三角形式与指数形式之间并不是简单的相等关系。指数形式中的 (C_n) 是一个复数它同时包含了幅度和相位信息。其对应关系如下[ C_n \frac{a_n - jb_n}{2}, \qquad C_{-n} \frac{a_n jb_n}{2} C_n^* ][ C_0 a_0 ]对于实信号(C_n) 与 (C_{-n}) 是共轭对称的。因此工程上画幅度谱时经常只看正频率部分然后把负频率的幅度对应叠加到正频率上。再进一步如果把第 (n) 次谐波合并成单一的余弦项[ a_n \cos(n\omega_0 t) b_n \sin(n\omega_0 t) A_n \cos(n\omega_0 t \varphi_n) ]则[ A_n \sqrt{a_n^2 b_n^2}, \qquad \varphi_n -\arctan\left(\frac{b_n}{a_n}\right) ]这里尤其要注意 (b_n) 的符号位置以及 (\arctan) 需要根据 (a_n) 和 (b_n) 的具体象限来判断不能只靠计算器默认的反正切值。2.5 实偶信号与实奇信号的规律实际工程中大量信号具有对称性利用对称性可以大幅减少计算量。如果一个信号是实偶信号即 (x(t) x(-t))那么所有 (b_n 0)展开式中只包含余弦项。典型例子是全波整流信号。如果一个信号是实奇信号即 (x(t) -x(-t))那么所有 (a_n 0)包括 (a_0 0)展开式中只包含正弦项。典型例子是标准方波峰谷对称时。这个规律不仅能检验计算是否正确还有实际作用在电路谐波分析中偶对称的周期信号不会产生正弦项这说明它不包含某些特定类型的谐波失真。2.6 存在条件与收敛性不是所有周期信号都能进行傅里叶级数展开。数学上比较严格的条件是 Dirichlet 条件工程上通常理解为以下几点信号在一个周期内绝对可积。在一个周期内只有有限个极值点。在一个周期内只有有限个不连续点且每个不连续点的左极限和右极限都存在。满足 Dirichlet 条件的周期信号傅里叶级数在连续点收敛于原始信号值在不连续点收敛于左右极限的平均值。这意味着如果你用有限项级数去重建一个方波在跳变沿附近一定会出现振荡这个现象就是著名的 Gibbs 现象。我后面会专门演示它。3. 典型周期信号的傅里叶级数与频谱含义3.1 方波的频谱考虑一个周期为 (T)、幅度为 1、占空比为 50% 的奇对称方波。利用奇对称性可直接消掉所有余弦项。计算正弦项系数后展开式可以写成[ x(t) \frac{4}{\pi} \left[ \sin(\omega_0 t) \frac{1}{3}\sin(3\omega_0 t) \frac{1}{5}\sin(5\omega_0 t) \cdots \right] \frac{4}{\pi} \sum_{k1}^{\infty} \frac{1}{2k-1} \sin\left((2k-1)\omega_0 t\right) ]这个表达式非常重要体现出两个关键事实。第一方波只包含奇次谐波偶次谐波的幅度严格为零。第二谐波幅度按 (1/n) 的速度衰减。这意味着方波的能量集中在低频部分但高频分量衰减较慢所以它的边缘很陡峭。如果方波的占空比不是 50%那频谱中就会出现偶次谐波这也是功率变换器设计中通过调整 PWM 占空比来改变谐波分布的理论基础。3.2 锯齿波的收敛速度问题再看一个周期锯齿波对峰值为 1、周期为 T 的三角波或锯齿波展开同样含有丰富的谐波幅度一般按 (1/n^2) 或 (1/n) 的速度衰减具体取决于波形是否连续。工程师关心收敛速度是因为它直接决定“你要截断到多少次谐波才能达到足够的重建精度”。波形越光滑谐波衰减越快。信号中存在不连续跳变频谱必然拖出长长的“尾巴”。这种高次谐波衰减速度的分析在电路设计里也对应着电磁干扰问题——陡峭的开关波形之所以会带来宽带噪声正是因为其傅里叶级数衰减速度慢。3.3 频谱的含义对于一个连续周期信号其傅里叶级数对应的频谱是离散谱。横坐标使用谐波次数 (n) 或频率 (n f_0)纵坐标使用幅度 (|C_n|) 或功率 (|C_n|^2)。每一条谱线对应时域中的一个正弦分量。在通信系统里周期性本身意味着频谱离散化。周期越大基波频率越小谱线越密当周期趋于无穷大时离散谱趋于连续谱傅里叶级数也自然过渡为傅里叶变换。这个理解是后面学习采样定理、混叠和加窗的基础。3.4 功率与傅里叶系数Parseval 定理工程中经常需要估计信号功率或者在若干次谐波中判断主要能量集中在哪个频段。这时需要用到周期信号傅里叶级数的一个重要能量关系即 Parseval 定理[ P \frac{1}{T} \int_{t_0}^{t_0T} |x(t)|^2 dt \sum_{n-\infty}^{\infty} |C_n|^2 a_0^2 \frac{1}{2} \sum_{n1}^{\infty} (a_n^2 b_n^2) ]这组公式的含义是时域中一个周期内的平均功率等于频域中各次谐波功率之和。因此如果你想评估某个方波信号在 10 次谐波以下集中了多少能量只需要把前 10 个系数的平方相加再除以总功率即可。这在滤波器设计和电能质量分析中经常用到。4. 环境准备与工具选择傅里叶级数的核心是积分运算和频谱可视化。手工计算适合简单波形和理论学习工程验证一般有两条主流工具路线。第一是 MATLAB它的信号处理工具箱完善fourier符号计算函数、fft数值计算函数都很成熟画频谱图方便传统通信与电子信息领域用得最多。第二是 Python使用numpy、scipy、sympy和matplotlib可以完整实现系数计算、数值积分、符号积分和绘图。对做算法或开源生态的人更友好。本文示例统一使用 Python因为 Python 生态对初学信号处理的人更透明每一步都可以看到数据结构的变化。建议环境如下Python 3.8 或更高版本。numpy用于数值积分和数组运算。scipy提供积分函数quad处理数值积分更方便。matplotlib用于绘制原始信号、频谱和重建信号。sympy用于展示符号推导过程对某些不连续函数可能不够稳后面会说明替代方案。安装命令为pip install numpy scipy matplotlib sympy版本不要求最新能正常导入即可。这篇文章的核心流程是“定义一个周期信号 - 计算傅里叶系数 - 重建信号并对比”不依赖具体版本特性。5. 傅里叶级数完整计算流程与 Python 代码实现5.1 整体流程设计一个典型的“从时域到频域再回到时域”的验证流程包含以下几步定义信号及其周期。判断信号的奇偶性决定需要计算的系数类型。用数值积分计算傅里叶系数。根据系数重建有限项级数近似信号。绘制原始信号、频谱图和重建信号对比验证。下面用一个幅度为 1、周期 (T2)、基波频率 (\omega_0\pi) 的奇对称方波作为示例。5.2 用 numpy 与 scipy 计算三角形式系数# 文件路径fourier_series_demo.py import numpy as np from scipy.integrate import quad import matplotlib.pyplot as plt # 方波定义周期 T2[-1, 1] 区间内 # x(t) 1 当 0 t 1 # x(t) -1 当 -1 t 0 def square_wave(t): t_mod np.mod(t 1, 2) - 1 # 映射到 [-1, 1) return np.where(t_mod 0, 1.0, -1.0) T 2.0 w0 2 * np.pi / T # 基波角频率 pi def compute_fourier_coeffs(func, T, N, t00): 计算三角形式傅里叶系数 a0, a_n, b_n func: 信号函数要求支持标量和数组输入 T : 周期 N : 最高谐波次数 t0 : 积分起点一般取 0 a0 (1 / T) * quad(lambda t: func(t), t0, t0 T)[0] a_n np.zeros(N 1) b_n np.zeros(N 1) for n in range(1, N 1): a_n[n], _ quad(lambda t: func(t) * np.cos(n * w0 * t), t0, t0 T) a_n[n] * 2.0 / T b_n[n], _ quad(lambda t: func(t) * np.sin(n * w0 * t), t0, t0 T) b_n[n] * 2.0 / T return a0, a_n, b_n N 50 a0, a_n, b_n compute_fourier_coeffs(square_wave, T, N) print(fa0 {a0:.4f}) print(前10个 a_n:, np.round(a_n[1:11], 4)) print(前10个 b_n:, np.round(b_n[1:11], 4))运行这段代码你会看到 (a0) 和所有 (a_n) 都接近 0而奇数 (b_n) 约等于 (4/(n\pi))偶数 (b_n) 接近 0。这正是方波解析解的数值体现。需要特别说明的是compute_fourier_coeffs里对 (\cos) 和 (\sin) 项都乘了 (2/T)这是三角形式的标准约定。如果你要将其转成指数形式需要再处理复数域的系数组合工程中也很常用。5.3 指数形式系数的等价计算很多频谱绘制的工具默认使用指数形式的系数。指数形式与三角形式的关系是 (C_n (a_n - j b_n)/2)注意 (C_0 a_0)不乘二分之一。可以这样计算def compute_exponential_coeffs(func, T, N, t00): 计算指数形式傅里叶系数 C_nn 从 -N 到 N C np.zeros(2 * N 1, dtypecomplex) for idx, n in enumerate(range(-N, N 1)): C[idx], _ quad(lambda t: func(t) * np.exp(-1j * n * w0 * t), t0, t0 T) C[idx] * 1.0 / T return C C compute_exponential_coeffs(square_wave, T, N) # 打印中间的几项 for n in [-5, -3, -1, 0, 1, 3, 5]: idx n N print(fC_{n:d} {C[idx].real:.4f} {C[idx].imag:.4f}j)从输出可以看到 (C_1, C_3, C_5) 是纯虚数且为负值(C_{-1}, C_{-3}, C_{-5}) 是它们的共轭。这对实信号来说总成立是检查计算过程和理解负频率含义的一个好抓手。不过在实际的工程代码中指数形式系数通常不会用逐项quad去算而是通过 DFT/FFT 直接得到。这里逐项积分主要是为了学术演示和理解。FFT 计算的系数与本文的数值积分结果在比例关系上容易出问题我将在常见问题部分重点解释。5.4 有限项重建与 Gibbs 现象演示有了前 (N) 次谐波系数就可以重建信号的有限项近似def reconstruct_signal(func_coeffs, t, T, N): 根据三角形式系数重建信号返回 t 时刻的重建值 a0, a_n, b_n func_coeffs result np.full_like(t, a0, dtypefloat) w0 2 * np.pi / T for n in range(1, N 1): result a_n[n] * np.cos(n * w0 * t) b_n[n] * np.sin(n * w0 * t) return result # 生成密集时间轴用于绘图 t_dense np.linspace(-2, 2, 2000) x_orig square_wave(t_dense) fig, axes plt.subplots(3, 1, figsize(10, 9)) axes[0].plot(t_dense, x_orig, k--, linewidth1.2, label原始方波) for N_plot, color in zip([5, 15, 50], [steelblue, darkorange, seagreen]): coeffs compute_fourier_coeffs(square_wave, T, N_plot) x_recon reconstruct_signal(coeffs, t_dense, T, N_plot) axes[0].plot(t_dense, x_recon, linewidth1.0, labelfN{N_plot} 重建) axes[0].set_title(方波的有限项傅里叶级数重建) axes[0].set_xlabel(t) axes[0].set_ylabel(x(t)) axes[0].legend() axes[0].grid(alpha0.3) axes[0].set_xlim([-0.2, 1.2]) # 另画一张图放大跳变处观察 Gibbs 现象 axes[1].plot(t_dense, x_orig, k--, linewidth1.2, label原始方波) for N_plot, color in zip([5, 15, 50], [steelblue, darkorange, seagreen]): coeffs compute_fourier_coeffs(square_wave, T, N_plot) x_recon reconstruct_signal(coeffs, t_dense, T, N_plot) axes[1].plot(t_dense, x_recon, linewidth1.0, colorcolor, labelfN{N_plot}) axes[1].set_xlim([0.9, 1.1]) axes[1].set_ylim([0.8, 1.3]) axes[1].set_title(跳变附近放大Gibbs 现象) axes[1].grid(alpha0.3) axes[1].legend() # 绘制单边幅度谱正频率侧 n_vals np.arange(0, 31) A_n np.sqrt(a_n[n_vals]**2 b_n[n_vals]**2) # 注意 A_0 其实对应 a_0这里只画 n1 axes[2].stem(n_vals, A_n[0:31], basefmt ) axes[2].set_title(方波幅度谱三角形式前 30 次谐波) axes[2].set_xlabel(谐波次数 n) axes[2].set_ylabel($A_n$) axes[2].grid(alpha0.3) plt.tight_layout() plt.show()这段代码会输出两个关键结果。第一个是中间的时序对比图原始方波和不同截断次数的重建信号几乎重合但在跳变处保留了一个固定的约 9% 的过冲这个过冲不会随 (N) 增大而消失只是越来越靠近跳变点。第二个是右侧的幅度谱只包含奇数谱线并且按 (1/n) 衰减。5.5 用 sympy 做符号计算验证公式推导如果你希望得到某个具体波形系数的解析表达式可以用sympy做符号积分。下面用对称方波展示一个更贴近真实学习场景的例子请注意符号积分在方波上可能警告因此改用连续的三角波或者直接在区间里写分段符号函数会更好import sympy as sp t, n, T_sym sp.symbols(t n T, realTrue) # 这里为了简化定义 T2[-1,1] 内的三角波 f(t)|t|-0.5偶函数 # 三角波在跳变问题上更友好利于符号积分 n_sym sp.symbols(n, positiveTrue, integerTrue) f_tri sp.Abs(t) - sp.Rational(1, 2) # 因为它是偶函数b_n 必然是 0直接算 a_n a_n_sym sp.integrate(f_tri * sp.cos(n_sym * sp.pi * t), (t, -1, 1)) a_n_sym sp.simplify(a_n_sym) print(a_n , a_n_sym)sp.Abs(t)在符号积分里表现良好适合演示偶函数、奇函数化简和自动消去无用系数的过程。实际使用时优先用手写化简表达式代替大量符号运算否则复杂的integrate调用会很慢。符号计算的用途更适合验证公式而不是替代数值流程。6. 运行结果与效果验证以 5.4 节方波为例做几个关键检查。第一检查系数数值。运行 5.2 节代码后应当看到前几个 (b_n) 近似为(b_1 \approx 1.2732)对应 (4/\pi)(b_3 \approx 0.4244)对应 (4/(3\pi))(b_5 \approx 0.2546)对应 (4/(5\pi))偶数次 (b_2, b_4) 应非常接近 0通常数值误差在 (10^{-8}) 量级。第二检查 (a_0) 和 (a_n)。对称方波的平均值为 0所以 (a_0) 应该接近 0。所有 (a_n) 也都接近 0这符合奇对称信号的预期结果。第三检查重建信号。运行绘图代码后时序图中方波边缘会保留明显的振荡过冲。就算把 (N) 调到 500过冲幅度也仍然保持在约 9% 左右持续时间变窄但幅度不降。初学者如果发现这个现象不要以为是代码写错了这是吉布斯现象。第四检查幅度谱。幅度谱中应只在 (n1, 3, 5, 7...) 处有非零幅度且幅度值按 (1/n) 下降。谱线的横坐标是谐波次数不是物理频率。如果希望横坐标显示为频率需要自行换算 (f_n n / T)。最后可以验证 Parseval 定理。比如用数值方法分别计算时域信号一个周期内的平均功率和频域前若干次谐波功率之和误差会随截断项数的增加而变小。这个验证对理解“截断误差”的含义很有帮助如果你有兴趣可以在这个基础上把 50 次项算出的重建信号做逐点误差统计。# 时域平均功率 t_integral np.linspace(0, T, 10000) x_vals square_wave(t_integral) P_time np.mean(x_vals**2) # 频域功率三角形式 P_freq a0**2 0.5 * np.sum(a_n[1:] ** 2 b_n[1:] ** 2) print(f时域功率 {P_time:.6f}频域功率 {P_freq:.6f})对幅度为 1 的对称方波预期时域平均功率为 1.0。频域功率取很多项后会非常接近 1.0。如果差距较大优先检查计算系数的积分区间是否取满一个完整周期。7. 连续周期信号傅里叶级数的常见问题与排查思路问题现象可能原因排查方式解决方案所有系数算出来都是 0积分区间不是信号的完整周期或信号没有对准打印积分区间内的函数值确认周期边界使用信号周期 T 的整数倍区间进行积分(a_0) 与直流分量不符忘记区分 (a_0) 和 (a_0/2) 的约定对照教材或代码里的公式检查统一使用 (a_0 \frac{1}{T}\int x(t)dt)重建信号在跳变处严重过冲这是方波等不连续信号的 Gibbs 现象不是 bug对比不同 N 的过冲幅度接受该现象或用滤波器平滑截断用 FFT 计算的频谱幅度与公式算出的系数对不上FFT 结果是离散采样序列的 DFT和连续时间傅里叶系数存在比例关系检查采样率、序列长度和归一化因子按 (C_k \approx X[k] / N) 的关系进行换算指数形式重建后的信号为复数忘记用共轭对称的 C 完成求和检查 (C_{-n}) 与 (C_n) 是否共轭保留完整的正负频率项再求和符号积分不收敛或报警告sympy对分段不连续函数支持有限改用分段符号表达式或数值积分用scipy.integrate.quad代替符号积分频谱图有负频率谱线看不懂频率轴包含正负两部分是数学表示的结果绘制单边谱只看正频率将幅度乘以 2直流除外得到单边谱谐波次数越高幅度谱不从 0 收敛积分中数值采样点不够密提高quad的容差或提高采样密度检查波形高次谐波的真实衰减速度这里重点说 FFT 与傅里叶级数系数的比例关系。如果先把周期信号在计算机里采样成离散序列再用 FFT 计算频谱得到的是离散傅里叶系数 (X[k])。用 FFT 估计连续周期信号傅里叶级数系数时一个常用近似是 (C_k \approx X[k] / N)其中 (N) 是一个周期内的采样点数。这个近似成立的前提是采样率足够高保证没有混叠。很多人直接用 FFT 画幅值谱时觉得数值太大或太小通常就是忽略了比例因子。还有一点需要提醒数值积分方法对不连续信号的效率并不高。quad会努力在跳变点附近做自适应细分但面对方波这类信号如果你只是写一个lambda t: np.where(t 0, 1, -1)的简单函数仍然可能出现细微的积分误差。更好的办法是把积分区间分成几个连续子区间在每一段上分别积分然后求和。对周期信号可以选择 (t_0 0)将 ([0, T]) 按跳变点切分。8. 最佳实践与工程建议8.1 先判断对称性再决定是否全套计算实际工程中很多周期波形是偶函数或奇函数。如果一个信号在时域上有明确的对称轴它的傅里叶级数就会自然地少掉一半项。例如偶函数只有余弦项和直流项。奇函数只有正弦项。半波对称即 (x(t T/2) -x(t))只含奇次谐波无论余弦还是正弦项都不会有偶次。这个性质对代码和硬件系统都有价值。你可以先画出信号波形观察是否存在半波对称或镜像对称这样可以节省一半甚至更多的计算量也能防止算出结果后无法判断正误。8.2 数值积分前对信号做区间切分scipy.integrate.quad不是一个通用黑盒它要求被积函数尽量连续。对包含跳变的周期信号直接把整个周期丢给quad虽然有时能算出来但经常返回警告或精度不佳的结果。更稳妥的做法是手动切分def integrate_over_period(func, T, n, kindcos): 对 [0, T] 上含有跳变的函数分段积分 # 这里以在 t1 处跳变的 T2 方波为例 w0 2 * np.pi / T if kind cos: g lambda t: func(t) * np.cos(n * w0 * t) else: g lambda t: func(t) * np.sin(n * w0 * t) val, _ quad(g, 0, 1) val quad(g, 1, T)[0] return val这个例子以跳变点为 1 来切分实际信号跳变点可能更多。切分的通用原则是把信号写成若干连续的分段函数确保每段的端点正好位于跳变处。8.3 用符号推导验证手算用数值计算验证工程在理论学习阶段用sympy推导一个三角波或脉冲串的系数表达式能够加深对公式的把握。在项目代码里优先用numpy和scipy做数值计算因为真实信号往往不存在可用初等函数写出的解析式。符号计算适合验证数值计算适合落地。8.4 明确频域结果的归一化约定这是工程里最容易翻车的点。MATLAB 的fft、Python 的numpy.fft.fft以及手动实现的 DFT对同一个信号的输出幅度都不一样根源在于归一化因子的约定不同。使用前先确认你关心的是单边幅度谱还是双边幅度谱。你要的是三角函数系数 (a_n, b_n) 还是指数形式系数 (C_n)。你的采样率是否已经能够覆盖你要分析的最高次谐波。很多频谱分析初学者画出来的频谱“幅度不对”其实不是算法错了而是缺少对归一化约定的检查。8.5 工程谐波分析要设置合理的截止次数理论上傅里叶级数需要无穷多项才能重构原始信号工程上只能截断。截断次数取决于目标系统的带宽和误差容限。比如电能质量分析关注 50 次以内的谐波音频处理关注 20 kHz 以内的成分电机控制中电流环的谐波抑制通常分析到几十次。设置合理的截止次数的做法是先画出频谱或计算累积功率然后在累积功率占比超过某个阈值比如 95% 或 99%的地方截断而不是随意选一个整数。8.6 安全与数据边界提醒这篇文章涉及的计算和仿真都属于基础数学工具没有直接操作真实生产系统的内容但仍建议你遵守几条通用规范在真实系统中做谐波分析时必须确保采样设备、传感器和数据记录符合设备量程要求涉及电网或强电设备的谐波测量要有授权并在安全环境下进行分析结果如果用于控制系统的参数整定建议先在仿真环境验证后再上机避免直接在生产设备上反复试验。9. 从傅里叶级数走向傅里叶变换与离散分析当你把连续周期信号的傅里叶级数真正理解到位后面很多知识的门槛就会顺势跨过去。首先是傅里叶变换。对周期信号而言傅里叶级数的谱线是离散的每根谱线之间的距离是基波频率 (f_0)。如果周期趋近于无穷大基波频率趋近于零谱线从离散变成连续求和就变成了积分傅里叶级数就自然过渡为傅里叶变换。频域连续化的代价是时域不再具有周期性。其次是离散周期信号的 DFS 与非周期序列的 DTFT。连续世界里的傅里叶级数对应离散周期信号里的离散傅里叶级数DFS但当序列长度有限时DFT 成了计算机可以处理的版本。FFT 并不是一种新的变换而是 DFT 的快速算法。你只要能清楚地区分“连续周期 - 傅里叶级数”和“离散有限长 - DFT”这两条坐标系就不会再被各种频谱结果绕晕。从计算角度说用代码连续算 1000 次谐波的数值积分是一种低效的做法。真实工程大量使用 FFT把连续周期信号采样后一次性拿到所有谐波幅度。为什么我在正文里仍然把逐项积分作为主线因为 FFT 的黑盒性太强如果直接讲 FFT初学者很难理解系数为什么长那个样子。先把级数系数讲透再用 FFT 验证自己的代码才是更扎实的学习路径。建议你做完方波之后再挑三角波、半波整流波形各跑一遍流程。观察这几类波形各自的收敛速度差异然后尝试把方波的占空比从 50% 改成 30%看看频谱中是否出现偶次谐波这个练习会同时检验你对对称性的理解和代码实现的正确性。做完这些傅里叶级数这一块就算真正过关了。