Zurück (C) Christof Ermer, Regensburg
Gästebuch (public. sonst besser Email)



30.04.2013
Überarbeitet 2.2018
Es sind noch einige Link Fehler drin
Ursache
ATMEL wurde von Microchip aufgekauft

Hinweis:
ATMEL wurde von Microchip aufgekauft und die haben die ursprünglichen Links gelöscht und verändert
Also, selbst neu suchen

 PID-Regler mit µController
Aufgabe:
Ein Objekt ohne thermische Schwingung auf genau eine Temperatur zu bringen. Abweichng < 1%
Relatetd Parameter:
Wärmekapazität von xx,x J/K, woraus bei x W eine
Anstiegsgeschwindigkeit von 0,xx K/s der Temperatur unter vernachlässigung der Wärmeabgabe an die Umgebung resultiert.
Aufbereitung des Sensor Signals...
Sensor-SignalAufbereitung.html
KX Fausformeverfahren http://de.wikipedia.org/wiki/Faustformelverfahren_%28Automatisierungstechnik%29
siehe http://www.mikrocontroller.net/topic/222102


Links Fehlerursache
ATMEL wurde von Microchip aufgekauft und die haben die Links gelöscht und verändert

Interessantes aus dem Netz:
geht nicht mehr: http://www.atmel.com/  und dann nach" AVR221" suchen

erneuerter LINK: http://www.microchip.com/wwwappnotes/appnotes.aspx?appnote=en591227
oder direkt von meiner Platte 02.2018:
pid.zip
PID_APP_NOTE_AVR221.zip
Lehr-PDF zum Thema PID:
AVR221 Discrete PID controller.pdf
Atmel-2558-Discrete-PID-Controller-on-tinyAVR-and-megaAVR_ApplicationNote_AVR221.pdf
PID-Regler.pdf

Parametereinstellung:
https://de.wikipedia.org/wiki/Faustformelverfahren_%28Automatisierungstechnik%29



aus: http://rn-wissen.de/wiki/index.php?title=Regelungstechnik
Dimensionierung nach Einstellregeln

Die Dimensionierung nach Rezept ist eine praktische Methode ohne viel Rechnerei und Hilfsmittel, eine Methode also für den Praktiker. Die bekanntesten Einstellregeln sind von Ziegler/Nichols und von Chien/Hrones/Reswick. Darüber hinaus gibt es noch eine Vielzahl anderer Einstelltabellen, auf die hier aber verzichtet wird, das würde den Rahmen dieses Artikels sprengen.

An der Vielzahl kann man schon ersehen, dass es kein universell gültiges Rezept gibt und mit diesen Tabellen nicht unbedingt das Optimum erreicht wird, eine Nachoptimierung kann nötig sein.

Bei Ziegler/Nichols unterscheidet man noch zwischen der Schwingungsmethode und der Einstellung nach der Sprungantwort.

Einstellung nach der Schwingungsmethode:

Bei der Schwingungsmethode nach Ziegler/Nichols werden die Reglerparameter so verstellt, dass die Stabilitätsgrenze erreicht wird und der Regelkreis zu schwingen beginnt, d.h. die Regelgröße periodische Schwingungen ausführt. Aus der so gefundenen Einstellung können die Reglerparameter ermittelt werden. Dieses Verfahren ist nur auf Regelstrecken anwendbar, bei denen ein Schwingen keinen Schaden anrichtet und die überhaupt instabil gemacht werden können. Die Vorgehensweise ist folgende:

  1. Einstellung des Reglers als reinen P-Regler: Ki = 0 und Kd = 0
  2. Die Reglerverstärkung Kp wird solange vergrößert, bis sich der geschlossene Regelkreis an der Stabilitätsgrenze befindet und Dauerschwingungen ausführt.
  3. Der dabei eingestellte Wert Kp wird als Kpkrit bezeichnet.
  4. Die Periodendauer der sich einstellenden Dauerschwingung Tkrit wird gemessen.
  5. Anhand der folgenden Tabelle werden dann die Reglerparameter bestimmt.

