Saltar al contenido principal

Control y regulación

Control y regulación con el módulo matemático​

Dada la amplia biblioteca de funciones del módulo matemático, resulta natural implementar también funciones de control y regulación en este lenguaje de script.

Esto es realmente posible con algunas limitaciones y teniendo en cuenta los puntos siguientes:

Procesamiento de señales, comportamiento temporal​

El proceso de control y regulación requiere cálculos en tiempo real estricto, es decir, un tiempo de propagación de la señal fijo y lo más corto posible desde la adquisición de la magnitud medida hasta la salida de la variable manipulada al proceso. En los sistemas de control, este tiempo es habitualmente de aprox. 1 .. 10 ms. ¡Esta condición estricta no puede cumplirse con la arquitectura del smartCORE!

Los datos de medición son adquiridos por determinados plugins y escritos en los canales del smartCORE. De forma asíncrona respecto a ello, en intervalos de tiempo grandes y aproximadamente constantes, el conjunto de fórmulas del módulo matemático se calcula sobre los datos disponibles, siempre que todos los canales necesarios proporcionen datos para un instante determinado. Los resultados se escriben en canales del smartCORE al ritmo de este cálculo. De forma asíncrona respecto a ello, los datos de los canales del smartCORE se emiten al proceso, p. ej., a través de CAN.

Por tanto, hay suficientes puntos en los que el requisito de tiempo real se incumple debido a la asincronía en el smartCORE, a los canales como búfer o memoria circular y al procesamiento por lotes del módulo matemático.

Si los procesos que se van a influir son suficientemente lentos1, al menos puede ajustarse el proceso de cálculo y la salida de modo que el tiempo de muestreo discreto discreteSampleTimeMs coincida con el tiempo de ejecución del proceso por lotes evaluationTimeMs y con la salida al proceso. Los valores posibles se encuentran aquí en el intervalo 50 .. 1000 ms. Entonces, la asincronía y el almacenamiento en búfer de los flujos de datos resultan tolerables.

Reguladores​

Regulador PID "pidCtrl"​

Under Construction Esta función está en fase de implementación y todavía no está prevista para el funcionamiento productivo.

La función pidCtrl() implementa un regulador con comportamiento proporcional (P), integral (I) y derivativo (D) configurable.

Los parámetros de entrada de la función son, como mínimo, la magnitud de referencia (valor de consigna, setpoint) sp y la magnitud de proceso que se debe controlar (valor real, current value) cur. La salida es la variable manipulada.

Opcionalmente puede seleccionarse el modo de funcionamiento mediante la entrada mode:

modeDescripción
1, trueFuncionamiento de regulación normal (Enable, def.)
2La variable manipulada sigue la entrada de control manual u_man (Bypass)
0, false y todos los demás valoresMantener la variable manipulada (Hold)

Opcionalmente, con el vector de entrada parVec los parámetros de regulación [K, Ti, Td] también pueden asignarse de forma dinámica. El vector puede contener de 1 a 3 valores de parámetro. Estos sobrescriben las indicaciones del objeto de configuración.

important

El cálculo se realiza siempre con el tiempo de muestreo discreto discreteSampleTimeMs. Para que la variable de control pueda transferirse al proceso de forma puntual, el tiempo de ejecución del proceso por lotes evaluationTimeMs debe ajustarse al mismo valor.

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|<time>
                 , Td: off|<time>
                 , reverse: <bool>
                 , commonK: <bool>
                 , dWidth: <uint>
                  , lower: off|<dbl>
                  , upper: off|<dbl>
});

La descripción de las propiedades se realiza con ejemplos en el dominio del tiempo, aunque internamente la función calcula en pasos de muestreo discretos.

