Che cos'è un algoritmo per ricampionare da un tasso variabile a un tasso fisso?


27

Ho un sensore che riporta le sue letture con un timestamp e un valore. Tuttavia, non genera letture a velocità fissa.

Trovo che i dati a tasso variabile siano difficili da gestire. La maggior parte dei filtri prevede una frequenza di campionamento fissa. Disegnare grafici è più facile anche con una frequenza di campionamento fissa.

Esiste un algoritmo per ricampionare da una frequenza di campionamento variabile a una frequenza di campionamento fissa?


Questo è un post trasversale di programmatori. Mi è stato detto che questo è un posto migliore per chiedere. programmers.stackexchange.com/questions/193795/…
FigBug

Cosa determina quando il sensore segnalerà una lettura? Invia una lettura solo quando la lettura cambia? Un approccio semplice sarebbe quello di scegliere un "intervallo di campionamento virtuale" (T) che è appena inferiore al tempo più breve tra le letture generate. All'ingresso dell'algoritmo, memorizzare solo l'ultima lettura riportata (CurrentReading). All'uscita dell'algoritmo, riportare CurrentReading come "nuovo campione" ogni T secondi in modo che il servizio di filtro o grafico riceva letture a una velocità costante (ogni T secondi). Non ho idea se questo è adeguato nel tuo caso però.
user2718

Cerca di campionare ogni 5ms o 10ms. Ma è un'attività a bassa priorità, quindi potrebbe essere persa o ritardata. I tempi sono precisi a 1 ms. L'elaborazione viene eseguita sul PC, non in tempo reale, quindi un algoritmo lento va bene se è più facile da implementare.
FigBug

1
Hai dato un'occhiata a una ricostruzione di Fourier? Esiste una trasformazione di Fourier basata su dati campionati in modo non uniforme. Il solito approccio è quello di trasformare un'immagine di Fourier in un dominio temporale uniformemente campionato.
mbaitoff,

3
Conosci qualche caratteristica del segnale sottostante che stai campionando? Se i dati con spaziatura irregolare sono ancora a una frequenza di campionamento ragionevolmente elevata rispetto alla larghezza di banda del segnale misurato, qualcosa di semplice come l'interpolazione polinomiale con una griglia temporale con spaziatura uniforme potrebbe funzionare correttamente.
Jason R,

Risposte:


21

L'approccio più semplice è fare una sorta di interpolazione della spline come suggerisce Jim Clay (lineare o altro). Tuttavia, se si dispone del lusso dell'elaborazione batch, e soprattutto se si dispone di una serie eccessiva di campioni non uniformi, esiste un algoritmo di "ricostruzione perfetta" estremamente elegante. Per ragioni numeriche, potrebbe non essere pratico in tutti i casi, ma almeno vale la pena conoscerlo concettualmente. L'ho letto per la prima volta in questo documento .

Il trucco è considerare il tuo set di campioni non uniformi come già ricostruito da campioni uniformi attraverso un'interpolazione sincera . Seguendo la notazione nel documento:

y(t)=k=1Ny(kT)sin(π(tkT)/T)π(tkT)/T=k=1Ny(kT)sinc(tkTT).

Si noti che ciò fornisce un insieme di equazioni lineari, una per ciascun campione non uniforme , dove gli incogniti sono i campioni equidistanziati , in questo modo:y ( k T )y(t)y(kT)

[y(t0)y(t1)y(tm)]=[sinc(t0TT)sinc(t02TT)sinc(t0nTT)sinc(t1TT)sinc(t12TT)sinc(t1nTT)sinc(tmTT)sinc(tm2TT)sinc(tmnTT)][y(T)y(2T)y(nT)].

Nell'equazione precedente, è il numero di campioni uniformi sconosciuti, è l'inverso della frequenza di campionamento uniforme e è il numero di campioni non uniformi (che può essere maggiore di ). Calcolando la soluzione dei minimi quadrati di quel sistema, è possibile ricostruire i campioni uniformi. Tecnicamente, sono necessari solo campioni non uniformi, ma a seconda di quanto "dispersi" siano nel tempo, la matrice di interpolazione può essere orribilmente mal condizionata . In questo caso, l'utilizzo di più campioni non uniformi di solito aiuta.T m n nnTmnn

A titolo di esempio di giocattolo, ecco un confronto (usando numpy ) tra il metodo sopra e l'interpolazione della spline cubica su una griglia leggermente jitter:

