Пример #1
0
class Exploration:
    """Implementation of the Right-Wall hugging algorithm for a maze solving
       robot.
       The implementation assumes that the robot starts at the bottom-left corner of the map,
       i.e. (Rows - 2, 1). And the robot is facing North


    Attributes:
        currentMap (Numpy array): To store the current state of the exploration map
        exploredArea (int): Count of the number of cells explored
        robot (Robot): Instance of the Robot class
        sensors (list of Numpy arrays): Readings from all sensors
        timeLimit (int): Maximum time allowed for exploration
    """
    def __init__(self, realMap=None, timeLimit=None, calibrateLim=6, sim=True):
        """Constructor to initialise an instance of the Exploration class

        Args:
            realMap (string): File name for real map during simulation stage
            timeLimit (int): Maximum time allowed for exploration
            sim (bool, optional): To specify is the exploration mode is simulation or real
        """
        self.timeLimit = timeLimit
        self.exploredArea = 0
        self.currentMap = np.zeros([20, 15])
        if sim:
            from Simulator import Robot
            self.robot = Robot(self.currentMap, EAST, START, realMap)
            self.sensors = self.robot.getSensors()
        else:
            from Real import Robot
            self.robot = Robot(self.currentMap, EAST, START)
        self.exploredNeighbours = dict()
        self.sim = sim
        self.calibrateLim = calibrateLim
        self.virtualWall = [0, 0, MAX_ROWS, MAX_COLS]

    def __validInds(self, inds):
        """To check if the passed indices are valid or not
        To be valid the following conditions should be met:
            * A 3x3 neighbourhood around the center should lie within the arena
            * A 3x3 neighbourhood around the center should have no obstacle

        Args:
            inds (list of list): List of coordinates to be checked

        Returns:
            list of list: All indices that were valid
        """
        valid = []
        for i in inds:
            r, c = i
            x, y = np.meshgrid([-1, 0, 1], [-1, 0, 1])
            x, y = x + r, y + c
            if (np.any(x < 0) or np.any(y < 0) or np.any(x >= MAX_ROWS)
                    or np.any(y >= MAX_COLS)):
                valid.append(False)
            elif (np.any(self.currentMap[x[0, 0]:x[0, 2] + 1,
                                         y[0, 0]:y[2, 0] + 1] != 1)):
                valid.append(False)
            else:
                valid.append(True)
        return [tuple(inds[i]) for i in range(len(inds)) if valid[i]]

    def getExploredArea(self):
        """Updates the total number of cells explored at the current state
        """
        self.exploredArea = (np.sum(self.currentMap != 0) / 300.0) * 100

    def nextMove(self):
        """Decides which direction is free and commands the robot the next action
        """
        move = []
        # multi step
        front = self.frontFree()
        if (self.checkFree([1, 2, 3, 0], self.robot.center)):
            self.robot.moveBot(RIGHT)
            move.append(RIGHT)
            front = self.frontFree()
            for i in range(front):
                self.robot.moveBot(FORWARD)
            move.extend([FORWARD] * front)
        elif (front):
            for i in range(front):
                self.robot.moveBot(FORWARD)
            move.extend([FORWARD] * front)
        elif (self.checkFree([3, 0, 1, 2], self.robot.center)):
            self.robot.moveBot(LEFT)
            move.append(LEFT)
            front = self.frontFree()
            for i in range(front):
                self.robot.moveBot(FORWARD)
            move.extend([FORWARD] * front)
        else:
            self.robot.moveBot(RIGHT)
            self.robot.moveBot(RIGHT)
            move.extend(('O'))
        # single step
        # if (self.checkFree([1, 2, 3, 0], self.robot.center)):
        #     self.robot.moveBot(RIGHT)
        #     move.append(RIGHT)
        #     if (self.checkFree([0, 1, 2, 3], self.robot.center)):
        #         self.robot.moveBot(FORWARD)
        #         move.append(FORWARD)
        # elif (self.checkFree([0, 1, 2, 3], self.robot.center)):
        #     self.robot.moveBot(FORWARD)
        #     move.append(FORWARD)
        # elif (self.checkFree([3, 0, 1, 2], self.robot.center)):
        #     self.robot.moveBot(LEFT)
        #     move.append(LEFT)
        #     if (self.checkFree([0, 1, 2, 3], self.robot.center)):
        #         self.robot.moveBot(FORWARD)
        #         move.append(FORWARD)
        # else:
        #     self.robot.moveBot(RIGHT)
        #     self.robot.moveBot(RIGHT)
        #     move.extend(('O'))
        if not (self.sim):
            calibrate_front = self.robot.can_calibrate_front()
            calibrate_right = self.robot.can_calibrate_right()
            if self.robot.is_corner():
                move.append('L')
            elif (calibrate_right[0]):
                move.append(calibrate_right[1])
            elif (calibrate_front[0]):
                move.append(calibrate_front[1])
        return move

    def checkFree(self, order, center):
        """Checks if a specific direction is free to move to

        Args:
            order (list): Ordering for the directionFree list based on the
                          the next move (Right, Left, Forward)

        Returns:
            bool: If the queried direction is free
        """
        directionFree = np.asarray([
            self.northFree(center),
            self.eastFree(center),
            self.southFree(center),
            self.westFree(center)
        ])
        directionFree = directionFree[order]
        if self.robot.direction == NORTH:
            return directionFree[0]
        elif self.robot.direction == EAST:
            return directionFree[1]
        elif self.robot.direction == SOUTH:
            return directionFree[2]
        else:
            return directionFree[3]

    def validMove(self, inds):
        """Checks if all the three cells on one side of the robot are free

        Args:
            inds (list of list): List of cell indices to be checked

        Returns:
            bool: If all indices are free (no obstacle)
        """
        for (r, c) in inds:
            if not ((self.virtualWall[0] <= r < self.virtualWall[2]) and
                    (self.virtualWall[1] <= c < self.virtualWall[3])):
                return False
        return (self.currentMap[inds[0][0], inds[0][1]] == 1
                and self.currentMap[inds[1][0], inds[1][1]] == 1
                and self.currentMap[inds[2][0], inds[2][1]] == 1)

    def northFree(self, center):
        """Checks if the north direction is free to move

        Returns:
            bool: if north is free
        """
        r, c = center
        inds = [[r - 2, c], [r - 2, c - 1], [r - 2, c + 1]]
        return self.validMove(inds)

    def eastFree(self, center):
        """Checks if the east direction is free to move

        Returns:
            bool: if east is free
        """
        r, c = center
        inds = [[r, c + 2], [r - 1, c + 2], [r + 1, c + 2]]
        return self.validMove(inds)

    def southFree(self, center):
        """Checks if the south direction is free to move

        Returns:
            bool: if south is free
        """
        r, c = center
        inds = [[r + 2, c], [r + 2, c - 1], [r + 2, c + 1]]
        return self.validMove(inds)

    def westFree(self, center):
        """Checks if the west direction is free to move

        Returns:
            bool: if west is free
        """
        r, c = center
        inds = [[r, c - 2], [r - 1, c - 2], [r + 1, c - 2]]
        return self.validMove(inds)

    def frontFree(self):
        r, c = self.robot.center
        counter = 0
        if self.robot.direction == NORTH and self.validMove(
            [[r - 2, c], [r - 2, c - 1], [r - 2, c + 1]]):
            counter = 1
            while (True):
                if (self.validMove([[r-2-counter, c], [r-2-counter, c-1], [r-2-counter, c+1]])) and\
                        not self.checkFree([1, 2, 3, 0], [r-(counter), c]) and\
                        self.checkExplored([r-(counter), c]):
                    counter += 1
                else:
                    break
        elif self.robot.direction == EAST and self.validMove(
            [[r, c + 2], [r - 1, c + 2], [r + 1, c + 2]]):
            counter = 1
            while (True):
                if (self.validMove([[r, c+2+counter], [r-1, c+2+counter], [r+1, c+2+counter]])) and\
                        not self.checkFree([1, 2, 3, 0], [r, c+(counter)]) and\
                        self.checkExplored([r, c+(counter)]):
                    counter += 1
                else:
                    break
        elif self.robot.direction == WEST and self.validMove(
            [[r, c - 2], [r - 1, c - 2], [r + 1, c - 2]]):
            counter = 1
            while (True):
                if (self.validMove([[r, c-2-counter], [r-1, c-2-counter], [r+1, c-2-counter]])) and\
                        not self.checkFree([1, 2, 3, 0], [r, c-(counter)]) and\
                        self.checkExplored([r, c-(counter)]):
                    counter += 1
                else:
                    break
        elif self.robot.direction == SOUTH and self.validMove(
            [[r + 2, c], [r + 2, c - 1], [r + 2, c + 1]]):
            counter = 1
            while (True):
                if (self.validMove([[r+2+counter, c], [r+2+counter, c-1], [r+2+counter, c+1]])) and\
                        not self.checkFree([1, 2, 3, 0], [r+(counter), c]) and\
                        self.checkExplored([r+(counter), c]):
                    counter += 1
                else:
                    break
        return counter

    def checkExplored(self, center):
        r, c = center
        flag = True
        inds = []
        distanceShort = 3
        distanceLong = 5
        if self.robot.direction == NORTH:
            inds.append(
                zip([r - 1] * distanceShort, range(c + 2,
                                                   c + distanceShort + 2)))
            inds.append(
                zip([r + 1] * distanceLong, range(c + 2,
                                                  c + distanceLong + 2)))
            inds.append(
                zip([r - 1] * distanceLong, range(c - distanceLong - 1,
                                                  c - 1))[::-1])
        elif self.robot.direction == EAST:
            inds.append(
                zip(range(r + 2, r + distanceShort + 2),
                    [c + 1] * distanceShort))
            inds.append(
                zip(range(r + 2, r + distanceLong + 2),
                    [c - 1] * distanceLong))
            inds.append(
                zip(range(r - distanceLong - 1, r - 1),
                    [c + 1] * distanceLong)[::-1])
        elif self.robot.direction == WEST:
            inds.append(
                zip(range(r - distanceShort - 1, r - 1),
                    [c - 1] * distanceShort)[::-1])
            inds.append(
                zip(range(r - distanceLong - 1, r - 1),
                    [c + 1] * distanceLong)[::-1])
            inds.append(
                zip(range(r + 2, r + distanceLong + 2),
                    [c - 1] * distanceLong))
        else:
            inds.append(
                zip([r + 1] * distanceShort, range(c - distanceShort - 1,
                                                   c - 1))[::-1])
            inds.append(
                zip([r - 1] * distanceLong, range(c - distanceLong - 1,
                                                  c - 1))[::-1])
            inds.append(
                zip([r + 1] * distanceLong, range(c + 2,
                                                  c + distanceLong + 2)))
        for sensor in inds:
            if flag:
                for (x, y) in sensor:
                    if (x < self.virtualWall[0] or x == self.virtualWall[2]
                            or y < self.virtualWall[1]
                            or y == self.virtualWall[3]
                            or self.currentMap[x, y] == 2):
                        break
                    elif (self.currentMap[x, y] == 0):
                        flag = False
                        break
        return flag

    def moveStep(self, sensor_vals=None):
        """Moves the robot one step for exploration

        Returns:
            bool: True is the map is fully explored
        """
        if (sensor_vals):
            self.robot.getSensors(sensor_vals)
        else:
            self.robot.getSensors()
        move = self.nextMove()
        self.getExploredArea()
        if (self.exploredArea == 100):
            return move, True
        else:
            return move, False

    def explore(self):
        """Runs the exploration till the map is fully explored of time runs out
        """
        print "Starting exploration ..."
        startTime = time.time()
        endTime = startTime + self.timeLimit

        while (time.time() <= endTime):
            if (self.moveStep()):
                print "Exploration completed !"
                return

        print "Time over !"
        return

    def getExploredNeighbour(self):
        locs = np.where(self.currentMap == 0)
        self.virtualWall = [
            np.min(locs[0]),
            np.min(locs[1]),
            np.max(locs[0]) + 1,
            np.max(locs[1]) + 1
        ]
        if ((self.virtualWall[2] - self.virtualWall[0] < 3)
                and self.virtualWall[2] < MAX_ROWS - 3):
            self.virtualWall[2] += 3
        locs = np.asarray(zip(locs[0], locs[1]))
        cost = np.abs(locs[:, 0] -
                      self.robot.center[0]) + np.abs(locs[:, 1] -
                                                     self.robot.center[1])
        cost = cost.tolist()
        locs = locs.tolist()
        while (cost):
            position = np.argmin(cost)
            coord = locs.pop(position)
            cost.pop(position)
            neighbours = np.asarray([[-2, 0], [2, 0], [0, -2], [0, 2]]) + coord
            neighbours = self.__validInds(neighbours)
            for neighbour in neighbours:
                if (neighbour not in self.exploredNeighbours):
                    self.exploredNeighbours[neighbour] = True
                    return neighbour
        return None
