我试图找到正弦波的零交叉点,将正弦波转换为方波。唯一的问题是正弦波很嘈杂,所以我得到了很多抖动和错误的过零。
任何人都可以推荐任何简单的伪代码或相关材料吗?到目前为止,我有这样的事情:
if (sample[i]>0 && sample[i+1]<0) || (sample[i]<0 && sample[i+1]>0)
任何人都可以推荐一种更强大的方法吗?
我试图找到正弦波的零交叉点,将正弦波转换为方波。唯一的问题是正弦波很嘈杂,所以我得到了很多抖动和错误的过零。
任何人都可以推荐任何简单的伪代码或相关材料吗?到目前为止,我有这样的事情:
if (sample[i]>0 && sample[i+1]<0) || (sample[i]<0 && sample[i+1]>0)
任何人都可以推荐一种更强大的方法吗?
您可以尝试对输入信号进行低通滤波以获得更平滑的过零(如果您对正弦波的频率位置有很好的了解,甚至可以使用带通滤波)。风险在于,如果样本准确的相位信息对您的应用程序至关重要,则来自过滤器的额外延迟可能是一个问题。
另一种方法:与其尝试将正弦波转换为方波,不如让一个独立的方波振荡器在相位/频率上与正弦波对齐呢?这可以通过锁相环来完成。
你所展示的肯定是一个过零检测器。想到几件事可能会改善您的情况:
如果您有超出信号频带的噪声(几乎可以肯定是这种情况,因为您的输入是纯音),那么您可以通过在感兴趣的信号周围应用带通滤波器来提高信噪比. 滤波器的通带宽度应根据您先验地知道正弦频率的精确程度来选择。通过减少正弦曲线上存在的噪声量,将减少错误过零的数量及其对正确交叉时间的抖动。
对于过零检测器本身,您可能会在过程中添加一些滞后。这将防止在正确的交叉瞬间周围产生额外的虚假测量交叉。向检测器添加滞后可能如下所示:
if ((state == POSITIVE) && (sample[i - 1] > -T) && (sample[i] < -T))
{
// handle negative zero-crossing
state = NEGATIVE;
}
else if ((state == NEGATIVE) && (sample[i - 1] < T) && (sample[i] > T))
{
// handle positive zero-crossing
state = POSITIVE;
}
实际上,您向过零检测器添加了一些状态。如果您认为输入信号具有正值,则要求信号下降到所选阈值以下-T
,以声明真正的过零。同样,您要求信号回升到阈值以上,T
以声明信号再次振荡回正。
您可以选择任何您想要的阈值,但对于像正弦曲线这样的平衡信号,让它们关于零对称是有意义的。这种方法可以帮助您获得更清晰的输出,但由于您实际上测量的是非零阈值交叉而不是零交叉,因此会增加一些时间延迟。
正如 pichenettes 在他的回答中所建议的那样,锁相环很可能是最好的方法,因为 PLL 几乎完全符合您的要求。简而言之,您运行一个与输入正弦波并行运行的方波发生器。PLL 对正弦波进行周期性相位测量,然后过滤测量流以控制方波发生器的瞬时频率。在某些时候,环路将(希望)锁定,此时方波应该与输入的正弦曲线在频率和相位上锁定(当然会有一定的误差;工程中没有什么是完美的)。
我有一个非常简单的方法来查找信号中的符号变化的良好经验:
每个集群的平均值/中值,这是你的符号变化
在 4 预测的点处与阶跃函数相关
在我的情况下,5 和 6 不会增加方法的精度。你可以用噪声来抖动你的信号,看看它是否有帮助。
我知道这个问题很老了,但我最近不得不实施过零。我实施了 Dan 建议的方式,并且对结果非常满意。这是我的python代码,如果有人感兴趣的话。我不是一个优雅的程序员,请多多包涵。
import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle
fig = plt.figure()
ax = fig.add_subplot(111)
sample_time = 0.01
sample_freq = 1/sample_time
# a-priori knowledge of frequency, in this case 1Hz, make target_voltage variable to use as trigger?
target_freq = 1
target_voltage = 0
time = np.arange(0.0, 5.0, 0.01)
data = np.cos(2*np.pi*time)
noise = np.random.normal(0,0.2, len(data))
data = data + noise
line, = ax.plot(time, data, lw=2)
candidates = [] #indizes of candidates (values better?)
for i in range(0, len(data)-1):
if data[i] < target_voltage and data[i+1] > target_voltage:
#positive crossing
candidates.append(time[i])
elif data[i] > target_voltage and data[i+1] < target_voltage:
#negative crossing
candidates.append(time[i])
ax.plot(candidates, np.ones(len(candidates)) * target_voltage, 'rx')
print('candidates: ' + str(candidates))
#group candidates by threshhold
groups = [[]]
time_thresh = target_freq / 8;
group_idx = 0;
for i in range(0, len(candidates)-1):
if(candidates[i+1] - candidates[i] < time_thresh):
groups[group_idx].append(candidates[i])
if i == (len(candidates) - 2):
# special case for last candidate
# in this case last candidate belongs to the present group
groups[group_idx].append(candidates[i+1])
else:
groups[group_idx].append(candidates[i])
groups.append([])
group_idx = group_idx + 1
if i == (len(candidates) - 2):
# special case for last candidate
# in this case last candidate belongs to the next group
groups[group_idx].append(candidates[i+1])
cycol = cycle('bgcmk')
for i in range(0, len(groups)):
for j in range(0, len(groups[i])):
print('group' + str(i) + ' candidate nr ' + str(j) + ' value: ' + str(groups[i][j]))
ax.plot(groups[i], np.ones(len(groups[i])) * target_voltage, color=next(cycol), marker='o', markersize=4)
#determine zero_crosses from groups
zero_crosses = []
for i in range(0, len(groups)):
group_median = groups[i][0] + ((groups[i][-1] - groups [i][0])/2)
print('group median: ' + str(group_median))
#find index that best matches time-vector
idx = np.argmin(np.abs(time - group_median))
print('index of timestamp: ' + str(idx))
zero_crosses.append(time[idx])
#plot zero crosses
ax.plot(zero_crosses, np.ones(len(zero_crosses)) * target_voltage, 'bx', markersize=10)
plt.show()
请注意:我的代码没有检测到迹象,并使用目标频率的一些先验知识来确定时间阈值。该阈值用于对多个交叉点(图片中的不同颜色点)进行分组,从中选择最接近组中位数的一个(图片中的蓝色交叉点)。