Vai al contenuto
PLC Forum


Alternativa a Variac per controllo potenza carico resistivo (autoclave)


mading

Messaggi consigliati

Guarda questo mio codice

 

// parte di "setup"
TCNT1 = 6491;
  TCCR1B |= (1<<CS12);
  TIMSK1 |= (1<<TOIE1);
  iccz = 0;
  interrupts();
 
}
/**************************************************************************
***                                                                     ***
***        Routine di servizio interrupt a tempo: 10 ms                 ***
***                                                                     ***
**************************************************************************/
ISR (TIMER1_OVF_vect)
 {
   TCNT1 = 64911; //ricarica timer

  It_cntd--;
  if (It_cntd == 0)
    {
         //azzera comandodi on SSR
         
    } 
  It_pid--;
   {
      It_pid = 200 //ricarico per avere tempo = 2"
      pid();
    }

 

ho il timer che è prescalato a 10ms per aumentare la risoluzione sul comando del relè.

Il PID carica in It_cntd il valore per avere lo SSR on, al termine si azzera il comando e SSR si spegne

Ogni 2" chiamo il PID che carica un nuovo valore in IT_cntd in base ai calcoli.

Modificato: da Livio Orsini
Link al commento
Condividi su altri siti


Grazie Livio,

mi rendo conto che la mia esperienza di Arduino è limitata, e non prevede lo strumento degli interrupts.

Anche i timer sono un'esperienza nuova, quindi per me è necessario analizzare passo passo.

 

Per prima cosa: l'algoritmo base.

 

Penso che sia schematizzabile con:

 

-leggo la T al tempo t0

-calcolo la risposta con la libreria PID: tempo di apertura del relay 0<tPID<tMAX, con tMAX 5 secondi

-faccio partire il contatore (a scalare) di durata TMAX

-se previsto dal calcolo PID: relay ON e parte il contatore di durata tPID

-esaurisco tPID

-esaurisco tMAX

-leggo la T al tempo t0+tMAX

-ecc..

 

Nel mio sketch ho fatto copia-incolla di varie cose trovate in rete.

Ma di base non ho capito il senso e la sintassi della funzione interrupt e timer1 che ho implementato, e di conseguenza anche il tuo esempio.

Materia per qualche sera di dopolavoro.

 

a presto

 

 

Link al commento
Condividi su altri siti

Buonasera e buon primo maggio!
Allora, son partito dall'esempio di Michele Ardito.

Ho avuto parecchi problemi prima di capire che l'interfaccia MAX6675 richiede un delay minimo per poter effettuare due letture successive, come discusso qui: https://forum.arduino.cc/index.php?topic=333233.0

Me ne ero accorto andando a modificare il delay del mio primo test del MAX6675 e del display.

 

Ho optato per misurare la temperatura del sistema ogni 5000 ms.

 

#include <PID_v1.h>
#define RelayPin 2 //nel mio caso uso il pin 2
#include <LiquidCrystal_I2C.h> // Libreria LCD I2C
LiquidCrystal_I2C lcd(0x3F,16,2);  // set the LCD address to 0x3F for a 16 chars and 2 line display

#include "max6675.h"
int ktcSO = 8;
int ktcCS = 9;
int ktcCLK = 10;
MAX6675 ktc(ktcCLK, ktcCS, ktcSO);

//Define Variables we'll be connecting to
double Setpoint, Input, Output; 
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, 1000, 0, 0, DIRECT); //P a 100 come prova

int WindowSize = 5000;
unsigned long windowStartTime;

void setup()
{
  pinMode(RelayPin, OUTPUT);
  windowStartTime = millis();
  //initialize the variables we're linked to
  Setpoint =40;  
  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  
  lcd.init();
  lcd.backlight();
  Serial.begin(9600);
  delay(500);
  digitalWrite(RelayPin, LOW);
  Input = ktc.readCelsius();
}

void loop()
{
  myPID.Compute();
  
  lcd.setCursor(0,0);
  lcd.print(Setpoint);
  lcd.print(" ");
  lcd.print(Input);
  lcd.setCursor(0,1);
  lcd.print(millis());
  lcd.print("  ");
  lcd.print(Output);
  /************************************************
     turn the output pin on/off based on pid output
   ************************************************/
  if (millis() - windowStartTime > WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
    Input = ktc.readCelsius();
    Serial.print(windowStartTime);
    Serial.print("  ");
    Serial.print(Input);
    Serial.print("  ");
    Serial.println(Output);
    digitalWrite(RelayPin, (Output < (millis() - windowStartTime)));
  }
}

 

