Créer un simulateur de contrôleur JavaScript on-off

L'essence de la théorie du contrôle automatique implique la construction d'un tel système qui maintient un certain paramètre d'un objet dans un état donné, par exemple la température dans un four ou le niveau d'eau dans un réservoir. Pour une meilleure compréhension du processus, il est pratique d'envisager immédiatement un modèle de contrôle spécifique, par exemple, le contrôle du niveau d'eau dans un réservoir. À propos, dans les manuels et articles sur TAU, ce processus est assez souvent mentionné comme référence à l'histoire, car dans le lointain 1763, l'inventeur russe I.I. a développé un système de contrôle du niveau d'eau pour sa machine à vapeur. Une sorte de régulateur classique, qui, soit dit en passant, est essentiellement un régulateur à deux positions comme sur cette photo (il n'y a pas d'eau - ouvrez la vanne, il y a de l'eau, fermez la vanne).







Il est à deux positions car il a 2 positions: ouvert (on) et fermé (off), dans la littérature anglophone on-off. Il existe également trois régulateurs de position ou plus, c'est-à-dire que la vanne de remplissage d'eau est ouverte ou fermée aux positions principales, et la position «légèrement ouverte» est ajoutée. Après avoir vidangé l'eau dans les toilettes, le flotteur descend, ouvrant complètement la vanne et l'eau pénètre dans le réservoir à pleine pression, mais plus près d'atteindre le niveau réglé, le flotteur monte, fermant la vanne et réduisant le débit d'eau. Et dès que le niveau d'eau actuel (en anglais PV - Process Value - Current value ) monte au set (en anglais SP - Set Point - Setpoint), la vanne se ferme et le niveau d'eau cesse de monter. Dans le cas décrit, le régulateur est encore plus similaire au régulateur proportionnel - l'action de régulation diminue avec la diminution de la discordance (erreur), c'est-à-dire la différence entre le niveau réglé et le niveau actuel.



En ouvrant légèrement le tuyau inférieur pour drainer l'eau, il sera possible d'atteindre un tel état lorsque la vanne est complètement ouverte et que le niveau d'eau ne diminue pas (c'est-à-dire que l'afflux d'eau devient égal à la source) - le système entre dans un état d'équilibre. Mais le problème est que cet état est très précaire - toute perturbation externe peut rompre cet équilibre - disons que nous pouvons ramasser une certaine quantité d'eau du réservoir, et puis il peut arriver que toute l'eau s'écoule ensuite du réservoir (en raison du changement de pression), ou le tuyau de remplissage se bouchera et le débit diminuera, ou le flotteur se cassera et l'eau débordera. C'est la complexité des systèmes de contrôle des bâtiments - les systèmes réels sont assez complexes et ont de nombreuses caractéristiques dont il faut tenir compte.Il existe une caractéristique telle que l'inertie du système - si vous éteignez la plaque chauffante, elle restera chaude pendant assez longtemps, c'est pourquoi des régulateurs plus complexes sont utilisés pour contrôler la température, à savoirPID - Différentiel intégral proportionnel . Chacun des composants a ses propres caractéristiques - ils se comportent tous différemment dans des conditions différentes, mais lorsqu'ils sont utilisés ensemble, ils permettent une réglementation assez claire. Tous ces systèmes sont calculés selon des formules, mais dans ce cas, il est juste important de comprendre comment le système se comportera lorsque les coefficients du contrôleur PID changeront: avec une augmentation du lien proportionnel, l'impact initial augmente et ainsi le système sera en mesure d'atteindre rapidement les paramètres requis. Mais si vous en faites trop, un dépassement peut apparaître, ce qui peut être encore pire que la faible vitesse du système.



