Está en la página 1de 36

Procesamiento Adaptativo de Señales

Dr. Ing. Marcelo Risk, Ing. Federico Milano

Índice
1. Introducción 2

2. Filtrado adaptativo 2
2.1. Estructura general de los filtros adaptativos . . . . . . . . . . . . . . 2
2.2. Identificación de los parámetros . . . . . . . . . . . . . . . . . . . . . 3
2.3. Corrección adaptativa de señales . . . . . . . . . . . . . . . . . . . . . 3
2.4. Técnicas de estimación óptima . . . . . . . . . . . . . . . . . . . . . . 4
2.5. Método de resolución directa . . . . . . . . . . . . . . . . . . . . . . . 6
2.6. Métodos iterativos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.6.1. Algoritmo de gradiente mediante el método de Newton . . . . 7
2.6.2. Método de gradiente buscado por descenso escalonado . . . . . 8
2.6.3. Algoritmo LMS de Widrow . . . . . . . . . . . . . . . . . . . 8

3. Aplicaciones del filtrado adaptativo 9


3.1. Modelización adaptativa en la síntesis de filtros FIR . . . . . . . . . . 9
3.1.1. Cancelación adaptativa de ruido . . . . . . . . . . . . . . . . . 9
3.1.2. Modelización adaptativa en la síntesis de filtros IIR . . . . . . 10
3.2. Implementación de filtros adaptativos . . . . . . . . . . . . . . . . . . 10
3.2.1. Estructura básica de un filtro adaptativo . . . . . . . . . . . . 10
3.3. Implementación del combinador lineal adaptativo . . . . . . . . . . . 14
3.3.1. Código fuente de FILTADAPT.H . . . . . . . . . . . . . . . . 16
3.3.2. Código fuente de FILTADAPT.CPP . . . . . . . . . . . . . . 16
3.3.3. Código fuente de FiltroAdaptativo.py . . . . . . . . . . . . . . 19

4. Ejemplos de aplicaciones del filtrado adaptativo 20


4.1. Ejemplo de identificación de un sistema . . . . . . . . . . . . . . . . . 20
4.1.1. Código fuente de FILTRO.CPP . . . . . . . . . . . . . . . . . 21
4.1.2. Código fuente de IdentificacionSistema.py . . . . . . . . . . . 26
4.2. Ejemplo de cancelación de interferencias . . . . . . . . . . . . . . . . 28
4.2.1. Código fuente de FILTRO2.CPP . . . . . . . . . . . . . . . . 29
4.2.2. Código fuente de CancelacionRuido.py . . . . . . . . . . . . . 34

1
1. Introducción
En el presente apunte se puede apreciar una breve introducción al procesamiento
adaptativo de señales, y su relación con las redes neuronales (RN), es especial con
las arquitecturas de RN basadas el modelo de neurona denominado perceptrón.
La segunda sección de este documento describe los fundamentos del filtrado
adaptativo, y luego una implementación de los mismos a partir de un combinador
lineal adaptativo. Finalmente se estudiarán dos aplicaciones, uno a la identificación
de un sistema y otro para la cancelación de interferencias.

2. Filtrado adaptativo
Los filtros adaptativos son aquellos filtros autoprogramables cuya respuesta en
frecuencia o función transferencia está alterada o adaptada para dejar pasar sin
degradación las componentes deseadas y atenuar las señales no deseadas, o que
generan interferencias, o reducir cualquier distorsión de seǹal de entrada.
Los primeros filtros adaptativos o de autoaprendizaje fueron descriptos por Lucky
[3], quien los utilizó en el diseño de un ecualizador para compensar la distorsión en
sistemas de transmisión de datos.
Como generalmente no existe información disponible a priori, los filtros adaptati-
vos requieren un período inicial de aprendizaje, también denominado de adaptación.
Los algoritmos utiizados en el procesamiento digital de señales han tenido su
precedente en la teoría de los sistemas adaptativos de control, si bien existen con
respecto de éstos ciertas diferencias.

2.1. Estructura general de los filtros adaptativos


Un filtro adaptativo está compuesto principalmente por tres partes:

a) El índice de performance, el cual debe ser optimizado.

b) El algoritmo que recalcula los parámetros del filtro.

c) La estructura del filtro, que realiza las operaciones requeridas sobre la señal.

El índice de performance depende de la aplicación, es decir, está determinado


por el tipo de trabajo del sistema adaptativo. En general se toma como evaluador
la minimización del error cuadrático medio e el cual generalmente es satisfactoria,
como lo muestra la ecuación 9, donde y es la salida del sistema, ymodelo es la salida
del modelo.
X
e= (y − ymodelo )2 (1)
El algoritmo de optimización es el mecanismo mediante el cual los parámetros,
optimizando el criterio, son calculados. Existen dos tipos de algoritmos, el primero

2
es no recursivo, y requiere la colección de todos los datos en una ventana temporal,
para luego resolver un sistema de ecuaciones.
El segundo tipo de algoritmo es el algoritmo del gradiente, el cual requiere la
solución de un conjunto de ecuaciones lineales por inversión de una matriz y de esta
forma los resultados no estarán disponibles en tiempo real.
En rigor los algoritmos más utilizados desde el punto de vista del procesamiento
digital son del tipo recursivo, el cual se actualiza a partir de cada entrada de la señal
de entrada o con un pequeño grupo de muestras.
Los resultados están disponibles inmediatamente y es posible el seguimiento de
señales no estacionarias. Un ejemplo de ello es el algoritmo LMS (least mean squa-
re). Como es obvio la estructura del filtro depende tanto del algoritmo como de la
aplicación.

2.2. Identificación de los parámetros


La identificación de parámetros de un sistema es un procedimiento muy utilizado
para el análisis en sistemas de control. Para controlar el comportamiento óptimo de
un sistema es necesario conocer su conducta dinámica.
Esta está usualmente dada por ecuaciones diferenciales que relacionan la entrada
y la salida del sistema. Si sólo se reconoce la estructura de las ecuaciones, pero no
los parámetros, algún tipo de algoritmo de computación podría ser aplicado para
estimarlos.
La figura 1 muestra el diagrama en bloques de un sistema de identificación adap-
tativo.
Las entradas y salidas ruidosas son medidas tanto durante la aplicación normal o
durante la prueba de especificaciones. Estas son ingresadas a un filtro con coeficientes
variables, los cuales están ajustados por un algoritmo optimizador.
Luego de la adaptación, el filtro representa el mejor modelo para el sistema, y
sus coeficientes son los parámetros identificados del sistema.

2.3. Corrección adaptativa de señales


Si para una entrada del sistema innaccesible se debe actuar sobre la señal de
salida de forma tal de realizar algún tipo de corrección de la señal, como por ejemplo
la eliminación de interferencia en la señal de electrocardiograma (ECG), o para la
separación del ECG materno del fetal, entonces en este caso la información de la
señal y de la corrección requerida es de extrema importancia. Un diagrama de este
tipo de filtro correctivo se ve en la figura 2.
Otro tipo de ejemplo muy utilizado de filtro adaptativo que se conoce como filtro
de predicción lineal, es aquel que provee a su salida las salidas futuras del proceso.
La misión específica de un filtro de predicción lineal es la de estimar la salida
futura del proceso como una combinación lineal de las salidas presente y pasada.
Originariamente los filtros de predicción se utilizaron para predecir las trayecto-
rias de un blanco en movimiento en aplicaciones militares, y hoy en día se utilizan

3
ruido

e(t) s(t)
Sistema +

ruido
+
Criterio
de Error
Modelo

Ajuste del
Modelo

Figura 1: Diagrama en bloques de un sistema de identificación adaptativo.

para predecir la evolución de procesos muy diversos. La figura 3 muestra el dDia-


grama en bloques de un filtro de prediccion lineal.
La señal de entrada s, y versión retardada de la misma son enviadas al procesador
adaptativo, el cual debe intentar predecir la entrada para hacer que con y y d se
calcule el error, buscando que este último se minimize. Los filtros de predicción se
utilizan además en decodificación de señales y reducción de ruido.

2.4. Técnicas de estimación óptima


Como se dijo anteriormente el error es la resta (según la aplicación) entre la
salida del sistema y la del modelo:

e = y − ymodelo (2)
En el caso de señales discretas este será:

en = yn − wn (3)
si para simplificar la nomenclatura hacemos ymodelo = w:

e n = yn − wn (4)

4
Señal a ser corregida

Ruido Σ
Señal corregida
Filtro
Adaptativo

Algoritmo
Adaptativo

Conocimiento
apriori de
señal y ruido

Criterio

Figura 2: Diagrama en bloques de un sistema de correccion adaptativa de señales.

s d

+
y
Procesador
Delay
Adaptativo Σ
-

error

Figura 3: Diagrama en bloques de un filtro de prediccion lineal.

5
si tomamos para identificar un modelo tipo ARMA:
Pm −j
W (z) j=0 βj z
= (5)
1 − pi=0 αi z −j
P
X(z)
de donde:
p m
0 0
X X
wn = αi wn−i + βj xn−j (6)
i=1 j=0

Entonces para el error entre la salida del sistema físico y modelo se utiliza la
ecuación 3, luego:
p m
X X
yn = αi yn−i + βj xn−j (7)
i=1 j=0
p m
0 0
X X
en = (αi − αi )yn−i + (βj − βj )xn−j (8)
i=1 j=0

El error cuadrático medio (ECM ) es una magnitud se calcual como:

ξ = E(e2n ) (9)
La expresión anterior representa en el espacio generado por los coeficientes αi y
βj un hiperparaboloide de dimensiones p + m + 1, que posee un mínimo absoluto en
el punto dado por:

0
αj = αj 1≤i≤p
0 (10)
βj = βj 0≤j≤m

El problema de estimar los coeficientes se reduce a calcular los coeficientes αi y


βj que minimizan el error cuadrático ξ (ver ecuación 9).

2.5. Método de resolución directa


Los métodos de resolución directa consisten en calcular el punto en que se anulan
todas las derivadas del error cuadrático medio, para las siguientes condiciones:

δξ
=0 1≤i≤p
δαi
(11)
δξ
=0 0≤j≤m
δβj

Dado que la función ξ no posee máximos para los valores finitos de αi y βj la


solución de las ecuaciones conducen al mínimo deseado.

6
Si se trata de un proceso cuasiestacionario, debe emplearse la esperanza mate-
mática de ECM como la suma temporal dentro del período de estacionareidad del
proceso, luego el cálculo debe repetirse periódicamente, de manera de cubrir todo el
tiempo en estudio.

2.6. Métodos iterativos


Estos métodos involucran un menor número de operaciones matemáticas y pro-
0 0
veen además una estimación de αi y βj adaptable a las fluctuaciones de los coe-
ficientes verdaderos αi y βj . De los diversos métodos iterativos sólo estudiaremos
el algoritmo del gradiente mediante el método de Newton-Raphson, el método del
gradiente buscado por descenso escalonado, y el algoritmo LMS de Widrow.

2.6.1. Algoritmo de gradiente mediante el método de Newton


Este método consiste en descender sobre el hiperparaboloide de ECM siguiendo
la dirección impuesta por el método de Newton-Raphson para la determinación de
una raíz y por ende con una sencilla transformación, el mínimo de:

0 f (x0 )
f (x0 ) = (12)
x 0 − x1
entonces:

f (x0 )
x1 = x0 − (13)
f 0 (x0 )
luego:

f (xk )
xk+1 = xk − k = 0, 1, ... (14)
f 0 (xk )

La convergencia depende del valor inicial y de la naturaleza de f (x). Si ahora


tomamos:
0
f (x) = ξ (x) (15)
en vez de un cero se tendrá un mínimo, entonces:

0
ξ (x)
αik+1 = αik − µ 00 1≤i≤p
ξ (x)
0 (16)
ξ (x)
βik+1 = βik − µ 00 0≤j≤m
ξ (x)

El coeficiente µ es una constante que determina la dimensión del peso, y gobierna


la velocidad de convergencia, es decir un valor de µ demasiado pequeño requiere un
elevado número de iteraciones para alcanzar el mínimo.

7
2.6.2. Método de gradiente buscado por descenso escalonado
En este caso los coeficientes o pesos son ajustados según el gradiente en cada
paso, lo cual simplifica notablemente el cálculo.
Este método está gobernado por las ecuaciones en la cual µ es una constante
que regula el tamaño del paso. Al igual que el mt́odo anterior, un valor demasiado
pequeño de µ requerirá un elevado número de iteraciones para alcanzar un mínimo,
y un valor excesivo puede ocasionar inestabilidades en el algoritmo.
Tanto en este caso como en el anterior se parte utilizando todo el conocimiento
previo sobre los valores a estimar y luego es desplazada la estimación inicial descen-
diendo sobre la superficie de ECM en la dirección del gradiente (perpendicular a las
curvas de nivel) hasta alcanzar un mínimo.
La diferencia entre este método y el de Newton-Raphson radica en la practicidad
del algoritmo puesto que en el primero es necesario resolver una matriz inversa.
El cálculo de los coeficientes en este método se realiza con las siguientes ecuacio-
nes:

δξ
αik+1 = αik − µ 1≤i≤p
δαi
(17)
δξ
βik+1 = βik − µ 0≤j≤m
δβj
Donde el supraíndice indica el orden de iteración.

2.6.3. Algoritmo LMS de Widrow


En el algoritmo anterior se requiere la determinación del gradiente del ECM en
cada iteración, es decir se estimo el gradiente de ξ.
Bernard Widrow analizó las condiciones de estabilidad, la constante de tiempo
de convergencia, y el error de estimación del algoritmo anterior, reemplazando el
valor del gradiente por una estimación del mismo empleando una sola muestra [7].
Es decir, aproximando la esperanza matemática del ECM por el mismo pero en
cada iteración. En otras palabras se toma a e2k como una estimación directa de ξ,
entonces:

δ(e2k )
αik+1 = αik − µ 1≤i≤p
δαi
(18)
δ(e2 )
βik+1 = βik − µ k 0≤j≤m
δβj
Si tomamos la definición del ECM y elevamos al cuadrado ambos miembros,
calculamos la derivadas parciales y reemplazamos en la ecuación anterior se obtiene:

αik+1 = αik + 2µek y k−1 (19)

βik+1 = βik + 2µek xk−1 (20)

8
3. Aplicaciones del filtrado adaptativo
3.1. Modelización adaptativa en la síntesis de filtros FIR
La idea básica es asociar a un pseudofiltro las especificaciones ideales de un filtro
que generalmente no será físicamente realizable.
El pseudofiltro es únicamente conceptual; el esquema utilizado puede verse en la
figura 4.

f1
~
f2
~ Pseudofiltro

fn
~ d

x +
y
Filtro
Σ Adaptativo
- Σ

error

Figura 4: Diagrama en bloques de un sistema de filtrado adaptativo a través de un


pseudofiltro.

3.1.1. Cancelación adaptativa de ruido


La situación básica de cancelación de ruido está ilustrada en la siguiente figura
5.
Una señal es transmitida de un canal a un sensor que recibe la señal más ruido no
correlacionado n0. Esta combinación aditiva forma la primera entrada. Un segundo
sensor recibe ruido n1 el cual no está correlacionado con la señal pero sí lo está de
alguna manera con el ruido n0. Esta constituye la entrada de referencia al cancelador.
el ruido n1 es filtrado de forma tal de obtener una réplica n0.
La salida se resta de la entrada primaria para producir una salida del sistema
igual a s + n0 + y.
Ejemplo de ello son la cancelación de ruido en señales de audio (n1 es la in-
terferencia), cancelación de la interferencia en el ECG del corazón transplantado y

9
Entrada
Primaria
s+nO Salida
Fuente +
de señal Σ
nO
-
y
Entrada
Referencia

Fuente Filtro
de ruido Adaptativo
n1

error

Cancelador Adaptativo de Ruido

Figura 5: Diagrama en bloques de un sistema de cancelación de ruido con filtrado


adaptativo.

separación del ECG materno del fetal como así también la cancelación de ecos en
circuitos telefónicos de larga distancia.

3.1.2. Modelización adaptativa en la síntesis de filtros IIR


Como se ha visto anteriormente la transferencia de un filtro de respuesta infinita
al impulso (IIR) a sintetizar será:

A(z)
H(z) = (21)
1 − B(z)
El diagrama en bloques de tal filtro se puede apreciar en la figura 6.

3.2. Implementación de filtros adaptativos


3.2.1. Estructura básica de un filtro adaptativo
Los filtros adaptativos se pueden implementar tanto como filtros de respuesta
finita al impulso (FIR) o de respuesta infinita al impulso (IIR), pero en general se
prefiere el esquema FIR debido a una mayor simplicidad y estabilidad, teniendo en
cuenta que el algoritmo de adaptación es el encargado de calcular los coeficientes
del filtro, por lo cual un esquema FIR asegura la estabilidad del filtro a pesar de las
combinaciones de coeficientes calculadas.
La estructura de un filtro FIR se puede desarrollar con un combinador lineal,
cuyo diagrama se muestra en la figura 7.

