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.
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:
-
Erstellen der Teile
-
Definieren, wie sie sich bewegen
-
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()
You can select between the two options in the functions that take these entries:
-
hal_comp
-
The HAL component Object or None.
In QtVCP if you are reading system pins directly, then the component argument is set toNone
.
-
hal_pin
-
The name of the BIT HAL IN pin that will change the color.
if hal_comp is None then this must be the full name of a system pin otherwise this is the pin name excluding the component name
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.
-
assembly = collction([part1,part2,part3])
-
Collection is a general container of related parts
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.
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 aufNone
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()])
7.5. HAL Adjustable Primitives
All shape primitives can have HAL pin names substituted for coordinates.
Either by adding the component object as the first variable and substituting the pinname string for a coordinate, or
by substituting the full component/pinname string for a coordinate.
This example creates a rectangular prism with opposite corners at the specified positions and edges parallel to the XYZ axes.
the Z start coordinate will be controlled by the HAL pin Zstart
box = Box(component, x1, y1, 'Zstart', x2, y2, z2) box = Box(x1, y1, 'componentName.Zstart', x2, y2, z2)
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
9.1. Farbe
Sets the display color of the part.
-
part = Color([_colorspec_], [_part_])
-
Note that unlike the other functions, the part definition comes second in this case.
-
_colorspec_
-
Three (0-1.0) RGB values and opacity. [R,G,B,A]
For example [1.0,0,0,0.5] for a 50% opacity red.
-
9.2. HALColorFlip
Sets the display color of the part based on a designated HAL bit pin state.
-
part = HALColorFlip([_colorspec_], [_colorspec_], [_part_], hal_comp, hal_pin)
-
Note that unlike the other functions, the part definition comes second in this case.
-
_colorspec_
-
Three (0-1.0) RGB values and opacity.
For example [1.0,0,0,0.5] for a 50% opacity red. -
hal_comp
-
The HAL component Object or None.
In QtVCP if you are reading system pins directly, then the component argument is set toNone
.
-
hal_pin
-
The name of the BIT HAL IN pin that will change the color.
if hal_comp is None then this must be the full name of a system pin other wise this is the pin name excluding the component name
-
9.3. HALColorRGB
Sets the display color of the part based on a designated HAL U32 pin value.
The color is decoded from the U32 value. each color is a 0-255 decimal value (shown here in hex)
red = 0xXXXXXXRR
green = 0xXXXXGGXX
blue = 0xXXBBXXXX
combined as 0xXXBBGGRR
-
part = HALColorRGB([_part_], hal_comp, hal_pin, alpha=1.0)
-
-
hal_comp
-
The HAL component Object or None.
In QtVCP if you are reading system pins directly, then the component argument is set toNone
.
-
hal_pin
-
The name of the U32 HAL IN pin that will change the color.
if hal_comp is None then this must be the full name of a system pin other wise this is the pin name excluding the component name
-
alpha=
-
Sets the opacity. (0-1.0)
-
9.4. Heads Up Display
Erzeugt ein Heads-up-Display in der Vismach-GUI, um Elemente wie Achsenpositionen, Titel oder Meldungen anzuzeigen.
-
myhud = Hud()
myhud = Hud() myhud.show("Mill_XYZ")`
9.5. HAL Heads Up Display
Eine erweiterte Version des Hud, mit der HAL-Pins angezeigt werden können:
-
myhud = HalHud()
myhud = HalHud() myhud.display_on_right() myhud.set_background_color(0,.1,.2,0) myHud.set_text_color(1,1,1) 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("-------------")
Some of the available HalHUD function:
-
set_background_color(red, green, blue, alpha)
-
add_pin(text, format, pinname)
-
set_text_color(red, green, blue)
9.6. HideCollection
HideCollection is a container that uses a HAL pin to control display of the contained parts. +
A logic high on the HAL pin will hide the contained parts.
comp.newpin("hide-chuck", hal.HAL_BIT, hal.HAL_IN) # <snip make a machine with an A axis chuck assembly> chuckassembly = HideCollection([chuckassembly],comp,'hide-chuck') # also can be used like this chuckassembly = HideCollection([chuckassembly],None,'myvismach.hide-chuck')
9.7. Plot Color Based on Mtotion Type
If you wish to plot different colors for different motions you need to add some more python code.
add this at the top of the file:
from qtvcp.core import Status STATUS = Status()
and this to the Window class:
STATUS.connect('motion-type-changed', lambda w, data: v.choosePlotColor(data)) # uncomment to change feed color and to see all colors printed to the terminal #v.setColorsAttribute('FEED',(0,1,0)) #print(v.colors)
You can set DEFAULT, FEED, TRAVERSE, ARC, PROBE, ROTARYINDEX, TOOLCHANGE colors with setColorsAttribute()
9.8. Capture
Damit wird die aktuelle Position im Modell festgelegt.
part = Capture()
9.9. main
This is the command that makes it all happen, creates the display, etc. if invoked directly from Python.
Usually this file is imported by QtVCP and the window()
object is instantiated and embedded into another screen.
-
main(model, tooltip, work, size=10, hud=myhud, rotation_vectors=None, lat=0, lon=0)
-
-
_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. Siehemill_xyz.py
für ein Beispiel, wie man die Werkzeugspitze mit einem Werkzeug und das Werkzeug mit dem Modell verbindet. -
_size_
-
Sets the extent of the volume visualized in the initial view.
-
_hud_
-
refers to a head-up display.
- "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 * # import Status for motion type messages from qtvcp.core import Status STATUS = Status() # 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 # update plot color based on motion type STATUS.connect('motion-type-changed', lambda w, data: v.choosePlotColor(data)) # uncomment to change feed color #v.setColorsAttribute('FEED',(0,1,0)) # and to see all colors printed to the terminal #print(v.colors) 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)