Нулевое пересечение шумной синусоиды

9

Я пытаюсь найти нулевые пересечения синусоидальной волны, чтобы преобразовать синусоидальную волну в квадратную. Единственная проблема в том, что синусоида шумит, поэтому я получаю много джиттера и ложных пересечений нуля.

Кто-нибудь может порекомендовать какой-нибудь простой psuedocode или соответствующие материалы? Пока что у меня есть что-то вроде этого:

if (sample[i]>0 && sample[i+1]<0) || (sample[i]<0 && sample[i+1]>0)

Кто-нибудь может порекомендовать более надежный метод?

Кевин Насто
источник
С какой целью вы пытаетесь сделать это прямоугольной волной? Вы пытаетесь выяснить, где начинается и заканчивается сигнал? Если вы, я могу порекомендовать метод.
Спейси
if ((sample [i] * sample [i + 1]) <0) zero_crossing ++;
Мариус Хриска

Ответы:

8

Вы можете попробовать низкочастотную фильтрацию входного сигнала, чтобы получить более плавные пересечения нуля (или даже полосовую фильтрацию, если у вас есть хорошее представление о частотном расположении синусоидальной волны). Существует риск того, что если для вашего приложения важна точная информация о фазе, может возникнуть проблема с дополнительным запаздыванием фильтра.

Другой подход: вместо того, чтобы пытаться преобразовать синусоидальную волну в прямоугольную, как насчет того, чтобы заставить независимый осциллятор прямоугольной формы совмещаться по фазе / частоте с синусоидальной волной? Это можно сделать с помощью фазовой автоподстройки частоты .

pichenettes
источник
6

То, что вы показали, это детектор пересечения нуля. На ум приходит пара вещей, которые могут улучшить вашу ситуацию:

  • Если у вас есть шум, который находится за пределами полосы вашего сигнала (что почти наверняка имеет место, поскольку ваш вход является чистым тоном), то вы можете улучшить отношение сигнал / шум, применив полосовой фильтр вокруг интересующего сигнала. , Ширина полосы пропускания фильтра должна выбираться исходя из того, насколько точно вы знаете синусоидальную частоту априори . За счет уменьшения количества шума, присутствующего на синусоиде, количество ложных пересечений нуля и их дрожание относительно правильного времени пересечения будут уменьшены.

    • Как примечание: если у вас нет хорошей информации заранее, вы можете использовать более сложную технику, известную как адаптивный линейный усилитель , который, как следует из его названия, является адаптивным фильтром, который будет усиливать периодический входной сигнал. Однако это довольно сложная тема, и вы, как правило, достаточно хорошо представляете частоту своего сигнала, что такой подход не нужен.
  • Что касается самого детектора пересечения нуля, вы можете добавить некоторый гистерезис к процессу. Это предотвратит создание дополнительных ложных измеренных пересечений вокруг правильного момента пересечения. Добавление гистерезиса к детектору может выглядеть примерно так:

    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, чтобы объявить, что сигнал снова вернулся в положительное положение.

    Вы можете выбрать пороговые значения так, как хотите, но для сбалансированного сигнала, такого как синусоида, имеет смысл, чтобы они были симметричны относительно нуля. Этот подход может помочь вам получить более чистый внешний вид, но он добавит некоторую задержку по времени из-за того, что вы фактически измеряете пересечения с ненулевым порогом вместо пересечений с нулем.

Как предположили в своем ответе пикенетки, петля с фазовой синхронизацией, скорее всего, была бы наилучшим способом, поскольку ФАПЧ делает в значительной степени именно то, что вы пытаетесь сделать. Короче говоря, вы запускаете генератор прямоугольных импульсов, который работает параллельно входной синусоиде. ФАПЧ выполняет периодические измерения фазы на синусоиде, затем фильтрует этот поток измерений, чтобы управлять мгновенной частотой генератора прямоугольных импульсов. В какой-то момент петля будет (надеюсь) заблокирована, и в этом случае прямоугольная волна должна быть синхронизирована по частоте и фазе с синусоидой входа (конечно, с некоторой ошибкой; в разработке ничего не бывает идеально).

Джейсон Р
источник
Это триггер Шмитта?
Даворин
Действительно, можно сказать, что это версия программного обеспечения триггера Шмитта . Определяющей характеристикой триггера Шмитта является то, что он является компаратором с гистерезисом
Джейсон Р.
Чтобы не обнаруживать переход, включите в любое из двух условий также порог T. Значение вместо && (sample[i - 1] > -T) && (sample[i] < -T)), используйте && (sample[i - 1] >= -T) && (sample[i] < -T)). Это должно быть применено к обоим ifи else ifзаявлениям.
Марк
2

У меня есть хороший опыт с очень простым методом, чтобы найти изменения знака сигнала в разы:

  1. a = diff (знак (сигнал))! = 0 # это обнаруживает изменения знака
  2. кандидаты = времена [а] # это все баллы кандидатов, включая ложные пересечения
  3. найти кластеры точек в кандидатах
  4. среднее / медиана каждого кластера, это ваш знак изменения

  5. сделать корреляцию с функцией шага в точке, предсказанной 4

  6. подогнать кривую к результатам корреляции и найти пик

В моем случае 5 и 6 не увеличивают точность метода. Вы можете смешать ваш сигнал с шумом и посмотреть, поможет ли это.

Дэн
источник
2

Я знаю, что этот вопрос довольно старый, но недавно мне пришлось внедрить пересечение нуля. Я реализовал способ, предложенный Даном, и довольно доволен результатом. Вот мой код на 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()

Примечание Pls: мой код не обнаруживает признаков и использует немного априорное знание целевой частоты для определения порога времени. Этот порог используется для группировки нескольких пересечений (разные цветные точки на рисунке), из которых выбирается ближайший к медиане групп (синие крестики на рисунке).

Шумная синусоида с отмеченными нулевыми крестами

Штефан Шалмайнер
источник