10
y

x
A(z) + B(z)

Figura 6: Diagrama en bloques de un filtro adaptativo IIR.

W0
X0

W1
X1

Σ Y
W2
X2

W3

X3

Figura 7: Diagrama en bloques de un combinador lineal.

11
La expresión del combinador es:
N
X −1
Y = Wk Xk (22)
k=0

donde N es la cantidad de coeficientes u orden del filtro, X es el vector de


entrada, W es el vector de pesos, e Y es la salida.
La figura anterior muestra el combinador lineal en la forma de entrada paralelo,
pero en la mayoría de las aplicaciones es necesario ingresar los datos (muestras) en
forma serie, especialmente cuando el filtro se implementa en tiempo real y por lo
tanto las muestras van llegando una a una, en este caso se debe utilizar la forma
serie del combinador lineal, como lo muestra la figura 7.

W0
X(n)

T
W1
X(n-1)

T Σ Y(n)
W2
X(n-2)

T W3

X(n-3)

Figura 8: Diagrama en bloques de un combinador lineal serie.

En el combinador lineal con entrada serie podemos apreciar cómo se forma el


vector de entrada X con la muestra actual X(n) y las muestras anteriores X(n − 1)
a X(n − 3), para lograr el retardo entre dichas muestras se intercala un bloque de
retardo T . La expresión del combinador lineal con entrada serie es la siguiente:
N
X −1
Y (n) = Wk Xk (n − k) (23)
k=0

Como se describió en la introducción, los sistemas de lazo cerrado utilizan la


diferencia entre la salida deseada d(n) y salida actual Y (n) para calcular en error:

e(n) = d(n) − Y (n) (24)

12
por lo tanto:
N
X −1
e(n) = d(n) − Wk Xk (n − k) (25)
k=0

Elevando el error al cuadrado obtenemos el denominado error cuadrático instan-


táneo:

N −1 N −1
!2
X X
e2 (n) = d2 (n) − 2 Wk Xk (n − k) + Wk Xk (n − k) (26)
k=0 k=0

Por otro lado se define a error cuadrático medio como:


−1
 2  M X
ξ = E e (n) = e2 (n) (27)
m=0

quedando:
M
X −1 N
X −1 N
X −1 N
X −1
2
ξ= d (n) − 2 Wk rdx (n) + Wk Wl rxx (n − l) (28)
m=0 n=0 n=0 l=0

donde las ecuaciones rdx y rxx las correlaciones cruzadas entre la salida deseada
y la entrada y la autocorrelación da la entrada respectivamente.
El error cuadrático medio ξ describe una superficie de performance, cuando se
lo grafica con respecto a los pesos W , en la figura siguiente podemos observar una
superficie de performance para el caso de dos pesos, donde también apreciamos
los pesos óptimos W1 y W2 que corresponden al mínimo de ξ de la superficie de
performance.
El proceso de adaptación busca minimizar el error cuadrático medio, para lograr
este objetivo se propone el método de descenso a pasos a través de la superficie de
performance.
Con el método de descenso a pasos es posible recalcular cada peso teniendo
información acerca del peso anterior y del gradiente en el punto de peso anterior
sobre la superficie de performance, multiplicado por un coeficiente de velocidad de
convergencia, como lo indica la ecuación 29.

Wk (n + 1) = Wk (n) + µ(−5k ) (29)


En el método de adaptación basado en el error cuadrático medio, el gradiente se
define como:
 
δ e2 (n)
5k = (30)
δWk (n)
entonces sustituyendo la ecuación 30 en la 29:
 
δ e2 (n)
Wk (n + 1) = Wk (n) − µ (31)
δWk (n)

13
Se utiliza el error instantáneo porque está disponible muestra a muestra du-
rante la optimización de los pesos del filtro. Entonces desarrolando la derivada y
reemplazando por la definición de la ecuación 25, tenemos:
 
δ e(n)
Wk (n + 1) = Wk (n) − 2µe(n) (32)
δWk (n)
y reemplazando con 25, quedando:
P −1
δ d(n) − N
 
k=0 W k X k (n − k)
Wk (n + 1) = Wk (n) − 2µe(n) (33)
δWk (n)
Note que d(n) y Xk (n) son independientes de Wk , por lo tanto:

Wk (n + 1) = Wk (n) − 2µe(n)Xk (n) (34)


La ecuación 34 es la expresión final del algoritmo del método de los cuadrados
mínimos.
El coeficiente de velocidad de convergencia µ debe elegirse de forma tal de llegar
al valor óptimo lo suficientemente rápido, pero sin perder estabilidad, por lo tanto
los valores recomendados para µ deben estar en el rango descripto en la ecuación
??:
1
0<µ< (35)
20N Px
donde N es la cantidad de coeficientes del filtro, y Px es la potencia de los datos
durante el proceso de optimización:
M −1
1 X
Px = X 2 (n) (36)
M + 1 n=0
En la ecuación 36 M es la cantidad de muestras durante la optimización de los
pesos.
El combinador lineal cuando utiliza el método de los cuadrados mínimos se puede
describir con el diagrama en bloques de la figura 9, de esta forma se denomina
combinador lineal adaptativo.

3.3. Implementación del combinador lineal adaptativo


El combinador lineal adaptativo se puede implementar como una clase, en len-
guaje C++ [4], y en Python [5, 2]. Dentro de la clase FiltroAdaptativo están encap-
suladas todas las propiedades necesarias para el funcionamiento de un objeto que
maneja un vector de datos Xk, un vector de pesos Wk, y otras variables tales el
error Ek, la cantidad de elementos Cantidad y el valor de coeficiente de velocidad de
optimización Alfa, demás de las funciones necesarias para la carga de los vectores,
el cálculo de la salida y la optimización de los coeficientes del filtro.

