# PWM für PID-Ausgang - Twincat3



## MZo (14 Mai 2019)

Hallo Zusammen,

Projekt an der Hochschule: PID-Regler für Temperaturregelung.

Ich habe einen PID-Regler realisiert der die Temperatur in einem Modul regeln soll (Entwicklungsumgebung Twincat3).
Dazu habe ich den Funktionsblock FB_BasicPID verwendet die Soll- und Isttemperatur und die PID Werte werden hier übergeben und intern berechnet,
sodass ich am Ausgang des Reglers einen Wert habe, der sich ständig ändert. 
Das heizen soll über ein Halbleiterrelai (SSR) funktionieren.

Mein Problem:
Ich weis, dasss ich dazu eine PWM verwenden muss, damit ich die gewünschte Temperatur mit möglichst wenig Überschwingung erreiche.
Doch ich weis nicht wie ich diese PWM realisieren soll.
Ich darf keine weitere lizensabhängiges FB verwenden, sondern sollte/muss die PWM Steuerung "zu Fuß" programmieren.

Bin recht neu hier und arbeite mich seit 1-2 Monaten erst ein in Twincat3.

Kann mir da evtl jemand weiterhelfen?

Vielen Dank schonmal im Voraus


----------



## ADS_0x1 (14 Mai 2019)

Hallo MZo,

wenn das Ausgangssignal ein normiertes Signal im Bereich 0 ... 1 ist, kannst du beispielsweise den PWM Baustein von OSCAT benutzen. Den kannst du mit n bisschen Gehirnschmalz aber auch selber schreiben.

Du musst nur beachten, was der Tyro / das Halbleiterrelais für Nenndaten hat (minimales Puls-Pausenverhältnis, maximale 100% Zeit... schau da mal ins Datenblatt).

Viele Grüße


----------



## Grisuh (14 Mai 2019)

Hallo MZo,

das ist recht einfach. Ich kann Dir einen fertiogen FB zusenden, den ich unter twinCat 2 in AWL gemacht habe. 
Der Eingang in dieses Bausteines bekommt 0 - 100 %, und einen Wert ( Zeitbasis ) der bestimmt wie schnell das 
PWM Signal ist.
Er sieht wie folgt aus:

FUNCTION_BLOCK Allg_FB_PWM
VAR_INPUT
    IN_Freigabe            : BOOL;
    IN_Sollwert             : REAL;
    IN_Zeitbasis            : INT := 1000;
END_VAR
VAR_OUTPUT
    OUT_PWM_Signal    : BOOL;
END_VAR
VAR
    Sollwert_INT            : INT;
    Taktlaenge            : INT;
    Zeitbasis_Time        : TIME;
    Zeitbasis_Timer        : TON;
    Taktlaenge_Time        : TIME;
    Takt_Timer            : TOF;
    Takt_Trigger            : BOOL;
END_VAR
VAR
END_VAR

    (* Dieser Baustein erzeugt ein PWM Signal bei einem Eingang von
    0-100% des Einganges. Der Baustein ist Zykluszeitabhängig, deshalb muss die
    Zeitbasis angegeben werden. *)

    (* Wandlung Sollwert *)
    LD        IN_Sollwert
    REAL_TO_DINT
    DINT_TO_INT
    ST        Sollwert_INT

    LD        Sollwert_INT
    GT        1.0
    AND        IN_Freigabe
    JMPC    Go
    LD        0
    ST        Sollwert_INT
    LD        FALSE
    ST        OUT_PWM_Signal
    RET
    (* Prozentuale Taktlänge berechnen *)
