我需要一种算法来检测纯正弦信号的频率和相位。输入信号的频率在 0 到 100 Hz 之间变化。
以 20 kHz 的频率捕获信号的值(因此我每秒获得 20000 个值) - 这是给定的,无法更改。我需要检测此输入信号的频率和相位,并使用 PWM 生成与输入信号频率相同的 MCU 中断。
谁能建议使用什么算法来做到这一点简单而有效?也许是 Goertzel 算法?
我需要一种算法来检测纯正弦信号的频率和相位。输入信号的频率在 0 到 100 Hz 之间变化。
以 20 kHz 的频率捕获信号的值(因此我每秒获得 20000 个值) - 这是给定的,无法更改。我需要检测此输入信号的频率和相位,并使用 PWM 生成与输入信号频率相同的 MCU 中断。
谁能建议使用什么算法来做到这一点简单而有效?也许是 Goertzel 算法?
注意:我最初为这个问题的Stack Overflow 副本发布了这个答案,然后才意识到这里也有人问过这个问题。它有些重复pichenettes 的答案,但我觉得它仍然值得在这里(重新)发布,因为它包含一些额外的细节。(这些细节是否有用,我会留给你和OP来判断。)
如果您知道您的信号是纯正弦波,则可以使用过零检测。正弦波的每个周期将有两个过零:一个从负到正,一个从正到负。这将比尝试做一些像傅里叶变换这样的花哨的东西更简单、更有效。
不过,有一些细节需要牢记:
信号稍微有偏差是可以的,但如果偏差可能超过信号的幅度,您需要以某种方式对其进行校正。
您可以在使用模拟高通滤波器进行采样之前执行此操作,也可以跟踪采样信号的移动平均值并将其用作“零电平”进行比较。或者,您可以改为查看连续样本之间差异的零交叉点(对应于信号中每个周期的最大值和最小值)以避免任何偏差问题。
如果您的输入(或 ADC)有噪声,则当信号接近零电平时,您的样本可能会在零电平附近随机波动。在这种情况下,天真地比较连续的样本可能会检测到多个过零,而实际上只有一个。
解决此问题的一种方法是在处理信号之前对其进行平滑处理,但在过零检测器中实现滞后可能更容易、更有效。也就是说,只有当信号下降到某个预设阈值水平 -ε 以下时,您才会检测到正到负交叉,并且只有当信号上升到 +ε 以上时,才会检测到负到正交叉,就像这个伪 C代码:
boolean isPositive = (firstSample > 0);
while (running) {
int signalLevel = getNextSample();
if (isPositive && signalLevel < -threshold) {
isPositive = false;
handleZeroCrossing();
}
else if (!isPositive && signalLevel > +threshold) {
isPositive = true;
handleZeroCrossing();
}
}
事实上,您甚至可能根本不需要算法,因为正如下面评论中所指出的,您可以使用简单的施密特触发器在硬件中实现带有滞后的过零检测,该触发器基本上将正弦波输入转换为具有相同频率和(几乎)相同相位的方波信号,您可以将其读取为简单的数字输入。您甚至可以使用施密特触发器的输出直接驱动 MCU 中断引脚。
如果信号是纯正弦且无噪声的,您可以简单地计算正零交叉之间的样本数 - 并将其用作信号周期的估计,从中推断出频率。如有必要,请等待并平均几个时期以获得更可靠的估计。很有可能无需对信号进行采样就可以实现这一点 - 直接通过微控制器的输入引脚和其中一个定时器。
至于相位,嗯,相位相对于什么?
通过回复的共识删除了算法。
正弦曲线的频率估计(在软件中)可以使用自相关来稳健地完成。
中心峰值(滞后 0)将始终是(或)最大值。对于周期性信号,每n滞后一次,您将获得额外的峰值。第二个峰值位置以样本为单位告诉您信号的周期。但是,您不会得到相位信息。
然后,您可以使用已知的相位参考构建自己的相同频率的正弦曲线。然后计算原始信号和参考信号之间的互相关函数。现在,第一个峰值位置将告诉您信号之间的相位差。其中相位 = 2*pi*(样本中的滞后)/(样本中的正弦周期)