TwinCAT 2 Rampe sanft abbremsen projektieren

ece8248

Level-1
Beiträge
26
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
ich versuche eine Rampe zu projektieren bei einer Anlage die hebt und senkt. Das Ziel ist wenn ich die Maschine bediene und eine der Tasten zum Heben oder Senken loslasse, dass die Maschine weiterhebt oder entsprechend senkt und sanft abbremst. Die Werte die ich in den zwei Arrays kriege machen keinen Sinn. Woran könnte der Fehler liegen?


-------------------------------------------------------------------------------------------
FUNCTION_BLOCK Taste_Loslassen_Kurve
VAR_INPUT
ENABLE: BOOL;
i_W1_Winkel: REAL;
i_W2_Winkel: REAL;
i_T_Heben: BOOL;
i_T_Senken: BOOL;
i_W_Max_Heben: REAL;
i_W_Min_Senken: REAL;
i_Zeit_bis_Stop: TIME;
i_Heben_Modus: BOOL;
i_Senken_Modus: BOOL;
i_Akt_V_Heben: REAL;
i_Akt_V_Senken: REAL;
END_VAR
VAR_OUTPUT
q_Berechnete_Geschwindigkeit: REAL;
q_Taste_Losgelassen_BewAnf_Heben: BOOL;
q_Taste_Losgelassen_BewAnf_Senken: BOOL;
END_VAR
VAR
R_TRIG_Heben_Taste_Losgelassen: R_TRIG;
R_TRIG_Senken_Taste_Losgelassen: R_TRIG;
Counter_Heben: INT;
Counter_Senken: INT;
TP_Heben: TP;
TP_Senken: TP;
Geschwindigkeiten: ARRAY[1..i_Kurve_Anzahl_Punkte] OF REAL;
Zeitpunkte: ARRAY[1..i_Kurve_Anzahl_Punkte] OF INT;
END_VAR
VAR_STAT
Momentaufnahme_V_Heben_Senken: REAL;
END_VAR
VAR_INPUT CONSTANT
i_Kurve_Anzahl_Punkte: INT;
END_VAR
-------------------------------------------------------------------------------------------
IF ENABLE THEN
(*-----------------------------------------Heben Taste Losgelassen------------------------------------------------------*)
IF NOT q_Taste_Losgelassen_BewAnf_Senken THEN
TP_Heben(IN := i_Heben_Modus AND NOT i_T_Heben, PT := i_Zeit_bis_Stop);
q_Taste_Losgelassen_BewAnf_Heben := TP_Heben.Q AND NOT (i_W1_Winkel > i_W_Max_Heben OR i_W2_Winkel > i_W_Max_Heben);
R_TRIG_Heben_Taste_Losgelassen(CLK := TP_Heben.Q);
IF R_TRIG_Heben_Taste_Losgelassen.Q THEN
Momentaufnahme_V_Heben_Senken := i_Akt_V_Heben;
END_IF
FOR Counter_Heben := 1 TO i_Kurve_Anzahl_Punkte BY 1 DO
Zeitpunkte[Counter_Heben] := Counter_Heben * TIME_TO_INT(TP_Heben.PT / i_Kurve_Anzahl_Punkte);
Geschwindigkeiten[Counter_Heben] := Momentaufnahme_V_Heben_Senken - Counter_Heben * (Momentaufnahme_V_Heben_Senken / i_Kurve_Anzahl_Punkte);
END_FOR
IF q_Taste_Losgelassen_BewAnf_Heben THEN
FOR Counter_Heben := i_Kurve_Anzahl_Punkte TO 1 BY -1 DO
IF TIME_TO_INT(TP_Heben.ET) >= Zeitpunkte[Counter_Heben] THEN
q_Berechnete_Geschwindigkeit := Geschwindigkeiten[Counter_Heben];
EXIT;
END_IF
END_FOR
END_IF
END_IF


(*-----------------------------------------Senken Taste Losgelassen-----------------------------------------------------*)
IF NOT q_Taste_Losgelassen_BewAnf_Heben THEN
TP_Senken(IN := i_Senken_Modus AND NOT i_T_Senken, PT := i_Zeit_bis_Stop);
q_Taste_Losgelassen_BewAnf_Senken := TP_Senken.Q AND NOT (i_W1_Winkel < i_W_Min_Senken AND i_W2_Winkel < i_W_Min_Senken);
R_TRIG_Senken_Taste_Losgelassen(CLK := TP_Senken.Q);
IF R_TRIG_Senken_Taste_Losgelassen.Q THEN
Momentaufnahme_V_Heben_Senken := i_Akt_V_Senken;
END_IF
FOR Counter_Senken := 1 TO i_Kurve_Anzahl_Punkte BY 1 DO
Zeitpunkte[Counter_Senken] := Counter_Senken * TIME_TO_INT(TP_Senken.PT / i_Kurve_Anzahl_Punkte);
Geschwindigkeiten[Counter_Senken] := Momentaufnahme_V_Heben_Senken - Counter_Senken * (Momentaufnahme_V_Heben_Senken / i_Kurve_Anzahl_Punkte);
END_FOR
IF q_Taste_Losgelassen_BewAnf_Senken THEN
FOR Counter_Senken := i_Kurve_Anzahl_Punkte TO 1 BY -1 DO
IF TIME_TO_INT(TP_Senken.ET) >= Zeitpunkte[Counter_Senken] THEN
q_Berechnete_Geschwindigkeit := Geschwindigkeiten[Counter_Senken];
EXIT;
END_IF
END_FOR
END_IF
END_IF
END_IF
 
Kannst du den FB überhaupt ohne Fehler übersetzen? Var_Input CONSTANT ist KEINE Konstante. Der Compiler sollte eigentlich schon bei der Deklaration der Arrays mit einem Fehler abbrechen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich möchte dass die Länge der Arrays einstellbar ist, deswegen habe ich die Variable i_Kurve_Anzahl_Punkte als Eingang deklariert, damit aber diese Variable als Länge der Arrays eingegeben werden kann, muss die als CONSTANT deklariert sein, deshalb habe ich in diesem Fall VAR_INPUT CONSTANT. Die Übersetzung gibt keine Fehler raus. Baustein läuft normal, aber die Werte der Arrays passen nicht. Vielleicht hat es mit der Formel zu tun oder mit den Datentypen. Ich muss später ein Screenshot hochladen von den Werten die angezeigt werden.
 
Du kannst dir die Ausgabe mal im Scope anschauen. Vielleicht hilft das.

Aber mal aber so eine grundsätzliche Frage:
Alle Drehzahlsteuerbare Antriebsverstärker haben so Parameter wie Start-Rampe, Stop-Rampe, S-Verschliff, Beschleunigung, Verzögerung, Ruck usw.
Warum machst Du es nicht da drüber?
 
damit aber diese Variable als Länge der Arrays eingegeben werden kann, muss die als CONSTANT deklariert sein, deshalb habe ich in diesem Fall VAR_INPUT CONSTANT

So funktioniert das nicht. Dynamische Arrays kennt TwinCAT 2 nicht. CONSTANT bewirkt in dem Fall, dass es völlig unerheblich ist, was am Eingang des Bausteines steht. Das Array is fertig kompiliert und ändert sich zur Laufzeit auch nicht mehr.

Wenn man das machen will, muss man ein Array auf die maximale Länge vordefinieren und über einen Eingangsparameter die maximale Anzahl der zu benutzenden Array-Felder festlegen. Dabei muss man sehr genau darauf achten, dass man die Array-Grenzen nicht überschreitet. TC2 überprüft das nämlich nicht. Man würde sonst auf andersweitig deklarierte Speicherbereiche zugreifen und im schlimmsten Fall macht die SPS nur noch komische Sachen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
TC2 gibt da echt keine Fehlermeldung, oha. TC3 hat mir die Deklaration gleich um die Ohren gehauen.

TC2 kennt auf jeden Fall keine Arrays mit dynamischer länge. Ich weiß nicht was der Compiler mit den Arrays macht die du Deklariert hast, aber es kann gut sein, dass du beim Zugriff auf deine Arrays in Speicherbereiche gerätst, die schon anderweitig benutzt werden. Dass kann dann z.B. zu dem Phänomen führen, dass da plötzlich komische Werte drin stehen oder Variablen an anderen Stellen in deinem Projekt überschrieben werden.
 
Anscheinend verstehe ich noch nicht, warum die Tabellen so oft und so mühsam berechnet werden.
Könnte man die ArrayElemente nicht mit sinnvollen Werten initialisieren, theoretisch oder experimentell ermittelt?
Was es mit der MomentAufnahme auf sich hat, vermag ich im Moment auch nicht aufzunehmen.
Die eingelesenen Winkel scheinen eine Art von EndschalterFunktion zu erfüllen. Woher kommen eigentlich diese Werte? Gibt's da irgendwo einen Encoder? Und kann man den auch sinnvoller als IstPositionsMelder verwenden?

Habe mir mal eine primitivere Variante überlegt, bei der allerdings zusätzlich auch an der Rampe beschleunigt wird. Ausser 2 Endschaltern gibt es 2 Vorab-Endschalter zum Einleiten des Abbremsens, falls die Taste zu spät losgelassen wird. Im Bereich zwischen dem Vorab- und dem eigentlichen Endschalter kann man in Richtung Endschalter nötigenfalls mit nur z.B. 1% weiterfahren.
Mit Geschwindigkeiten und Zeiten (und Wegen) wird nichts "gezaubert".
Die dargestellte "BasisVersion" arbeitet ohne Tabellen. Es ist aber angedeutet, wo sich diese "anflanschen" lassen. Die von der BasisVersion berechneten SollWerte werden dann quasi als Indizes für TabellenZugriffe verwendet. Es muss dann aber dafür gesorgt werden, dass aus den SollWerten nur gültige Indizes errechnet werden (z.B. den SollWert von 100 auf 20 verkleinern per Division durch 5, wenn die Arrays die Elemente 0..20 haben, also z.B. oiAuf := aAuf[(siSollAuf+4)/5] ).

Ungetestet:
Code:
VAR_INPUT
    ibAb         : BOOL ;             // Taster Ab
    ibEsAbEnd    : BOOL ;             // EndSchalter Ab
    ibEsAbVor    : BOOL ;             // VorEndSchalter Ab (startet Rampe auf 0)
    ibAuf        : BOOL ;             // Taster Auf
    ibEsAufEnd   : BOOL ;             // EndSchalter Auf
    ibEsAufVor   : BOOL ;             // VorEndSchalter Auf (startet Rampe auf 0)
    ibTakt       : BOOL ;             // ZeitTakt für Zählen
END_VAR
VAR_OUTPUT
    oiMot        : INT ;              // Ausgabe an Motor (pos. für Auf, neg. für Ab)
END_VAR
VAR // _STATIC !!
    sbTaktAlt    : BOOL ;             // "FlankenMerker" für ZeitTakt  
    siSollAb     : INT ;              // Gedächtnis für SollWert Ab
    siSollAuf    : INT ;              // Gedächtnis für SollWert Auf
END_VAR
VAR_TEMP
    tiSollAbPro  : INT ;              // Vorgabe des ProzentWertes Ab  (0, 1, 100)
    tiSollAufPro : INT ;              // Vorgabe des ProzentWertes Auf (0, 1, 100)
END_VAR

If ibAuf AND ibEsAufEnd AND NOT ibEsAufVor AND siSollAuf = 0 AND siSollAb = 0 Then
    tiSollAufPro := 1;   // Wert bzw. Index für 1% - für man. Weiterfahren zwischen VorEndSchalter bis EndSchalter
ElsIf ibAuf AND ibEsAufEnd AND siSollAb = 0 Then
    tiSollAufPro := 100; // Wert bzw. Index für 100%
ElsIf ibAb AND ibEsAbEnd AND NOT ibEsAbVor AND siSollAb = 0 AND siSollAuf = 0 Then
    tiSollAbPro  := 1;   // Wert bzw. Index für 1% - für man. Weiterfahren zwischen VorEndSchalter bis EndSchalter
ElsIf ibAb AND ibEsAbEnd AND siSollAuf = 0 Then
    tiSollAbPro  := 100; // Wert bzw. Index für 100%
