Newer
Older
# Die Challenges und der Code
Es wurden verschiedene Challenges gestellt, die der Bot mit unseren Skripten lösen sollte.
Die verschiedenen Challenges sind in [diesem Repository](https://mygit.th-deg.de/gaydos/tb3-maze-challenges) zu finden.
Das [eigene Repository](https://mygit.th-deg.de/ps16214/ros2_project/) enthält die Skripte zu den einzelnen Challenges.
Diese sind aber bei jeder einzelnen Challenge nochmal mitverlinkt. Die einzelne Challenge, die ausgelassen wurde, ist
die dritte Challenge, bei der es hieß, es sei nicht zwingend notwendig.
> Wie weit ist die minimale Distanz zur Wand, ohne diese zu berühren?
Mithilfe der Laser-Sensor-Daten des Wafflebot-Roboters konnte man die Distanz 360 Grad um den Roboter herum messen und
ausgeben lassen. Der hierfür notwendige Parameter war `msg.ranges[]`. Die vier voreingestellten Distanzen waren folgende:
* `msg.ranges[0]`: Die Distanz vor dem Roboter
* `msg.ranges[90]`: Die Distanz links vom Roboter
* `msg.ranges[-90]`: Die Distanz rechts vom Roboter
* `msg.ranges[180]`: Die Distanz hinter dem Roboter
Die Angaben der Distanz waren zwischen __inf.__ und __0__:
* `inf.`: Das nächste Objekt weiter entfernt, als der Sensor messen kann
* `0`: Das nächste Objekt würde quasi __im__ Sensor stehen. 0 ist eigentlich nicht zu erreichen, da um den Sensor herum
noch ein Bereich dazuzurechnen ist, da der Wafflebot breiter ist, als nur der Sensor.
## Challenge 1:
> Fahr so nah wie möglich zur roten Wand und bleib davor stehen, ohne damit in Berührung zu kommen
### Lösung
Die Herangehensweise hierfür war schlicht und ergreifend, in die Methode `scan_callback` eine if-else-Schleife einzubauen.
In dieser Schleife wurde lediglich festgelegt, dass der Roboter so lange beschleunigen, also geradeaus fahren sollte,
bis `msg.ranges[0]` kleiner oder gleich 0.2 war. Ist dieser Wert erreicht, so wird die Beschleunigung wieder auf den
Wert __0__ gesetzt; d.h. der Bot bleibt stehen. 0.2 war die perfekte Distanz, um ganz knapp vor der roten Wand stehen zu
bleiben und nicht anzufahren.
Der relevante Code ([hier zu finden](https://mygit.th-deg.de/ps16214/ros2_project/-/blob/main/tb3_challenge1.py)) aus der `scan_callback`-Methode:
```python
def __init__(self):
self.safe_distance = 0.2
def scan_callback(self, msg):
if msg.ranges[0] > self.safe_distance:
self.vel(100, 0)
else:
self.vel(0, 0)
print("Wall found! Stopping..")
sys.exit()
```
Wörtlich bedeutet der Code:
Wenn die vordere Distanz größer als die festgelegte `self.safe_distance` ist, dann soll der Roboter mit 100% der
maximalen Geschwindigkeit fahren. Wenn dies jedoch nicht zutrifft (was beim Unterschreiten der safe_distance erreicht
wird), dann fährt der Roboter mit 0% der maximalen Geschwindigkeit; d.h. er bleibt stehen. Anschließend wird noch ein
print-statement ausgeführt und das Programm wird mit `sys.exit()` beendet.
<video width="320" height="240" controls src="_static/Challenge_1.mp4"></video>
## Challenge 2:
### Aufgabenstellung
> Fahr zur roten Wand und bleib in einer sicheren Distanz stehen. Rotiere anschließend gegen den Uhrzeigersinn und fahr
> zur Holzwand und bleib stehen.
### Lösung
Hierfür wurde das Skript aus Challenge 1 mitunter wiederverwendet. Als Lösung wurden nested if-else-Schleifen verbaut
und die globalen Variablen `self.drive_allowed` und `self.turn_allowed` eingeführt, welche zu Beginn als __True__
initialisiert wurden.
Das Prinzip bleibt wie bei Challenge 1: Es wird erst wieder solange beschleunigt, bis die `self.safe_distance` erreicht
wird. Dieses mal jedoch wurde die Distanz auf __0.4__ statt auf __0.2__ gesetzt. Grund ist: Bei einer sicheren Distanz
von 0.2 steht der Bot sehr nah an der Wand und bei einer darauf folgenden Drehung wird diese Wand mitgerissen. Bei einer
sicheren Distanz von 0.4 ist genug Abstand zur Wand, sodass diese nicht umgeworfen wird.
Ist der Bot nun unter der sicheren Distanz von 0.4, wird mit einer weiteren if-else-Schleife geprüft, ob die Intensität
vorne bei 2.0 liegt und `self.turn_allowed` auf True gesetzt ist. Die Intensität 2.0 entspricht der roten Wand. Kommt
das Skript in diesen Bereich, so wird die Geschwindigkeit des Bots erst auf 0 gesetzt, um stehen zu bleiben. Anschließend
wird mit einer Drehgeschwindigkeit von 85% der maximalen Drehgeschwindigkeit beschleunigt. Durch das nachfolgende
`sleep(1)` wird festgelegt, dass der Bot genau eine Sekunde drehen soll und anschließend wieder auf eine Geschwindigkeit
von 0% gesetzt wird und er somit stehen bleibt. Ist dies erfolgt, so wird `self.turn_allowed` auf False gesetzt, da
drehen nun nicht mehr erlaubt ist. Somit fährt der Bot durch den äußeren Part der if-else-Schleife wieder los.
Anschließend kommt man in den elif-Part der Schleife. Hier wird geprüft, ob die Intensität auch nicht wieder 2.0 ist.
Dann wird lediglich die Geschwindigkeit wieder auf 0% gesetzt und `self.drive_allowed` auf False gesetzt und das Skript
beendet.
Der relevante Code aus der `scan_callback`-Methode:
```python
def __init__(self):
self.safe_distance = 0.4
self.drive_allowed = True
self.turn_allowed = True
if self.drive_allowed:
if msg.ranges[0] > self.safe_distance:
self.vel(80, 0)
else:
if msg.intensities[0] == 2.0 and self.turn_allowed:
self.vel(0, 0)
print("Red wall found! \nTurning...")
self.vel(0, 85)
sleep(1)
self.vel(0, 0)
self.turn_allowed = False
pass
elif msg.intensities[0] != 2.0:
print("Wall found!")
print("Stopping..")
self.vel(0, 0)
self.drive_allowed = False
sys.exit()
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
Hier nochmal dargestellt in Bildern:
<p float="left">
<img src="source/Challenge_2_1.png" width="180" />
<img src="source/Challenge_2_2.png" width="188.7" />
<img src="source/Challenge_2_3.png" width="181.6" />
</p>
## Challenge 4:
### Aufgabenstellung
> Weiche Gegenständen aus und fahre nicht an
### Lösung
Die Herangehensweise an diese Challenge war etwas anders. Hierfür war meine Idee, den kompletten Bereich an Sensorwerten
vor dem Wafflebot abzugleichen. Das heißt: Im Prinzip werden alle Laserdaten von `msg.ranges[-90]` bis `msg.ranges[90]`
ausgelesen und ausgewertet.
Es wurde eine `self.stop` Variable eingeführt, die festlegt, ob der Bot nun fahren darf oder
stoppen soll. Diese ist standardmäßig mit __False__ initialisiert. Auch die `self.turn_allowed` Variable lässt sich hier
wieder finden, die in diesem Fall aber von Anfang an mit __False__ initialisiert wurde. Zuletzt gibt es noch die neue
Variable `self.timestamp`, welche später die aktuelle Zeit in Sekunden abspeichern soll.
Für diese Challenge wurde eine neue Methode namens `drive()` eingeführt, in der sich der hauptsächliche Code befindet.
Neben dieser Methode gibt es auch noch eine Methode mit dem Namen `check_to_stop_turn()`. Mehr dazu anschließend.
* Zur `drive()`-Methode:
* Zu Beginn wird geprüft, ob `self.turn_allowed` auf __False__ gesetzt ist, sodass der Roboter nicht drehen soll. Ist
dies der Fall, so kommt man in die Schleife hinein. Dann wird in einer weiteren if-Schleife geprüft, ob die sichere
Distanz zur Wand in den vorgenannten Richtungen unterschritten ist oder nicht. Hierfür existiert eine Liste namens
`front_area`, in der die Distanzwerte zwischen `msg.ranges[-90]` und `msg.ranges[90]` gespeichert sind.
Ist die sichere Distanz in einem der Bereiche unterschritten, so wird geprüft, ob dort eine rote Wand (also das Ziel)
ist. Falls ja, so ist der Bot angekommen, bleibt stehen und die Variable `self.stop` wird auf __False__ gesetzt. Wenn nicht,
wird `self.turn_allowed` auf __True__ gesetzt und der Roboter hat damit prinzipiell die "Erlaubnis", zu drehen.
* Damit kommt man in die nächste if-Schleife, in der es um das Drehen des Roboters geht. Hier wird zu Beginn geprüft,
ob die Distanz links kleiner als rechts ist. Ist dies der Fall, so dreht sich der Roboter nach rechts und es wird
kontinuierlich mit `check_to_stop_turn()`, ob der Roboter noch weiter drehen darf. Darf er das nicht mehr, also gibt
die Methode __True__ aus, so wird der Roboter gestopt und `self.turn_allowed` wird auf __False__ gesetzt.
Ist links die Distanz jedoch größer als rechts, so dreht sich der Roboter nach links und es wird, wie gerade erklärt,
wieder geprüft, ob der Roboter den Dreh stoppen soll oder nicht.
* Zur `check_to_stop_turn()`-Methode:
Anfangs wird der Bereich, der zu prüfen ist, festgelegt und wieder in einer Liste namens `front_area` abgespeichert.
Dann dreht sich der Wafflebot solange, bis die vordere Distanz größer als 1.0, die Distanz links und rechts jeweils
größer als 2.0 und jede einzelne andere Distanz größer als die sichere Distanz ist. Erst dann wird __True__
zurückgegeben und der Roboter soll somit das Drehen beenden. Ansonsten wird immer __False__ zurückgegeben und der Bot
dreht sich solange weiter.
Die beiden neuen Methoden als Code:
```python
def __init__(self):
self.safe_distance = 0.4
self.turn_allowed = False
self.stop = False
self.timestamp = 0
def drive(self, msg):
front = msg.ranges[0]
left = msg.ranges[90]
right = msg.ranges[-90]
rear = msg.ranges[180]
left_area = msg.ranges[330:360]
right_area = msg.ranges[0:30]
front_area = left_area + right_area
if not self.stop:
if not self.turn_allowed:
for distance in front_area:
if distance > self.safe_distance or distance == 0.0:
self.vel(60, 0)
else:
# stop at red color
if msg.intensities[0] == 2.0:
self.vel(0, 0)
self.stop = False
else:
self.turn_allowed = True
self.timestamp = time()
if self.turn_allowed:
if left < right:
print("Turning right!")
self.vel(0, -50)
sleep(0.25)
if self.check_to_stop_turn(msg):
sleep(0.1)
self.vel(0, 0)
self.turn_allowed = False
else:
print("Turning left!")
self.vel(0, 50)
sleep(0.25)
if self.check_to_stop_turn(msg):
sleep(0.1)
self.vel(0, 0)
self.turn_allowed = False
def check_to_stop_turn(self, msg):
left_area = msg.ranges[330:360]
right_area = msg.ranges[0:30]
front_area = left_area + right_area
for distance in front_area:
if msg.ranges[0] > 1.0 and msg.ranges[90] > 0.2 and msg.ranges[-90] > 0.2 and distance > self.safe_distance:
print("Turn stopped!")
return True
else:
return False
```
Der Ablauf in Bildern:
<img src="source/Challenge_4_1.png" width="219.5"/>
<img src="source/Challenge_4_2.png" width="220" />
<img src="source/Challenge_4_3.png" width="220" />
<img src="source/Challenge_4_4.png" width="218" />
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
## Blockquotes
Kermit from the Muppet Movie:
> Life's like a movie. Write your own ending.
## Lists
- apple
- banana
- cherry
## Links
More info can be found on [Python docs – Exceptions](https://docs.python.org/3/reference/executionmodel.html#exceptions).
## Images

## Code snippets
We can use `find -iname STRING` to search for a file in Unix.
The following code[^1] shuffles a list:
```python
from random import shuffle
foo = [1, 2, 3, 4]
shuffle(foo)
print(foo)
```
## The end
If you want to try these, follow this [Markdown tutorial](https://commonmark.org/help/tutorial). It also includes a web-based code editor.
[^1]: Code from [PythonSnippets.dev](https://pythonsnippets.dev/snippet/23/)