Vismach is a set of Python functions that can be used to create and animate models of machines.

In diesem Kapitel geht es um die in Qt eingebettete Version von Vismach, siehe auch: https://sa-cnc.com/linuxcnc-vismach/ .

1. Einführung

Vismach zeigt das Modell in einem 3D-Ansichtsfenster (engl. viewport) an und die Modellteile werden animiert, wenn sich die Werte der zugehörigen HAL-Pins ändern.

QtVismach 3D Ansicht
Abbildung 1. QtVismach 3D Ansicht

Das Vismach 3D Ansichtsfenster (engl. viewport view) kann wie folgt manipuliert werden:

  • zoom by scroll wheel

  • pan by middle button drag

  • rotate by right-button drag

  • tilt by left button drag

Ein Vismach-Modell hat die Form eines Python-Skripts und kann die Standard-Python-Syntax verwenden.

Das bedeutet, dass es mehr als eine Möglichkeit gibt, das Skript zu gestalten, aber in den Beispielen in diesem Dokument wird die einfachste und grundlegendste davon verwendet.

Die grundlegende Reihenfolge bei der Erstellung des Vismach-Modells ist:

  1. Erstellen der Teile

  2. Definieren, wie sie sich bewegen

  3. In Bewegungsgruppen zusammenstellen

2. Hierarchie des Maschinendesigns

Das Modell folgt einer logischen Baumstruktur.

Stellen Sie sich einen Baum vor, mit Wurzel/Stamm, Ästen und kleineren Zweigen. Wenn Sie den größeren Ast bewegen, dann bewegen sich die kleineren Äste mit, aber wenn Sie den kleineren Ast bewegen, so bewegen sich die größeren dennoch nicht.

Das Maschinendesign folgt diesem konzeptionellen Design.

Nehmen wir als Beispiel die Fräse, die im obigen Bild des 3D-Ansichtsfensters zu sehen ist:

  • Wenn Sie X bewegen, kann sie sich von selbst bewegen,

  • aber wenn Sie die Baugruppe Y verschieben, wird auch die Baugruppe X verschoben, da sie mit der Baugruppe Y verbunden ist.

Für diese Maschine sieht der Baum so aus:

model
  |
  |---frame
  |     |
  |     |---base
  |     |
  |     |---column
  |     |
  |     |---top
  |
  |---yassembly
  |      |
  |      |---xassembly
  |      |      |
  |      |      |---xbase
  |      |      |
  |      |      |---work
  |      |
  |      |---ybase
  |
  |---zassembly
          |
          |---zframe
          |     |
          |     |---zbody
          |     |
          |     |---spindle
          |
          |---toolassembly
                    |
                    |---cat30
                    |
                    |---tool
                         |
                         |---tooltip
                         |
                         |---(tool cylinder function)

Wie Sie sehen, muss das niedrigste Teil zuerst existieren, bevor es mit anderen zu einer Baugruppe zusammengefasst werden kann. Sie bauen also vom niedrigsten Punkt im Baum aufwärts und fügen sie zusammen.

Dasselbe gilt für jede Art von Maschinenkonstruktion: Schauen Sie sich das Beispiel des Maschinenarms an, und Sie werden sehen, dass er mit der Spitze beginnt und sich zum größeren Teil des Arms hinzugesellt, um sich schließlich mit der Basis zu vereinen.

3. Starten des Skripts

Zum Testen ist es nützlich, die Shebang-Zeile #!/usr/bin/env python3 einzufügen, damit die Datei direkt von der Kommandozeile ausgeführt werden kann.

Zunächst sind die erforderlichen Bibliotheken zu importieren.

#!/usr/bin/env python3

import hal
import math
import sys

from qtvcp.lib.qt_vismach.qt_vismach import *

4. HAL-Pins.

Ursprünglich erforderte die Vismach-Bibliothek die Erstellung einer Komponente und den Anschluss von HAL-Pins zur Steuerung der Simulation.

qt_vismach kann die HAL-Systempins direkt lesen oder, wenn Sie es wünschen, separate HAL-Pins verwenden, die Sie in einer HAL-Komponente definieren müssen:

c = hal.component("samplegui")
c.newpin("joint0", hal.HAL_FLOAT, hal.HAL_IN)
c.newpin("joint1", hal.HAL_FLOAT, hal.HAL_IN)
c.ready()

5. Erstellen von Teilen

5.1. Importieren von STL- oder OBJ-Dateien

Das ist wahrscheinlich am einfachsten:

  • Herstellung einer Geometrie in einem CAD-Paket_

  • _Import in das Modellskript unter Verwendung der Funktionen "ASCIISTL()" oder "ASCIIOBJ()".

Beide Funktionen können eines von zwei benannten Argumenten annehmen, entweder einen Dateinamen oder Daten:

part = AsciiSTL(filename="path/to/file.stl")
part = AsciiSTL(data="solid part1 facet normal ...")
part = AsciiOBJ(filename="path/to/file.obj")
part = AsciiOBJ(data="v 0.123 0.234 0.345 1.0 ...")
  • STL-Modellteile werden dem Vismach-Raum an denselben Stellen hinzugefügt, an denen sie im STL- oder OBJ-Raum erstellt wurden, d. h. idealerweise mit einem Rotationspunkt an ihrem Ursprung.

Anmerkung
Es ist viel einfacher, sich während des Bauens zu bewegen, wenn der Ursprung des Modells an einem Drehpunkt liegt.

5.2. Aufbau aus geometrischen Primitiven

Alternativ können Teile auch im Modellskript aus einer Reihe von Formprimitiven erstellt werden.

Viele Formen werden am Ursprung erstellt und müssen nach der Erstellung an den gewünschten Ort verschoben werden.

cylinder = CylinderX(x1, r1, x2, r2)
cylinder = CylinderY(y1, r1, y2, r2)
cylinder = CylinderZ(z1, r1, z2, r2)

Erzeugt einen (optional verjüngten) Zylinder auf der gegebenen Achse mit den gegebenen Radien an den gegebenen Punkten auf der Achse.

sphere = Sphere(x, y, z, r)

Erzeugt eine Kugel mit Radius r bei (x,y,z).

triangle = TriangleXY(x1, y1, x2, y2, x3, y3, z1, z2)
triangle = TriangleXZ(x1, z1, x2, z2, x3, z3, y1, y2)
triangle = TriangleYZ(y1, z1, y2, z2, y3, z3, x1, x2)

Erzeugt eine dreieckige Platte zwischen Ebenen, die durch die letzten beiden Werte parallel zur angegebenen Ebene definiert ist und deren Eckpunkte durch die drei Koordinatenpaare gegeben sind.

arc = ArcX(x1, x2, r1, r2, a1, a2)

Erstellen Sie eine Bogenform.

box = Box(x1, y1, z1, x2, y2, z2)

Erzeugt ein rechteckiges Prisma mit gegenüberliegenden Ecken an den angegebenen Positionen und Kanten parallel zu den XYZ-Achsen.

box = BoxCentered(xw, yw, zw)

Erzeugt eine xw mal yw mal zw Box, die auf den Ursprung zentriert ist.

box = BoxCenteredXY(xw, yw, z)

Erstellt einen Kastenboden auf der WY-Ebene mit der Breite xw / yw und der Höhe z.

Zusammengesetzte Teile können durch Zusammenfügen dieser Primitive entweder zum Zeitpunkt der Erstellung oder später erstellt werden:

part1 = Collection([Sphere(100,100,100,50), CylinderX(100,40,150,30)])
part2 = Box(50,40,75,100,75,100)
part3 = Collection([part2, TriangleXY(10,10,20,10,15,20,100,101)])
part4 = Collection([part1, part2])

6. Bewegliche Teile des Modells

Möglicherweise müssen Teile im Vismach-Raum verschoben werden, um das Modell zusammenzusetzen. Der Ursprung bewegt sich nicht - Translate() und Rotate() verschieben die Collection, während Sie Teile hinzufügen, relativ zu einem stationären Ursprung.

Möglicherweise müssen sie auch verschoben werden, um die Animation zu erstellen, da die Drehachse der Animation am Ursprung erstellt wird (aber sich mit dem Teil bewegt).

6.1. Verschieben von Teilen des Modells

part1 = Translate([part1], x, y, z)

