# igus D1 über CANopen - Homing funktioniert - andere Modi nicht



## LeFish (5 September 2020)

Hallo allerseits,

ich hab eine igus D1 Motorsteuerung an einer Beckhoff CX9020 mit EL6751 als CANopen Master hängen.

igus D1 Handbuch: https://igus.widen.net/content/kda7...=Vo3t8rhefqmfyd0rbZhwfQ6lVO0ktmDGNsgAuKxI81o=

Die Kommunikation mit der D1 funktioniert, ich kann alle SDOs auslesen und beschreiben.

Als Start habe ich ein Homing-Programm erstellt:

Mittels Methode werden die SDOs dazu auf der D1 geschrieben und das Homing getriggert:


```
METHOD PUBLIC Start_Homing
VAR_INPUT
    Feed_constant_Feed                    : UDINT;        // Vorschub des Antriebs
    Feed_constant_Shaft_revolutions        : UDINT;        // Motorwellendrehzahl
    Switch_Search_VEL                    : UDINT;        // Endlagenschaltersuchgeschwindigkeit 
    Zero_Search_VEL                        : UDINT;        // Nullpunktsuchgeschwindigkeit
    Homing_ACC                            : UDINT;        // Beschleunigung/Verzögerung für die Referenzfahrt
END_VAR

IF NOT _Homing.x_inProgress THEN
    //Set parameters for Homing lt D1 Handbuch S 88
    _dict.Feed_constant_Feed_RWW := Feed_constant_Feed;
    _dict.Feed_constant_Shaft_revolutions_RWW := Feed_constant_Shaft_revolutions;
    _dict.Homing_speeds_Speed_during_search_for_switch_RWW := Switch_Search_VEL;
    _dict.Homing_speeds_Speed_during_search_for_zero_RWW := Zero_Search_VEL;
    _dict.Homing_acceleration_RWW := Homing_ACC;
    _dict.Modes_of_operation_RWW := 6;
    //Trigger FB_Homing
    _Homing.x_StartHoming := TRUE;
END_IF
```

Das Homing selbst habe ich als State-Machine in einem zyklisch (Zykluszeit 1ms) aufgerufenen Funktionsblock abgebildet:


```
FUNCTION_BLOCK FB_Homing
VAR_INPUT
    _p_statusword            : POINTER TO _D1_states;            // Pointer zum Statusword (_status    in FB_D1)
    _p_controlword            : POINTER TO _D1_controls;            // Pointer zum Controlword
    CAN_is_inSync            : BOOL;
END_VAR
VAR_OUTPUT
END_VAR
VAR
    state                    : BYTE := 0;
    _x_isHomed                : BOOL;
    _x_StartHoming            : BOOL;
    _x_inProgress            : BOOL;
    _fb_Delay                : TON;                // Verzögerung für 100ms
    _str_Status_User        : STRING;            // User-friendly status messages
    _fb_Toggle                : BLINK;
END_VAR

// External Trigger Homing Action
IF _x_StartHoming AND NOT _x_inProgress THEN
    _x_StartHoming := FALSE;
    _x_isHomed := FALSE;
    _x_inProgress := TRUE;
    state := 1; // Start Homing
END_IF

CASE state OF
    
    0:
        //do nothing - no Homing required
        
    1:
        IF CAN_is_inSync THEN
            state := state + 1;
        END_IF
        
    2: 
        // Rücksetzen von delay
        _fb_Delay(IN := FALSE);
        // Starte Homing durch setzen von Controlword bit 4
        _p_controlword^.x_04_Mode_specific := TRUE;
        state := state + 1;
        
    3:
        //Warte für 100 ms.
        _fb_Delay(IN := TRUE, PT := T#100MS);
        IF _fb_Delay.Q THEN
            state := state + 1;
            // Rücksetzen von delay
            _fb_Delay(IN := FALSE);
        END_IF
        
    4:
        //Failsafe: Toggle Bit 4 Controlword um zu starten
        _fb_Toggle(ENABLE := TRUE, TIMELOW := T#50MS, TIMEHIGH := T#50MS);
        _p_controlword^.x_04_Mode_specific := _fb_Toggle.OUT;
        // Überprüfe ob Referenzfahrt im Gange?
        // Lt. D1 Handbuch S 88
        IF     NOT _p_statusword^.x_12_Operation_mode_specific AND 
            NOT _p_statusword^.x_10_Target_reached THEN
                _str_Status_User := 'Das Homing wird ausgeführt.';
                state := state + 1;
        END_IF
        
    5:
        //Überprüfe ob Referenzfahrt beendet?
        IF         _p_statusword^.x_12_Operation_mode_specific AND 
                _p_statusword^.x_10_Target_reached THEN
                _str_Status_User := 'Das Homing wurde erfolgreich ausgeführt.';
                state := state + 1;
        END_IF
        
    6:
        //Beende Homing process
        _p_controlword^.x_04_Mode_specific := FALSE;
        _x_isHomed := TRUE;
        _x_inProgress := FALSE;
        state := 0; // Reset Statemachine
END_CASE
```