PropiedadValorDescripción
K<dbl>Ganancia del regulador
Tioff|<time>Activación y tiempo de integración de la componente I
I(e)=1Ti⋅∫e(t)dtI(e) = \frac{1}{T_i} \cdot \int e(t) dt
Tdoff|<time>Activación y tiempo de derivación de la componente D
D(e)=Td⋅ddte(t)D(e)=T_d \cdot \frac{d}{dt} e(t)
reverse<bool>Inversión del sentido de actuación
false: e=sp−cure=sp-cur, (def.)
true: e=cur−spe=cur - sp
commonK<bool>Interpretación de la ganancia:
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>Número de intervalos de muestreo para el promedio ponderado del valor diferencial (def.: 3)
loweroff|<dbl>Límite inferior de la variable manipulada,
la componente integral se reajusta para permitir una reanudación sin saltos.
upperoff|<dbl>Límite superior de la variable manipulada,

Estructura del regulador: la propiedad commonK conmuta la interpretación del factor de ganancia K. En ingeniería de control, K actúa habitualmente por igual sobre las tres vías P, I y D (commonK: true, ¡def.!) y se encarga también de la conversión de las dimensiones físicas entre la desviación de regulación (p. ej., en hPa) y la variable manipulada (p. ej., en %). Con ello, K no influye en el comportamiento dinámico del regulador, que viene determinado por sus polos y ceros, sino solo en la agilidad y la estabilidad del lazo de regulación cerrado. Muchas reglas de ajuste para reguladores PID se basan en esta estructura.

Raramente se encuentra la interpretación de que K influye realmente solo en la vía proporcional del regulador (commonK: false). Con ello, la tarea de la conversión de unidades se traslada también a los parámetros de tiempo Ti y Td, y la modificación de K altera directamente los polos y ceros del regulador y, con ello, su comportamiento dinámico. El ajuste del regulador resulta así considerablemente más difícil.

Sobre el uso de la componente D: como la función de regulador está ligada al ciclo discreto, muy lento, del módulo matemático, la evaluación del gradiente de la desviación de regulación en la componente D también debe utilizarse con cautela. Debido a la filosofía "On-Change" en la puesta a disposición de los datos de medición en un canal de smartCORE, pueden producirse largos intervalos de tiempo en los que la señal no cambia. Para poder detectar, no obstante, p. ej., una deriva lenta de una temperatura, los valores de varios pasos de muestreo se combinan con una función de ponderación para obtener el valor de la derivada. La anchura de esta ventana se indica con la propiedad dWidth. Sin embargo, esto genera también un tiempo muerto adicional en la vía D del regulador (dWidth/2 * discreteSampleTimeMs), que puede influir negativamente en el proceso de regulación. Por tanto, dWidth debe elegirse lo más pequeño posible, pero tan grande como sea necesario. Esto depende del proceso que se va a regular.

Referencia temporal: en el caso de que la magnitud de referencia sp proceda de un atributo (compartido, shared) y se actualice solo raramente, este canal debe liberarse imperativamente de la referencia temporal mediante la macro #timeless. Lo mismo puede aplicarse también a la magnitud de proceso cur.

Autómata de estados (Finite State Machine)​

Implementación de una Finite State Machine (FSM)​

Puede encontrarse una descripción general en el artículo Finite-state machine - Wikipedia o también aquí (DE).

En esta implementación, la evaluación se realiza con el ritmo del tiempo de muestreo discreto global discreteSampleTimeMs. En cada ciclo, exactamente una transición puede provocar un cambio de estado.

Este autómata de estados finito posee un número finito de estados (States), identificados por un identificador unívoco sx. Cada estado es estable hasta que una transición de estado (Transition) desencadena un cambio. Con ello, un estado puede contener información sobre el pasado, ya que el sistema lo ha alcanzado en su recorrido hasta ese momento, es decir, refleja en cierta medida los cambios de la entrada desde el arranque del sistema hasta el instante actual.

Una transición de estado (Transition) es un paso del estado actual a un nuevo estado (distinto). Esta transición se produce cuando se dan las condiciones lógicas / «entradas» indicadas, que deben cumplirse para permitirla. En esta implementación están disponibles para ello una condición booleana, así como una ventana de tiempo definible, un contador de repeticiones y un valor de prioridad para la habilitación.

