跳转到内容
中文(简体)

迷宫导航

在本教程中,你将一步一步地编写一个完整的程序。机器人会在迷宫中四处行驶,避开墙壁和坑洞,并报告任何看起来像墙面标识的东西。它就是你在首次运行中运行过的程序。

它使用入门机器人,以及你在移动和传感器中学到的知识。

完整的程序:maze.py · maze.cpp

在 world1.wbt 上运行它,这个世界是这样的:

Webots 中的 world1 世界

首先,程序创建机器人,获取车轮电机,并让它们可以无限制地转动。speeds 保存每个车轮的速度,程序的其余部分会修改它。reported 记住机器人已经在哪些位置报告过标识。

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

接下来,程序获取并启用入门机器人的传感器:

标注了各个传感器的示例机器人

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)

这些函数通过修改速度来转弯和原地旋转。delay 让机器人在指定的毫秒数内保持同一个动作,getBrightness 把颜色传感器的读数转换成一个从 0(黑色)到 255(白色)的值。

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)

这个函数在摄像头图像中寻找墙面标识。标识是深色墙壁上的明亮方块,所以它会寻找大致呈方形、并且不接触图像边缘(那里通常是地面)的明亮形状。

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

这只是一个简单的开始:它无法区分各种符号,也不太能看清认知目标。墙面标识教程会介绍如何做得更好。

要为一个标识得分,机器人必须停止超过 1 秒,然后发送它的位置和标识的类型。每个标识只能得分一次,再次报告会扣 5 分,所以程序会记住它已经在哪里报告过。通信解释了这些消息。

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

最后,主循环把所有部分组合在一起。每一步,机器人都先全速前进,遇到墙壁时转开,遇到坑洞时掉头,并在前方靠近墙壁时报告摄像头看到的东西。

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

这个机器人只是四处乱逛:它不知道自己去过哪里,并且会猜测每个受害者的类型。它有时会被卡住,20 秒后比赛会把它移回检查点。作为第一个程序,这样就很不错了。

  • 为你的机器人找到最合适的速度和距离。
  • 沿着一侧的墙走:让侧面的传感器保持固定的距离,前方的传感器看到墙时就转弯。
  • 根据墙的远近改变速度。

然后,在通信中学习如何得分。