Zum Inhalt springen
Deutsch

Durch das Labyrinth navigieren

In diesem Tutorial baust du Schritt für Schritt ein vollständiges Programm. Der Roboter fährt durch das Labyrinth, weicht Wänden und Löchern aus und meldet alles, was wie eine Wandmarkierung aussieht. Es ist das Programm, das du in Dein erster Lauf ausgeführt hast.

Es verwendet den Starter-Roboter und das, was du unter Bewegung und Sensoren gelernt hast.

Das vollständige Programm: maze.py · maze.cpp

Führe es in world1.wbt aus. Die Welt sieht so aus:

Die Welt world1 in Webots

Zuerst erzeugt das Programm den Roboter, holt die Radmotoren und lässt sie sich ohne Begrenzung drehen. speeds enthält die Geschwindigkeit jedes Rads, die der Rest des Programms ändert. reported merkt sich, wo der Roboter schon eine Markierung gemeldet hat.

from controller import Robot
import struct
import numpy as np
import cv2 as cv
robot = Robot()
timeStep = 32 # Milliseconds between steps
maxVelocity = 6.28 # The maximum speed of the wheels, in radians per second
leftWheel = robot.getDevice("left wheel motor")
rightWheel = robot.getDevice("right wheel motor")
leftWheel.setPosition(float("inf")) # Let the wheels turn without limit
rightWheel.setPosition(float("inf"))
speeds = [maxVelocity, maxVelocity] # [left wheel speed, right wheel speed]
reported = [] # Positions where a token has already been reported

Als Nächstes holt und aktiviert es die Sensoren des Starter-Roboters:

Der Beispielroboter mit beschrifteten Sensoren

leftDist = robot.getDevice("leftDist")
frontDist = robot.getDevice("frontDist")
rightDist = robot.getDevice("rightDist")
camera = robot.getDevice("camera")
colorSensor = robot.getDevice("color")
gps = robot.getDevice("gps")
emitter = robot.getDevice("emitter") # The emitter does not need to be enabled
for sensor in [leftDist, frontDist, rightDist, camera, colorSensor, gps]:
sensor.enable(timeStep)

Diese Funktionen ändern die Geschwindigkeiten, um Kurven zu fahren und sich auf der Stelle zu drehen. delay lässt den Roboter eine bestimmte Anzahl von Millisekunden lang dasselbe tun, und getBrightness liest den Farbsensor als einen einzigen Wert von 0 (Schwarz) bis 255 (Weiß) aus.

def turnRight():
speeds[0] = 0.6 * maxVelocity
speeds[1] = -0.2 * maxVelocity
def turnLeft():
speeds[0] = -0.2 * maxVelocity
speeds[1] = 0.6 * maxVelocity
def spin():
speeds[0] = 0.6 * maxVelocity
speeds[1] = -0.6 * maxVelocity
def delay(ms):
start = robot.getTime()
while robot.step(timeStep) != -1:
if (robot.getTime() - start) * 1000 > ms:
break
def getBrightness():
image = colorSensor.getImage()
return colorSensor.imageGetGray(image, colorSensor.getWidth(), 0, 0) # 0 (black) to 255 (white)

Diese Funktion sucht in einem Kamerabild nach Wandmarkierungen. Markierungen sind helle Quadrate auf dunklen Wänden, also sucht sie nach hellen, ungefähr quadratischen Formen, die nicht den Bildrand berühren, wo normalerweise der Boden ist.

def seesToken(image):
height, width = camera.getHeight(), camera.getWidth()
image = np.frombuffer(image, np.uint8).reshape((height, width, 4)) # BGRA image for OpenCV
gray = cv.cvtColor(image, cv.COLOR_BGRA2GRAY)
_, bright = cv.threshold(gray, 200, 255, cv.THRESH_BINARY) # Bright parts in white
contours, _ = cv.findContours(bright, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for contour in contours:
x, y, w, h = cv.boundingRect(contour)
if x == 0 or y == 0 or x + w == width or y + h == height:
continue # Shapes touching the edge of the image are usually the floor
if cv.contourArea(contour) > 40 and 0.65 < w / h < 1.5:
return True # A bright, roughly square shape on a wall
return False

Das ist ein einfacher Anfang: Die Funktion kann die Symbole nicht unterscheiden und erkennt kognitive Ziele nicht gut. Das Tutorial Wandmarkierungen zeigt, wie es besser geht.

Um für eine Markierung Punkte zu bekommen, muss der Roboter länger als 1 Sekunde anhalten und ihre Position und ihren Typ senden. Jede Markierung bringt nur einmal Punkte, und eine erneute Meldung kostet 5 Punkte. Deshalb merkt sich das Programm, wo es schon gemeldet hat. Kommunikation erklärt diese Nachrichten.

def report(tokenType):
leftWheel.setVelocity(0) # Stop for more than 1 second
rightWheel.setVelocity(0)
delay(1300)
x = int(gps.getValues()[0] * 100) # Meters to centimeters
z = int(gps.getValues()[2] * 100)
emitter.send(struct.pack("i i c", x, z, tokenType.encode()))
robot.step(timeStep)
reported.append((x, z))
def alreadyReported():
# Reporting the same token again is a misidentification
x = gps.getValues()[0] * 100
z = gps.getValues()[2] * 100
return any(abs(x - rx) < 12 and abs(z - rz) < 12 for rx, rz in reported) # Closer than 1 tile

Schließlich setzt die Hauptschleife alles zusammen. In jedem Schritt beginnt sie mit voller Geschwindigkeit, dreht von Wänden weg, dreht vor Löchern um und meldet, was die Kamera sieht, wenn vorne eine Wand nah ist.

while robot.step(timeStep) != -1:
speeds[0] = maxVelocity
speeds[1] = maxVelocity
if leftDist.getValue() < 0.05: # Wall on the left: turn right
turnRight()
if rightDist.getValue() < 0.05: # Wall on the right: turn left
turnLeft()
if frontDist.getValue() < 0.05: # Wall in front: spin
spin()
if getBrightness() < 80: # Black floor: turn away from the hole
spin()
leftWheel.setVelocity(speeds[0])
rightWheel.setVelocity(speeds[1])
delay(600)
# Close to a wall, and something on it: report it
if frontDist.getValue() < 0.07 and not alreadyReported() and seesToken(camera.getImage()):
report("H") # The type cannot be told yet, so guess "H"
leftWheel.setVelocity(speeds[0])
rightWheel.setVelocity(speeds[1])

Dieser Roboter fährt ziellos umher: Er weiß nicht, wo er schon war, und rät den Typ jedes Opfers. Manchmal bleibt er stecken, und das Spiel setzt ihn nach 20 Sekunden zu einem Checkpoint zurück. Für ein erstes Programm ist das in Ordnung.

  • Finde die besten Geschwindigkeiten und Abstände für deinen Roboter.
  • Folge der Wand auf einer Seite: Halte den seitlichen Sensor auf konstantem Abstand und dreh dich, wenn der vordere Sensor eine Wand sieht.
  • Ändere die Geschwindigkeit je nachdem, wie nah die Wände sind.

Danach lernst du unter Kommunikation, Punkte zu sammeln.