Funziona, adesso si lavora per ottimizzare i parametri. Ho messo D e I a zero, per semplificare al massimo come test iniziale.

Come si vede da grafico, ho esagerato con P, però il sistema funziona e parzializza la potenza.

 

Un passo alla volta ci arriviamo!! 🙂

 

f

 

 

 

Test01.jpg

Link al commento
Condividi su altri siti

Se hai solo proporionale sei ancora con guadagno troppo elelvato, se hai inserito anche l'integrale hai un tempo d'integrazione troppo veloce.

Link al commento
Condividi su altri siti

Buongiorno, D e I sono a zero, come indicato nel post.

Penso che passerò ad un termistore, perchè ho l'impressione che la termocoppia sia troppo ballerina.

 

Altro veloce test, con temperatura più alta (65°C) e P= 500, D e I sempre nulli.

 

 

immagine.thumb.png.122b035eb1f88957079f2791723077f0.png

 

immagine.thumb.png.a04e637d6bb69b02e013e445c005414f.png

Link al commento
Condividi su altri siti

12 minuti fa, mading ha scritto:

Altro veloce test, con temperatura più alta (65°C) e P= 500, D e I sempre nulli.

 

Questo sembra buono.

E strano che raggiunga e mantenga il valore di consegna con il solo proporzionale. Sembra, anche dal diagramma della potenza, che lavori in on-off.

 

16 minuti fa, mading ha scritto:

Penso che passerò ad un termistore,

 

Su quella gamma di temperatura il sensore ideale è PT100 o PT1000; tra l'altro c'è un ottimo modulobastato su chip maxim, che s'interfaccia direttamente con arduino con r fili: Clock, dato e CS.

 

Link al commento
Condividi su altri siti

Premessa: non ho ancora approfondito gli aspetti teorici.

Secondo quanto riportato sul metodo Ziegler-Nichols (wiki) avrei dovuto aumentare P fino ad arrivare ad un regime di oscillazioni autosostenute.

Ho invece fatto come i gamberi, diminuendo P.

Altre due prove di conduzione "a regime" per valutare il periodo di "ondeggiamento" della temperatura.

P=500, D,I=0.

Setpoint 65°C, media mobile periodo 8.

Periodo medio di oscillazione 160 s

immagine.png.68d7c1978d77967f28c4673f89d82ce3.png

 P=250, D,I=0.

Setpoint 65°C, media mobile periodo 8.

Periodo medio di oscillazione 193 s.

immagine.thumb.png.02bb3c9d5671ea4472e8b673cb7d0199.png

 

 

immagine.png

immagine.png

Link al commento
Condividi su altri siti

41 minuti fa, mading ha scritto:

Funzionava come ON/OFF ogni 5s senza parzializzare

 

Perbacco mi pareva proprio!

4 ore fa, Livio Orsini ha scritto:

Sembra, anche dal diagramma della potenza, che lavori in on-off.

 

 

1 ora fa, mading ha scritto:

avrei dovuto aumentare P fino ad arrivare ad un regime di oscillazioni autosostenute.

 

Queste son cose che scrivono i teorici che non son mai stati a mettere in marcia impianti.:smile:

Se tu avessi letto il mio tutorial sapressti che non si fa così.

Si prova la risposta al gradino e si osservano le sovra-sotto elengazioni. L'ottimizzazione si fa crescendo il guadagno sino al comparire delle prime oscillazioni smorzate ( sovra-sotto elengazioni), a questo punto si riduce il guadagno sino a rientrare nella zona di stabilità.

 

Il vero problema, se ho capito, è che tu stai usando un regolatore che non hai realizzato tu e che, probabilmente, non hai ancora ben digerito.

Link al commento
Condividi su altri siti

Eccoci.

Ovviamente la velocità di aumento della temperatura dipende dalla quantità di acqua nello scaldabiberon, che non è sempre la stessa (+-20% ad occhio).

Questo aumenta la variabilità dei risultati.

 

Ho modificato il codice mettendo un tempo di integrazione di 5000 ms nel PID, dato che lo richiamo ogni 5s e la lettura della termocoppia non è accessibile se non ogni 5s (il tempo minimo di aggiornamento è di 200ms).

 

