Connexions

You are here: Home » Content » Segnali nel tempo: soglie e filtri
Content Actions

Segnali nel tempo: soglie e filtri

Module by: Davide Rocchesso

Summary: Nell'elaborazione di segnali catturati da sensori c'è necessità di effettuare alcune operazioni ricorrenti, quali il filtraggio o la sogliatura.

Isteresi e soglie

L'isteresi è quella proprietà dei sistemi che li fa reagire con un certo ritardo alle forze ad essi applicate. Ad esempio, all'atto di sedersi su una poltrona imbottita, la relazione tra forza e compressione del cuscino può essere di forma isteretica, ed evidenza di ciò si può avere dall'impronta lasciata nel momento in cui ci si rialza. Nel design dell'interazione, l'isteresi viene introdotta deliberatamente per ritardare le azioni del sistema rispetto all'accadere di eventi. Per esempio, il click del mouse in una certa area può attivare un menu, la cui scomparsa viene però ritardata rispetto all'uscita da quella regione, in modo da consentire all'utente di esplorare le voci del menu anche con percorsi del mouse imperfetti. Nei regolatori automatici, quale è ad esempio il termostato per la regolazione di temperatura, è spesso presente un elemento isteretico per evitare commutazioni spurie, ad esempio dovute ai rimbalzi degli interruttori. Il termostato, in particolare, attiva il bruciatore quando la temperatura scende al di sotto di una soglia (threshold) A A, ma non lo spegne fintantoché la temperatura non raggiunge una seconda soglia B>A B A . Quindi, l'accensione e spegnimento del bruciatore dipendono dalla storia del segnale di temperatura. Ciò previene sequenze rapide di accensione/spegnimento per oscillazioni di temperatura intorno alla soglia. Nella sua forma più stilizzata, un sistema con isteresi è bistabile. Cioè caratterizzato da due stati stabili. Quindi, per programmare un interruttore con isteresi è sufficiente utilizzare una variabile boolean acceso e due soglie basso e alto:

	  if (acceso) {
	     if (signal < basso) {
	        // operazioni di spegnimento
	        acceso = false;
	     }
	  }
	  else //spento
	     if (signal > alto) {
	        // operazioni di accensione
	        acceso = true;
	     }

	
Problem 1
Si scriva un programma Processing che disegni la curva della pressione acustica catturata dal microfono. A basse pressioni la curva sia disegnata in verde. Quando la pressione acustica supera una soglia superiore la curva deve diventare di colore rosso, e tornare ad essere verde quando torna al di sotto di una soglia inferiore.
[ Click for Solution 1 ]
Solution 1
Una soluzione che fa uso della libreria audio Minim è la seguente:
	      
import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=80;
int alto=120;
 
void setup()
{
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  level = int(in.left.level()*HEIGHT);
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - level);
  x = (x+1)%WIDTH;
  prevLevel = level;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}

	    
[ Hide Solution 1 ]
Problem 2
Si modifichi il codice di 1 in modo da ottenere la curva di livello di un noise gate. Quando il livello scende al di sotto della soglia verde, si applica uno smorzamento che gradualmente forza il segnale ad essere inudibile. Quando il livello supera la soglia rossa, si applica una amplificazione che riporta il segnale al livello originale.
[ Click for Solution 2 ]
Solution 2
Nella soluzione seguente il segnale viene abbattuto, quando scende al di sotto della soglia inferiore, mediante moltiplicazioni successive per un coefficiente gDown, il cui valore (sempre minore di uno) viene impostato in base al tempo desiderato di smorzamento ed al frame rate. Lo smorzamento si considera esaurito quando il segnale di ingresso viene ad essere moltiplicato per 0.01 0.01. Analogamente, al superamento della soglia superiore, il moltiplicatore viene riportato a uno mediante moltiplicazioni successive per il coefficiente gUp, di valore sempre maggiore di uno.


import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel, levelG;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=30;
int alto=50;
float damp=1.0;
float gain = 1.0;

float decayTime = 10.0; // in seconds
float raiseTime = 2.0;
float gDown = pow(10,-2/frameRate/decayTime);
float gUp = pow(10,2/frameRate/raiseTime);

 
void setup()
{
  println("gDown = " + gDown + "gUp = " + gUp);
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  level = int(in.left.level()*HEIGHT);  
  gain = gain*damp;
  //println(gain);
  if (gain > 1.0) gain = 1.0;
  if (gain < 0.01) gain = 0.01;
  levelG = int(level*gain);
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - levelG);
  x = (x+1)%WIDTH;
  prevLevel = levelG;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
                damp = gDown;
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
                damp = gUp;
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}

	    
[ Hide Solution 2 ]
Gli esempi di sogliatura, isteresi, e gating visti nel caso dell'elaborazione del segnale audio sono di utilità generale per qualsiasi flusso di segnale proveniente da un sensore e, in particolare, anche nell'elaborazione di segnale video.

Filtri

