Tutorial KinematicController/de

Einleitung
Diese Anleitung beschreibt, wie man eine einfache Kinematiksteuerung aus ein paar Zeilen Python-Kode erstellt, die mit Baugruppen des Arbeitsbereiches Assembly3 verwendet werden kann.

Jeder Texteditor kann zum Erstellen von Kode genutzt werden. Mein Favorit ist Atom, aber FreeCADs eingebauter Editor funktioniert auch bestens.

Die folgenden Kode-Beispiele können kopiert, in eine leere Textdatei eingefügt und dann unter einem selbstgewählten Namen als oder * abgespeichert werden.

Grundstruktur
Die Grundstruktur besteht aus einer Hauptfunktion und einer Weiche, an der überprüft wird, ob das Makro als Behälter für Klassen, Methoden usw. verwendet wird oder ob es eigenständig läuft. Nur die zweite Möglichkeit startet die Hauptfunktion. Noch ist diese Funktion leer.

Randbedingungen zum Antreiben finden
Die Randbedingungen zum Antreiben (Antriebe) sind Objekte in einem FreeCAD-Dokument. Sie müssen markiert sein, um gefunden zu werden.

Für diese Steuerung muss das Suffix an das Label eines Antriebs  angehängt werden. Es kann zur Verdeutlichung mit einem oder  abgetrennt werden, da nur geprüft wird ob das Label auf  endet.

Eine Funktion, der das Dokumentobjekt übergeben wird und die eine Liste von Antrieben zurück gibt, wird diese Arbeit erledigen.

Die Hauptfunktion lädt das aktive Dokument in die Variable  und ruft im Anschluss die Funktion   (Antriebs-Randbedingungen finden) auf und übergibt den Inhalt von. Die zurückgegebene Liste wird in die Variable abgelegt, die dann überprüft wird, ob sie wenigstens ein Element enthält. Wenn das der Fall ist, wird die Liste im Ausgabefenster angezeigt.

Das Makro bis hierhin...

Steuerungsfenster
Das Steuerungsfenster ist aus Qt-Widgets (window gadgets ~ Fenstergelöt) aufgebaut, einem Hauptfenster und einigen Ein- und Ausgabe-Widgets.

Alle Widgets müssen importiert werden, bevor sie verwendet werden können, aber sie können als ein kompletter Satz importiert werden.

Hauptfenster
Für das Hauptfenster sieht die Import-Zeile so aus:

Das Hauptfenster, genannt, ist ein Klassenobjekt, das eine Instanz des -Widgets ist.

Es enthält zwei init-Methoden. initialisiert das neue Klassenobjekt, verarbeitet eingehende Variablen und startet, die alle Widgets innerhalb des Hauptfensters verwaltet.

Um ein einzelnes Steuerungsfenster zu starten, wird eine Instanz dieser Klasse namens erstellt, mit  (dem Dokumentobjekt) und  (dem ersten Element auf der Liste der Antriebe), die an diese Instanz übergeben wurden. Schließlich öffnet die Methode der Klasse das Dialogfenster.

Um mehr als eine Antrieb zu verwenden, muss die Liste der Antriebe durchgesehen werden und für jedes Element der Liste eine Instanz erstellt werden, der das jeweilige Element übergeben wird.

Diese Zeilen ersetzen den Ausgabebefehl im else-Abschnitt der Hauptfunktion.

Hinweis: Das Zusammenstellen einer (Fensterliste) erlaubt es alle Fenster auf einmal zu starten. (Ich kann dieses Verhalten bisher nicht erklären...)

Lässt man das Makro laufen, erstellt es ein sauberes, leeres Dialogfenster, das auf Widgets wartet.



Das Makro bis hierhin...

Parameter setzen
Nun ist es Zeit die Methode zu befüllen:

The actuator to be used is the first item in the actuators list. (The list contains a single item now, but, if the controller can handle more than one driving constraint in the future, it may hold more.) ((Dieser Abschnitt ist veraltet und muss im Original überarbeitet werden. Das vollständige Makro weiter unten ist korrekt.))

Methode getDriverType
Für spätere Verwendung muss die Art des Antriebs ermittelt werden, Winkel, Abstand oder Länge (Angle, Distance, Length), daher muss eine Methode namens (Finde den Antriebstyp) definiert werden.

Diese Methode überprüft, ob die Art der gegebenen Randbedingung in einer der Listen zu finden ist, und gibt zurück, welche Art Maß zu steuern ist

Es wird angenommen, dass der Antrieb im Kinematikdokument korrekt markiert wurde und funktioniert, wenn er von Hand geändert wird. In diesem Falle ist es nicht nötig geometrische Randbedingungen wie Colinear oder PointsCoincidence herauszufiltern (aber hier wäre die Stelle es zu tun...)

Fenstereigenschaften
Die Fenstergröße wird über seine Größt- und Kleinstmaße festgelegt. Verwendet man jeweils dieselben Werte, wird eine konstante Größe festgelegt

Im Titel findet sich der Name des Antriebs und ob es sich um einen Winkel, einen Abstand oder eine Länge handelt. Schließlich wird das Fenster angewiesen über allen anderen Fenstern (sichtbar) zu bleiben.

Weitere Parameter setzen
Im nächsten Schritt wird der Wert des Antriebs ausgelesen und die Start- und Endwerte abhängig von der Antriebsart gesetzt.

Ein Abstand kann nicht negativ sein und exakt null verwirrt den Gleichungslöser, daher wird der Startwert auf 0,001 gesetzt. Winkel akzeptieren negative Werte und erhalten symmetrische Werte. (Ob Längen negative Werte akzeptieren, muss noch abschließend festgestellt werden...)

Die Einheiten müssen gespeichert werden, bis die Werte am Ende an die Eigenschaften der Randbedingungen zurückgegeben werden. Abstände und Längen benötigen Werte mit Einheiten.

Der Umgang mit Einheiten und die Darstellung der Werte als Zeichenketten in einigen Widgets erfordert es häufig Zahlen in Zeichenketten und wieder zurück zu verwandeln.

Um die Parameter zu vervollständigen, wird ein Wert für die Anzahl von Schritten vorgegeben, die berechnet werden solle, wenn die Bewegung automatisch erfolgt; und wenn der Umschalter auf  gesetzt ist, wird bei jedem Schritt der Bewegung ein Bild aufgenommen.

Label
Nun werden drei Label(-Widgets) hizugefügt, um Start-, End-, sowie den aktuellen Wert anzuzeigen

Zuerst muss die Klasse importiert werden, d. h. die Import-Liste muss, wie hier gezeigt wird, ergänzt werden:

Zurück in der Methode fügt man folgendes ein:

Die Platzierung erfolgt mit der ererbten Methode. In diesem Falle wird die Beschreibung eines Rechtecks verwendet (X-Position, Y-Position, Breite, Höhe)

Die ersten und dritten Zeilen könnten zusammengefasst werden, wird aber aus Gründen der Übersichtlichkeit nicht empfohlen.

Startet man das Makro aus einem Kinematikdokument heraus, erhält man ein Dialogfenster wie dieses:



Das Makro bis hierhin...

Schieberegler
Um den aktuellen Wert auf jede mögliche Zahl zwischen Start- und Endwert einzustellen, würde ein Schieberegler passen.

First the class must be imported i.e. the import list has to be extended like this:

Back in the method and right after the labels section we insert:

The slider button is placed with the method. Its value has to be calculated from the current value and a step ratio. The ratio has to be calculated whenever a start or end value is changed and so we insert another method after the method.

To work with a ratio instead of altering the slider's min and max values has the advantage of a finer resolution for small values.

And after this one comes another method defining what to do when the slider position or the slider value changes. The method is called by the  method which also hands over the slider value as an argument.

It recalculates the current value from the slider position, rewrites the text of the label and changes the constraint property according to the driver type.

Running the command starts the solver to rearrange the assembly parts with the altered value.

The dialog window with the slider should look like this and is ready to control a motion:



We can start a dialog window for any opened document, they won't interfere with each other.

Text entry fields
To set the start and end value we use a line edit widget.

First the class must be imported i.e. the import list has to be extended like this:

Back in the method and between the labels and the slider sections we insert:

The entry fields display the default start and end values. They are not complete until we add the methods to deal with altered entries. This will be done by the methods and  that are inserted between the  and the  methods.

Both convert the received string value to a floating point number and change either or  and the corresponding label accordingly. After that the slider value is updated.

The dialog window with text entry fields should look like this and is ready to change the range of a motion:



And the macro so far...

Motion
To get the assembly in motion we need:
 * Buttons to trigger motion in the desired direction.
 * An input field to alter the number of steps for faster or smoother motions.
 * A check box to indicate if we want to shoot a sequence of images.

Forward and Backward buttons
To move the assembly parts automatically we need two buttons to trigger the motions, one towards the start position and one towards the end position. These two and a close button will use a widget.

Small assemblies compute a bit too fast and show jumps instead of a smooth motion. To slow it down we use the method of the  module which has to be imported first.

Another import and another widget:

Back in the method we insert the buttons after the slider section:

The methods dealing with pressed buttons are, , and. They are inserted after the method.

The method invokes the inherited method  which just closes the dialog window and thereby ends the macro.

Both and  count the steps that are left to go to reach the wanted position and calculate the length of a step according to the number of steps. For now we go with the default number of 10 steps.

Each round on the while loop increases/decreases the current value and updates the slider values which triggers in the background (see Slider paragraph). After a pause to let the computer provide another updated 3D view, counting down the steps left to go finishes the loop.

With no steps left the slider is set to the first/last slider position, just in case if a rounding error had occurred.

The dialog window with buttons should look like this and can now move the assembly by 10 steps towards the wanted start/end position:



And the macro so far...

Number of steps
The default setting is to get a quick impression if the assembly is moving as expected without wasting too much computing time.

If the parts jump rather than move smoothly, or if drivers based on angles tend to cause trouble when the difference between two angles is too large, then both can be fixed by increasing the number of steps.

And so another line edit widget is used to alter the number steps (placed after the existing line edit widgets):

The related method just fills the parameter  with the entered value. It is inserted after the method.

The dialog window able to change the number of steps should look like this:



Image sequence
When the motion of our assembly meets our expectations, we can take a picture of each step. The resulting sequence of images can be used to create a short gif animation.

To implement this functionality we need a widget, and a directory to store the images.

One more import and widget:

Back in the method we insert the check box after the slider section:

The method synchronises the parameter  and the display of the check mark.

To define the output parameters we use the method :

First the image path has to be adapted to your OS; the last part is the image name without current number and file tag. This must be done manually for now.

Then follow file tag to finish the image name, image height and width, and how the background should be filled ( (3D view background),, , or ).

To always have a 3 digit number leading zeros have to be prefixed to the counter parameter.

Finally the scripted version of the command Std ViewScreenShot is used to take a picture based on the mentioned parameters.

Still no pictures taken!?! No problem, as this method doesn't get called yet, and so we need to insert a call in the while loop of and. Right after we insert this line:

Now the macro should be ready to control an assembly and to take pictures for an animated gif.

The final version of the dialog window:



And finally the whole macro

Don't forget to set the path in the output method!

Some imperfections

 * The order of the image sequence is reversed as we use the variable steps_left which is counted down.
 * The image directory and the image name are hard-coded.
 * Multiple Controllers are not synchronised.