首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >如何在 FFT 算法里面精确的锁定一个频率.读者问题答疑

如何在 FFT 算法里面精确的锁定一个频率.读者问题答疑

作者头像
云深无际
发布2026-01-07 12:52:06
发布2026-01-07 12:52:06
910
举报
文章被收录于专栏:云深之无迹云深之无迹

如何在 FFT 算法里面精确的锁定一个频率

昨天的问题,有个读者询问:

好!
好!

好!

我觉得这是真真正正的读过思考过,也是一个好问题~其实这是在考虑 相干采样时如何避免重复模式 的问题。

我们先看条件:

相干采样的基本条件

相干采样要求:

即在采样窗口里恰好包含 C 个整数周期。这样 FFT 就不会发生谱泄漏。

“周期数 = 质数”的考虑

有的教材或论文会建议:

让 C(周期数)是质数,或者让 N(采样点数)与 C(周期数)互质

目的:避免“相位点重复模式”;如果 和 有公因数,信号的采样点可能在窗口内呈现某种重复格局(比如相位只取有限几个点),会让拟合或谐波分析“退化”。

举例:

,,,则 C=1。相当于只采了 1 个周期,FFT 会有很强的不确定性。

如果 C=2,N=40,信号其实就是重复两遍,频域信息有限;若 C=质数(比如 4093),采样点遍布整个正弦周期的不同相位,重复性最小。

模拟一下

A
A

A

B
B

B

C
C

C

上面这个 可视化 demo 展示了 3 种“相干采样”场景下,相位点分布FFT 峰值 的差异:

Case A(gcd(N,C)>1,示例 N=64、C=16)相位点在单位圆上出现强重复(只占据少数相位),虽然 FFT 峰仍然精确落在基频 bin,但“相位采样不充分”,对某些拟合/非正弦分析会不利。

Case B(互质,N=64、C=15)相位点在 0~2π 均匀铺开,没有重复模式;FFT 峰依旧精确对齐。这是工程上非常推荐的选择。

Case C(C 取质数且与 N 互质,N=64、C=17)同样均匀铺开,且质数 C 更容易避免隐藏的周期性重复。对于高精度拟合/谐波分析更“稳”。

结论:

相干采样(C 为整数)保证了不泄漏、峰值正落 bin让 C 与 N 互质(甚至 C 选质数),能最大化相位覆盖度、避免“相位点重复模式”,对sine fit、THD、相位估计更友好;

工程实践上:固定 N(比如 65536),为每个采样率 Fs 选一个互质/质数的 C,把信号源频率设成

就能让峰值稳在 1 kHz 对应的 bin,同时避免重复采样相位。

实际工程情况

FFT 分辨率:比“质数”更重要。工程上优先考虑 N 要多大(够长时间窗)。

FFT 高效计算:常常把 N 选成 2 的幂次,这和“质数周期”有矛盾。

我个人觉得大多数情况下,即便 C 不是质数,只要 N 够大,频率估计/幅值恢复都不会受太大影响。不强制必须质数,关键是 C 和 N 互质,能避免重复采样点模式;工程里优先满足 长时间窗 / FFT 效率,再在可行范围里把 C 选成质数(或至少与 N 互质),是更稳妥的做法。

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt
from math import gcd

def demo_case(N, C, title):
    """
    N: 采样点数
    C: 窗口内周期数(相干采样:C 整数)
    f0 = C/N * Fs
    """
    Fs = 1.0  # 只看相对关系即可,设 Fs=1,f0=C/N
    f0 = C / N * Fs
    t = np.arange(N) / Fs
    x = np.sin(2*np.pi*f0*t)

    # 计算相位分布(mod 2π)以及相位点个数
    phase = (2*np.pi*f0*t) % (2*np.pi)
    unique_phases = len(np.unique(np.round(phase, 12)))  # 去重
    g = gcd(N, C)

    # FFT(相干,无泄漏),看峰是否正落 bin
    X = np.fft.rfft(x) / (N/2)  # 简单归一化
    freqs = np.fft.rfftfreq(N, d=1/Fs)
    mag = 20*np.log10(np.abs(X) + 1e-15)

    # 绘图
    fig, axes = plt.subplots(1, 3, figsize=(13, 3.5))

    # (1) 相位分布在单位圆
    theta = np.linspace(0, 2*np.pi, 400)
    axes[0].plot(np.cos(theta), np.sin(theta))  # 单位圆
    axes[0].plot(np.cos(phase), np.sin(phase), '.', ms=6)
    axes[0].set_aspect('equal', 'box')
    axes[0].set_title(f"{title}\nN={N}, C={C}, gcd(N,C)={g}\nunique phases={unique_phases}")
    axes[0].set_xlabel("cos(phase)")
    axes[0].set_ylabel("sin(phase)")
    axes[0].grid(True)

    # (2) 时间域(显示前两周期)
    T0 = 2 / f0  # 两个周期的时长
    idx = t <= T0
    axes[1].plot(t[idx], x[idx], 'o-')
    axes[1].set_title("Time domain (first 2 periods)")
    axes[1].set_xlabel("time")
    axes[1].set_ylabel("amplitude")
    axes[1].grid(True)

    # (3) 频域
    axes[2].plot(freqs, mag, '-')
    axes[2].axvline(f0, ls='--')
    axes[2].set_xlim(0, 4*f0)  # 看 0~4f0 的范围
    axes[2].set_title("FFT magnitude (dB)")
    axes[2].set_xlabel("frequency (bins*Fs)")
    axes[2].set_ylabel("dB")
    axes[2].grid(True)

    plt.tight_layout()
    plt.show()

# 三个对比用例:
# A) gcd(N,C)>1:相位会重复,unique phases 少(例如 N=64, C=16, gcd=16)
demo_case(N=64, C=16, title="Case A: 相位点强重复(gcd>1)")

# B) 互质:gcd(N,C)=1,相位分布最均匀(N=64, C=15)
demo_case(N=64, C=15, title="Case B: 互质(相位均匀展开)")

# C) C 为质数且与 N 互质(N=64, C=17)
demo_case(N=64, C=17, title="Case C: C取质数且互质(更稳)")
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 相干采样的基本条件
  • “周期数 = 质数”的考虑
  • 模拟一下
  • 实际工程情况
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档