Verschiebe Teil1 um die angegebenen Abstände in x, y und z.

6.2. Rotation von Teilen des Modells

part1 = Rotate([part1], theta, x, y, z)

Drehen Sie das Teil um den Winkel theta [Grad] um eine Achse zwischen dem Ursprung und x, y, z.

7. Animieren von Teilen

Zur Animation des Modells durch die Werte der HAL-Pins gibt es vier Funktionen HalTranslate, HalRotate, HalToolCylinder und HalToolTriangle.

Damit sich Teile innerhalb einer Baugruppe bewegen können, müssen ihre HAL-Bewegungen definiert werden, bevor sie mit dem Befehl "Collection" zusammengebaut werden.

Die Rotationsachse und der Translationsvektor bewegen sich mit dem Teil:

  • wie es vom Vismach-Skript während der Modellmontage verschoben wird, oder

  • während es sich als Reaktion auf die HAL-Pins bewegt, während das Modell animiert wird.

7.1. HalTranslate

part = HalTranslate([part], hal_comp, hal_pin, xs, ys, zs)
part

Eine Sammlung oder ein Teil.
Sie kann zu einem früheren Zeitpunkt im Skript erstellt werden oder, falls gewünscht, an dieser Stelle, z. B.

`part1 = HalTranslate([Box(....)], ...)`. +
hal_comp

Die HAL Komponente ist das nächste Argument.
In QtVCP, wenn Sie System-Pins direkt lesen, wird das Komponentenargument auf None gesetzt.

hal_pin

Der Name des HAL-Pins, der die Bewegung animieren soll.
Dieser muss mit einem bestehenden HAL-Pin übereinstimmen, der die Gelenkposition beschreibt, wie z. B.:

"joint.2.pos-fb"

Andernfalls würde die Komponenteninstanz und der Pin-Name dieser Komponente angegeben werden. xs, ys, zs;; Die X, Y, Z Skalen.
Bei einer kartesischen Maschine, die im Maßstab 1:1 erstellt wurde, wäre dies normalerweise "1,0,0" für eine Bewegung in positiver X-Richtung.
Wenn die STL-Datei jedoch in cm und die Maschine in Zoll erstellt wurde, kann dies an dieser Stelle durch die Verwendung von 0,3937 ( = 1 cm/1 Zoll = 1 cm /2,54 cm ) als Maßstab festgelegt werden.

7.2. HalRotate

part = HalRotate([part], hal_comp, hal_pin, angle_scale, x, y, z)

Dieser Befehl funktioniert ähnlich wie HalTranslate, mit dem Unterschied, dass es normalerweise notwendig ist, das Teil zuerst zum Ursprung zu bewegen, um die Achse zu definieren.

x, y, z

Definiert die Drehachse vom Ursprung, dem Koordinatenpunkt (x,y,z).
Wenn das Teil vom Ursprung zurück an seine richtige Position bewegt wird, kann die Drehachse als im Teil "eingebettet" betrachtet werden.

angle_scale

Drehwinkel werden in Grad angegeben. Für ein Drehgelenk mit einer Skalierung von 0-1 müssten Sie also eine Winkelskala von 360 verwenden.

7.3. HalToolCylinder

tool = HalToolCylinder()

Stellen Sie einen Zylinder her, der ein zylindrisches Fräswerkzeug darstellt, basierend auf dem Werkzeugtisch und dem aktuell belasteten Werkzeug.

tool = HalToolCylinder()
toolshape = Color([1, .5, .5, .5],[tool])

# oder kompakter:
toolshape = Color([1, .5, .5, .5], [HalToolCylinder()])

7.4. HalToolTriangle

tool = HalToolTriangle()

Erstellen Sie ein Dreieck, um ein dreieckiges Drehwerkzeug darzustellen, basierend auf dem Werkzeugtisch und dem aktuell geladenen Werkzeug.

tool = HalToolTriangle()
toolshape = Color([1, 1, 0, 1],[tool])

# oder kompakter:
toolshape = Color([1, 1, 0, 1],[HalToolTriangle()])

8. Zusammenbau des Modells

Damit sich die Teile gemeinsam bewegen können, müssen sie mit dem Befehl Collection() zusammengefügt werden.

Es ist wichtig, die Teile zusammenzufügen und ihre Bewegungen in der richtigen Reihenfolge zu definieren.

Um beispielsweise eine Fräsmaschine mit beweglichem Kopf, einer rotierenden Spindel und einer animierten Zugstange zu erstellen, würden Sie dies tun:

  • Erstellen Sie den Hauptteil des Kopfes.

  • Erstellen Sie die Spindel im Ursprung.

  • Definieren Sie die Drehung.

  • Bewegen Sie den Kopf zur Spindel oder die Spindel zum Kopf.

  • Erstellen Sie die Zugstange (engl. draw bar).

  • Definieren Sie die Bewegung der Zugstange.

  • Bauen Sie die drei Teile zu einer Kopfeinheit zusammen.

  • Definieren Sie die Bewegung der Kopfeinheit.

In diesem Beispiel wird die Spindeldrehung durch die Drehung eines Satzes von Mitnehmern angezeigt:

#Drive dogs
dogs = Box(-6,-3,94,6,3,100)
dogs = Color([1,1,1,1],[dogs])
dogs = HalRotate([dogs],c,"spindle",360,0,0,1)
dogs = Translate([dogs],-1,49,0)

#Drawbar
draw = CylinderZ(120,3,125,3)
draw = Color([1,0,.5,1],[draw])
draw = Translate([draw],-1,49,0)
draw = HalTranslate([draw],c,"drawbar",0,0,1)

# head/spindle
head = AsciiSTL(filename="./head.stl")
head = Color([0.3,0.3,0.3,1],[head])
head = Translate([head],0,0,4)
head = Collection([head, tool, dogs, draw])
head = HalTranslate([head],c,"Z",0,0,0.1)

# base
base = AsciiSTL(filename="./base.stl")
base = Color([0.5,0.5,0.5,1],[base])
# mount head on it
base = Collection([head, base])

Schließlich muss eine einzige Sammlung aller Maschinenteile, Böden und Arbeiten (falls vorhanden) erstellt werden.

Für eine serial machine wird jedes neue Teil der Sammlung des vorherigen Teils hinzugefügt.

Bei einer Parallelmaschine kann es mehrere "Basis"-Teile geben.

So wird zum Beispiel in scaragui.py link3 zu link2, link2 zu link1 und link1 zu link0 hinzugefügt, so dass das endgültige Modell wie folgt erstellt wird:

model = Collection([link0, floor, table])

Ein VMC-Modell mit separaten Teilen, die sich auf dem Sockel bewegen, könnte hingegen haben

model = Collection([base, saddle, head, carousel])

9. Weitere Funktionen

part = Color([_colorspec_], [_part_])

Setzt die Anzeigefarbe des Teils.
Beachten Sie, dass im Gegensatz zu den anderen Funktionen, die Definition des Teils in diesem Fall an zweiter Stelle steht.

_colorspec_

Drei RGB-Werte und Deckkraft.
Zum Beispiel [1,0,0,0.5] für ein Rot mit 50% Deckkraft.

myhud = Hud()

Erzeugt ein Heads-up-Display in der Vismach-GUI, um Elemente wie Achsenpositionen, Titel oder Meldungen anzuzeigen.