Der Vorteil dieses Verfahrens liegt darin, dass die Untersuchung während des Betriebes und ohne Öffnen des Regelkreises durchgeführt werden kann. Der Nachteil an dem Verfahren ist, dass es nur auf Strecken angewendet werden kann, die auch zum Schwingen gebracht werden können.

Einstellung nach der Sprungantwort:
Diese Methode der Parameterbestimmung beruht auf der Aufnahme der Sprungantwort der Regelstrecke. Es eignet sich auch für Strecken, die nicht zum Schwingen gebracht werden können. Der Regelkreis muss allerdings geöffnet werden. Vorgehensweise: Es wird die Sprungantwort aufgenommen und durch Einzeichnen der Wendetangente die Verzugszeit Tu und die Ausgleichszeit Tg ermittelt.




Mit den so festgestellten Werten werden die Parameter für den einzusetzenden Regler gemäß nachstehenden Tabellen ermittelt.






------------------------------------------------------------------------

18.02.2018 Manuelle KP ermittlung:
    Das habe ich in einer China PDF gefunden und funktioniert erstaunlich gut.

1.) gewünschte Regelstrecke real aufbauen. Bsp Heizelemt und Thermosensor mit den thermischen Massen.

2.) Die PID Software: die  Zeitscheiben-Aufrufe  im Timerinterrupt, etwas schneller als die Trägheit des Systems, stellen.
     z.B. für Thermosysteme >~1 Sekunde.
    für Motoren muss da s schon sehr viel flotter ( < 1mS ) gegen0

3.) KI und KD=0;  Damit nur der Proportionalanteil arbeitet. Also die reine Fehlerdifferenz ( Soll -Ist = Fehler )
    ( Dabei wird der Ausgabewert seltsamerweise nicht größer als 255 ( Mit KI != 0 aber auch  bis  32767 )Liegt an der Software

4.) . KP Schrittweise von auf  1, >1,  5~~10--20,   erhörhen.

5.) mit einem Datenlogger, oder sonst wie!,  den Regelausgang über die Regelzeit (die ja ermittelt werden soll) beobachten/Anzeigen.
    .... beobacheten wie sich das System einschwingt.

Noch empirischer: Dafür etwas handbarer:
KP Wert so ermittlen dass das Sysstem gerade zu Oszillieren (bsp.. Heizen/Kühl Phasen ) beginnt, also die Oszillation gerade beginnt überzureagieren
Davon nehmen wir ~60.80%
Das ist schon KP !! z.b.   
Bsp:  --> KP= 3.1
    Dann diesen gefunden KP Wert eintragen.
Jetzt wird KI und KD ermittelt:
6.) Diese Zeit messen
7.) Daraus  -->  F = 1/( Perioden pro Sekunde )   = Herz
    Bps. bei  einem "trägen Thermo System" 4.5 Regelperioden pro 2,5Minuten = 0.03 Hz   
    Merke: Omega (dieses sitzende Hinternzeichen) = 2* PI * F

Ich habe es mal so gemacht , mit erstaunlich guten  Ergebnissen  ( KP haben wir schon.. )


KP=3.1
KI hat sich  bei trägen System als sehr niedrig herausgestellt.
Ich habe KI=0.02 genommen.
KD = 13.1 !!   ( Das hatte ich nicht erwartet, bei einem trägen Heizsystem )  
Ich bin immer noch ewas unsicher, weil KD bei trägen System keinen Sinn macht.
Das Ergebniss war jedoch gut brauchbar

