Controllore PID: Spike casuali durante il funzionamento

Ciao a tutti, ho un problema riguardo i controllori PID. Il progetto prevede l'implementazione di un controllore PID per la tensione e un controllore PI per la corrente. Sembra quasi che tutto funzioni ma quando il programma gira ho degli spike casuali sulla caratteristica di uscita che in questo caso è la tensione presente all'uscita di un ponte trifase SCR.
Di seguito vi allego il codice che sto utilizzando e il segnale di uscita

unsigned int Beta = 0;
int Beta1 = 0;
float Curr_ref = 0;
int Potenziometro = 0; //lettura potenziometro (riferimento)
int Feedback = 0; //tensione di feedback proveniente dal ponte
int Feedbackcurr = 0; //tensione di feedback proveniente dal loop di corrente
int lastFeedback = 0; //valore di feedback i-1, serve per il PID di tensione
float lasterror = 0; //valore di errore i-1, serve per il PID di tensione
float lasterror_c = 0; //valore di errore i-1, serve per il PID di tensione

int iia = 0; //variabili utilizzate per entrare nei "case" quando genero il periodo
int iib = 0;
int iic = 0;

int i = 0; //variabile utilizzata per migliore lettura degli ingressi analogici


float Kp_volt = 12;
float Ki_volt = 200;
float Kd_volt = 0.075;

float Kp_curr = 0.1;
float Ki_curr = 0;
float Kd_curr = 0;

float error_v = 0; //errori dati dalla differenza tra feedback e riferimento
float error_c = 0; //errori dati dalla differenza tra feedback di corrente e corrente di riferimento

float Total_error = 0; //errore totale dato dagli errori singoli del PID

float Prev_error_v = 0; //errore al controllo i-1

float Error_volt_P = 0; //inizializzo gli errori a 0 per il PID di tensione
float Error_volt_I = 0;
float Error_volt_D = 0;

float Error_curr_P = 0; //inizializzo gli errori a 0 per il PID di corrente
float Error_curr_I = 0;
float Error_curr_D = 0;

float Delta_T_volt = 0; //variabile di tempo dichiarata...Ogni quanto faccio agire il PID (2ms)
float Delta_T_curr = 0; //variabile di tempo dichiarata...Ogni quanto faccio agire il PID (1m)
//double actual_time = 0;
//double last_time = 0;

int SampleTimeVolt = 2000;
int SampleTimeCurr = 1000;

unsigned long PastTimeVolt = 0;
unsigned long PastTimeCurr = 0;

void setup() {
  // inizializzo le variabili
  Serial.begin(2000000);
  cli(); //stop interrupts

  DDRB = (1 << DDB7) | (1 << DDB6) | (1 << DDB5) | (1 << DDB4);
  DDRH = (1 << DDH6) | (1 << DDH5);

  PORTB = (0 << PB7) | (0 << PB6) | (0 << PB5) | (0 << PB4);
  PORTH = (0 << PH6) | (0 << PH5);

  pinMode(19, INPUT_PULLUP); //definisco i pin utili per gli interrupt esterni
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);

  TCCR1A = 0; //TIMER UTILIZZATO PER LA FASE S (PER SINCRONISMO)
  TCCR1B = (0 << CS12) | (1 << CS11) | (0 << CS10); //Prescaler nullo, fermo il contatore
  TIMSK1 = (1 << OCIE1C) | (1 << OCIE1B) | (1 << OCIE1A); //abilitazione interrupt
  TIFR1 = (0 << OCF1C) | (0 << OCF1B) | (0 << OCF1A); //flag azzerato raggiungimento valore scelto
  TCNT1 = 0; ///azzero contatore a 16 bit

  TCCR3A = 0; //TIMER UTILIZZATO PER LA FASE R (PER SINCRONISMO)
  TCCR3B = (0 << CS32) | (1 << CS31) | (0 << CS30); //prescaler nullo, fermo il contatore
  TIMSK3 = (1 << OCIE3C) | (1 << OCIE3B) | (1 << OCIE3A); //abilitazione interrupt
  TIFR3 = (0 << OCF3C) | (0 << OCF3B) | (0 << OCF3A); //flag azzerato raggiungimento valore scelto
  TCNT3 = 0; ///azzero contatore a 16 bit

  TCCR4A = 0; //TIMER UTILIZZATO PER LA FASE T (PER SINCRONISMO)
  TCCR4B = (0 << CS42) | (1 << CS41) | (0 << CS40); //Prescaler nullo, fermo il contatore
  TIMSK4 = (1 << OCIE4C) | (1 << OCIE4B) | (1 << OCIE4A); //abilitazione interrupt
  TIFR4 = (0 << OCF4C) | (0 << OCF4B) | (0 << OCF4A); //flag azzerato raggiungimento valore scelto
  TCNT4 = 0; ///azzero contatore a 10 bit

  attachInterrupt(digitalPinToInterrupt(19), sincronismo_RS, RISING); //richiamo l'interrupt quando la tensione AB, collegata sul pin digitale 0 diventa positiva, sincronismo_AB è il nome della funzione
  attachInterrupt(digitalPinToInterrupt(2), sincronismo_ST, RISING); //richiamo l'interrupt quando la tensione BC, collegata sul pin digitale 1 diventa positiva, sincronismo_BC è il nome della funzione
  attachInterrupt(digitalPinToInterrupt(3), sincronismo_TR, RISING); //richiamo l'interrupt quando la tensione CA, collegata sul pin digitale 2 diventa positiva, sincronismo_CA è il nome della funzione

  sei(); //Enable interrupt
}

void loop()
{
  Feedback = analogRead(A7);
  Potenziometro = analogRead(A5);
  Feedbackcurr = analogRead(A14);
  Curr_ref = MyPID(Potenziometro, Feedback, Kp_volt, Ki_volt, Kd_volt);
  Beta = MyPID2(Curr_ref, Feedbackcurr, Kp_curr, Ki_curr);

  // Serial.println(Total_error);
}



void sincronismo_RS()
{
  TCNT3 = 0; //azzeramento del contatore quando trovo il fronte di salita
  OCR3A = 18500 - Beta; //primo valore di uguaglianza per il primo impulso
}

void sincronismo_ST()
{
  TCNT1 = 0;
  OCR1A = 18500 - Beta; //primo valore di uguaglianza per il primo impulso
}

void sincronismo_TR()
{
  TCNT4 = 0;
  OCR4A = 18500 - Beta; //primo valore di uguaglianza per il primo impulso
}


float MyPID( int Potenziometro, int Feedback, float Kp_volt, float Ki_volt, float Kd_volt)
{
  unsigned long ActualTimeVolt = micros();

  int Delta_T_volt = ActualTimeVolt - PastTimeVolt;
  if (Delta_T_volt >= SampleTimeVolt)
  {
    error_v = Potenziometro - Feedback;
    Error_volt_P = error_v * Kp_volt;
    Error_volt_I = Error_volt_I + (error_v + lasterror) * 0.5 * Ki_volt * (Delta_T_volt);
    if (Error_volt_I >= 5)
    {
      Error_volt_I = 5;
    }
    else if (Error_volt_I <= -5)
    {
      Error_volt_I = -5;
    }
    else
    {
      Error_volt_I = Error_volt_I;
    }

    Error_volt_D = (error_v - lasterror) * Kd_volt / (Delta_T_volt);


    Total_error = Error_volt_P + Error_volt_I + Error_volt_D;
    lasterror = error_v;
    PastTimeVolt = ActualTimeVolt;
  }
  return Total_error;
}


int MyPID2(float Curr_ref,  int Feedbackcurr, float Kp_curr, float Ki_curr)
{
  unsigned long ActualTimeCurr = micros();

  int Delta_T_curr = ActualTimeCurr - PastTimeCurr;
  if (Delta_T_curr >= SampleTimeCurr)
  {
    error_c = Curr_ref - Feedbackcurr;
    Error_curr_P = error_c * Kp_curr;
    Error_curr_I = Error_curr_I + (error_c + lasterror_c) * 0.5 * Ki_curr * (Delta_T_curr);

    if (Error_curr_I >= 5)
    {
      Error_curr_I = 5;
    }
    else if (Error_curr_I <= -5)
    {
      Error_curr_I = -5;
    }
    else
    {
      Error_curr_I = Error_curr_I;
    }

    Error_curr_D = (error_c - lasterror_c) * Kd_curr / (Delta_T_curr);

    Beta1 += Error_curr_P + Error_curr_I + Error_curr_D;

    if (Beta1 >= 15166)
    {
      Beta1 = 15166; //15166 è il numero che porta l'angolo di conduzione alpha a 0°
    }
    else if (Beta1 <= 0)
    {
      Beta1 = 0;
    }
    else
    {
      Beta1 = Beta1;
    }
    lasterror_c = error_c;
    PastTimeCurr = ActualTimeCurr;
  }

  return Beta1;
}





ISR(TIMER3_COMPA_vect)
{
  iia++;
  switch (iia)
  {
    case 1:
      OCR3A += 1000;
      PORTH = (1 << PH6) | (1 << PH5);
      attachInterrupt(digitalPinToInterrupt(19), sincronismo_RS, FALLING);
      break;

    case 2:
      PORTH = (0 << PH6) | (0 << PH5);
      break;

    case 3:
      OCR3A += 1000;
      PORTB = (1 << PB7) | (1 << PB4);
      attachInterrupt(digitalPinToInterrupt(19), sincronismo_RS, RISING);
      break;

    case 4:
      PORTB = (0 << PB7) | (0 << PB4);
      iia = 0;
      break;
  }
}


ISR(TIMER1_COMPA_vect)
{
  iib++;
  switch (iib)
  {
    case 1:
      OCR1A += 1000;
      PORTB = (1 << PB7) | (1 << PB6);
      attachInterrupt(digitalPinToInterrupt(2), sincronismo_ST, FALLING);
      break;

    case 2:
      PORTB = (0 << PB7) | (0 << PB6);
      break;

    case 3:
      OCR1A += 1000;
      PORTB = (1 << PB5);
      PORTH = (1 << PH5);
      attachInterrupt(digitalPinToInterrupt(2), sincronismo_ST, RISING);
      break;

    case 4:
      PORTB = (0 << PB5);
      PORTH = (0 << PH5);
      iib = 0;
      break;
  }
}



ISR(TIMER4_COMPA_vect)
{
  iic++;
  switch (iic)
  {
    case 1:
      OCR4A += 1000;
      PORTB = (1 << PB5) | (1 << PB4);
      attachInterrupt(digitalPinToInterrupt(3), sincronismo_TR, FALLING);
      break;

    case 2:

      PORTB = (0 << PB5) | (0 << PB4);
      break;

    case 3:

      OCR4A += 1000;

      PORTB = (1 << PB6);
      PORTH = (1 << PH6);
      attachInterrupt(digitalPinToInterrupt(3), sincronismo_TR, RISING);
      break;

    case 4:

      PORTB = (0 << PB6);
      PORTH = (0 << PH6);
      iic = 0;
      break;
  }
}

SCRN0162

Per darvi più dettagli:

  • Beta è la variabile che muove degli impulsi utili ad azionare gli SCR del ponte e proviene
    dall'uscita del PI di corrente.

  • I tre TIMER vengono utilizzati per generare gli impulsi e quindi impostare "HIGH" o "LOW" dei pin digitali

  • Gli interrupt dei pin 19, 3, 2 generano interrupt esterni, in particolare ognuno di essi mi dice quando le tre sinusoidi passano lo zero.

Spero di essere stato abbastanza dettagliato. Se manca qualcosa fatemelo pure notare.

Grazie

Ti segnalo che, nella sezione in lingua Inglese, si può scrivere SOLO in Inglese ... quindi, per favore, la prossima volta presta più attenzione in quale sezione metti i tuoi post; questa volta esso è stato spostato, da un moderatore della sezione di lingua Inglese, nella sezione di lingua Italiana ... la prossima volta potrebbe venire direttamente eliminato. Grazie.

Guglielmo

Chiedo scusa, non mi ero accorto. La prossima volta farò più attenzione

1 Like

Ci sono variabili di tipo int che potrebbero benissimo essere di tipo byte, questa certamente non la soluzione ma aiuta a comprendere il codice a colui che lo vede per la prima volta e pensa ha usato int perché assumerà valori negativi.

Potresti pure dichiarare volatile le variabili interessate dagli interrupt anche se sembra proprio che li usi solo nelle ISR.

Non vedo nulla di strano nel codice che possa darmi un indicazione. Posso provare a disassemblare per vedere come il compilatore ha tradotto il sorgente in assembly. Ma per farlo mi serve almeno il .elf generato.

Ciao.

[SOLVED] Sono riuscito a risolvere il problema ma non sono sicuro di aver trovato la soluzione corretta. Il problema era dato dalla configurazione dei pin 19,2,3 che era settati come "INPUT_PULLUP". Li ho settati semplicemente come "INPUT". Forse il problema dipendeva anche dalla mia onda quadra in ingresso che andava già da 0 a 5V. Mi sono accorto però che con la configurazione "INPUT PULLUP" la mia onda aveva un offset di circa mezzo volt e forse questo creava dei problemi.

In ogni caso, il problema è risolto

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.