planner.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. import os
  2. import sys
  3. import math
  4. import heapq
  5. from heapdict import heapdict
  6. import time
  7. import numpy as np
  8. import matplotlib.pyplot as plt
  9. import scipy.spatial.kdtree as kd
  10. from planner.hybridastar import astar as astar
  11. from planner.hybridastar import planer_reeds_shepp as rs
  12. from input import make_car
  13. sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
  14. "/../../Onsite_Parking/")
  15. C = make_car.C
  16. class Node:
  17. def __init__(self, xind, yind, yawind, direction, x, y,
  18. yaw, directions, steer, cost, pind):
  19. self.xind = xind
  20. self.yind = yind
  21. self.yawind = yawind
  22. self.direction = direction
  23. self.x = x
  24. self.y = y
  25. self.yaw = yaw
  26. self.directions = directions
  27. self.steer = steer
  28. self.cost = cost
  29. self.pind = pind
  30. class Para:
  31. def __init__(self, minx, miny, minyaw, maxx, maxy, maxyaw,
  32. xw, yw, yaww, xyreso, yawreso, ox, oy, kdtree):
  33. self.minx = minx
  34. self.miny = miny
  35. self.minyaw = minyaw
  36. self.maxx = maxx
  37. self.maxy = maxy
  38. self.maxyaw = maxyaw
  39. self.xw = xw
  40. self.yw = yw
  41. self.yaww = yaww
  42. self.xyreso = xyreso
  43. self.yawreso = yawreso
  44. self.ox = ox
  45. self.oy = oy
  46. self.kdtree = kdtree
  47. class QueuePrior:
  48. def __init__(self):
  49. self.queue = heapdict()
  50. def empty(self):
  51. return len(self.queue) == 0 # if Q is empty
  52. def put(self, item, priority):
  53. self.queue[item] = priority # push
  54. def get(self):
  55. return self.queue.popitem()[0] # pop out element with smallest priority
  56. class Path:
  57. def __init__(self, x, y, yaw, direction, cost):
  58. self.x = x
  59. self.y = y
  60. self.yaw = yaw
  61. self.direction = direction
  62. self.cost = cost
  63. def calc_parameters(ox, oy, xyreso, yawreso, kdtree):
  64. minx = round(min(ox) / xyreso)
  65. miny = round(min(oy) / xyreso)
  66. maxx = round(max(ox) / xyreso)
  67. maxy = round(max(oy) / xyreso)
  68. xw, yw = maxx - minx, maxy - miny
  69. minyaw = round(-C.PI / yawreso) - 1
  70. maxyaw = round(C.PI / yawreso)
  71. yaww = maxyaw - minyaw
  72. return Para(minx, miny, minyaw, maxx, maxy, maxyaw,
  73. xw, yw, yaww, xyreso, yawreso, ox, oy, kdtree)
  74. def calc_motion_set():
  75. s = np.arange(C.MAX_STEER / C.N_STEER,
  76. C.MAX_STEER, C.MAX_STEER / C.N_STEER)
  77. steer = list(s) + [0.0] + list(-s)
  78. direc = [1.0 for _ in range(len(steer))] + [-1.0 for _ in range(len(steer))]
  79. steer = steer + steer
  80. return steer, direc
  81. def calc_index(node, P):
  82. ind = (node.yawind - P.minyaw) * P.xw * P.yw + \
  83. (node.yind - P.miny) * P.xw + \
  84. (node.xind - P.minx)
  85. return ind
  86. def calc_hybrid_cost(node, hmap, P):
  87. cost = node.cost + \
  88. C.H_COST * hmap[node.xind - P.minx][node.yind - P.miny]
  89. return cost
  90. def update_node_with_analystic_expantion(n_curr, ngoal, P):
  91. path = analystic_expantion(n_curr, ngoal, P) # rs path: n -> ngoal
  92. if not path:
  93. return False, None
  94. fx = path.x[1:-1]
  95. fy = path.y[1:-1]
  96. fyaw = path.yaw[1:-1]
  97. fd = path.directions[1:-1]
  98. fcost = n_curr.cost + calc_rs_path_cost(path)
  99. fpind = calc_index(n_curr, P)
  100. fsteer = 0.0
  101. fpath = Node(n_curr.xind, n_curr.yind, n_curr.yawind, n_curr.direction,
  102. fx, fy, fyaw, fd, fsteer, fcost, fpind)
  103. return True, fpath
  104. def calc_rs_path_cost(rspath):
  105. cost = 0.0
  106. for lr in rspath.lengths:
  107. if lr >= 0:
  108. cost += 1
  109. else:
  110. cost += abs(lr) * C.BACKWARD_COST
  111. for i in range(len(rspath.lengths) - 1):
  112. if rspath.lengths[i] * rspath.lengths[i + 1] < 0.0:
  113. cost += C.GEAR_COST
  114. for ctype in rspath.ctypes:
  115. if ctype != "S":
  116. cost += C.STEER_ANGLE_COST * abs(C.MAX_STEER)
  117. nctypes = len(rspath.ctypes)
  118. ulist = [0.0 for _ in range(nctypes)]
  119. for i in range(nctypes):
  120. if rspath.ctypes[i] == "R":
  121. ulist[i] = -C.MAX_STEER
  122. elif rspath.ctypes[i] == "WB":
  123. ulist[i] = C.MAX_STEER
  124. for i in range(nctypes - 1):
  125. cost += C.STEER_CHANGE_COST * abs(ulist[i + 1] - ulist[i])
  126. return cost
  127. def calc_motion_set():
  128. s = np.arange(C.MAX_STEER / C.N_STEER,
  129. C.MAX_STEER, C.MAX_STEER / C.N_STEER)
  130. steer = list(s) + [0.0] + list(-s)
  131. direc = [1.0 for _ in range(len(steer))] + [-1.0 for _ in range(len(steer))]
  132. steer = steer + steer
  133. return steer, direc
  134. def is_same_grid(node1, node2):
  135. if node1.xind != node2.xind or \
  136. node1.yind != node2.yind or \
  137. node1.yawind != node2.yawind:
  138. return False
  139. return True
  140. def calc_index(node, P):
  141. ind = (node.yawind - P.minyaw) * P.xw * P.yw + \
  142. (node.yind - P.miny) * P.xw + \
  143. (node.xind - P.minx)
  144. return ind
  145. def is_collision(x, y, yaw, P):
  146. for ix, iy, iyaw in zip(x, y, yaw):
  147. d = 20
  148. dl = (C.RF - C.RB) / 2.0
  149. r = (C.RF + C.RB) / 2.0 + d
  150. cx = ix + dl * math.cos(iyaw)
  151. cy = iy + dl * math.sin(iyaw)
  152. ids = P.kdtree.query_ball_point([cx, cy], r)
  153. if not ids:
  154. continue
  155. for i in ids:
  156. xo = P.ox[i] - cx
  157. yo = P.oy[i] - cy
  158. dx = xo * math.cos(iyaw) + yo * math.sin(iyaw)
  159. dy = -xo * math.sin(iyaw) + yo * math.cos(iyaw)
  160. if abs(dx) < r and abs(dy) < C.W / 2 + d:
  161. return True
  162. return False
  163. def calc_parameters(ox, oy, xyreso, yawreso, kdtree):
  164. minx = round(min(ox) / xyreso)
  165. miny = round(min(oy) / xyreso)
  166. maxx = round(max(ox) / xyreso)
  167. maxy = round(max(oy) / xyreso)
  168. xw, yw = maxx - minx, maxy - miny
  169. minyaw = round(-C.PI / yawreso) - 1
  170. maxyaw = round(C.PI / yawreso)
  171. yaww = maxyaw - minyaw
  172. return Para(minx, miny, minyaw, maxx, maxy, maxyaw,
  173. xw, yw, yaww, xyreso, yawreso, ox, oy, kdtree)
  174. def analystic_expantion(node, ngoal, P):
  175. sx, sy, syaw = node.x[-1], node.y[-1], node.yaw[-1]
  176. gx, gy, gyaw = ngoal.x[-1], ngoal.y[-1], ngoal.yaw[-1]
  177. maxc = math.tan(C.MAX_STEER) / C.WB
  178. paths = rs.calc_all_paths(sx, sy, syaw, gx, gy, gyaw, maxc, step_size=C.MOVE_STEP)
  179. if not paths:
  180. return None
  181. pq = QueuePrior()
  182. for path in paths:
  183. pq.put(path, calc_rs_path_cost(path))
  184. while not pq.empty():
  185. path = pq.get()
  186. ind = range(0, len(path.x), C.COLLISION_CHECK_STEP)
  187. pathx = [path.x[k] for k in ind]
  188. pathy = [path.y[k] for k in ind]
  189. pathyaw = [path.yaw[k] for k in ind]
  190. if not is_collision(pathx, pathy, pathyaw, P):
  191. return path
  192. return None
  193. def is_index_ok(xind, yind, xlist, ylist, yawlist, P):
  194. if xind <= P.minx or \
  195. xind >= P.maxx or \
  196. yind <= P.miny or \
  197. yind >= P.maxy:
  198. return False
  199. ind = range(0, len(xlist), C.COLLISION_CHECK_STEP)
  200. nodex = [xlist[k] for k in ind]
  201. nodey = [ylist[k] for k in ind]
  202. nodeyaw = [yawlist[k] for k in ind]
  203. if is_collision(nodex, nodey, nodeyaw, P):
  204. return False
  205. return True
  206. def extract_path(closed, ngoal, nstart):
  207. rx, ry, ryaw, direc = [], [], [], []
  208. cost = 0.0
  209. node = ngoal
  210. while True:
  211. rx += node.x[::-1]
  212. ry += node.y[::-1]
  213. ryaw += node.yaw[::-1]
  214. direc += node.directions[::-1]
  215. cost += node.cost
  216. if is_same_grid(node, nstart):
  217. break
  218. node = closed[node.pind]
  219. rx = rx[::-1]
  220. ry = ry[::-1]
  221. ryaw = ryaw[::-1]
  222. direc = direc[::-1]
  223. direc[0] = direc[1]
  224. path = Path(rx, ry, ryaw, direc, cost)
  225. return path
  226. def calc_next_node(n_curr, c_id, u, d, P):
  227. step = C.XY_RESO * 2
  228. nlist = math.ceil(step / C.MOVE_STEP)
  229. xlist = [n_curr.x[-1] + d * C.MOVE_STEP * math.cos(n_curr.yaw[-1])]
  230. ylist = [n_curr.y[-1] + d * C.MOVE_STEP * math.sin(n_curr.yaw[-1])]
  231. yawlist = [rs.pi_2_pi(n_curr.yaw[-1] + d * C.MOVE_STEP / C.WB * math.tan(u))]
  232. for i in range(nlist - 1):
  233. xlist.append(xlist[i] + d * C.MOVE_STEP * math.cos(yawlist[i]))
  234. ylist.append(ylist[i] + d * C.MOVE_STEP * math.sin(yawlist[i]))
  235. yawlist.append(rs.pi_2_pi(yawlist[i] + d * C.MOVE_STEP / C.WB * math.tan(u)))
  236. xind = round(xlist[-1] / P.xyreso)
  237. yind = round(ylist[-1] / P.xyreso)
  238. yawind = round(yawlist[-1] / P.yawreso)
  239. if not is_index_ok(xind, yind, xlist, ylist, yawlist, P):
  240. return None
  241. cost = 0.0
  242. if d > 0:
  243. direction = 1
  244. cost += abs(step)
  245. else:
  246. direction = -1
  247. cost += abs(step) * C.BACKWARD_COST
  248. if direction != n_curr.direction: # switch back penalty
  249. cost += C.GEAR_COST
  250. cost += C.STEER_ANGLE_COST * abs(u) # steer angle penalyty
  251. cost += C.STEER_CHANGE_COST * abs(n_curr.steer - u) # steer change penalty
  252. cost = n_curr.cost + cost
  253. directions = [direction for _ in range(len(xlist))]
  254. node = Node(xind, yind, yawind, direction, xlist, ylist,
  255. yawlist, directions, u, cost, c_id)
  256. return node
  257. def hybrid_astar_planning(sx, sy, syaw, gx, gy, gyaw, ox, oy, xyreso, yawreso):
  258. sxr, syr = round(sx / xyreso), round(sy / xyreso)
  259. gxr, gyr = round(gx / xyreso), round(gy / xyreso)
  260. syawr = round(rs.pi_2_pi(syaw) / yawreso)
  261. gyawr = round(rs.pi_2_pi(gyaw) / yawreso)
  262. nstart = Node(sxr, syr, syawr, 1, [sx], [sy], [syaw], [1], 0.0, 0.0, -1)
  263. ngoal = Node(gxr, gyr, gyawr, 1, [gx], [gy], [gyaw], [1], 0.0, 0.0, -1)
  264. kdtree = kd.KDTree([[x, y] for x, y in zip(ox, oy)])
  265. P = calc_parameters(ox, oy, xyreso, yawreso, kdtree)
  266. hmap = astar.calc_holonomic_heuristic_with_obstacle(ngoal, P.ox, P.oy, P.xyreso, 1.0)
  267. steer_set, direc_set = calc_motion_set()
  268. open_set, closed_set = {calc_index(nstart, P): nstart}, {}
  269. qp = QueuePrior()
  270. qp.put(calc_index(nstart, P), calc_hybrid_cost(nstart, hmap, P))
  271. while True:
  272. if not open_set:
  273. return None
  274. ind = qp.get()
  275. n_curr = open_set[ind]
  276. closed_set[ind] = n_curr
  277. open_set.pop(ind)
  278. update, fpath = update_node_with_analystic_expantion(n_curr, ngoal, P)
  279. if update:
  280. fnode = fpath
  281. break
  282. for i in range(len(steer_set)):
  283. node = calc_next_node(n_curr, ind, steer_set[i], direc_set[i], P)
  284. if not node:
  285. continue
  286. node_ind = calc_index(node, P)
  287. if node_ind in closed_set:
  288. continue
  289. if node_ind not in open_set:
  290. open_set[node_ind] = node
  291. qp.put(node_ind, calc_hybrid_cost(node, hmap, P))
  292. else:
  293. if open_set[node_ind].cost > node.cost:
  294. open_set[node_ind] = node
  295. qp.put(node_ind, calc_hybrid_cost(node, hmap, P))
  296. return extract_path(closed_set, fnode, nstart)