Au cours de l'existence de TAU, des descriptions mathématiques de nombreux processus ont été trouvées, et maintenant nous pouvons prédire comment le système se comportera dans certaines circonstances. Il existe de nombreux programmes de simulation dans lesquels vous pouvez définir les paramètres du système, définir les paramètres du contrôleur et voir approximativement ce qui en résultera. En parcourant Internet, je suis tombé sur un site Excel pour les ingénieurs, et il existe plusieurs simulateurs de régulateurs, grâce auxquels vous pouvez regarder le changement dans le processus lors du changement des facteurs de contrôle. Le plus simple à répéter était, bien sûr, la commande ON-OFF., c'est-à-dire, en russe, un régulateur à deux positions. Permettez-moi de vous rappeler le principe de fonctionnement: si la valeur de processus actuelle (valeur de processus = PV) est la température, par exemple, en dessous du point de consigne (SP), le régulateur se met en marche (OP) - les éléments chauffants sont démarrés à pleine puissance. Dès que la température atteint le point de consigne, le régulateur coupe l'alimentation en tension des éléments chauffants.



Créer un simulateur JavaScript



Pour créer un graphique, j'utiliserai la bibliothèque ZingChart - elle s'est avérée assez simple et facile à utiliser. Il existe de nombreux exemples dans la documentation pour lesquels vous pouvez créer n'importe quoi. Le principe du tracé est assez simple - il existe un tableau de valeurs qui sont automatiquement placées sur le graphique dans l'ordre, et ainsi un graphique de processus continu apparaît à partir de quelques centaines de points. À propos, dans l'original dans Excel, tout se fait de la même manière - 300 valeurs sont générées et un graphique est construit.



En fait, c'est la génération de valeurs qui est la plus difficile, à savoir la difficulté de décrire correctement un processus qui réagit correctement à nos actions de contrôle - allumer les éléments chauffants - la température monte, s'éteint - diminue, plus l'inertie du système doit être mise ici. De plus, l'environnement de chauffage peut être différent et certains supports chauffent et refroidissent plus rapidement, et d'autres vice versa, et si nous ajustons le niveau, alors avec le même débit d'en haut, le niveau augmentera plus haut dans le réservoir où la zone inférieure est plus petite. Tout cela, je conduis au fait que le processus dépendra également de la transmission (gain). Dans l'original, un paramètre de retard a également été introduit dans le processus (enfin, comme le système ne répond pas immédiatement au signal de commande), mais j'ai décidé de l'abandonner - deux suffisent. Mais j'ai ajouté un changement dans le cadre,bien qu'en fait, il s'est avéré que le point de consigne peut changer de zéro à 100, sur 100, le processus commence à se comporter différemment, et apparemment la raison est que la formule du processus est universelle et ne décrit pas un cas particulier. En général, commençons:



Nous créons 5 champs pour entrer les paramètres, nous mettons tout cela dans un tableau, que nous peignons dans une belle couleur ci-dessus en css et le plaçons au centre:



<table align="center" oninput="setvalues ();">
	<tr>
	<td>
Process parameters <br>
Gain: <input id="gain" type="number" value ="1" ><br>
Time Constant: <input id="time" type="number" value ="100" ><br>
	</td>
	<td>
Control parameters <br>
SetPoint(0-100): <input id="sp" type="number" value ="50"><br>
Hysteresis: <input id="hyst" type="number" value ="1">%<br>
	</td>
	<td>
Plot parameters <br>	
Points: <input id="points" type="number" value ="200"><br>
	</td>
	</tr>
</table>


Comme vous pouvez le voir, chaque fois que la valeur des champs à l'intérieur de la table change, la fonction setvalues ​​() sera appelée. Dans celui-ci, nous lisons les données de chaque champ dans des variables spéciales.



	let gain = document.getElementById('gain').value;
	let time = document.getElementById('time').value;
	let sp = document.getElementById('sp').value;
	let points = document.getElementById('points').value;
	let hyst = document.getElementById('hyst').value;


Comme déjà mentionné, pour créer un graphique, vous avez besoin de tableaux avec des données sur la base desquelles le graphique sera construit, nous créons donc un tas de tableaux:



let pv = []; //    
let pv100 = []; //   *100
let op = []; //   1 , 0 
let pvp = 0; //  
let low = sp-sp*hyst/100;//  
let high = +sp+(sp*hyst/100); //   
let st=true; //  