La siguiente imagen muestra una máquina de estados ficticia con 5 estados "A" a "D" y "end".

Imagen de la máquina de estados

consejo

Para el diseño se recomienda dibujar precisamente un grafo así para la máquina de estados que se va a implementar, con los estados y transiciones necesarios.

Con el arranque del intérprete de fórmulas "init" se adopta un estado básico "A". Distintas transiciones de estado hacen que el autómata cambie a nuevos estados o, mediante prioridades definidas de las transiciones, mantienen un estado fijo bajo determinadas condiciones. Un estado del que no parte ninguna transición se denomina estado final.

Las transiciones "from-anywhere" pueden provocar un cambio de estado independientemente del estado activo (incluido un estado final).

La estructura mostrada se traduciría al texto de fórmulas según el siguiente esquema; los argumentos para la condición de conmutación o los parámetros de tiempo, así como los parámetros JSON de las transiciones, deben completarse según corresponda:

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 });             
consejo

La notación en varias líneas con la sangría correspondiente ayuda a mantener la visión de conjunto de estados y transiciones. Lo mismo ocurre con el uso intensivo de comentarios.

La variable act contiene, debido a outIdx: true, el índice del estado actual (1, 2, 3, …) y puede utilizarse en las expresiones siguientes para controlar las acciones de la máquina de estados. Para ello son adecuadas, p. ej., comparaciones sencillas en someResult = (act == 3) ? ... : ...; o la función select(act, ...), pero también la detección de flancos mediante . Distintos valores de parámetro podrían leerse también directamente con parVec[act-1] de un vector parVec = [1.3, 5.6, -3.0, 0.0]; o mediante la función de búsqueda de una tabla con selectRow() y getField(). Véase también la siguiente descripción de states(...).

Autómata de estados "states"2​

Con esta función states() puede modelarse un comportamiento que consta de estados, transiciones de estado y acciones.

s1, … sN: definen los identificadores de los estados sx de la máquina de estados como const <uint/int/str> (!). El primer estado es el estado inicial, a menos que otro se determine mediante la propiedad init: sx. La salida de states() es el valor correspondiente de sx del estado activo, a menos que con la propiedad outIdx: true se fuerce la salida del índice de estado como [1,2,…N][1, 2, … N]. Todos los sx deben ser biunívocos, ya que se utilizan en las funciones transit() para indicar el siguiente estado.

trXY: definen objetos transit() que se evalúan para el estado mencionado anteriormente. La primera función transit() cuyas condiciones configuradas se cumplen desencadena la transición de estado.

Las transiciones de estado definidas antes del primer estado tienen un papel especial. Se evalúan en cualquier estado, antes de que se evalúen las transiciones definidas en el propio estado. Con ello asumen el papel de transiciones "from any", p. ej., para interceptar condiciones de error, realizar un TimeOut o forzar restablecimientos.

Los parámetros de la función states() describen así el autómata, en el que un parámetro const <uint/int/str> define bien un nuevo estado, o bien, mediante transit(), transiciones de estado desde ese estado.

act = states(tr00, tr01, …
, s1, tr10, tr11, …
, s2, tr20, tr21, …);
// Optionale Konfiguration
actX = states(..., { init: <sx>
, outIdx: <bool>
, ageVar: <str>
, dbgVar: <str>
});
PropiedadValorDescripción
init<var>Estado inicial del autómata, si no es por defecto el primero
outIdx<bool>true: salida del índice del estado activo <uint> [1,2,…N][1, 2, … N]
false: salida del identificador definido para el estado <uint/int/str>
ageVar<str>La «edad» del estado activo en segundos puede publicarse en la variable aquí indicada.
dbgVar<str>El número de nodo de la transición desencadenante en cada caso se escribe en la variable aquí indicada. "0", si no hay ninguna transición pendiente.
storage<str>(en preparación)

