diff --git a/Search-based Planning/.idea/.gitignore b/Search-based Planning/.idea/.gitignore deleted file mode 100644 index 0e40fe8..0000000 --- a/Search-based Planning/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ - -# Default ignored files -/workspace.xml \ No newline at end of file diff --git a/Search-based Planning/.idea/Search-based Planning.iml b/Search-based Planning/.idea/Search-based Planning.iml index 7c9d48f..f3d7bc9 100644 --- a/Search-based Planning/.idea/Search-based Planning.iml +++ b/Search-based Planning/.idea/Search-based Planning.iml @@ -2,11 +2,10 @@ - + - \ No newline at end of file diff --git a/Search-based Planning/.idea/dictionaries/Huiming_Zhou.xml b/Search-based Planning/.idea/dictionaries/Huiming_Zhou.xml new file mode 100644 index 0000000..f550db9 --- /dev/null +++ b/Search-based Planning/.idea/dictionaries/Huiming_Zhou.xml @@ -0,0 +1,10 @@ + + + + astar + dijk + huiming + zhou + + + \ No newline at end of file diff --git a/Search-based Planning/.idea/vcs.xml b/Search-based Planning/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/Search-based Planning/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Search-based Planning/.idea/workspace.xml b/Search-based Planning/.idea/workspace.xml new file mode 100644 index 0000000..e522e54 --- /dev/null +++ b/Search-based Planning/.idea/workspace.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1592347358698 + + + + \ No newline at end of file diff --git a/Search-based Planning/__pycache__/astar.cpython-37.pyc b/Search-based Planning/__pycache__/astar.cpython-37.pyc deleted file mode 100644 index e577f7b..0000000 Binary files a/Search-based Planning/__pycache__/astar.cpython-37.pyc and /dev/null differ diff --git a/Search-based Planning/__pycache__/bfs.cpython-37.pyc b/Search-based Planning/__pycache__/bfs.cpython-37.pyc deleted file mode 100644 index bb9e43e..0000000 Binary files a/Search-based Planning/__pycache__/bfs.cpython-37.pyc and /dev/null differ diff --git a/Search-based Planning/__pycache__/dijkstra.cpython-37.pyc b/Search-based Planning/__pycache__/dijkstra.cpython-37.pyc deleted file mode 100644 index 2e9ea78..0000000 Binary files a/Search-based Planning/__pycache__/dijkstra.cpython-37.pyc and /dev/null differ diff --git a/Search-based Planning/__pycache__/environment.cpython-37.pyc b/Search-based Planning/__pycache__/environment.cpython-37.pyc index 30a3d62..fbb784b 100644 Binary files a/Search-based Planning/__pycache__/environment.cpython-37.pyc and b/Search-based Planning/__pycache__/environment.cpython-37.pyc differ diff --git a/Search-based Planning/__pycache__/mazemods.cpython-37.pyc b/Search-based Planning/__pycache__/mazemods.cpython-37.pyc deleted file mode 100644 index f326950..0000000 Binary files a/Search-based Planning/__pycache__/mazemods.cpython-37.pyc and /dev/null differ diff --git a/Search-based Planning/__pycache__/queue.cpython-37.pyc b/Search-based Planning/__pycache__/queue.cpython-37.pyc index ce8d5d7..65db690 100644 Binary files a/Search-based Planning/__pycache__/queue.cpython-37.pyc and b/Search-based Planning/__pycache__/queue.cpython-37.pyc differ diff --git a/Search-based Planning/__pycache__/tools.cpython-37.pyc b/Search-based Planning/__pycache__/tools.cpython-37.pyc new file mode 100644 index 0000000..9ac48b1 Binary files /dev/null and b/Search-based Planning/__pycache__/tools.cpython-37.pyc differ diff --git a/Search-based Planning/a_star.py b/Search-based Planning/a_star.py new file mode 100644 index 0000000..6fa6329 --- /dev/null +++ b/Search-based Planning/a_star.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +@author: huiming zhou +""" + +import queue +import environment +import tools + +class Astar: + def __init__(self, Start_State, Goal_State, n, m, heuristic_type): + self.xI = Start_State + self.xG = Goal_State + self.u_set = environment.motions # feasible input set + self.obs_map = environment.map_obs() # position of obstacles + self.n = n + self.m = m + self.heuristic_type = heuristic_type + + def searching(self): + """ + Searching using A_star. + + :return: planning path, action in each node, visited nodes in the planning process + """ + + q_astar = queue.QueuePrior() # priority queue + q_astar.put(self.xI, 0) + parent = {self.xI: self.xI} # record parents of nodes + actions = {self.xI: (0, 0)} # record actions of nodes + cost = {self.xI: 0} + visited = [] + + while not q_astar.empty(): + x_current = q_astar.get() + visited.append(x_current) # record visited nodes + if x_current == self.xG: # stop condition + break + for u_next in self.u_set: # explore neighborhoods of current node + x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) + # if neighbor node is not in obstacles -> ... + if 0 <= x_next[0] < self.n and 0 <= x_next[1] < self.m \ + and not tools.obs_detect(x_current, u_next, self.obs_map): + new_cost = cost[x_current] + int(self.get_cost(x_current, u_next)) + if x_next not in cost or new_cost < cost[x_next]: # conditions for updating cost + cost[x_next] = new_cost + priority = new_cost + self.Heuristic(x_next, self.xG, self.heuristic_type) + q_astar.put(x_next, priority) # put node into queue using priority "f+h" + parent[x_next] = x_current + actions[x_next] = u_next + [path_astar, actions_astar] = tools.extract_path(self.xI, self.xG, parent, actions) + return path_astar, actions_astar, visited + + def get_cost(self, x, u): + """ + Calculate cost for this motion + + :param x: current node + :param u: input + :return: cost for this motion + :note: cost function could be more complicate! + """ + + return 1 + + def Heuristic(self, state, goal, heuristic_type): + """ + Calculate heuristic. + + :param state: current node (state) + :param goal: goal node (state) + :param heuristic_type: choosing different heuristic functions + :return: heuristic + """ + + if heuristic_type == "manhattan": + return abs(goal[0] - state[0]) + abs(goal[1] - state[1]) + elif heuristic_type == "euclidean": + return ((goal[0] - state[0]) ** 2 + (goal[1] - state[1]) ** 2) ** (1 / 2) + else: + print("Please choose right heuristic type!") + + +if __name__ == '__main__': + x_Start = (15, 10) # Starting node + x_Goal = (48, 15) # Goal node + astar = Astar(x_Start, x_Goal, environment.col, environment.row, "manhattan") + [path_astar, actions_astar, visited_astar] = astar.searching() + tools.showPath(x_Start, x_Goal, path_astar, visited_astar, 'Astar_searching') # Plot path and visited nodes \ No newline at end of file diff --git a/Search-based Planning/astar.py b/Search-based Planning/astar.py deleted file mode 100644 index b024240..0000000 --- a/Search-based Planning/astar.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -@author: Huiming Zhou -""" - - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import colors - -from queue import * -from mazemods import * -from environment import * - - -def aStarSearch(xI, xG, n, m, O, heuristic_type): - q_astar = QueuePrior() - q_astar.put(xI, 0) - parent = {xI: xI} - actions = {xI: (0, 0)} - rec_cost = {xI: 0} - u_set = {(-1, 0), (1, 0), (0, 1), (0, -1)} - - while not q_astar.empty(): - x_current = q_astar.get() - if x_current == xG: - break - for u_next in u_set: - x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) - if 0 <= x_next[0] < n and 0 <= x_next[1] < m \ - and not collisionCheck(x_current, u_next, O): - new_cost = rec_cost[x_current] + 1 - if x_next not in rec_cost or new_cost < rec_cost[x_next]: - rec_cost[x_next] = new_cost - priority = new_cost + Heuristic(x_next, xG, heuristic_type) - q_astar.put(x_next, priority) - parent[x_next] = x_current - actions[x_next] = u_next - [path_astar, actions_astar] = extractpath(xI, xG, parent, actions) - [simple_cost, west_cost, east_cost] = cost_calculation(xI, actions_astar, O) - return path_astar, actions_astar, len(parent), simple_cost, west_cost, east_cost - - -# Heuristic function used in A* algorithm -def Heuristic(state, goal, heuristic_type): - if heuristic_type == "manhattanHeuristic": - return abs(goal[0] - state[0]) + abs(goal[1] - state[1]) - elif heuristic_type == "euclideanHeuristic": - return ((goal[0] - state[0]) ** 2 + (goal[1] - state[1]) ** 2) ** (1 / 2) - else: - print("Please choose right heuristic type!") diff --git a/Search-based Planning/bfs.py b/Search-based Planning/bfs.py index 7064110..4e82c5d 100644 --- a/Search-based Planning/bfs.py +++ b/Search-based Planning/bfs.py @@ -1,37 +1,59 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -@author: Huiming Zhou +@author: huiming zhou """ +import queue +import environment +import tools -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import colors +class BFS: + """ + BFS -> Breadth-first Searching + """ -from queue import * -from mazemods import * + def __init__(self, Start_State, Goal_State, n, m): + self.xI = Start_State + self.xG = Goal_State + self.u_set = environment.motions # feasible input set + self.obs_map = environment.map_obs() # position of obstacles + self.n = n + self.m = m + + def searching(self): + """ + Searching using BFS. + + :return: planning path, action in each node, visited nodes in the planning process + """ + + q_bfs = queue.QueueFIFO() # first-in-first-out queue + q_bfs.put(self.xI) + parent = {self.xI: self.xI} # record parents of nodes + actions = {self.xI: (0, 0)} # record actions of nodes + visited = [] + while not q_bfs.empty(): + x_current = q_bfs.get() + visited.append(x_current) # record visited nodes + if x_current == self.xG: # stop condition + break + for u_next in self.u_set: # explore neighborhoods of current node + x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) # neighbor node + # if neighbor node is not in obstacles and has not been visited -> ... + if 0 <= x_next[0] < self.n and 0 <= x_next[1] < self.m \ + and x_next not in parent \ + and not tools.obs_detect(x_current, u_next, self.obs_map): + q_bfs.put(x_next) + parent[x_next] = x_current + actions[x_next] = u_next + [path_bfs, actions_bfs] = tools.extract_path(self.xI, self.xG, parent, actions) # extract path + return path_bfs, actions_bfs, visited -def breadthFirstSearch(xI, xG, n, m, O): - q_bfs = QueueFIFO() - q_bfs.put(xI) - parent = {xI: xI} - actions = {xI: (0, 0)} - u_set = {(-1, 0), (1, 0), (0, 1), (0, -1)} - - while not q_bfs.empty(): - x_current = q_bfs.get() - if x_current == xG: - break - for u_next in u_set: - x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) - if 0 <= x_next[0] < n and 0 <= x_next[1] < m \ - and x_next not in parent \ - and not collisionCheck(x_current, u_next, O): - q_bfs.put(x_next) - parent[x_next] = x_current - actions[x_next] = u_next - [path_bfs, actions_bfs] = extractpath(xI, xG, parent, actions) - [simple_cost, west_cost, east_cost] = cost_calculation(xI, actions_bfs, O) - return path_bfs, actions_bfs, len(parent), simple_cost, west_cost, east_cost \ No newline at end of file +if __name__ == '__main__': + x_Start = (15, 10) # Starting node + x_Goal = (48, 15) # Goal node + bfs = BFS(x_Start, x_Goal, environment.col, environment.row) + [path_bf, actions_bf, visited_bfs] = bfs.searching() + tools.showPath(x_Start, x_Goal, path_bf, visited_bfs, 'breadth_first_searching') # Plot path and visited nodes \ No newline at end of file diff --git a/Search-based Planning/dfs.py b/Search-based Planning/dfs.py index 6300851..04e4ab5 100644 --- a/Search-based Planning/dfs.py +++ b/Search-based Planning/dfs.py @@ -1,37 +1,60 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -@author: Huiming Zhou +@author: huiming zhou """ +import queue +import environment +import tools -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import colors +class DFS: + """ + DFS -> Depth-first Searching + """ -from queue import * -from mazemods import * + def __init__(self, Start_State, Goal_State, n, m): + self.xI = Start_State + self.xG = Goal_State + self.u_set = environment.motions # feasible input set + self.obs_map = environment.map_obs() # position of obstacles + self.n = n + self.m = m + + def searching(self): + """ + Searching using DFS. + + :return: planning path, action in each node, visited nodes in the planning process + """ + + q_dfs = queue.QueueLIFO() # last-in-first-out queue + q_dfs.put(self.xI) + parent = {self.xI: self.xI} # record parents of nodes + actions = {self.xI: (0, 0)} # record actions of nodes + visited = [] + + while not q_dfs.empty(): + x_current = q_dfs.get() + visited.append(x_current) # record visited nodes + if x_current == self.xG: # stop condition + break + for u_next in self.u_set: # explore neighborhoods of current node + x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) # neighbor node + # if neighbor node is not in obstacles and has not been visited -> ... + if 0 <= x_next[0] < self.n and 0 <= x_next[1] < self.m \ + and x_next not in parent \ + and not tools.obs_detect(x_current, u_next, self.obs_map): + q_dfs.put(x_next) + parent[x_next] = x_current + actions[x_next] = u_next + [path_dfs, actions_dfs] = tools.extract_path(self.xI, self.xG, parent, actions) + return path_dfs, actions_dfs, visited -def depth_fist_search(xI, xG, n, m, O): - q_dfs = QueueLIFO() - q_dfs.put(xI) - parent = {xI: xI} - actions = {xI: (0, 0)} - u_set = {(-1, 0), (1, 0), (0, 1), (0, -1)} - - while not q_dfs.empty(): - x_current = q_dfs.get() - if x_current == xG: - break - for u_next in u_set: - x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) - if 0 <= x_next[0] < n and 0 <= x_next[1] < m \ - and x_next not in parent \ - and not collisionCheck(x_current, u_next, O): - q_dfs.put(x_next) - parent[x_next] = x_current - actions[x_next] = u_next - [path_dfs, actions_dfs] = extractpath(xI, xG, parent, actions) - [simple_cost, west_cost, east_cost] = cost_calculation(xI, actions_dfs, O) - return path_dfs, actions_dfs, len(parent), simple_cost, west_cost, east_cost \ No newline at end of file +if __name__ == '__main__': + x_Start = (15, 10) # Starting node + x_Goal = (48, 15) # Goal node + dfs = DFS(x_Start, x_Goal, environment.col, environment.row) + [path_dfs, actions_dfs, visited_dfs] = dfs.searching() + tools.showPath(x_Start, x_Goal, path_dfs, visited_dfs, 'depth_first_searching') # Plot path and visited nodes \ No newline at end of file diff --git a/Search-based Planning/dijkstra.py b/Search-based Planning/dijkstra.py index 6341100..06dea0f 100644 --- a/Search-based Planning/dijkstra.py +++ b/Search-based Planning/dijkstra.py @@ -1,57 +1,72 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -@author: Huiming Zhou +@author: huiming zhou """ +import queue +import environment +import tools -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import colors +class Dijkstra: + def __init__(self, Start_State, Goal_State, n, m): + self.xI = Start_State + self.xG = Goal_State + self.u_set = environment.motions # feasible input set + self.obs_map = environment.map_obs() # position of obstacles + self.n = n + self.m = m -from queue import * -from mazemods import * + def searching(self): + """ + Searching using Dijkstra. + + :return: planning path, action in each node, visited nodes in the planning process + """ + + q_dijk = queue.QueuePrior() # priority queue + q_dijk.put(self.xI, 0) + parent = {self.xI: self.xI} # record parents of nodes + actions = {self.xI: (0, 0)} # record actions of nodes + cost = {self.xI: 0} + visited = [] + + while not q_dijk.empty(): + x_current = q_dijk.get() + visited.append(x_current) # record visited nodes + if x_current == self.xG: # stop condition + break + for u_next in self.u_set: # explore neighborhoods of current node + x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) + # if neighbor node is not in obstacles -> ... + if 0 <= x_next[0] < self.n and 0 <= x_next[1] < self.m \ + and not tools.obs_detect(x_current, u_next, self.obs_map): + new_cost = cost[x_current] + int(self.get_cost(x_current, u_next)) + if x_next not in cost or new_cost < cost[x_next]: + cost[x_next] = new_cost + priority = new_cost + q_dijk.put(x_next, priority) # put node into queue using cost to come as priority + parent[x_next] = x_current + actions[x_next] = u_next + [path_dijk, actions_dijk] = tools.extract_path(self.xI, self.xG, parent, actions) + return path_dijk, actions_dijk, visited + + def get_cost(self, x, u): + """ + Calculate cost for this motion + + :param x: current node + :param u: input + :return: cost for this motion + :note: cost function could be more complicate! + """ + + return 1 -def DijkstraSearch(xI, xG, n, m, O, cost_type): - q_dijk = QueuePrior() - q_dijk.put(xI, 0) - parent = {xI: xI} - actions = {xI: (0, 0)} - rec_cost = {xI: 0} - u_set = {(-1, 0), (1, 0), (0, 1), (0, -1)} - - while not q_dijk.empty(): - x_current = q_dijk.get() - if x_current == xG: - break - for u_next in u_set: - x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) - if 0 <= x_next[0] < n and 0 <= x_next[1] < m \ - and not collisionCheck(x_current, u_next, O): - cost_x = costfunc(x_current, x_next, O, cost_type) - new_cost = rec_cost[x_current] + cost_x - if x_next not in rec_cost or new_cost < rec_cost[x_next]: - rec_cost[x_next] = new_cost - priority = new_cost - q_dijk.put(x_next, priority) - parent[x_next] = x_current - actions[x_next] = u_next - [path_dijk, actions_dijk] = extractpath(xI, xG, parent, actions) - [simple_cost, west_cost, east_cost] = cost_calculation(xI, actions_dijk, O) - return path_dijk, actions_dijk, len(parent), simple_cost, west_cost, east_cost - - -# Cost function used in Dijkstra's algorithm -def costfunc(x_current, x_next, O, function_type): - if function_type == "westcost": - return x_next[0] ** 2 - elif function_type == "eastcost": - maxX = 0 - for k in range(len(O)): - westxO = O[k][1] - if westxO > maxX: - maxX = westxO - return (maxX - x_next[0]) ** 2 - else: - print("Please choose right cost function!") +if __name__ == '__main__': + x_Start = (15, 10) # Starting node + x_Goal = (48, 15) # Goal node + dijkstra = Dijkstra(x_Start, x_Goal, environment.col, environment.row) + [path_dijk, actions_dijk, visited_dijk] = dijkstra.searching() + tools.showPath(x_Start, x_Goal, path_dijk, visited_dijk, 'dijkstra_searching') \ No newline at end of file diff --git a/Search-based Planning/environment.py b/Search-based Planning/environment.py index ff22b3e..3a6533c 100644 --- a/Search-based Planning/environment.py +++ b/Search-based Planning/environment.py @@ -1,16 +1,49 @@ -n, m = 36, 18 +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +@author: huiming zhou +""" -O=[[0,35,0,0],[0,0,0,17],[0,35,17,17],[35,35,0,17],[11,26,1,1],[1,9,2,2], -[28,33,2,2],[9,14,3,3],[16,26,3,3],[28,29,3,3],[32,33,3,3],[2,7,4,4], -[14,14,4,4],[22,26,4,4],[28,29,4,4],[7,12,5,5],[14,18,5,5],[20,20,5,5], -[25,26,5,5],[28,29,5,5],[31,34,5,5],[1,5,6,6],[12,12,6,6],[20,23,6,6], -[25,26,6,6],[28,28,6,6],[5,10,7,7],[12,18,7,7],[25,26,7,7],[28,33,7,7], -[2,3,8,8],[5,5,8,8],[9,10,8,8],[18,26,8,8],[28,29,8,8],[2,3,9,9],[5,5,9,9], -[7,7,9,9],[9,16,9,9],[18,19,9,9],[28,29,9,9],[31,34,9,9],[2,2,10,10], -[5,5,10,10],[7,7,10,10],[9,9,10,10],[13,13,10,10],[18,19,10,10],[21,28,10,10], -[2,3,11,11],[5,5,11,11],[7,7,11,11],[9,9,11,11],[11,11,11,11],[13,13,11,11], -[15,18,11,11],[21,23,11,11],[28,33,11,11],[2,3,12,12],[5,5,12,12],[7,7,12,12], -[9,9,12,12],[11,11,12,12],[25,26,12,12],[28,29,12,12],[2,3,13,13],[5,5,13,13], -[7,7,13,13],[9,9,13,13],[11,11,13,13],[13,16,13,13],[18,26,13,13], -[28,29,13,13],[31,34,13,13],[2,3,14,14],[7,7,14,14],[11,11,14,14], -[18,24,14,14],[28,29,14,14],[2,24,15,15],[26,33,15,15]] +import numpy as np + +col, row = 50, 30 # size of background +motions = [(1, 0), (-1, 0), (0, 1), (0, -1)] # feasible motion sets + + +def obstacles(): + """ + Design the obstacles' positions. + :return: the map of obstacles. + """ + + background = [[[1., 1., 1.] + for x in range(col)] for y in range(row)] + for j in range(col): + background[0][j] = [0., 0., 0.] + background[row - 1][j] = [0., 0., 0.] + for i in range(row): + background[i][0] = [0., 0., 0.] + background[i][col - 1] = [0., 0., 0.] + for i in range(10, 20): + background[15][i] = [0., 0., 0.] + for i in range(15): + background[row - 1 - i][30] = [0., 0., 0.] + background[i + 1][20] = [0., 0., 0.] + background[i + 1][40] = [0., 0., 0.] + return background + + +def map_obs(): + """ + Using a matrix to represent the position of obstacles, + which is used for obstacle detection. + :return: a matrix, in which '1' represents obstacle. + """ + + obs_map = np.zeros((col, row)) + pos_map = obstacles() + for i in range(col): + for j in range(row): + if pos_map[j][i] == [0., 0., 0.]: + obs_map[i][j] = 1 + return obs_map \ No newline at end of file diff --git a/Search-based Planning/mazemods.py b/Search-based Planning/mazemods.py deleted file mode 100644 index 5f93885..0000000 --- a/Search-based Planning/mazemods.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -@author: Huiming Zhou -""" - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import colors - - -# Makes the maze data with obstacles stored as black RGB values, -# free space stored as white RGB values" -def makeMaze(n, m, O): - # Initialize to lists of 1. for RGB color index = white - gridvals = [[[1. for i in range(3)] for col in range(n)] for row in range(m)] - # Iterate through each obstacle - for l in range(len(O)): - # Find boundaries of current obstacle - west, east = [O[l][0], O[l][1]] - south, north = [O[l][2], O[l][3]] - # Iterate through each cell of obstacle (clunky, but works) - for i in range(west, east + 1): - for j in range(south, north + 1): - gridvals[j][i] = [0., 0., 0.] # Change entry to RGB black - return gridvals - - -# Function to actually plot the maze -def maze(n, m, O): - gridvals = makeMaze(n, m, O) - fig, ax = plt.subplots() # make a figure + axes - ax.imshow(gridvals) # Plot it - ax.invert_yaxis() # Needed so that bottom left is (0,0) - # ax.axis('off') - - -# Checks for collisions given position x, control u, obstacle list O -def collisionCheck(x, u, O): - # Check input - if u != (-1, 0) and u != (1, 0) and u != (0, -1) and u != (0, 1): - print('collision_check error: Invalid input u!') - return - nextx = [x[i] + u[i] for i in range(len(x))] - for l in range(len(O)): - # Find boundaries of current obstacle - west, east = [O[l][0], O[l][1]] - south, north = [O[l][2], O[l][3]] - # Check if nextx is contained in obstacle boundaries - if west <= nextx[0] <= east and south <= nextx[1] <= north: - return True - # If we iterate through whole list and don't trigger the "if", then no collisions - return False - - -# Makes a piece of data with obstacles stored as black RGB values, -# free space stored as white RGB values, and path stored as increasing hue of -# yellow RGB values -def makePath(xI, xG, path, n, m, O): - # Obtain the grid populated with obstacles and free space RGB values first - gridpath = makeMaze(n, m, O) - L = len(path) - # Iterate through the path to plot as increasing shades of yellow - for l in range(L - 1): - gridpath[path[l][1]][path[l][0]] = [1., 1., 1 - l / (L - 1)] # white-->yellow - gridpath[xI[1]][xI[0]] = [0., 0., 1.] # Initial node (plotted as blue) - gridpath[xG[1]][xG[0]] = [0., 1., 0.] # Goal node (plotted as green) - return gridpath - - -# Constructs path list from initial point and list of actions -def getPathFromActions(xI, actions): - L = len(actions) - path = [] - nextx = xI - for l in range(L): - u = actions[l] - if u != (-1, 0) and u != (1, 0) and u != (0, -1) and u != (0, 1): - print('getPath error: Invalid input u!') - return - nextx = [nextx[i] + u[i] for i in range(len(nextx))] # nextx = nextx + u - path.append(nextx) # Builds the path - return path - - -# If any collisions, cost is 999999, else cost is one for each action -def getCostOfActions(xI, actions, O): - L = len(actions) - costsum = 0 - nextx = xI - for l in range(L): - u = actions[l] - if u != (-1, 0) and u != (1, 0) and u != (0, -1) and u != (0, 1): - print('getCostOfActions error: Invalid input u!') - return - collision = collisionCheck(nextx, u, O) - if collision: return 999999999 - nextx = [nextx[i] + u[i] for i in range(len(nextx))] # nextx = nextx + u - costsum = costsum + 1 - return costsum - - -# If any collisions, cost is 999999, else cost is 2^(x[0]) for each action -def stayWestCost(xI, actions, O): - L = len(actions) - costsum = 0 - nextx = xI - for l in range(L): - u = actions[l] - if u != (-1, 0) and u != (1, 0) and u != (0, -1) and u != (0, 1): - print('stayWestCost error: Invalid input u!') - return - collision = collisionCheck(nextx, u, O) - if collision: return 999999999 - nextx = [nextx[i] + u[i] for i in range(len(nextx))] # nextx = nextx + u - costsum = costsum + nextx[0] ** 2 - return costsum - - -# If any collisions, cost is 999999, else cost is 2^(maxX - x[0]) for each action -def stayEastCost(xI, actions, O): - # Determine maximum x coordinate of workspace from obstacle list - maxX = 0 - for k in range(len(O)): - westxO = O[k][1] - if westxO > maxX: - maxX = westxO - - L = len(actions) - costsum = 0 - nextx = xI - for l in range(L): - u = actions[l] - if u != (-1, 0) and u != (1, 0) and u != (0, -1) and u != (0, 1): - print('stayEastCost error: Invalid input u!') - return - collision = collisionCheck(nextx, u, O) - if collision: return 999999999 - nextx = [nextx[i] + u[i] for i in range(len(nextx))] # nextx = nextx + u - costsum = costsum + (maxX - nextx[0]) ** 2 - return costsum - - -# Calculate different types of cost for searching algorithm -def cost_calculation(xI, actions, O): - simple_cost = getCostOfActions(xI, actions, O) - west_cost = stayWestCost(xI, actions, O) - east_cost = stayEastCost(xI, actions, O) - return simple_cost, west_cost, east_cost - - -# Extract path from results of searching algorithm -def extractpath(xI, xG, parent, actions): - pathback = [xG] - actionsback = [] - actionsback.append(actions[xG]) - x_current = xG - while parent[x_current] != x_current: - x_current = parent[x_current] - pathback.append(x_current) - actionsback.append(actions[x_current]) - path_extract = list(reversed(pathback)) - actions_extract = list(reversed(actionsback)) - path_extract.pop(0) - actions_extract.pop(0) - return path_extract, actions_extract - - -# Plots the path -def showPath(xI, xG, path, n, m, O, name): - gridpath = makePath(xI, xG, path, n, m, O) - fig, ax = plt.subplots(1, 1) # make a figure + axes - ax.imshow(gridpath) # Plot it - ax.invert_yaxis() # Needed so that bottom left is (0,0) - plt.title(name, fontdict=None) - plt.show() - - - diff --git a/Search-based Planning/searching.py b/Search-based Planning/searching.py deleted file mode 100644 index a7abb45..0000000 --- a/Search-based Planning/searching.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -@author: Huiming Zhou -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import colors - -from queue import * -from mazemods import * -from environment import * - -from bfs import * -from dijkstra import * -from astar import * - - -class Searching: - def __init__(self, Start_State, Goal_State, n, m, O): - self.xI = Start_State - self.xG = Goal_State - self.u_set = [(-1, 0), (1, 0), (0, 1), (0, -1)] - self.n = n - self.m = m - self.O = O - - def depth_fist_search(self): - q_dfs = QueueLIFO() - q_dfs.put(self.xI) - parent = {self.xI: self.xI} - actions = {self.xI: (0, 0)} - - while not q_dfs.empty(): - x_current = q_dfs.get() - if x_current == xG: - break - for u_next in self.u_set: - x_next = tuple([x_current[i] + u_next[i] for i in range(len(x_current))]) - if 0 <= x_next[0] < n and 0 <= x_next[1] < m \ - and x_next not in parent \ - and not collisionCheck(x_current, u_next, O): - q_dfs.put(x_next) - parent[x_next] = x_current - actions[x_next] = u_next - [path_dfs, actions_dfs] = extractpath(xI, xG, parent, actions) - [simple_cost, west_cost, east_cost] = cost_calculation(xI, actions_dfs, O) - return path_dfs, actions_dfs, len(parent), simple_cost, west_cost, east_cost - - -if __name__ == '__main__': - xI = (1, 1) - xG = (23, 12) - searching = Searching(xI, xG, n, m, O) - - [path_df, actions_df, num_visited_df, simple_cost_df, west_cost_df, east_cost_df] = searching.depth_fist_search() - print('1 - Depth_First_Searching algorithm: ') - print('Legal control actions: \n', actions_df) - print('Number of explored nodes was [%d], basic cost was [%d], stay west cost was [%d], stay east cost was [%d] \n' - % (num_visited_df, simple_cost_df, west_cost_df, east_cost_df)) - - showPath(xI, xG, path_df, n, m, O, 'Depth First Searching algorithm') \ No newline at end of file diff --git a/Search-based Planning/tools.py b/Search-based Planning/tools.py new file mode 100644 index 0000000..ff7ae20 --- /dev/null +++ b/Search-based Planning/tools.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +@author: huiming zhou +""" + +import matplotlib.pyplot as plt +import environment + + +def obs_detect(x, u, obs_map): + """ + Detect if the next state is in obstacles using this input. + + :param x: current state + :param u: input + :param obs_map: map of obstacles + :return: in obstacles: True / not in obstacles: False + """ + + x_next = [x[0] + u[0], x[1] + u[1]] # next state using input 'u' + if u not in environment.motions or \ + obs_map[x_next[0]][x_next[1]] == 1: # if 'u' is feasible and next state is not in obstacles + return True + return False + + +def extract_path(xI, xG, parent, actions): + """ + Extract the path based on the relationship of nodes. + + :param xI: Starting node + :param xG: Goal node + :param parent: Relationship between nodes + :param actions: Action needed for transfer between two nodes + :return: The planning path + """ + + path_back = [xG] + acts_back = [actions[xG]] + x_current = xG + while True: + x_current = parent[x_current] + path_back.append(x_current) + acts_back.append(actions[x_current]) + if x_current == xI: break + return list(reversed(path_back)), list(reversed(acts_back)) + + +def showPath(xI, xG, path, visited, name): + """ + Plot the path. + + :param xI: Starting node + :param xG: Goal node + :param path: Planning path + :param visited: Visited nodes + :param name: Name of this figure + :return: A plot + """ + + background = environment.obstacles() + fig, ax = plt.subplots() + for k in range(len(visited)): + background[visited[k][1]][visited[k][0]] = [.5, .5, .5] # visited nodes: gray color + for k in range(len(path)): + background[path[k][1]][path[k][0]] = [1., 0., 0.] # path: red color + background[xI[1]][xI[0]] = [0., 0., 1.] # starting node: blue color + background[xG[1]][xG[0]] = [0., 1., .5] # goal node: green color + ax.imshow(background) + ax.invert_yaxis() # put origin of coordinate to left-bottom + plt.title(name, fontdict=None) + plt.show() +