//Esempio di PID con relais:https://playground.arduino.cc/Code/PIDLibraryRelayOutputExample/
//Qualche esempio su: http://www.micheleardito.info/ma/it/arduino-it/controllo-pid-arduino-on-off/
//MAX6675 non può essere richiamato ad ogni ciclo, ho provato a chiamarlo ad ogni inizio finestra di 5 secondi https://forum.arduino.cc/index.php?topic=333233.0 qui alcune dritte sulla frequenza massima di chiamata a MAX6675

#include <PID_v1.h>
#define RelayPin 2
#include "max6675.h"
int ktcSO = 8;
int ktcCS = 9;
int ktcCLK = 10;
MAX6675 ktc(ktcCLK, ktcCS, ktcSO);
double Setpoint, Input, Output; 
PID myPID(&Input, &Output, &Setpoint, 250, 0.4,0, DIRECT); 
int WindowSize = 5000;
unsigned long windowStartTime;

void setup()
{
  pinMode(RelayPin, OUTPUT);
  windowStartTime = millis();
  Setpoint =65;  
  myPID.SetOutputLimits(0, WindowSize);
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(5000);
  Serial.begin(9600);
  delay(500);
  digitalWrite(RelayPin, LOW); //spengo il relay
  Input = ktc.readCelsius(); //inizializzo la lettura della temperatura prima di iniziare il loop
}

void loop()
{
  if (millis() - windowStartTime > WindowSize)
  { windowStartTime += WindowSize;
    Input = ktc.readCelsius();
    myPID.Compute();
    Serial.print(windowStartTime/1000); //parto con la scrittura dei dati per grafici in calc
    Serial.print("  ");
    Serial.print(Input);
    Serial.print("  ");
    Serial.println(Output);}  //aggiorno la temperatura al tempo n*WindowSize
    digitalWrite(RelayPin, (Output < (millis() - windowStartTime)));           
}

P 500, I 0.2

immagine.png.3d5744ca2b7f49b95f047278a336d3ff.png

 

P500 I0.4

immagine.png.957a015491650ec95deee66c94a670a4.png

 

La logica della libreria PID che utilizzo è spiegata qui: http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/

 

Prossimo passo:

-test con Kd

-costruire l'HW per pilotare i 2kW

-implementare dei pulsanti per impostare setpoint e costanti Kp Ki Kd

 

Per Livio: grazie per i feedback! Hai dei suggerimenti per un'interfaccia seriale wireless? Ho provato senza successo a far verdere un HC-08 che era contenuto nel kit della macchinetta che ho preso per mio figlio.

Mi basta leggere la seriale senza cavo USB.

 

Link al commento
Condividi su altri siti

I diagrammi dicono che sei a "xsi" ottimale o quasi, specialmente con integrale 0.2.

Per i cintrolli di temperatura di questo tipo il derivativo non apporta molti benefici, in compenso è facile avere rogne. Funziona meglio il derivativonon sull'errore, ma sulla reazione perchè ti evita overshutting sulle variazioni di set point.

 

1 ora fa, mading ha scritto:

Mi basta leggere la seriale senza cavo USB.

 

Io ho ottenuto dei buoni risultati con dei modm LoRa, il tipo che ho usato lavora a 868 MHz, è il tipo E32 868T20D, Devi solo connettere, incrociandoli, Rx e TX. Si alimenta a 5V, ma i segnali sono a 3.3V ed è meglio uisare un traslatore bidirezionale.

Ovviamente bisogna lavorarare con una coppia di questi.

La velocità "in aria" non è elevata proprio per la codifica LoRa. In comenso con 10mW arrivi sino a 3km in aria libera. In casa ho fatto più di 30 m, passando pareti di forati e solette in calcestruzzo; non so se va oltre perchè è la lunghezza di casa mia.

 

Due parole sulla libreria che usi.

Il calcolo impiegato è canonico, direi scolastico, ma per regolatori semplici come questo è più che buono.

Per conto mio ha due difetti che con questa applicazione non sono evidenziati dai risultati, visto che il tempo di campionamento è lunghissimo, però se dovessi fare controlli più veloci emergerebbero.

Non è richiamata ad interruopt di tempo e la correzione può essere affetta da jitter anche perchè la sua uscita dipende dal tempo di calcolo.

Ma visto che l'autore l'ha definita "Beginner’s PID" è più che valida.

 

Modificato: da Livio Orsini
Link al commento
Condividi su altri siti

Son contento di averci racappezzato qualcosa!

Riguardo i moduli LORA: interessante, soprattutto per la gittata. Ma immagino che lavorare in coppia significa due arduino (uno che riceve attaccato al PC).

 