Пример #2
0
class Exploration:
    """Implementation of the Right-Wall hugging algorithm for a maze solving
       robot.
       The implementation assumes that the robot starts at the bottom-left corner of the map,
       i.e. (MAX_ROWS - 2, 1). And the robot is facing North

    Attributes:
        currentMap (Numpy array): To store the current state of the exploration map
        exploredArea (float): Percentage of arena explored
        robot (Robot): Instance of the Robot class
        sensors (list of Numpy arrays): Readings from all sensors
        timeLimit (int): Maximum time allowed for exploration
    """
    def __init__(self, realMap=None, timeLimit=None, calibrateLim=6, sim=True):
        """Constructor to initialise an instance of the Exploration class

        Args:
            realMap (string): File name for real map during simulation stage
            timeLimit (int): Maximum time allowed for exploration
            sim (bool, optional): To specify is the exploration mode is simulation or real
        """
        self.timeLimit = timeLimit
        self.exploredArea = 0
        self.currentMap = np.zeros([20, 15])
        # Mark start as free
        self.currentMap[17:20, 0:3] = 1
        self.currentMap[0:3, 12:15] = 1
        # call class according to running simulator or real run
        if sim:
            from Simulator import Robot
            self.robot = Robot(self.currentMap, EAST, START, realMap)
            self.sensors = self.robot.getSensors(
            )  # Returns a numpy array of numpy arrays of the sensor values from all sensors
        else:
            from Real import Robot
            self.robot = Robot(self.currentMap, EAST, START)
        self.exploredNeighbours = dict()
        self.sim = sim
        # self.calibrateLim = calibrateLim
        # initialize as boundary of the arena
        # Set the limits of unexplored area on the map
        self.virtualWall = [0, 0, MAX_ROWS, MAX_COLS]
        self.alignRightCount = 0
        self.justCheckedRight = False

    def __validInds(self, inds):
        """To check if the passed indices are valid or not
        To be valid the following conditions should be met:
            * A 3x3 neighbourhood around the center should lie within the arena
            * A 3x3 neighbourhood around the center should have no obstacle

        Args:
            inds (list of list): List of coordinates to be checked

        Returns:
            list of list: All indices that were valid
        """
        valid = []
        for i in inds:
            r, c = i
            x, y = np.meshgrid([-1, 0, 1], [-1, 0, 1])
            # np.meshgrid([-1, 0, 1], [-1, 0, 1]) outputs
            # [array([[-1,  0,  1],
            #        [-1,  0,  1],
            #        [-1,  0,  1]]),
            # array([[-1, -1, -1],
            #        [ 0,  0,  0],
            #        [ 1,  1,  1]])]
            # x is row coordinate while y is column coordinate
            x, y = x + r, y + c
            # If any of the x coordinate is less than zero
            # or if any of the y coordinate is less than
            # or if any of the x coordinate is greater than or equal to the maximum number of rows
            # or if any of the y coordinates is greater than or equal to the maximum number of columns
            if (np.any(x < 0) or np.any(y < 0) or np.any(x >= MAX_ROWS)
                    or np.any(y >= MAX_COLS)):
                # Indicate that the coordinate is invalid
                valid.append(False)
            # If the value of any of the coordinate is not 1 (meaning that it is free)
            # We assume that there is an obstacle in the 3 by 3 area
            elif (np.any(self.currentMap[x[0, 0]:x[0, 2] + 1,
                                         y[0, 0]:y[2, 0] + 1] != 1)):
                # Indicate that the coordinate is invalid
                valid.append(False)
            # If coordinates within arena and there are no obstacles
            else:
                # Indicate that the coordinate is valid
                valid.append(True)
        # Return all valid coordinates
        return [tuple(inds[i]) for i in range(len(inds)) if valid[i]]

    def getExploredArea(self):
        """Updates the total number of cells explored at the current state
        """
        # returns percent of map explored
        self.exploredArea = (np.sum(self.currentMap != 0) / 300.0) * 100

    def nextMove(self):
        # Decides which direction is free and commands the robot the next action
        move = []

        if (not (self.sim) and self.robot.is_corner() == True):
            move = [ALIGNFRONT, LEFT]
            self.robot.moveBot(LEFT)
        else:
            if not (self.sim):
                # Check if there is a wall in front for robot to calibrate
                calibrate_front = self.robot.can_calibrate_front()
                # Check if there is a wall to the right for the robot to calibrate
                calibrate_right = self.robot.can_calibrate_right()
                # If the robot is at a corner
                if self.robot.is_corner():
                    move.append(ALIGNFRONT)
                    move.append(ALIGNRIGHT)
                # If robot is not at a corner but there is a wall to the right for calibration
                elif (calibrate_right[0]):
                    self.alignRightCount += 1
                    if (self.alignRightCount % 3) == 0:
                        move.append(RIGHT)
                        move.append(ALIGNFRONT)
                        move.append(LEFT)
                    else:
                        # Append command from can_calibrate_right function
                        move.append(calibrate_right[1])
                # If robot is not at a corner and there is no wall to the right of the robot
                # If there is a wall to the front for calibration
                elif (calibrate_front[0]):
                    # Append command from can_calibrate_front function
                    move.append(calibrate_front[1])
            # multi step
            # Number of spaces in front of the robot which is free and where there are obstacles on the right
            # and all spaces detectable by the left middle, right top and right bottom sensors at these spaces have been explored
            front = self.frontFree()

            # If right of robot is free
            if (self.justCheckedRight == True):
                self.justCheckedRight = False
                if (front):
                    # Move robot forward
                    for i in range(front):
                        self.robot.moveBot(FORWARD)
                    move.extend([FORWARD] * front)
                elif (self.checkFree([1, 2, 3, 0], self.robot.center)):
                    # Move robot to the right
                    self.robot.moveBot(RIGHT)
                    move.append(RIGHT)
                    front = self.frontFree()
                    # Move robot forward according to frontFree function
                    for i in range(front):
                        self.robot.moveBot(FORWARD)
                    move.extend([FORWARD] * front)
                # Else if the robot's left is free
                elif (self.checkFree([3, 0, 1, 2], self.robot.center)):
                    # Move robot to the left
                    self.robot.moveBot(LEFT)
                    move.append(LEFT)
                    front = self.frontFree()
                    for i in range(front):
                        self.robot.moveBot(FORWARD)
                    move.extend([FORWARD] * front)
                elif (self.checkLeftUnexplored()):
                    self.robot.moveBot(LEFT)
                    move.append(LEFT)
                # Else, turn the robot around
                else:
                    self.robot.moveBot(RIGHT)
                    self.robot.moveBot(RIGHT)
                    move.append(RIGHT)
                    move.append(RIGHT)
            else:
                if (self.checkFree([1, 2, 3, 0], self.robot.center)):
                    # Move robot to the right
                    self.robot.moveBot(RIGHT)
                    move.append(RIGHT)
                    front = self.frontFree()
                    # Move robot forward according to frontFree function
                    for i in range(front):
                        self.robot.moveBot(FORWARD)
                    move.extend([FORWARD] * front)
                elif (self.checkRightUnexplored()):
                    self.justCheckedRight = True
                    self.robot.moveBot(RIGHT)
                    move.append(RIGHT)
                # If front > 0
                elif (front):
                    # Move robot forward
                    for i in range(front):
                        self.robot.moveBot(FORWARD)
                    move.extend([FORWARD] * front)
                # Else if the robot's left is free
                elif (self.checkFree([3, 0, 1, 2], self.robot.center)):
                    # Move robot to the left
                    self.robot.moveBot(LEFT)
                    move.append(LEFT)
                    front = self.frontFree()
                    for i in range(front):
                        self.robot.moveBot(FORWARD)
                    move.extend([FORWARD] * front)
                elif (self.checkLeftUnexplored()):
                    self.robot.moveBot(LEFT)
                    move.append(LEFT)
                # Else, turn the robot around
                else:
                    self.robot.moveBot(RIGHT)
                    self.robot.moveBot(RIGHT)
                    move.append(RIGHT)
                    move.append(RIGHT)
        # Return list of moves
        return move

    def checkFree(self, order, center):
        """Checks if a specific direction is free to move to

        Args:
            order (list): Ordering for the directionFree list based on the
                          the next move (Right, Left, Forward)

        Returns:
            bool: If the queried direction is free
        """
        # Creates an array of boolean values
        directionFree = np.asarray([
            self.northFree(center),
            self.eastFree(center),
            self.southFree(center),
            self.westFree(center)
        ])
        directionFree = directionFree[order]
        # If directionFree = np.asarray(["North", "East", "South", "West"])
        # directionFree[[1, 2, 3, 0]] = ['East' 'South' 'West' 'North']
        # directionFree[[3, 0, 1, 2]] = ['West' 'North' 'East' 'South']
        if self.robot.direction == NORTH:
            return directionFree[0]
        elif self.robot.direction == EAST:
            return directionFree[1]
        elif self.robot.direction == SOUTH:
            return directionFree[2]
        else:
            return directionFree[3]

    def checkLeftUnexplored(self):
        r, c = self.robot.center
        if self.robot.direction == NORTH:
            if (c - 2 < 0):
                return False
            return (self.currentMap[r][c - 2] == 0
                    or self.currentMap[r + 1][c - 2] == 0)
        elif self.robot.direction == EAST:
            if (r - 2 < 0):
                return False
            return (self.currentMap[r - 2][c] == 0
                    or self.currentMap[r - 2][c - 1] == 0)
        elif self.robot.direction == SOUTH:
            if (c + 2 >= MAX_COLS):
                return False
            return (self.currentMap[r][c + 2] == 0
                    or self.currentMap[r - 1][c + 2] == 0)
        else:
            if (r + 2 >= MAX_ROWS):
                return False
            return (self.currentMap[r + 2][c] == 0
                    or self.currentMap[r + 2][c + 1] == 0)

    def checkRightUnexplored(self):
        r, c = self.robot.center
        if self.robot.direction == NORTH:
            if (c + 2 >= MAX_COLS):
                return False
            return (self.currentMap[r][c + 2] == 0)
        elif self.robot.direction == EAST:
            if (r + 2 >= MAX_ROWS):
                return False
            return (self.currentMap[r + 2][c] == 0)
        elif self.robot.direction == SOUTH:
            if (c - 2 < 0):
                return False
            return (self.currentMap[r][c - 2] == 0)
        else:
            if (r - 2 < 0):
                return False
            return (self.currentMap[r - 2][c] == 0)

    def validMove(self, inds):
        """Checks if all the three cells on one side of the robot are free

        Args:
            inds (list of list): List of cell indices to be checked

        Returns:
            bool: If all indices are free (no obstacle)
        """
        for (r, c) in inds:
            # self.virtualWall indicates the boundaries of unexplored area
            # If the indices are not within the unexplored area
            if not ((0 <= r < MAX_ROWS) and (0 <= c < MAX_COLS)):
                # Indicate that the move is not valid
                return False
        # Check if all three indices have a value of 1 (Explored and is free)
        return (self.currentMap[inds[0][0], inds[0][1]] == 1
                and self.currentMap[inds[1][0], inds[1][1]] == 1
                and self.currentMap[inds[2][0], inds[2][1]] == 1)

    def northFree(self, center):
        """Checks if the north direction is free to move

        Returns:
            bool: if north is free
        """
        r, c = center
        # Checks if the three spaces immediately to the north of the robot is free and within the unexplored area
        inds = [[r - 2, c], [r - 2, c - 1], [r - 2, c + 1]]
        return self.validMove(inds)

    def eastFree(self, center):
        """Checks if the east direction is free to move

        Returns:
            bool: if east is free
        """
        r, c = center
        # Checks if the three spaces immediately to the east of the robot is free and within the unexplored area
        inds = [[r, c + 2], [r - 1, c + 2], [r + 1, c + 2]]
        return self.validMove(inds)

    def southFree(self, center):
        """Checks if the south direction is free to move

        Returns:
            bool: if south is free
        """
        r, c = center
        # Checks if the three spaces immediately to the south of the robot is free and within the unexplored area
        inds = [[r + 2, c], [r + 2, c - 1], [r + 2, c + 1]]
        return self.validMove(inds)

    def westFree(self, center):
        """Checks if the west direction is free to move

        Returns:
            bool: if west is free
        """
        r, c = center
        # Checks if the three spaces immediately to the west of the robot is free and within the unexplored area
        inds = [[r, c - 2], [r - 1, c - 2], [r + 1, c - 2]]
        return self.validMove(inds)

    def frontFree(self):
        """
        returns number of spaces in front of the robot which is free and where there are obstacles on the right
        and all spaces detectable by the left middle, right top and right bottom sensors at these spaces have been explored
        """
        r, c = self.robot.center
        counter = 0  # Keeps track of number of moves the robot can make that are directly in front
        # If robot is facing north and three spaces immediately to the north of robot is within unexplored boundaries
        # and has no obstacles

        if self.robot.direction == NORTH and self.validMove(
            [[r - 2, c], [r - 2, c - 1], [r - 2, c + 1]]):
            # Increase counter by 1
            counter = 1
            # while(True):
            #     # If all three spaces immediately to the north of the robot is free
            #     # and the three spaces immediately to the east of the robot is not free
            #     # and all coordinates reachable by sensors are explored at position with coordinate [r-counter, c]
            #     # increase counter by 1
            #     if (self.validMove([[r-2-counter, c], [r-2-counter, c-1], [r-2-counter, c+1]])) and\
            #             not self.checkFree([1, 2, 3, 0], [r-(counter), c]) and\
            #             self.checkExplored([r-(counter), c]):
            #         counter += 1
            #     else:
            #         break
        elif self.robot.direction == EAST and self.validMove(
            [[r, c + 2], [r - 1, c + 2], [r + 1, c + 2]]):
            counter = 1
            # while(True):
            #     if (self.validMove([[r, c+2+counter], [r-1, c+2+counter], [r+1, c+2+counter]])) and\
            #             not self.checkFree([1, 2, 3, 0], [r, c+(counter)]) and\
            #             self.checkExplored([r, c+(counter)]):
            #         counter += 1
            #     else:
            #         break
        elif self.robot.direction == WEST and self.validMove(
            [[r, c - 2], [r - 1, c - 2], [r + 1, c - 2]]):
            counter = 1
            # while(True):
            #     if (self.validMove([[r, c-2-counter], [r-1, c-2-counter], [r+1, c-2-counter]])) and\
            #             not self.checkFree([1, 2, 3, 0], [r, c-(counter)]) and\
            #             self.checkExplored([r, c-(counter)]):
            #         counter += 1
            #     else:
            #         break
        elif self.robot.direction == SOUTH and self.validMove(
            [[r + 2, c], [r + 2, c - 1], [r + 2, c + 1]]):
            counter = 1
            # while(True):
            #     if (self.validMove([[r+2+counter, c], [r+2+counter, c-1], [r+2+counter, c+1]])) and\
            #             not self.checkFree([1, 2, 3, 0], [r+(counter), c]) and\
            #             self.checkExplored([r+(counter), c]):
            #         counter += 1
            #     else:
            #         break
        return counter

    # ***Update according to sensor placement
    def checkExplored(self, center):
        r, c = center
        flag = True
        inds = []
        distanceShort = 3
        distanceLong = 5
        if self.robot.direction == NORTH:
            # If r = 5, c = 8, distanceShort = 3 and distanceLong = 5
            # zip([r-1]*distanceShort, range(c+2, c+distanceShort+2))
            # gives [(4, 10), (4, 11), (4, 12)]
            inds.append(
                zip([r - 1] * distanceShort, range(c + 2,
                                                   c + distanceShort + 2)))
            # zip([r+1]*distanceLong, range(c+2, c+distanceLong+2))
            # gives [(6, 10), (6, 11), (6, 12), (6, 13), (6, 14)]
            inds.append(
                zip([r + 1] * distanceLong, range(c + 2,
                                                  c + distanceLong + 2)))
            # zip([r]*distanceLong, range(c-distanceLong-1, c-1))[::-1]
            # gives [(5, 6), (5, 5), (5, 4), (5, 3), (5, 2)]
            inds.append(
                zip([r] * distanceLong, range(c - distanceLong - 1,
                                              c - 1))[::-1])
        elif self.robot.direction == EAST:
            # If r = 5, c = 8, distanceShort = 3 and distanceLong = 5
            # zip(range(r+2, r+distanceShort+2), [c+1]*distanceShort)
            # [(7, 9), (8, 9), (9, 9)]
            inds.append(
                zip(range(r + 2, r + distanceShort + 2),
                    [c + 1] * distanceShort))
            # zip(range(r+2, r+distanceLong+2), [c-1]*distanceLong)
            # gives [(7, 7), (8, 7), (9, 7), (10, 7), (11, 7)]
            inds.append(
                zip(range(r + 2, r + distanceLong + 2),
                    [c - 1] * distanceLong))
            # zip(range(r-distanceLong-1, r-1), [c]*distanceLong)[::-1]
            # gives [(3, 8), (2, 8), (1, 8), (0, 8), (-1, 8)]
            inds.append(
                zip(range(r - distanceLong - 1, r - 1),
                    [c] * distanceLong)[::-1])
        elif self.robot.direction == WEST:
            # zip(range(r-distanceShort-1, r-1), [c-1]*distanceShort)[::-1]
            # gives [(3, 7), (2, 7), (1, 7)]
            inds.append(
                zip(range(r - distanceShort - 1, r - 1),
                    [c - 1] * distanceShort)[::-1])
            # zip(range(r-distanceLong-1, r-1), [c+1]*distanceLong)[::-1]
            # gives [(3, 9), (2, 9), (1, 9), (0, 9), (-1, 9)]
            inds.append(
                zip(range(r - distanceLong - 1, r - 1),
                    [c + 1] * distanceLong)[::-1])
            # zip(range(r+2, r+distanceLong+2), [c]*distanceLong)
            # gives [(7, 8), (8, 8), (9, 8), (10, 8), (11, 8)]
            inds.append(
                zip(range(r + 2, r + distanceLong + 2), [c] * distanceLong))
        else:  # self.direction == SOUTH
            # zip([r+1]*distanceShort, range(c-distanceShort-1, c-1))[::-1]
            # gives [(6, 6), (6, 5), (6, 4)]
            inds.append(
                zip([r + 1] * distanceShort, range(c - distanceShort - 1,
                                                   c - 1))[::-1])
            # zip([r-1]*distanceLong, range(c-distanceLong-1, c-1))[::-1]
            # gives [(4, 6), (4, 5), (4, 4), (4, 3), (4, 2)]
            inds.append(
                zip([r - 1] * distanceLong, range(c - distanceLong - 1,
                                                  c - 1))[::-1])
            # zip([r]*distanceLong, range(c+2, c+distanceLong+2))
            # [(5, 10), (5, 11), (5, 12), (5, 13), (5, 14)]
            inds.append(
                zip([r] * distanceLong, range(c + 2, c + distanceLong + 2)))
        # For each list of coordinates
        for sensor in inds:
            if flag:
                # For each coordinate
                for (x, y) in sensor:
                    # If x coordinate is outside the unexplored boundaries
                    # or x coordinate is equal to upper limit of unexplored boundaries in terms of row
                    # or if y is outside the unexplored boundaries
                    # or if y is equal to upper limit of unexplored boundaries in terms of column
                    # of if the coordinate is an obstacle
                    # break out of the for loop, where flag will still be true
                    if (x < self.virtualWall[0] or x == self.virtualWall[2]
                            or y < self.virtualWall[1]
                            or y == self.virtualWall[3]
                            or self.currentMap[x, y] == 2):
                        break
                    # If the coordinate has a value of 0, indicating that it is unexplored
                    # Set flag to be False and break out of the loop
                    elif (self.currentMap[x, y] == 0):
                        flag = False
                        break
        # return final value of flag
        return flag

    def moveStep(self, sensor_vals=None):
        """Moves the robot one step for exploration

        Returns:
            bool: True is the map is fully explored
        """
        # If sensor values are given, update self.exploredMap
        if (sensor_vals):
            self.robot.getSensors(sensor_vals)
        else:
            self.robot.getSensors()
        move = self.nextMove()
        self.getExploredArea()
        # If full coverage
        if (self.exploredArea == 100):
            return move, True

    # if self.robot.center[0]==START[0] and self.robot.center[1]==START[1] and self.exploredArea>50:
    #    return move,True
        else:
            return move, False

    def explore(self):
        """Runs the exploration till the map is fully explored of time runs out
        """
        print("Starting exploration ...")
        startTime = time.time()
        endTime = startTime + self.timeLimit

        while (time.time() <= endTime):
            if (self.moveStep()):
                print("Exploration completed !")
                return

        print("Time over !")
        return

    def getCloseExploredNeighbour(self):
        locs = np.where(self.currentMap == 0)
        if (len(locs[0]) == 0):
            return None
        self.virtualWall = [
            np.min(locs[0]),
            np.min(locs[1]),
            np.max(locs[0]) + 1,
            np.max(locs[1]) + 1
        ]
        if ((self.virtualWall[2] - self.virtualWall[0] < 3)
                and self.virtualWall[2] < MAX_ROWS - 3):
            self.virtualWall[2] += 3
        locs = list(np.asarray(zip(locs[0], locs[1])))

        if (0 <= self.robot.center[0] < 10):
            if (0 <= self.robot.center[1] < 8):
                locs = [
                    loc for loc in locs if 0 <= loc[0] < 10 and 0 <= loc[1] < 8
                ]
            else:
                locs = [
                    loc for loc in locs
                    if 0 <= loc[0] < 10 and 8 <= loc[1] < 15
                ]
        else:
            if (10 <= self.robot.center[0] < 20):
                locs = [
                    loc for loc in locs
                    if 10 <= loc[0] < 20 and 0 <= loc[1] < 8
                ]
            else:
                locs = [
                    loc for loc in locs
                    if 10 <= loc[0] < 20 and 8 <= loc[1] < 15
                ]
        if (len(locs) == 0):
            return None
        locs = np.asarray(locs)
        cost = np.abs(locs[:, 0] -
                      self.robot.center[0]) + np.abs(locs[:, 1] -
                                                     self.robot.center[1])
        cost = cost.tolist()
        # for each coordinate the cost is (x- center_x   + y- center_y)
        # the distance of the neighbour away from the center of robot
        locs = locs.tolist()
        while (cost):
            position = np.argmin(cost)  # position gives index of minimum cost
            coord = locs.pop(position)
            cost.pop(position)
            neighbours = np.asarray(
                [[-2, 0], [-2, -1], [-2, 1], [2, 0], [2, -1], [2, 1], [0, -2],
                 [1, -2], [-1, -2], [0, 2], [1, 2], [-1, 2]]) + coord
            neighbours = self.__validInds(
                neighbours
            )  # check if the coordinates in the list are valid or not
            for neighbour in neighbours:
                if (0 <= self.robot.center[0] < 10):
                    if (0 <= self.robot.center[1] < 8):
                        if (neighbour not in self.exploredNeighbours
                                and 0 <= neighbour[0] < 10
                                and 0 <= neighbour[1] < 8):
                            self.exploredNeighbours[neighbour] = True
                            return neighbour
                    else:
                        if (neighbour not in self.exploredNeighbours
                                and 0 <= neighbour[0] < 10
                                and 8 <= neighbour[1] < 15):
                            self.exploredNeighbours[neighbour] = True
                            return neighbour
                else:
                    if (10 <= self.robot.center[0] < 20):
                        if (neighbour not in self.exploredNeighbours
                                and 10 <= neighbour[0] < 20
                                and 0 <= neighbour[1] < 8):
                            self.exploredNeighbours[neighbour] = True
                            return neighbour
                    else:
                        if (neighbour not in self.exploredNeighbours
                                and 10 <= neighbour[0] < 20
                                and 8 <= neighbour[1] < 15):
                            self.exploredNeighbours[neighbour] = True
                            return neighbour
        return None

    # Get valid and explored neighbours of unexplored spaces
    def getExploredNeighbour(self):
        # initial virtual wall = [0, 0, MAX_ROWS, MAX_COLS]

        # returns all location where current map is unexplored, with x coordinate in one array and y coordinate in different array
        locs = np.where(self.currentMap == 0)
        if (len(locs[0]) == 0):
            return None
        # forms a virtual arena of unexplored area as rectangle
        self.virtualWall = [
            np.min(locs[0]),
            np.min(locs[1]),
            np.max(locs[0]) + 1,
            np.max(locs[1]) + 1
        ]
        if ((self.virtualWall[2] - self.virtualWall[0] < 3)
                and self.virtualWall[2] < MAX_ROWS - 3):
            self.virtualWall[2] += 3
        # if there is more than 3 blocks between this new wall and the actual arean wall, we move the virtual wall 3 blocks away from the top of areana to
        # accomodaate the size of the robot
        # new virtual arena is    [ unexplored rows +3  x  unexplored column]
        locs = np.asarray(zip(locs[0], locs[1]))
        # converts locs back into unexplored coordinates
        cost = np.abs(locs[:, 0] -
                      self.robot.center[0]) + np.abs(locs[:, 1] -
                                                     self.robot.center[1])
        cost = cost.tolist()
        # for each coordinate the cost is (x- center_x   + y- center_y)
        # the distance of the neighbour away from the center of robot
        locs = locs.tolist()
        while (cost):
            position = np.argmin(cost)  # position gives index of minimum cost
            coord = locs.pop(position)
            cost.pop(position)
            neighbours = np.asarray(
                [[-2, 0], [-2, -1], [-2, 1], [2, 0], [2, -1], [2, 1], [0, -2],
                 [1, -2], [-1, -2], [0, 2], [1, 2], [-1, 2]]) + coord
            neighbours = self.__validInds(
                neighbours
            )  # check if the coordinates in the list are valid or not
            for neighbour in neighbours:
                # added a new item to the exploredNeighbours dictionary
                if (neighbour not in self.exploredNeighbours):
                    self.exploredNeighbours[neighbour] = True
                    return neighbour
                return neighbour
        return None