Wie schon gesagt, das Homing funktioniert!

Was nicht funktioniert, sind alle anderen Modi. Das sind in meinem Fall:


Profile Position Mode (absolut und relativ) 
Profile Velocity Mode 

z.B.: für den Profile Position Mode (relativ) habe ich eine ähnliche Routine:

Methode zum Triggern:


```
METHOD PUBLIC GoToPosition_Rel
VAR_INPUT
    Feed_constant_Feed                    : UDINT;        // Vorschub des Antriebs
    Feed_constant_Shaft_revolutions        : UDINT;        // Motorwellendrehzahl
    Target_Position                        : DINT;            // Angabe der neuen Zielposition
    Profile_Velocity                    : UDINT;         // Geschwindigkeit
    Profile_Acceleration                : UDINT;        // Beschleunigung
    Profile_Deceleration                : UDINT;        // Verzögerung
END_VAR


//Set parameters for Profile Position Mode lt D1 Handbuch S 89
_dict.Feed_constant_Feed_RWW := Feed_constant_Feed;
_dict.Feed_constant_Shaft_revolutions_RWW := Feed_constant_Shaft_revolutions;
_dict.Target_position_RWW := Target_Position;
_dict.Profile_velocity_RWW := Profile_Velocity;
_dict.Profile_acceleration_RWW := Profile_Acceleration;
_dict.Profile_deceleration_RWW := Profile_Deceleration;
_dict.Modes_of_operation_RWW := 1;
_control.x_06_Mode_specific := TRUE;    // Control Bit 6 true-> Relativpositionierung
//Trigger FB_GoToPos_Abs
_RelPos.x_StartPos := TRUE;
```

Und FB für die State-Machine:


```
FUNCTION_BLOCK FB_GoToPos_Rel
VAR_INPUT
    _p_statusword            : POINTER TO _D1_states;            // Pointer zum Statusword (_status    in FB_D1)
    _p_controlword            : POINTER TO _D1_controls;            // Pointer zum Controlword
    CAN_is_inSync            : BOOL;
END_VAR
VAR_OUTPUT
END_VAR
VAR
    state                        : BYTE := 0;
    _x_StartPos                    : BOOL;
    _x_inProgress                : BOOL;
    _fb_Delay                : TON;                // Verzögerung für 10ms
    _str_Status_User            : STRING;            // User-friendly status messages
    _fb_Toggle                    : BLINK;
END_VAR

// External Trigger Homing Action
IF _x_StartPos AND NOT _x_inProgress THEN
    _x_StartPos := FALSE;
    _x_inProgress := TRUE;
    state := 1; // Start
END_IF

CASE state OF
    
    0:
        //do nothing - no Positioning required
        
    1:
        IF CAN_is_inSync THEN
            state := state + 1;
        END_IF
        
    2: 
        //Failsafe: Toggle Bit 4 Controlword um zu starten
        _fb_Toggle(ENABLE := TRUE, TIMELOW := T#50MS, TIMEHIGH := T#50MS);
        _p_controlword^.x_04_Mode_specific := _fb_Toggle.OUT;
        
        IF NOT _p_statusword^.x_10_Target_reached THEN
            //Motor in Bewegung
            state := state + 1;
        END_IF
        
    3:
        //Warte für 100 ms.
        _fb_Delay(IN := TRUE, PT := T#100MS);
        IF _fb_Delay.Q THEN
            state := state + 1;
        END_IF
        
    4:
        // Rücksetzen von delay
        _fb_Delay(IN := FALSE);
        _p_controlword^.x_04_Mode_specific := FALSE;
        IF NOT _p_statusword^.x_10_Target_reached AND _p_statusword^.x_12_Operation_mode_specific THEN
            // Rücksetzen von Startbit
            _str_Status_User := 'Positionierung im Gange.';
        END_IF
        IF _p_statusword^.x_10_Target_reached THEN
            // Ziel erreicht
            _str_Status_User := 'Position erreicht.';
            state := state + 1;
        END_IF
        
    5:
        //Beende Positioning process
        _x_inProgress := FALSE;
        state := 0; // Reset Statemachine

END_CASE
```