Go:LD        IN_Zeitbasis                (* 1ne Sekunde *)
    DIV        100                             (* 10 ms *)
    MUL        Sollwert_INT
    ST        Taktlaenge

    (* Zeitbasis, also Länge des Taktes pro Zeitbasis in Zeit umrechnen *)
    LD        IN_Zeitbasis
    INT_TO_TIME
    ST        Zeitbasis_Time

    (* Timer für Zeitbasis *)
    LD        IN_Freigabe
    ANDN    Takt_Trigger
    ST        Zeitbasis_Timer.IN
    CAL        Zeitbasis_Timer(PT :=Zeitbasis_Time)
    LD        Zeitbasis_Timer.Q
    ST        Takt_Trigger

    (* Länge des Taktes pro Zeitbasis in Time umrechnen *)
    LD        Taktlaenge
    INT_TO_TIME
    ST        Taktlaenge_Time

    (* Takt ausgeben *)
    LD        IN_Freigabe
    AND        Takt_Trigger
    ST        Takt_Timer.IN
    CAL        Takt_Timer(PT :=Taktlaenge_Time)
    LD        Takt_Timer.Q
    ST        OUT_PWM_Signal

Also bei einer Zeitbasis von 1000 und einer Zykluszeit von 1 ms ergibt sich 
für jeden Takt des PWM Signals eine Länge von 1 sek. (1000 ms )
Bei einem Sollwert von 10.0 ergibt sich 900ms aus und 100 ms ein.

Das ganze habe ich bei Bedarf auch für sehr schnelle Zykluszeiten gemacht,
um Beispielsweise Gleichstrommotoren in der Drehzahl zu regeln.


Gruß Jörg


----------



## Plan_B (14 Mai 2019)

Abhängig von der Zykluszeit des Reglers und der Taskzeit der Steuerung wär zum Beispiel für den PWM-Teil ein freilaufender Zähler denkbar, der in jedem Zyklus inkrementiert wird (Byte). Der zählt jetzt bis zu einem von Dir definierten Wert hoch und wird auf Null zurückgesetzt.
Der Ausgang wird über einen <= Vergleich des PID-Ausgangswerts mit dem Zahlerwert geschaltet. Dafür muss der PID-Ausgangswert auf Deinen gewählten Zählbereich normiert werden.

Beispiel:
Zähler 0...100
PID-Ausgang 0...100%

Wenn PID-Ausgang > 0 und Zähler >= PID-Ausgang dann Heizausgang ein.


----------



## wollvieh (14 Mai 2019)

Ich hab's mal so gelöst :


Wollvieh.


----------



## MZo (23 Mai 2019)

Soo, Hallo nochmal,

Tut mir leid für die späte Rückmeldung, war ziemlich angeschlagen und lag im Krankenhaus..

Danke auf jeden Fall für eure Hilfe 



> das ist recht einfach. Ich kann Dir einen fertiogen FB zusenden, den ich unter twinCat 2 in AWL gemacht habe.



Dein Code wäre ziemlich interessant eigentlich sowas ähnliches habe ich auch versucht nur ich blicke nicht so genau durch, da ich nur in ST programmiere und nicht AWL, schade..

@wollvieh danke für dein Codebeispiel


----------



## MZo (24 Mai 2019)

Hallo wollvieh,

ich habe in meinem Programm die PI Werte bestimmt und zwischen 0-255 skaliert.  Sprich ich mach bei Reglerausgang>255 Heizen und Reglerausgang zwischen 0-255 PWM.
Dein Code habe ich zum Testen in einem Funktionsblock verwendet. Die rPwmFrequenz ist bei mir 255 und rDutyCycle der skallierter Reglerausgang. 

Habe ich die Werte soweit richtig bestimmt?, muss ich noch andere Variablen definieren? und wie genau muss ich diesen Funktionsblock in main() dann aufrufen?

Sry wenn ich mich gerade dumm anstelle, aber ich mach sowas zum ersten mal ^^

Danke schon mal für deine Hilfe


----------



## wollvieh (24 Mai 2019)