14
W0
X(n)

T
W1
X(n-1)

T Σ Y(n)
W2
X(n-2)

T W3
-
+
X(n-3) Σ d(n)

e(n)

Algoritmo de
cuadrados mínimos

Figura 9: Diagrama en bloques de un combinador lineal serie adaptativo.

15
3.3.1. Código fuente de FILTADAPT.H
// FILTADAPT.H

#include <math.h>
#include <stdlib.h>
#include <iostream.h>
#include <fstream.h>

class FiltroAdaptativo {
double *Xk;
double *Wk;
double Ek;
double Sk;
int Cantidad;

public:
double Alfa; // en Alfa a mu por 2

FiltroAdaptativo( int n = 20);


~FiltroAdaptativo();

double Calcula( void);


void LlenaPesos( double *Array, int cantidad);
void LlenaEntradas( double *Array, int cantidad);
void LlenaPesos( double *Array);
void LlenaEntradas( double *Array);
double MuestraSalida( void); void MuestraSet( void);
void CalculaNuevoSet( double e);
double put ( double d); double operator ()( double d)
{
return put (d);
}
int GuardaPesos( char *name); int LeePesos( char *name);
};

3.3.2. Código fuente de FILTADAPT.CPP


// FILTADAPT.CPP:

#include "filtadapt.h"
#include <stdlib.h>

FiltroAdaptativo::FiltroAdaptativo( int n)
{

16
Alfa = 1.0 / (100.0*double(n));
Ek = 1.0;
Sk = 0.0;
Cantidad = n;
Xk = new double[Cantidad];
Wk = new double[Cantidad];
for( int j = 0; j < Cantidad; j++)
{
Xk[j] = 0.0; Wk[j] = 0.0;
}
}

FiltroAdaptativo::~FiltroAdaptativo()
{
delete Xk;
delete Wk;
}

void FiltroAdaptativo::LlenaPesos( double *Array, int cantidad)


{
for( int j = 0; j < cantidad; j++)
Wk[j] = Array[j];
}

void FiltroAdaptativo::LlenaPesos( double *Array)


{
for( int j = 0; j < Cantidad; j++)
Wk[j] = Array[j];
}

void FiltroAdaptativo::CalculaNuevoSet( double e)


{
Ek = e;
for( int j = 0; j < Cantidad; j++)
{
Wk[j] = Wk[j] + Alfa*Ek*Xk[j];
}
}

void FiltroAdaptativo::MuestraSet( void)


{
cout << endl;
for( int j = 0; j < Cantidad; j++)
cout << Wk[j] << ", ";
}

17
double FiltroAdaptativo::Calcula( void)
{
double Suma = 0.0;
for( int j= 0; j < Cantidad; j++)
Suma += Wk[j] * Xk[j];
Sk = Suma;
return Sk;
}

double FiltroAdaptativo::put( double d)


{
for( int j = Cantidad-1; j > 0; j--)
Xk[j] = Xk[j-1];
Xk[0] = d;
return Calcula(); }

void FiltroAdaptativo::LlenaEntradas( double *Array, int cantidad)


{
for( int j = 0; j < Cantidad; j++)
Xk[j] = Array[j];
Calcula();
}

void FiltroAdaptativo::LlenaEntradas( double *Array)


{
for( int j = 0; j < Cantidad; j++)
Xk[j] = Array[j];
Calcula();
}

double FiltroAdaptativo::MuestraSalida( void)


{
return Sk;
}

int FiltroAdaptativo::GuardaPesos( char *name)


{
ofstream A;
A.open(name);
if( !A)
return -1; A << Cantidad << endl;
for( int j = 0; j < Cantidad; j++)
A << Wk[j] << endl;

18
A.close();
return 0;
}

int FiltroAdaptativo::LeePesos( char *name)


{
ifstream A;
A.open(name);
if( !A)
return -1;
int j;
A >> j;
if( j != Cantidad)
return -2;
for( int j = 0; j < Cantidad; j++)
A >> Wk[j];
A.close();
return 0;
}

3.3.3. Código fuente de FiltroAdaptativo.py


import sys
import math
from random import *
from pylab import *

class FiltroAdaptativo:

def __init__(self,n):
self.Ek = 1
self.Sk = 0
self.Cantidad = n
self.Alfa = 1.0 / (100.0*float(self.Cantidad)) # en Alfa va mu por 2
self.Xk = zeros(self.Cantidad)
self.Wk = zeros(self.Cantidad)

def Calcula(self):
Suma = 0.0
for j in range(self.Cantidad):
Suma += self.Wk[j] * self.Xk[j]
self.Sk = Suma

def LlenaPesos(self,Array):

19
for j in range(self.Cantidad):
self.Wk[j] = Array[j]

def LlenaEntradas(self,Array):
for j in range(self.Cantidad):
self.Xk[j] = Array[j]

def MuestraSalida(self):
print "Sk = " + self.Sk

def MuestraSet(self):
print self.Wk

def MuestraEntradas(self):
print self.Xk

def CalculaNuevoSet(self,ee):
self.Ek = ee
for j in range(self.Cantidad):
self.Wk[j] = self.Wk[j] + self.Alfa*self.Ek*self.Xk[j]

def put(self,dd):
for j in range(1,self.Cantidad):
self.Xk[self.Cantidad-j] = self.Xk[self.Cantidad-1-j]
self.Xk[0] = dd
self.Calcula()
return self.Sk

4. Ejemplos de aplicaciones del filtrado adaptativo