Else
    tiSollAufPro := 0;
    tiSollAbPro  := 0;
End_If;

If sbTaktAlt <> ibTakt Then
    sbTaktAlt := ibTakt;

    If siSollAuf < tiSollAufPro Then
        siSollAuf := siSollAuf + 1;
    ElsIf siSollAuf > tiSollAufPro Then
        siSollAuf := siSollAuf - 1;
    End_If;

    If siSollAb < tiSollAbPro Then
        siSollAb := siSollAb + 1;
    ElsIf siSollAb > tiSollAbPro Then
        siSollAb := siSollAb - 1;
    End_If;
End_If;

oiMot := siSollAuf - siSollAb; // alternativ per Tabelle: oiMot := aAuf[siSollAuf] - aAuf[siSollAb]
 
Anscheinend verstehe ich noch nicht, warum die Tabellen so oft und so mühsam berechnet werden.
Könnte man die ArrayElemente nicht mit sinnvollen Werten initialisieren, theoretisch oder experimentell ermittelt?
Was es mit der MomentAufnahme auf sich hat, vermag ich im Moment auch nicht aufzunehmen.
Die eingelesenen Winkel scheinen eine Art von EndschalterFunktion zu erfüllen. Woher kommen eigentlich diese Werte? Gibt's da irgendwo einen Encoder? Und kann man den auch sinnvoller als IstPositionsMelder verwenden?
[/CODE]

Du hast Recht dass sie zu oft berechnet werden. Vielleicht wäre es besser die als statisch zu deklarieren und nur einmal überschreiben wenn der Trigger aktiv ist, wie im Fall der Momentaufnahme.

Bezüglich der Momentaufnahme, meine Kipparme haben sowohl Schnell- als auch Langsammodus und je nach dem in welchem Modus ich mich jedes Mal befinde möchte ich die Rampe entweder von der schnellen oder langsamen Geschwindigkeit runterfahren.

Die Winkel kommen von Sensoren.

Danke für deinen Code. Ich muss ihn lesen und ich melde mich.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
... meine Kipparme haben sowohl Schnell- als auch Langsammodus ...
Habe diesen Aspekt noch reingestrickt.
Statt der Eingänge 'ibAuf' und 'ibAb' hier jetzt mit den 4 Eingängen 'ibAufSG', 'ibAufEG', 'ibAbSG' und 'ibAbEG' (SG=Schleichgang, EG=Eilgang).
Für den Schleichgang habe ich willkürlich 10% angesetzt. Eigentlich müsste ich noch entsprechende VorabEndschalter für den Schleichgang hinzufügen ...

Edit: Habe jetzt auch noch die zusätzlichen VorabEndschalter reingestrickt.

Wieder mal ungetestet:
Code:
VAR_INPUT
    ibAbSG       : BOOL ;             // Taster Ab Schleichgang
    ibAbEG       : BOOL ;             // Taster Ab Eilgang
    ibAufSG      : BOOL ;             // Taster Auf Schleichgang
    ibAufEG      : BOOL ;             // Taster Auf Eilgang
    ibEsAbEnd    : BOOL ;             // EndSchalter Ab
    ibEsAbVorSG  : BOOL ;             // VorEndSchalter Ab bei SG (startet Rampe auf 0)
    ibEsAbVorEG  : BOOL ;             // VorEndSchalter Ab bei EG (startet Rampe auf 0)
    ibEsAufEnd   : BOOL ;             // EndSchalter Auf
    ibEsAufVorEG : BOOL ;             // VorEndSchalter Auf bei SG (startet Rampe auf 0)
    ibEsAufVorSG : BOOL ;             // VorEndSchalter Auf bei EG (startet Rampe auf 0)
    ibTakt       : BOOL ;             // ZeitTakt für Zählen
END_VAR
VAR_OUTPUT
    oiMot        : INT ;              // Ausgabe an Motor (pos. für Auf, neg. für Ab)
END_VAR
VAR // _STATIC !!
    sbTaktAlt    : BOOL ;             // "FlankenMerker" für ZeitTakt  
    siSollAb     : INT ;              // Gedächtnis für SollWert Ab
    siSollAuf    : INT ;              // Gedächtnis für SollWert Auf
