Esempi di utilizzo e applicazione del regolatore. Controller PID: descrizione completa, applicazione. Coordinamento dei dispositivi di uscita dei regolatori continui

Sistemi di controllo automatico (ACS) sono progettati per modificare automaticamente uno o più parametri di un oggetto di controllo al fine di stabilire la modalità richiesta del suo funzionamento. L'ACS garantisce il mantenimento della costanza dei valori specificati dei parametri regolamentati o la loro modifica secondo una determinata legge, oppure ottimizza determinati criteri per la qualità del controllo. Ad esempio, tali sistemi includono:

  • sistemi di stabilizzazione,
  • sistemi di controllo del programma,
  • sistemi di tracciamento

Questa è una classe abbastanza ampia di sistemi che può essere trovata ovunque. Ma cosa c'entra questo con Unity3D e probabilmente con i giochi in particolare? In linea di principio, è semplice: in qualsiasi gioco che in qualche modo utilizza la simulazione come elemento di gioco, vengono implementate armi semoventi, tali giochi includono, ad esempio, Kerbal Space Program, Digital Combat Simulator (ex Lock On), Strike Suit Zero, eccetera. (chi conosce più esempi - scrivi nei commenti). In linea di principio, qualsiasi gioco che simula processi fisici reali, inclusa semplicemente la cinematica con dinamica del movimento, può implementare l'una o l'altra pistola semovente: questo approccio è più semplice, più naturale e lo sviluppatore ha già una serie di strumenti già pronti forniti da tutti i tipi di Vyshnegradsky, Lyapunov, Kalman, Chebyshev e altri Kolomogorov, quindi puoi fare a meno di reinventare la ruota, perché è già stata inventata, tanto da diventare una scienza a parte: la Teoria del Controllo Automatico. La cosa principale qui è non esagerare. C’è solo un problema: di TAU non si parla ovunque, non a tutti, spesso poco e poco chiaramente.

Una piccola teoria

Il classico sistema di controllo automatico è mostrato nella figura seguente:



L'elemento chiave di qualsiasi cannone semovente è regolatore che è un dispositivo che monitora lo stato dell'oggetto di controllo e fornisce la legge di controllo richiesta. Il processo di controllo comprende: calcolo dell'errore di controllo o segnale di errore e(T) qual è la differenza tra il desiderato collocamento(setpoint o SP ) e il valore corrente del processo (process vale or PV ), dopodiché il regolatore genera segnali di controllo (valore manipolato o MV ).


Un tipo di regolatore è regolatore proporzionale-integrale-derivativo (PID)., che genera un segnale di controllo, che è la somma di tre termini: proporzionale, integrale e differenziale.



Dove, errore di disadattamento, nonché componenti (termini) - proporzionali, - integrali, - differenziali della legge di controllo, che nella sua forma finale è descritta dalle seguenti formule




Componente proporzionale P- è responsabile del cosiddetto controllo proporzionale, il cui significato è che il segnale di uscita del controller contrasta la deviazione della variabile controllata (errore di non corrispondenza o anche chiamato residuo) dal valore impostato. Maggiore è l'errore di mancata corrispondenza, maggiore è la deviazione del comando del controller. Questa è la legge di controllo più semplice e ovvia. Difetto La legge del controllo proporzionale è che il regolatore non si stabilizza mai ad un dato valore e un aumento del coefficiente di proporzionalità porta sempre ad auto-oscillazioni. Ecco perché, oltre alla legge di controllo proporzionale, è necessario utilizzare quella integrale e differenziale.


Componente integrale I accumula (integra) l'errore di controllo, che consente al controller PID di eliminare l'errore statico (errore stazionario, errore residuo). O in altre parole: collegamento integrale introduce sempre qualche pregiudizio e se il sistema è soggetto ad alcuni errori permanenti, li compensa (a causa della sua distorsione). Ma se tali errori non sono presenti o sono trascurabili, l'effetto sarà opposto: la componente integrale stessa introdurrà un errore di spostamento. Per questo motivo non viene utilizzato, ad esempio, in compiti di posizionamento ultraprecisi. Chiave svantaggio La legge di controllo integrale è l'effetto di saturazione dell'integratore (Integrator windup).


Componente differenziale Dè proporzionale al tasso di variazione della deviazione della variabile controllata ed è progettato per contrastare le deviazioni dal valore target, che sono previsti in futuro. È interessante notare che il componente differenziale elimina le oscillazioni smorzate. Il controllo differenziale è particolarmente efficace per i processi che presentano grandi ritardi. Svantaggio La legge di controllo differenziale è la sua instabilità agli effetti del rumore (Rumore di differenziazione).


Pertanto, a seconda della situazione, possono essere utilizzati regolatori P, PD, PI e PID, ma la legge di controllo principale è principalmente proporzionale (sebbene in alcuni compiti specifici possano essere utilizzati esclusivamente solo gli elementi di differenziatori e integratori).


Sembrerebbe che la questione dell'implementazione dei controller PID sia stata a lungo banale e qui su Habré ci sono un paio di buoni articoli su questo argomento, incluso su Unity3D, c'è anche un buon articolo PID Without a PhD (traduzione) e una serie di articoli sulla rivista "Modern Automation Technologies" in due parti: prima e seconda. C'è anche un articolo su Wikipedia al tuo servizio (leggi quello più completo nella versione inglese). E sui forum della community Unity3D no, no, e il controller PID apparirà proprio come su gamedev.stackexchange


La questione sull’implementazione dei controller PID è un po’ più profonda di quanto sembri. Tanto che molte scoperte meravigliose attendono i giovani fai-da-te che decidono di attuare un simile schema normativo, e l'argomento è rilevante. Quindi spero che quest’opera possa essere utile a qualcuno, quindi cominciamo.

Tentativo numero uno

Ad esempio, proveremo a implementare uno schema di controllo utilizzando l'esempio della rotazione del controllo in un semplice gioco arcade spaziale 2D, passo dopo passo, partendo dall'inizio (hai dimenticato che questo è un tutorial?).


Perché non il 3D? Perché l'implementazione non cambierà, tranne che dovrai attivare il controller PID per controllare beccheggio, imbardata e rollio. Anche se il tema della corretta applicazione del controllo PID insieme ai quaternioni è davvero interessante, magari ne parlerò in futuro, ma anche la NASA preferisce gli angoli di Eulero invece dei quaternioni, quindi ci accontenteremo di un semplice modello su piano bidimensionale aereo.


Per cominciare, creiamo l'oggetto di gioco dell'astronave stesso, che consisterà nell'oggetto nave vera e propria al livello più alto della gerarchia, e alleghiamo ad esso un oggetto Engine figlio (puramente per motivi di effetti speciali). Questo è quello che mi sembra:



E sull'oggetto stesso dell'astronave inseriamo ispettore tutti i tipi di componenti. Guardando al futuro, fornirò uno screenshot di come apparirà alla fine:



Ma questo sarà per dopo, e per ora non ci sono script, solo il set standard per gentiluomini: Sprite Render, RigidBody2D, Polygon Collider, Audio Source (perché?).


In realtà, la fisica è la cosa più importante per noi ora e il controllo verrà effettuato esclusivamente attraverso di essa, altrimenti l'uso di un controller PID perderebbe il suo significato. Lasciamo anche la massa della nostra navicella spaziale a 1 kg e tutti i coefficienti di attrito e gravità siano pari a zero - nello spazio.


Perché Oltre all'astronave stessa, ci sono un sacco di altri oggetti spaziali meno intelligenti, quindi per prima cosa descriveremo la classe genitore BaseBody, che conterrà collegamenti ai nostri componenti, metodi di inizializzazione e distruzione, nonché una serie di campi e metodi aggiuntivi, ad esempio, per implementare la meccanica celeste:


BaseBody.cs

utilizzando UnityEngine; utilizzando System.Collections; utilizzando System.Collections.Generic; namespace Assets.Scripts.SpaceShooter.Bodies (classe pubblica BaseBody: MonoBehaviour (sola lettura float _deafultTimeDelay = 0.05f; elenco statico pubblico _corpi = nuova Lista (); #region Corpo Rigido public Corpo Rigido2D _rb2d; pubblico Collider2D_c2d; #endregion #region Riferimenti public Transform _myTransform; pubblico GameObject _myObject; ///

/// Un oggetto che appare quando viene distrutto /// pubblico GameObject_explodePrefab; #endregion #region Audio public AudioSource _audioSource; /// /// Suoni riprodotti quando si riceve un danno /// pubblico AudioClip_hitSounds; /// /// Suoni riprodotti quando appare un oggetto /// pubblico AudioClip_awakeSounds; /// /// Suoni riprodotti prima della morte /// pubblico AudioClip_deadSounds; #endregion #region Variabili di forza esterna /// /// Forze esterne che agiscono su un oggetto /// public Vector2 _ExternalForces = new Vector2(); /// /// Vettore velocità attuale /// pubblico Vettore2 _V = nuovo Vettore2(); /// /// Vettore di gravità attuale /// pubblico Vettore2 _G = nuovo Vettore2(); #endregion public virtual void Awake() ( Init(); ) public virtual void Start() ( ) public virtual void Init() ( _myTransform = this.transform; _myObject = gameObject; _rb2d = GetComponent (); _c2d = OttieniComponentiInChildren (); _audioSource = OttieniComponente (); RiproduciSuonoRandom(_awakeSounds); BaseBody bb = OttieniComponente (); _corpi.Add(bb); ) /// /// Distruzione di un personaggio /// public virtual void Destroy() ( _bodies.Remove(this); for (int i = 0; i< _c2d.Length; i++) { _c2d[i].enabled = false; } float _t = PlayRandomSound(_deadSounds); StartCoroutine(WaitAndDestroy(_t)); } /// /// Aspettiamo un po' prima della distruzione /// /// Tempo di attesa /// public IEnumerator WaitAndDestroy(float waitTime) ( yield return new WaitForSeconds(waitTime); if (_explodePrefab) ( Instantiate(_explodePrefab, transform.position, Quaternion.identity); ) Destroy(gameObject, _deafultTimeDelay); ) /// /// Riproduce un suono casuale /// /// Matrice di suoni /// Durata del suono riprodotto public float PlayRandomSound(AudioClip audioClip) ( float _t = 0; if (audioClip.Length > 0) ( int _i = UnityEngine.Random.Range(0, audioClip.Length - 1); AudioClip _audioClip = audioClip[_i]; _t = _audioClip.length; _audioSource.PlayOneShot(_audioClip); ) return _t; ) /// /// Subire danni /// /// Livello del danno vuoto virtuale pubblico Danno (danno float) (PlayRandomSound(_hitSounds); ) ) )


Sembra che abbiamo descritto tutto ciò che era necessario, anche più del necessario (nell'ambito di questo articolo). Ora ereditiamo da esso la classe della nave Nave, che dovrebbe essere in grado di muoversi e girare:


SpaceShip.cs

utilizzando UnityEngine; utilizzando System.Collections; utilizzando System.Collections.Generic; namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _rotation = 0f; public void FixedUpdate() ( float torque = ControlRotate( _rotazione); Forza Vector2 = ControlForce(_movimento); _rb2d.AddTorque(coppia); _rb2d.AddRelativeForce(forza); ) public float ControlRotate(Vector2 ruota) ( float risultato = 0f; return result; ) public Vector2 ControlForce(movimento Vettore2) ( Risultato Vettore2 = nuovo Vettore2(); restituisce il risultato; ) ) )


Sebbene non contenga nulla di interessante, al momento è solo una lezione stub.


Descriveremo anche la classe base (astratta) per tutti i controller di input BaseInputController:


BaseInputController.cs

utilizzando UnityEngine; utilizzando Assets.Scripts.SpaceShooter.Bodies; namespace Assets.Scripts.SpaceShooter.InputController ( public enum eSpriteRotation ( Rigth = 0, Up = -90, Left = -180, Down = -270 ) public abstract class BaseInputController: MonoBehaviour ( public GameObject _agentObject; public Ship _agentBody; // Link al componente logico della nave public eSpriteRotation _spriteOrientation = eSpriteRotation.Up; //Ciò è dovuto all'orientamento // non standard dello sprite "su" invece che "destra" public abstract void ControlRotate(float dt); public abstract void ControlForce (float dt); public virtual void Start() ( _agentObject = gameObject; _agentBody = gameObject.GetComponent (); ) public virtual void FixedUpdate() ( float dt = Time.fixedDeltaTime; ControlRotate(dt); ControlForce(dt); ) public virtual void Update() ( //TO DO ) ) )


E infine, la classe controller del giocatore PlayerFigtherInput:


PlayerInput.cs

utilizzando UnityEngine; utilizzando Assets.Scripts.SpaceShooter.Bodies; namespace Assets.Scripts.SpaceShooter.InputController ( public class PlayerFigtherInput: BaseInputController ( public override void ControlRotate(float dt) ( // Determina la posizione del mouse rispetto al giocatore Vector3 worldPos = Input.mousePosition; worldPos = Camera.main.ScreenToWorldPoint (worldPos); // Salva le coordinate del puntatore del mouse float dx = -this.transform.position.x + worldPos.x; float dy = -this.transform.position.y + worldPos.y; // Passa la direzione Vector2 target = new Vector2(dx, dy ); _agentBody._target = target; //Calcola la rotazione in base alla pressione dei tasti float targetAngle = Mathf.Atan2(dy, dx) * Mathf.Rad2Deg; _agentBody._targetAngle = targetAngle + (float)_spriteOrientation ; ) public override void ControlForce( float dt) ( //Passa movimento _agentBody._movement = Input.GetAxis("Vertical") * Vector2.up + Input.GetAxis("Horizontal") * Vector2.right; ) ) )


Sembra che abbiamo finito, ora possiamo finalmente passare allo scopo per cui tutto questo è iniziato, ovvero. Regolatori PID (spero che non l'abbiate dimenticato?). La sua implementazione sembra semplicissima:


utilizzando il sistema; utilizzando System.Collections.Generic; utilizzando System.Linq; utilizzando System.Text; namespace Assets.Scripts.Regulator ( // Questo attributo è necessario affinché i campi del regolatore // siano visualizzati nell'ispettore e nella classe pubblica serializzata SimplePID ( public float Kp, Ki, Kd; private float lastError; private float P, I, D ; public SimplePID() ( Kp = 1f; Ki = 0; Kd = 0.2f; ) public SimplePID(float pFactor, float iFactor, float dFactor) ( this.Kp = pFactor; this.Ki = iFactor; this.Kd = dFactor ; ) public float Update(float errore, float dt) ( P = errore; I += errore * dt; D = (errore - lastError) / dt; lastError = errore; float CO = P * Kp + I * Ki + D * Kd ; ritorno CO; ) ) )

Prenderemo dal nulla i valori predefiniti dei coefficienti: questo sarà un banale coefficiente singolo della legge di controllo proporzionale Kp = 1, un piccolo valore del coefficiente della legge di controllo differenziale Kd = 0,2, che dovrebbe eliminare le fluttuazioni attese e un valore zero per Ki, che è stato scelto perché nel nostro software il modello non presenta errori statici (ma puoi sempre introdurli, e poi combattere eroicamente con l'aiuto dell'integratore).


Ora torniamo alla nostra classe SpaceShip e proviamo a utilizzare la nostra creazione come controller di rotazione dell'astronave nel metodo ControlRotate:


public float ControlRotate(Vector2 ruota) ( float MV = 0f; float dt = Time.fixedDeltaTime; //Calcola l'errore float AngleError = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); //Ottieni l'accelerazione correttiva MV = _angleController .Aggiornamento (angleError, dt); return MV; )

Il controller PID eseguirà il posizionamento angolare preciso del veicolo spaziale utilizzando solo la coppia. Tutto è giusto, fisica e armi semoventi, quasi come nella vita reale.


E senza questi tuoi Quaternion.Lerp

if (!_rb2d.freezeRotation) rb2d.freezeRotation = vero; float deltaAngle = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); float T = dt * Mathf.Abs(_rotationSpeed/deltaAngle); // Trasforma l'angolo in un vettore Quaternion rot = Quaternion.Lerp(_myTransform.rotation, Quaternion.Euler(new Vector3(0, 0, targetAngle)), T); // Cambia la rotazione dell'oggetto _myTransform.rotation = rot;


Il codice sorgente risultante di Ship.cs è sotto spoiler

utilizzando UnityEngine; utilizzando Assets.Scripts.Regulator; namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public GameObject _flame; public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _targetAngle = 0f; public float _angle = 0f; public SimplePID _angleController = new SimplePID(); public void FixedUpdate() ( float torque = ControlRotate(_targetAngle); Vector2 force = ControlForce(_movement); _rb2d.AddTorque(torque); _rb2d.AddRelativeForce(force); ) public float ControlRotate(float ruotare) ( float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; //Calcola l'errore float AngleError = Mathf.DeltaAngle(_angle,rota); //Ottieni l'accelerazione correttiva MV = _angleController. Update( AngleError, dt); return MV; ) public Vector2 ControlForce(Vector2 movement) ( Vector2 MV = new Vector2(); //Un pezzo di codice per l'effetto speciale di un motore in funzione per il bene di if (movimento!= Vector2.zero) ( if (_flame != null) ( _flame.SetActive(true); ) ) else ( if (_flame != null) ( _flame.SetActive(false); ) ) MV = movimento; ritorno MV; ) ) )


Tutto? Andiamo a casa?



WTF! Cosa sta succedendo? Perché la nave gira in modo strano? E perché rimbalza così bruscamente sugli altri oggetti? Questo stupido controller PID non funziona?


Niente panico! Proviamo a capire cosa sta succedendo.


Nel momento in cui viene ricevuto un nuovo valore SP, si verifica un brusco salto (graduale) nella mancata corrispondenza dell'errore, che, come ricordiamo, viene calcolato in questo modo: di conseguenza, c'è un brusco salto nell'errore derivativo, che calcoliamo in questa riga di codice:


D = (errore - ultimoErrore) / dt;

Ovviamente puoi provare altri schemi di differenziazione, ad esempio tre punti, cinque punti o... ma comunque non aiuterà. Ebbene, a loro non piacciono i derivati ​​​​dei salti bruschi: in questi punti la funzione non è differenziabile. Tuttavia, vale la pena sperimentare diversi schemi di differenziazione e integrazione, ma non in questo articolo.


Penso che sia giunto il momento di costruire grafici del processo di transizione: azione graduale da S(t) = 0 a SP(t) = 90 gradi per un corpo che pesa 1 kg, un braccio di forza lungo 1 metro e un passo della griglia di differenziazione di 0,02 s - proprio come nel nostro esempio in Unity3D (in effetti, non del tutto; durante la costruzione di questi grafici, non si è tenuto conto del fatto che il momento di inerzia dipende dalla geometria del corpo solido, quindi il processo transitorio sarà leggermente diversi, ma comunque abbastanza simili per essere dimostrati). Tutti i valori sul grafico sono espressi in valori assoluti:


Hmm, cosa sta succedendo qui? Dov'è andata a finire la risposta del controller PID?


Congratulazioni, abbiamo appena riscontrato un fenomeno come il "calcio". Ovviamente, nel momento in cui il processo è ancora PV = 0, e il setpoint è già SP = 90, allora con la differenziazione numerica otteniamo un valore della derivata dell'ordine di 4500, che andrà moltiplicato per Kd=0,2 e sommare con un therom proporzionale, in modo che in uscita otteniamo un valore di accelerazione angolare di 990, e questo è già un completo oltraggio al modello fisico Unity3D (le velocità angolari raggiungeranno 18000 gradi/s... penso che questo sia il valore limite della velocità angolare per RigidBody2D).


  • Forse vale la pena scegliere manualmente i coefficienti in modo che il salto non sia così forte?
  • NO! La cosa migliore che possiamo ottenere in questo modo è una piccola ampiezza del salto derivativo, ma il salto stesso rimarrà com'era, e in questo caso la componente differenziale può diventare completamente inefficace.

Tuttavia, puoi sperimentare.

Tentativo numero due. Saturazione

E' logico unità di azionamento(nel nostro caso, i motori di manovra virtuale della SpaceShip), non possono gestire i grandi valori che il nostro pazzo regolatore può produrre. Quindi la prima cosa che faremo è saturare l'uscita del regolatore:


public float ControlRotate(Vector2 rotazione, float spinta) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; //Calcola l'errore float AngleError = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); / / Otteniamo l'accelerazione correttiva CO = _angleController.Update(angleError, dt); //Saturazione MV = CO; if (MV > spinta) MV = spinta; if (MV< -thrust) MV = -thrust; return MV; }

E ancora una volta la classe della nave riscritta assomiglia a questa:

namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public GameObject _flame; public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _targetAngle = 0f; public float _angle = 0f; public float _thrust = 1f; public SimplePID _angleController = new SimplePID(0.1f,0f,0.05f); public void FixedUpdate() ( _torque = ControlRotate(_targetAngle, _thrust); _force = ControlForce(_movement); _rb2d.AddTorque(_torque); _rb2d.AddRelativeForce(_force); ) public float ControlRotate(float targetAngle, float Thrust) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; //Calcola l'errore float AngleError = Mathf.DeltaAngle(_myTransform. eulerAngles .z, targetAngle); //Ottieni l'accelerazione correttiva CO = _angleController.Update(angleError, dt); //Satura MV = CO; if (MV > spinta) MV = spinta; if (MV< -thrust) MV = -thrust; return MV; } public Vector2 ControlForce(Vector2 movement) { Vector2 MV = new Vector2(); if (movement != Vector2.zero) { if (_flame != null) { _flame.SetActive(true); } } else { if (_flame != null) { _flame.SetActive(false); } } MV = movement * _thrust; return MV; } public void Update() { } } }


Lo schema finale dei nostri cannoni semoventi sarà quindi simile a questo


Allo stesso tempo, diventa chiaro che l'uscita del controller Culla) leggermente diverso dalla variabile controllata del processo MT(t).


In realtà, da questo posto puoi già aggiungere una nuova entità di gioco - unità di azionamento, attraverso il quale verrà controllato il processo, la cui logica può essere più complessa del semplice Mathf.Clamp(), ad esempio, è possibile introdurre la discretizzazione dei valori (in modo da non sovraccaricare la fisica del gioco con valori provenienti in sesti dopo il punto decimale), una zona morta (di nuovo, non ha senso sovraccaricare la fisica con reazioni ultrapiccole), introdurre un ritardo nel controllo e nella non linearità (ad esempio, un sigmoide) dell'azionamento, e quindi vedere cosa ne viene fuori.


Avviato il gioco, scopriremo che l'astronave è finalmente diventata controllabile:



Se costruisci grafici, puoi vedere che la risposta del controller è diventata così:


Qui vengono già utilizzati valori normalizzati, gli angoli vengono divisi per il valore SP e l'uscita del controller viene normalizzata rispetto al valore massimo al quale si verifica già la saturazione.

Di seguito è riportata una tabella ben nota dell'effetto dell'aumento dei parametri del controller PID ( Come posso ridurre il carattere, altrimenti la tabella di sillabazione della meringa non si adatta?):



E l'algoritmo generale per la regolazione manuale del controller PID è il seguente:


  1. Selezioniamo i coefficienti proporzionali con i collegamenti differenziale e integrale disattivati ​​fino all'inizio delle auto-oscillazioni.
  2. Aumentando gradualmente la componente differenziale ci liberiamo delle auto-oscillazioni
  3. Se è presente un errore di controllo residuo (spostamento), lo eliminiamo utilizzando la componente integrale.

Non esistono valori generali per i parametri del controller PID: valori specifici dipendono esclusivamente dai parametri del processo (le sue caratteristiche di trasferimento): un controller PID che funziona perfettamente con un oggetto di controllo non funzionerà con un altro. Inoltre, anche i coefficienti delle componenti proporzionale, integrale e differenziale sono interdipendenti.


Tentativo numero tre. Ancora una volta derivati

Avendo attaccato una stampella sotto forma di limitazione dei valori di uscita del controller, non abbiamo ancora risolto il problema più importante del nostro controller: il componente differenziale non funziona bene quando l'errore all'ingresso del controller cambia gradualmente. In effetti, ci sono molte altre stampelle, ad esempio, al momento di un brusco cambiamento di SP, "spegnere" il componente differenziale o installare filtri passa-basso tra PS(t) e un'operazione grazie alla quale l'errore aumenterà gradualmente, oppure puoi girarti completamente e utilizzare un vero filtro di Kalman per livellare i dati di input. In generale, ci sono molte stampelle e aggiungi osservatore Certo che mi piacerebbe, ma non questa volta.


Torniamo quindi alla derivata dell’errore di mancata corrispondenza e osserviamola attentamente:



Hai notato qualcosa? Se guardi da vicino, scoprirai che, in generale, SP(t) non cambia nel tempo (ad eccezione dei momenti di cambio di gradino quando il controller riceve un nuovo comando), cioè la sua derivata è zero:





In altre parole, invece di un errore derivativo che è differenziabile non ovunque possiamo usare la derivata di un processo, che nel mondo della meccanica classica è solitamente continuo e differenziato ovunque, e il diagramma del nostro sistema di controllo automatico assumerà già la seguente forma:




Modifichiamo il codice del controller:


utilizzando il sistema; utilizzando System.Collections.Generic; utilizzando System.Linq; utilizzando System.Text; namespace Assets.Scripts.Regulator ( public class SimplePID ( public float Kp, Ki, Kd; private float P, I, D; private float lastPV = 0f; public SimplePID() ( Kp = 1f; Ki = 0f; Kd = 0.2f ; ) public SimplePID(float pFactor, float iFactor, float dFactor) ( this.Kp = pFactor; this.Ki = iFactor; this.Kd = dFactor; ) public float Update(float errore, float PV, float dt) ( P = errore; I += errore * dt; D = -(PV - lastPV) / dt; lastPV = PV; float CO = Kp * P + Ki * I + Kd * D; return CO; ) ) )

E cambiamo un po' il metodo ControlRotate:


public float ControlRotate(Vector2 rotazione, float spinta) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; //Calcola l'errore float AngleError = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); / / Otteniamo l'accelerazione correttiva CO = _angleController.Update(angleError, _myTransform.eulerAngles.z, dt); //Saturate MV = CO; if (CO >< -thrust) MV = -thrust; return MV; }

E-e-e-e... se inizi il gioco, scoprirai che in realtà non è cambiato nulla dall'ultimo tentativo, che è ciò che doveva essere dimostrato. Tuttavia, se rimuovi la saturazione, il grafico della risposta del regolatore sarà simile a questo:


Salto Culla)è ancora presente, ma non è più così grande come all’inizio e, soprattutto, è diventato prevedibile, perché è fornito esclusivamente dalla componente proporzionale, ed è limitato dal massimo errore di disadattamento possibile e dal guadagno proporzionale del controller PID (e questo già suggerisce che Kp ha senso scegliere meno dell'unità, ad esempio 1/90f), ma non dipende dal passo della griglia di differenziazione (cioè dt). In generale, consiglio vivamente di utilizzare la derivata del processo anziché l'errore.


Penso che ora non sorprenderà nessuno, ma allo stesso modo puoi sostituirlo con , ma non ci soffermeremo su questo, puoi sperimentare tu stesso e raccontare nei commenti cosa ne è venuto fuori (il più interessante)

Tentativo numero quattro. Implementazioni alternative del controller PID

Oltre alla rappresentazione ideale del regolatore PID sopra descritta, nella pratica viene spesso utilizzata la forma standard, senza coefficienti Ki E Kd, al posto delle quali vengono utilizzate costanti temporanee.


Questo approccio è dovuto al fatto che numerose tecniche di regolazione del controller PID si basano sulle caratteristiche di frequenza del controller PID e del processo. In realtà, l'intero TAU ruota attorno alle caratteristiche di frequenza dei processi, quindi per coloro che vogliono andare più a fondo e improvvisamente incontrano una nomenclatura alternativa, darò un esempio del cosiddetto. modulo standard Regolatore PID:




dove è la costante di differenziazione che influenza la previsione dello stato del sistema da parte del regolatore,
- costante di integrazione, che influenza l'intervallo di media dell'errore tramite il collegamento integrale.


I principi di base per l'ottimizzazione di un controller PID in forma standard sono simili al controller PID idealizzato:

  • aumentando il coefficiente proporzionale si aumentano le prestazioni e si riduce il margine di stabilità;
  • al diminuire della componente integrale l'errore di controllo diminuisce più velocemente nel tempo;
  • diminuendo la costante di integrazione si riduce il margine di stabilità;
  • un aumento della componente differenziale aumenta il margine di stabilità e di prestazione

Il codice sorgente del modulo standard può essere trovato sotto lo spoiler

namespace Assets.Scripts.Regulator ( public class StandartPID ( public float Kp, Ti, Td; errore public float, CO; public float P, I, D; private float lastPV = 0f; public StandartPID() ( Kp = 0.1f; Ti = 10000f; Td = 0.5f; bias = 0f; ) public StandardPID(float Kp, float Ti, float Td) ( this.Kp = Kp; this.Ti = Ti; this.Td = Td; ) public float Update(float errore, float PV, float dt) ( this.error = errore; P = errore; I += (1 / Ti) * errore * dt; D = -Td * (PV - lastPV) / dt; CO = Kp * ( P + I + D); ultimoPV = PV; ritorno CO; ) ) )

I valori predefiniti sono Kp = 0,01, Ti = 10000, Td = 0,5 - con questi valori la nave gira abbastanza velocemente e ha un certo margine di stabilità.


Oltre a questa forma di controller PID, il cosiddetto forma ricorrente:



Non ci soffermeremo su questo, perché... è rilevante principalmente per i programmatori hardware che lavorano con FPGA e microcontrollori, dove tale implementazione è molto più conveniente ed efficiente. Nel nostro caso - diamo qualcosa alle pile su Unity3D - questa è solo un'altra implementazione di un controller PID, che non è migliore di altri e nemmeno meno comprensibile, quindi ancora una volta rallegriamoci tutti di quanto sia bello programmare nell'accogliente C# , e non nel raccapricciante e terribile VHDL, per esempio.

Invece di una conclusione. Dove altro aggiungeresti un controller PID?

Ora proviamo a complicare un po' il controllo della nave utilizzando il controllo a doppio loop: un controller PID, a noi già familiare _angleController, è ancora responsabile del posizionamento angolare, ma il secondo - nuovo, _angularVelocityController - controlla la velocità di rotazione:


public float ControlRotate(float targetAngle, float spinta) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; //Controller dell'angolo di rotazione float AngleError = Mathf.DeltaAngle(_angle, targetAngle); float torqueCorrectionForAngle = _angleController.Update(angleError, _angle, dt); //Controller di stabilizzazione della velocità float angularVelocityError = -_rb2d.angularVelocity; float torqueCorrectionForAngularVelocity = _angularVelocityController.Update(angularVelocityError, -angularVelocityError, dt); //Totale uscita del controller CO = torqueCorrectionForAngle + torqueCorrectionForAngularVelocity; //Esempio in passi di 100 CO = Mathf.Round(100f * CO) / 100f; //Saturazione MV = CO; if (CO > spinta) MV = spinta; if (CO< -thrust) MV = -thrust; return MV; }

Lo scopo del secondo regolatore è smorzare le velocità angolari in eccesso modificando la coppia: questo è simile alla presenza di attrito angolare, che abbiamo disattivato durante la creazione dell'oggetto del gioco. Un tale schema di controllo consentirà [possibilmente] di ottenere un comportamento più stabile della nave e persino di cavarsela solo con coefficienti di controllo proporzionali: il secondo regolatore smorzerà tutte le fluttuazioni, eseguendo una funzione simile alla componente differenziale del primo regolatore .


Inoltre, aggiungeremo una nuova classe di input del giocatore: PlayerInputCorvette, in cui i turni verranno eseguiti premendo i tasti destra-sinistra e lasceremo la designazione del bersaglio con il mouse per qualcosa di più utile, ad esempio, per controllare una torretta . Allo stesso tempo, ora abbiamo un parametro come _turnRate, che è responsabile della velocità/reattività della virata (non è chiaro dove sia meglio posizionarlo nell'InputCOntroller o nella Nave ferma).


public class PlayerCorvetteInput: BaseInputController ( public float _turnSpeed ​​​​= 90f; public override void ControlRotate() ( // Trova il puntatore del mouse Vector3 worldPos = Input.mousePosition; worldPos = Camera.main.ScreenToWorldPoint(worldPos); // Memorizza il relativo coordinate del puntatore del mouse float dx = -this.transform.position.x + worldPos.x; float dy = -this.transform.position.y + worldPos.y; //Passa la direzione del puntatore del mouse Vector2 target = new Vector2( dx, dy); _agentBody. _target = target; //Calcola la rotazione in base alla pressione dei tasti _agentBody._rotation -= Input.GetAxis("Horizontal") * _turnSpeed ​​​​* Time.deltaTime; ) public override void ControlForce() ( //Passa movimento _agentBody._movement = Input .GetAxis("Vertical") * Vector2.up; ) )

Inoltre, per chiarezza, metteremo uno script in ginocchio per visualizzare le informazioni di debug

namespace Assets.Scripts.SpaceShooter.UI ( public class Debugger: MonoBehaviour ( Ship _ship; BaseInputController _controller; List _pids = nuova lista (); Elenco _nomi = nuova lista (); Vector2 _orientamento = new Vector2(); // Usalo per l'inizializzazione void Start() ( _ship = GetComponent (); _controller = OttieniComponente (); _pids.Add(_ship._angleController); _names.Add("Controllore angolare"); _pids.Add(_ship._angularVelocityController); _names.Add("Regolatore di velocità angolare"); ) // L'aggiornamento viene chiamato una volta per frame void Update() ( DrawDebug(); ) Vector3 GetDiretion(eSpriteRotation spriteRotation) ( switch (_controller._spriteOrientation) ( case eSpriteRotation.Rigth: return transform.right; case eSpriteRotation.Up: return transform .up; case eSpriteRotation.Left: return -transform.right; case eSpriteRotation.Down: return -transform.up; ) return Vector3.zero; ) void DrawDebug() ( //Direzione di rotazione Vector3 vettoreToTarget = trasforma.posizione + 5f * new Vector3(-Mathf.Sin(_ship._targetAngle * Mathf.Deg2Rad), Mathf.Cos(_ship._targetAngle * Mathf.Deg2Rad), 0f); // Direzione corrente Intestazione Vector3 = trasforma.posizione + 4f * GetDiretion(_controller. _spriteOrientation); //Accelerazione angolare Coppia Vector3 = direzione - trasforma.destra * _ship._Torque; Debug.DrawLine(transform.position, vectorToTarget, Color.white); Debug.DrawLine(transform.position, direzione, Color.green); Debug.DrawLine(intestazione, coppia, Color.red); ) void OnGUI() ( float x0 = 10; galleggiante y0 = 100; float dx = 200; float dy = 40; float SliderKpMax = 1; float SliderKpMin = 0; float SliderKiMax = .5f; float SliderKiMin = -.5f; float SliderKdMax = .5f; float SliderKdMin = 0; int io = 0; foreach (SimplePID pid in _pids) ( y0 += 2 * dy; GUI.Box(new Rect(25 + x0, 5 + y0, dx, dy), ""); pid.Kp = GUI.HorizontalSlider(new Rect( 25 + x0, 5 + y0, 200, 10), pid.Kp, SliderKpMin, SliderKpMax); pid.Ki = GUI.HorizontalSlider(new Rect(25 + x0, 20 + y0, 200, 10), pid.Ki, SliderKiMin, SliderKiMax); pid.Kd = GUI.HorizontalSlider(new Rect(25 + x0, 35 + y0, 200, 10), pid.Kd, SliderKdMin, SliderKdMax); GUIStyle style1 = new GUIStyle(); style1.alignment = TextAnchor.MiddleRight; style1.fontStyle = FontStyle.Bold; style1.normal.textColor = Color.giallo; style1.fontSize = 9; GUI.Label(new Rect(0 + x0, 5 + y0, 20, 10), "Kp ", stile1); GUI.Label(new Rect(0 + x0, 20 + y0, 20, 10), "Ki", ​​stile1); GUI.Label(new Rect(0 + x0, 35 + y0, 20 , 10 ), "Kd", style1); GUIStyle style2 = nuovo GUIStyle(); style2.alignment = TextAnchor.MiddleLeft; style2.fontStyle = FontStyle.Bold; style2.normal.textColor = Color.giallo; style2.fontSize = 9 ; GUI .TextField(new Rect(235 + x0, 5 + y0, 60, 10), pid.Kp.ToString(), style2); GUI.TextField(new Rect(235 + x0, 20 + y0, 60, 10), pid. Ki.ToString(), stile2); GUI.TextField(new Rect(235 + x0, 35 + y0, 60, 10), pid.Kd.ToString(), style2); GUI.Label(new Rect(0 + x0, -8 + y0, 200, 10), _names, style2); ) ) ) )


Anche la classe Nave ha subito mutazioni irreversibili e ora dovrebbe assomigliare a questa:

namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public GameObject _flame; public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _targetAngle = 0f; public float _angle = 0f; public float _thrust = 1f; public SimplePID _angleController = new SimplePID(0.1f,0f,0.05f); public SimplePID _angularVelocityController = new SimplePID(0f,0f,0f); private float _torque = 0f; public float _Torque ( get ( return _torque; ) ) private Vector2 _force = new Vector2(); public Vector2 _Force ( get ( return _force; ) ) public void FixedUpdate() ( _torque = ControlRotate(_targetAngle, _thrust); _force = ControlForce(_movement, _thrust); _rb2d.AddTorque( _torque); _rb2d.AddRelativeForce(_force); ) public float ControlRotate(float targetAngle, float spinta) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; //Controller angolo di rotazione float angoloError = Mathf.DeltaAngle(_angle, targetAngle); float torqueCorrectionForAngle = _angleController.Update(angleError, _angle, dt); //Controller di stabilizzazione della velocità float angularVelocityError = -_rb2d.angularVelocity; float torqueCorrectionForAngularVelocity = _angularVelocityController.Update(angularVelocityError, -angularVelocityError, dt); // Uscita totale del controller CO = torqueCorrectionForAngle + torqueCorrectionForAngularVelocity; //Discretizza in passi di 100 CO = Mathf.Round(100f * CO) / 100f; //VM saturo = CO; se (CO > spinta) MV = spinta; se(CO< -thrust) MV = -thrust; return MV; } public Vector2 ControlForce(Vector2 movement, float thrust) { Vector2 MV = new Vector2(); if (movement != Vector2.zero) { if (_flame != null) { _flame.SetActive(true); } } else { if (_flame != null) { _flame.SetActive(false); } } MV = movement * thrust; return MV; } public void Update() { } } }

Alcuni altri collegamenti ad altri esempi

La banda di proporzionalità X p, come la deviazione E, è espressa in unità del parametro controllato. Quanto più ampia è la banda proporzionale X p, tanto più piccolo è il segnale di uscita Y a parità di deviazione E.

Al di fuori della banda proporzionale, l'uscita Y è 0 o 100%.

Quando funziona la legge P, il controller produce impulsi in cui è presente solo una componente proporzionale del valore del segnale di uscita.


Quando il dispositivo funziona in modalità controller PD, l'entità del segnale di uscita Yi dipende non solo dall'entità della deviazione Ei, ma anche dalla velocità della sua variazione:

La variazione del segnale di uscita del controller con una variazione graduale della deviazione è mostrata nella figura. Nel primo periodo dopo un cambio di gradino in E i, il regolatore emette un impulso di controllo, in cui, oltre alla componente proporzionale causata dal disadattamento E i, viene aggiunto un differenziale (parte ombreggiata) ΔYd, che dipende dal valore del coefficiente ΔE i e τ l. Negli impulsi successivi c'è solo una componente proporzionale, poiché non vi è alcuna variazione in E i.


La figura mostra che nel primo momento, quando non c'è deviazione (E i =0), non c'è segnale di uscita (Y i =0). Con la comparsa della deviazione E i, compaiono gli impulsi, la cui durata aumenta gradualmente. Gli impulsi contengono una componente proporzionale, che dipende dal valore di E (parte non ombreggiata degli impulsi) e una componente integrale (parte ombreggiata). Un aumento della durata dell'impulso si verifica a causa di un aumento della componente integrale, che dipende dal disadattamento E i e dal coefficiente τ i.

Un controller differenziale proporzionale-integrale è un dispositivo installato in sistemi automatizzati per mantenere un determinato parametro suscettibile di modifica.

A prima vista, tutto è confuso, ma il controllo PID può essere spiegato per i manichini, ad es. persone che non hanno molta familiarità con i sistemi e i dispositivi elettronici.

Cos'è un controller PID?

Il controller PID è un dispositivo integrato nel circuito di controllo con feedback obbligatorio. È progettato per mantenere livelli stabiliti di valori specifici, ad esempio la temperatura dell'aria.

Il dispositivo fornisce un segnale di controllo o di uscita al dispositivo di controllo, in base ai dati ricevuti da sensori o sensori. I controllori hanno un'elevata precisione dei processi transitori e la qualità dell'esecuzione del compito assegnato.

Tre coefficienti del controller PID e principio di funzionamento

Il compito del controller PID è fornire un segnale di uscita sulla potenza richiesta per mantenere il parametro controllato a un determinato livello. Per calcolare l'indicatore viene utilizzata una formula matematica complessa che contiene 3 coefficienti: proporzionale, integrale, differenziale.

Prendiamo come oggetto di regolazione un contenitore d'acqua in cui è necessario mantenere la temperatura a un determinato livello regolando il grado di apertura della valvola con vapore.

La componente proporzionale appare nel momento della mancata corrispondenza con i dati di input. In parole semplici, suona così: viene presa la differenza tra la temperatura effettiva e quella desiderata, moltiplicata per un coefficiente regolabile e si ottiene un segnale di uscita che deve essere fornito alla valvola. Quelli. Non appena i gradi scendono inizia il processo di riscaldamento; se salgono sopra il livello desiderato si verifica uno spegnimento o addirittura un raffreddamento.

Poi c'è la componente integrale, progettata per compensare l'influenza dell'ambiente o altri fattori di disturbo sul mantenimento della nostra temperatura a un determinato livello. Poiché ci sono sempre ulteriori fattori che influenzano i dispositivi controllati, nel momento in cui arrivano i dati per calcolare la componente proporzionale, il dato sta già cambiando. E maggiore è l'influenza esterna, più forti sono le fluttuazioni dell'indicatore. Si verificano picchi di potenza fornita.

La componente integrale tenta, in base ai valori di temperatura passati, di restituire il suo valore se è cambiato. Il processo è descritto più dettagliatamente nel video qui sotto.

L'integrale viene utilizzato per eliminare gli errori calcolando l'errore statico. La cosa principale in questo processo è selezionare il coefficiente corretto, altrimenti l'errore (mancata corrispondenza) influenzerà anche la componente integrale.

Il terzo componente del PID è la differenziazione. È progettato per compensare gli effetti dei ritardi che si verificano tra l'impatto sul sistema e il feedback. Il controller proporzionale fornisce energia finché la temperatura non raggiunge il livello desiderato, ma si verificano sempre errori quando le informazioni passano al dispositivo, soprattutto a valori elevati. Ciò potrebbe causare il surriscaldamento. Il differenziale prevede deviazioni causate da ritardi o influenze ambientali e riduce in anticipo la potenza fornita.

Impostazione del controller PID

Il controller PID è configurato utilizzando 2 metodi:

  1. La sintesi prevede il calcolo dei parametri sulla base di un modello di sistema. Questa impostazione è accurata, ma richiede una profonda conoscenza della teoria del controllo automatico. È soggetto solo a ingegneri e scienziati. Poiché è necessario prendere le caratteristiche di consumo e fare una serie di calcoli.
  2. Il metodo manuale si basa su tentativi ed errori. Per fare ciò, vengono presi come base i dati di un sistema già pronto e vengono apportate alcune modifiche a uno o più coefficienti del regolatore. Dopo l'accensione e l'osservazione del risultato finale, i parametri vengono modificati nella direzione desiderata. E così via fino al raggiungimento del livello di prestazione desiderato.

Il metodo teorico di analisi e regolazione viene utilizzato molto raramente nella pratica, a causa dell'ignoranza delle caratteristiche dell'oggetto di controllo e di una serie di possibili influenze disturbanti. Più comuni sono i metodi sperimentali basati sull'osservazione del sistema.

I moderni processi automatizzati sono implementati come moduli specializzati controllati da programmi per la regolazione dei coefficienti del controller.

Scopo del controller PID

Il controller PID è progettato per mantenere un determinato valore al livello richiesto: temperatura, pressione, livello nel serbatoio, portata nella tubazione, concentrazione di qualcosa, ecc., modificando l'azione di controllo sugli attuatori, come le valvole di controllo automatiche , utilizzando quantità proporzionali, integrative e differenzianti per la sua impostazione.

Lo scopo di utilizzo è ottenere un segnale di controllo accurato in grado di controllare grandi industrie e persino i reattori delle centrali elettriche.

Esempio di circuito di controllo della temperatura

I controller PID vengono spesso utilizzati per regolare la temperatura; diamo un'occhiata a questo processo automatico utilizzando un semplice esempio di riscaldamento dell'acqua in un contenitore.

Il contenitore è pieno di liquido, che deve essere riscaldato alla temperatura desiderata e mantenuto a un determinato livello. Un sensore di misurazione della temperatura è installato all'interno del serbatoio o è collegato direttamente al controller PID.

Per riscaldare il liquido forniremo vapore, come mostrato nella figura seguente, con una valvola di controllo automatico. La valvola stessa riceve un segnale dal regolatore. L'operatore inserisce nel controller PID il valore di setpoint della temperatura che deve essere mantenuto nel serbatoio.

Se le impostazioni del coefficiente del regolatore non sono corrette, la temperatura dell'acqua oscillerà, con la valvola completamente aperta o completamente chiusa. In questo caso è necessario calcolare i coefficienti del regolatore PID e inserirli nuovamente. Se tutto è stato eseguito correttamente, dopo un breve periodo di tempo il sistema livellerà il processo e la temperatura nel contenitore verrà mantenuta al valore impostato, mentre il grado di apertura della valvola di controllo sarà nella posizione centrale.

Il controller PID è un dispositivo già pronto che consentirà all'utente di implementare un algoritmo software per il controllo di determinate apparecchiature di un sistema automatizzato. Costruire e configurare sistemi di controllo diventa molto più semplice se si utilizzano dispositivi già pronti come il controller PID universale TRM148 per 8 canali dell'azienda Aries.

Diciamo che devi automatizzare il mantenimento delle corrette condizioni climatiche in una serra: prendi in considerazione la temperatura del terreno vicino alle radici delle piante, la pressione dell'aria, l'umidità dell'aria e del suolo e mantieni i parametri impostati controllando i ventilatori. Non c'è niente di più semplice, basta configurare il controller PID.

Ricordiamo innanzitutto cos'è un controller PID? Un controller PID è un dispositivo speciale che esegue una regolazione continua e precisa dei parametri di uscita in tre modi: proporzionalmente, integralmente e differenziale, e i parametri iniziali sono parametri di ingresso ricevuti dai sensori (pressione, umidità, temperatura, illuminazione, ecc.).

Il parametro di ingresso viene fornito all'ingresso del controller PID da un sensore, ad esempio da un sensore di umidità. Il regolatore riceve un valore di tensione o corrente, lo misura, quindi esegue i calcoli secondo il suo algoritmo e infine invia un segnale all'uscita corrispondente, a seguito della quale l'automazione riceve un'azione di controllo. L'umidità del terreno è diminuita: l'irrigazione è stata attivata per alcuni secondi.

L'obiettivo è raggiungere un valore di umidità specificato dall'utente. O per esempio: l'illuminazione è diminuita - accendi i fitolamp sopra le piante, ecc.

Controllo PID

Infatti, anche se all'apparenza tutto è semplice, all'interno del regolatore la matematica è più complicata; non avviene tutto in un unico passaggio. Dopo aver attivato l'irrigazione, il controller PID effettua nuovamente una misurazione, misurando quanto è cambiato il valore di ingresso: ecco come viene rilevato l'errore di controllo. L'impatto successivo sull'organo esecutivo verrà adeguato tenendo conto dell'errore di regolamentazione misurato, e così via in ogni fase di controllo fino al raggiungimento dell'obiettivo, un parametro specificato dall'utente.

Tre componenti sono coinvolte nella regolazione: proporzionale, integrale e differenziale. Ciascuna componente ha il proprio grado di importanza in ciascun sistema specifico, e quanto maggiore è il contributo dell'una o dell'altra componente, tanto più significativamente dovrebbe essere modificato nel processo di regolamentazione.

La componente proporzionale è la più semplice; maggiore è la variazione, maggiore è il coefficiente (proporzionalità nella formula), e per ridurre l'impatto è sufficiente ridurre semplicemente il coefficiente (moltiplicatore).

Supponiamo che l'umidità del terreno nella serra sia molto inferiore a quella stabilita, quindi il tempo di irrigazione dovrebbe essere più lungo della stessa quantità in quanto l'umidità attuale è inferiore a quella stabilita. Questo è un esempio approssimativo, ma questo è il principio generale.

Componente integrale: è necessario aumentare la precisione del controllo in base agli eventi di controllo precedenti: gli errori precedenti vengono integrati, vengono apportate correzioni per ottenere alla fine una deviazione zero nella regolazione futura.

Infine, la componente differenziale. Qui viene preso in considerazione il tasso di variazione della variabile controllata. Sia che il valore specificato cambi in modo graduale o brusco, di conseguenza, l'azione normativa non dovrebbe portare a deviazioni eccessive del valore durante la regolazione.

Non resta che selezionare un dispositivo per il controllo PID. Oggi ce ne sono molti sul mercato, ce ne sono di multicanale che consentono di modificare più parametri contemporaneamente, come nell'esempio sopra con una serra.

Diamo un'occhiata alla progettazione del controller utilizzando l'esempio del controller PID universale TRM148 dell'azienda Aries.

Gli otto sensori di ingresso forniscono segnali agli ingressi corrispondenti. I segnali vengono scalati, filtrati, corretti, i loro valori possono essere visualizzati sul display commutando con i pulsanti.

Le uscite del dispositivo sono prodotte in diverse modifiche nelle combinazioni richieste di quanto segue:

    relè 4 A 220 V;

    optoaccoppiatori a transistor tipo n–p–n 400 mA 60 V;

    optoaccoppiatori triac 50 mA 300 V;

    DAC “parametro-corrente 4...20 mA”;

    DAC “parametro-tensione 0...10 V”;

    Uscita 4...6 V 100 mA per il controllo di un relè a stato solido.

Pertanto, l'azione di controllo può essere analogica o digitale. - si tratta di impulsi di larghezza variabile e analogici - sotto forma di tensione o corrente che varia dolcemente in un intervallo unificato: da 0 a 10 V per tensione e da 4 a 20 mA per un segnale di corrente.

Questi segnali di uscita vengono utilizzati precisamente per controllare gli attuatori, ad esempio una pompa del sistema di irrigazione o un relè che accende e spegne un elemento riscaldante o un motore di controllo della valvola. Sono presenti indicatori di segnale sul pannello del regolatore.


Per interagire con un PC, il regolatore TRM148 è dotato di un'interfaccia RS-485, che permette:

    configurare il dispositivo su PC (i programmi di configurazione sono forniti gratuitamente);

    trasmettere alla rete i valori attuali dei valori misurati, la potenza di uscita del controller e tutti i parametri programmabili;

  • ricevere dati operativi dalla rete per generare segnali di controllo.

Si può sostenere che le prestazioni più elevate sono fornite da Legge P, - in base al rapporto tp / T d .

Tuttavia, se il guadagno del regolatore P Kr è piccolo (molto spesso questo viene osservato con un ritardo), ciò non fornisce un'elevata precisione di controllo, perché in questo caso il valore è grande.

Se Kp > 10, allora il regolatore P è accettabile, e se Kp< 10, то требуется введение в закон управления составляющей.

Legge di regolamentazione PI

Il più comune nella pratica è regolatore PI, che presenta i seguenti vantaggi:

  1. Fornisce una regolamentazione zero.
  2. Abbastanza facile da configurare, perché... Vengono regolati solo due parametri, vale a dire il guadagno Kp e la costante di tempo di integrazione Ti. In un tale controllore è possibile ottimizzare il valore del rapporto Kp/Ti-min, che garantisce il controllo con la minima regolazione efficace possibile.
  3. Bassa sensibilità al rumore nelle misurazioni (a differenza del controller PID).

Legge di controllo PID

Per i circuiti di controllo più critici, possiamo consigliare l'utilizzo , fornendo le massime prestazioni del sistema.

Tuttavia, tieni presente che ciò avviene solo con le impostazioni ottimali (sono configurati tre parametri).

Con l'aumento del ritardo nel sistema, gli sfasamenti negativi aumentano notevolmente, il che riduce l'effetto della componente differenziale del controller. Pertanto, la qualità del controller PID per sistemi con grandi ritardi diventa paragonabile alla qualità del controller PI.

Inoltre, la presenza di rumore nel canale di misurazione in un sistema con controller PID porta a fluttuazioni casuali significative nel segnale di controllo del controller, che aumentano la varianza dell'errore di controllo e l'usura del meccanismo.

Pertanto, il controller PID dovrebbe essere selezionato per sistemi di controllo con un livello di rumore e un ritardo di controllo relativamente bassi. Esempi di tali sistemi sono i sistemi di controllo della temperatura.



Continuando l'argomento:
Malta

Tutti sanno cosa sono i cereali. Dopotutto, l'uomo ha iniziato a coltivare queste piante più di 10mila anni fa. Ecco perché ancora oggi nomi di cereali come grano, segale, orzo, riso,...