Passa al contenuto principale

Controllo e regolazione

Controllo e regolazione con il modulo matematico​

Data l'ampia libreria di funzioni del modulo matematico, è naturale pensare di implementare in questo linguaggio di scripting anche funzioni di controllo e di regolazione.

Ciò è effettivamente possibile, con alcune limitazioni e tenendo conto dei punti seguenti:

Elaborazione del segnale, comportamento temporale​

Il processo di controllo e regolazione richiede calcoli in tempo reale rigoroso (hard real-time), cioè un tempo di propagazione del segnale fisso e il più breve possibile dall'acquisizione della grandezza misurata all'emissione della variabile di comando verso il processo. Nei sistemi di controllo questo valore è tipicamente di circa 1 .. 10 ms. Questo vincolo rigoroso non può essere rispettato con l'architettura di smartCORE!

I dati di misura vengono acquisiti da determinati plug-in e scritti nei canali di smartCORE. In modo asincrono rispetto a ciò, a intervalli di tempo approssimativamente costanti e ampi, il set di formule del modulo matematico viene calcolato sui dati disponibili, purché per un dato istante tutti i canali necessari forniscano dati. I risultati vengono scritti nei canali smartCORE al ritmo di questo calcolo. In modo asincrono rispetto a ciò, i dati dei canali smartCORE vengono emessi verso il processo, ad esempio tramite CAN.

Vi sono quindi numerosi punti in cui il requisito di tempo reale viene violato dall'asincronia in smartCORE, dai canali come buffer o buffer circolari e dall'elaborazione batch del modulo matematico.

Se i processi da influenzare sono sufficientemente lenti1, è almeno possibile adattare il processo di calcolo e l'emissione in modo che il tempo di campionamento discreto discreteSampleTimeMs coincida con il tempo di esecuzione del processo batch evaluationTimeMs e con l'emissione verso il processo. I valori possibili si collocano nell'intervallo 50 .. 1000 ms. In tal caso l'asincronia e il buffering dei flussi di dati diventano tollerabili.

Regolatori​

Regolatore PID "pidCtrl"​

Under Construction Questa funzione è in fase di implementazione e non è ancora destinata all'uso produttivo.

La funzione pidCtrl() implementa un regolatore con comportamento proporzionale (P), integrale (I) e derivativo (D) configurabile.

I parametri di ingresso della funzione sono almeno la variabile di riferimento (valore di consegna, setpoint) sp e la variabile di processo da guidare (valore effettivo, current value) cur. L'uscita è la variabile di comando.

Opzionalmente è possibile selezionare la modalità di funzionamento tramite l'ingresso mode:

modeDescrizione
0, falseNormale funzionamento di regolazione (Enable)
1, trueMantenimento della variabile di comando (Hold)
2La variabile di comando segue l'ingresso di controllo manuale u_man (Bypass)

Opzionalmente, con il vettore di ingresso parVec i parametri di regolazione [K, Ti, Td] possono essere assegnati anche dinamicamente. Il vettore può contenere da 1 a 3 valori di parametro. Questi sovrascrivono le indicazioni nell'oggetto di configurazione.

important

Il calcolo avviene per principio con il tempo di campionamento discreto discreteSampleTimeMs. Affinché la variabile di comando possa essere trasmessa al processo in modo tempestivo, il tempo di esecuzione del processo batch evaluationTimeMs deve essere impostato sullo stesso valore.

u1 = pidCtrl(sp, cur, {...});
u2 = pidCtrl(sp, cur, mode, {...});
u3 = pidCtrl(sp, cur, mode, u_man, {...});
u4 = pidCtrl(sp, cur, mode, u_man, parVec, {...});
// Verpflichtende Konfiguration für alle Varianten
ux = pidCtrl(..., { K: <dbl>
             , Ti: off|<dbl>
                 , Td: off|<dbl>
                 , reverse: <bool>
                 , commonK: <bool>
                 , dWidth: <uint>
                  , lower: off|<dbl>
                  , upper: off|<dbl>
});

La descrizione delle proprietà avviene con esempi nel dominio del tempo, sebbene la funzione calcoli internamente in passi di campionamento discreti.