Пример #3
0
class Exploration:
    def __init__(self, realMap=None, timeLimit=None, calibrateLim=6, sim=True):

        self.timeLimit = timeLimit
        self.exploredArea = 0
        self.currentMap = np.zeros([20, 15])
        if sim:
            from Simulator import Robot
            self.robot = Robot(self.currentMap, EAST, START, realMap)
            self.sensors = self.robot.getSensors()
        else:
            from Real import Robot
            self.robot = Robot(self.currentMap, EAST, START)
        self.exploredNeighbours = dict()
        self.sim = sim
        self.calibrateLim = calibrateLim
        self.virtualWall = [0, 0, MAX_ROWS, MAX_COLS]

    def __validInds(self, inds):

        front = self.frontFree()
        print(front)
        if (self.checkFree([1, 2, 3, 0], self.robot.center)):
            self.robot.moveBot(RIGHT)
            move.append(RIGHT)
            front = self.frontFree()
            for i in range(front):
                self.robot.moveBot(FORWARD)
            move.extend([FORWARD] * front)
            print("try right")
        elif (front):
            for i in range(front):
                self.robot.moveBot(FORWARD)
            move.extend([FORWARD] * front)
            print("try front")
        elif (self.checkFree([3, 0, 1, 2], self.robot.center)):
            self.robot.moveBot(LEFT)
            move.append(LEFT)
            front = self.frontFree()
            for i in range(front):
                self.robot.moveBot(FORWARD)
            move.extend([FORWARD] * front)
            print("try Left")
        else:
            self.robot.moveBot(RIGHT)
            self.robot.moveBot(RIGHT)
            move.append(BACK)

        if not (self.sim):
            calibrate_front = self.robot.can_calibrate_front()
            calibrate_right = self.robot.can_calibrate_right()
            if self.robot.is_corner():
                move.append('L')
            elif (calibrate_right[0]):
                global cal_count
                cal_count = cal_count + 1
                if (cal_count % 2 == 0):
                    move.append(calibrate_right[1])
            elif (calibrate_front[0]):
                move.append(calibrate_front[1])
        return move

    def checkFree(self, order, center):

        directionFree = np.asarray([
            self.northFree(center),
            self.eastFree(center),
            self.southFree(center),
            self.westFree(center)
        ])
        directionFree = directionFree[order]
        if self.robot.direction == NORTH:
            return directionFree[0]
        elif self.robot.direction == EAST:
            return directionFree[1]
        elif self.robot.direction == SOUTH:
            return directionFree[2]
        else:
            return directionFree[3]

    def validMove(self, inds):

        for (r, c) in inds:
            if not ((self.virtualWall[0] <= r < self.virtualWall[2]) and
                    (self.virtualWall[1] <= c < self.virtualWall[3])):
                return False
        return (self.currentMap[inds[0][0], inds[0][1]] == 1
                and self.currentMap[inds[1][0], inds[1][1]] == 1
                and self.currentMap[inds[2][0], inds[2][1]] == 1)

    def northFree(self, center):

        r, c = center
        inds = [[r - 2, c], [r - 2, c - 1], [r - 2, c + 1]]
        return self.validMove(inds)

    def eastFree(self, center):

        r, c = center
        inds = [[r, c + 2], [r - 1, c + 2], [r + 1, c + 2]]
        return self.validMove(inds)

    def southFree(self, center):

        r, c = center
        inds = [[r + 2, c], [r + 2, c - 1], [r + 2, c + 1]]
        return self.validMove(inds)

    def westFree(self, center):

        r, c = center
        inds = [[r, c - 2], [r - 1, c - 2], [r + 1, c - 2]]
        return self.validMove(inds)

    def frontFree(self):
        r, c = self.robot.center
        counter = 0
        if self.robot.direction == NORTH and self.validMove(
            [[r - 2, c], [r - 2, c - 1], [r - 2, c + 1]]):
            counter = 1
            while (True):
                if (self.validMove([[r-2-counter, c], [r-2-counter, c-1], [r-2-counter, c+1]])) and\
                        not self.checkFree([1, 2, 3, 0], [r-(counter), c]) and\
                        self.checkExplored([r-(counter), c]):
                    counter += 1
                else:
                    break
        elif self.robot.direction == EAST and self.validMove(
            [[r, c + 2], [r - 1, c + 2], [r + 1, c + 2]]):
            counter = 1
            while (True):
                if (self.validMove([[r, c+2+counter], [r-1, c+2+counter], [r+1, c+2+counter]])) and\
                        not self.checkFree([1, 2, 3, 0], [r, c+(counter)]) and\
                        self.checkExplored([r, c+(counter)]):
                    counter += 1
                else:
                    break
        elif self.robot.direction == WEST and self.validMove(
            [[r, c - 2], [r - 1, c - 2], [r + 1, c - 2]]):
            counter = 1
            while (True):
                if (self.validMove([[r, c-2-counter], [r-1, c-2-counter], [r+1, c-2-counter]])) and\
                        not self.checkFree([1, 2, 3, 0], [r, c-(counter)]) and\
                        self.checkExplored([r, c-(counter)]):
                    counter += 1
                else:
                    break
        elif self.robot.direction == SOUTH and self.validMove(
            [[r + 2, c], [r + 2, c - 1], [r + 2, c + 1]]):
            counter = 1
            while (True):
                if (self.validMove([[r+2+counter, c], [r+2+counter, c-1], [r+2+counter, c+1]])) and\
                        not self.checkFree([1, 2, 3, 0], [r+(counter), c]) and\
                        self.checkExplored([r+(counter), c]):
                    counter += 1
                else:
                    break
        return counter

    def checkExplored(self, center):
        r, c = center
        flag = True
        inds = []
        distanceShort = 3
        distanceLong = 5
        if self.robot.direction == NORTH:
            inds.append(
                zip([r - 1] * distanceShort, range(c + 2,
                                                   c + distanceShort + 2)))
            inds.append(
                zip([r + 1] * distanceLong, range(c + 2,
                                                  c + distanceLong + 2)))
            inds.append(
                zip([r - 1] * distanceLong, range(c - distanceLong - 1,
                                                  c - 1))[::-1])
        elif self.robot.direction == EAST:
            inds.append(
                zip(range(r + 2, r + distanceShort + 2),
                    [c + 1] * distanceShort))
            inds.append(
                zip(range(r + 2, r + distanceLong + 2),
                    [c - 1] * distanceLong))
            inds.append(
                zip(range(r - distanceLong - 1, r - 1),
                    [c + 1] * distanceLong)[::-1])
        elif self.robot.direction == WEST:
            inds.append(
                zip(range(r - distanceShort - 1, r - 1),
                    [c - 1] * distanceShort)[::-1])
            inds.append(
                zip(range(r - distanceLong - 1, r - 1),
                    [c + 1] * distanceLong)[::-1])
            inds.append(
                zip(range(r + 2, r + distanceLong + 2),
                    [c - 1] * distanceLong))
        else:
            inds.append(
                zip([r + 1] * distanceShort, range(c - distanceShort - 1,
                                                   c - 1))[::-1])
            inds.append(
                zip([r - 1] * distanceLong, range(c - distanceLong - 1,
                                                  c - 1))[::-1])
            inds.append(
                zip([r + 1] * distanceLong, range(c + 2,
                                                  c + distanceLong + 2)))
        for sensor in inds:
            if flag:
                for (x, y) in sensor:
                    if (x < self.virtualWall[0] or x == self.virtualWall[2]
                            or y < self.virtualWall[1]
                            or y == self.virtualWall[3]
                            or self.currentMap[x, y] == 2):
                        break
                    elif (self.currentMap[x, y] == 0):
                        flag = False
                        break
        return flag

    def moveStep(self, sensor_vals=None):

        if (sensor_vals):
            self.robot.getSensors(sensor_vals)
        else:
            self.robot.getSensors()
        move = self.nextMove()
        self.getExploredArea()
        if (self.exploredArea == 100):
            return move, True
        else:
            return move, False

    def explore(self):

        print "Starting exploration ..."
        startTime = time.time()
        endTime = startTime + self.timeLimit

        while (time.time() <= endTime):
            if (self.moveStep()):
                print "Exploration completed !"
                return

        print "Time over !"
        return

    def getExploredNeighbour(self):
        locs = np.where(self.currentMap == 0)
        self.virtualWall = [
            np.min(locs[0]),
            np.min(locs[1]),
            np.max(locs[0]) + 4,
            np.max(locs[1]) + 4
        ]
        if ((self.virtualWall[2] - self.virtualWall[0] < 3)
                and self.virtualWall[2] < MAX_ROWS - 3):
            self.virtualWall[2] += 3
        locs = np.asarray(zip(locs[0], locs[1]))
        cost = np.abs(locs[:, 0] -
                      self.robot.center[0]) + np.abs(locs[:, 1] -
                                                     self.robot.center[1])
        cost = cost.tolist()
        locs = locs.tolist()
        while (cost):
            position = np.argmin(cost)
            coord = locs.pop(position)
            cost.pop(position)
            neighbours = np.asarray([[-2, 0], [2, 0], [0, -2], [0, 2]]) + coord
            neighbours = self.__validInds(neighbours)
            for neighbour in neighbours:
                if (neighbour not in self.exploredNeighbours):
                    self.exploredNeighbours[neighbour] = True
                    return neighbour
        return None