Zerowe przejście głośnej fali sinusoidalnej

9

Próbuję znaleźć przejścia przez zero fali sinusoidalnej, aby przekształcić falę sinusoidalną w falę kwadratową. Jedyny problem polega na tym, że fala sinusoidalna jest hałaśliwa, więc mam dużo drgań i fałszywych przejść przez zero.

Czy ktoś może polecić prosty kod psued lub odpowiednie materiały? Do tej pory mam coś takiego:

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

Czy ktoś może polecić bardziej niezawodną metodę?

Kevin Nasto
źródło
Jaki jest cel twojej próby przekształcenia go w kwadratową falę? Czy próbujesz dowiedzieć się, gdzie zaczyna się i kończy sygnał? Jeśli tak, mogę polecić metodę.
Spacey,
if ((próbka [i] * próbka [i + 1]) <0) zero_crossing ++;
Marius Hrisca

Odpowiedzi:

8

Możesz spróbować filtrować dolnoprzepustowy sygnał wejściowy, aby uzyskać płynniejsze przejścia przez zero (lub nawet filtrowanie pasmowo-przepustowe, jeśli masz dobre pojęcie o lokalizacji częstotliwości fali sinusoidalnej). Ryzyko polega na tym, że jeśli dokładne informacje o fazie są niezbędne dla twojej aplikacji, dodatkowe opóźnienie z filtra może stanowić problem.

Inne podejście: zamiast próbować przekształcić falę sinusoidalną w falę kwadratową, co powiesz na uzyskanie niezależnego oscylatora fali prostokątnej, który wyrówna się fazowo / częstotliwościowo z falą sinusoidalną? Można to zrobić za pomocą pętli synchronizacji fazy .

fenenety
źródło
6

Z pewnością pokazałeś wykrywacz przejścia przez zero. Przychodzi mi na myśl kilka rzeczy, które mogą poprawić twoją sytuację:

  • Jeśli masz szum, który znajduje się poza pasmem sygnału (co jest prawie na pewno tak, ponieważ twój sygnał wejściowy jest czystym tonem), możesz poprawić stosunek sygnału do szumu, stosując filtr pasmowy wokół interesującego sygnału . Szerokość pasma przepustowego filtra należy wybrać na podstawie tego, jak dokładnie znasz częstotliwość sinusoidalną z góry . Poprzez zmniejszenie ilości szumu obecnego na sinusoidzie, liczba fałszywych przejść przez zero i ich fluktuacje dotyczące prawidłowych czasów przekroczenia zostaną zmniejszone.

    • Na marginesie, jeśli nie masz dobrych informacji z wyprzedzeniem, możesz użyć bardziej wyrafinowanej techniki znanej jako adaptacyjny wzmacniacz linii , który, jak sama nazwa wskazuje, jest adaptacyjnym filtrem, który wzmocni okresowy sygnał wejściowy. Jest to jednak dość zaawansowany temat i zazwyczaj masz wystarczająco dobre pojęcie o częstotliwości sygnału, że takie podejście nie jest potrzebne.
  • W odniesieniu do samego detektora przejścia przez zero możesz dodać do procesu pewną histerezę . Zapobiegnie to generowaniu dodatkowych fałszywych zmierzonych skrzyżowań wokół właściwej chwili przejścia. Dodanie histerezy do detektora może wyglądać mniej więcej tak:

    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;
    }
    

    Skutecznie dodajesz pewien stan do detektora przejścia przez zero. Jeśli uważasz, że sygnał wejściowy ma wartość dodatnią, musisz obniżyć sygnał poniżej wybranej wartości progowej -T, aby zadeklarować rzeczywiste przekroczenie zera. Podobnie, wymaga się, aby sygnał wzrósł ponownie powyżej progu T, aby zadeklarować, że sygnał ponownie oscyluje z powrotem na dodatni.

    Możesz wybrać progi, które chcesz, ale dla zrównoważonego sygnału, takiego jak sinusoida, sensowne jest, aby były symetryczne względem zera. Takie podejście może pomóc w uzyskaniu bardziej przejrzystego wyniku, ale doda pewne opóźnienie czasowe ze względu na fakt, że faktycznie mierzysz niezerowe przejścia progowe zamiast przejść przez zero.

Jak sugerują piknetyle w swojej odpowiedzi, pętla z zablokowaną fazą byłaby najprawdopodobniej najlepszym sposobem, ponieważ PLL robi dokładnie to, co próbujesz zrobić. Krótko mówiąc, uruchamiasz generator fali prostokątnej, który działa równolegle z sinusoidą wejściową. PLL dokonuje okresowych pomiarów fazy na sinusoidzie, a następnie filtruje ten strumień pomiarów, aby sterować chwilową częstotliwością generatora fali kwadratowej. W pewnym momencie pętla zostanie (miejmy nadzieję) zablokowana, w którym to momencie fala prostokątna powinna zostać zablokowana częstotliwościowo i fazowo za pomocą sinusoidy wejściowej (oczywiście z pewnym błędem; nic w inżynierii nie jest idealne).

Jason R.
źródło
Czy to spust Schmitta?
Davorin
Rzeczywiście można powiedzieć, że jest to wersja oprogramowania wyzwalacza Schmitta . Cechą charakterystyczną wyzwalacza Schmitta jest to, że jest to komparator z histerezą
Jason R
Aby uniknąć niewykrycia przejścia, należy uwzględnić w dowolnym z obu warunków również próg T. Znaczenie zamiast && (sample[i - 1] > -T) && (sample[i] < -T)), użyj && (sample[i - 1] >= -T) && (sample[i] < -T)). Należy to zastosować zarówno do oświadczeń, jak ifi do nich else if.
MARC
2

Mam dobre doświadczenie z bardzo prostą metodą znajdowania zmian w znakach w czasie:

  1. a = diff (znak (sygnał))! = 0 # wykrywa zmiany znaku
  2. kandydaci = razy [a] # to wszystkie punkty kandydujące, w tym fałszywe przekroczenia
  3. znaleźć skupiska punktów u kandydatów
  4. średnia / mediana każdego klastra, jest to zmiana znaku

  5. wykonać korelację z funkcją kroku w punkcie przewidywanym przez 4

  6. dopasuj krzywą do wyników korelacji i znajdź pik

W moim przypadku 5 i 6 nie zwiększają precyzji metody. Możesz roztrzaskać swój sygnał hałasem i sprawdzić, czy to pomaga.

Dan
źródło
2

Wiem, że to pytanie jest dość stare, ale ostatnio musiałem wdrożyć przejście przez zero. Wdrożyłem zgodnie z sugestią Dana i jestem raczej zadowolony z wyniku. Oto mój kod python, jeśli ktoś jest zainteresowany. Nie jestem naprawdę eleganckim programistą, proszę o wyrozumiałość.

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()

Uwaga: mój kod nie wykrywa znaków i wykorzystuje niewielką a priori wiedzę na temat częstotliwości docelowej w celu ustalenia progu czasowego. Ten próg służy do grupowania wielokrotnego przekraczania (różne kolorowe kropki na zdjęciu), z którego wybierana jest ta najbliższa medianie grup (niebieskie krzyże na zdjęciu).

Głośna fala sinusoidalna z zaznaczonymi zerowymi krzyżami

Stefan Schallmeiner
źródło