Programmiersprache Aseba

Diese Hilfe finden Sie auch innerhalb des Aseba Studios im Menu Hilfe -> Sprache. Technische Ausdrücke sind mit der entsprechenden Erklärung auf Wikipedia verlinkt. Die Seite stellt die Sprache ab der Version 1.2 des Aseba, die für frühere Versionen, lesen Sie bitte diese Seite.

Die Syntax der Programmiersprache Aseba gleicht derjenigen von Matlab (verbreitete wissenschaftliche Programmiersprache). Dies ermöglicht Programmierern mit Vorkenntnissen sich schnell zu orientieren und Aseba in Kürze zu erlernen. Semantisch gesehen, ist Aseba eine einfache, imperative Programmierung mit einem einzelnen Datentyp (16 Bit Ganzzahlen mit Vorzeichen) und Vektoren davon. Diese Einfachheit erlaubt auch Programmierern ohne Vorkenntnisse Mikrokontroller-basierte Roboter zu steuern.

Kommentare

Kommentare erlauben, in den Programmtext Information einzufügen, die vom Compiler ignoriert wird. Dies ist sehr nützlich um Informationen in natürlicher Sprache einzufügen oder zwischenzeitlich Code-Teile auszuschalten.
Kommentare beginnen mit dem Symbol # und enden auf derselben Zeile.

Beispiel:

# dies ist ein Kommentar
var b    # ein anderer Kommentar

Bemerkungen über mehrere Linien sind auch möglich. Sie beginnen mit dem Symbol #* und enden mit *#.

Beispiel:

#*
Dies ist ein Kommentar
über mehrere Linien verteilt.
*#
var b    # kurzer Kommentar

Skalare

Skalare Variablen sind Variablen, die Nummern enthalten. Sie können in allen Ausdrücken vorkommen, wie zum Beispiel bei der Initialisierung einer Variable, in einem mathematischen Ausdruck oder in einer logischen Bedingung.

Notation

Skalare können in verschiedenen Basen dargestellt werden. Für uns ist die natürlichste Basis die Basis 10, die zum Dezimalsystem führt, bei dem die Ziffern von 0 bis 9 verwendet werden. Eine negative Zahl wird durch ein vorangestelltes - (Minus-) Zeichen gekennzeichnet.

i = 42
i = 31415
i = -7

Sowohl das Dualsystem als auch das Hexadezimalsystem werden unterstützt. Binäre (Dual-) Zahlen bekommen ein 0b vorangestellt, hexadezimale Zahlen ein 0x.

# Binäre Schreibweise
i = 0b110         # i = 6
i = 0b11111111    # i = 255

# Hexadezimale Schreibweise
i = 0x10    # i = 16
i = 0xff    # i = 255

Variablen

Variablen beziehen sich entweder auf einfache ganze Zahlen oder Vektoren von ganzen Zahlen. Die zulässigen Werte sind alle ganzen Zahlen zwischen -32768 und 32767, was einem Speicherbereich von 16 Bit (2 Byte) entspricht. Man kann Vektorkomponenten mit eckigen Klammern [Zahl] aufrufen. Die Indexierung der Vektoren beginnt bei Null. Alle benutzerdefinierten Variablen müssen am Anfang des Asebaprogramms definiert werden, vor allen weiteren Programmteilen.

Der Name einer Variablen muss diesen drei Regeln genügen:

Variablen dürfen während der Deklaration initialisiert werden, indem das Zuweisungssymbol mit einem beliebigen gültigen mathematischen Ausdruck verbunden wird. Eine Variable, die nicht initialisiert wurde, enthält einen nicht-deterministischen Wert. Dieser ist höchstwahrscheinlich nicht null!

Beispiel:

var a
var b = 0
var c = 2*a + b        # Warnung: 'a' wurde nicht initialisiert

Reservierte Schlüsselwörter

Die folgenden Schlüsselwörter dürfen nicht als Namen für Variablen benutzt werden, da sie Bestandteil der Aseba-Sprache sind.

Schlüsselwörter
abs call callsub do
else elseif emit end
for if in onevent
return step sub then
var when while

Konstanten

Konstanten können in Aseba Studio im "Konstanten"-Panel definiert werden, aber nicht direkt im Programm-Code. Eine Konstante ist eine Zahl, die überall dort benutzt werden kann, wo eine einfach Zahl direkt benutzt werden könnte. Der Unterschied zu einer herkömmlichen Variable ist der, dass Konstanten nach ihrer Definition nicht mehr verändert werden können. Dies ist nützlich, wenn Sie das Verhalten verschiedener Abläufe an einer zentralen Stelle kontrollieren möchten, wie zum Beispiel einen Schwellwert anpassen, der von mehreren Aseba-Knoten genutzt wird. Eine Konstante muss einen eindeutigen Namen haben, insbesondere nicht den Namen einer bereits bestehenden Variable. Andernfalls wird bei der Code-Übersetzung ein Fehler bemängelt.

