Inhaltsverzeichnis
|
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 von Aseba dar, 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 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
Beispiel:
#*
Dies ist ein Kommentar
über mehrere Linien verteilt.
*#
var b # kurzer Kommentar
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.
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 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
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 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 definiert
var i = 600
if i > THRESHOLD then
i = THRESHOLD - 1
end
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'
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 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]
Mathematische Ausdrücke sind ein sehr allgemeines Werkzeug. Sie können in vielen verschiedenen Situationen benutzt werden, zum Beispiel:
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
Zwei Konstruktionen ermöglichen Schleifen: while und for.
Eine while Schleife 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
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 gefolgt vom Namen des Unterprogramms definiert werden. Ein Unterprogramm kann mit dem Stichwort callsub aufgerufen werden. Unterprogramme müssen immer erst definiert werden, bevor sie aufgerufen werden können. Unterprogramme können weder Argumente beinhalten noch rekursiv sich selbst aufrufen, weder direkt noch indirekt. Allerdings haben Unterprogramme Zugriff auf alle Variablen.
Beispiel:
var v = 0
sub toto
#Unterprogramm Namens toto
v = 1
onevent test
#falls Ereignis Namens test eintritt, dann
callsub toto
#rufe Unterprogramm toto auf
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
Mit einem return-Befehl ist es möglich ein Unterprogramm früh zu verlassen oder die Ausführung einer Ereignisse abzubrechen.
Beispiel:
var v = 0
sub toto
if v == 0 then
return
end
v = 1
onevent test
callsub toto
return
v = 2
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)
Diese Dokumenten sind empfehlenswert: