TC2 / TC3 Zeitdifferenz für PID Regler mit GetSystemTime()

MySPS

Level-1
Beiträge
14
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Zusammen,
da mir die Controller Library von TwinCat zu mächtig ist, wollte ich angelehnt an den PID Regler und P-T1 Strecke von OSCAT das "mal eben" abgespeckt selbst programmieren und simulieren. Scheitere aber schon an der Zeitdifferenz delta-t: Ta von Aufruf zu Aufruf.
GetSystemTime liefert mir UDINT timeLoDW und timeHiDW mit je digit 100ns. Soweit so gut. Nun möchte ich die Zeifdifferenz bilden da in PID und P-T1 verwendet.
Die Task ist auf 20ms gesetzt, die reale Zyklus-Zeit im Task Monitor wird mit Total 80..150us angezeigt. Wie kommt dieser riesen Wert unten bei TA im Screenshot zustande? Muss dazu sagen, dass das ganze in VirtualBox läuft.

PROGRAM Controller
VAR
T_PLC_100NS: GetSystemTime;

txl: UDINT;
txh: UDINT;

Ta: DWORD;
lastTa: DWORD;
END_VAR


CODE...
(*Get Systemtime*)
T_PLC_100NS();
txl:=T_PLC_100NS.timeLoDW;
txh:=T_PLC_100NS.timeHiDW;
Ta:=txl-lastTa;


hier stark vereinfacht ....
(*Regler*)
PID( u.a. VAR_INPUT Ta :dword;
e:=w-x;
esum:=esum+e;

y := KP * e + KI * DWORD_TO_REAL(Ta) * esum + KD * (e-ealt)/DWORD_TO_REAL(Ta);
ealt:=e;
);

(*Strecke*)
PT1( u.a. VAR_INPUT Ta :dword;
out := out + (y * K - out) * DWORD_TO_REAL(Ta) / TIME_TO_REAL(T) * 1.0E-6;
)
lastTa:=Ta;


Screenshot von den Werten. Die dritte Stelle von links sind Sekunden.
time diff.png
 
Hallo, ich bestimme die Zykluszeit immer so :

PROGRAM MAIN VAR timex : TIME; // aktueller Zeitstempel oldtimex : TIME; // letzter Zeitstempel Cycletime : TIME; // differnzzeit END_VAR // detect plc cycletime timex := TIME(); IF timex <> oldtimex THEN Cycletime := timex - oldtimex; oldtimex := timex; END_IF
 
Zuviel Werbung?
-> Hier kostenlos registrieren
GetSystemTime liefert mir UDINT timeLoDW und timeHiDW mit je digit 100ns. Soweit so gut.
Ich glaube, hier fängt das Problem an.
Wenn timeLoDW die Einheit 100 ns hat, dann hat timeHiDW doch die Einheit 65536 * 100 ns.
Wenn Dir GetSystemTime UDINT liefert, wozu dann die Aufteilung in timeHiDW und timeLoDW?
Differenz bilden aus
zeitAktuell - zeitDerVorherigenMessung
Die aktuelle Zeit musst Du Dir abspeichern, um sie im nächsten Zyklus von der dann aktuellen Zeit zu subtrahieren (zeitDerVorherigenMessung).

Du machst etwas ähnliches ...
Code:
Ta:=txl-lastTa;
...
lastTa:=Ta;
... aber lastTa ist Quatsch, da Ta bereits die/eine Differenz ist - die brauchst Du Dir für den nächsten Zyklus nicht zu merken.

Edit: wollvieh war schneller

Was mich verwirrt: mit UDINT in 100 ns komme ich auf max. ca. 7 min und 9 s. Habe ich ein Brett vorm Kopf? Wo ist der Haken?
Wo verstecken sich die restlichen 1433 Minuten des Tages?
Da müsste man doch in LINT rechnen und auch das allein bringt's nicht.
 
Zuletzt bearbeitet:
Und sicherheitshalber immer den Überlauf beachten, bei Time() ca. alle 47 Tage, bei dem txl hier dürfte es ca. alle 7,15 Minuten passieren.

Gruß
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke für eure Anmerkungen!!

"Wenn timeLoDW die Einheit 100 ns hat, dann hat timeHiDW doch die Einheit 65536 * 100 ns."
Das ist völlig korrekt aber ich brauche nur die kleinen Zeitintervalle timeHiDW ändert sich ewig nicht.

Ta ist ist die Zeit zwischen aktueller Zeit txl - vorheriger Zeit lastTa. Diese vorherige Zeit lastTa muss ich mir für den nächsten Zyklus merken.

Den Codeschnipsel von Wollvieh probiere ich gleich mal aus.
der macht den Part ja genauso : Cycletime := timex - oldtimex; oldtimex := timex;
 