Ich habe mich auf folgenden Ablauf lt. Handbuch für einen Positioniervorgang gehalten (S.89 im D1 Handbuch):
Anhang anzeigen 50960


Lt. der Beschreibung sollte nach Setzen des Startbefehls Bit4 das Bit 10 im Statusword (Target reached) auf FALSE gesetzt werden. Das passiert aber nicht. Sehrwohl wird jedoch Bit 12 (New Setpoint) auf TRUE gesetzt.

Das sind die SDOs auf der D1:
Anhang anzeigen 50961


Und das ist der Zustand nach setzen von Bit 4 (Startbefehl)
Anhang anzeigen 50962


Der Motor dreht sich nicht.

Hat jemand einen Rat, was ich falsch mache?

Danke!

Beste Grüße
LeFish


----------



## oliver.tonn (6 September 2020)

Wo setzt Du denn _x_StartPos?
Nur wenn der gesetzt wird läuft ja Deine Schrittkette los.


----------



## oliver.tonn (6 September 2020)

Was mir noch auffält, Du machst den klassischen Anfängerfehler. Du rufst Timer und ähnliche FBs innerhalb der CASE-Anweisung (Gilt auch für IF-Anweisungen und Schleifen) auf, anstatt nur die Eingänge zu setzen und die Ausgänge auszuwerten und den Aufruf außerhalb zu machen. Was gibt Dein _fb_Toggle denn als erstes aus, high oder low Signal?


----------



## StructuredTrash (6 September 2020)

oliver.tonn schrieb:


> Wo setzt Du denn _x_StartPos?


In der Triggermethode.

Ich nehme mal an, dass Du irgendeinen ADS Write-FB benutzt, um die CoE-Einträge zu beschreiben. Das läuft ja asynchron zum PLC-Programm. Hast Du sichergestellt, dass alle Parameter geschrieben wurden, bevor du Bit 4 im Controlword setzt?


----------



## oliver.tonn (6 September 2020)

Außerdem befolgst Du die Anweisung von igus nicht. Laut dem Ausschnitt des Handbuchs soll das Startbit erst zurückgesetzt werden, wenn der Controller eine Rückmeldung geschickt hat. Vielleicht reichen ihm 50ms nicht.
Wieso nutzt Du übrigens SDOs und machst das nicht über PDOs? Da sollte es eigentlich eine passende PDO Konfiguration für geben oder man kann sie erstellen (Weiß leider nicht was bei igus möglich ist).
Bei der Gelegenheit, wofür brauchst Du eine so schnelle Task?


----------



## LeFish (6 September 2020)

StructuredTrash schrieb:


> Ich nehme mal an, dass Du irgendeinen ADS Write-FB benutzt, um die  CoE-Einträge zu beschreiben. Das läuft ja asynchron zum PLC-Programm.  Hast Du sichergestellt, dass alle Parameter geschrieben wurden, bevor du  Bit 4 im Controlword setzt?



Wie du richtig geschrieben hast benutze ich ADSREAD und ADSWRITE.

Ich schreibe zyklisch veränderungen und lese zyklisch alle SDOs aus. Es wird verglichen, ob meine Änderungen auch tatsächlich von der igus D1 übernommen wurde. Erst wenn das der Fall ist wird die VAR_INPUT

```
CAN_is_inSync            : BOOL;
```

auf TRUE gesetzt.

Die wird im Schritt 1 überprüft.


----------



## LeFish (6 September 2020)

oliver.tonn schrieb:


> Außerdem befolgst Du die Anweisung von igus nicht. Laut dem Ausschnitt des Handbuchs soll das Startbit erst zurückgesetzt werden, wenn der Controller eine Rückmeldung geschickt hat. Vielleicht reichen ihm 50ms nicht.



Beim testen der Homing funktion bin ich daufgekommen, dass die igus D1 manchmal "fehlzündungen" hat. Es sind also alle Register gesetzt, und das Startbit 4 auf TRUE, aber es rührt sich nix. Erst wenn ich es wieder auf FALSE und wieder auf TRUE setze beginnt die Bewegung. Daher der 50 ms Blinker.

Ich habe die Werte sogar schon mal händisch gesetzt und erst dann das Startbit gesetzt. Hat sich auch nix gerührt. Außer wie schon oben beschrieben, Bit 12 im Statusword ist auf TRUE gegangen.



oliver.tonn schrieb:


> Wieso nutzt Du übrigens SDOs und machst das nicht über PDOs? Da sollte  es eigentlich eine passende PDO Konfiguration für geben oder man kann  sie erstellen (Weiß leider nicht was bei igus möglich ist).



Die igus D1 bietet max. 4 TX und 4 RX PDOs mit je 4 Byte an. Das ist zu wenig, um alle Werte (BeschleunigungEN, GeschwindigkeitEN etc) für alle Modi zu übertragen. Die meisten Werte sind 32bit Werte.



oliver.tonn schrieb:


> Bei der Gelegenheit, wofür brauchst Du eine so schnelle Task?



Auf 1 ms Zykluszeit bin ich gegangen, damit das Lesen und Schreiben der SDOs über ADSREAD und ADSWRITE möglichst schnell geht. Ich lese ja zyklisch alle 25 Werte aus und damit da nicht zu viel Zeit verloren geht  (ADS.busy == FALSE, aber Programm steht) bin ich auf 1 ms gegangen.
 Mein gesamtes Programm braucht nämlich bis jetzt ca. 300 µs.


----------



## oliver.tonn (6 September 2020)

LeFish schrieb:


> Wie du richtig geschrieben hast benutze ich ADSREAD und ADSWRITE.
> 
> Ich schreibe zyklisch veränderungen und lese zyklisch alle SDOs aus



Wie StructuredTrash schon schrieb tust Du das nicht, die SDO Kommandos arbeiten immer azyklisch. Zyklisch geht es nur mit den SDOs.


----------



## LeFish (6 September 2020)

oliver.tonn schrieb:


> Wie StructuredTrash schon schrieb tust Du das nicht, die SDO Kommandos arbeiten immer azyklisch. Zyklisch geht es nur mit den SDOs.



Mit zyklisch meine ich, dass ADSREAD zyklisch aufgerufen wird. Und wenn ADSREAD einen Wert (ADSREAD nicht mehr busy) hat, wird er der entsprechenden Variable übergeben und der Wert  der nächsten Adresse ausgelesen.

Das Auslesen aller SDOs dauert ca. 200 ms. Wenn alle ausgelsenen sind beginnt der Spaß von vorne. Vor jedem auslesen werden alle SDOs, bei denen sich ein Wert geändert hat beschrieben.
Mit dem Auslesen danach vergleiche ich, ob die D1 den Wert auch übernommen hat. Dann wird x_Sync auf TRUE gesetzt.

Ich hab lange gebraucht, um das stabil hinzubekommen. Ich bin mir jetzt aber sicher, dass das Beschreiben und Auslesen der SDOs stabil funktioniert.


----------



## LeFish (6 September 2020)

Was ich noch anmerken wollte: 
Das Controlword und das Statusword der D1 hab ich als PDOs mittels %I* bzw %Q* auf Variablen gelegt.
Damit ist sichergestellt, dass die beiden Werte in Echtzeit (-> zyklisch, synchron) über den Bus ausgetauscht werden.


----------



## oliver.tonn (6 September 2020)

LeFish schrieb:


> Was ich noch anmerken wollte:
> Das Controlword und das Statusword der D1 hab ich als PDOs mittels %I* bzw %Q* auf Variablen gelegt.
> Damit ist sichergestellt, dass die beiden Werte in Echtzeit (-> zyklisch, synchron) über den Bus ausgetauscht werden.


Schade, das wäre meine nächste Idee gewesen.
Ich kenne mich mit dem CAN-Bus nicht so aus, daher kann das Folgende völliger Blödsinn sein. Du schreibst ja Daten auf mehrere SDOs und liest diese dann aus, um zu prüfen, ob diese geschrieben wurden. Die Frage wäre jetzt, ob der Controller diese dann auch schon verarbeitet hat. Nicht das Du alle Daten zwar geschrieben hast, der Controller diese aber noch nicht verarbeitet hatte und schon ein Startsignal bekommt.


----------



## StructuredTrash (6 September 2020)

Das Toggeln des Start-Bits könnte ein Problem sein. Der Befehl wird zwar nur beim High Trigger übernommen, aber danach solltest Du das Bit High lassen, bis das Bestätigungs-Bit im Status Word kommt.
Denkbar ist auch, dass der Antrieb Profilparameter nur akzeptiert, wenn der Operation Mode dazu passt. Übertrag den mal als erstes.

Ansonsten schliesse ich mich Oliver an. PDOs sind die bessere Wahl. Die Parameter für PP und PV kriegst Du doch in 32 Bytes unter. z. B.:
PDO 1:
Control Word
Operation mode
PDO2:
Target position
Profile velocity für PP
PDO3:
Profile acceleration
Profile deceleration
PDO4:
Target velocity für PV
Alles andere würde ich konstant im Antrieb einstellen.


----------



## LeFish (6 September 2020)

oliver.tonn schrieb:


> Nicht das Du alle Daten zwar geschrieben hast, der Controller diese aber  noch nicht verarbeitet hatte und schon ein Startsignal bekommt.





StructuredTrash schrieb:


> Das Toggeln des Start-Bits könnte ein Problem sein. Der Befehl wird zwar nur beim High Trigger übernommen, aber danach solltest Du das Bit High lassen, bis das Bestätigungs-Bit im Status Word kommt.
> Denkbar ist auch, dass der Antrieb Profilparameter nur akzeptiert, wenn der Operation Mode dazu passt. Übertrag den mal als erstes.



Ich habe schon versucht alle Werte händisch zu setzen. Ich habe es genau in der Reihenfolge der Anleitung gemacht (zuerst Mode_of_Operation auf 1):
	

		
			
		

		
	

Anhang anzeigen 50964


Danach habe ich händisch Bit 4 im Controlword gesetzt. Nicht getoggelt.

Es ging Bit 12 im Statusword auf TRUE, Bit 10 jedoch nicht wie in der Beschreibung steht auf FALSE. Der Motor drehte nicht.
Setzte ich danach Bit 4 im COntrolword wieder auf FALSE, ging auf Bit 12 im Statusword wieder auf FALSE.

Irgendeinen Trick gibt es noch, der nicht eindeutig im Handbuch beschrieben ist.

Das Homing funktioniert bei mir interessanterweise sowohl händisch als auch aus dem Programm heraus nach obigem Ablauf.



StructuredTrash schrieb:


> Ansonsten schliesse ich mich Oliver an. PDOs sind die bessere Wahl. Die  Parameter für PP und PV kriegst Du doch in 32 Bytes unter.



Das alles mit PDOs zu lösen war auch mein erster Zugang. Hätte mir da viel Arbeit erspart...
Lt. igus Support habe ich nur 16 Byte Receive und 16 Byte Transmit PDOs. Das heißt ich hab noch 3 RxPDOs vollkommen frei. Das wären 3 32bit Werte. Um den PP-Mode vollständig zu parametrieren brauch ich aber mind. 4 32 bit Werte. Erst in einigen Wochen soll laut igus ein Update kommen, wo sie die PDOs aufreißen und dann auch zB den Motorstrom über den Bus verfügbar machen. 
So lange kann ich aber nicht warten. 
Generell: Warum zB die Beschleunigungen und Geschwindigkeiten unbedingt (U)DINTs sein müssen versteh ich nicht, muss ich aber glaub ich auch nicht verstehen...



