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
class FastestPath: """Implementation of the A* algorithm for getting the fastest path in a 2D grid maze Attributes: exploredMap (Numpy array): The map after the exploration stage goal (list): Coordinate of the goal cell graph (list): To form the fastest path graph index (int): Counter path (list): The fastest path robot (Robot): Instance of the Robot class start (list): Coordinate of the initial cell waypoint (list): Coordinate of the way-point if specified """ def __init__(self, exploredMap, start, goal, direction, waypoint=None, calibrateLim=5, sim=True, fastest=False): """Constructor to initialize an instance of the FastestPath class Args: exploredMap (Numpy array): The map after the exploration stage start (list): Coordinate of the initial cell goal (list): Coordinate of the goal cell direction (int): The starting direction of the robot waypoint (list, optional): The coordinate of the way-point if specified sim (bool, optional): If specify if run is simulation or real """ self.exploredMap = exploredMap self.graph = [] self.start = start self.goal = goal self.waypoint = waypoint self.index = 1 self.direction = direction self.path = [] self.movement = [] self.calibrateLim = calibrateLim self.sim = sim self.fastestAlignRightCount = 0 self.fastest = fastest #will be true only during the fastestpath run, not the exploration if sim: from Simulator import Robot self.robot = Robot(self.exploredMap, direction, start, None) else: from Real import Robot self.robot = Robot(self.exploredMap, direction, start) def __getHeuristicCosts(self, goal): """Calculates the Manhattan distance between each cell and the goal cell Args: goal (list): Coordinates of the goal cell Returns: Numpy array: 2D grid with each cell storing the distance to the goal cell """ # this will create a grid of coordinates cols, rows = np.meshgrid(range(0, 15), range(0, MAX_ROWS)) cost = np.zeros([MAX_ROWS, 15]) cost = np.sqrt(np.square(rows - goal[0]) + np.square(cols - goal[1])) cost /= np.max(cost) return cost # e.g. If goal = np.asarray([1, 13]) # cost will be # [[0.58722022 0.54232614 0.49745804 0.45262363 0.40783404 0.36310583 # 0.31846488 0.27395385 0.22964829 0.18569534 0.14242182 0.10070744 # 0.06369298 0.04503773 0.06369298] # [0.58549055 0.54045282 0.49541508 0.45037735 0.40533961 0.36030188 # 0.31526414 0.27022641 0.22518867 0.18015094 0.1351132 0.09007547 # 0.04503773 0. 0.04503773] # [0.58722022 # 0.54232614 # 0.49745804 # 0.45262363 # 0.40783404 # 0.36310583 # 0.31846488 # 0.27395385 # 0.22964829 # 0.18569534 # 0.14242182 # 0.10070744 # 0.06369298 # 0.04503773 # 0.06369298] # [0.59237891 0.54790769 0.50353718 0.45929658 0.4152274 0.37139068 # 0.32787966 0.28484365 0.24253563 0.20141487 0.16238586 0.12738595 # 0.10070744 0.09007547 0.10070744] # [0.60087833 # 0.55708601 # 0.51350919 # 0.47020776 # 0.42726547 # 0.38480258 # 0.34299717 # 0.30212231 # 0.26261287 # 0.22518867 # 0.19107893 # 0.16238586 # 0.14242182 # 0.1351132 # 0.14242182] # [0.61257942 0.56968729 0.52715317 0.48507125 0.44357025 0.40282975 # 0.36310583 0.32477173 0.28838221 0.2547719 0.22518867 0.20141487 # 0.18569534 0.18015094 0.18569534] # [0.62730306 # 0.58549055 # 0.54419302 # 0.50353718 # 0.46369186 # 0.42488514 # 0.38742924 # 0.35175595 # 0.31846488 # 0.28838221 # 0.26261287 # 0.24253563 # 0.22964829 # 0.22518867 # 0.22964829] # [0.64484223 0.60424462 0.5643212 0.52522573 0.48715759 0.45037735 # 0.4152274 0.38215785 0.35175595 0.32477173 0.30212231 0.28484365 # 0.27395385 0.27022641 0.27395385] # [0.66497419 # 0.62568421 # 0.58722022 # 0.54975562 # 0.51350919 # 0.47875769 # 0.44585083 # 0.4152274 # 0.38742924 # 0.36310583 # 0.34299717 # 0.32787966 # 0.31846488 # 0.31526414 # 0.31846488] # [0.68747119 0.64954345 0.61257942 0.57676442 0.54232614 0.5095438 # 0.47875769 0.45037735 0.42488514 0.40282975 0.38480258 0.37139068 # 0.36310583 0.36030188 0.36310583] # [0.71210911 # 0.67556602 # 0.64010648 # 0.60592075 # 0.57323678 # 0.54232614 # 0.51350919 # 0.48715759 # 0.46369186 # 0.44357025 # 0.42726547 # 0.4152274 # 0.40783404 # 0.40533961 # 0.40783404] # [0.73867377 0.70351191 0.66953406 0.63692976 0.60592075 0.57676442 # 0.54975562 0.52522573 0.50353718 0.48507125 0.47020776 0.45929658 # 0.45262363 0.45037735 0.45262363] # [0.76696499 # 0.73316121 # 0.70062273 # 0.66953406 # 0.64010648 # 0.61257942 # 0.58722022 # 0.5643212 # 0.54419302 # 0.52715317 # 0.51350919 # 0.50353718 # 0.49745804 # 0.49541508 # 0.49745804] # [0.79679887 0.76431571 0.73316121 0.70351191 0.67556602 0.64954345 # 0.62568421 0.60424462 0.58549055 0.56968729 0.55708601 0.54790769 # 0.54232614 0.54045282 0.54232614] # [0.82800868 # 0.79679887 # 0.76696499 # 0.73867377 # 0.71210911 # 0.68747119 # 0.66497419 # 0.64484223 # 0.62730306 # 0.61257942 # 0.60087833 # 0.59237891 # 0.58722022 # 0.58549055 # 0.58722022] # [0.86044472 0.8304548 0.80187407 0.77485849 0.7495773 0.72621165 # 0.70495206 0.68599434 0.66953406 0.65575932 0.64484223 0.63692976 # 0.63213473 0.63052829 0.63213473] # [0.89397351 # 0.86514664 # 0.8377503 # 0.81192931 # 0.7878386 # 0.76564149 # 0.74550716 # 0.72760688 # 0.71210911 # 0.69917366 # 0.68894487 # 0.6815446 # 0.67706562 # 0.67556602 # 0.67706562] # [0.92847669 0.9007547 0.87447463 0.84977028 0.82678291 0.80565949 # 0.78655023 0.76960515 0.75497001 0.74278135 0.73316121 0.72621165 # 0.72200982 0.72060376 0.72200982] # [0.96384962 # 0.93717455 # 0.91194463 # 0.88828298 # 0.86631813 # 0.84618221 # 0.82800868 # 0.81192931 # 0.7980707 # 0.78655023 # 0.77747185 # 0.77092184 # 0.76696499 # 0.76564149 # 0.76696499] # [1. 0.97431518 0.95007206 0.92738372 0.90636693 0.88714049 # 0.86982314 0.85453094 0.84137432 0.8304548 0.82186154 0.81566807 # 0.81192931 0.81067923 0.81192931]] 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: [[-1 0 1] # [-1 0 1] # [-1 0 1]] # y: [[-1 -1 -1] # [ 0 0 0] # [ 1 1 1]] x, y = x + r, y + c # e.g. If r = 5 and c = 12 # x: [[4 5 6] # [4 5 6] # [4 5 6]] # y: [[11 11 11] # [12 12 12] # [13 13 13]] 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.exploredMap[x[0, 0]:x[0, 2] + 1, y[0, 0]:y[2, 0] + 1] != 1)): # If any of the space within the 3 by 3 space is not a free space, append false valid.append(False) else: valid.append(True) return [inds[i] for i in range(len(inds)) if valid[i]] # Returns only valid indices def __getNeighbours(self, loc): """Returns the coordinates of a 3x3 neighbourhood around the passed location Only those neighbours are returned that have been explored and are not obstacles Args: loc (list): Coordinates of a cell Returns: list of list: Coordinates of all valid neighbours of the cell """ r, c = loc.coord inds = [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)] inds = self.__validInds(inds) neighbours = [self.graph[n[0]][n[1]] for n in inds] # if its explored and not an obstacle return [n for n in neighbours if n.value == 1] def __getCost(self, current_pos, next_pos): # Cost is one if the robot needs to turn if it wants to move from current position to the next position if self.direction in [NORTH, SOUTH]: if current_pos[1] == next_pos[1]: return 0 else: return 1 else: if current_pos[0] == next_pos[0]: return 0 else: return 1 return 1 # Function to set current direction of robot based on previous and current position def __setDirection(self, prev_pos, current_pos, backwards=False): # If row index increases, the robot has moved south if prev_pos[0] < current_pos[0]: if (backwards == False): self.direction = SOUTH else: self.direction = NORTH # If the column index increases, the robot has moved east elif prev_pos[1] < current_pos[1]: if (backwards == False): self.direction = EAST else: self.direction = WEST # If the column index has decreased, the robot has moved west elif prev_pos[1] > current_pos[1]: if (backwards == False): self.direction = WEST else: self.direction = EAST # If row index decreases, the robot has moved north else: if (backwards == False): self.direction = NORTH else: self.direction = SOUTH def __astar(self, start, goal, backwards=False): """Implementation of the a* algorithm for finding the shortest path in a 2d grid maze Args: start (list): Coordinates of the starting cell goal (list): Coordinates of the goal cell Returns: list of list: A list of coordinates specifying the path from start to goal Raises: ValueError: If no path is found between the given start and goal """ goal = self.graph[goal[0]][goal[1]] # set of nodes already evaluated closedSet = set() # set of discovered nodes that have not been evaluated yet openSet = set() current = self.graph[start[0]][start[1]] current.G = 0 # add start node to openSet openSet.add(current) prev = None # While there are still nodes to be evaluated while (openSet): # Get the node with the minimum sum of cost from start to that position and from that position to the goal current = min(openSet, key=lambda o: o.G + o.H) # If the node has a previous node (not start), set the new direction of robot if prev: if (backwards == False): self.__setDirection(prev.coord, current.coord) else: self.__setDirection(prev.coord, current.coord, backwards=True) # if goal is reached trace back the path if (current == goal): path = [] while (current.parent): path.append(current.coord) current = current.parent # Append the coordinate which does not have a parent (which is the start point) path.append(current.coord) # Return path backwards (from start to goal) return path[::-1] # If goal is not yet reached else: # Mark current node to be evaluated openSet.remove(current) closedSet.add(current) # Get indices of 3 by 3 area around the current coordinate which are free and explored for node in self.__getNeighbours(current): # For each node returned, # Do nothing if the node has already been evaluated if node in closedSet: continue # If the node has yet to be evaluated if node in openSet: # calculate new G cost new_g = current.G + self.__getCost( current.coord, node.coord) # If previous cost from goal to the coordinate is larger than the new cost if node.G > new_g: # Update to new, lower cost node.G = new_g # Update node's parent to be the current node node.parent = current else: # if neither in openSet nor in closedSet # cost of moving between neighbours is 1 node.G = current.G + self.__getCost( current.coord, node.coord) node.parent = current # Add new node to be evaluated openSet.add(node) prev = copy.deepcopy(current) # exception is no path is found raise ValueError('No Path Found') def __initGraph(self, h_n): """To create a grid of graph nodes from the exploration map Args: h_n (Numpy array): The heuristic cost of each node to goal node """ self.graph = [] for row in xrange(MAX_ROWS): self.graph.append([]) for col in xrange(MAX_COLS): self.graph[row].append( Node(self.exploredMap[row][col], (row, col), h_n[row][col])) def getFastestPath(self, backwards=False): """To call the fastest path algorithm and handle the case if there is a way-point or not """ path = [] start = copy.copy(self.start) # If there is a waypoint if (self.waypoint): h_n = self.__getHeuristicCosts(self.waypoint) self.__initGraph(h_n) # Set waypoint as the goal first and find shortest path from start to goal if (backwards == False): fsp = self.__astar(start, self.waypoint) else: fsp = self.__astar(start, self.waypoint, backwards=True) # Set start to be way point start = copy.copy(self.waypoint) # Leaves out the last element as it will be the starting node for the next fastest path # (Otherwise the way-point will be added twice) path.extend(fsp[:-1]) # Up to but not including the last element h_n = self.__getHeuristicCosts(self.goal) self.__initGraph(h_n) # start will be waypoint at this point if there is a waypoint if (backwards == False): fsp = self.__astar(start, self.goal) else: fsp = self.__astar(start, self.goal, backwards=True) path.extend(fsp) self.path = path self.markMap() def markMap(self): """To mark the path on the exploration map """ # Related to the GUI (Refer to the color coding in the javascript code for the GUI) for ind in self.path: self.exploredMap[tuple(ind)] = 6 # Adds more movements to self.movement so that robot moves one space if possible def moveStep(self, backwards=False): """To take the fastest path, robot location and determine the next move for the robot to follow the path """ movement = [] if (backwards == False): move_command = FORWARD else: move_command = BACKWARDS # self.index is first initialised to 1 when the FastestPath class is first initialised # Moving one step will increase self.index by one, thereby making self.path[self.index]) go to the next space allong the path # If the next space in the path is not equal to robot's centre and hence, robot should move to the next space in the path if (self.robot.center.tolist() != self.path[self.index]): # diff will be an array where first element is vertical distance from robot's centre to next space in path # Second element is horizontal distance from robot's centre to next space in path diff = self.robot.center - np.asarray(self.path[self.index]) # If difference between row index of robot's centre and the next space is -1, ot means that the robot should move south #every 3 steps can calibrate right, it calibrate right. if self.fastest: calibrate_right = self.robot.can_calibrate_right() if (calibrate_right[0]): self.fastestAlignRightCount += 1 if (self.fastestAlignRightCount % 3) == 0: movement.extend(ALIGNRIGHT) if (diff[0] == -1 and diff[1] == 0): # Going south if self.robot.direction == NORTH: # If robot is facing north, it needs to turn right twice and move forward once before it can move south by one space movement.extend((RIGHT, RIGHT, move_command)) elif self.robot.direction == EAST: movement.extend((RIGHT, move_command)) elif self.robot.direction == SOUTH: movement.append(move_command) else: movement.extend((LEFT, move_command)) elif (diff[0] == 0 and diff[1] == 1): # Going west if self.robot.direction == NORTH: movement.extend((LEFT, move_command)) elif self.robot.direction == EAST: movement.extend((RIGHT, RIGHT, move_command)) elif self.robot.direction == SOUTH: movement.extend((RIGHT, move_command)) else: movement.append(move_command) elif (diff[0] == 0 and diff[1] == -1): # Going east if self.robot.direction == NORTH: movement.extend((RIGHT, move_command)) elif self.robot.direction == EAST: movement.append(move_command) elif self.robot.direction == SOUTH: movement.extend((LEFT, move_command)) else: movement.extend((RIGHT, RIGHT, move_command)) else: # Going north if self.robot.direction == NORTH: movement.append(move_command) elif self.robot.direction == EAST: movement.extend((LEFT, move_command)) elif self.robot.direction == SOUTH: movement.extend((RIGHT, RIGHT, move_command)) else: movement.extend((RIGHT, move_command)) for move in movement: self.robot.moveBot(move) # Add movements to self.movement self.movement.extend(movement) # if not (self.sim): # if (self.robot.stepCounter + len(movement) > self.calibrateLim): # calibrate = self.robot.can_calibrate() # if (calibrate[0]): # movement.append(calibrate[1]) # self.robot.stepCounter = 0 # Increase self.index by one so that the next space in self.path can be evaluated self.index += 1
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
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