# angenommen, wir haben eine Konstante mit dem Namen THRESHOLD defiiniert
var i = 600

if i > THRESHOLD then
    i = THRESHOLD - 1
end

Vektoren

Vektoren entsprechen einem zusammenhängenden Bereich im Speicher, der wie eine einzelne logische Instanz verwendet werden kann. Sie können mit den üblichen eckigen Klammern [] deklariert werden. Die Zahl zwischen den eckigen Klammern legt die Anzahl der in diesem Vektor enthaltenen Elemente fest. Dies ist die Grösse des Vektors und kann im Nachhinein nicht verändert werden. Die Grösse wird durch einen konstanten Ausdruck angegeben, der auch mathematische Operationen mit Skalaren und Konstanten umfassen darf. Optional können dem Vektor mit dem Vektor-Konstruktor (Beispiel siehe unten) Werte zugewiesen werden. Wird dieser Konstruktor benutzt, so muss die Grösse des Vektors nicht mehr angegeben werden.

Beispiel:

var a[10]                   # Vektor mit 10 Element
var b[3] = [2, 3, 4]        # Initialisierung
var c[] = [3, 1, 4, 1, 5]   # Grösse wird implizit auf 5 gesetzt
var d[3*FOO-1]              # Grösse wird durch einen konstanten Ausdruck bestimmt (FOO ist eine Konstante)

Auf Vektoren kann auf verschiedene Weise zugegriffen werden:

Beispiel:

var foo[5] = [1,2,3,4,5]
var i = 1
var a
var b[3]
var c[5]
var d[5]

a = foo[0]       # Kopiere das erste Element
a = foo[2*i-2]       # von 'foo' nach 'a'

b = foo[1:3]       # Kopiere das 2., 3. und 4. Element
b = foo[1:2*2-1]   # aus 'foo' nach 'b'

c = foo           # Kopiere alle 5 Element von 'foo' nach 'c'
d = c * foo       # Multipliziere die Vektoren 'foo' und 'c' ein Element nach dem anderen und speichere das Ergebnis in 'd'

Vektor-Konstruktoren

Mithilfe von Vektor-Konstruktoren können Arrays aus Variablen, anderen Arrays, Skalaren und sogar komplexen Ausdrücken erzeugt werden. Sie kommen vor allem bei der Initialisierung anderer Arrays oder als Operanden in Ausdrücken, Funktionen und Ereignissen vor. Ein Vektor wird mit eckigen Klammern konstruiert, die mehrere Ausdrücke enthalten können, die jeweils durch ein Komma ',' getrennt werden. Die Grösse des Konstruktors ist die Summe der Grössen aller individueller Ausdrücke und muss mit der Grösse des Vektors, in dem der Konstruktor gespeichert werden soll, übereinstimmen.

Beispiel:

var a[5] = [1,2,3,4,5]    # Konstruktor um einen Vektor zu initialisieren
var b[3] = [a[1:2],0]    # b wird mit [2,3,0] initialisiert
a = a + [1,1,1,1,1]    # addiere zu jedem Element aus 'a' eins dazu
a = [b[1]+2,a[0:3]]    # a ist jetzt [5,2,3,4,5]

Ausdrücke und Zuweisungen

Ausdrücke erlauben mathematische Berechnungen und sind in einer normalen traditionellen mathematischen Notation (Infix syntax) geschrieben. Zuweisungen benutzen = und weisen das Resultat einer Ausdrucksberechnung einer Variable zu (oder einer Komponente einer Variable, falls die Variable ein Vektor ist). In Aseba gibt es verschiedene Operatoren, welche in der Tabelle unten mit ihrer Priorität aufgeführt sind. Um einen Ausdruck in einer anderen Reihenfolge zu evaluieren, können Klammern benutzt werden.

Priorität Operator Beschreibung Assoziativität Stelligkeit
1 () Gruppieren eines Unter-Ausdrucks unär
2 * / Multiplikation, Division binär
% Modulo binär
3 + - Addition, Subtraktion binär
4 << >> Linksverschiebung, Rechtsverschiebung binär
5 | binäres oder (or) links assoziativ binär
6 ^ binäres, exklusives oder (xor) links assoziativ binär
7 & binäres und links assoziativ binär
8 - unäres minus unär
9 ~ binäres nicht unär
10 abs absoluter Wert unär
11 = Zuweisung binär
|= ^= &= Zuweisung durch binäres oder, xor, und binär
~= Zuweisung durch binäres nicht binär
*= /= Zuweisung durch Produkt und Quotient binär
%= Zuweisung durch modulo binär
+= -= Zuweisung durch Summe und Differenz binär
<<= >>= Zuweisung durch Links- oder Rechtsverschiebung binär
++ -- unäres Inkrement und Dekrement unär

