Vismach is a set of Python functions that can be used to create and animate models of machines.
This chapter is about the Qt embedded version of Vismach, also see: https://sa-cnc.com/linuxcnc-vismach/ .
1. Einführung
Vismach displays the model in a 3D viewport and the model parts are animated as the values of associated HAL pins change.
The Vismach 3D viewport view can be manipulated as follows:
-
zoom by scroll wheel
-
pan by middle button drag
-
rotate by right-button drag
-
tilt by left button drag
A Vismach model takes the form of a Python script and can use standard Python syntax.
This means that there is more than one way to lay out the script, but in the examples given in this document the simplest and most basic of them will be used.
The basic sequence in creating the Vismach model is:
-
Create the parts
-
Define how they move
-
Assemble into movement groups
2. Hierarchy of Machine Design
The model follows logical tree design.
Picture the tree, with root/trunk, branches and smaller branches off it. If you move the larger branch, smaller branches will move with it, but if you move the smaller branch, the larger will not.
Machine design follows that conceptual design.
Taking the mill shown in the 3D Viewport picture above for example:
-
If you move X, it can move on its own,
-
but if you move Y, it will also move X assembly, as it is attached to Y assembly.
So for this machine, the tree looks like this:
model
|
|---frame
| |
| |---base
| |
| |---column
| |
| |---top
|
|---yassembly
| |
| |---xassembly
| | |
| | |---xbase
| | |
| | |---work
| |
| |---ybase
|
|---zassembly
|
|---zframe
| |
| |---zbody
| |
| |---spindle
|
|---toolassembly
|
|---cat30
|
|---tool
|
|---tooltip
|
|---(tool cylinder function)
As you can see, the lowest parts must exist first before those can be grouped with others into an assembly. So you build upwards from lowest point in tree and assemble them together.
The same is applicable for any design of machine: look at the machine arm example and you will see that it starts with the tip and adds to the larger part of the arm, then it finally groups with the base.
3. Start the script
It is useful for testing to include the #!/usr/bin/env python3
shebang line to _allow the file to be executed directly from the command line.
The first thing to do is to import the required libraries.
#!/usr/bin/env python3 import hal import math import sys from qtvcp.lib.qt_vismach.qt_vismach import *
4. HAL pins.
Originally the vismach library required creating a component and connecting HAL pins to control the simulation.
qt_vismach
can read the HAL system pins directly or if you wish, to use separate HAL pins that you must define in a HAL component:
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()".
Both functions can take one of two named arguments, either a filename or data:
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 model parts are added to the Vismach space in the same locations as they were created in the STL or OBJ space, i.e. ideally with a rotational point at their origin.
Anmerkung
|
It is much easier to move while building if the origin of the model is at a rotational pivot point. |
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)
-
Create an arc shape.
-
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. Moving Model Parts
Parts may need to be moved in the Vismach space to assemble the model. The origin does not move - Translate() and Rotate() move the Collection as you add parts, relative to a stationary origin.
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. Translating Model parts
-
part1 = Translate([part1], x, y, z)
-
Verschiebe Teil1 um die angegebenen Abstände in x, y und z.
6.2. Rotating Model Parts
-
part1 = Rotate([part1], theta, x, y, z)
-
Rotate the part by angle theta [degrees] about an axis between the origin and x, y, z.
7. Animating Parts
To animate the model controlled by the values of HAL pins there are four functions: HalTranslate
, HalRotate
, HalToolCylinder
and HalToolTriangle
.
For parts to move inside an assembly they need to have their HAL motions defined before being assembled with the "Collection" command.
The rotation axis and translation vector move with the part:
-
as it is moved by the Vismach script during model assembly, or
-
as it moves in response to the HAL pins as the model is animated.
7.1. HalTranslate
-
part = HalTranslate([part], hal_comp, hal_pin, xs, ys, zs)
-
-
part
-
A collection or part.
It can be pre-created earlier in the script, or could be created at this point if preferred, e.g.,
`part1 = HalTranslate([Box(....)], ...)`. +
-
hal_comp
-
The HAL component is the next argument.
In QtVCP if you are reading system pins directly then the component argument is set toNone
.
-
hal_pin
-
The name of the HAL pin that will animate the motion.
This needs to match an existing HAL pin that describes the joint position such as:"joint.2.pos-fb"
Andernfalls würde die Komponenteninstanz und der Pin-Name dieser Komponente angegeben werden.
xs, ys, zs
;; The X, Y, Z scales.
For a Cartesian machine created at 1:1 scale this would typically be1,0,0
for a motion in the positive X direction.
However if the STL file happened to be in cm and the machine was in inches, this could be fixed at this point by using 0.3937 ( = 1 cm/1 inch = 1 cm /2.54 cm ) as the scale.
-
7.2. HalRotate
-
part = HalRotate([part], hal_comp, hal_pin, angle_scale, x, y, z)
-
This command is similar in its operation to
HalTranslate
, except that it is typically necessary to move the part to the origin first to define the axis.
-
x, y, z
-
Defines the axis of rotation from the origin the point of coordinates (x,y,z).
When the part is moved back away from the origin to its correct location, the axis of rotation can be considered to remain "embedded" in the part. -
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()
-
Make a cylinder to represent a cylindrical mill tool, based on the tool table and current loaded tool.
tool = HalToolCylinder() toolshape = Color([1, .5, .5, .5],[tool]) # or more compact: toolshape = Color([1, .5, .5, .5], [HalToolCylinder()])
7.4. HalToolTriangle
-
tool = HalToolTriangle()
-
Make a triangle to represent a triangular lathe tool, based on the tool table and current loaded tool.
tool = HalToolTriangle() toolshape = Color([1, 1, 0, 1],[tool]) # or more compact: toolshape = Color([1, 1, 0, 1],[HalToolTriangle()])
8. Zusammenbau des Modells
In order for parts to move together they need to be assembled with the Collection()
command.
It is important to assemble the parts and define their motions in the correct sequence.
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.
-
Create the draw bar.
-
Define the motion of the draw bar.
-
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.
For a serial machine each new part will be added to the collection of the previous part.
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()
-
A more advanced version of the Hud that allows HAL pins to be displayed:
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)
-
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.-
_model_
-
Should be a collection that contains all the machine parts.
- tooltip und work
-
Need to be created by
Capture()
to visualize their motion in the backplot. Seemill_xyz.py
for an example of how to connect the tool tip to a tool and the tool to the model. -
_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"
-
Can be used to set the original viewpoint. It is advisable to do as the default initial viewpoint is rather unhelpful from immediately overhead.
-
10. Tips
Create an axes origin marker to be able to see parts relative to it, for construction purposes. You can remove it when you are done.
# build axis origin markers 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])
Add it to the Window class Collection so it is never moved from the origin.
v.model = Collection([origin, model, world])
Start from the cutting tip and work your way back. Add each collection to the model at the origin and run the script to confirm the location, then rotate/translate and run the script to confirm again.
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)