Pular para o conteúdo
Português (Brasil)

Navegue pelo labirinto

Neste tutorial, você constrói um programa completo, passo a passo. O robô anda pelo labirinto evitando paredes e buracos e informa tudo o que parece ser um marcador de parede. É o programa que você executou em Sua primeira execução.

Ele usa o robô inicial e o que você aprendeu em Movimento e Sensores.

O programa completo: maze.py · maze.cpp

Execute-o em world1.wbt, que é assim:

O mundo world1 no Webots

Primeiro, o programa cria o robô, obtém os motores das rodas e deixa que eles girem sem limite. speeds guarda a velocidade de cada roda, que o resto do programa altera. reported lembra onde o robô já informou um marcador.

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

Em seguida, ele obtém e ativa os sensores do robô inicial:

O robô de exemplo com os sensores identificados

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)

Estas funções mudam as velocidades para fazer curvas e girar no lugar. delay mantém o robô fazendo a mesma coisa por um determinado número de milissegundos, e getBrightness lê o sensor de cor como um único valor de 0 (preto) a 255 (branco).

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)

Esta função procura marcadores de parede em uma imagem da câmera. Os marcadores são quadrados claros em paredes escuras, então ela procura formas claras que sejam mais ou menos quadradas e não encostem na borda da imagem, onde normalmente fica o piso.

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

Este é um começo simples: ele não consegue diferenciar os símbolos e não enxerga bem os alvos cognitivos. O tutorial Marcadores nas paredes mostra como fazer melhor.

Para pontuar com um marcador, o robô precisa parar por mais de 1 segundo e enviar a sua posição e o tipo do marcador. Cada marcador só pode ser pontuado uma vez, e informá-lo de novo custa 5 pontos, então o programa lembra onde já informou. Comunicação explica essas mensagens.

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

Por fim, o laço principal junta tudo. A cada passo, ele começa em velocidade máxima, desvia das paredes, recua dos buracos e informa o que a câmera vê quando está perto de uma parede à frente.

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])

Este robô vagueia: ele não sabe por onde já passou e chuta o tipo de cada vítima. Às vezes ele fica preso, e a partida o leva de volta a um ponto de controle após 20 segundos. Para um primeiro programa, tudo bem.

  • Encontre as melhores velocidades e distâncias para o seu robô.
  • Siga a parede de um lado: mantenha o sensor lateral a uma distância constante e vire quando o sensor da frente vir uma parede.
  • Mude a velocidade de acordo com a proximidade das paredes.

Depois, aprenda a marcar pontos em Comunicação.