ProprietàValoreDescrizione
K<dbl>Guadagno del regolatore
Tioff|<dbl>Attivazione e tempo di integrazione per la componente I
I(e)=1Ti⋅∫e(t)dtI(e) = \frac{1}{T_i} \cdot \int e(t) dt
Tdoff|<dbl>Attivazione e tempo di derivazione per la componente D
D(e)=Td⋅ddte(t)D(e)=T_d \cdot \frac{d}{dt} e(t)
reverse<bool>Inversione del senso di azione
false: e=sp−cure=sp-cur, (def.)
true: e=cur−spe=cur - sp
commonK<bool>Interpretazione del guadagno:
false: u=K(e)+I(e)+D(e)u = K(e) + I(e) + D(e)
true: u=K⋅(1+I(e)+D(e))u=K\cdot(1+I(e)+D(e)), (def.)
dWidth<uint>Numero di intervalli di campionamento per la media pesata del valore differenziale (def.: 3)
loweroff|<dbl>Limite inferiore della variabile di comando,
la componente integrale viene adeguata per consentire una ripresa senza urti.
upperoff|<dbl>Limite superiore della variabile di comando,

Struttura del regolatore: La proprietà commonK commuta l'interpretazione del fattore di guadagno K. Nella tecnica di regolazione K agisce di norma in modo uguale su tutti e tre i percorsi P, I e D (commonK: true, def.!) e si fa carico anche della conversione delle dimensioni fisiche tra errore di regolazione (ad es. in hPa) e variabile di comando (ad es. in %). In questo modo K non influenza il comportamento dinamico del regolatore, determinato dai suoi poli e zeri, ma solo l'agilità e la stabilità dell'anello di regolazione chiuso. Molte regole di taratura per regolatori PID si basano su questa struttura.

Raramente si trova l'interpretazione per cui K influenza effettivamente solo il percorso proporzionale del regolatore (commonK: false). In tal caso il compito della conversione delle unità si sposta anche sui parametri temporali Ti e Td e la modifica di K cambia direttamente i poli e gli zeri del regolatore e quindi il suo comportamento dinamico. La taratura del regolatore diventa così notevolmente più difficile.

Sull'uso della componente D: Poiché la funzione del regolatore è legata alla cadenza discreta molto lenta del modulo matematico, anche la valutazione del gradiente dell'errore di regolazione nella componente D va utilizzata con cautela. A causa della filosofia "On-Change" nella messa a disposizione dei dati di misura in un canale smartCORE, possono crearsi lunghi intervalli di tempo in cui il segnale non cambia. Per poter comunque rilevare, ad esempio, una lenta deriva di una temperatura, i valori di più passi di campionamento vengono combinati con una funzione di ponderazione per ottenere il valore della derivata. L'ampiezza di questa finestra è indicata dalla proprietà dWidth. In questo modo, però, nel percorso D del regolatore si genera anche un tempo morto aggiuntivo (dWidth/2 * discreteSampleTimeMs), che può influenzare negativamente il processo di regolazione. dWidth va quindi scelto il più piccolo possibile ma abbastanza grande da essere sufficiente. Ciò dipende dal processo da regolare.

Riferimento temporale: Nel caso in cui la variabile di riferimento sp provenga da un attributo (shared) e venga aggiornata solo raramente, questo canale dovrebbe assolutamente essere svincolato dal riferimento temporale mediante la macro #timeless. Lo stesso può valere anche per la variabile di processo cur.

Macchina a stati (Finite State Machine)​

Implementazione di una Finite State Machine (FSM)​

Una descrizione generale si trova nell'articolo Finite-state machine - Wikipedia oppure anche qui (DE).

In questa implementazione la valutazione avviene con la cadenza del tempo di campionamento discreto globale discreteSampleTimeMs. In ogni ciclo una sola transizione può provocare un cambio di stato.

Questa macchina a stati finiti possiede un numero finito di stati (States), identificati da un identificatore univoco sx. Ogni stato è stabile fino a quando una transizione di stato (transition) ne provoca il cambio. Uno stato può quindi contenere informazioni sul passato, poiché il sistema lo ha raggiunto attraverso il proprio percorso precedente, cioè riflette in una certa misura le variazioni dell'ingresso dall'avvio del sistema fino all'istante attuale.

Una transizione di stato (transition) è un passaggio dallo stato attuale a un nuovo (altro) stato. Questo passaggio avviene quando sono presenti le condizioni logiche / gli "ingressi" indicati, che devono essere soddisfatti per consentire la transizione. In questa implementazione sono disponibili a tale scopo una condizione booleana, nonché una finestra temporale definibile, un contatore di ripetizioni e un valore di priorità per l'abilitazione.

La figura seguente mostra una macchina a stati fittizia con 5 stati da "A" a "D" e "end".

Bild Zustandmaschine

suggerimento

Per la progettazione si raccomanda di disegnare proprio un grafo di questo tipo per la macchina a stati da implementare, con gli stati e le transizioni necessari.

All'avvio dell'interprete di formule "init" viene assunto uno stato di base "A". Diverse transizioni di stato fanno passare l'automa a nuovi stati oppure, mediante priorità definite delle transizioni, mantengono uno stato in determinate condizioni. Uno stato da cui non partono transizioni viene definito stato finale.