Außerdem ist das parametrieren von PDOs mit der Beckhoff EL6751 soweit ich bis jetzt weiß eine Wissenschaft für sich...
https://download.beckhoff.com/download/document/io/ethercat-terminals/el6751de.pdf
Anhang anzeigen 50965



Von Seiten igus sagte man mir auch: Lösen Sie das doch mit SDOs.


----------



## LeFish (6 September 2020)

Ich habe soeben einen Beitrag von Anfang 2020 gefunden. Scheinbar bin ich nicht der einzige mit dem Problem bei der igus D1.
https://www.sps-forum.de/feldbusse/...en-tia-portal-pn-link-igus-d1.html#post745842

VIELLEICHT
überschreibt das defaultmäßig gemappte "Target Position" PDO den Wert des Objekts, den ich mittels ADSWRITE setze. 
-> solange ich das PDO nicht beschreibe bleibt der Wert auf 0 und mein mittels ADSWRITE gesetzter Wert wird von der D1 nicht übernommen.

Defaultmäßig sind foldende PDOs gemappt - ich habe das Mapping nicht verändert:

RxPDO1.1: 0x6040h Controlword
RxPDO2.1: 0x6040h Controlword
*RxPDO2.2: 0x607Ah Target Position*
RxPDO3.1: 0x6040h Controlword
RxPDO3.2: 0x60FFh Target Velocity
RxPDO4.1: 0x6040h Controlword

TxPDO1.1: 0x6041h Statusword
TxPDO2.1: 0x6041h Statusword
TxPDO2.2: 0x6064h Position Actual Value
TxPDO3.1: 0x6041h Statusword
TxPDO3.2: 0x606Ch Velocity Actual Value
TxPDO4.1: 0x6041h Statusword

Meine Hoffnung ist, dass die PDOs meine über ADSWRITE gesetzten Objekte überschreiben, obwohl ich die entsprechenden PDOs nicht in meinem Projekt verknüpft habe.
Das werde ich morgen testen und berichten.

Interessanterweise kommt aber auf der Adresse 0x6064h über mein SDO_Read ein Wert an, der während des Homings hinaufzählt. Also ADSREAD geht scheinbar...


----------



## StructuredTrash (6 September 2020)

LeFish schrieb:


> VIELLEICHT
> überschreibt das defaultmäßig gemappte "Target Position" PDO den Wert des Objekts, den ich mittels ADSWRITE setze.
> -> solange ich das PDO nicht beschreibe bleibt der Wert auf 0 und mein mittels ADSWRITE gesetzter Wert wird von der D1 nicht übernommen.


Das wird es wohl sein. Werte, die Du nicht per PDO schreiben willst, dürfen dort auch nicht aufgeführt sein.
Du könntest auf der Beckhoff-Seite alle PDOs löschen, in der Hoffnung, dass der Antrieb damit auch sein Default Mapping verwirft.
Oder die Target Position doch per PDO schreiben.

PS: 16 Byte in/out sind natürlich mager. Damit ist der Antrieb für PP eigentlich ungeeignet.


----------



## LeFish (6 September 2020)

StructuredTrash schrieb:


> Das wird es wohl sein. Werte, die Du nicht per PDO schreiben willst, dürfen dort auch nicht aufgeführt sein.



Ich hoffe auch, dass es das war.
Ich bin ein Neuling beim CAN Bus, darum die Frage: Zieht PDO immer vor SDO?

Wenn ich online auf die Register schaue, sehe ich ja, dass zB Target Position scheinbar durch ADSWRITE gesetzt wurde:


----------



## StructuredTrash (6 September 2020)

Schon seltsam. Aber versuch es mal mit der Target Position auf dem dafür vorgesehenen PDO statt per SDO.


----------



## LeFish (6 September 2020)

StructuredTrash schrieb:


> PS: 16 Byte in/out sind natürlich mager. Damit ist der Antrieb für PP eigentlich ungeeignet.