Come alternativa ho preso questo tempo fa, un modulo ESP32 con cui ho messo su una turntable come descritto qui: www.photopizza.org. Che funziona egregiamente.

Me ne avanza uno.

Credo si possa programmare con la IDE di arduino senza grossi problemi.

L'uso che voglio farci è come monitor seriale wireless per salvare i dati delle cotture e monitorare a distanza. Se qualche santo ha già affrontato lo scoglio e vuole condividere, molto volentieri.

Link al commento
Condividi su altri siti

Se la portata ti basta, dato che tipicamente i collegamenti bluetootth sono di pochi metri, è un prodotto egregio.

Quanto tempo hai impiegato per riceverlo? Io ho fatto una sola esperienza con Aliexpress e oltre 2 mesi di attesa mi hanno disincentivato da ulteriori acquisti.

 

E anche interessante quella tavola rotante, che motorizzazioni hai usato?

Link al commento
Condividi su altri siti

Parecchio tempo.

bisogna aver pazienza, ho sempre ricevuto tutto.

Classico Nema17. La cosa più delicata è che lavora a frizione. Sono finito ad usare un gommino antivibrante bucato e infilato sull'albero. Fa il suo lavoro.

 

Devo farmi stampare 3D da un amico un gommino da accoppiare con una puleggia sincrona. dovrebbe andare meglio.

In merito ad "ESPRUINO", ecco che si aggiungono altre complicazioni per la creazione e compilazione dello sketch!

Potenzialmente è molto interessante, soprattutto per fare data logging con PC o android.

Link al commento
Condividi su altri siti

Oggi sono riuscito ad installare lo sketch sulla scheda ESPDuino. Il che ha richiesto di reinstallare Arduino e le varie librerie. Un'occasione per fare pulizia.

Istruzioni per usare ESP32 con Arduino: https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/boards_manager.md

MAX6675 ha delle rognette, una libreria che funziona con ESP32: MAX6675_library.zip

 

Il prossimo passo sarà di trovare il modo di leggere la seriale via WiFi o Bluetooth.

 

Ho fatto un veloce test e la scheda si collega al mio router ed invia segnali via USB seriale.

Ora cerchiamo di farla lavorare come si deve.

immagine.png

Link al commento
Condividi su altri siti

  • 2 weeks later...

Ciao a tutti,

sto implementando una funzione di modifica del setpoint e dei K, che richiamo dentro il loop.

 

Uso un Joystick: premo il tasto ed attivo il settaggio (ogni tanto perde un colpo), poi con il joystick seleziono Setpoint e Kp (per il momento).

Mai programmato in C. Una nuova esperienza.

 

Nei prossimi giorni testo il relay SSR. Faccio una misura di caduta di potenziale a pieno carico, per stimare la potenza dissipata e dimensionare il dissipatore di calore.

 

void settaggio()
{
  value = analogRead(A0);
  if (value<10)
  {
  Setpoint=Setpoint+0.2;
  }
  if (value>900)
  {
    Setpoint=Setpoint-0.2;
  }
  
  value = analogRead(A1);
  if (value<10)
  {
  Kp=Kp+1;
  }
  if (value>800)
  {
    Kp=Kp-1;
  }
 
  lcd.setCursor(0,0);
  lcd.print("Setpoint ");
  lcd.print (Setpoint);
  lcd.setCursor(0,1);
  lcd.print("Kp ");
  lcd.print (Kp);
  delay(500);
}



void loop()
{
  if (millis() - windowStartTime > WindowSize)
  { windowStartTime += WindowSize;
    Input = ktc.readCelsius();  //aggiorno la temperatura al tempo n*WindowSize
    myPID.Compute();
    lcd.setCursor(0,0); //scrivo la situazione al tempo n*WindowSize
    lcd.print(Setpoint);
    lcd.print(" ");
    lcd.print(Input);
    lcd.setCursor(0,1);
    lcd.print(millis()/1000); 
    lcd.print("  ");
    lcd.print(Output); 
    Serial.print(windowStartTime/1000); //parto con la scrittura dei dati per grafici in calc
    Serial.print("  ");
    Serial.print(Input);
    Serial.print("  ");
    Serial.println(Output);
    }                                
    
    if (digitalRead(pulsante)==0)
    {
      var=1;
      delay(1000);
    }
  
    if (var==1)
    {
      lcd.clear();
      settaggio();
      }
    if (digitalRead(pulsante)==0)
        {
      lcd.clear();    
      var=0;
      delay(500);
    }
    
     digitalWrite(RelayPin, (Output < (millis() - windowStartTime)));           
}

 