Die Zuweisung durch Versionen der binären Operatoren funktionieren, indem sie den Operator auf eine Variable anwenden und das Resultat in derselben Variablen speichern. Zum Beispiel bedeutet A* = 2 das Gleiche wie A = A*2. Diese Abkürzungen sollen den Code einfacher lesbar machen.

Beispiel:

a = 1 + 1
# Resultat: a = 2
a *= 3
# Resultat: a = 6
a++
# Resultat: a = 7

b = b + d[0]
b = (a - 7) % 5
c[a] = d[a]
c[0:1] = d[2:3] * [3,2]

Benutzung

Mathematische Ausdrücke sind ein sehr allgemeines Werkzeug. Sie können in vielen verschiedenen Situationen benutzt werden, zum Beispiel:

Ablaufsteuerung

Bedingungen

Aseba stellt zwei Arten von Bedingungen zur Verfügung: if und when. Vergleichende Operatoren sind ==, !=, >, >=, <, und <=; beliebige Ausdrücke können verglichen werden. Vergleiche können gruppiert werden mit Hilfe von and (logische Konjunktion), or (logische Disjunktion), und not (logische Negation) Operatoren sowie mit Klammern.
Ein Vergleich besteht aus einem Operator und zwei Operanden und kann entweder richtig oder falsch sein. Die Operanden können beliebige Ausdrücke sein. Die folgende Tabelle listet die Vergleichsoperatoren auf:

Operator Wahrheitswert
== richtig, wenn die Operanden gleich sind (gleich)
!= richtig, wenn die Operanden unterschiedlich sind (ungleich)
> richtig, wenn der erste Operand grösser ist als der zweite (grösser)
>= richtig, wenn der erste Operand grösser oder gleich gross ist wie der zweite (grösser-gleich)
< richtig, wenn der erste Operand kleiner ist als der zweite (kleiner)
<= richtig, falls der erste Operand kleiner oder gleich gross ist wie der zweite (kleiner-gleich)

Sowohl if als auch when führen den darauffolgenden Befehlsblock aus, falls die Bedingung nach if bzw when erfüllt ist. Allerdings führt when den darauffolgenden Befehlsblock ausschliesslich dann aus, wenn die letzte Auswertung der Bedingung falsch war und die momentane Bedingung richtig ist. Dies ermöglicht eine effizientere Arbeitsweise, da der Befehlsblock nur dann ausgeführt wird, wenn sich etwas geändert hat.

Beispiel:

if a - b > c[0] then
#wenn a - b > [0] dann
    c[0] = a
elseif a > 0 then
#falls die erste Bedingung (a - b > c[0]) falsch war und (a>0) richtig, dann
       b = a
else
#falls keine der vorhergehenden Bedingungen richtig war
       b = 0
end
#Ende des if Befehlsblocks 

when a > b do
#wenn a > b mach
    leds[0] = 1
end
#Ende des when Befehlsblocks

Hier wird der Befehlsblock when nur ausgeführt, falls a grösser als b ist.

Endlosschleifen

Zwei Konstruktionen ermöglichen Endlosschleifen: while und for.

Eine while Endlosschleife wiederholt einen Befehlsblock solange wie die verlangte Bedingung am Ende zutrifft. Die Funktion hat dieselbe Form wie wenn man if benutzt.

Beispiel:

while i < 10 do
#während i < 10 führe den folgenden Befehlsblock aus
    v = v + i * i
    i = i + 1
end
#Ende des while Blocks

Eine for-Schleife erlaubt es, eine Variable über einen Bereich ganzer Zahlen laufen zu lassen, optional mit einer Schrittweite.

Beispiel:

for i in 1:10 do
#für i in 1,2,3,...,9,10 führe aus
    v = v + i * i
end
#Ende
for i in 30:1 step -3 do
#für i in 30,27,24,21,...,6,3 führe aus
    v = v - i * i
end
#Ende

Blöcke

Unterprogramme

Wenn Sie an zwei oder mehr Plätzen die gleiche Abfolge von Befehlen ausführen möchte, können sie die gemeinsamen Abfolgen einmal in ein Unterprogramm auslagen und dieses dann von verschiedenen Stellen im Code aus aufrufen. Ein Unterprogramm können sie durch das sub-Schlüsselwort, gefolgt vom Namen des Unterprogramms, definieren. Um dieses Unterprogramm aufzurufen, benutzen Sie das Schlüsselwort callsub, wieder gefolgt von dem Namen Ihres Unterprogramms. Unterprogramme müssen immer erst definiert werden, bevor sie aufgerufen werden können. Sie können weder Argumente entgegennehmen noch rekursiv sich selbst aufrufen, weder direkt noch indirekt. Sie haben jedoch freien Zugriff auf alle Variablen.