Hier darf noch etwas getunt werden.
evtl. KI noch kleiner.. ( Merke  K* (Intern *127 Integerwert).
     Kx < 0.01 macht keinen Sinn, da 0.01 *127 = 1,27 --> und das rundet ab.

----------------------------------------------
weitere LINKS:
Verständliche Anleitung : https://forum.arduino.cc/index.php?topic=433030.0
Sehr übersichtlich:   http://www.rn-wissen.de/index.php/Regelungstechnik

Regler im Wiki: http://de.wikipedia.org/wiki/Regler
http://de.wikipedia.org/wiki/Regler#PID-Regler

http://www.rn-wissen.de/index.php/Regelungstechnik

http://www.mikrocontroller.net/topic/1853
http://www.mikrocontroller.net/topic/17603#757516
http://www.mikrocontroller.net/svn/list
http://www.mikrocontroller.net/topic/82685#new



aus http://www.mikrocontroller.net/topic/57656


Also die Formel für den PID-Regler sieht wie folgt aus:
u(t) = Kp ( e(t) + 1/Tn * Integral(e(t) * dt) +Tv * e'(t) )

wobei u(t ) die Stellgröße, e(t) die Regeldifferenz, KR die 
Reglerverstärkung, TN die Nachstellzeit
und TV die Vorhaltzeit ist. (siehe 1 seite vom pdf über mir)

So, das wollen wir in einer zeitdiskreten Form haben. Da nen Rechner nur
diskrete Werte rechnen kann...
Also wird aus:

Integral(e(t) * dt) => Summe(e(k) ) * delta t // Fläche unter einer
Kurve
und e'(t) => (e(k) - e(k-1) ) / delta t // Steilheit einer
Kurve...

wenn man das in die ürsprüngliche Gleichung setzt und bisschen hin und
her rechnet (die unendliche Summe z.B. muss man eleminieren) kommt man
auf:

u(k) = u(k-1) * Kr ( ( (1+Tv)/delta t) * e(k) - (1- delta t /Tn +
2*Tv/delta t) * e(k-1) + Tv/delta t *e(k-2) )

Diese obige Formel haut man in eine Programmierzeile. Danach speichert
man noch u(k-1), e(k-1), e(k-2) für die nächste Rechnung...

Achja:
Kr = Kp ; Tn = Kr/Ki ; Tv = Kd/Kr

Tn = -26 und Tv = 0.16 sind gute richtwerte...

Viel Spaß damit...wer nachlesen will kann sich das obige pdf durchlesen
:)


ZWEITER TEIL:

Das Problem bei der Differenzengleichung für den zeitdiskreten 
PID-Regler ist ja die Summe. Man müsste also die Summe immer wieder neu
berechnen bzw. wird auch mit der Zeit ziemlich groß. Um diese Problem zu
umgehen macht man folgendes:

Man betrachtet die Gleichung für einen Zeitpunnkt früher, also y(k-1).
Der D-Anteil wird also zu:
(e(k-1) - e(k-2))/Ta
Die Summe beim I-Anteil wird jetzt von i=0 bis k-1 gezählt.
Der P-Anteil wird einfach zu y(k-1)...

Nun kommt der Trick: Man subtrahiert diese Gleichung von der Gl. für
y(k).
Also: y(k) - y(k-1)
Was passiert bei der Summe? Folgendes:
Die Summe bei y(k) sieht explizit so aus: e0 + e1+ e2 + ...+e(k)
Die Summe bei y(k-1) hat ein Glied weniger: e0 + e1+ ...+e(k-1)
subrahiert man beide bleibt von den zwei großen Summen nur noch das
Glied e(k) übrig! Und ist damit für den Rechner geeignet...





Knowledge Base:
 von Atmel
Siehe (googlen nach..) Atmel Application Note AVR221 - A universal PID Controller with AVR   bei http://www.atmel.com/
Artikel dazu :  AVR221 Discrete PID controller.pdf
Das orginal Atmel PID Modul:
AVR221.zip
und die rain enthaltene Readme HTM: AVR221/IAR/readme.html

gefundenes: http://www.ermicro.com/blog/?p=1163



Du  mußt die folgenden drei Größen berechnen und diese mit geeigneten Verstärkungen als Korrekturgröße verwenden.
1) für P-Anteil:  (Istwert-Sollwert)
2) für D-Anteil: ((Änderung des istwertes seit letzter  Berechnung)/zeitdifferenz)
3) für I-Anteil: (Istwert-Sollwert)*Zeitdifferenz