Le transizioni "from-anywhere" possono provocare un cambio di stato indipendentemente dallo stato attivo (compreso uno stato finale).

La struttura mostrata verrebbe tradotta nel testo della formula secondo il seguente schema; gli argomenti per la condizione di commutazione o i parametri temporali, nonché i parametri JSON per le transizioni, vanno completati di conseguenza:

act = states( // from anywhere to ...
transit('A', ...) // reset/restart
             , transit('C', ...) // resume
            , 'A' // State A: (re)start and prepare the tasks
, transit('B', ...) // preparation completed
            , 'B' // State B: doing crazy stuff
, transit(..., {prio: 100})     // explicit locker-transition
, transit('D', ...) // condition 1 to move on
, transit('D', ...) // condition 2 to move on
            , 'C' // State C: await feedback and decide where to continue
, transit('A', ...)
, transit('B', ...)
             , transit('D', ...)
            , 'D' // State D: doing some nice stuff to complete
, transit('C', ...) // there's more to do
, transit('end', ...)
            , 'end' // final, stable state
// properties of state machine:
         , { init: 'A'
, outIdx: true });             
suggerimento

La notazione su più righe con rientro adeguato aiuta a mantenere una visione d'insieme di stati e transizioni. Lo stesso vale per l'uso intensivo dei commenti.

La variabile act contiene, a causa di outIdx: true, l'indice dello stato attuale (1, 2, 3, …) e può essere utilizzata nelle espressioni successive per controllare le azioni della macchina a stati. A tale scopo sono adatti, ad esempio, semplici confronti in someResult = (act == 3) ? ... : ...; oppure la funzione select(act, ...), ma anche il rilevamento dei fronti mediante . Diversi valori di parametro potrebbero inoltre essere letti direttamente con parVec[act-1] da un vettore parVec = [1.3, 5.6, -3.0, 0.0]; oppure tramite la funzione di ricerca da una tabella con selectRow() e getField(). Vedere a questo proposito anche la descrizione seguente per states(...).

Macchina a stati "states"2​

Con questa funzione states() è possibile modellare un comportamento costituito da stati, transizioni di stato e azioni.

s1, … sN: definiscono gli identificatori degli stati sx della macchina a stati come const <uint/int/str> (!) Il primo stato è lo stato iniziale, a meno che con la proprietà init: sx non ne venga stabilito un altro. L'uscita di states() è il valore di sx dello stato attivo, a meno che con la proprietà outIdx: true non venga forzata l'uscita dell'indice dello stato come [1,2,…N][1, 2, … N]. Tutti gli sx devono essere biunivoci, poiché vengono utilizzati nelle funzioni transit() per l'indicazione dello stato successivo.

trXY: definiscono oggetti transit() che vengono valutati per lo stato menzionato in precedenza. La prima funzione transit() per cui le condizioni configurate sono soddisfatte provoca la transizione di stato.

Le transizioni di stato definite prima del primo stato hanno un ruolo particolare. Esse vengono valutate in qualsiasi stato, prima che vengano valutate le transizioni definite sullo stato stesso. In questo modo assumono il ruolo di transizioni "from any", ad esempio per intercettare condizioni di errore, realizzare timeout o ripristini forzati.

I parametri della funzione states() descrivono quindi l'automa, in cui un parametro const <uint/int/str> definisce un nuovo stato oppure, mediante transit(), vengono definite transizioni di stato a partire da quello stato.

act = states(tr00, tr01, …
, s1, tr10, tr11, …
, s2, tr20, tr21, …);
// Optionale Konfiguration
actX = states(..., { init: <sx>
, outIdx: <bool>
, ageVar: <str>
, dbgVar: <str>
});
ProprietàValoreDescrizione
init<var>Stato iniziale dell'automa, se non è per definizione il primo
outIdx<bool>true: emissione dell'indice dello stato attivo <uint> [1,2,…N][1, 2, … N]
false: emissione dell'identificatore definito per lo stato <uint/int/str>
ageVar<str>L'"età" dello stato attivo in secondi può essere pubblicata nella variabile qui indicata.
dbgVar<str>Il numero di nodo della transizione che di volta in volta scatta viene scritto nella variabile qui indicata. "0" se non è in corso alcuna transizione.
storage<str>(in preparazione)

