ARAstar.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. """
  2. ARA_star 2D (Anytime Repairing A*)
  3. @author: huiming zhou
  4. """
  5. import os
  6. import sys
  7. import math
  8. sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
  9. "/../../Search_based_Planning/")
  10. from Search_2D import plotting
  11. from Search_2D import env
  12. class AraStar:
  13. def __init__(self, s_start, s_goal, e, heuristic_type):
  14. self.s_start, self.s_goal = s_start, s_goal
  15. self.heuristic_type = heuristic_type
  16. self.Env = env.Env() # class Env
  17. self.u_set = self.Env.motions # feasible input set
  18. self.obs = self.Env.obs # position of obstacles
  19. self.e = e # initial weight
  20. self.g = {self.s_start: 0, self.s_goal: float("inf")} # Cost to come
  21. self.OPEN = {self.s_start: self.fvalue(self.s_start)} # priority queue / OPEN set
  22. self.CLOSED = set() # CLOSED set
  23. self.INCONS = {} # INCONS set
  24. self.PARENT = {self.s_start: self.s_start} # relations
  25. self.path = [] # planning path
  26. self.visited = [] # order of visited nodes
  27. def searching(self):
  28. self.ImprovePath()
  29. self.path.append(self.extract_path())
  30. while self.update_e() > 1: # continue condition
  31. self.e -= 0.5 # increase weight
  32. self.OPEN.update(self.INCONS)
  33. for s in self.OPEN:
  34. self.OPEN[s] = self.fvalue(s)
  35. self.INCONS = {}
  36. self.CLOSED = set()
  37. self.ImprovePath() # improve path
  38. self.path.append(self.extract_path())
  39. return self.path, self.visited
  40. def ImprovePath(self):
  41. """
  42. :return: a e'-suboptimal path
  43. """
  44. visited_each = []
  45. while True:
  46. s, f_small = self.get_smallest_f()
  47. if self.fvalue(self.s_goal) <= f_small:
  48. break
  49. self.CLOSED.add(s)
  50. for s_n in self.get_neighbor(s):
  51. new_cost = self.g[s] + self.cost(s, s_n)
  52. if s_n not in self.g or new_cost < self.g[s_n]:
  53. self.g[s_n] = new_cost
  54. self.PARENT[s_n] = s
  55. visited_each.append(s_n)
  56. if s_n not in self.CLOSED:
  57. self.OPEN[s_n] = self.fvalue(s_n)
  58. else:
  59. self.INCONS[s_n] = 0
  60. self.visited.append(visited_each)
  61. def get_smallest_f(self):
  62. """
  63. :return: node with smallest f_value in OPEN set.
  64. """
  65. s_list = {}
  66. for s in self.OPEN:
  67. s_list[s] = self.fvalue(s)
  68. s_small = min(s_list, key=s_list.get)
  69. self.OPEN.pop(s_small)
  70. return s_small, s_list[s_small]
  71. def get_neighbor(self, s):
  72. """
  73. find neighbors of state s that not in obstacles.
  74. :param s: state
  75. :return: neighbors
  76. """
  77. s_list = set()
  78. for u in self.u_set:
  79. s_next = tuple([s[i] + u[i] for i in range(2)])
  80. if s_next not in self.obs:
  81. s_list.add(s_next)
  82. return s_list
  83. def update_e(self):
  84. v = float("inf")
  85. if self.OPEN:
  86. v = min(self.g[s] + self.h(s) for s in self.OPEN)
  87. if self.INCONS:
  88. v = min(v, min(self.g[s] + self.h(s) for s in self.INCONS))
  89. return min(self.e, self.g[self.s_goal] / v)
  90. def fvalue(self, x):
  91. return self.g[x] + self.e * self.h(x)
  92. def extract_path(self):
  93. """
  94. Extract the path based on the PARENT set.
  95. :return: The planning path
  96. """
  97. path = [self.s_goal]
  98. s = self.s_goal
  99. while True:
  100. s = self.PARENT[s]
  101. path.append(s)
  102. if s == self.s_start:
  103. break
  104. return list(path)
  105. def h(self, s):
  106. """
  107. Calculate heuristic.
  108. :param s: current node (state)
  109. :return: heuristic function value
  110. """
  111. heuristic_type = self.heuristic_type # heuristic type
  112. goal = self.s_goal # goal node
  113. if heuristic_type == "manhattan":
  114. return abs(goal[0] - s[0]) + abs(goal[1] - s[1])
  115. else:
  116. return math.hypot(goal[0] - s[0], goal[1] - s[1])
  117. def cost(self, s_start, s_goal):
  118. """
  119. Calculate Cost for this motion
  120. :param s_start: starting node
  121. :param s_goal: end node
  122. :return: Cost for this motion
  123. :note: Cost function could be more complicate!
  124. """
  125. if self.is_collision(s_start, s_goal):
  126. return float("inf")
  127. return math.hypot(s_goal[0] - s_start[0], s_goal[1] - s_start[1])
  128. def is_collision(self, s_start, s_end):
  129. if s_start in self.obs or s_end in self.obs:
  130. return True
  131. if s_start[0] != s_end[0] and s_start[1] != s_end[1]:
  132. if s_end[0] - s_start[0] == s_start[1] - s_end[1]:
  133. s1 = (min(s_start[0], s_end[0]), min(s_start[1], s_end[1]))
  134. s2 = (max(s_start[0], s_end[0]), max(s_start[1], s_end[1]))
  135. else:
  136. s1 = (min(s_start[0], s_end[0]), max(s_start[1], s_end[1]))
  137. s2 = (max(s_start[0], s_end[0]), min(s_start[1], s_end[1]))
  138. if s1 in self.obs or s2 in self.obs:
  139. return True
  140. return False
  141. def main():
  142. s_start = (5, 5)
  143. s_goal = (45, 25)
  144. arastar = AraStar(s_start, s_goal, 2.5, "euclidean")
  145. plot = plotting.Plotting(s_start, s_goal)
  146. path, visited = arastar.searching()
  147. plot.animation_ara_star(path, visited, "Anytime Repairing A* (ARA*)")
  148. if __name__ == '__main__':
  149. main()