Una acción es la «salida» de la FSM que se produce en una situación determinada. Existen cuatro tipos de acciones:

  • Acción de entrada: la acción se ejecuta/emite al entrar en un estado (independientemente de la transición de estado por la que se haya alcanzado el estado, si hay varias).

  • Acción de salida: la acción se genera al abandonar un estado (independientemente de la transición de estado por la que se abandone el estado).

  • Acción de entrada de datos: la acción se genera en función del estado actual y de la entrada. A un estado pueden estar asignadas, por tanto, varias acciones, que se ejecutan según la transición de estado por la que se alcance/abandone.

  • Acción de transición: la acción se ejecuta durante una transición de estado

Si act = state(…) es una variable que representa el estado activo, las acciones dependientes del estado pueden realizarse como sigue:

  • Acción de entrada: posedge(act == K)

  • Acción de salida: negedge(act == K)

  • Acción de entrada de datos: (act == K)

Con actVar: “TR“, que puede definirse en la transición, también pueden realizarse en última instancia

  • Acciones de transición: TR

Transiciones "transit"2​

La función transit() genera los objetos de transición que se pasan como parámetros al autómata de estados states(). El resultado de esta función solo puede procesarse en la función states().

  • sx: es el siguiente estado, siempre que se cumpla la condición de la transición. Si falta sx, la transición vuelve al estado de origen activo en cada caso, sin reiniciarlo. sx también puede proceder de un cálculo o de una variable. Si sx no designa un estado conocido, se utiliza el valor sustitutivo sFail.

  • cond: condición para la transición de estado, def.: true. Si una condición debe actuar solo después de una duración mínima de respuesta (retardo de conexión / antirrebote), se recomienda prefiltrar la condición con delay(), trigger() o threshold().

  • tBegin, tEnd: intervalo de tiempo referido al tiempo de actividad del estado en el que se evalúa la condición. 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: <time>
, tEnd: off|<time>
, sFail: <var>
, nMax: <int>
, nVar: <str>
, actVar: <str>
, edge: <int>
, prio: <int>
})
PropiedadValorDescripción
tBegin<time>Instante en segundos, referido al tiempo de actividad del estado, a partir del cual se evalúa la condición, def.: 0.0
tEndoff/<time>Instante en segundos, referido al tiempo de actividad del estado, hasta el cual se evalúa la condición (exclusivo), def.: ∞\infty
off: ∞\infty
sFail<var>Identificador sustitutivo / estado de destino, si sx no designa un estado válido
nMax<int>Número máximo de activaciones consecutivas (contador de bucle). El contador se restablece en cuanto se vuelve a entrar en el estado de destino de esta transición a través de una transición externa o no relacionada. En bucles correctamente anidados (primera definición = bucle interno), los contadores no se influyen mutuamente. El destino del salto debe ser una etiqueta de estado estática; una expresión dinámica se trata como error.
nVar<str>El contador de bucle puede publicarse en la variable indicada y se actualiza cuando se dispara la transición.
actVar<str>En el paso en el que se dispara la transición, en la variable indicada se emite true; en caso contrario, false.
edge<int>determina si la transición se dispara de forma estática (0), con posedge (1) o con negedge (-1) de la condición.
prio<int>Prioridad opcional, si se disparan varias transiciones al mismo tiempo. En principio gana, en el orden de definición, la primera con el nivel de prio más alto.
Bucles entrelazados

El comportamiento de restablecimiento de nMax solo está definido para bucles correctamente anidados, es decir, cuando un bucle interno vuelve siempre al estado de destino antes que su bucle externo. Si se solapan los destinos de salto de dos transiciones nMax (p. ej., C→A y D→B en una cadena A→B→C→D), el comportamiento de restablecimiento del contador no es predecible.

Footnotes​

  1. La constante de tiempo más rápida del proceso (T0=12πf0T_0 = \frac{1}{2\pi f_0}) debería ser de 10 a 15 veces el tiempo de muestreo del sistema de control/regulación. ↩

  2. Disponible a partir de la versión 13 del catálogo. ↩ ↩2