Der Aufruf ist wie bereits geschrieben falsch.
Gegenfrage:
Was erwartest du bei der Differenzberechnung?
Die Funktion wird alle 20ms aufgerufen. Die Differenz ist also immer 20ms, es sei denn der Controller ist überlastet.
und mit der korrekten Form : LastTa := txl; kommen wie von dir geschrieben 20ms raus.
Wäre zu erwarten gewesen 🤦‍♂️
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Anmerkung:
Wenn ich FB schreibe die eine Zykluszeit als Grundlage haben gehe ich folgendermaßen vor:
1. Annahme: die Taskzeit läst sich nur während der Programmierung ändern, anschließend ist sie konstant.
In einem Init Baustein wird nach Programmstart einmalig die Taskzeit ermittelt und global zur Verfügung gestellt.
Damit hast du auch keine Probleme mit dem Überlauf der Time Funktion.
 
Vielleicht kann ich Dir ein wenig Arbeit ersparen: Einen einfachen PID-Regler findest Du auch in der Bibliothek Tc2Utilities: FB_BasicPID.
 
Jeder Zähler mit begrenztem Zählumfang hat irgendwann einen Überlauf.
Bei der Differenzbildung spielt die Überschreitung des Maximalwertes aber keine Rolle. Nur bei Siemens muss man an dieser Stelle den Überlauf berücksichtigen, da Siemens mitten im Zählen (bei Erreichen des vermeintlichen Vorzeichenbits) ganz einfach mal von vorne anfängt zu zählen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Heinileini, nun zu dir :) .

Bevor Beckhoff und 3S in die Verlegenheit kommen und ihre Softwarefehler beseitigen, muss ich mal ein paar grundsätzliche Dinge infrage stellen. Es macht ja sonst keiner.
Ich glaube, hier fängt das Problem an.
Genau ;) !

.. Wenn timeLoDW die Einheit 100 ns hat, dann hat timeHiDW doch die Einheit 65536 * 100 ns.
Wenn Dir GetSystemTime UDINT liefert, wozu dann die Aufteilung in timeHiDW und timeLoDW? ..
Es ist schwer zu glauben, aber timeHiDW und timeLoDW scheinen jeweils vorzeichenlose 32-Bit-Werte zu sein, also jeweils UDINT (0..4294967295)?

.. Habe ich ein Brett vorm Kopf? ..
Das würde ich niemals behaupten, aber wenn das die Ursache ist, dann wäre das das kleinere Übel ;) ?

.. Wo verstecken sich die restlichen 1433 Minuten des Tages? ..
Sieh es doch mal nüchtern, Heinileini, die Systemzeit hat doch nicht wirklich etwas mit der Uhrzeit zu tun? Oder verstehe ich deine Frage nicht?
 
Jeder Zähler mit begrenztem Zählumfang hat irgendwann einen Überlauf.
Bei der Differenzbildung spielt die Überschreitung des Maximalwertes aber keine Rolle. Nur bei Siemens muss man an dieser Stelle den Überlauf berücksichtigen, da Siemens mitten im Zählen (bei Erreichen des vermeintlichen Vorzeichenbits) ganz einfach mal von vorne anfängt zu zählen.
Auch beim Codesys-Systemzeitgeber gibt es irgendwann einen Überlauf. Doch glücklicherweise hat man bei Codesys bei der Implementierung einfach nicht den selben Fehler wie Siemens gemacht, so daß man bei Zeit-Differenzmessungen auch über den Überlauf hinweg (dank Zweierkomplement) nichts besonderes beachten muß, solange die Zeitmessung kürzer als der halbe Zeitgeber-Zählumfang ist.

Bei Siemens hatte man es bestimmt gut gemeint und hat den Systemzeitgeber fatalerweise auf 31 Bit begrenzt, damit keine als negative IEC-Zeit (T#-...) interpretierbare Werte vorkommen. Dadurch kann man allerdings nicht mehr auf die Vorteile des Zweierkomplements hoffen und muß den Überlauf speziell behandeln (---> einfach immer das höchste Bit .31 der Differenz auf 0 ablöschen).

Harald
 
GetSystemTime liefert mir UDINT timeLoDW und timeHiDW mit je digit 100ns. Soweit so gut. Nun möchte ich die Zeifdifferenz bilden
timeLoDW kann Werte 0 .. 4_294_967_295 haben, entspricht max 429_496_729,5µs ~ 429s
Solange Deine Zeitdifferenzmessung immer kürzer als ca. 214s ist (was bei Aufruf in jedem Zyklus sicher gegeben ist), kannst Du timeHiDW komplett ignorieren.

Welches Ergebnis liefert TC2/TC3, wenn bei UDINT-Subtraktion ein Überlauf auftritt (der Subtrahend ist größer als der Minuend)?
UDINT: 0 - 4_294_967_295 = 1 ?

Dann könntest Du ganz einfach rechnen:
Code:
VAR
  T_PLC_100NS : GetSystemTime;
  tlast : UDINT;
  Ta : UDINT;
END_VAR

T_PLC_100NS();
Ta := T_PLC_100NS.timeLoDW - tlast;
tlast := T_PLC_100NS.timeLoDW;

Harald
 
Zurück
Oben