Ricostruzione di Sinc vs Spline cubiche di campioni non uniformi

(Il codice per riprodurre la trama sopra è incluso alla fine di questa risposta)

Detto questo, per metodi robusti e di alta qualità, partire da qualcosa in uno dei seguenti documenti sarebbe probabilmente più appropriato:

A. Aldroubi e Karlheinz Grochenig, Campionamento non uniforme e ricostruzione in spazi invarianti a turni , SIAM Rev., 2001, n. 4, 585-620. ( link pdf ).

K. Grochenig e H. Schwab, Metodi di ricostruzione locale veloce per campionamento non uniforme in spazi invarianti a turni , SIAM J. Matrix Anal. Appl., 24 (2003), 899-913.

-

import numpy as np
import pylab as py

import scipy.interpolate as spi
import numpy.random as npr
import numpy.linalg as npl

npr.seed(0)

class Signal(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def plot(self, title):
        self._plot(title)
        py.plot(self.x, self.y ,'bo-')
        py.ylim([-1.8,1.8])
        py.plot(hires.x,hires.y, 'k-', alpha=.5)

    def _plot(self, title):
        py.grid()
        py.title(title)
        py.xlim([0.0,1.0])

    def sinc_resample(self, xnew):
        m,n = (len(self.x), len(xnew))
        T = 1./n
        A = np.zeros((m,n))

        for i in range(0,m):
            A[i,:] = np.sinc((self.x[i] - xnew)/T)

        return Signal(xnew, npl.lstsq(A,self.y)[0])

    def spline_resample(self, xnew):
        s = spi.splrep(self.x, self.y)
        return Signal(xnew, spi.splev(xnew, s))

class Error(Signal):

    def __init__(self, a, b):
        self.x = a.x
        self.y = np.abs(a.y - b.y)

    def plot(self, title):
        self._plot(title)
        py.plot(self.x, self.y, 'bo-')
        py.ylim([0.0,.5])

def grid(n): return np.linspace(0.0,1.0,n)
def sample(f, x): return Signal(x, f(x))

def random_offsets(n, amt=.5):
    return (amt/n) * (npr.random(n) - .5)

def jittered_grid(n, amt=.5):
    return np.sort(grid(n) + random_offsets(n,amt))

def f(x):
    t = np.pi * 2.0 * x
    return np.sin(t) + .5 * np.sin(14.0*t)

n = 30
m = n + 1

# Signals
even   = sample(f, np.r_[1:n+1] / float(n))
uneven = sample(f, jittered_grid(m))
hires  = sample(f, grid(10*n))

sinc   = uneven.sinc_resample(even.x)
spline = uneven.spline_resample(even.x)

sinc_err   = Error(sinc, even)
spline_err = Error(spline, even)

# Plot Labels
sn = lambda x,n: "%sly Sampled (%s points)" % (x,n)
r  = lambda x: "%s Reconstruction" % x
re = lambda x: "%s Error" % r(x)

plots = [
    [even,       sn("Even", n)],
    [uneven,     sn("Uneven", m)],
    [sinc,       r("Sinc")],
    [sinc_err,   re("Sinc")],
    [spline,     r("Cubic Spline")],
    [spline_err, re("Cubic Spline")]
]

for i in range(0,len(plots)):
    py.subplot(3, 2, i+1)
    p = plots[i]
    p[0].plot(p[1])

py.show()

Bel metodo e codice. Ma per con alcuni abbandoni (ad es. [0 1 - 3 4 5 - 7 8] T), che penso sia la domanda dei PO, i sinc nella matrice non sono tutti 0? Certo, ci sono modi per risolverlo, ma. tj=jT
denis,

A quanto ho capito, la domanda del PO riguarda i campioni che vengono eliminati e / o ritardati. Questo metodo è fondamentalmente solo un sistema di equazioni sovradeterminato, quindi i campioni rilasciati vengono visualizzati solo come sconosciuti (non come punti dati con un valore pari a 0). O forse non è quello che stai chiedendo?
datageist

Cosa succede se sono tutti numeri interi (T = 1)? Supponiamo di avere punti dati [ ] per , un insieme di numeri interi diversi da zero, ad esempio {-1 1} o {-2 -1 1 2}; non è l'interpolato , indipendentemente da - o ho perso qualcosa? tjj,yjjJy0=0yj
denis,

Se le frequenze di campionamento sono esattamente identiche (con punti mancanti), la matrice di interpolazione sarà sparsa (poiché ogni uscita dipende da un solo ingresso). In generale, la frequenza media di campionamento dei campioni non uniformi deve essere maggiore della frequenza di ricostruzione uniforme. In altre parole, dovrai ricostruire a una velocità inferiore per "colmare le lacune" (T> 1 per il tuo esempio). Vedo il tuo punto però.
datageist

2
Le risposte come questa sono oro puro.
Ahmed Fasih,

6

Sembra un problema di conversione asincrona della frequenza di campionamento. Per convertire da una frequenza di campionamento a un'altra, possiamo calcolare la rappresentazione del tempo continuo del segnale eseguendo un'interpolazione sincera, quindi ricampionare alla nostra nuova frequenza di campionamento. Quello che stai facendo non è molto diverso. È necessario ricampionare il segnale per avere tempi di campionamento fissi.

Il segnale di tempo continuo può essere calcolato coinvolgendo ogni campione con una funzione sinc. Dato che la funzione sinc continua all'infinito, usiamo qualcosa di più pratico come un sinc finestra di una lunghezza finita pratica. La parte difficile è che, poiché i campioni si muovono nel tempo, potrebbe essere necessario utilizzare un sinc con un diverso sfasamento per ciascun campione durante il ricampionamento.

Segnale di tempo continuo dal segnale campionato:

x(t)=n=x[n]sinc(tnTsTs)

dove è il tempo di campionamento. Nel tuo caso, tuttavia, il tempo di campionamento non è fisso. Quindi penso che sia necessario sostituirlo con il tempo di campionamento in quel campione.Ts

x(t)=n=x[n]sinc(tnTs[n]Ts[n])

Da questo puoi ricampionare il segnale:

y[n]=x(nTns )

dove è il tempo di campionamento desiderato.Tns

Mettendo tutto insieme ottieni:

y[m]=n=x[n]sinc(mTnsnTs[n]Ts[n])

Poiché ciò non è causale o trattabile, la funzione sinc può essere sostituita con una funzione di supporto finito e i limiti della somma vengono adeguati di conseguenza.

Lascia che kernel (t) sia una finestra sinc o un'altra funzione simile di lunghezza 2k quindi:

y[m]=n=kkx[n]kernel(mTnsnTs[n]Ts[n])

Spero che questo aiuti ..., ma potrei aver fatto un errore lungo la strada e potrebbe essere un po 'intensivo in matematica. Consiglierei di ricercare la conversione della frequenza di campionamento per ulteriori informazioni. Forse qualcun altro qui potrebbe dare anche una spiegazione o una soluzione migliore.


L'utilizzo di una funzione sinc per ricostruire una versione continua di un segnale richiede che i campioni siano equidistanti, quindi la funzione sinc dovrà adattarsi alla spaziatura dei campioni. Potrebbe essere piuttosto difficile da implementare.
user2718

sì, non sarebbe molto efficiente fare esattamente come visto qui. Richiederebbe il calcolo di nuovi coefficienti del kernel per ogni diverso tempo di campionamento. Tuttavia, potrebbe essere calcolata una raccolta di diversi kernel e il tempo è stato quantificato in uno di questi. Ci sarebbe un colpo di prestazione relativo all'errore di quantizzazione.
Jacob,

È inoltre possibile pre-calcolare una singola tabella di ricerca sinc e interpolare tra i punti di quella tabella di ricerca.
jms

5

Penso che la risposta di Jacob sia molto praticabile.

Un metodo più semplice che probabilmente non è altrettanto efficace in termini di introduzione della distorsione è fare l'interpolazione polinomiale. Userei l'interpolazione lineare (facile, non altrettanto buona per quanto riguarda le prestazioni del segnale) o le spline cubiche (ancora non troppo difficile, migliori prestazioni del segnale) per produrre campioni in qualsiasi momento desiderato dai campioni temporali arbitrari.


1
La tua risposta sembra molto più semplice da implementare di quella di Jacob, quindi ci sono andato prima io. Sembra funzionare, ma non ho ancora eseguito un set completo di test.
FigBug

1
@FigBug -Se hai tempo, aggiungi un commento con la tua soluzione finale.
user2718

2

(Un mese dopo) ci sono due scelte principali per qualsiasi metodo di interpolazione:
1) il numero di punti dati più vicini al punto mancante da usare, 2 4 6 ... 2) la classe di funzioni di base da usare: lineare, polinomiale, seno-coseno (Fourier), cubico a tratti (B-spline o spline interpolante), sincero ... (La scelta 0 è se usare il metodo e il codice di qualcun altro, o fai-da-te.)Nnear