Beispiel:

var v = 0

sub toto
v = 1

onevent test
callsub toto

Ereignisse

Aseba besitzt eine ereignisbasierte (event-based) Architektur, was eine asynchrone Ereignisauslösung ermöglicht. Extern können Ereignisse zum Beispiel von einem anderen Aseba-Netzwerkelement ausgelöst werden. Intern können Ereignisse zum Beispiel von einem Sensor mit aktualisierten Daten ausgelöst werden.

Die Wahrnehmung eines Ereignisses kann einen Codeblock aktivieren, falls dieser zuvor mit dem Stichwort onevent und der Bezeichnung des Ereignisses definiert worden ist. Der Code am Anfang des Befehls bestimmt, wann die darauf folgenden Befehle ausgeführt oder zurückgesetzt werden. Befehle können auch Ereignisse senden mit dem Stichwort emit, gefolgt von der Bezeichnung des Ereignisses und gegebenenfalls den zu sendenden Variablen. Falls eine Variable gegeben wird, muss die Grösse des Ereignisses der Grösse des zu schickenden Arguments entsprechen. Ereignisse erlauben Befehle an anderen Netzwerkelementen auszulösen oder mit einem externen Programm zu kommunizieren.

Um die Ausführung von ähnlichen Codes bei neuen Ereignissen zu ermöglichen, dürfen die Befehle nicht blockieren und dürfen deshalb keine Endlosschlaufen enthalten. In der Robotik bedeutet dies, dass ein traditionelles Robotersteuerungsprogramm gewisse Vorgänge in einer Endlosschlaufe durchführt, während die Befehlssprache Aseba dieselben Vorgänge nur in einem auf Sensoren bezogenen Ereigniss ausführt.

Beispiel:

var run = 0

onevent start
#falls das Ereignis 'start' eintritt, starte
run = 1

onevent stop
#falls das Ereignis 'stop' eintritt, halte an
run = 0

onevent ir_sensors
if run == 1 then
#wenn==1, dann
    emit sensors_values proximity_sensors_values
    #Gib das Ereignis 'sensors_values' aus mit den Variablen 'proximity_sensors_values'
end  
#Ende des 'if' blocks

Unterprogramme

Falls dieselben Sequenzen von Operatoren an zwei oder mehreren Orten innerhalb des Codes auftreten, genügt es, ein Unterprogramm zu schreiben und dieses von diversen Orten her aufzurufen. Ein Unterprogramm kann mit dem Stichwort sub und dem Namen des Unterprogramms definiert werden. Ein Unterprogramm kann mit dem Stichwort callsub aufgerufen werden. Unterprogramme können keine Argumente beinhalten und weder direkt noch indirekt rekursiv wirken. Allerdings haben Unterprogramme Zugriff auf alle Variablen.

Beispiel:

var v = 0

sub toto
#Unterprogramm Namens toto
v = 1

onevent test
#falls Ereignis Namens teste eintritt, dann
callsub toto
#rufe Unterprogramm toto auf

Vorgegebene Funktionen

Die Befehlssprache Aseba wurde entwickelt mit dem Ziel, auch Programmierern ohne Vorkenntnissen zu ermöglichen, die einfachen Befehle schnell zu verstehen und effizient in Mikrosteuerungen zu implementieren. Um komplexe oder stark Ressourcen verbrauchende Prozesse zu programmieren, werden gewisse vorprogrammierte Funktionen zur Verfügung gestellt. Zum Beipiel gibt es in Aseba eine vorgegebene Funktion, die das Skalarprodukt berechnet.

Vorgegebene Funktionen sind sicher, weil sie die Argumente spezifizieren und überprüfen. Argumente können Konstanten, Variablen oder Feldzugänge sein. Später können auf ganze Felder, einzelne Elemente oder Teilfelder zugegriffen werden. Vorgegebene Funktionen sehen ihre Argumente als Referenz an und können ihre Inhalte verändern, aber keine Werte generieren. Durch das Stichwort call können vorgegebene Funktionen aufgerufen werden.

Beispiel:

var a[3] = 1, 2, 3
var b[3] = 2, 3, 4
var c[5] = 5, 10, 15
var d
call math.dot(d, a, b, 3)
call math.dot(d, a, c[0:2], 3)
call math.dot(a[0], c[0:2], 3)