Bastelprojekt Lasershow mit Arduino: Vektorgrafiken an die Wand projizieren

Seite 5: Programmierung und Zeichnen

Inhaltsverzeichnis

Die Hardware steht, die Lasershow läuft – im Prinzip. Fehlen nur noch die passenden Retro-Spiele, also ran ans Coden!

Beim Programmieren von Asteroids stieß ich allerdings auf ein zentrales Problem: Je umfangreicher die Berechnungen wurden, desto mehr ruckelte das Bild. Somit musste ich das Berechnen der einzelnen Frames unabhängig von der Galvo-Steuerung bewerkstelligen. An einem PC wäre dies in Zeiten von Multitasking sicher kein Problem gewesen. Aber bei einem Arduino?

Anfangs versuchte ich, mit zwei Frame-Arrays zu arbeiten. In das eine werden die berechneten Daten geschrieben, während das zweite zur selben Zeit die Galvos mit Daten versorgt. Anschließend werden die zwei Arrays getauscht. Leider kam ich dabei schnell an die Grenzen des Variablenspeichers des Arduino Mega. 8 KB sind halt auch nicht die Welt.

Gelöst habe ich das Problem schließlich mit einer Art Stream: Die Berechnungsroutinen zu Positionen von Objekten und Ähnlichem legen ihre Ergebnisse in einem Array ab. Genaugenommen handelt es sich dabei um drei Arrays, die parallel befüllt werden, eines für die X-Koordinaten, eines für die Y-Koordinaten und das dritte für die Laserfarbe. Daraus werden Timer-Interrupt-gesteuert die Galvos und die Laser mit Daten versorgt. Das Befüllen (PointerSet) mit Daten kann dabei sowohl vor dem Anzeigezeiger (PointerShow) als auch hinter dem Anzeigezeiger erfolgen. Man muss beim Programmieren lediglich darauf achten, dass die Berechnung eines Frames weniger Zeit in Anspruch nimmt als seine Anzeige. Anderenfalls beginnt das Bild wieder zu ruckeln, denn der Anzeigezeiger kann den Berechnungszeiger nicht überholen. Der möglichen Komplexität von Spielen und anderen Arduino-Anwendungen sind also Grenzen gesetzt.

An der ersten Stelle des Streams steht immer die Bildmitte als Koordinate. Das Ende des Streams signalisiert ein Color-Wert von 128 (binär 10000000). Die maximale Länge des Streams und damit auch die maximale Anzahl an Punkten ist MAXPOS.

Nachdem diese technischen Voraussetzungen geschaffen wurden, geht es nun ans Zeichnen. Auch hier gilt es jede Menge zu beachten. Leider genügt es nicht, die Galvo-Spiegel auf die Startposition zu bewegen, den Laser einzuschalten und dann die Spiegel auf die Endposition zu dirigieren. Anders als ein Elektronenstrahl in einer Fernsehröhre oder einem Oszilloskop kommt bei Galvo-Spiegeln die Trägheit ins Spiel, denn auch die besten und schnellsten Galvos besitzen eine Masse, die beschleunigt und am Ende wieder abgebremst werden muss. Dafür ist Zeit nötig, die bei der Programmierung berücksichtigt werden muss, um ein sauberes Bild zu erhalten.

Um eine gleichmäßige Linie zeichnen zu können, muss man diese in einzelne Teile zerlegen. Befindet sich der Laser auf der Startposition, wird er eingeschaltet. Dann bewegt man die Spiegel in festgelegten Zeitintervallen immer um einen kleinen Teilbereich weiter (Parameter maxDistDrawn). Ist die rechnerische Zielposition erreicht, wird der Laser jedoch nicht sofort ausgeschaltet, sondern noch für eine festgelegte Zeit (post Draw) gewartet, bis die Spiegel tatsächlich am Ziel angelangt sind und abgebremst haben.

Anschließend bewegen wir die Spiegel bei ausgeschaltetem Laser wieder zur Startposition zurück. Diesen Vorgang nennt man Blanking. Hierbei spielt es keine Rolle, welchen Weg der Laser dabei nimmt, er sollte nur möglichst kurz sein, um nicht unnötig Zeit für das Blanking zu verschwenden. Jedoch muss auch beim Blanking dem Laser genügend Zeit fürs Schalten eingeräumt (preBlanking) und am Ende gewartet werden, damit die Zielposition sicher erreicht werden kann (postBlanking).

Die Grafik zeigt den Ablauf beim Zeichnen einer Linie mit dem Laser, der anschließend ausgeschaltet und an den Ursprungsort zurückfährt. Zur Vereinfachung wird nur die Bewegung in einer Dimension gezeigt. Die Parameter maxDistDrawn, postDraw, preBlanking und postBlanking sind am Beginn der Datei Draw.ino definiert.

Es kann übrigens sehr interessant sein, sich einmal die Wege des Lasers beim Blanking anzeigen zu lassen. Dazu müssen Sie in der Datei Draw.ino in der Routine blanking(byte x, byte y) lediglich in der Zeile DrawFastLine(x, y, 0); die 0 durch eine 1 ersetzen. Zum einen lässt sich dann erkennen, welche interessanten Bahnen der Laser beim Blanking beschreibt. Zum anderen bietet es auch die Chance, den Programmablauf zu optimieren und unnötige Blanking-Strecken einzusparen.

Pong mit sichtbarem Blanking

Asteroids mit sichtbarem Blanking