Un' azione è l'"uscita" della FSM, che avviene in una determinata situazione. Esistono quattro tipi di azioni:

  • Azione di ingresso: L'azione viene eseguita/emessa all'ingresso in uno stato (indipendentemente dalla transizione di stato attraverso cui lo stato è stato raggiunto, se ve ne sono più di una).

  • Azione di uscita: L'azione viene generata all'uscita da uno stato (indipendentemente dalla transizione di stato attraverso cui lo stato viene lasciato).

  • Azione di input: L'azione viene generata in funzione dello stato attuale e dell'ingresso. A uno stato possono quindi essere associate più azioni, eseguite a seconda della transizione di stato attraverso cui viene raggiunto/lasciato.

  • Azione di transizione: L'azione viene eseguita durante una transizione di stato

Se act = state(…) è una variabile che rappresenta lo stato attivo, le azioni dipendenti dallo stato possono essere realizzate come segue:

  • Azione di ingresso: posedge(act == K)

  • Azione di uscita: negedge(act == K)

  • Azione di input: (act == K)

Con actVar: “TR“ definibile nella transizione è infine possibile realizzare anche

  • azioni di transizione: TR

Transizioni "transit"2​

La funzione transit() genera gli oggetti di transizione che vengono passati come parametri alla macchina a stati states(). Il risultato di questa funzione può essere elaborato esclusivamente nella funzione states().

  • sx: è lo stato successivo, se la condizione della transizione è soddisfatta. Se sx manca, la transizione riporta allo stato di partenza attivo senza però riavviarlo. sx può anche provenire da un calcolo o da una variabile. Se sx non designa uno stato noto, viene utilizzato il valore sostitutivo sFail.

  • cond: Condizione per la transizione di stato, def.: true. Se una condizione deve agire solo dopo una durata minima di risposta (ritardo all'attivazione / antirimbalzo), si raccomanda di prefiltrare la condizione con delay(), trigger() o threshold().

  • tBegin, tEnd: Intervallo di tempo riferito al tempo di attività dello stato in cui la condizione viene valutata. Def.: [tBegin,tEnd[=[0,∞[[t_{Begin}, t_{End}[ = [0, \infty[

states(..., transit({...}) // Transition in den Ausgangszustand
, transit(sx, {...})    // Transition nach sx
, transit(sx, cond)     // ...mit dynamischer Bedingung
, transit(sx, cond, tBegin)        // ...gültig ab Zeitpunkt
, transit(sx, cond, tBegin, tEnd)  // ...gültig im Zeitintervall
, ...);
// Optionale Konfiguration für alle Varianten, sofern nicht
// explizit gefordert
transit(..., { tBegin: <dbl>
, tEnd: <dbl>
, sFail: <var>
, nMax: <int>
, nVar: <str>
, actVar: <str>
, edge: <int>
, prio: <int>
})
ProprietàValoreDescrizione
tBegin<dbl>Istante in secondi, riferito al tempo di attività dello stato, a partire dal quale la condizione viene valutata, def.: 0.0
tEnd<dbl>Istante in secondi, riferito al tempo di attività dello stato, fino al quale la condizione viene valutata (esclusivo), def.: ∞\infty
sFail<var>Identificatore sostitutivo / stato di destinazione, se sx non designa uno stato valido
nMax<int>Numero massimo di attivazioni consecutive (contatore di cicli). Il contatore viene azzerato non appena lo stato di destinazione di questa transizione viene rientrato tramite una transizione esterna o non correlata. Con cicli annidati correttamente (prima definizione = ciclo interno) i contatori non si influenzano a vicenda. La destinazione del salto deve essere un'etichetta di stato statica: un'espressione dinamica viene trattata come errore.
nVar<str>Il contatore di cicli può essere pubblicato nella variabile indicata e viene aggiornato quando la transizione scatta.
actVar<str>Nel passo in cui la transizione scatta, sulla variabile indicata viene emesso true, altrimenti false.
edge<int>determina se la transizione viene attivata in modo statico (0), con posedge (1) o negedge (-1) della condizione.
prio<int>Priorità opzionale, nel caso in cui più transizioni scattino contemporaneamente. In linea di principio vince, nell'ordine di definizione, la prima con il livello di prio più alto.
Cicli intrecciati

Il comportamento di reset di nMax è definito solo per cicli correttamente annidati, cioè quando un ciclo interno ritorna sempre allo stato di destinazione prima di quello esterno. Se le destinazioni di salto di due transizioni nMax si sovrappongono (ad es. C→A e D→B in una catena A→B→C→D), il comportamento di reset del contatore non è prevedibile.

Footnotes​

  1. La costante di tempo più rapida del processo (T0=12πf0T_0 = \frac{1}{2\pi f_0}) dovrebbe essere da 10 a 15 volte il tempo di campionamento del sistema di controllo/regolazione. ↩

  2. Disponibile a partire dalla versione di catalogo 13. ↩ ↩2