Universität Paderborn

Werbung
Technical Reports and Working Papers
Angewandte Datentechnik (Software Engineering)
Dept. Of Electrical Engineering and Information Technology
The University of Paderborn
Graphen und elementare
Algorithmen/
Roboterprogrammierung
Tutorial zum Projekt „Angewandte
Programmierung“
F. Belli, A. Hollmann, S. Padberg
Copyright
All rights including translation into other languages is reserved by the authors.
No part of this report may be reproduced or used in any form or by any means
- graphically, electronically or mechanically, including photocopying, recording,
taping or information and retrieval systems - without written permissions from
the authors and or projects.
Technical Report 2006/2 (Version 2.0, März 2009)
D-33095 Paderborn, Pohlweg 47-49
http://adt.upb.de
Tel.:++49-5251-603445
Fax: 603246
email: [email protected]
Zusammenfassung
Dieses Tutorial dient zur Vorbereitung auf das Projekt „Angewandte Programmierung“ im
Grundstudium des Studiengangs Elektrotechnik/Informationstechnik. Im Anschluss an eine
Klärung verwendeter Begriffe, wie z.B. von Adjazenzmatrizen und Adjazenzlisten zur
Darstellung von Graphen, werden elementare Algorithmen für Tiefen- und Breitensuche
vorgestellt. Es folgen Hinweise auf die Implementierung des eingeführten graphtheoretischen
Instrumentariums in der Programmiersprache C. Zusätzlich wird eine Einführung in die
Roboterprogrammierung mit dem RV-M1 gegeben.
Gliederung
1.
2.
Einleitung ........................................................................................................................... 2
(Un-)Gerichtete Graphen – formale Einführung................................................................ 5
2.1.
Begriffliche Klärung .................................................................................................. 6
2.2.
Darstellung von Graphen ........................................................................................... 7
2.3.
Algorithmen auf Graphen........................................................................................... 9
2.3.1.
Breitensuche ....................................................................................................... 9
2.3.2.
Tiefensuche ...................................................................................................... 12
3. Matrizen und Felder in C ................................................................................................. 15
4. Roboterprogrammierung (RV-M1) .................................................................................. 21
4.1.
Abkürzungsverzeichnis ............................................................................................ 21
4.2.
Einführung des Roboters RV-M1 ............................................................................ 21
4.3.
Roboterarm............................................................................................................... 24
4.3.1.
Roboterhand ..................................................................................................... 25
4.4.
Drive Unit (DU) ....................................................................................................... 26
4.5.
Teaching Box (TB)................................................................................................... 27
4.5.1.
Ansteuerungsbefehle ........................................................................................ 27
4.6.
Einzelbefehleingabe mit Hilfe eines PCs ................................................................. 29
4.7.
Ein kleines „RoboterASM“ Programm (Linux-Bash) ............................................. 30
5. Ansteuerung des Roboters mit Hilfe der Programmiersprache C .................................... 32
5.1.
C-Code-Gliederung und Einbinden von Header-Dateien ........................................ 32
5.2.
Verarbeitungsprozeß vom Quellcode zur ausführbaren Datei ................................. 34
5.3.
Ein kleines C-Programm für den Roboter................................................................ 36
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
1. Einleitung
Viele Probleme lassen sich durch Objekte und deren Beziehungen zueinander modellieren.
„Modellieren“ bedeutet in diesem Zusammenhang, dass das Weltbild auf den betrachteten, relevanten Teil des Ganzen und dessen Eigenschaften reduziert wird. Ein anschauliches, nichtsdestoweniger exaktes Mittel dafür stellen Graphen zur Verfügung, die aus Knoten und Kanten
bestehen. Knoten sind Elemente einer Menge gleichartiger Objekte; Kanten hingegen stellen
die Beziehungen zwischen den Objekten dar. Folgende Beispiele mögen die Verwendung von
Graphen in der Technik und dem Alltag veranschaulichen.
Eine elektrische Schaltung besteht aus verschiedenen Bauteilen wie z.B. Transistoren, Widerständen, Kondensatoren etc., die durch Leitungen miteinander verbunden sind. Es ergeben
sich Fragen, wie beispielsweise die Bauteile dimensioniert werden oder ob die Schaltung korrekt funktioniert, wenn sie hergestellt wird.
Abbildung 1: Schaltplan 1
Bei der Analyse von Schaltwerken wird die Funktionsweise diverser Komponenten grafisch
und/oder tabellarisch dargestellt. Dabei ist es üblich, den Knoten und Kanten des jeweiligen
Graphen besondere Bedeutungen zuzuweisen. Diese Sachverhalte werden Sie später im Studium, z.B. in der Digitaltechnik, kennen lernen. In diesem Tutorial geht es darum, dass Graphen als allgemeines Darstellungsmittel eingeführt werden und vor allem der Umgang mit
Graphen durch geeignete Algorithmen demonstriert wird.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
2
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Sequential Network
Circuit Diagram
State Diagram
x=0
x=0
I
y1
&
≥1
T
y2
&
D
≥1
x=1
U
D
A
D
x=1
x=1
F/F
&
&
F/F
B
x
clock
x=0
a
y1 y2
A
B
C
D
0
0
1
1
0
1
1
0
x=1
C
x=0
U
0
1
1
0
Truth Table (State Assignments)
Abbildung 2: Schaltplan 2
Nicht nur für technische Inhalte, sondern auch für das tägliche Leben werden komplexe Zusammenhänge oft grafisch dargestellt, wie z.B. das Schienennetz der Bahn durch einen Verbund von Bahnhöfen. Ebenso sind Städte durch Straßen verbunden. Kürzeste oder schnellste
Wege zwischen zwei Bahnhöfen/Städten oder eine Rundreise, bei der jeder Bahnhof bzw.
jede Stadt besucht wird, sind hier von Interesse.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
3
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Abbildung 3: Liniennetzplan
Programmablaufpläne oder Steuerflussgraphen, wie sie in der Vorlesung Datenverarbeitung
vorgestellt wurden, bilden ein weiteres Beispiel.
Abbildung 4: Programmablaufplan
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
4
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
2. (Un-)Gerichtete Graphen – formale Einführung
Ein gerichteter Graph G=(V, E) besteht aus einer Menge V von Knoten (engl. vertex bzw.
node) und einer Menge E von (gerichteten) Kanten (engl. arc bzw. edge), wobei gilt E⊆V×V,
d.h. E stellt eine Teilmenge des kartesischen Produktes V×V dar.
Beispiel (s. Abbildung 5):
V = {0,1,2,3,4}; E = {(0, 4), (1, 0), (1, 2), (1, 3), (3, 2), (4, 4)}
Die Kante (4, 4) wird auch als Schleife (self loop) bezeichnet.
0
4
1
2
3
Abbildung 5: Ein gerichteter Graph
Bei einem ungerichteten Graphen G=(V, E) ist die Kantenmenge E wie folgt definiert:
E ⊆ {{x,y} | x,y ∈ V}
Beispiel (s. Abbildung 6):
V = {a, b, c, d, e}
E = {{a, e}, {b, a}, {b, c}, {b, d}, {d, c}, {e}}
a
e
b
c
d
Abbildung 6: Ein ungerichteter Graph
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
5
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
2.1. Begriffliche Klärung
Im Zusammenhang mit Graphen existieren eine Reihe von Begrifflichkeiten, von denen einige elementare im Folgenden erläutert werden.
Man spricht von einem Teilgraphen G’=(V’, E’) eines Graphen G=(V, E), wenn gilt: E’⊆ E
und V’⊆ V.
Der Knotengrad eines Knotens in einem ungerichteten Graphen entspricht der Summe der
Kanten, die in dem Knoten enden. Für den Knoten b des Graphen aus Abbildung 6 beträgt der
Knotengrad beispielsweise 3.
Im gerichteten Graphen unterscheidet man zusätzlich den Eingangsgrad (Summe der Kanten,
die im Knoten eingehen) als auch den Ausgangsgrad (Summe der ausgehenden Kanten). Der
Knotengrad eines Knotens entspricht der Summe aus dem Eingangsgrad und Ausgangsgrad
des Knotens. Beispielsweise hat der Knoten 1 des Graphen aus Abbildung 5 den Ausgangsgrad 3 und Eingangsgrad 0.
Ein Weg bzw. Pfad in einem Graph besteht aus einer Folge von Knoten, die durch Kanten
miteinander verbunden sind: (v1, v2, ..., vk) Die Länge des Weges beträgt dann k-1.
Im gerichteten Graphen gilt für Pfade: (vi, vi+1) ∈ E, im ungerichteten {vi, vi+1} ∈ E mit
i∈ {1,...,k-1}.
Ein ungerichteter Graph ist zusammenhängend, wenn von jedem Knoten ein Pfad zu allen anderen Knoten des Graphen existiert. Im gerichteten Graphen wird vom starken Zusammenhang gesprochen.
Wenn für einen Weg (v1, v2, ..., vk) gilt, dass seine Kanten paarweise verschieden sind und
v1=vk ist, so nennt man diesen Weg im gerichteten Graph einen Zyklus, im ungerichtetem
Graphen nennt man solch einen Weg Kreis.
Ein Euler-Weg bzw. Euler-Kreis enthält jede Kante eines Graphen genau einmal. Ein Hamilton-Kreis enthält jeden Knoten genau einmal.
Von Leonard Euler stammt das bekannte Königsberger Brückenproblem. Es lautet: Gibt es
einen Rundweg, so dass man genau einmal die sieben Brücken des Pregels überquert und
beim Ausgangspunkt wieder ankommt?
Abbildung 7: Königsberger Brückenproblem
Oft ist es notwendig, den Knoten und Kanten eines Graphen weitere Informationen zuzuordnen. Kantengewichte können beispielsweise dafür benutzt werden, um Entfernungen
zwischen Knoten oder bestimmte Kosten zu ermitteln.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
6
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
2.2. Darstellung von Graphen
Um Graphen bzw. Graphalgorithmen in Programmen zu behandeln, ist eine geeignete Repräsentation notwendig. Gebräuchliche Arten der Darstellung sind durch die Adjazenzmatrix und
die Adjazenzliste gegeben. Dazu werden Knotennamen auf ganze Zahlen abgebildet. Im Weiteren wird davon ausgegangen, dass die Implementierung in einer Programmiersprache erfolgt, in der der Zugriff auf ein Feld mit n Komponenten mit den Indizes von 0,..., n-1 erfolgt.
Für den Graphen in Abbildung 5 ist eine Abbildung auf Zahlen nicht notwendig; die Knotenbezeichner können hier direkt als Indizes verwendet werden. Für den Graphen in
Abbildung 6 ist eine Abbildung auf die Zahlen von 0,...,4 erforderlich. Im Allgemeinen erfolgt eine Abbildung auf die Zahlen von 0,...,|V|-1; die Schreibweise |V| gibt die Kardinalität
der Menge V an, d.h. die Anzahl der Knoten des Graphen.
Bei der Darstellung als Adjazenzmatrix wird eine Matrix der Dimension |V|×|V|, bestehend
aus booleschen Variablen, genutzt. Ein Eintrag [x][y] wird in der Matrix auf true bzw. 1 gesetzt, falls im gerichteten Graphen eine Kante (x,y) existiert. Eine Kante {x,y} eines ungerichteten Graphen wird aus Vereinfachungsgründen in den Einträgen [x][y] und [y][x] gespeichert, obwohl dieses eine redundante Form der Speicherung darstellt. Falls zwischen zwei
Knoten keine Kante existiert, wird der Eintrag im Feld auf false bzw. 0 gesetzt. Der Graph
aus Abbildung 5 sieht als Adjazenzmatrix wie folgt aus:
0
1
2
3
4
0
0
0
0
0
1
1
1
0
1
1
0
2
0
0
0
0
0
3
0
0
1
0
0
4
0
0
0
0
1
Eine weitere Darstellungsart ist die Adjazenzliste. Zu jedem Knoten werden die ihm adjazenten (benachbarten) Knoten in einer Liste gespeichert. Dementsprechend sieht der Graph aus
Abbildung 5 wie folgt aus:
0
4
1
0
2
3
2
3
2
4
4
Die nachfolgende Tabelle enthält eine Gegenüberstellung der beiden Darstellungsarten bezüglich der Laufzeit verschiedener Operationen (im O-Kalkül).
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
7
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Adjazenzmatrix
Adjazenzliste
Speicherbedarf
O(|V|∗|V|)
O(|V|+|E|)
Einfügen einer Kante
O(1)
O(1)
Einfügen eines Knotens
O(|V|)
O(1)
Löschen einer Kante
O(1)
O(|V|)
Löschen eines Knotens
O(|V|∗|V|)
O(|V|+|E|)
In der Regel werden Adjazenzmatrizen nur dann benutzt, wenn der Graph genügend viele
Kanten enthält (im worst case gilt für die Anzahl der Kanten eines Graphen: |E| = |V|2). Ansonsten wird relativ viel Speicherplatz verschwendet. Allein das Initialisieren der Matrix
kostet quadratisch viel Zeit in Bezug auf die Anzahl der Knoten. Somit kann allein das Initialisieren die dominierende Laufzeit für einen Algorithmus darstellen, während der eigentliche
Algorithmus in Linearzeit in Bezug auf die Anzahl der Knoten abläuft.
Werden Kantengewichte im Graphen benötigt, so können die Einträge innerhalb der Matrix
benutzt werden, d.h. anstatt true und false werden Kantengewichte eingetragen, die zugleich
anzeigen, ob eine Kante existiert. An den Positionen der Matrix, die mit false gekennzeichnet
wurden, muss nun ein Wert stehen, der ein unzulässiges Kantengewicht darstellt, wie z.B. -1
für Kantengewichte ∈ ℕ oder −∞ für Kantengewichte ∈ ℤ. Falls keine Kantengewichte angegeben sind, wird in der Regel von einer Gewichtung von 1 ausgegangen.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
8
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
2.3. Algorithmen auf Graphen
Für Graphen existiert eine Vielzahl von Algorithmen. Zwei elementare, oft benutzte Algorithmen werden im Folgenden vorgestellt – die Tiefensuche (engl. depth-first search) und Breitensuche (engl. breadth-first search). Beide Verfahren dienen dazu, systematisch die Knoten
und Kanten eines Graphen zu überprüfen, bis das gesuchte Objekt gefunden wird – falls vorhanden. Damit lassen sich weitere Fragestellungen beantworten, wie zum Beispiel, ob ein
Graph zusammenhängend ist, ob er Zyklen enthält oder ob zwischen zwei Knoten ein Weg
existiert.
Ausgangspunkt für die Suche stellt ein (beliebiger) Startknoten dar, von dem dann die anderen Knoten aus „besucht“ werden (sofern möglich).
2.3.1. Breitensuche
Bei der Breitensuche werden jeweils die Knoten besucht, die die gleiche Entfernung zum
Startknoten haben, d.h. es werden alle Knoten mit der Pfadlänge k zum Startknoten vor allen
Knoten der Pfadlänge k+1 besucht, d.h. ausgehend von einem Startknoten s werden zunächst
alle Knoten besucht, die die Entfernung 1 haben, danach die Entfernung 2, 3, ... usw.
Das nachfolgende Beispiel verdeutlicht die Vorgehensweise der Breitensuche. Der Startknoten sei Knoten 5. Zunächst wird Knoten 1 besucht (Entfernung beträgt 1 zum Startknoten).
Ein Knoten bekommt eine graue Schraffierung, wenn er zum ersten Mal besucht wird. Eine
Kante wird gestrichelt dargestellt, wenn sie zum ersten Mal „durchlaufen“ wurde:
1
5
1
5
0
0
2
4
3
2
4
3
Anschließend werden die Knoten 4 und 5 besucht (Entfernung beträgt 2). Knoten 5 wurde bereits besucht und wird daher nicht weiter betrachtet.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
9
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
1
5
1
5
0
0
2
4
2
4
3
3
Es folgen die Knoten mit der Entfernung 3. Dieses sind die Knoten 0 und 3.
1
5
1
5
0
0
2
4
2
4
3
3
Nachfolgeknoten von 0 und 3 haben die Entfernung 4, wobei die Nachfolger 1 und 5 des
Knotens 0 sowie der Nachfolger 0 des Knotens 3 schon betrachtet wurden.
1
5
1
5
0
0
2
4
3
2
4
3
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
10
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
1
5
1
5
0
0
2
4
2
4
3
3
Zuletzt wird von Knoten 2 aus Knoten 5 besucht, der allerdings schon besucht wurde.
1
5
0
2
4
3
Der folgende Algorithmus (dargestellt in einem Pseudocode, d.h. vereinfachter Schreibweise)
stellt eine einfache Breitensuche dar. Die Funktion BFS hat die drei Parameter G (der Graph
G=(V, E)), s (der Startknoten) und ein Feld visited[] der Größe |V|, in dem vermerkt wird, ob
ein Knoten bereits besucht wurde.
Zudem wird eine verkettete Liste Q benutzt, die nach dem first-in first-out-Prinzip (FiFo) arbeitet. Für die Liste Q gibt es die Funktion enqueue zum Einfügen eines Elementes und die
Funktion dequeue, die das erste Element der Liste löscht und zurückgibt (Erinnerung:
„queue“ bedeutet „Warteschlange“).
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
11
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
BFS(G, s, visited[])
1
Q ←∅
2
ENQUEUE(Q,s)
3
while Q ≠ ∅
4
u ← DEQUEUE(Q)
5
visited[u] := true
6
for each node v ∈ successors(u)
7
if visited[v] = false
8
ENQUEUE(Q,v)
Der Funktion BFS arbeitet wie folgt:
-
In Zeile 1 wird die Liste initialisiert – angedeutet durch die leere Menge.
-
Dann wird der Startknoten in die Liste eingefügt, in Zeile 2.
-
Solange die Liste nicht leer ist (Zeile 3), d.h. solange noch Knoten existieren, die noch
nicht besucht wurden, wird zunächst ein Knoten u aus der Liste herausgeholt (Zeile 4)
-
und als besucht markiert (Zeile 5).
-
Für jeden Nachfolgeknoten v von u (d.h. (u,v) ∈ E) (Zeile 6) wird geprüft, ob dieser
bereits besucht wurde (Zeile 7).
-
Falls nicht, wird v in die Liste eingefügt (Zeile 8).
2.3.2. Tiefensuche
Im Gegensatz zur Breitensuche, wo jeweils Knoten mit der gleichen Entfernung zum Startknoten besucht werden, besteht die Strategie der Tiefensuche darin, immer tiefer im Graphen
zu suchen, sofern möglich.
Das folgende Beispiel soll das Prinzip der Tiefensuche verdeutlichen. Die Tiefensuche wird
im Knoten 5 gestartet. Gibt es mehrere Möglichkeiten einen Nachfolgeknoten zu wählen, so
wird im Beispiel der Knoten mit dem kleineren Index gewählt. Im Beispiel wird somit als
nächstes der Knoten 1 besucht (andere Knoten stehen hier nicht zur Auswahl).
Ein Knoten bekommt eine graue Schraffierung, wenn er zum ersten Mal besucht wird. Eine
Kante wird gestrichelt dargestellt, wenn sie zum ersten Mal „durchlaufen“ wurde.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
12
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
1
5
1
5
0
0
2
4
2
4
3
3
Nach Knoten 1 wird der Knoten 4 besucht. Dort besteht die Wahl, zunächst Knoten 0 oder
Knoten 3 zu besuchen. Es wird der Knoten mit dem kleineren Index gewählt, d.h. Knoten 0.
1
5
1
5
0
0
2
4
2
4
3
3
Im Knoten 0 gibt es 3 mögliche Nachfolger: 1, 2 und 5. Wieder wird der Knoten mit dem
kleinsten Index gewählt – also Knoten 1. Bei Besuch des Knoten 1 wird nun festgestellt, dass
dieser bereits besucht worden ist. Dies hat zur Folge, dass die Suche im Knoten 1 nicht mehr
fortgesetzt wird. Es erfolgt ein (rekursiver) Rücksprung zum Knoten 0. Von dort aus wird
nun Knoten 2 besucht.
1
5
1
5
0
0
2
4
3
2
4
3
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
13
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Von Knoten 2 aus wird Knoten 5 besucht. Da Knoten 5 bereits besucht wurde, erfolgt ein
Rücksprung zu Knoten 2. Dort gibt es allerdings keine weiteren Nachfolger, so dass ein
zweiter Rücksprung zu Knoten 0 erfolgt. Von dort wird nochmals Knoten 5 besucht.
1
5
1
5
0
0
2
4
2
4
3
3
Es erfolgt ein Rücksprung bis zum Knoten 4. Von dort aus werden die Knoten 3 und dann der
Knoten 0 besucht.
1
5
1
5
0
0
2
4
3
2
4
3
Es erfolgen Rücksprünge von Knoten 0 zu 3, von 3 zu 4 und von 4 zu 1. Dort wird noch einmal Knoten 5 besucht. Abschließend erfolgt ein Rücksprung zum Knoten 5, in dem die Suche
gestartet wurde. Da keine weiteren Nachfolgeknoten von Knoten 5 aus existieren, wird die
Tiefensuche beendet.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
14
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
1
5
0
2
4
3
Der nachfolgende Pseudocode stellt eine rekursive Variante der Tiefensuche dar.
Die Tiefensuche DFS hat die drei Parameter G (der Graph G=(V, E)), v (ein (Start-)Knoten)
und ein Feld visited[] der Größe |V|, in dem vermerkt wird, ob ein Knoten bereits besucht
wurde.
DFS(G, u, visited[])
1
visited[u] := true
2
for each node v ∈ successors(u)
3
if visited[v] = false
4
DFS(G, v, visited)
-
In Zeile 1 wird der Knoten v als besucht markiert.
-
Für jeden Nachfolgeknoten v von u (d.h. (u,v) ∈ E) (Zeile 2) wird geprüft, ob dieser
bereits besucht wurde (Zeile 3).
-
Falls nicht, wird die Tiefensuche rekursiv aufgerufen.
Die Tiefensuche kann ebenfalls nicht-rekursiv implementiert werden – ähnlich wie die Breitensuche. Dafür ist die FiFo-Liste durch eine last-in first-out-Liste (LiFo) zu ersetzen.
3. Matrizen und Felder in C
In der Programmiersprache C gibt es verschiedene Möglichkeiten, eine Matrix bzw. ein Feld
mit 2 Dimensionen anzulegen. Im Folgenden wird beschrieben, wie eine n×n-Matrix, die aus
int-Werten bestehen soll, angelegt werden kann. Falls die Dimension der Matrix beim Programmieren bekannt ist und nur lokal innerhalb einer Funktion benötigt wird, reicht die folgende Deklaration:
int matrix[5][5];
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
15
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Mit Beendigung des umfassenden Blocks (in der Regel mit Funktionsende) wird der Speicher
automatisch wieder freigegeben.
Das folgende Konstrukt darf erst seit dem ANSI-C99 Standard benutzt werden - die Größe
der Matrix bestimmt sich hier erst zur Laufzeit:
int n;
...
//n bestimmt sich zur Laufzeit
n = ...
...
int matrix[n][n];
Falls die Gültigkeit mit Blockende/Funktionsende nicht aufhören darf oder sich die Größe erst
zur Laufzeit bestimmt (Compiler bis ANSI-C90 Standard), muss der Speicher dynamisch
angefordert werden. Dieses wird durch den nachfolgenden Programmcode geleistet:
int n;
int i,j;
int **ppMatrix;
//n = ...
ppMatrix = (int **) malloc (n*sizeof(int*));
for(i=0; i<n; i++)
ppMatrix[i] = (int*) malloc(n*sizeof(int));
//Initialisierung/Berechnung
for(i=0; i<n; i++)
for(j=0; j<n; j++)
ppMatrix[i][j] = 0;
//Speicherfreigabe
for(i=0; i<n; i++)
free(ppMatrix[i]);
free(ppMatrix);
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
16
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Nun folgt eine detailliertere Betrachtung. Die folgende Deklaration legt eine Zeigervariable
namens ppMatrix an. Typ ist ein Zeiger auf einen int-Zeiger (**).
int **ppMatrix;
Anschließend wird ein dynamisches Feld der Länge n angelegt, dessen Komponenten aus
Zeigern auf int bestehen:
ppMatrix = (int **) malloc (n*sizeof(int*));
Für n=4 sieht das Feld nun folgendermaßen aus:
Die folgende Schleife legt nun den „eigentlichen“ Speicher für die Matrix an:
for(i=0; i<n; i++)
ppMatrix[i] = (int*) malloc(n*sizeof(int));
Die Freigabe des Speichers läuft wiederum spaltenweise in zwei Schritten ab:
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
17
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
for(i=0; i<n; i++)
free(ppMatrix[i]);
free(ppMatrix);
Alternativ zu dem eben beschriebenen Verfahren ist es möglich, ein eindimensionales Feld
der Größe n2 anzulegen und dieses über einen entsprechenden Index anzusprechen:
int *pMatrix, n;
...
pMatrix = (int *) malloc (n*n*sizeof(int));
Der Zugriff auf das Feld sieht nun wie folgt aus:
for(i=0; i<n; i++)
for(j=0; j<n; j++)
pMatrix[i*n+j] = 0;
Die Speicherfreigabe erfolgt durch:
free(pMatrix);
Vorteilhaft an dieser Alternative ist die einfachere Art der Speicheranforderung und -freigabe.
Allerdings ist der Zugriff auf das Feld schwieriger bzw. in der Regel fehlerträchtiger.
Bei der Verwendung von Matrizen (Feldern) kann der Bedarf entstehen, die Matrix zur Laufzeit zu verkleinern oder zu vergrößern. Eine denkbare Lösung ist es, eine zweite Matrix der
neuen benötigen Dimension zu erstellen, die Werte der alten Matrix zu kopieren und schließlich die alte Matrix freizugeben.
Eine schnellere bzw. effizientere Möglichkeit bietet sich in C mittels des Befehls realloc an:
void *realloc (void *pointer, int size);
Wurde mittels der Funktion malloc dynamischer Speicher angefordert, so lässt sich mittels
realloc die Größe des Speicherbereichs nachträglich ändern. Beim Aufruf von realloc wird
der Zeiger auf den Anfangsbereich des Speichers übergeben (pointer) sowie die neue
Größe (size). Die Daten des Speicherbereichs bleiben bis zum Minimum aus der alten und
neuen Größe unverändert. Zurückgegeben wird die Anfangsadresse des (neuen) Speicherbereichs oder NULL, falls die Anforderung nicht erfüllt worden kann.
Im folgenden Beispiel wird ein Feld, dessen Speicher dynamisch angefordert wurde, um eine
weitere Komponente vergrößert:
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
18
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
int n=5;
int *pMatrix = (int*) malloc (n*sizeof(int));
...
pMatrix = (int *) realloc(pMatrix, (n+1)*sizeof(int));
Analog lässt sich auch ein zweidimensionales Feld vergrößern. Das nachfolgende Beispiel
deutet an, wie ein dynamisch angelegtes Feld der Dimension 4×4 auf 5×5 erweitert werden
kann:
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
19
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
20
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
4. Roboterprogrammierung (RV-M1)
In der Industrie werden seit den sechziger Jahren des vorigen Jahrhunderts Industrieroboter
eingesetzt, um gefährliche Arbeiten zu erledigen oder monotone Vorgänge nicht von
Menschen durchführen zu lassen.
In diesem Projekt wird der Industrieroboter „RV – M1“ der Firma Mitsubishi verwendet.
Die Abbildungen stammen aus dem Benutzerhandbuch des Roboters.
4.1. Abkürzungsverzeichnis
DU
LH
PC
Pos.
RAM
RH
TB
Drive Unit
Linke Hand
Personal Computer
Position
Random Access Memory
Rechte Hand
Teaching Box
4.2. Einführung des Roboters RV-M1
Abbildung 1 zeigt den Aufbau des Robotersystems.
Zu diesem System gehören neben den motorgesteuerten Roboterarmen bzw. der
motorgesteuerten Händen, auch die „Drive Units (DUs)“ (die Ansteuerungsrechner) im
Schaltschrank, die „Teaching Boxes (TBs)“, mit der die Roboter manuell gesteuert werden
können und ein PC, der die Ansteuerbefehle später an die Drive Units senden wird.
In diesem Projekt werden zwei Roboter mit Hilfe von zwei DUs und einem PC angesteuert
(siehe Abbildung 2 ).
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
21
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Abbildung 1 Das Robotersystem (zwei RV-M1 von Mitsubishi)
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
22
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Qualitätsdepots
1&2
Roboter 2
Förderband
2
LH
RH
1
LH
RH
Greifarm
Roboter 1
Förderband
Ausschussablage
Greifarm
Förderband
Qualitätsablage
Förderband
Qualitätsablage
Förderband
Ausschussablage
Förderband
Qualitätsablage
Objektmatrix
Abbildung 2 Schematischer Aufbau des Projektsystems
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
23
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
4.3. Roboterarm
Die Namen der Gelenke des Roboters entsprechen denen des menschlichen Körpers (siehe
Abbildung 3 ).
Der Bewegungsraum des Roboters ist abgegrenzt, es ergeben sich folgende Bereiche:
Drehung (Rumpf/Mittelstück):
Drehgeschwindigkeit (Rumpf/Mittelstück):
Drehung (Schulter):
Drehgeschwindigkeit (Schulter):
Drehung (Ellenbogen):
Drehgeschwindigkeit (Ellenbogen):
Neigungswinkelbereich (Handgelenk):
Neigungswinkelgeschwindigkeit (Handgelenk):
Drehung (Handgelenk):
Drehgeschwindigkeit (Handgelenk):
max. 300°
max. 120° / s
max. 130°
max. 72° / s
max. 130°
max. 72° / s
±90°
max.100° / s
max. 300°
max. 120° / s
bel
Abbildung 3 Bezeichnungen und Arbeitsraum
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
24
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
4.3.1. Roboterhand
Die Roboterhand (siehe Abbildung 4 ) kann eine maximale Masse von 3,5kg tragen, wobei
die Haltekraft in 16 Schritten erhöht werden kann.
Die maximale Griffweite ist mit 60mm erreicht.
Die Hand wird über das Spiralkabel an die DU angeschlossen und kann die binären Zustände
offen (O, open) und geschlossen (C, closed) annehmen.
Abbildung 4 Roboterhand
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
25
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
4.4. Drive Unit (DU)
Abbildung 5 zeigt das Bedienfeld und die Rückseite der DU.
Das Bedienfeld bietet einfache Schaltmöglichkeiten, die auf das interne Programm Einfluss
nehmen kann.
EMG. STOP unterbricht einen Programmablauf (Error-LED blinkt), es ist ein Power-Reset
zum erneuten Starten notwendig.
STOP unterbricht einen Programmablauf (ein laufender move-Befehl wird noch ausgeführt).
START beginnt einen unterbrochenen oder neuen Programmablauf von vorn.
Es gibt je einen separaten Anschluss für die TB und für die serielle Schnittstelle, über welche
ein Programm vom PC an die DU gesendet werden kann.
Der technische Aufbau der DU bleibt bei diesem Projekt unverändert.
Rückansicht
Abbildung 5 Drive Unit
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
26
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
4.5. Teaching Box (TB)
Wie in Kapitel 4.2 schon erwähnt wurde, ist es möglich, den Roboterarm und die
Rotoberhand manuell zu steuern. Dazu wird die in Abbildung 6 gegebene TB verwendet.
Die Funktionen der wichtigsten Tasten werden im Kapitel 4.5.1 erläutert.
Abbildung 6 Teaching Box
4.5.1. Ansteuerungsbefehle
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
27
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Folgende Befehle werden im Projekt für die Einführung (manuelle Ansteuerung) benötigt:
NST (+ENT)
ORG (+ENT)
Anfahren der Nestposition (Kalibrierung Mechanik und Elektronik)
Anfahren der Origin-Position
X+/B+
X-/BY+/S+
Y-/SZ+/E+
Z-/EP+
PR+
R◄O►
►C◄
Rumpfdrehung im Uhrzeigersinn
Rumpfdrehung entgegen dem Uhrzeigersinn
Schulter aufwärts
Schulter abwärts
Ellebogen aufwärts
Ellebogen abwärts
Handgelenk aufwärts
Handgelenk abwärts
Drehung Handgelenk im Uhrzeigersinn
Drehung Handgelenk entgegen dem Uhrzeigersinn
Öffnen der Hand
Schließen der Hand
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
28
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
4.6. Einzelbefehleingabe mit Hilfe eines PCs
Die Befehle der TU und viele weitere können auch über eine serielle Schnittstelle an die DU
gesendet werden, um den Roboter zu steuern.
Die Befehle, die über die Schnittstelle als ASCII-Zeichen an die DU übertragen werden,
bestehen nur aus zwei Buchstaben und optional aus einem oder mehreren Parametern.
Beispiel:
NST (TB)
->
NT
In unserem Projekt wird ein PC mit Linux als Betriebssystem im Shell-Modus
(Eingabeaufforderung) verwendet.
Daher lautet die Befehlsfolge für das obere Beispiel:
echo “NT“ > ttyS0
echo “NT“ > ttyS1
für Roboter 1 (an serieller Schnittstelle 0) und
für Roboter 2 (an serieller Schnittstelle 1)
Um die Bewegungsgeschwindigkeit eines Roboters einzustellen, wird folgender Befehl
verwendet:
SP <Geschwindigkeit (0 bis 9)>, <Beschleunigungsparameter (H oder L)>
z.B.: echo “SP 9, H“ > ttyS0
für max. Geschwindigkeit für Roboter 1
Da sich die Roboterbefehlssprache sehr einer hardwarenahen Programmiersprache
(„Assembler“) ähnelt, kann im Folgenden der Begriff „RobASM“ verwendet werden.
Sämtliche Befehle sind im Handbuch des Roboters aufgelistet.
Desweiteren sind im Projektverzeichnis des PCs zwei Links auf die seriellen Schnittstellen
angelegt, um die Angabe „ttySx“ zu umgehen. Die Namen der Links lauten „robo1“ und
„robo2“.
Somit ist es möglich, die Einzelbefehle, wie folgt, zu senden:
echo “<Befehl>“ > robo1
echo “<Befehl>“ > robo2.
oder
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
29
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
4.7. Ein kleines „RoboterASM“ Programm (Linux-Bash)
Die Befehle, die in Kapitel 4.6 erläutert wurden, werden immer sofort ausgeführt.
Um ein RobASM-Programm zu erstellen, müssen Zahlen, die die Reihenfolge angeben, vor
jeden Befehl angegeben werden. Die nummerierten Befehle werden in das RAM der DU
geladen, um diese später durch den Befehl „RN“ zu starten.
Ein ausführbares Programm kann durch eine Linux-Bash-Datei realisiert werden. Eine BashDatei enthält Unix-Kommandozeilenbefehle, die nacheinander abgearbeitet werden. Den
Kopf der Datei bildet die Anweisung „#!/bin/zsh“. Diese gibt an, dass die folgenden
Befehle in einer Unix/Linux Eingabeaufforderung mit Namen „zShell“ ausgeführt werden
sollen.
Beispiel (Linux-Bash code) der Datei: „Mini-Programm.robasm“:
#!/bin/zsh
echo "10 NT" > robo1
echo "11 SP 3" > robo1
echo "12 TI 10" > robo1
echo "13 MO 1" > robo1
echo "14 TI 10" > robo1
#
#
#
#
#
#
Folgendes Programm mit Linux zShell ausfuehren
Roboter 1: Fahre in die Nestposition
Roboter 1: Geschwindigkeit auf 3 setzen
Roboter 1: 1 sec warten
Roboter 1: Bewegung zur einprogrammierten Pos.1
Roboter 1: 1 sec warten
echo "15 MJ 0, 0, 0, +90, 0" > robo1
# Roboter 1: Handgelenk 90deg nach oben bewegen
echo
echo
echo
echo
echo
echo
echo
"16
"17
"18
"19
"22
"23
"24
GC" > robo1
GO" > robo1
GC" > robo1
GO" > robo1
TI 10" > robo1
MO 1" > robo1
ED" > robo1
#
#
#
#
#
#
#
Roboter
Roboter
Roboter
Roboter
Roboter
Roboter
Roboter
1:
1:
1:
1:
1:
1:
1:
Hand schliessen
Hand oeffen
Hand schliessen
Hand oeffen
1 sec warten
Bewegung zur einprogrammierten Pos.1
Programmende
echo "PD 1, -2.7, +287.4, +333.3, -90.2, -3.1" > robo1
# Roboter 1: Pos.1 definieren
echo "TI 5" > robo1
# Roboter 1: 0,5 sec warten
Durch die folgenden manuellen Eingaben wird das obige Programm 1)geladen, dann
2)gestartet:
1) ./Mini-Programm.robasm
2) echo "RN" > robo1
Hinweis:
Die Befehle „PD 1,…“ und „TI 5“ werden in dieser Reihenfolge als erstes
ausgeführt, ohne den Befehl „RN“ aufzurufen, da keine Programmzeilennummern definiert sind.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
30
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Es kann auch eine reine txt-Datei an den Roboter gesendet werden, es gilt:
1) cat Mini-Programm.txt > robo1
2) echo "RN" > robo1
(oder: ./run.sh, startet beide Roboter gleichzeitig)
Beispiel der Datei „Mini-Programm.robasm“ als txt-Datei:
10
11
12
13
14
NT
SP
TI
MO
TI
3
10
1
10
15 MJ 0, 0, 0, +90, 0
16
17
18
19
22
23
24
GC
GO
GC
GO
TI 10
MO 1
ED
PD 1, -2.7, +287.4, +333.3, -90.2, -3.1
TI 5
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
31
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
5. Ansteuerung des Roboters mit Hilfe der
Programmiersprache C
In diesem Projekt sollen die bereits vorliegende C-Bibliothek „robot.c“ verwendet werden.
Ein C-Programm dieses Projekts muss die Anweisung: #include "robot.h" enthalten.
Im Weiteren können die Prototypen, die Kopfzeilen der Funktionen abgeschlossen mit
Semikolon, der Datei robot.h verwendet werden. Eine Liste der Befehle liegt im Anhang 1.1
des Aufgabenblatts vor.
Mit den Funktionen können Konstrukte gebildet werden, die für die Lösung der Aufgaben
notwendig sind.
5.1. C-Code-Gliederung und Einbinden von Header-Dateien
Wenn ein großes Programm mit der Programmiersprache C implementiert werden soll,
empfiehlt es sich nicht nur Unterfunktionen über der Hauptfunktion int main() in einer
Datei zu erstellen, sondern den C-Code in verschiedene Dateien aufzugliedern.
Dies hat den Vorteil, daß einzelne Funktionalitäten voneinander getrennt werden können und
dadurch der Code übersichtlicher wird. Es werden mehrere Moduln erzeugt, die zunächst
getrennt kompiliert und später durch den Linker verknüpft werden (siehe dazu Kapitel 5.2).
Folgende Konvention erleichtert die Modularisierung:
Die Hauptfunktion int main() wird separat vom übrigen C-Code in die Hauptdatei main.c
gespeichert.
In der Hauptfunktion werden nur die Aufgaben aufgerufen, die ausgeführt werden sollen.
Die Aufgaben werden in getrennten Dateien (jedoch in einem gemeinsamen Ordner) abgelegt
(z.B.: aufg1.c, aufg2.c, usw.).
Diese Aufgaben-Dateien sollten dann die Pseudo-Hauptfunktion int aufgx() enthalten.
Dann müssen die Funktionen der Aufgaben-Dateien in die Datei main.c eingebunden
werden.
Beispiel:
main.c
aufgx.c
#include “aufgx.h“
#include “aufgx.h“
int main()
{
aufgx();
return 0;
}
int aufgx()
{
...
return 0;
}
Dazu werden Header(.h)-Dateien verwendet.
Eine Einbindung der c-Datei selbst würde bei der Linker-Phase zu einem Fehler führen, da
dann der Code mehrfach eingebunden würde.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
32
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Header-Dateien enthalten die Prototypen der Funktionen aus der zugehörigen C-Code(.c)Datei; zusätzlich werden Datentyp-Definitionen und Präprozessor-Befehle aus dem
zugehörigen C-Code ausgelagert.
Beispiel:
main.c
aufgx.c
#include “aufgx.h“
#include “aufgx.h“
int main()
{
aufgx();
return 0;
}
int aufgx()
{
...
return 0;
}
aufgx.h
int aufgx();
Wird in der Hauptdatei die Aufgaben-Headerdatei eingebunden, so können die Funktionen
der Aufgaben-Datei aufgerufen werden.
Dabei bedeutet: #include “aufgx.h“, daß die Hauptdatei, sowie die Aufgaben- und
Aufgaben-Header-Datei in einem Verzeichnis liegen müssen, damit der Verarbeitungsprozeß
die Header-Datei und die entsprechende C-Code-Datei berücksichtigt.
Die bekannte Schreibweise mit eckigen Klammern #include <aufgx.h> bedeutet, daß die
Header-Datei sich in einem System-Verzeichnis (des Compilers) befindet
(Systembibliotheken).
Selbstverständlich können wiederum die Aufgaben-Dateien weitere Unteraufgaben-Dateien
über Header-Dateien einbinden.
Es kann vorkommen, daß in der Hauptfunktion Aufgaben, sowie Unteraufgaben aufgerufen
werden sollen, wobei die Aufgaben-Datei die Unteraufgaben-Datei einbindet.
Dann entsteht das „Problem“, daß im Dateienverbund die Unteraufgaben-Header-Datei
mehrfach eingebunden wird. Dies kann durch eine „Präprozessor-Anweisung“ verhindert
werden.
Beispiel:
aufgx.h
#ifndef AUFGX_H_
#define AUFGX_H_
...
int aufgx();
...
#endif // AUFGX_H_
// falls Makro AUFGX_H_ noch nicht def.=> springe in
Bedingung
// und definiere Makro AUFGX_H_
// Ende des “Blocks“
Es folgt der Verarbeitungsprozeß vom Quellcode zur ausführbaren Datei.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
33
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
5.2. Verarbeitungsprozeß vom Quellcode zur ausführbaren Datei
Die folgende Abbildung 7 zeigt die Verarbeitungsstufen vom Quellcode zur ausführbaren Datei.
main.c
aufgx.c
Uaufgy.c
#include „aufgx.h“
#include „Uaufgy.h“
…
#include „aufgx.h“
…
#include „aufgy.h“
…
main.c
aufgx.c
Uaufgy.c
…
…
…
main.o
aufgx.o
Uaufgy.o
…
…
…
linker
main.exe
(Unix: a.out)
…
Abbildung 7 Verarbeitungstufen
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
compiler
…
preprocessor
#include „Uaufgy.h“
…
source code
Uaufgy.h
header-files
aufgx.h
34
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Während der Präprozessor-Verarbeitungsphase werden die definierten PräprozessorAnweisungen (z.B. #include, #ifndef, #define und #endif) expandiert und die Kommentare
werden entfernt. Der Präprozessor löst die Makros und die Anweisungen auf.
Anschließend wird der modifizierte Quellcode dateiweise kompiliert und es wird für jede
Quellcode-Datei getrennt der Objekt-Code erstellt.
Falls Syntax- oder Semantik-Fehler vorliegen, wird die Übersetzung mit einer Fehlermeldung
abgebrochen. Typische Syntax-Fehler sind z.B. falsch geschriebene Anweisungen. Ein
typischer Semantik-Fehler ist die Verwendung von nicht deklarierten Variablen im Quellcode.
Erst durch den Linker werden die einzelnen Objekt-Codes zusammen mit anderen ObjektCodes, wie z. B. C-Bibliotheken, verknüpft.
Hier wird zusätzlich überprüft, ob Objekt-Code doppelt vorhanden ist und es wird, falls nötig,
eine entsprechende Fehlermeldung ausgegeben.
Wenn keine Fehler vorliegen, wird die ausführbare Datei erstellt, welche im jeweiligen
Betriebssystem gestartet werden kann.
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
35
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
5.3. Ein kleines C-Programm für den Roboter
Zur Einführung in die Roboter-Programmierung sei folgendes Beispiel gegeben:
Roboter 1 soll den ersten Stein der Objektmatrix auf das Förderband setzen und mit Hilfe der
Rückgabedaten des Förderbandsystems einen identifizierten Metall-Stein in das
Qualitätsdepot 1, RH bzw. einen identifizierten weißen Stein zurück an die alte Matrix
Position legen.
Da das Robotersystem einer hardwarenahen Programmiersprache („Assembler“) ähnelt,
müssen Marken gesetzt werden, damit durch Sprung zu einer Marke Unterfunktionen
definiert werden können.
Sämtliche Funktionen, die in der Prozedur-Funktion int ItemSort(robot *r1) aufgerufen
werden, stammen aus der Roboter-Bibliothek robot.c. Die Funktionen der Bibliothek
werden im Aufgabenblatt aufgeführt.
Realisierung:
/*****************************************************
* File:
main.c
WORKS !!
*
* Gruppe 0:
Sascha Padberg
7654321
*
*
*
* Date:
11.02.2009
*
* Description:
main function for Project
*
*****************************************************/
#include "robot.h"
#include "ItemSort.h"
int main()
{
robot rob_r1;
robot *r1=&rob_r1;
Variablendefinition für Roboter 1
ItemSort(r1);
Aufrufen der Funktion ItemSort für Roboter 1
return 0;
Ende des Programms
}
/*****************************************************
* File:
ItemSort.h
WORKS !!
*
* Gruppe 0:
Sascha Padberg
7654321
*
*
*
* Date:
09.03.2009
*
* Description:
Header file of Example-Program
*
*
for Tutorial
*
*****************************************************/
#ifndef ITEMSORT_H_
#define ITEMSORT_H_
int ItemSort(robot *r1);
#endif /* ITEMSORT_H_ */
Makrodefinition von Header-Datei
ItemSort.h beginnen
Prototyp der Funktion ItemSort
Ende der Makrodefinition
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
36
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
/*****************************************************
* File:
ItemSort.c
WORKS !!
*
* Gruppe 0:
Sascha Padberg
7654321
*
*
*
* Date:
09.03.2009
*
* Description:
Example-Program for Tutorial
*
*****************************************************/
#include "robot.h"
#include "ItemSort.h"
int ItemSort(robot *r1)
{
init(r1,"ItemSort.txt",true,"r1"); // Erstelle eine txt-Datei
nest(r1);
speed(r1,3);
sleep(r1,10);
move(r1,1);
sleep(r1,10);
set_counter(r1,11,0);
set_counter(r1,12,1);
set_counter(r1,13,0);
Roboterinitialisierung
mit Setzen des Matrix-Spaltenzählers
(11) und Qualitätsdepotzählers (13)
auf 0, sowie des Zeilenzählers (12)
auf 1.
mark(r1,30);
go_to(r1,100);
Überspringe Programmteile, die
für alle Steine gelten.
mark(r1,32);
output_bit(r1,-13);
output_bit(r1,-15);
move_handed(r1,1,true);
move_handed(r1,2,true);
move_handed(r1,3,true);
hand_open(r1);
move(r1,2);
output_bit(r1,+14);
sleep(r1,20);
output_bit(r1,-14);
mark(r1,50);
input_direct(r1);
if_equal(r1,0,60);
test_bit(r1,+13,400);
test_bit(r1,+14,500);
go_to(r1,50);
end(r1);
mark(r1,60);
sleep(r1,2);
input_direct(r1);
go_to(r1,50);
mark(r1,100);
compare_counter(r1,12);
sleep(r1,20);
output_bit(r1,-12);
if_equal(r1,1,200);
/*
if_equal(r1,2,210);
if_equal(r1,3,220);
if_equal(r1,4,230);*/
Lösche die Outputbits 13 und 15 zur
Freigabe des Förderbands.
Bewege gehaltenen (true) Stein
(x,y) zum Förderband.
Setze Stein und fahre zur Pos. 2.
Schalte Förderband ein, warte 2 sec,
schalte Förderband aus.
Lese solange das Eingangsbit aus, bis
Daten von Förderband vorhanden sind
weißer Stein: führe Unterprg 400 aus
Metall-Stein: führe Unterprg 500 aus
Auswahl der Matrix-Spalte
(hier nur für die erste Spalte
definiert)
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
37
Universität Paderborn
mark(r1,200);
increment_counter(r1,11);
compare_counter(r1,11);
if_equal(r1,1,250);
/*
if_equal(r1,2,258);
if_equal(r1,3,266);
if_equal(r1,4,240);*/
Lehrstuhl Angewandte Datentechnik
Auswahl der Matrix-Zeile
(hier nur für die erste Zeile
definiert)
/*mark(r1,210);
...
*/
mark(r1,250);
move(r1,10);
move(r1,11);
hand_close(r1);
move_handed(r1,15,true);
move_handed(r1,10,true);
go_to(r1,32);
Gehe zu erster Stein-Position
Greife Stein
Bewege Stein in eine sichere
Position
/*mark(r1,258);
...
*/
mark(r1,400);
move(r1,6);
move(r1,7);
hand_close(r1);
move_handed(r1,6,true);
move_handed(r1,1,true);
output_bit(r1,+13);
go_to(r1,600);
mark(r1,500);
move(r1,2);
move(r1,4);
move(r1,9);
move(r1,5);
hand_close(r1);
move_handed(r1,9,true);
move_handed(r1,4,true);
output_bit(r1,+15);
output_bit(r1,-12);
go_to(r1,1300);
mark(r1,600);
compare_counter(r1,12);
if_equal(r1,1,650);
/*
if_equal(r1,2,700);
if_equal(r1,3,750);*/
mark(r1,650);
compare_counter(r1,11);
if_equal(r1,1,800);
/*
if_equal(r1,2,850);
if_equal(r1,3,900);*/
Gehe zur Ausschußablage
Greife Stein
Bewege Stein in eine sichere
Position
Setze Ausgangsbit für
Förderband (weißer Stein)
Gehe zur Qualitätsablage
Greife Stein
Bewege Stein in eine sichere
Position
Setze Ausgangsbits für
Förderband (Metall-Stein)
Auswahl der Matrix-Spalte
zur Rücklage des weißen Steins
(hier nur für die erste Spalte
definiert)
Auswahl der Matrix-Zeile
zur Rücklage des weißen Steins
(hier nur für die erste Zeile
definiert)
/*mark(r1,700);
...
*/
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
38
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
mark(r1,800);
move_handed(r1,10,true);
move_handed(r1,15,true);
hand_open(r1);
move(r1,10);
move(r1,1);
end(r1);
Bewege weißen Stein zurück
zur Matrix-Position
Rücklage des weißen Steins
Gehe zur Ruhe-Pos. (Pos. 1)
Programmende
/*mark(r1,850);
...
*/
mark(r1,1300);
increment_counter(r1,13);
Auswahl des Qualitätsdepots
zur Ablage des Metall- Steins
(hier nur für das Depot 1, RH
definiert)
mark(r1,1360);
compare_counter(r1,13);
if_equal(r1,1,1400);
/*
if_equal(r1,2,1470);
if_equal(r1,3,1500);
if_equal(r1,4,1570);
if_equal(r1,5,2000);*/
mark(r1,1400);
move_handed(r1,1,true);
move_handed(r1,8,true);
move_handed(r1,50,true);
move_handed(r1,55,true);
hand_open(r1);
move(r1,50);
move(r1,1);
output_bit(r1,+12);
end(r1);
Bewege Metall-Stein zum
Qualitätsdepot 1, RH.
Ablage des Metall- Steins
Gehe zur Ruhe-Pos. (Pos. 1)
Programmende
/*mark(r1,1470);
...
*/
// Positionsdefinitionen
pos_def(r1,1,-2.7,+287.4,+333.3,-90.2,-3.1);
pos_def(r1,2,-255.2,+125.9,+333.3,-91.1,+4.7);
pos_def(r1,3,-285.2,+102.1,+92.9,-89.2,+19.3);
pos_def(r1,4,-241.2,-106.4,+250.5,-89.4,-24.0);
pos_def(r1,5,-244.0,-107.6,+95.3,-87.9,-23.9);
pos_def(r1,6,-241.5,-150.4,+333.2,-91.1,+4.7);
pos_def(r1,7,-288.0,-215.4,+92.6,-91.6,+53.5);
pos_def(r1,8,+259.2,+47.9,+250.4,-89.4,-10.4);
pos_def(r1,9,-220.3,-108.9,+119.0,-92.3,-23.9);
pos_def(r1,10,+80.5,+325.8,+97.8,-88.6,+17.2);
pos_def(r1,11,+72.8,+240.1,+21.2,-90.5,+17.2);
pos_def(r1,15,+73.1,+241.1,+30.4,-89.9,+17.2);
pos_def(r1,50,+359.5,+23.1,+106.3,-90.5,-3.8);
pos_def(r1,55,+321.3,+19.4,+36.9,-92.4,-5.8);
destroy(r1);
// Schließe die txt-Datei ab.
return 0;
// Gehe zurück zur Main-Funktion
}
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
39
Universität Paderborn
Lehrstuhl Angewandte Datentechnik
Literaturhinweise
•
Robert Sedgewick, Algorithms in C. Addison-Wesley Publishing Company, Inc.,
1990. - ISBN 0-201-51425-7
•
T.H. Cormen, C.E. Leiserson, R.L. Rivest, Introduction to Algorithms, The MIT Press,
2001. - ISBN 0-26-253196-8
•
R. Diestel, Graphentheorie. Springer-Verlag, 2006. - ISBN 3-540-67656-2.
• L. Volkmann, Fundamente zur Graphentheorie. Springer-Verlag, 2001. - ISBN 3-21182774-9
Universität Paderborn, ADT, Tutorial zum Projekt „Angewandte Programmierung“
40
Herunterladen