Allerding sollte bei der gewünschten Genauigkeit mit PWM  (Pulsbreiten-Modulation) deie  Heizungsregelung steuern.

Die Wirkung der einzelnen Parameter ist wie folgt:
Der P-Anteil macht die Regelung schnell (mit nur dieser Verstärkung  solltest Du anfangen bis der Regelvorgang anfängt zu schwingen).
Der D-Anteil (Geschwindigkeitsrückführung) wird zur Dämpfung von  Überschwingen verwendet. er macht die Regelung langsamer.
Der I-Anteil macht deine Regelung genau (eigentlich unendlich genau da die Heizleistung so lange verändert wird bis Soll=Ist)
Das Optimun hast Du falls der Istwert einer Sprunghafte Änderung des Sollwertes mit ca 10 % Überschwingen folgt.



das heisst also fuer meinen Programm-Algorithmus:

* messe ist-Wert
* speichere Ist-Wert (fuer naechste Differenzbildung)
* berechne P
* berechne D
* berechne I





Jetzt definierst Du 3 Verstärkungen also beispielsweise KP, KD, KI.
Deine Ausgangsgröße also dein PWM Ausgang berechnest Du wie folgt:
Ausgang = KP*P + KD*D + KI*I
Wichtig ist noch daß du am Start mit dem Wert I=0 anfängst damit der
Heizvorgang langsam anfängt.
Nehmen wir an Du willst 100 Grad haben und hast die Verstärkungen
KP=5 bits/Grad
KD=1 bit/(grad/sec)
KI=2 (bit*sec) /grad
und nehmen wir weiter an Du hast 20 Grad als Istwert und 19 Grad beim
letzten loop und dein loop dauer 0,1 sec so ergibt sich
KP*P=(20-100)*5=-400
KD*D=(20-19)/0,1=+10
KI*I=(20-100)*0,1*2=-16+(Summe aller bisherigen KI*Is)

Ausgang=-(-400+10-16=-406)gilt nur beim ersten loop.
Beim zweiten loop haben wir bei gleichen Temperaturen -400+10-16-16=-422
Hinweis: Das Minus vor der Klammer hatte ich vergessen
Nach mehreren loops wird KP*P immer kleiner weil Soll und Istwert sich
annähern. KD*D geht gegen Null weil sich die Temperatur nicht mehr



ändert und Integral(KI*I) wird praktisch gleich dem Ausgang.



// MEINE MAIN.c

/*
#define K_P     ( gf_K_Faktor * 0.65)  //1
#define K_I     ( gf_K_Faktor  *0.5)
#define K_D        ( gf_K_Faktor  * 0.12)
*/


/*! \brief Flags for status information
 */
struct GLOBAL_FLAGS {
  //! True when PID control loop should run one time
  uint8_t pidTimer:1;
  uint8_t dummy:7;
} gFlags = {0, 0};

//! Parameters for regulator
struct PID_DATA pidData;

/*! \brief Sampling Time Interval
 *
 * Specify the desired PID sample time (255 cylces to overflow), the time interval value is calculated as follows:
 * TIME_INTERVAL = ( desired interval [sec] interval
 * With a 8-bit counter ) * ( frequency [Hz] ) / 256
 */
//! \xrefitem todo "Todo" "Todo list"

/*! \brief Timer interrupt to control the sampling interval  */
ISR( TIMER0_OVF_vect )
{
volatile static uint16_t uiNN = 0;
if(uiNN < TIME_INTERVAL)
    {
    uiNN++;
    }
  else
    {
    gFlags.pidTimer = TRUE;
    uiNN = 0;
  }
}


/*! \brief Init of PID controller demo
 */