Je vais expliquer un peu l'hystérésis. La situation est la suivante: lorsque la température atteint la valeur de consigne, les éléments chauffants s'éteignent et immédiatement (en fait, pas immédiatement, car il y a une inertie) le processus de refroidissement commence. Et après avoir refroidi d'un degré ou même d'une certaine fraction de degré - le système se rend compte qu'il a déjà dépassé à nouveau le cadre de la tâche et qu'il est nécessaire de rallumer les éléments chauffants. Dans ce mode, les éléments chauffants s'allumeront et s'éteindront très souvent, peut-être quelque chose que plusieurs fois par minute - pour les équipements, un tel mode n'est pas très bon, et par conséquent, afin d'exclure de telles fluctuations, une soi-disant hystérésis est introduite - bande morte - une bande morte - disons 1 degré plus haut et en dessous du point de consigne, nous ne réagirons pas, puis le nombre de commutations peut être considérablement réduit. Par conséquent, la variable basse est la limite inférieure du point de consigne et la variable haute est la limite supérieure.La variable st garde une trace de l'atteinte du niveau supérieur et permet au processus de tomber vers le bas. La logique de l'ensemble du processus est en boucle:



	for (var i=0;i<points;i++) {
		if (pvp<=(low/100)) {
			st=true;
			op[i]=1;
			}//
		else if (pvp<=(high/100)&& st) op[i] = 1;
		else { st=false; op[i]=0;}
		
		let a = Math.pow(2.71828182845904, -1/time);
		let b = gain*(1 -a);
		pv[i] = op[i]*b+pvp*a;
		pv100[i] = pv[i]*100;
		pvp = pv[i];
	}


En conséquence, nous obtenons un tableau avec un nombre donné de points, que nous envoyons au script de création de graphiques.



scaleX: {
 	zooming: true
  },
      series: [
		{ values: op , text: 'OP' },
        { values: pv100 , text: 'PV'}
      ]
    };


Code complet sous le spoiler
<!DOCTYPE html>
<html>
 
<head>
  <meta charset="utf-8">
  <title></title>
 
  <script src="https://cdn.zingchart.com/zingchart.min.js"></script>
  <style>
    html,
    body,
    #myChart {
      width: 100%;
      height: 100%;
    }
	input {
	width: 25%;
	text-align:center;
	}
	td {
	
	background-color: peachpuff;
	text-align: center;
	}	
  </style>
</head>
<body>
<table align="center" oninput="setvalues ();">
	<tr>
	<td>
Process parameters <br>
Gain: <input id="gain" type="number" value ="1" ><br>
Time Constant: <input id="time" type="number" value ="100" ><br>
	</td>
	<td>
Control parameters <br>
SetPoint(0-100): <input id="sp" type="number" value ="50"><br>
Hysteresis: <input id="hyst" type="number" value ="2">%<br>
	</td>
	<td>
Plot parameters <br>	
Points: <input id="points" type="number" value ="250"><br>
Animation: <input type="checkbox" id="animation">
	</td>
	</tr>
</table>

<script>

setTimeout('setvalues ()', 0);

function setvalues (){

	let gain = document.getElementById('gain').value;
	let time = document.getElementById('time').value;
	let sp = document.getElementById('sp').value;
	let points = document.getElementById('points').value;
	let hyst = document.getElementById('hyst').value;
	let anim = document.getElementById('animation').checked ? +1 : 0;
	let pv = []; //    
	let pv100 = []; //   *100
	let op = []; //   1 , 0 
	let pvp = 0; //  
	let low = sp-sp*hyst/100; //  
	let high = +sp+(sp*hyst/100); //  
	let st=true; //  
	for (var i=0;i<points;i++) {
		if (pvp<=(low/100)) {
			st=true;
			op[i]=1;
			}
		else if (pvp<=(high/100)&& st) op[i] = 1;
		else { st=false; op[i]=0;}
		
		let a = Math.pow(2.71828182845904, -1/time);
		let b = gain*(1 -a);
		pv[i] = op[i]*b+pvp*a;
		pv100[i] = pv[i]*100;
		pvp = pv[i];
	}
	
	ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"];
    var myConfig = {
    type: "line",
    "plot": {
		"animation": {
          "effect": anim,
          "sequence": 2,
          "speed": 200,
        }
		},
	legend: {
    layout: "1x2", //row x column
    x: "20%",
    y: "5%",
	},
 	crosshairX:{
 	  plotLabel:{
 	    text: "%v"
 	  }
 	},
      "scale-y": {
    item: {
      fontColor: "#7CA82B"
    },
    markers: [
	 {
        type: "area",
        range: [low, high],
        backgroundColor: "#d89108",
        alpha: 0.7
      },
	{
        type: "line",
        range: [sp],
        lineColor: "#7CA82B",
        lineWidth: 2,
		  label: { //define label within marker
          text: "SP = "+sp,
          backgroundColor: "white",
          alpha: 0.7,
          textAlpha: 1,
          offsetX: 60,
          offsetY: -5
        }
      }]
	},	
	scaleX: {
		zooming: true
	},
	  'scale-y-2': {
	  values: "0:1"
	},
      series: [
		{ scales: "scale-x,scale-y-2", values: op , 'legend-text': 'OP' },
        { values: pv100 , text: 'PV'}
      ]
    };
 
    zingchart.render({
      id: 'myChart',
      data: myConfig,
      height: "90%",
      width: "100%"
    });
}


</script>
  <div id='myChart'></div>
</body> 
</html>




Eh bien, puisque le simulateur est prêt, il est temps de vérifier son fonctionnement. Vous pouvez tester ici le même code mais sur le github: simulateur de contrôle on-off Réglage



standard: lien amplificateur 1, constante de temps 100 secondes, hystérésis 2%







Maintenant, si vous définissez un réglage plus grand, par exemple 92, alors soudainement le processus ralentit beaucoup, bien que le réglage soit 50 il gagne dans les mêmes 71 secondes, mais ce n'est qu'alors que la courbe commence à approcher la tâche plus lentement de manière exponentielle, et atteint la consigne en seulement 278 secondes, c'est pourquoi il était nécessaire d'étendre la plage de traçage à 300 points







Cet exemple est très indicatif, traduisant la situation en un modèle avec température, nous pouvons conclure qu'il n'y a pas assez de puissance de chauffage: le chauffage est chargé à 100%, mais la température cesse d'augmenter après un certain moment. Il peut y avoir plusieurs solutions: mettre un deuxième élément chauffant du même, ou lui appliquer 2 fois plus de tension (mais cela peut endommager l'élément chauffant), ou mettre un appareil de chauffage avec 2 fois plus de puissance, ou verser un liquide plus conducteur de chaleur dans le système lorsqu'il s'agit de chauffer liquides. Il est assez intéressant de noter que si vous devez maintenir la température dans la région de 95 à 100 degrés, vous n'avez même pas besoin de mettre le régulateur - mettez un appareil de chauffage de faible puissance, coupez-le au maximum et c'est tout - après 300 secondes (300 secondes conditionnelles), vous pouvez obtenir les 100 degrés souhaités.Le problème avec un tel système est que si vous ouvrez la fenêtre en hiver à moins 40, la température baissera immédiatement et de manière assez significative, et les performances d'un tel système sont très faibles.



Augmentons la section de gain de 2 fois - c'est comme installer un deuxième élément chauffant du même type ou ajouter un autre tuyau pour remplir le réservoir.







Le graphique s'est avéré également assez indicatif - la température atteignait 51 degrés en fait 2 fois plus vite, mais atteignait 92 degrés 4 fois plus vite. Je ne sais pas à quel point un tel simulateur est proche des processus réels, mais comme la dépendance qui y est spécifiée est exponentielle, il s'agit d'un comportement complètement attendu du système, mais je ne peux même pas imaginer expliquer du point de vue de l'ajout d'un deuxième tuyau et d'augmenter le taux de remplissage de 4 fois. La réponse d'une fonction linéaire serait plus prévisible à une augmentation du coefficient, mais les systèmes réels dans la vie sont rarement linéaires.



All Articles