END_VAR
VAR_TEMP
    tiSollAbPro  : INT ;              // Vorgabe des ProzentWertes Ab  (0, 1, 100)
    tiSollAufPro : INT ;              // Vorgabe des ProzentWertes Auf (0, 1, 100)
END_VAR

If (ibAufSG OR ibAufEG) AND ibEsAufEnd AND NOT ibEsAufVorSG AND siSollAuf = 0 AND siSollAb = 0 Then
    tiSollAufPro := 1;   // Wert bzw. Index z.B. für 1% - für man. Weiterfahren zwischen VorEndSchalterSG bis EndSchalter
ElsIf ibAufSG AND ibEsAufEnd AND ibEsAufVorSG AND siSollAb = 0 Then
    tiSollAufPro := 10;  // Wert bzw. Index z.B. für 10%
ElsIf ibAufEG AND ibEsAufEnd AND ibEsAufVorEG AND siSollAb = 0 Then
    tiSollAufPro := 100; // Wert bzw. Index für 100%
ElsIf (ibAbSG OR ibAbEG) AND ibEsAbEnd AND NOT ibEsAbVorSG AND siSollAb = 0 AND siSollAuf = 0 Then
    tiSollAbPro  := 1;   // Wert bzw. Index z.B. für 1% - für man. Weiterfahren zwischen VorEndSchalterSG bis EndSchalter
ElsIf ibAbSG AND ibEsAbEnd AND ibEsAbVorSG AND siSollAuf = 0 Then
    tiSollAbPro  := 10;  // Wert bzw. Index z.B. für 10%
ElsIf ibAbEG AND ibEsAbEnd AND ibEsAbVorEG AND siSollAuf = 0 Then
    tiSollAbPro  := 100; // Wert bzw. Index für 100%
Else
    tiSollAufPro := 0;
    tiSollAbPro  := 0;
End_If;

If sbTaktAlt <> ibTakt Then
    sbTaktAlt := ibTakt;

    If siSollAuf < tiSollAufPro Then
        siSollAuf := siSollAuf + 1;
    ElsIf siSollAuf > tiSollAufPro Then
        siSollAuf := siSollAuf - 1;
    End_If;

    If siSollAb < tiSollAbPro Then
        siSollAb := siSollAb + 1;
    ElsIf siSollAb > tiSollAbPro Then
        siSollAb := siSollAb - 1;
    End_If;
End_If;

oiMot := siSollAuf - siSollAb; // alternativ per Tabelle: oiMot := aAuf[(siSollAuf + 4)/5] - aAuf[(siSollAb+4)/5]
// als Beispiel für Skalierung von 100 1%-Schritten auf Arrays[0..20] durch Addition von 4 (wegen "Rundung") und Division durch 5


//  ibEsAbEnd   ibEsAbVorSG   ibEsAbVorEG   ... ...   ibEsAufVorSG   ibEsAufVorEG   ibEsAufEnd
//     |             |             |                        |              |              |
//     |<------------|             |           1.           |              |------------->|
//     |             |             |                        |              |              |
//     |       <-----|======       |           2.           |        ======|----->        |
//     |             |             |                        |              |              |
//     |             |       <-----|======     3.     ======|----->        |              |
//   unten                                                                               oben
//  1. Wird zwischen VorSG und ES in Richtung ES gestartet, so kann nur mit langsamster Stufe gefahren werden.
//  2. Wird VorSG bei gedrückter SG-Taste überfahren, so wird Herunterfahren an Rampe auf 0% gestartet.
//  3. Wird VorEG bei gedrückter EG-Taste überfahren, so wird Herunterfahren an Rampe auf 0% gestartet.
//  Alle (Vor-)Endschalter sind natürlich Richtungs-abhängig wirksam. Ihre Positionen müssen so eingestellt
//  werden, dass das Herunterfahren an der Rampe VOR Erreichen der (nicht-Vor-)Endschalter zum StillStand führt.
 
Zuletzt bearbeitet:
Zurück
Oben