Das finde ich auch. Vorallem, da igus viele Werte auf 4 Byte ausgedehnt hat. Welcher Motor schafft eine Beschleunigung von 4.294.967.295 ? Der Wert ist einheitenlos, da entweder mm/s^2 oder °/s^2 gelten... Aber wurscht für beide würden kA über den Controller fließen. Da sind die möglichen 21 A dann doch begrenzend.


----------



## StructuredTrash (7 September 2020)

LeFish schrieb:


> Das finde ich auch. Vorallem, da igus viele Werte auf 4 Byte ausgedehnt hat.


Das war nicht Igus, sondern es ist im CiA DS402 Antriebsprotokoll so definiert. Bei manchen Reglern kann man die Einheiten frei skalieren und sogar direkt mit Geberinkrementen arbeiten. Da sind 32 Bit schon angebracht.


----------



## LeFish (8 September 2020)

Mittlerweile kann ich andrehen.

Mein Fehler war, dass ich den Vorschub (Feed_constant_Feed) mit 1 definiert habe. Nachdem ich hier die für die Rotation erforderlichen 360°/Umdrehung definiert habe geht es - mehr schlecht als recht jedoch.

Das Problem ist, dass das Target reached Bit 10 im Statusword nicht auf 0 wechselt nachdem ich ihm über Bit 4 den Startbefehl gegeben habe. Nach ca. 1,5 s (!!!) wird dann das New Setpoint Bit 12 im Statusword TRUE, der Motor fährt dann.

Es passt also nicht mit der im Handbuch beschriebenen Abfolge zusammen.

Die State-Machine für die relativ-positionierung lautet folgendermaßen, wobei CANComm_SYNC bei jedem erfolgreichen Sync der PDOs (Zykluszeit 10ms) zwischen TRUE und FALSE toggelt (macht die EL6751).


```
// External Trigger Position Action
IF _x_StartPos AND NOT _x_inProgress THEN
    _x_StartPos := FALSE;
    _x_inProgress := TRUE;
    _x_PosReached := FALSE;
    state := 1; // Start
END_IF

_r_trig(CLK := CANComm_SYNC);
_f_trig(CLK := CANComm_SYNC);

CASE state OF
    
    0:
        //do nothing - no Positioning required
        
    1:
        IF CAN_is_inSync THEN
            // Rücksetzen von Startbit
            _p_controlword^.x_04_Mode_specific := FALSE;
            state := state + 1;
        END_IF
        
    2:
        //Einen vollen CAN-Update Zyklus warten
        IF _r_trig.Q THEN
            state := state + 1;
        END_IF
        
    3: 
        //Einen vollen CAN-Update Zyklus warten
        IF _f_trig.Q THEN
            state := state + 1;
        END_IF
        
    4:
        //Starte Bewegung
        _p_controlword^.x_04_Mode_specific := TRUE;
        state := state + 1;
        
    5:
        IF NOT _p_statusword^.x_10_Target_reached OR _p_statusword^.x_12_Operation_mode_specific THEN    // Bit 10 wird nicht immer zuverlässig FALSE, daher die verorderung.
            //Motor in Bewegung
            _str_Status_User := 'Positionierung im Gange.';
            state := state + 1;
        END_IF
        
    6:
        _p_controlword^.x_04_Mode_specific := FALSE;

        IF _p_statusword^.x_10_Target_reached THEN
            // Ziel erreicht
            _str_Status_User := 'Position erreicht.';
            state := state + 1;
        END_IF
        
    7:
        //Beende Positioning process
        _x_inProgress := FALSE;
        _x_PosReached := TRUE;
        state := 0; // Reset Statemachine

END_CASE
```

Ich hab Echtzeit Traces von dem Bewegungsvorgang gemacht:

Parameter:


```
Feed_constant_Feed : UDINT := 360;                                // Vorschub des Antriebs
Feed_constant_Shaft_revolutions : UDINT := 1;                  // Motorwellendrehzahl
Velocity          : UDINT := 3000;
Acceleration    : UDINT := 10000;
Deceleration    : UDINT := 10000;
                                           
Target_Position      : DINT := 100000;
```

X_StartPos Triggert die Statemachine für die Relativpositionierung.