4.1. Ejemplo de identificación de un sistema
Los filtros adaptativos son capaces de identificar un sistema desconocido, en otras
palabras de desarrollar una modelización, el objetivo es encontrar un filtro FIR que
modelice el sistema desconocido [1].
El diagrama en bloques del proceso de modelización se muestra en la figura
3b. En los párrafos siguientes se describirá una implementación en C++ para la
identificación de un sistema, en este ejemplo el sistema desconocido es un filtro IIR
con la siguiente ecuación de recurrencia:

y(n) = ax(n) + by(n − 1) (37)


donde a y b son los coeficientes del filtro, cuya condición de estabilidad es que la
suma de los coeficientes sea menor o igual a uno.

20
A continuación se describen los pasos a seguir para lograr dicho proceso:

1) Ambos sistemas, el filtro y el sistema desconocido, deben ser excitados por un


generador de ruido aleatorio de valor medio nulo.

2) El filtro adaptativo comienza un proceso de optimización, comenzando los pesos


con valores iniciales aleatorios pequeños; utilizando el método de los cuadrados
mínimos se optimizan los pesos (coeficientes del filtro) minimizando el error.

3) La cantidad de iteraciones necesarias para la optimización se pueden calcular


buscando un error mínimo y una cantidad máxima de iteraciones, en caso de no
lograr el error mínimo.

En el ejemplo presentado el sistema desconocido (filtro IIR) y el filtro tienen una


respuesta al impulso y al escalón como lo muestran las figuras 10 y 11, respectiva-
mente, mientras que la figura 12 muestra el error2 para las 1000 primeras iteraciones
de la adaptación.

Figura 10: Respuesta del filtro IIR y del filtro adaptativo a un impulso.

4.1.1. Código fuente de FILTRO.CPP


// FILTRO.CPP: Identificacion de un sistema con un filtro

21
Figura 11: Respuesta del filtro IIR y del filtro adaptativo a un escalón.

22
Figura 12: error2 para las 1000 primeras iteraciones de la adaptación.

23
// adaptativo

#include <iostream.h>
#include <fstream.h>
#include <math.h>
#include "filtadapt.h"

void main( void) {


int CantidadCoeficientes = 20;

cout << "Orden del filtro adaptativo = ";


cin >> CantidadCoeficientes;

FiltroAdaptativo F(CantidadCoeficientes);

int Number = 5000;

ofstream Salida;
Salida.open( c:\neural\\filtro.txt);

int j;
double Px = 0.0;
double Mean = 0.0;
double *noise;
noise = new double[Number];
// Calcula ruido blnco sin valor medio
for( j = 0; j < Number; j++){
noise[j] = double(random(1000))/1000.0;
mean += noise[j];
}
Mean /= double(Number);
for( j = 0; j < Number; j++){
noise[j] -= Mean;
Px += (noise[j]*noise[j]);
}
// Calcula potencia del ruido
Px /= double(Number+1);

double x, y, d, dAnt, e;
// coeficientes del sistema a identificar
double a = 0.3;
double b = 0.7;
cout << "Coeficientes del sistema a identificar: " << endl;
cout << "y[n] = a*x[n] + b*y[n-1]" << endl;
cout << "a = ";

24
cin >> a;
cout << "b = ";
cin >> b;

dAnt = 0.0;

// ajuste valor Alfa (coeficiente de velocidad de


convergencia)
// del algoritmo LMS
F.Alfa = 1.0 / (20.0*double(CantidadCoeficientes)*Px);
cout << "Busca la solucion optima por el metodo LMS ..." << endl;
for( j = 0; j < Number; j++){
x = noise[j];
d = a*x + b*dAnt;
y = F(x);
e = d - y;
F.CalculaNuevoSet( e);
dAnt = d;
if( j == 0)
cout << "Error inicial = " << e << endl;
}
cout << "Error final = " << e << endl;
F.GuardaPesos( "c:\\neural\\pesos.txt");

// Respuesta al impulso de ambos filtros


Salida << "Respuesta al impulso" << endl;
cout << "Respuesta al impulso..." << endl;
Salida << "x\td\ty" << endl;
for( j = 0; j < 50; j++){
x = 0.0;
if( j == 25)
x = 1.0;
d = a*x + b*dAnt;
y = F(x);
Salida << x << "\t" << d << "\t" << y << endl;
dAnt = d;
}

Salida << endl;


Salida << "Respuesta al escalon" << endl;
cout << "Respuesta al escalon..." << endl;
Salida << "x\td\ty" << endl;
for( j = 0; j < 50; j++){
x = 0.0;
if( j == 25)

25
x = 1.0;
d = a*x + b*dAnt;
y = F(x);
Salida << x << "\t" << d << "\t" << y << endl;
dAnt = d;
}

delete noise;
cout << endl << "FIN";
}

4.1.2. Código fuente de IdentificacionSistema.py


import sys
sys.path.append(’....’) # poner el path de FiltroAdaptativo.py

import math
from random import *
from pylab import *
from FiltroAdaptativo import *

F = FiltroAdaptativo(20)
Number = 5000
Px = 0.0
Mean = 0.0
noise = zeros(Number)

seed()
for j in range(Number):
noise[j] = normalvariate(0.0,1.0)
Px += noise[j] * noise[j]

Px /= float(Number+1)

x = 0.0
y = 0.0
dAnt = 0.0
e = 0.0
a = 0.3
b = 0.7

F.MuestraSet()

F.Alfa = 1.0 / (20.0*float(F.Cantidad)*Px)

26
print F.Alfa

for j in range(Number):
x = noise[j]
d = a*x + b*dAnt
y = F.put(x)
e = d - y
F.CalculaNuevoSet(e)
dAnt = d

F.MuestraSet()

N = 100
xx = arange(N)