Link al commento
Condividi su altri siti

E brutto usare i ritardi bloccanti (delay()).

Dovresti imparare ad usare i timer interni con interrupt.

Link al commento
Condividi su altri siti

Ho visto una bella guida sugli interrupts qui:

Caro Livio ci proverò! promesso.

Così posso provare ad implementare il tuo esempio.

 

 

 

Link al commento
Condividi su altri siti

Usare il timer ad interrupt è estremamente semplice, c'è tutto nel mio esempio.

Poi se vuoi andare a fondo sui vari interrupt che permette di usare il micro di arduino, il discorso diventa sicuramente più complesso.

Comunque in rete trovi tantissimi documenti scritti sull'argomento. Io non amo, anzi odio, le "video lezioni", ma questo è un mio modo di pensare.

Link al commento
Condividi su altri siti

Perdona Livio ma il mio punto di vista è diverso.

Per me è difficile anche capire il significato della sintassi della prima riga del codice da te proposto, e a meno che qualcuno non me le spieghi passo passo per me è arabo.

A questo punto preferisco guardare l'esempio generico sugli interrupt e metterlo in pratica.

 

 

 

Link al commento
Condividi su altri siti

4 ore fa, mading ha scritto:

Per me è difficile anche capire il significato della sintassi della prima riga del codice da te proposto, e a meno che qualcuno non me le spieghi passo passo per me è arabo.

 

Scusa ma come hai fatto a sscrivere il programma che sta girando? hai copiato pari pari?

Link al commento
Condividi su altri siti

No,

l'ho sviluppato un passo alla volta.

Con l'aiuto dell"ABC di Arduino", un libro del 2015. Con gli esempi della libreria PID citati. Con gli Sketch di esempio nella IDE delle librerie i2C del display, della MAX6675 e con qualche esempio per il joystick.

Prendiamo il tuo suggerimento e proviamo a decifrare il codice

TCNT1 = 6491;
  TCCR1B |= (1<<CS12);
  TIMSK1 |= (1<<TOIE1);
  iccz = 0;
  interrupts();

-Alla prima riga assegna 6491 a TCNT1: cos'è TCNT1? cercando in rete leggo che è il timer1, assegnato sulla board Uno al PIN 3.

http://www.agt70.com/paciughi-elettronici/corsoarduino-lezione5.pdf

Per il significato di TCCRB1 e TIMSK1 ho trovato al volo questo:  https://www.robotshop.com/community/forum/t/arduino-101-timers-and-interrupts/13072

ecc... ecc...

 

Nella mia specifica applicazione (attivare una funzione di cambio setpoint e Kp Ki) devo usare un interrupt da evento esterno (pulsante joystick sul PIN 3.

Dovrei riuscire ad implementarlo, e ci penserò in un secondo momento a impostare gli interrupt anche sul core dello sketch (misura temperatura, calcolo della frazione di tempo per cui il relay resta acceso, accensione e spegimento realay).

 

Buon fine settimana e grazie per gli stimoli! Mi ci vorrà un bel po' per digerirli, ma sto scoprendo un sacco di cose interessanti.

 

 

 

Link al commento
Condividi su altri siti

1 ora fa, mading ha scritto:

devo usare un interrupt da evento esterno (pulsante joystick sul PIN 3.

Volendo puoi farlo, il pin 3 è collegato in effetti ad un interrupt, ma se devi leggere un pulsante azionato 'a mano' non ne hai bisogno, ti basta mantenere il loop veloce (senza delay()) e leggere lo stato ad ogni ciclo, previo ovviamente il debounce, hardware o software che sia.

 

 

Ciao, Ale.

Link al commento
Condividi su altri siti

10 ore fa, ilguargua ha scritto:

previo ovviamente il debounce, hardware o software che sia.

 

 

Se non è molto critico, basta leggere l'ingresso ad ogni ciclo di programma; se per 3 cicli consecutivi, ad esempio, ha lo stesso stato si considera il comando valido.

Leggere lo stato di un pulsante ad interrupt, se non è un evento catastrofico, è anche dannoso perchè si rischia di avere più interrpts consecutivi dovuti ai rimbalzi.

Link al commento
Condividi su altri siti

Crea un account o accedi per commentare

Devi essere un utente per poter lasciare un commento

Crea un account

Registrati per un nuovo account nella nostra comunità. è facile!

Registra un nuovo account

Accedi

Hai già un account? Accedi qui.

Accedi ora
×
×
  • Crea nuovo/a...