Hacer un simulador de controlador on-off de JavaScript

La esencia de la teoría del control automático implica la construcción de tal sistema que mantiene un cierto parámetro de algún objeto en un estado dado, por ejemplo, la temperatura en un horno o el nivel de agua en un tanque. Para una mejor comprensión del proceso, es conveniente considerar inmediatamente un modelo de control específico, por ejemplo, controlar el nivel del agua en un tanque. Por cierto, en libros de texto y artículos sobre TAU, este proceso se menciona con bastante frecuencia como una referencia a la historia, porque en el lejano 1763 el inventor ruso I.I. desarrolló un sistema de control del nivel de agua para su máquina de vapor. Una especie de regulador clásico, que, por cierto, es esencialmente un regulador de dos posiciones como en esta imagen (no hay agua, abra la válvula, hay agua, cierre la válvula).







Es de dos posiciones porque tiene 2 posiciones: abierta (activada) y cerrada (desactivada), en la literatura en inglés on-off. También hay tres o más reguladores de posición, es decir, la válvula de llenado de agua está abierta o cerrada a las posiciones principales, y se agrega la posición "ligeramente abierta". Después de drenar el agua en el inodoro, el flotador baja, abre la válvula completamente y el agua ingresa al tanque a presión máxima, pero más cerca de alcanzar el nivel establecido, el flotador sube, cierra la válvula y reduce el flujo de agua. Y tan pronto como el nivel de agua actual (en inglés PV - Process Value - Current value ) sube al valor establecido (en inglés SP - Set Point - Setpoint), la válvula se cierra y el nivel del agua deja de subir. En el caso descrito, el regulador es aún más similar al proporcional: la acción de regulación disminuye con la disminución del desajuste (error), es decir, la diferencia entre el nivel establecido y el nivel actual.



Al abrir ligeramente la tubería inferior para drenar el agua, será posible lograr ese estado cuando la válvula esté completamente abierta y el nivel del agua no disminuya (es decir, la entrada de agua se vuelve igual a la fuente): el sistema entra en un estado de equilibrio. Pero el problema es que este estado es muy precario - cualquier perturbación externa puede romper este equilibrio - digamos que podemos recoger una cierta cantidad de agua del tanque, y luego puede suceder que toda el agua fluya fuera del tanque (debido al cambio de presión), o la tubería de llenado se obstruirá y el flujo disminuirá, o el flotador se romperá y el agua se desbordará. Ésta es la complejidad de los sistemas de control de edificios: los sistemas reales son bastante complejos y tienen muchas características que deben tenerse en cuenta.Existe una característica como la inercia del sistema: si apaga la estufa calentada, permanecerá caliente durante bastante tiempo, por lo que se utilizan reguladores más complejos para controlar la temperatura, a saberPID: diferencial integral proporcional . Cada uno de los componentes tiene sus propias características: todos se comportan de manera diferente en diferentes condiciones, pero cuando se usan juntos, permiten lograr una regulación bastante clara. Todos estos sistemas se calculan de acuerdo con fórmulas, pero en este caso solo es importante comprender cómo se comportará el sistema cuando cambien los coeficientes del controlador PID: con un aumento en el enlace proporcional, el impacto inicial aumenta y, por lo tanto, el sistema podrá alcanzar rápidamente los parámetros requeridos. Pero si se excede, puede aparecer un sobreimpulso, que puede ser incluso peor que la baja velocidad del sistema.



Durante la existencia de TAU, se encontraron descripciones matemáticas de muchos procesos, y ahora podemos predecir cómo se comportará el sistema en determinadas circunstancias. Hay muchos programas de simulación en los que puede configurar los parámetros del sistema, configurar los parámetros del controlador y ver de forma aproximada lo que resultará de ello. Caminando por Internet, me encontré con un sitio de Excel para ingenieros, y hay varios simuladores de reguladores, gracias a los cuales se puede ver el cambio en el proceso al cambiar los factores de control. El más fácil de repetir fue, por supuesto, el control ON-OFF., es decir, en ruso, un regulador de dos posiciones. Permítame recordarle el principio de funcionamiento: si el valor de proceso actual (valor de proceso = PV) es la temperatura, por ejemplo, por debajo del punto de ajuste (SP), entonces el regulador se enciende (OP): los elementos calefactores se inician a plena capacidad. Tan pronto como la temperatura alcanza el punto de ajuste, el regulador apaga el suministro de voltaje a los elementos calefactores.



Hacer un simulador de JavaScript



Para crear un gráfico, utilizaré la biblioteca ZingChart; resultó ser bastante simple y fácil de usar. Hay muchos ejemplos en la documentación para los que puede construir cualquier cosa. El principio de trazado es bastante simple: hay una matriz de valores que se colocan automáticamente en el gráfico en orden y, por lo tanto, aparece un gráfico de proceso continuo a partir de un par de cientos de puntos. Por cierto, en el original en Excel, todo se hace de la misma manera: se generan 300 valores y se construye un gráfico.