yy = zeros(N)
zz = zeros(N)

yy[N/2] = 1.0

F.LlenaEntradas(zeros(20))

for j in range(1,N):
zz[j] = F.put(yy[j])
yy[j] = a*yy[j] + b*yy[j-1]

subplot(211)
plot(xx,yy,’b’)
ylim(-0.1,1.1)
title(’Filtrado con $y(n)=ax(n)+by(n-1)$’)

subplot(212)
plot(xx,zz,’g’)
ylim(-0.1,1.1)
title(’Filtrado con $FiltroAdaptativo$’)

savefig("EjercicioIdentificacionSistemaRespImpulso.eps")
close()

yy = zeros(N)
zz = zeros(N)

for j in range(N/2,N):
yy[j] = 1.0

27
F.LlenaEntradas(zeros(20))

for j in range(1,N):
zz[j] = F.put(yy[j])
yy[j] = a*yy[j] + b*yy[j-1]

subplot(211)
plot(xx,yy,’b’)
ylim(-0.1,1.1)
title(’Filtrado con $y(n)=ax(n)+by(n-1)$’)

subplot(212)
plot(xx,zz,’g’)
ylim(-0.1,1.1)
title(’Filtrado con $FiltroAdaptativo$’)

savefig("EjercicioIdentificacionSistemaRespEscalon.eps")
close()

4.2. Ejemplo de cancelación de interferencias


En muchas ocasiones una señal de gran ancho de banda w(n) se encuentra inter-
ferida por la suma de ruido de ancho de banda estrecho s(n), conformando la señal
suma x(n); las dos señales que forman x(n) no están correlacionadas:

x(n) = w(n) + s(n) (38)


La solución a este problema es diseñar un filtro de ancho de banda estrecho
(notch), pero en ciertas ocasiones la banda de ruido no es conocida o varía lentamente
en el tiempo, para lo cual los filtros adaptativos pueden utiizarse, pues en cualquiera
de las dos situaciones son capaces de optimizar su funcionamiento [6].
El filtro adaptativo debe ser capaz de sustraer s(n) de x(n), teniendo en cuenta
que debe predecir la componente en cada muestra a través de las muestras anteriores,
dicha predicción se logra gracias al ancho de banda estrecho de s(n), y debido a que
como el ancho de banda de s(n) es mucho menor que el de w(n), las muestras de
s(n) tienen tienen una alta correlación; por otro lado las muestras de w(n) tienen
una muy baja correlación.
En la figura 13 podemos apreciar el diagrama en bloques del sistema de can-
celación de interferencias, donde el decorrelacionador introduce un retardo de T
muestras, con el propósito de decorrelacionar las componentes s(n) de la señal x(n).

28
x(n) + salida
Σ
-

s’(n)
retardo Filtro
T Adaptativo

decorrelacionador e(n) = x(n) - s’(n)

Figura 13: Diagrama en bloques de un filtro adaptativo para cancelación de interfe-


rencias.

La salida del filtro adaptativo tiene la siguiente expresión:


N −1
0
X
s (n) = Wk (n)x(n − k − T ) (39)
k=0

La señal de error e(n) se utiliza en el proceso de optimización del filtro adaptativo,


con el método de los cuadrados mínimos. En dicho proceso de optimización x(n) es
la suma de una secuencia aleatoria de valor medio nulo, sobre la cual se suma el
ruido de ancho de banda estrecha, por ejemplo un tono senoidal, o bien una suma de
dos o tres tonos puros. El retardo del decorrelacionador en la mayoría de los casos
es suficiente con una muestra de retardo.
Una vez adaptado el filtro, la señal a filtrar debe ser ingresada directamente
al filtro; eventualmente el filtro, puede reoptimizarse para seguir las variaciones de
señal interferente.
El ejemplo presentado trata de suprimir una interferencia producida por un tono
puro sobre una señal de electrocardiograma (ECG). El programa perteneciente a este
ejemplo es FILTRO2.CPP. y la figura iguiente muestra los resultados del filtrado
sobre una porción de la señal de ECG.
La figura 14 muestra el error2 para las primeras 500 iteraciones durante la adap-
tación del filtro, mientras que la figura 15 muestra en la señal superior el ECG
original sin ruido ni interferencias, en la señal central el mismo ECG pero con la
interferencia sumada, y finalmente en la señal inferior la salida del filtro adaptativo,
note en esta última el comportamiento para las primeras 20 muestras, en las cuales
el filtro adaptativo todavia no se enganchó.

4.2.1. Código fuente de FILTRO2.CPP


// FILTRO2.CPP: cancelacion de interferencias con un filtro adaptativo.

#include <iostream.h>

29
Figura 14: error2 para las primeras 500 iteraciones durante la adaptación del filtro.

30
Figura 15: La señal superior el ECG original sin ruido ni interferencias, en la señal
central el mismo ECG pero con la interferencia sumada, y finalmente en la señal
inferior la salida del filtro adaptativo, note en esta última el comportamiento para
las primeras 20 muestras, en las cuales el filtro adaptativo todavia no se enganchó.

31
#include <fstream.h>
#include <math.h>
#include "filtadapt.h"

void main( void) {

int CantidadCoeficientes = 20;

cout << "Orden del filtro adaptativo = ";


cin >> CantidadCoeficientes;
FiltroAdaptativo F(CantidadCoeficientes);

int Number = 4000;

ofstream Salida;
Salida.open("filtro2.txt");

int j;
double Px = 0.0;
double Mean = 0.0;
noise = new double[Number];
double *ecg;
ecg = new double[Number];
double *ecgl;
ecg1 = new double[Number];

double Pi = 3.14;
// Calcula ruido blanco sin valor medio mas ruido
// de senoidal
for( j = 0; j < Number; j++){
noise[j] = double(random(1000))/10000.0;
noise[j] += sin( 1000.0*2.0*Pi*double(j)/double(number));
Mean += noise[j];
}
Mean /= double(Number);
for( j = 0; j < Number; j++){
noise[j] -= Mean;
Px += noise[j]*noise[j]);
}
// Calcula potencia del ruido
Px /= double(Number+1);