rPwmFrequenz von 255 würde 255Hz bedeuten, das heißt 4ms PWM Grundzeit,  so schnell ist deine SPS sicher nicht.  Sinnvolle Werte sind hier 0,1 (10s) bis 10,0 (100ms) PWM Grundzeit. Und Deine 0 bis 255 für 0..100% PWM Duty Cycle musst Du dann mittels Dreisatz nach 0..1.0 rDutyCycle umrechnen und dem FB Eingang rDutyCycle zuweisen.

https://5volt-junkie.net/was-ist-pwm-ein-kleines-tutorial/


----------



## MZo (28 Mai 2019)

Okay verstehe, danke für deine Antwort 

und was genau ist "rtmp" ? Muss da bei der Berechnung die Zahl 1000.0 bleiben oder ist die Zahl gleich der Taskzyklus?

Mein Beispiel: Das hier rufe ich bei mir im Main auf


```
IF Output < 255 AND Output > 0 THEN
temp:= (Output * 100) / 255;
fb_PWM(rDutyCycle:= temp, rPwmFrequency:= 0.1, xPwmOut=> );
IF fb_PWM.xPwmOut THEN
Heizen := TRUE;
END_IF
END_IF
```

Mein fb_PWM Baustein habe ich von dir übernommen, doch ich kann hier nicht pulsen (siehe Bild) auf dem Pulstimer.Q kommt nie eine True zustande bei mir..


----------



## wollvieh (28 Mai 2019)

Hallo

Auszug aus Deinem Code :  temp:= (Output * 100) / 255;
Das ist nicht richtig. Bei Output 255 ergibt das 255*100 /255 = 100.

richtig wäre :
temp:= ( 1.0 / 255 ) * Output --> ( 1.0/255 ) * 255 = 1.0
rDutyCycle MUSS zwichen 0.0 und 1.0 betragen !

P.S.: die rTemp 1000 sind 1000ms, da die Timer Millisekunden als Auflösung verwenden


----------



## Plan_B (28 Mai 2019)

wollvieh schrieb:


> Bei Output 255 ergibt das 255*100 /255 = 255.




Hat sich nach der Europawahl was geändert?


----------



## wollvieh (28 Mai 2019)

Gut. Korrigiere auf 100.0.
Mein Fehler.
Danke. Allet im Lot.
;-)


----------



## MZo (6 Juni 2019)

vielen Dank für deine Hilfe, hat jetzt super geklappt


----------



## Joe_Joe (25 November 2020)

Hallo, 

ich muss für eine Arbeit in der Uni genau das gleiche wie du machen. Also mit der BasicPID Funktion in TwinCAT3 einen digitalen Ausgang über eine "selbstprogrammierten" PWM regeln.

Meine Frage:

Wie kann ich meine Ausgabewert der BasicPID Funktion auf einen Wert zwischen 0 und 1 normieren wenn dieser vom Typ LREAL ist und damit theoretisch Werte bis 1,8·10[SUP]308 [/SUP] annehmen könnte?

würde mich freuen wenn Ihr mir helfen könntet!


----------



## Heinileini (26 November 2020)

Joe_Joe schrieb:


> ... eine "selbstprogrammierten" PWM ...
> 
> Wie kann ich meine Ausgabewert der BasicPID Funktion auf einen Wert zwischen 0 und 1 normieren wenn dieser vom Typ LREAL ist und damit theoretisch Werte bis 1,8·10[SUP]308 [/SUP] annehmen könnte?


Den kompletten mit LREAL darstellbaren Bereich wirst Du sicherlich nicht nutzen.
Welcher Bereich wird denn vom PID genutzt und begrenzt Du diesen Bereich (vorsichtshalber) noch zusätzlich?
Dann könntest Du Dich daran orientieren.
Man kann und sollte sich auch an dem gewünschten Ergebnis orientieren und vielleicht in Prozent (oder Promille oder ...) des vom Stellglied abgedeckten Bereichs "denken".
Fütterst Du Deine selbstprogrammierte PWM wirklich mit einem REAL- oder LREAL-Wert 0.0 .. 1.0?


----------