Adattare una linea retta ai punti è facile: 2 punti [-1, ], [1, ]: stimare punti con media : media general : vedi ad es. Ricette numeriche p. 781: adatta una linea e stima . Uno può adattarsi a quadratica, cubica, seno-coseno ... allo stesso modo.Nnear
y1y1
[ x i , y i ] x i = 0y0(y1+y1)/2
[xi,yi]xi=0
y i [ x i , y i ]y0yi
[xi,yi]
y 0aa+bxy0a

Capisco che hai dati distribuiti uniformemente con alcuni punti mancanti, giusto?
Quanto funziona l'interpolazione lineare per questo caso?
Bene, proviamo cos con = 0.25: 1 0 -1 0 1 0 -1 0 ... 2 vicini di qualsiasi punto media a 0, terribile. 4 vicini: media di [1 0 (mancante -1) 0 1] = 1/2, terribile. (Prova il filtro a 4 vicini [-1 3 3 -1] / 4 su questo.)f2πftf


L'interpolazione lineare con 4 o 6 o 8 vicini potrebbe funzionare abbastanza bene per i tuoi dati.
Suggerirei di iniziare con un metodo che capisci a fondo prima di immergerti nelle spline, come se fosse sincero ... anche se possono essere anche divertenti.


Un altro metodo abbastanza diverso è la ponderazione a distanza inversa . È facile da implementare (vedi idw-interpolazione-con-python su SO), funziona anche in 2d 3d e fino, ma è difficile da analizzare teoricamente.