En realidad, es la generación de valores lo más difícil, es decir, la dificultad de describir correctamente un proceso que reacciona correctamente a nuestras acciones de control: encender los elementos calefactores, la temperatura aumenta, se apaga, cae, más la inercia del sistema debe ponerse aquí. Además, el ambiente de calefacción puede ser diferente y algunos medios se calientan y enfrían más rápido, y otros viceversa, y si ajustamos el nivel, entonces con el mismo flujo desde arriba, el nivel subirá más en el tanque donde el área inferior es más pequeña. Todo esto me lleva al hecho de que el proceso también dependerá de la transmisión (ganancia). En el original, también se introdujo un parámetro de retardo en el proceso (bueno, como si el sistema no respondiera inmediatamente a la señal de control), pero decidí abandonarlo: dos son suficientes. Pero agregó un cambio en el escenario,aunque de hecho resultó que el setpoint puede cambiar de cero a 100, sobre 100 el proceso comienza a comportarse de manera diferente, y aparentemente la razón es que la fórmula del proceso es universal y no describe un caso particular. En general, comencemos:



Creamos 5 campos para ingresar parámetros, ponemos todo esto en una tabla, que pintamos de un bonito color arriba en css y la colocamos en el centro:



<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>


Como puede ver, cada vez que cambie el valor de los campos dentro de la tabla, se llamará a la función setvalues ​​(). En él, leemos datos de cada campo en variables especiales.



	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;


Como ya se mencionó, para construir un gráfico, necesita matrices con datos en base a los cuales se construirá el gráfico, por lo que creamos un montón de matrices:



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; //  


Déjame explicarte un poco sobre la histéresis. La situación es la siguiente: cuando la temperatura alcanza el valor establecido, los elementos calefactores se apagan e inmediatamente (de hecho, no inmediatamente, porque hay inercia) comienza el proceso de enfriamiento. Y después de haberse enfriado un grado o incluso una cierta fracción de grado, el sistema comprende que ya ha ido más allá del alcance de la tarea nuevamente y debe encender los elementos calefactores nuevamente. En este modo, los elementos calefactores se encenderán y apagarán con mucha frecuencia, tal vez incluso algo que varias veces por minuto; para equipos, este modo no es muy bueno y, por lo tanto, para excluir tales fluctuaciones, se introduce una llamada histéresis: banda muerta, una banda muerta, digamos 1 grado más alto y por debajo del punto de ajuste, no reaccionaremos, y entonces el número de conmutaciones puede reducirse significativamente. Por lo tanto, la variable baja es el límite inferior del punto de ajuste y la variable alta es la superior.La variable st realiza un seguimiento de alcanzar el nivel superior y permite que el proceso caiga al fondo. La lógica de todo el proceso está en bucle:



	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];
	}


Como resultado, obtenemos una matriz con un número determinado de puntos, que enviamos al script de gráficos.



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


Código completo debajo del 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>




Bueno, dado que el simulador está listo, es hora de comprobar cómo funciona. Puede probar aquí el mismo código pero en github: simulador de control de encendido y apagado



Configuración estándar: enlace del amplificador 1, constante de tiempo 100 segundos, histéresis 2%







Ahora, si establece una configuración más grande, por ejemplo 92, de repente el proceso se ralentiza mucho, aunque la configuración es 50 gana en los mismos 71 segundos, pero solo entonces la curva comienza a acercarse a la tarea de manera más lenta exponencialmente, y alcanza el setpoint en solo 278 segundos, por lo que fue necesario ampliar el rango de trazado a 300 puntos







Este ejemplo es muy indicativo, traduciendo la situación a un modelo con temperatura, podemos concluir que no hay suficiente potencia del calentador: el calentador está cargado al 100%, pero la temperatura deja de subir después de cierto momento. Puede haber varias soluciones: poner un segundo elemento calefactor del mismo, o aplicarle voltaje 2 veces más (pero esto puede dañar el elemento calefactor), o poner un calentador con 2 veces más potencia, o verter un líquido más conductor de calor en el sistema cuando se trata de calentar. líquidos Es bastante interesante que si necesita mantener la temperatura en la región de 95-100 grados, entonces ni siquiera necesita poner el regulador; coloque un calentador de baja potencia, córtelo al máximo y listo, después de 300 segundos (300 segundos condicionales) puede obtener los 100 grados deseados.El problema con dicho sistema es que si abre una ventana en invierno a menos 40ºC, la temperatura bajará inmediatamente y de manera bastante significativa, y la velocidad de dicho sistema es muy baja.



Aumentemos la sección de ganancia 2 veces; es como instalar un segundo elemento calefactor del mismo tipo o agregar otra tubería para reponer el tanque.







El gráfico resultó ser también bastante indicativo: la temperatura alcanzó los 51 grados en realidad alcanzó 2 veces más rápido, pero alcanzó los 92 grados 4 veces más rápido. No sé qué tan cerca está un simulador de este tipo de los procesos reales, pero dado que la dependencia especificada en él es exponencial, este es un comportamiento completamente esperado del sistema, pero ni siquiera puedo imaginar explicarlo desde la perspectiva de agregar una segunda tubería y aumentar la tasa de llenado en 4 veces. La respuesta de una función lineal sería más predecible a un aumento en el coeficiente, pero los sistemas reales en la vida rara vez son lineales.



All Articles