Was auffällig ist, dass Zustand 5 ca. 1,5 s dauert. Hier warte ich entweder dass Bit 10 FALSE wird oder Bit 12 TRUE, da Bit 10 nicht immer FALSE wird.




Das Bit 4 im Controlword (Startbefehl) wird sofort nach Start der State-Machine auf TRUE gesetzt.




Bei dieser längeren Positionierfahrt wird Bit 10 auf FALSE gesetzt, wenn Bit 12 auf TRUE gesetzt wird. (Lt. Handbuch). Bei kürzeren Fahrten (Targetposition 1000 Statt 100000) wird Target reached gar nie auf FALSE gesetzt.
Interessant bleibt das Delay von ca. 1,5 s.




Das Verhalten ist unabhängig davon, ob ich SDOs zyklisch, asynchron auslese ( ca. 3% Buslast) oder überhaupt keine nennenswerte Buslast außer PDOs verursache.

Hat jemand ein solches Verhalten bei der igus schonmal beobachten können?


----------



## StructuredTrash (9 September 2020)

Das Target Reached-Bit wird gesetzt, wenn der Antrieb sein In Position-Fenster erreicht. Bei den meisten Antrieben ist dieses Fenster in der Default-Einstellung sehr großzügig bemessen.
Grundsätzlich ist das Bit mit Vorsicht zu geniessen. Wenn Du während einer Positionierung das Drive Halt-Bit im Steuerwort setzt und der Antrieb dann zum Stillstand kommt, wird es auch gesetzt, obwohl die Sollposition noch nicht erreicht ist.

Zu der Verzögerung kann ich nichts sagen, weil ich den Antrieb nicht kenne.

PS: Mein letzter Gedanke ging in die falsche Richtung, weil ich EtherCat-CoE-Antriebe vor Augen hatte. Bei CanOpen werden die PDOs in der Regel aber nicht zyklisch, sondern nur bei Änderung ihres Inhaltes übertragen. Nicht verwendete PDOs stören dort also nicht.


----------



## LeFish (9 September 2020)

Ich kann mit der igus D1 nur über das Target Reached Bit während der Bewegung kommunizieren. Auszug aus der igus Doku:




Derzeitiges Fazit:



Der Support von igus sagt mir, dass sie kein Referenzprojekt haben, bei dem eine igus D1 mit einer Beckhoff Hauptsteuerung über CANopen kommuniziert hat. Sie haben nur Modbus TCP über eine Eaton Steuerung in CodeSys. 
Der Systemintegrator von igus rät mir in einem Telefonat ab die D1 für meinen Anwendungsfall prinzipiell zu verwenden, er kennt niemanden, der Beckhoff <-> igus D1 über CANopen überhaupt schon implementiert hat. Er hatte schonmal probiert mehrere Achsen mittels Modbus TCP zu steuern, scheiterte mit der igus D1 und wechselte zu Festo (Type nicht bekannt). 
Habe ein Public Azure Projekt zu meinem derzeitigen Stand angelegt (igus wird sich  - hoffentlich - freuen) - vielleicht entsteht ja in der Community was Schönes: https://dev.azure.com/johannesf/igusD1 
Ich bin auf Beckhoff gewechselt, kann die Achsen simulieren und komme damit bei meinem Projekt weiter. 
Das wars vorerst mit dem Ausflug: igus D1 

PS: Auch wenn ich glaube, dass die D1 durchaus Potenzial hat (21 A Motorstrom - da geht was), glaube ich, dass die Integration nicht optimal ist. Zuweilen igus ja damit wirbt, dass Beckhoff Kommunikation einfach integrierbar ist:



Wie schon gesagt, man konnte mir kein TwinCAT Referenzprojekt mit CANopen liefern.

Ich persönlich bin jetzt auf Beckhoff gegangen (EL7047) und bin heute schon, ohne überhaupt Hardware vorliegen zu haben schon mit der Achse virtuell gefahren.

Das macht Spaß und bringt das Projekt weiter.

Würd mich dennoch über rege Beteiligung an dem Projekt "igus über CANopen an Beckhoff EL 6751" freuen: https://dev.azure.com/johannesf/igusD1

Admin: "Thread closed"

Beste Grüße
LeFish


----------