(Ovviamente, NESSUN metodo di interpolazione singola può eventualmente adattarsi a miliardi di combinazioni di
[segnale, rumore, metrica di errore, funzione di test] che si verificano nella realtà.
Esistono più metodi al mondo, con più manopole, di funzioni di test.
Tuttavia di metodi e funzioni di test potrebbero essere utili.)


1

Se lavori con matlab puoi farlo lavorando con la serie.

time  % is your starting vector of time

data % vector of data you want to resample 

data_TS = timeseries(data,time); % define the data as a timeseries 

new_time = time(0):dt:time(end); % new vector of time with fixed dt=1/fs

data_res = resample(data_TS,new_time); % data resampled at constant fs

0

Prima di iniziare una lavorazione esotica potresti provare qualcosa di semplice come questo (pseudo codice - nessuna interpolazione, ma che potrebbe essere aggiunto)

TimeStamp[]  //Array of Sensor TimeStamps -NULL terminated – TimeStamp[i] corresponds to Reading[i]
Reading[]      //Array of Sensor Readings       -NULL terminated

AlgorithmOut   //Delimited file of of readings in fixed sample time (5ms) 
CurrentSavedReading = Reading[0]

SampleTime=TimeStamp[0] //ms virtual sample time, 5ms fixed samples

i = 0 // loop index
While(TimeStamp[i] != NULL)
{
   FileWrite (CurrentSavedReading, AlgorithmOut)//write value to file
   SampleTime = SampleTime + 5//ms
   if(SampleTime > TimeStamp[i])
   {
      i++
      CurrentSavedReading = Reading[i]
   }
}

0

La risposta di IMHO Datageist è corretta, la risposta di Jacob non lo è. Un modo semplice per verificare ciò è che l'algoritmo suggerito da datageist è garantito per interpolare attraverso i campioni originali (assumendo una precisione numerica infinita), mentre la risposta di Jacob non lo fa.

  • Per il caso di campionamento uniforme, l'insieme di funzioni sinc è ortogonale: se ogni funzione sinc spostata viene discretizzata sui campioni di input, formano una matrice di identità infinita. Questo perché sin (n pi) / (n pi) è zero per tutti n tranne n = 0.
  • Tuttavia, questa proprietà non può essere semplicemente estrapolata al caso non uniforme: un insieme simile di funzioni sinc, discretizzate sui campioni di input, produrrà una matrice non banale. Pertanto, i contributi dei campioni circostanti non saranno pari a zero e la ricostruzione non interpolerà più attraverso i campioni di input.
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.