Commande et régulation
Commande et régulation avec le module mathématique
Compte tenu de la riche bibliothèque de fonctions du module mathématique, il est naturel d'implémenter aussi des fonctions de commande et de régulation dans ce langage de script.
Cela est effectivement possible, avec certaines restrictions et en tenant compte des points suivants :
Traitement du signal, comportement temporel
Le processus de commande et de régulation exige des calculs en temps réel strict, c'est-à-dire un temps de propagation du signal fixe et le plus court possible, de l'acquisition de la grandeur mesurée jusqu'à la sortie de la grandeur de réglage vers le processus. Dans les automates, il se situe typiquement autour de 1 .. 10 ms. Cette contrainte stricte ne peut pas être respectée avec l'architecture du smartCORE !
Les données de mesure sont acquises par certains plug-ins et écrites dans les canaux du smartCORE. De manière asynchrone à cela, à des intervalles de temps approximativement constants et grands, le jeu de formules du module mathématique est calculé sur les données disponibles, pour autant que tous les canaux requis fournissent aussi des données pour un instant donné. Les résultats sont écrits dans des canaux smartCORE au rythme de ce calcul. De manière asynchrone à cela, des données des canaux smartCORE sont sorties vers le processus, par exemple via CAN.
Il y a donc suffisamment d'endroits où l'exigence de temps réel est violée par l'asynchronisme dans le smartCORE, par les canaux servant de tampons ou de mémoires circulaires et par le traitement par lots du module mathématique.
Si les processus à influencer sont suffisamment lents1, on peut au moins adapter le processus de calcul et la sortie de sorte que le temps d'échantillonnage discret discreteSampleTimeMs coïncide avec le temps d'exécution du processus par lots evaluationTimeMs et la sortie vers le processus. Les valeurs possibles se situent ici dans la plage 50 .. 1000 ms. L'asynchronisme et la mise en tampon des flux de données deviennent alors tolérables.
Régulateurs
Régulateur PID « pidCtrl »
Cette fonction est en cours de réalisation et n'est pas encore destinée à un usage productif.
La fonction pidCtrl() implémente un régulateur à comportement proportionnel (P), intégrateur (I) et dérivateur (D) configurable.
Les paramètres d'entrée de la fonction sont au minimum la grandeur de consigne (valeur de consigne, setpoint) sp et la grandeur de processus à asservir (valeur réelle, current value) cur. La sortie est la grandeur de réglage.
Le mode de fonctionnement peut en option être choisi via l'entrée mode :
| mode | Description |
|---|---|
| 0, false | Fonctionnement normal en régulation (Enable) |
| 1, true | Maintien de la grandeur de réglage (Hold) |
| 2 | La grandeur de réglage suit l'entrée de commande manuelle u_man (Bypass) |
En option, les paramètres de régulation [K, Ti, Td] peuvent aussi être attribués dynamiquement avec le vecteur d'entrée parVec. Le vecteur peut contenir 1 .. 3 valeurs de paramètres. Celles-ci écrasent les indications de l'objet de configuration.
Le calcul s'effectue toujours avec le temps d'échantillonnage discret discreteSampleTimeMs. Pour que la grandeur de commande puisse être transmise rapidement au processus, le temps d'exécution du processus par lots evaluationTimeMs doit être réglé à la même valeur.
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 description des propriétés s'appuie sur des exemples du domaine temporel, bien que la fonction calcule en interne par pas d'échantillonnage discrets.
| Propriété | Valeur | Description |
|---|---|---|
| K | <dbl> | Gain du régulateur |
| Ti | off|<dbl> | Activation et temps d'intégration pour l'action I |
| Td | off|<dbl> | Activation et temps de dérivation pour l'action D |
| reverse | <bool> | Inversion du sens d'action false : , (déf.) true : |
| commonK | <bool> | Interprétation du gain : false : true : , (déf.) |
| dWidth | <uint> | Nombre d'intervalles d'échantillonnage pour la moyenne pondérée de la valeur différentielle (déf. : 3) |
| lower | off|<dbl> | Limite inférieure de la grandeur de réglage, la part intégrale est poursuivie afin de permettre une reprise sans à-coup. |
| upper | off|<dbl> | Limite supérieure de la grandeur de réglage, |
Structure du régulateur : la propriété commonK commute l'interprétation du facteur de gain K. En automatique, K agit habituellement de la même manière sur les trois voies P, I et D (commonK : true, déf. !) et assure aussi la conversion des dimensions physiques entre l'écart de régulation (par ex. en hPa) et la grandeur de réglage (par ex. en %). Ainsi, K n'influence pas le comportement dynamique du régulateur, qui est déterminé par ses pôles et ses zéros, mais seulement l'agilité et la stabilité de la boucle de régulation fermée. De nombreuses règles de réglage pour régulateurs PID reposent sur cette structure.
On rencontre rarement l'interprétation selon laquelle K n'influence réellement que la voie proportionnelle du régulateur (commonK : false). La tâche de conversion d'unités se reporte alors aussi sur les paramètres de temps Ti et Td, et la modification de K modifie directement les pôles et les zéros du régulateur, donc son comportement dynamique. Le réglage du régulateur en devient considérablement plus difficile.
À propos de l'utilisation de l'action D : comme la fonction de régulation est liée à la cadence discrète très lente du module mathématique, l'évaluation du gradient de l'écart de régulation dans l'action D ne doit elle aussi être employée qu'avec prudence. En raison de la philosophie « On-Change » de la mise à disposition des données de mesure dans un canal smartCORE, il peut y avoir de longues périodes pendant lesquelles le signal ne change pas. Pour pouvoir néanmoins détecter par exemple une lente dérive de température, les valeurs de plusieurs pas d'échantillonnage sont combinées avec une fonction de pondération pour donner la valeur de la dérivée. La largeur de cette fenêtre est indiquée par la propriété dWidth. Cela crée toutefois aussi, dans la voie D du régulateur, un temps mort supplémentaire (dWidth/2 * discreteSampleTimeMs), qui peut influencer négativement le processus de régulation. dWidth doit donc être choisi aussi petit que possible mais aussi grand que nécessaire. Cela dépend du processus à réguler.
Référence temporelle : dans le cas où la grandeur de consigne sp provient d'un attribut (shared) et n'est que rarement mise à jour, ce canal doit impérativement être dégagé de la référence temporelle au moyen de la macro #timeless. Il en va peut-être de même pour la grandeur de processus cur.
Automate d'états (Finite State Machine)
Implémentation d'une Finite State Machine (FSM)
Une description générale se trouve dans l'article Finite-state machine - Wikipedia ou aussi ici (DE).
Dans cette implémentation, l'évaluation est cadencée avec le temps d'échantillonnage discret global discreteSampleTimeMs. À chaque cycle, exactement une transition peut provoquer un changement d'état.
Cet automate d'états fini possède un nombre fini d'états (States), identifiés par un identifiant univoque sx. Chaque état est stable jusqu'à ce qu'une transition d'état déclenche un changement. Un état peut ainsi contenir une information sur le passé, puisque le système l'a atteint par son parcours antérieur, c'est-à-dire qu'il reflète dans une certaine mesure les changements de l'entrée depuis le démarrage du système jusqu'à l'instant actuel.
Une transition d'état (Transition) est un passage de l'état actuel à un nouvel état (différent). Cette transition se produit lorsque les conditions logiques / « entrées » indiquées sont présentes ; elles doivent être satisfaites pour permettre la transition. Dans cette implémentation, une condition booléenne ainsi qu'une fenêtre de temps définissable, un compteur de répétitions et une valeur de priorité sont disponibles à cet effet pour la validation.
L'image suivante montre une machine d'états fictive avec 5 états « A » à « D » et « end ».
Pour la conception, il est recommandé de dessiner précisément un tel graphe pour la machine d'états à implémenter, avec les états et les transitions nécessaires.
Au démarrage de l'interpréteur de formules « init », un état de base « A » est adopté. Diverses transitions d'état font passer l'automate dans de nouveaux états ou, par des priorités définies des transitions, maintiennent un état dans certaines conditions. Un état dont aucune transition ne part est appelé état final.
Les transitions « from-anywhere » peuvent déclencher un changement d'état indépendamment de l'état actif (y compris un état final).
La structure représentée serait traduite en texte de formule selon le schéma suivant ; les arguments pour la condition de commutation ou les paramètres de temps, ainsi que les paramètres JSON des transitions, sont à compléter en conséquence :
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 });
La notation sur plusieurs lignes avec une indentation appropriée aide à garder une vue d'ensemble des états et des transitions. De même, l'utilisation intensive de commentaires.
En raison de outIdx: true, la variable act contient l'index de l'état actuel (1, 2, 3, …) et peut être utilisée dans les expressions suivantes pour piloter les actions de la machine d'états. Se prêtent à cela par exemple de simples comparaisons dans someResult = (act == 3) ? ... : ...; ou la fonction select(act, ...), mais aussi la détection de fronts au moyen de . Diverses valeurs de paramètres pourraient aussi être lues directement avec parVec[act-1] dans un vecteur parVec = [1.3, 5.6, -3.0, 0.0]; ou au moyen de la fonction de recherche dans un tableau avec selectRow() et getField(). Voir à ce sujet également la description suivante de states(...).
Automate d'états « states »2
Cette fonction states() permet de modéliser un comportement composé d'états, de transitions d'état et d'actions.
s1, … sN : définissent les identifiants des états sx de la machine d'états comme const <uint/int/str> (!). Le premier état est l'état de départ, sauf si un autre est déterminé via la propriété init : sx. La sortie de states() est la valeur respective de sx de l'état actif, sauf si la sortie de l'index d'état sous la forme est imposée avec la propriété outIdx : true. Tous les sx doivent être univoques, car ils sont utilisés dans les fonctions transit() pour indiquer l'état suivant.
trXY : définissent des objets transit() qui sont évalués pour l'état cité précédemment. La première fonction transit() dont les conditions configurées sont remplies déclenche la transition d'état.
Les transitions définies avant le premier état jouent un rôle particulier. Elles sont évaluées dans n'importe quel état, avant que les transitions définies sur l'état lui-même ne soient évaluées. Elles assument ainsi le rôle de transitions « from any », par exemple pour intercepter des conditions d'erreur, réaliser un timeout ou des remises à zéro forcées.
Les paramètres de la fonction states() décrivent donc l'automate, dans lequel un paramètre const <uint/int/str> soit définit un nouvel état, soit, au moyen de transit(), définit des transitions d'état à partir de cet état.
act = states(tr00, tr01, …
, s1, tr10, tr11, …
, s2, tr20, tr21, …);
// Optionale Konfiguration
actX = states(..., { init: <sx>
, outIdx: <bool>
, ageVar: <str>
, dbgVar: <str>
});
| Propriété | Valeur | Description |
|---|---|---|
| init | <var> | État de départ de l'automate, s'il n'est pas par définition le premier |
| outIdx | <bool> | true : sortie de l'index de l'état actif <uint> false : sortie de l'identifiant défini pour l'état <uint/int/str> |
| ageVar | <str> | L'« âge » de l'état actif en secondes peut être publié dans la variable désignée ici. |
| dbgVar | <str> | Le numéro de nœud de la transition déclenchante est écrit dans la variable désignée ici. « 0 » si aucune transition n'est en attente. |
| storage | <str> | (en préparation) |
Une action est la « sortie » de la FSM, qui a lieu dans une situation déterminée. Il existe quatre types d'actions :
-
Action d'entrée : l'action est exécutée/émise à l'entrée dans un état (quelle que soit la transition d'état par laquelle l'état a été atteint, s'il y en a plusieurs).
-
Action de sortie : l'action est générée lorsqu'on quitte un état (quelle que soit la transition d'état par laquelle l'état est quitté).
-
Action d'entrée (input) : l'action est générée en fonction de l'état actuel et de l'entrée. Plusieurs actions peuvent donc être associées à un état, exécutées selon la transition d'état par laquelle il est atteint/quitté.
-
Action de transition : l'action est exécutée pendant une transition d'état
Si act = state(…) est une variable qui représente l'état actif, les actions dépendant de l'état peuvent être réalisées comme suit :
-
Action d'entrée :
posedge(act == K) -
Action de sortie :
negedge(act == K) -
Action d'entrée (input) :
(act == K)
Avec actVar : “TR“ définissable dans la transition, on peut finalement aussi
- réaliser des actions de transition : TR
Transitions « transit »2
La fonction transit() crée les objets de transition qui sont transmis en paramètres à l'automate d'états states(). Le résultat de cette fonction ne peut être traité que dans la fonction states().
-
sx : est l'état suivant, si la condition de la transition est remplie. Si sx est absent, la transition ramène à l'état de départ actif, sans toutefois le redémarrer. sx peut aussi provenir d'un calcul ou d'une variable. Si sx ne désigne aucun état connu, la valeur de remplacement sFail est utilisée.
-
cond : condition de la transition d'état, déf. : true. Si une condition ne doit agir qu'après une durée de réponse minimale (temporisation à l'enclenchement / antirebond), il est recommandé de préfiltrer la condition avec
delay(),trigger()outhreshold(). -
tBegin, tEnd : intervalle de temps rapporté à la durée d'activité de l'état dans lequel la condition est évaluée. Déf. :
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>
})
| Propriété | Valeur | Description |
|---|---|---|
| tBegin | <dbl> | Instant en secondes, rapporté à la durée d'activité de l'état, à partir duquel la condition est évaluée, déf. : 0.0 |
| tEnd | <dbl> | Instant en secondes, rapporté à la durée d'activité de l'état, jusqu'auquel la condition est évaluée (exclusif), déf. : |
| sFail | <var> | Identifiant de remplacement / état cible, si sx ne désigne pas un état valide |
| nMax | <int> | Nombre maximal d'activations consécutives (compteur de boucle). Le compteur est remis à zéro dès que l'état cible de cette transition est de nouveau atteint via une transition extérieure ou sans rapport. Pour des boucles correctement imbriquées (première définition = boucle intérieure), les compteurs ne s'influencent pas mutuellement. La cible du saut doit être une étiquette d'état statique — une expression dynamique est traitée comme une erreur. |
| nVar | <str> | Le compteur de boucle peut être publié dans la variable désignée et est mis à jour au déclenchement de la transition. |
| actVar | <str> | Dans le pas où la transition se déclenche, true est émis sur la variable désignée, sinon false. |
| edge | <int> | détermine si la transition est déclenchée de manière statique (0), avec posedge (1) ou negedge (-1) de la condition. |
| prio | <int> | Priorité facultative, si plusieurs transitions se déclenchent simultanément. En principe, c'est la première, dans l'ordre de définition, avec le niveau de prio le plus élevé qui l'emporte. |
Le comportement de réinitialisation de nMax n'est défini que pour des boucles correctement imbriquées, c'est-à-dire lorsqu'une boucle intérieure revient toujours à l'état cible avant sa boucle extérieure. Si les cibles de saut de deux transitions nMax se chevauchent (par ex. C→A et D→B dans une chaîne A→B→C→D), le comportement de réinitialisation du compteur n'est pas prévisible.