Segnali e sistemi in Python - campioni, convoluzione e filtri
In questa pagina 8
Il programma del corso comprende le "attività pratiche e dimostrazioni al calcolatore" (Matlab o Python): rappresentare segnali, convolvere, calcolare trasformate con la FFT. Gli esami contengono una domanda di calcolo numerico (FFT e zero-padding - TF, TFtd e asse delle pulsazioniLa FFT dei campioni di un segnale a durata finita, moltiplicata per il passo $T_c$, approssima la trasformata di Fourier: $X(\omega_k)\approx T_c,\mathtt{fft}(x,M)[k]$ con $\omega_k=\frac{2\pi k}{MT_c}$ (e fattore di fase $e^{-j\omega t_0}$ se l'asse parte da $t_0$). Lo zero-padding ($M>N$) infittisce i punti della stessa TFtd senza aggiungere informazione; la risoluzione dipende dalla durata osservata. Per un segnale reale $|X|$ è simmetrico: il picco in $k$ ha un gemello in $M-k$.FFT e zero-padding - TF, TFtd e asse delle pulsazioni →). Questa nota mostra gli strumenti di base con numpy; il codice è stato eseguito e i risultati riportati sono quelli ottenuti. (I grafici si ottengono con matplotlib, per esempio plt.plot(t, y); non sono riprodotti qui.)
Rappresentare un segnale
Un segnale continuo si campiona con un passo piccolo (qui s): si usa un vettore di tempi t e il vettore dei valori x = f(t). L'asse dei tempi serve sempre: senza di esso non si sa a quale istante appartiene ogni campione.
import numpy as np
from scipy.signal import lfilter
rect = lambda t: (np.abs(t) < 0.5).astype(float) # finestra rettangolare
u = lambda t: (t >= 0).astype(float) # gradino
dt = 1e-3
t = np.arange(-5, 5, dt) # asse dei tempinp.sinc(x) è già , cioè la definizione di usata nel corso (in Matlab: sinc, con la stessa convenzione; non è ). Gli operatori *, +, ** agiscono elemento per elemento.
Area ed energia. Gli integrali diventano somme moltiplicate per il passo (approssimazione rettangolare):
x = np.exp(-3*t) * u(t)
print(np.sum(np.abs(x)**2) * dt) # energia -> 0.16717 (esatta 1/6 = 0.16667)L'errore è dell'ordine del passo: qui il campione in vale (il gradino numerico usa ), mentre la convenzione dell'emivalore darebbe , e la differenza è circa , proprio lo scarto osservato (Segnali - supporto, area, valor medio, energia e potenzaUn segnale è una funzione del tempo (continuo $t$ o discreto $n$). Si descrive con pochi numeri: estensione, area, valor medio, energia $\int|x|^2$ e potenza (energia media). Energia finita implica potenza nulla; potenza finita non nulla implica energia infinita; per i segnali periodici tutto si calcola su un periodo.Segnali - supporto, area, valor medio, energia e potenza →).
Convoluzione di segnali continui
np.convolve(x, y) calcola la convoluzione discreta . Per approssimare l'integrale di convoluzione (Sistemi LTI, risposta impulsiva e convoluzioneUn sistema lineare tempo-invariante (LTI) è completamente descritto dalla sua risposta impulsiva $h=\Sigma[\delta]$: l'uscita è la convoluzione $y=x*h$, cioè $y(t)=\int x(u)h(t-u)du$ (somma $\sum_k x(k)h(n-k)$ nel discreto). Il teorema discende da linearità e tempo-invarianza applicate alla scomposizione del segnale in impulsi.Sistemi LTI, risposta impulsiva e convoluzione →) servono due accorgimenti:
- fattore : ;
- asse dei tempi: l'uscita comincia all'istante (somma degli istanti iniziali, per la regola del supporto in Calcolo della convoluzione e sue proprietàIl supporto della convoluzione è la somma dei supporti, $\operatorname{rect}*\operatorname{rect}=\Lambda$, e due esponenziali causali danno $(e^{-bt}-e^{-at})/(a-b)$. Si calcola con il metodo grafico a casi (ribaltare, traslare, individuare gli intervalli di sovrapposizione). Proprietà: lineare, commutativa, associativa, $\delta$ è l'elemento neutro, la traslazione si somma, l'area è il prodotto delle aree.Calcolo della convoluzione e sue proprietà →) e ha campioni.
x = rect(t)
z = np.convolve(x, x) * dt
tz = t[0] + t[0] + np.arange(len(z)) * dt
print(z.max(), tz[z.argmax()], z.sum()*dt) # 1.0 -0.001 1.0Il risultato è il triangolo : picco in e area (regola dell'area).
h = np.exp(-t) * u(t)
x = rect((t - 1) / 2)
y = np.convolve(x, h) * dt
ty = t[0] + t[0] + np.arange(len(y)) * dtConfrontando in alcuni istanti con la formula esatta per e per :
| numerico | esatto | |
|---|---|---|
(errori dell'ordine di ).
Convoluzione discreta esatta. Per le sequenze non serve nessun fattore:
print(np.convolve([1, 2, 1], [1, -1, 2])) # [1 1 1 3 2]Filtri con equazioni alle differenze
Un sistema (Trasformata Z ed equazioni alle differenzeLa trasformata Z, $X(z)=\sum x(n)z^{-n}$, è la versione discreta di Laplace (la TFtd è $X(e^{j\omega})$). Un sistema descritto da un'equazione alle differenze $\sum a_ky(n-k)=\sum b_kx(n-k)$ ha $H(z)=\frac{\sum b_kz^{-k}}{\sum a_kz^{-k}}$ ed è BIBO stabile (se causale) quando tutti i poli stanno dentro il cerchio unitario, $|p|<1$. Cenni.Trasformata Z ed equazioni alle differenze →) si simula con lfilter(b, a, x), dove b e a sono i coefficienti e di :
d = np.zeros(8); d[0] = 1 # impulso
print(lfilter([1], [1, -0.5], d)) # 1, 0.5, 0.25, ...L'uscita è per , cioè la risposta impulsiva attesa . In Matlab la funzione omonima è filter(b, a, x). Nel caso FIR (a = [1]) lfilter coincide con la convoluzione con b.
Aliasing
Si campiona a Hz e a Hz:
n = np.arange(10)
print(np.allclose(np.cos(2*np.pi*7*n/10), np.cos(2*np.pi*3*n/10))) # TrueI due toni producono gli stessi campioni: dopo il campionamento a Hz il tono a Hz non si distingue da quello a Hz (Teorema del campionamento, interpolazione e aliasingTeorema di Shannon: un segnale a banda limitata $\omega_M$ si ricostruisce esattamente dai campioni se $T_c<\pi/\omega_M$ (frequenza di campionamento maggiore di quella di Nyquist $2f_{\max}$), con la formula di interpolazione ideale $x(t)=\sum_nx(nT_c)\operatorname{sinc}\left(\frac{t-nT_c}{T_c}\right)$. Sotto Nyquist c'è aliasing: le frequenze alte si confondono con quelle basse e l'informazione è persa.Teorema del campionamento, interpolazione e aliasing →).
Somme parziali della serie di Fourier e fenomeno di Gibbs
def S(t, K):
s = 0.5 * np.ones_like(t)
for k in range(1, K + 1, 2):
s += 2/(k*np.pi) * np.sin(k*np.pi/2) * np.cos(k*np.pi*t)
return s
tt = np.linspace(0.3, 0.7, 200001)
for K in (9, 49, 199):
print(K, S(tt, K).max()) # 1.0912 1.0896 1.0895La somma parziale dell'onda quadra (Serie di Fourier - analisi e sintesiUn segnale periodico di periodo $T$ si scrive come somma di esponenziali in relazione armonica, $x(t)=\sum_ka_ke^{jk\omega_0t}$ con $\omega_0=2\pi/T$, e i coefficienti si ottengono per proiezione, $a_k=\frac1T\int_Tx(t)e^{-jk\omega_0t}dt$. L'ortogonalità degli esponenziali dà la formula; la convergenza è in media quadratica (Riesz-Fischer), con il fenomeno di Gibbs nei salti.Serie di Fourier - analisi e sintesi →) ha sempre un picco di circa vicino al salto: il fenomeno di Gibbs, che non scompare aumentando .
Verificare numericamente le proprietà dei sistemi
Per un sistema (una funzione che trasforma un vettore) si possono eseguire i test di linearità e tempo-invarianza di Sistemi e loro proprietàUn sistema trasforma un segnale di ingresso in uno di uscita, $y=\Sigma[x]$. Le proprietà da saper verificare sono: memoria (statico/dinamico), causalità, linearità (additività + omogeneità), tempo-invarianza, stabilità BIBO, invertibilità, realtà. Per negare una proprietà basta un controesempio; per affermarla serve una dimostrazione con segnali generici.Sistemi e loro proprietà → su segnali casuali. Un risultato True non è una dimostrazione, ma un risultato False è un controesempio (e quindi una dimostrazione di non linearità).
def lineare(sis, x1, x2, a=2.0, b=-3.0):
return np.allclose(sis(a*x1 + b*x2), a*sis(x1) + b*sis(x2))
def tempo_inv(sis, x, n0=3):
xr = np.roll(x, n0) # ritardo ciclico: i segnali finiscono a zero
return np.allclose(sis(xr), np.roll(sis(x), n0))Con segnali che hanno 20 campioni casuali e poi zeri: (lineare: False, tempo-invariante: True); media mobile (True, True); (True, False). Sono le tre classificazioni della tabella di Sistemi e loro proprietàUn sistema trasforma un segnale di ingresso in uno di uscita, $y=\Sigma[x]$. Le proprietà da saper verificare sono: memoria (statico/dinamico), causalità, linearità (additività + omogeneità), tempo-invarianza, stabilità BIBO, invertibilità, realtà. Per negare una proprietà basta un controesempio; per affermarla serve una dimostrazione con segnali generici.Sistemi e loro proprietà →.
Tabella Python Matlab
| Operazione | Python (numpy) | Matlab |
|---|---|---|
| asse dei tempi | t = np.arange(t0, t1, dt) |
t = t0:dt:t1 |
| esponenziale, coseno | np.exp, np.cos |
exp, cos |
| sinc normalizzato | np.sinc(x) |
sinc(x) |
| prodotto elemento per elemento | x * y |
x .* y |
| convoluzione | np.convolve(x, y) |
conv(x, y) |
| somma, integrale approssimato | np.sum(x) * dt |
sum(x) * dt |
| filtro con equazione alle differenze | lfilter(b, a, x) |
filter(b, a, x) |
| trasformata discreta | np.fft.fft(x, M) |
fft(x, M) |
| spostamento di zero al centro | np.fft.fftshift |
fftshift |
| grafico | plt.plot(t, x) |
plot(t, x) |
Le differenze da ricordare: in Python gli indici partono da e a:b esclude b; in Matlab si parte da e a:b include b; il prodotto elemento per elemento è * in numpy e .* in Matlab.
Errori comuni
- Dimenticare il fattore nella convoluzione e nell'integrale approssimato (i risultati sono sbagliati di un fattore ).
- Dimenticare l'asse dei tempi della convoluzione (la somma degli istanti iniziali).
- Usare
np.sincpensando che sia (è ). - Prendere un passo troppo grande rispetto alla scala di variazione del segnale.
Versione ripasso
- Segnali: vettori di campioni con asse dei tempi
t = np.arange(t0,t1,dt);rect,uconnp.abs,>=;np.sinc(come nel corso). Area/energia:np.sum(x)*dt,np.sum(abs(x)**2)*dt; es. (esatta ; scarto per ). - Convoluzione continua:
z = np.convolve(x,y)*dt, assetz = t[0]+t[0]+np.arange(len(z))*dt.rect*rect: picco , area . Filtro RC con : (), (), (), () contro . Discreta:np.convolve([1,2,1],[1,-1,2]). - Filtri:
lfilter(b,a,x)per (Trasformata Z ed equazioni alle differenzeLa trasformata Z, $X(z)=\sum x(n)z^{-n}$, è la versione discreta di Laplace (la TFtd è $X(e^{j\omega})$). Un sistema descritto da un'equazione alle differenze $\sum a_ky(n-k)=\sum b_kx(n-k)$ ha $H(z)=\frac{\sum b_kz^{-k}}{\sum a_kz^{-k}}$ ed è BIBO stabile (se causale) quando tutti i poli stanno dentro il cerchio unitario, $|p|<1$. Cenni.Trasformata Z ed equazioni alle differenze →); : . - Aliasing: e a Hz: stessi campioni.
- Gibbs: picco per .
- Test numerici:
False= controesempio; non lineare; non TI; media mobile lineare e TI. - Python/Matlab:
arange:;*.*;convolveconv;lfilterfilter; indici da contro da . - Errori: fattore dimenticato; asse della convoluzione;
sinc. Vedi FFT e zero-padding - TF, TFtd e asse delle pulsazioniLa FFT dei campioni di un segnale a durata finita, moltiplicata per il passo $T_c$, approssima la trasformata di Fourier: $X(\omega_k)\approx T_c,\mathtt{fft}(x,M)[k]$ con $\omega_k=\frac{2\pi k}{MT_c}$ (e fattore di fase $e^{-j\omega t_0}$ se l'asse parte da $t_0$). Lo zero-padding ($M>N$) infittisce i punti della stessa TFtd senza aggiungere informazione; la risoluzione dipende dalla durata osservata. Per un segnale reale $|X|$ è simmetrico: il picco in $k$ ha un gemello in $M-k$.FFT e zero-padding - TF, TFtd e asse delle pulsazioni →.