Nella maggior parte delle applicazioni che trattano flussi di segnale provenienti da sensori, c'è la necessità di addolcire (smoothing) il profilo dei segnali stessi, in modo da evitare picchi improvvisi e rumore a rapida variabilità. Ciò si può fare con i filtri. Il più semplice filtro di smoothing è il filtro di media, il quale restituisce ad ogni istante il valor medio tra due campioni temporalmente contigui del segnale di ingresso. Interpretando i segnali nel dominio delle frequenze, l'operazione di smoothing corrisponde ad un filtraggio passa-basso, cioè ad una attenuazione delle frequenze elevate. Infatti, le variazioni repentine corrispondono a componenti di frequenza elevata. Le capacità di smoothing del filtro di media sono limitate, e per ottenere degli smoothing più decisi (maggiore attenuazione delle alte frequenze) bisogna fare medie pesate di un numero maggiore di campioni del segnale di ingresso. Un filtro molto utile allo scopo è il filtro del secondo ordine simmetrico. Qualunque sia l'ordine del filtro, la sua realizzazione come media pesata di campioni del segnale di ingresso può essere effettuata convenientemente appoggiandosi alla realizzazione a buffer circolare. In questo caso, la lettura avverrà da un certo numero di celle contigue a quella puntata da OUT, queste letture saranno pesate per i coefficienti del filtro, e il puntatore IN coinciderà con il puntatore OUT.
Problem 3
Si modifichi il codice di 1 pre-filtrando il segnale di livello con un filtro del secondo ordine simmetrico.
[ Click for Solution 3 ]
Solution 3
Nel codice qui proposto si possono sperimentare diversi livelli di smoothing, variando i valori di a0 e a1. Più questi valori sono vicini, maggiore è l'entità dello smoothing. Per non alterare con il filtraggio l'ampiezza generale del segnale è opportuno che il doppio di a0, sommato ad a1, dia come risultato 1 1.
import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=80;
int alto=120;

float GAIN = 10;
int BUFLEN = 5;
float[] buffer = new float[BUFLEN];
int bufPoint=2, bufPoint1=1, bufPoint2=0; 
float a0 = 0.3, a1 = 0.4; //smoothing
//float a0 = 0, a1 = 1; // no smoothing
void setup()
{
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  buffer[bufPoint] = in.left.level();
  level = int((a0*buffer[bufPoint] + a1*buffer[bufPoint1] + a0*buffer[bufPoint2])*GAIN*HEIGHT);
  bufPoint = (bufPoint+1)%BUFLEN;   bufPoint1 = (bufPoint1+1)%BUFLEN;   bufPoint2 = (bufPoint2+1)%BUFLEN;
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - level);
  x = (x+1)%WIDTH;
  prevLevel = level;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}

[ Hide Solution 3 ]
Come si può dedurre dalla sperimentazione proposta in 3, prendendo medie pesate di pochi campioni di segnale l'effetto di smoothing non è macroscopico. Per avere un filtraggio più radicale bisogna usare filtri di ordine elevato (elaborazione di molti campioni di segnale) ovvero ricorrere a filtri ricorsivi.
Un filtro è un oggetto che, preso un segnale di ingresso, produce un segnale di uscita. Quando la produzione di un campione del segnale di uscita si ottiene elaborando campioni precedenti del segnale di uscita stesso (oltre che del segnale di ingresso) si dice che il filtro è ricorsivo. La ricorsività consente filtraggi radicali a complessità modeste. Ad esempio, il filtro del primo ordine rappresentato dall'equazione alle differenze
yn=ayn-1+1-axn y n a y n 1 1 a x n (1)
è molto efficace come filtro di smoothing. E' importante mantenere la stabilità del filtro, cioè evitare che un segnale di ingresso limitato produca un'uscita illimitata. Nel caso del filtro ricorsivo del primo ordine, ciò è garantito se |a|<1 a 1 . Lo smoothing del segnale è giocoforza accompagnato da una certa latenza. Un segnale a gradino presentato in ingresso provoca in uscita un segnale che raggiunge asintoticamente il livello del gradino. Nel caso del filtro ricorsivo del primo ordine, la transizione è tanto più lenta quanto più a a è prossimo a 1 1. Per la precisione, il numero di campioni necessari per raggiungere il 99 per cento del valore finale è pari a n=-2log10a n -2 10 a . Ad esempio, con a=0.9 a 0.9 la transizione dura circa 44 campioni. Naturalmente, la relazione a=10-2n a 10 -2 n consente di ricavare il valore del coefficiente che consente una transizione in n n campioni. Tale formula è stata usata anche per impostare i tempi di apertura e chiusura del noise gate.
Problem 4
Si modifichi il codice di 1 pre-filtrando il segnale di livello con un filtro ricorsivo del primo ordine.
[ Click for Solution 4 ]
Solution 4
Si provino diversi valori di a nel codice qui proposto, mantenendo il vincolo di stabilità.

import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=80;
int alto=120;

float GAIN = 10;
float currentLev;
float prevLev = 0;
float a = 0.95; // the closer to 1, the smoother the signal

void setup()
{
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  currentLev = a*prevLev + (1 - a)*in.left.level(); 
  prevLev = currentLev;
  level = int(currentLev*GAIN*HEIGHT);  
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - level);
  x = (x+1)%WIDTH;
  prevLevel = level;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}

	  
[ Hide Solution 4 ]
Particolare importanza rivestono i filtri ricorsivi del secondo ordine, i quali fungono da modello per la realizzazioni di risonanze. Una risonanza elementare si manifesta come oscillazione smorzata. Se i coefficienti del filtro del secondo ordine sono opportunamente scelti in modo da produrre una risposta ai limiti della stabilità, quello che si ottiene è una realizzazione dell'oscillatore, alternativa a quelle proposte in (Riferimento).

Comments, questions, feedback, criticisms?

Discussion forum

Send feedback