Simulando un circuito RC simple usando un arduino y la función de transferencia de tiempo discreto equivalente del circuito

0

Supongamos que tenemos un circuito RC con la función de transferencia continua: \ $ G (s) = \ frac {1} {RCs + 1} \ $ con R = 1.6k, y C = 2uF. Usando la transformación bilineal (o mejor conocido como método de Tustin) \ $ s = \ frac {2} {T_s} \ frac {z-1} {z + 1} \ $ con el período de muestreo siendo \ $ T_s = 0.5ms \ $, Llego a la siguiente función de transferencia discreta:

\ $ G (z) = \ frac {z + 1} {13.8z-11.8} = \ frac {X (z)} {U (z)} \ $

En términos de una ecuación de diferencia (relación de recurrencia) el sistema discreto se describe así como \ $ u_n + u_ {n-1} = 13.8x_n-11.8x_ {n-1} \ $, donde \ $ x_n \ $ es la variable de salida actual, \ $ x_ {n-1} \ $ es la salida anterior. De manera similar, \ $ u_n \ $ es la variable de entrada actual, y \ $ u_ {n-1} \ $ es la entrada anterior. La salida de este sistema obviamente será discreta, a diferencia de la respuesta real del circuito RC.

El código que he escrito para el arduino (modelo Uno) está aquí:

#include "Wire.h"
#define PCF8591 (0x90 >> 1)

void AnalogOut(uint8_t value)
{
 sei(); //enable interrupts
 Wire.beginTransmission(PCF8591); // turn on the PCF8591
 Wire.write(0x40); // control byte
 Wire.write(value); 
 Wire.endTransmission(); 
}


int preload;
void setup()
{
 pinMode(A0, INPUT);
 Wire.begin();
 // timer2 initialization
 noInterrupts();
 TCCR2A = 0;
 TCCR2B = 0;
 preload = 6;
 TCNT2 = preload;                                   //setting the preload
 TCCR2B = (0<<CS22) | (1<<CS20)| (1<<CS21);        //and the prescaler for a   0.5ms sampling time
 TIMSK2 |= (1 << TOIE2);
 interrupts();
}



 uint8_t x_n_1=0;   //x[n-1]=0; initial  value of the output

 uint8_t u_n;       //u[n]  current value of the input
 uint8_t x_n;       //x[n] current value for the ouptut
 uint8_t u_n_1;     //u[n-1] previous value for the input
 //interrupt routine
 ISR(TIMER2_OVF_vect)
  {
   TCNT2 = preload;  
   u_n=analogRead(A0);                   //reading the current input value, a/d conversion takes up about 0.15ms
   x_n=uint8_t(0.07246*u_n+0.07246*u_n_1+0.855*x_n_1);   //x[n]=1/13.8(u[n]+u[n-1]+11.8x[n], also converting it to a 8bite integer
   AnalogOut(x_n);                       //writing to the PC8591 d/a converter            
   x_n_1=x_n  ;                         //x[n-1] for the next sampling interval becomes the current x[n]
   u_n_1=u_n;                                  //u[n-1] for the next sampling interval becomes the current u[n]


   }
  void loop()
  {
    TCNT2=preload;
    u_n_1=analogRead(A0);  //getting an initial value for u[n-1]
    while(1);

   }

Tenga en cuenta que he usado una rutina de interrupción para generar un retraso de 0.5 ms. Además, la parte del código en la función loop () se explica mejor a continuación:

 TCNT2=preload; //the timer starts ticking at its inital value
 u_n_1=analogRead(A0); //we are doing this a/d conversion before even one interrupt has happened.

Aquí está el circuito que armé en proteus:

Enproteus,estoyenviando\$y=3+\sin(314t)voltios\$alaentradaanalógicaA0.Además,establezcoelvoltajedereferenciaparaelcovnertera/da5V(ennuestrolaboratoriorealpodemosconfigurarVrefusandounpotenciómetro).Estoesloqueobtuveenelosciloscopioenproteusalejecutarlasimulación: No estoy seguro de si esto funcionaría en el dispositivo real ya que lo haremos en nuestro laboratorio en un par de días como parte de un curso de sistemas de control digital. ¿Es la señal de mierda solo el resultado de una simulación deficiente o hay otro error? He intentado generar una onda de diente de sierra (discreta) usando este mismo circuito (pero sin ninguna entrada o función de transferencia, solo salida de señal simple) y funcionó bien.

    
pregunta Emir Šemšić

1 respuesta

2

Creo que es un problema de escalar los coeficientes de su ecuación de filtro de primer orden. Por lo tanto, puede manipular los valores con la precisión adecuada, en lugar de simplemente convertir a uint8_t el resultado de las sumas / multiplicaciones, como está haciendo. Como al final restringirá la salida del filtro a 8 bits (DAC) y también para evitar el uso de variables de 32 bits, sugiero que defina un factor de escala de 256. De esta manera, será necesario utilizar solo variables de 16 bits (uint16_t) para las operaciones MAC. La función analogRead () devuelve un número de 0 a 1023 (ADC de 10 bits). Por razones similares a las ya discutidas, limitaré el resultado. de la conversión digital a 8 bits (división por 4, o desplazamiento a la derecha por 2 bits). Entonces:

uint16_t u_n;
u_n = analogRead(A0) >> 2;

La ecuación original:

$$ x_n = 0.855072x_ {n-1} +0.072464 \ left (u_n + u_ {n-1} \ right) $$

Se convertirá a:

$$ 256x_n = 219x_ {n-1} +19 \ left (u_n + u_ {n-1} \ right) $$

e implementado en código, con redondeo, como:

x_n = (219*x_n_1 + 19*(u_n + u_n_1) + 128) >> 8;

En la gráfica resultante (\ $ T_s = 0.5 ms \ $) es posible notar que el sistema, cuando se excita aproximadamente a la frecuencia del polo (50 Hz), muestra una respuesta con una magnitud de 0.707 (-3 dB) De la entrada y del desfase de 45 grados. Vale la pena recordar que, por ejemplo, un voltaje de 4V es equivalente a un valor digital de 4/5 * 256 (aprox. 204).

ACTUALIZAR:

Esposiblequeenlagráficaanteriornoteelefectodeladiscretizaciónconprecisióninsuficiente,porejemplo,lapresenciadeunerrordecompensacióndeCC.Porlotanto,alcambiarelfactordeescalaa1024yusarvariablesde32bits,esposibleminimizarelerrordediscretización:

uint32_tu_n;u_n=analogRead(A0)>>2;

Laecuaciónescaladaserá:

$$1024x_n=876x_{n-1}+74\left(u_n+u_{n-1}\right)$$

eimplementadoencódigo,conredondeo,como:

x_n=(876*x_n_1+74*(u_n+u_n_1)+512)>>10;

Conlatrama:

    
respondido por el Dirceu Rodrigues Jr

Lea otras preguntas en las etiquetas