// ******************************************
void MY_PIDInit(void)
// ******************************************
{

/*
float gfK_P = KP_DEFAULT;
float gfK_I = KI_DEFAULT;
float gfK_D = KD_DEFAULT;
float gf_K_Faktor = KC_FAKTOR_DEFAULT;
float gf_PC_Faktor = PC_OSC_PERIODE;

#define SCALING_FACTOR  128
*/

//INT16_t
  pid_Init(gfK_P * SCALING_FACTOR, gfK_I * SCALING_FACTOR , gfK_D * SCALING_FACTOR , &pidData);
  pid_Reset_Integrator(&pidData); // byme

//Initialisiere Timer0 für PID
  // Set up timer, enable timer/counte 0 overflow interrupt
    //TCCR0 = (1<<CS00);    // %1
  //TCCR0 = (1<<CS02);    // %256
  TCNT0=0;
  TIMSK |= (1<<TOIE0);
  TCCR0 = (1<<CS02);    // 256
};

/*! \brief Read reference value.
 *
 * This function must return the reference value.
 * May be constant or varying
 */
int16_t Get_Reference(void)
{
  return gf_SOLL_Temperatur * FLOATTOINT_KOMMA_FAKTOR; // = 44.44 GRAD
}

/*! \brief Read system process value
 *
 * This function must return the measured data
 */
int16_t Get_Measurement(void)
{
  return  (int16_t)(gf_IST_Temperatur * FLOATTOINT_KOMMA_FAKTOR);
}

/*! \brief Set control input to system
 *
 * Set the output from the controller as input
 * to system.
 */
void Set_Input(int16_t inputValue)
{
 if( inputValue > 0 )        // KANN JA NUR HEIZEN
    {
    OCR1A  = (inputValue / 32767.0) * 1023.0;  //0..1*PWM
    }
else
    {   
    OCR1A=0; // KANN NICHT KÜHLEN
    };
};


//hier die while(1)
while(1)  //ENDLESS
    {
      // Run PID calculations once every PID timer timeout
    if( gFlags.pidTimer )
        {
        gFlags.pidTimer = FALSE;

        gf_IST_Temperatur = ds18b20_gettemp( DS18B20_BIT );
      
        referenceValue = Get_Reference();
        measurementValue = Get_Measurement();
        inputValue = pid_Controller(referenceValue, measurementValue, &pidData);
     
        Set_Input(inputValue);       // Hier anpassen das aus dem Wert einee PWM wird.
        };   


// PID.C

/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief General PID implementation for AVR.
 *
 * Discrete PID controller implementation. Set up by giving P/I/D terms
 * to Init_PID(), and uses a struct PID_DATA to store internal values.
 *
 * - File:               pid.c
 * - Compiler:           IAR EWAAVR 4.11A
 * - Supported devices:  All AVR devices can be used.
 * - AppNote:            AVR221 - Discrete PID controller
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support email: avr@atmel.com
 *
 * $Name$
 * $Revision: 456 $
 * $RCSfile$
 * $Date: 2006-02-16 12:46:13 +0100 (to, 16 feb 2006) $
 *****************************************************************************/

#include "pid.h"
#include "stdint.h"

/*! \brief Initialisation of PID controller parameters.
 *
 *  Initialise the variables used by the PID algorithm.
 *
 *  \param p_factor  Proportional term.
 *  \param i_factor  Integral term.
 *  \param d_factor  Derivate term.
 *  \param pid  Struct with PID status.
 */
void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor, struct PID_DATA *pid)
// Set up PID controller parameters
{
  // Start values for PID controller
  pid->sumError = 0;
  pid->lastProcessValue = 0;
  // Tuning constants for PID loop
  pid->P_Factor = p_factor;
  pid->I_Factor = i_factor;
  pid->D_Factor = d_factor;
  // Limits to avoid overflow
  pid->maxError = MAX_INT / (pid->P_Factor + 1);
  pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);
}