double x, y, d, dAnt, dAnt2, e;

char NameInput[80] = "apb.txt";

32
ifstream In;
In.open( NameInput);
if( !In){
cout << "I can’t open the file !!!!!";
return
}

double data;
for( j = 0; j < Number; j++){
In >> data;
data /= 10.0;
ecg[j] = data;
ecg[j] += sin(
1000.0*2.0*Pi*double(j)/double(Number));
ecg1[j] = data;
In >> data;
}

// ajuste valor Alfa (coeficiente de velocidad de


convergencia)
// del algoritmo LMS
F.Alfa = 1.0 / (20.0*double(CantidadCoeficientes)*Px);
cout << "Alfa = " << F.Alfa << endl;
// Busca la solucion optima por el metodo LMS
cout << "Busca la solucion optima por el metodo LMS ..." << endl;
dAnt = 0.0;
dAnt2 = 0.0;
for( j = 0; j < Number; j++){
x = noise[j];
y = F(dAnt2);
dAnt = x;
dAnt2 = dAnt;
e = x - y;
// cout << e << endl;
F.CalculaNuevoSet( e);
if( j == 0)
cout << "Error inicial = " << e << endl;
}
cout << "Error final = " << e << endl;

Salida << endl;


Salida << "Filtrado de ruido" << endl;
cout << "Filtrado de ruido..." << endl;
Salida << "ecg\tx\ty" << endl;
dAnt = 0.0;

33
dAnt2 = 0.0;
for( j = 0; j < Number; j++){
x = ecg[j];
y = F(dAnt2);
dAnt = x;
dAnt2 = dAnt;
d = x - y;
Salida << ecgl[j] << "\t" << x << "\t" << d << endl;
dAnt = d;
}

delete noise;
delete ecg;
delete ecgl;
cout << endl << "FIN";
}

4.2.2. Código fuente de CancelacionRuido.py


import sys
sys.path.append(’...’)

import math
from random import *
from pylab import *
from FiltroAdaptativo import *

F = FiltroAdaptativo(20)

Number = 4000
ecg = zeros(Number)
ecgOrig = zeros(Number)
ecg1 = zeros(Number)
noise = zeros(Number)
Px = 0.0
Mean = 0.0

# calcula ruido blanco + senoidal


for j in range(Number):
noise[j] = normalvariate(0.0,1.0)/10
noise[j] += math.sin(1000.0*2.0*math.pi*float(j)/float(Number))
Mean += noise[j]

Mean /= float(Number)

34
for j in range(Number):
noise -= Mean
Px += noise[j]*noise[j]

Px /= float(Number+1)

fh = open(’apb.txt’)
for j in range(Number):
linea = fh.readline()
w = linea.split()
ecgOrig[j] = (float(w[0])-128.0)/10.0
ecg[j] = (float(w[0])-128.0)/10.0 +
math.sin(1000.0*2.0*math.pi*float(j)/float(Number))
ecg1[j] = (float(w[1])-128.0)/10.0

x = zeros(Number)
y = zeros(Number)
d = zeros(Number)
dAnt = 0.0
dAnt2 = 0.0
e = zeros(Number)

F.Alfa = 1.0 / (20.0*float(F.Cantidad)*Px)

# adaptacion del filtro adaptativo


for j in range(Number):
x[j] = noise[j]
y[j] = F.put(dAnt2)
dAnt = x[j]
dAnt2 = dAnt
e[j] = x[j] - y[j]
F.CalculaNuevoSet(e[j])

# filtrado del ECG


dAnt = 0.0
dAnt2 = 0.0
for j in range(Number):
x[j] = ecg[j]
y[j] = F.put(dAnt2)
dAnt = x[j]
dAnt2 = dAnt
d[j] = x[j] - y[j]
dAnt = d[j];

plot(ecgOrig[:250]+20,’g’)

35
plot(x[:250]+10,’b’)
plot(d[:250],’r’)
savefig("EjercicioCancelacionRuidoECG.eps")
close()

e2 = e*e
plot(e2[:500])
xlabel(’iteraciones’)
ylabel(’$error^2$’)
savefig("ErrorCancelacionRuidoECG.eps")
close()

Referencias
[1] Ricardo Armentano, Javier Fochesatto, and Marcelo Risk. Análisis de Señales
y Sistemas. Editorial Rocamora, 1996.

[2] Perry Greenfield and Robert Jedrzejewski. Using Python for interactive data
analysis. Association of Universities for Research in Astronomy, 2007.

[3] R. W. Lucky. Automatic equalization for digital communication. Bell Systems


Technology Journal, 44:547–588, 1965.

[4] Al Stevens and Clayton Walnut. Standard C++ Bible. IDG Books, 2000.

[5] Guido van Rossum and Fred L. Drake. Python tutorial, Release 2.5. Python
Software Foundation, docs@python.org, 2006.

[6] Bernard Widrow, John Glover, John MacCool, John Kaunitz, Charles Williams,
Robert Hearn, James Zeidler, Eugene Dong, and Robert Goodlin. Adaptive noise
cancelling: principles and applications. Proceedings of the IEEE, 63(12):1692–
1716, 1975.

[7] Bernard Widrow and Michael Lehr. 30 years of adaptive neural networks: percep-
tron, madaline, and backpropagation. Proceedings of the IEEE, 78(9):1415–1442,
1990.

36

También podría gustarte