Zero Crossing of a Noisy Sine Wave


9

Sto cercando di trovare gli incroci zero di un'onda sinusoidale per trasformare l'onda sinusoidale in un'onda quadra. L'unico problema è che l'onda sinusoidale è rumorosa, quindi sto ricevendo molti jitter e falsi incroci con zero.

Qualcuno può raccomandare qualche semplice psuedocode o materiali pertinenti? Finora ho qualcosa del genere:

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

Qualcuno può raccomandare un metodo più robusto?


Qual è lo scopo di te cercando di renderlo un'onda quadra? Stai cercando di scoprire dove inizia e finisce il segnale? Se sì, posso raccomandare un metodo.
Spacey,

if ((sample [i] * sample [i + 1]) <0) zero_crossing ++;
Marius Hrisca,

Risposte:


8

Potresti provare a filtrare passa-basso il segnale di ingresso per ottenere passaggi a zero più fluidi (o persino filtrare passa-banda se hai una buona idea della posizione della frequenza dell'onda sinusoidale). Il rischio è che se le informazioni sulla fase accurate al campione sono essenziali per l'applicazione, il ritardo aggiuntivo del filtro potrebbe essere un problema.

Un altro approccio: invece di provare a trasformare l'onda sinusoidale in un'onda quadra, che ne dici di ottenere un oscillatore ad onda quadra indipendente per allinearsi in fase / frequenza con l'onda sinusoidale? Questo può essere fatto con un loop a fase bloccata .


6

Quello che hai sicuramente mostrato è un rilevatore del passaggio per lo zero. Vengono in mente un paio di cose che potrebbero migliorare la tua situazione:

  • Se hai un rumore esterno alla banda del tuo segnale (che è quasi certamente il caso, dato che il tuo input è un tono puro), puoi migliorare il rapporto segnale-rumore applicando un filtro passa-banda attorno al segnale di interesse . La larghezza della banda passante del filtro dovrebbe essere scelta in base alla precisione con cui si conosce a priori la frequenza sinusoidale . Riducendo la quantità di rumore presente sulla sinusoide, si ridurrà il numero di incroci con zero zero e il loro jitter sui tempi di attraversamento corretti.

    • Come nota a margine, se non si hanno in anticipo buone informazioni, è possibile utilizzare una tecnica più sofisticata nota come potenziatore di linea adattivo , che, come suggerisce il nome, è un filtro adattivo che migliorerà un segnale di ingresso periodico. Tuttavia, questo è un argomento piuttosto avanzato e in genere hai un'idea abbastanza buona della frequenza del tuo segnale che questo tipo di approccio non è necessario.
  • Rispetto allo stesso rilevatore del passaggio per lo zero, è possibile aggiungere un'isteresi al processo. Ciò impedirebbe la generazione di incroci misurati extra spuri attorno all'istante di attraversamento corretto. L'aggiunta dell'isteresi al rivelatore potrebbe essere simile a questa:

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

    In effetti, aggiungi un po 'di stato al rilevatore di attraversamento zero. Se si ritiene che il segnale di ingresso abbia un valore positivo, è necessario che il segnale scenda al di sotto di un valore di soglia scelto -Tper dichiarare un passaggio per lo zero reale. Allo stesso modo, è necessario che il segnale risalga al di sopra della soglia Tper dichiarare che il segnale è tornato nuovamente positivo.

    Puoi scegliere le soglie in modo che siano come vuoi, ma per un segnale bilanciato come una sinusoide, ha senso che siano simmetriche rispetto allo zero. Questo approccio può aiutarti a fornire un output più pulito, ma aggiungerà un certo ritardo a causa del fatto che stai effettivamente misurando varchi di soglia diversi da zero anziché zero.

Come suggerito da Pichenettes nella sua risposta, un circuito a blocco di fase sarebbe molto probabilmente il modo migliore di procedere, poiché un PLL fa praticamente esattamente quello che stai cercando di fare. In breve, si esegue un generatore di onde quadrate che funziona in parallelo con la sinusoide di ingresso. Il PLL effettua misurazioni periodiche di fase sulla sinusoide, quindi filtra quel flusso di misurazioni per orientare la frequenza istantanea del generatore di onde quadrate. Ad un certo punto, il loop si bloccherà (si spera), a quel punto l'onda quadra dovrebbe essere bloccata in frequenza e fase con la sinusoide dell'ingresso (con una certa quantità di errore, ovviamente; nulla in ingegneria è perfetto).


È un trigger di Schmitt?
Davorin,

In effetti, si potrebbe dire che si tratta di una versione software di un trigger Schmitt . La caratteristica distintiva di un trigger di Schmitt è che è un comparatore con l'isteresi
Jason R

Per evitare di non rilevare la transizione, includere in entrambe le condizioni anche la soglia T. Significato invece di && (sample[i - 1] > -T) && (sample[i] < -T)), usare && (sample[i - 1] >= -T) && (sample[i] < -T)). Questo deve essere applicato a entrambe ife alle else ifdichiarazioni.
marc

2

Ho una buona esperienza con un metodo molto semplice per trovare le modifiche del segnale nel segnale a volte:

  1. a = diff (segno (segnale))! = 0 # rileva i cambiamenti del segno
  2. candidate = times [a] # questi sono tutti i punti candidati, inclusi i falsi incroci
  3. trovare gruppi di punti nei candidati
  4. media / mediana di ogni cluster, questo è il tuo cambio di segno

  5. eseguire la correlazione con la funzione step nel punto previsto da 4

  6. adatta la curva ai risultati di correlazione e trova il picco

Nel mio caso 5 e 6 non aumentano la precisione del metodo. Puoi controllare il tuo segnale con rumore e vedere se aiuta.


2

So che questa domanda è piuttosto vecchia, ma di recente ho dovuto implementare lo zero crossing. Ho implementato il modo suggerito da Dan e sono piuttosto soddisfatto del risultato. Ecco il mio codice Python, se qualcuno è interessato. Non sono davvero un programmatore elegante, ti prego.

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

Nota Pls: il mio codice non rileva i segni e usa una piccola conoscenza a priori di una frequenza target per determinare la soglia temporale. Questa soglia viene utilizzata per raggruppare l'attraversamento multiplo (diversi punti di colore nell'immagine) da cui viene selezionato quello più vicino alla mediana dei gruppi (croci blu nell'immagine).

Onda sinusoidale rumorosa con croci zero contrassegnate

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.