/*! \brief PID control algorithm.
 *
 *  Calculates output from setpoint, process value and PID status.
 *
 *  \param setPoint  Desired value.
 *  \param processValue  Measured value.
 *  \param pid_st  PID status struct.
 */
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct PID_DATA *pid_st)
{
  int16_t error, p_term, d_term;
  int32_t i_term, ret, temp;

  error = setPoint - processValue;

  // Calculate Pterm and limit error overflow
  if (error > pid_st->maxError){
    p_term = MAX_INT;
  }
  else if (error < -pid_st->maxError){
    p_term = -MAX_INT;
  }
  else{
    p_term = pid_st->P_Factor * error;
  }

  // Calculate Iterm and limit integral runaway
  temp = pid_st->sumError + error;
  if(temp > pid_st->maxSumError){
    i_term = MAX_I_TERM;
    pid_st->sumError = pid_st->maxSumError;
  }
  else if(temp < -pid_st->maxSumError){
    i_term = -MAX_I_TERM;
    pid_st->sumError = -pid_st->maxSumError;
  }
  else{
    pid_st->sumError = temp;
    i_term = pid_st->I_Factor * pid_st->sumError;
  }

  // Calculate Dterm
  d_term = pid_st->D_Factor * (pid_st->lastProcessValue - processValue);

  pid_st->lastProcessValue = processValue;

  ret = (p_term + i_term + d_term) / SCALING_FACTOR;
  if(ret > MAX_INT){
    ret = MAX_INT;
  }
  else if(ret < -MAX_INT){
    ret = -MAX_INT;
  }

  return((int16_t)ret);
}

/*! \brief Resets the integrator.
 *
 *  Calling this function will reset the integrator in the PID regulator.
 */
void pid_Reset_Integrator(pidData_t *pid_st)
{
  pid_st->sumError = 0;
}





/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief Header file for pid.c.
 *
 * - File:               pid.h
 * - Compiler:           IAR EWAAVR 4.11A
 * - Supported devices:  All AVR devices can be used.
 * - AppNote:            AVR221 - Discrete PID controller
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support email: avr@atmel.com
 *
 * $Name$
 * $Revision: 456 $
 * $RCSfile$
 * $Date: 2006-02-16 12:46:13 +0100 (to, 16 feb 2006) $
 *****************************************************************************/

#ifndef PID_H
#define PID_H

#include "stdint.h"

/// PID.h

// -ZUGEFÜGT C.E.----------------------------------------
/*
NAch Ziegler Nichosl Methode

T     = Periodendauer gemessen.  etwa drei Durchläufe für eine Automatisierung
A     = Amplitude

Pb = A * 1,5    = Proportional Band;
Ti = T/2        = Integrierzeit
Td = T/8         = Vorhaltezeit
Tc = T/20         = Zykluszeit
*/

#define T_PERIODENDAUER    1.0
#define A_AMPLITUDE        1.0
#define PB_FACTOR ( A_AMPLITUDE * 1.5 )
#define TI_FACTOR    ( T_PERIODENDAUER / 2.0 )
#define TD_FACTOR ( T_PERIODENDAUER / 8.0 )
#define TC_FACTOR ( T_PERIODENDAUER / 20.0 )
// ---------------------------------------------------


#define SCALING_FACTOR  128

/*! \brief PID Status
 *
 * Setpoints and data used by the PID control algorithm
 */
typedef struct PID_DATA{
  //! Last process value, used to find derivative of process value.
  int16_t lastProcessValue;
  //! Summation of errors, used for integrate calculations
  int32_t sumError;
  //! The Proportional tuning constant, multiplied with SCALING_FACTOR
  int16_t P_Factor;
  //! The Integral tuning constant, multiplied with SCALING_FACTOR
  int16_t I_Factor;
  //! The Derivative tuning constant, multiplied with SCALING_FACTOR
  int16_t D_Factor;
  //! Maximum allowed error, avoid overflow
  int16_t maxError;
  //! Maximum allowed sumerror, avoid overflow
  int32_t maxSumError;
} pidData_t;

/*! \brief Maximum values
 *
 * Needed to avoid sign/overflow problems
 */
// Maximum value of variables
#define MAX_INT         INT16_MAX
#define MAX_LONG        INT32_MAX
#define MAX_I_TERM      (MAX_LONG / 2)

// Boolean values
#define FALSE           0
#define TRUE            1

void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor, struct PID_DATA *pid);
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct PID_DATA *pid_st);
void pid_Reset_Integrator(pidData_t *pid_st);

#endif