myhud = Hud()
myhud.show("Mill_XYZ")`
myhud = HalHud()

Eine erweiterte Version des Hud, mit der HAL-Pins angezeigt werden können:

myhud = HalHud()
myhud.set_background_color(0,.1,.2,0)
myhud.show_top("Mill_XYZ")
myhud.show_top("------------")
myhud.add_pin('axis-x: ',"{:10.4f}","axis.x.pos-cmd")
myhud.add_pin('axis-y: ',"{:10.4f}","axis.y.pos-cmd")
myhud.add_pin('axis-z: ',"{:10.4f}","axis.z.pos-cmd")
myhud.show("-------------")
part = Capture()

Damit wird die aktuelle Position im Modell festgelegt.

main(model, tooltip, work, size=10, hud=myhud, rotation_vectors=None, lat=0, lon=0)

Dies ist der Befehl, der alles in Gang setzt, die Anzeige erstellt usw., wenn er direkt aus Python aufgerufen wird. Normalerweise wird diese Datei von QtVCP importiert und das window() Objekt wird instanziiert und in einen anderen Bildschirm eingebettet.

_model_

Sollte eine Sammlung sein, die alle Maschinenteile enthält.

tooltip und work

Müssen durch Capture() erstellt werden, um ihre Bewegung im Backplot zu visualisieren. Siehe mill_xyz.py für ein Beispiel, wie man die Werkzeugspitze mit einem Werkzeug und das Werkzeug mit dem Modell verbindet.

_size_

Legt die Ausdehnung des Volumens fest, das in der Ausgangsansicht angezeigt wird.
"hud" bezieht sich auf eine Head-up-Anzeige der Achsenpositionen.

"rotation_vectors" oder "lat, lon"

Kann verwendet werden, um den ursprünglichen Blickwinkel festzulegen. Es ist ratsam, zu tun, wie die Standard-Anfangsstandpunkt ist eher wenig hilfreich von unmittelbar über Kopf.

10. Hinweise

Erstellen Sie für Konstruktionszwecke eine Achsenursprungsmarkierung, um Teile relativ dazu sehen zu können. Sie können diese entfernen, wenn Sie fertig sind.

# Erstellen von Achsen-Ursprungs-Markierungen
X = CylinderX(-500,1,500,1)
X = Color([1, 0, 0, 1], [X])
Y = CylinderY(-500,1,500,1)
Y = Color([0, 1, 0, 1], [Y])
Z = CylinderZ(-500,1,500,1)
Z = Color([0, 0, 1, 1], [Z])
origin = Collection([X,Y,Z])

Fügen Sie es der Window-Klassensammlung hinzu, damit es nie vom Ursprung verschoben wird.

v.model = Collection([origin, model, world])

Beginnen Sie an der Schneidspitze (engl. cutting tip) und arbeiten Sie sich zurück. Fügen Sie jede Auflistung dem Modell am Ursprung hinzu, und führen Sie das Skript aus, um den Speicherort zu bestätigen. Drehen / Übersetzen und Ausführen des Skripts zur erneuten Bestätigung.

11. Grundstruktur eines QtVismach-Skripts

# imports
import hal
from qtvcp.lib.qt_vismach.qt_vismach import *

# hier HAL-Pins erstellen, falls erforderlich
#c = hal.component("samplegui")
#c.newpin("joint0", hal.HAL_FLOAT, hal.HAL_IN)

# Erstellen des Bodens, des Werkzeugs und des Werkstücks
floor = Box(-50, -50, -3, 50, 50, 0)
work = Capture()
tooltip = Capture()

# Das Modell aufbauen und zusammensetzen
part1 = Collection([Box(-6,-3,94,6,3,100)])
part1 = Color([1,1,1,1],[part1])
part1 = HalRotate([part1],None,"joint.0.pos-fb",360,0,0,1)
part1 = Translate([dogs],-1,49,0)

# ein Top-Level-Modell erstellen
model = Collection([base, saddle, head, carousel])

# wir wollen entweder in QtVCP einbetten oder direkt mit PyQt5 anzeigen
# also ein Fenster bauen, um das Modell anzuzeigen

class Window(QWidget):

    def __init__(self):
        super(Window, self).__init__()
        self.glWidget = GLWidget()
        v = self.glWidget
        v.set_latitudelimits(-180, 180)

        world = Capture()

        # unkommentiert lassen, wenn es ein HUD gibt
        # HUD muss wissen, wo es zeichnen soll
        #v.hud = myhud
        #v.hud.app = v

        v.model = Collection([model, world])
        size = 600
        v.distance = size * 3
        v.near = size * 0.01
        v.far = size * 10.0
        v.tool2view = tooltip
        v.world2view = world
        v.work2view = work

        mainLayout = QHBoxLayout()
        mainLayout.addWidget(self.glWidget)
        self.setLayout(mainLayout)

# Wenn Sie diese Datei direkt aus Python3 aufrufen, wird ein PyQt5-Fenster angezeigt.
# das sich gut eignet, um die Teile der Baugruppe zu bestätigen.

if __name__ == '__main__':
    main(model, tooltip, work, size=600, hud=None, lat=-75, lon=215)

12. Integrierte Vismach-Beispielpanels