当前位置:   article > 正文

自动驾驶算法(三):RRT算法讲解与代码实现(基于采样的路径规划)

rrt算法

目录

1 RRT算法原理

2 RRT算法代码解析

3 RRT完整代码


1 RRT算法原理

        RRT算法的全称是快速扩展随机树算法(Rapidly Exploring Random Tree),它的想法就是从根结点长出一棵树当树枝长到终点的时候这样就能找到从终点到根节点的唯一路径。

        算法流程:

        首先进行初始化设置起点和终点的设置,进入循环,进行随机采样在空间中随机选择一个点Xrand,寻找距离Xrand最近的节点(如果是第一次那就是初始节点),随后我们进行树的生长,将Xnear--Xrand连接起来作为树生长的方向,设置步长作为树枝的长度,就会产生Xnew节点,如下图:

        将树枝节点和根结点加在一起作为新节点。

        我们继续来采样:这次采样也是Xrand

        它最近的节点是Xnear节点,生长的时候会发现它会穿越障碍物。抛弃这次的生长。

        在不停的生长过后,Xnew越来越接近终点。我们每次产生Xnew'都会与终点进行连线,看他们的距离是否比步长小并且没有障碍物,如果true,我们连接Xnew和终点就找到了起点到终点的路径。

        我们执行代码:

        红色的就是我们找到的路径。好像挺糟糕的.....

2 RRT算法代码解析

        我们先看算法的节点类:

  1. class Node:
  2. def __init__(self, x, y):
  3. self.x = x
  4. self.y = y
  5. self.cost = 0.0
  6. self.parent = None

        和Dijkstra和A*算法是一样的,不过这里的x、y是真实坐标系的坐标而不是珊格地图的坐标,节点代价为距离,如果连接了下一个节点这个代价也是进行相加的,parent存储了父亲节点,方便于找到终点后计算路径。

        我们再看准备工作:

  1. obstacleList = [(5, 5, 1), (3, 6, 2), (3, 8, 2), (3, 10, 2), (7, 5, 2),
  2. (9, 5, 2), (8, 10, 1)]
  3. # Set params
  4. # 采样范围 设置的障碍物 最大迭代次数
  5. rrt = RRT(randArea=[-2, 18], obstacleList=obstacleList, maxIter=200)
  6. # 传入的是起点和终点
  7. path = rrt.rrt_planning(start=[0, 0], goal=[15, 12], animation=show_animation)

        我们设计了一系列的圆柱障碍物,并向RRT类中传参,圆柱障碍物是中心坐标+半径的形式,如下图:

        由上面的算法介绍可知,我们传入RRT算法的参数为障碍物、随机距离(-2-18)、最大迭代次数(如果超过200次我就不找路径了),我们看RRT类,首先看构造函数:

  1. def __init__(self, obstacleList, randArea,
  2. expandDis=2.0, goalSampleRate=10, maxIter=200):
  3. self.start = None
  4. self.goal = None
  5. self.min_rand = randArea[0]
  6. self.max_rand = randArea[1]
  7. self.expand_dis = expandDis
  8. self.goal_sample_rate = goalSampleRate
  9. self.max_iter = maxIter
  10. self.obstacle_list = obstacleList
  11. # 存储RRT树
  12. self.node_list = None

        将起始点、结束点置为null,最小随机点和最大随机取样点设置为-2与18,单次前进距离(X_near --> X_rand)为2,直接取终点为最终点的采样概率为10%,最大迭代次数为200,障碍物列表也传进来了。RRT树为none。

        我们看导航参数:

path = rrt.rrt_planning(start=[0, 0], goal=[15, 12], animation=show_animation)

        就是输入了起点和终点的坐标。

        我们进入函数:

  1. def rrt_planning(self, start, goal, animation=True):
  2. start_time = time.time()
  3. self.start = Node(start[0], start[1])
  4. self.goal = Node(goal[0], goal[1])
  5. # 将起点加入node_list作为树的根结点
  6. self.node_list = [self.start]
  7. path = None

        记录了起始时间计算时间,我们将起始点和终止点封装成node,因为我们没有指定代价cost和parent,因此代价由类默认设置为0,父亲节点设置为-1。将起点加入到node_list中为根结点。初始化路径。       

        我们进行采样:
 

  1. for i in range(self.max_iter):
  2. # 进行采样
  3. rnd = self.sample()
  4. # 取的距离采样点最近的节点下标
  5. n_ind = self.get_nearest_list_index(self.node_list, rnd)
  6. # 得到最近节点
  7. nearestNode = self.node_list[n_ind]
  8. # 将Xrandom和Xnear连线方向作为生长方向
  9. # math.atan2() 函数接受两个参数,分别是 y 坐标差值和 x 坐标差值。它返回的值是以弧度表示的角度,范围在 -π 到 π 之间。这个角度表示了从 nearestNode 指向 rnd 的方向。
  10. theta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)
  11. # 生长 : 输入参数为角度、下标、nodelist中最近的节点 得到生长过后的节点
  12. newNode = self.get_new_node(theta, n_ind, nearestNode)
  13. # 检查是否有障碍物 传入参数为新生城路径的两个节点
  14. noCollision = self.check_segment_collision(newNode.x, newNode.y, nearestNode.x, nearestNode.y)

        先看sample函数:

  1. def sample(self):
  2. # 取得1-100的随机数,如果比10大的话(以10%的概率取到终点)
  3. if random.randint(0, 100) > self.goal_sample_rate:
  4. # 在空间里随机采样一个点
  5. rnd = [random.uniform(self.min_rand, self.max_rand), random.uniform(self.min_rand, self.max_rand)]
  6. else: # goal point sampling
  7. # 终点作为采样点
  8. rnd = [self.goal.x, self.goal.y]
  9. return rnd

        先取得了一个1-100的随机数,如果大于self.goal_sample_rate(传进来的参数=10),我们就启动if语句:

        它使用了 Python 中的 random.uniform() 函数来生成两个在指定范围内的随机数,并将它们放入列表 rnd 中。(-2  --> 18)

  • random.uniform(a, b) 函数会返回一个在 ab 之间的随机浮点数。在这里,self.min_randself.max_rand 可能是两个指定的最小值和最大值。

        所以,rnd 是一个包含两个随机数的列表,这两个随机数分别位于 self.min_rand =2和 self.max_rand  = 18之间。这个坐标作为我们随机采样的点。

        如果是else语句的话我们直接将终点作为采样点。

        因此rnd获得了如下图的红圈所示的Xrand坐标。

        在来看get_nearest_list_index函数:

  1. def get_nearest_list_index(nodes, rnd):
  2. # 遍历所有节点 计算采样点和节点的距离
  3. dList = [(node.x - rnd[0]) ** 2
  4. + (node.y - rnd[1]) ** 2 for node in nodes]
  5. # 获得最近的距离所对应的索引
  6. minIndex = dList.index(min(dList))
  7. return minIndex

        第一行代码创建了一个列表 dList,其中包含了所有节点与指定点 rnd 之间的欧几里得距离的平方。

        具体来说,它使用了列表推导式(list comprehension)的语法,对于 nodes 中的每一个节点 node,计算了以下值:

(node.x - rnd[0]) ** 2 + (node.y - rnd[1]) ** 2

        最终,dList 中包含了所有节点与 rnd 之间距离的平方值。

        minIndex = dList.index(min(dList)) 获得了最近的距离所对应的索引。

        因此,nearestNode = self.node_list[n_ind]这个代码就得到了距离采样点Xrand最近的节点Xnear,如下图所示:

         theta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)                    

        这行代码求了一个角度,采样点Xrand与Xnear的角度。

        我们来看get_new_node函数:

  1. def get_new_node(self, theta, n_ind, nearestNode):
  2. newNode = copy.deepcopy(nearestNode)
  3. # 坐标
  4. newNode.x += self.expand_dis * math.cos(theta)
  5. newNode.y += self.expand_dis * math.sin(theta)
  6. # 代价
  7. newNode.cost += self.expand_dis
  8. # 父亲节点
  9. newNode.parent = n_ind
  10. return newNode

        我们先把随机采样节点Xrand的最近节点Xnear做了深拷贝,利用三角函数计算出新的节点的坐标(1.我们传进来的参数expand_dis意为每一次的导航步长 2.expand_dis * costheta就是x的增量,y同理),因此,我们实例化了一个新节点,它的代价就是它邻近节点的代价 + expand_dis(2),它的父亲节点为这个邻近节点Xnear保证了递归的顺利进行。

        因此,

newNode = self.get_new_node(theta, n_ind, nearestNode)

       这行代码我们利用expand_dis和邻近节点Xnear新生成了一个节点。

        在来看check_segment_collision函数:

  1. def check_segment_collision(self, x1, y1, x2, y2):
  2. # 遍历所有的障碍物
  3. for (ox, oy, size) in self.obstacle_list:
  4. dd = self.distance_squared_point_to_segment(
  5. np.array([x1, y1]),
  6. np.array([x2, y2]),
  7. np.array([ox, oy]))
  8. if dd <= size ** 2:
  9. return False # collision
  10. return True

        这个就是具体问题具体分析了,判断两条线间是否有障碍物,即Xnear和Xnew间:

        随后就是最后一部分代码啦~加加油:

  1. if noCollision:
  2. # 没有碰撞把新节点加入到树里面
  3. self.node_list.append(newNode)
  4. if animation:
  5. self.draw_graph(newNode, path)
  6. # 是否到终点附近
  7. if self.is_near_goal(newNode):
  8. # 是否这条路径与障碍物发生碰撞
  9. if self.check_segment_collision(newNode.x, newNode.y,
  10. self.goal.x, self.goal.y):
  11. lastIndex = len(self.node_list) - 1
  12. # 找路径
  13. path = self.get_final_course(lastIndex)
  14. pathLen = self.get_path_len(path)
  15. print("current path length: {}, It costs {} s".format(pathLen, time.time()-start_time))
  16. if animation:
  17. self.draw_graph(newNode, path)
  18. return path

        如果没有碰撞的话,我们认为这个Xnew是靠谱的,将它加入到节点队列中。再判断它是否在终点附近,我们来看is_near_goal函数:

  1. def is_near_goal(self, node):
  2. # 计算距离
  3. d = self.line_cost(node, self.goal)
  4. if d < self.expand_dis:
  5. return True
  6. return False

        这里就是计算我们新加的节点到终点的距离是否小于一次的步长2,如果小于的话就return true。

        若在终点附近,我们将这个节点和终点相连看是否中间有障碍物存在:

  1. self.check_segment_collision(newNode.x, newNode.y,
  2. self.goal.x, self.goal.y)

        如果没有障碍物的话,我们导航成功,开始输出路径:

  1. def get_final_course(self, lastIndex):
  2. path = [[self.goal.x, self.goal.y]]
  3. while self.node_list[lastIndex].parent is not None:
  4. node = self.node_list[lastIndex]
  5. path.append([node.x, node.y])
  6. lastIndex = node.parent
  7. path.append([self.start.x, self.start.y])
  8. return path

        我们一个一个找终止节点的父节点就可以了。

        通关~。

3 RRT完整代码

  1. import copy
  2. import math
  3. import random
  4. import time
  5. import matplotlib.pyplot as plt
  6. from scipy.spatial.transform import Rotation as Rot
  7. import numpy as np
  8. show_animation = True
  9. class RRT:
  10. # randArea采样范围[-2--18] obstacleList设置的障碍物 maxIter最大迭代次数 expandDis采样步长为2.0 goalSampleRate 以10%的概率将终点作为采样点
  11. def __init__(self, obstacleList, randArea,
  12. expandDis=2.0, goalSampleRate=10, maxIter=200):
  13. self.start = None
  14. self.goal = None
  15. self.min_rand = randArea[0]
  16. self.max_rand = randArea[1]
  17. self.expand_dis = expandDis
  18. self.goal_sample_rate = goalSampleRate
  19. self.max_iter = maxIter
  20. self.obstacle_list = obstacleList
  21. # 存储RRT树
  22. self.node_list = None
  23. # start、goal 起点终点坐标
  24. def rrt_planning(self, start, goal, animation=True):
  25. start_time = time.time()
  26. self.start = Node(start[0], start[1])
  27. self.goal = Node(goal[0], goal[1])
  28. # 将起点加入node_list作为树的根结点
  29. self.node_list = [self.start]
  30. path = None
  31. for i in range(self.max_iter):
  32. # 进行采样
  33. rnd = self.sample()
  34. # 取的距离采样点最近的节点下标
  35. n_ind = self.get_nearest_list_index(self.node_list, rnd)
  36. # 得到最近节点
  37. nearestNode = self.node_list[n_ind]
  38. # 将Xrandom和Xnear连线方向作为生长方向
  39. # math.atan2() 函数接受两个参数,分别是 y 坐标差值和 x 坐标差值。它返回的值是以弧度表示的角度,范围在 -π 到 π 之间。这个角度表示了从 nearestNode 指向 rnd 的方向。
  40. theta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)
  41. # 生长 : 输入参数为角度、下标、nodelist中最近的节点 得到生长过后的节点
  42. newNode = self.get_new_node(theta, n_ind, nearestNode)
  43. # 检查是否有障碍物 传入参数为新生城路径的两个节点
  44. noCollision = self.check_segment_collision(newNode.x, newNode.y, nearestNode.x, nearestNode.y)
  45. if noCollision:
  46. # 没有碰撞把新节点加入到树里面
  47. self.node_list.append(newNode)
  48. if animation:
  49. self.draw_graph(newNode, path)
  50. # 是否到终点附近
  51. if self.is_near_goal(newNode):
  52. # 是否这条路径与障碍物发生碰撞
  53. if self.check_segment_collision(newNode.x, newNode.y,
  54. self.goal.x, self.goal.y):
  55. lastIndex = len(self.node_list) - 1
  56. # 找路径
  57. path = self.get_final_course(lastIndex)
  58. pathLen = self.get_path_len(path)
  59. print("current path length: {}, It costs {} s".format(pathLen, time.time()-start_time))
  60. if animation:
  61. self.draw_graph(newNode, path)
  62. return path
  63. def rrt_star_planning(self, start, goal, animation=True):
  64. start_time = time.time()
  65. self.start = Node(start[0], start[1])
  66. self.goal = Node(goal[0], goal[1])
  67. self.node_list = [self.start]
  68. path = None
  69. lastPathLength = float('inf')
  70. for i in range(self.max_iter):
  71. rnd = self.sample()
  72. n_ind = self.get_nearest_list_index(self.node_list, rnd)
  73. nearestNode = self.node_list[n_ind]
  74. # steer
  75. theta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)
  76. newNode = self.get_new_node(theta, n_ind, nearestNode)
  77. noCollision = self.check_segment_collision(newNode.x, newNode.y, nearestNode.x, nearestNode.y)
  78. if noCollision:
  79. nearInds = self.find_near_nodes(newNode)
  80. newNode = self.choose_parent(newNode, nearInds)
  81. self.node_list.append(newNode)
  82. self.rewire(newNode, nearInds)
  83. if animation:
  84. self.draw_graph(newNode, path)
  85. if self.is_near_goal(newNode):
  86. if self.check_segment_collision(newNode.x, newNode.y,
  87. self.goal.x, self.goal.y):
  88. lastIndex = len(self.node_list) - 1
  89. tempPath = self.get_final_course(lastIndex)
  90. tempPathLen = self.get_path_len(tempPath)
  91. if lastPathLength > tempPathLen:
  92. path = tempPath
  93. lastPathLength = tempPathLen
  94. print("current path length: {}, It costs {} s".format(tempPathLen, time.time()-start_time))
  95. return path
  96. def informed_rrt_star_planning(self, start, goal, animation=True):
  97. start_time = time.time()
  98. self.start = Node(start[0], start[1])
  99. self.goal = Node(goal[0], goal[1])
  100. self.node_list = [self.start]
  101. # max length we expect to find in our 'informed' sample space,
  102. # starts as infinite
  103. cBest = float('inf')
  104. path = None
  105. # Computing the sampling space
  106. cMin = math.sqrt(pow(self.start.x - self.goal.x, 2)
  107. + pow(self.start.y - self.goal.y, 2))
  108. xCenter = np.array([[(self.start.x + self.goal.x) / 2.0],
  109. [(self.start.y + self.goal.y) / 2.0], [0]])
  110. a1 = np.array([[(self.goal.x - self.start.x) / cMin],
  111. [(self.goal.y - self.start.y) / cMin], [0]])
  112. e_theta = math.atan2(a1[1], a1[0])
  113. # 论文方法求旋转矩阵(2选1)
  114. # first column of identity matrix transposed
  115. # id1_t = np.array([1.0, 0.0, 0.0]).reshape(1, 3)
  116. # M = a1 @ id1_t
  117. # U, S, Vh = np.linalg.svd(M, True, True)
  118. # C = np.dot(np.dot(U, np.diag(
  119. # [1.0, 1.0, np.linalg.det(U) * np.linalg.det(np.transpose(Vh))])),
  120. # Vh)
  121. # 直接用二维平面上的公式(2选1)
  122. C = np.array([[math.cos(e_theta), -math.sin(e_theta), 0],
  123. [math.sin(e_theta), math.cos(e_theta), 0],
  124. [0, 0, 1]])
  125. for i in range(self.max_iter):
  126. # Sample space is defined by cBest
  127. # cMin is the minimum distance between the start point and the goal
  128. # xCenter is the midpoint between the start and the goal
  129. # cBest changes when a new path is found
  130. rnd = self.informed_sample(cBest, cMin, xCenter, C)
  131. n_ind = self.get_nearest_list_index(self.node_list, rnd)
  132. nearestNode = self.node_list[n_ind]
  133. # steer
  134. theta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)
  135. newNode = self.get_new_node(theta, n_ind, nearestNode)
  136. noCollision = self.check_segment_collision(newNode.x, newNode.y, nearestNode.x, nearestNode.y)
  137. if noCollision:
  138. nearInds = self.find_near_nodes(newNode)
  139. newNode = self.choose_parent(newNode, nearInds)
  140. self.node_list.append(newNode)
  141. self.rewire(newNode, nearInds)
  142. if self.is_near_goal(newNode):
  143. if self.check_segment_collision(newNode.x, newNode.y,
  144. self.goal.x, self.goal.y):
  145. lastIndex = len(self.node_list) - 1
  146. tempPath = self.get_final_course(lastIndex)
  147. tempPathLen = self.get_path_len(tempPath)
  148. if tempPathLen < cBest:
  149. path = tempPath
  150. cBest = tempPathLen
  151. print("current path length: {}, It costs {} s".format(tempPathLen, time.time()-start_time))
  152. if animation:
  153. self.draw_graph_informed_RRTStar(xCenter=xCenter,
  154. cBest=cBest, cMin=cMin,
  155. e_theta=e_theta, rnd=rnd, path=path)
  156. return path
  157. def sample(self):
  158. # 取得1-100的随机数,如果比10大的话(以10%的概率取到终点)
  159. if random.randint(0, 100) > self.goal_sample_rate:
  160. # 在空间里随机采样一个点
  161. rnd = [random.uniform(self.min_rand, self.max_rand), random.uniform(self.min_rand, self.max_rand)]
  162. else: # goal point sampling
  163. # 终点作为采样点
  164. rnd = [self.goal.x, self.goal.y]
  165. return rnd
  166. def choose_parent(self, newNode, nearInds):
  167. if len(nearInds) == 0:
  168. return newNode
  169. dList = []
  170. for i in nearInds:
  171. dx = newNode.x - self.node_list[i].x
  172. dy = newNode.y - self.node_list[i].y
  173. d = math.hypot(dx, dy)
  174. theta = math.atan2(dy, dx)
  175. if self.check_collision(self.node_list[i], theta, d):
  176. dList.append(self.node_list[i].cost + d)
  177. else:
  178. dList.append(float('inf'))
  179. minCost = min(dList)
  180. minInd = nearInds[dList.index(minCost)]
  181. if minCost == float('inf'):
  182. print("min cost is inf")
  183. return newNode
  184. newNode.cost = minCost
  185. newNode.parent = minInd
  186. return newNode
  187. def find_near_nodes(self, newNode):
  188. n_node = len(self.node_list)
  189. r = 50.0 * math.sqrt((math.log(n_node) / n_node))
  190. d_list = [(node.x - newNode.x) ** 2 + (node.y - newNode.y) ** 2
  191. for node in self.node_list]
  192. near_inds = [d_list.index(i) for i in d_list if i <= r ** 2]
  193. return near_inds
  194. def informed_sample(self, cMax, cMin, xCenter, C):
  195. if cMax < float('inf'):
  196. r = [cMax / 2.0,
  197. math.sqrt(cMax ** 2 - cMin ** 2) / 2.0,
  198. math.sqrt(cMax ** 2 - cMin ** 2) / 2.0]
  199. L = np.diag(r)
  200. xBall = self.sample_unit_ball()
  201. rnd = np.dot(np.dot(C, L), xBall) + xCenter
  202. rnd = [rnd[(0, 0)], rnd[(1, 0)]]
  203. else:
  204. rnd = self.sample()
  205. return rnd
  206. @staticmethod
  207. def sample_unit_ball():
  208. a = random.random()
  209. b = random.random()
  210. if b < a:
  211. a, b = b, a
  212. sample = (b * math.cos(2 * math.pi * a / b),
  213. b * math.sin(2 * math.pi * a / b))
  214. return np.array([[sample[0]], [sample[1]], [0]])
  215. @staticmethod
  216. def get_path_len(path):
  217. pathLen = 0
  218. for i in range(1, len(path)):
  219. node1_x = path[i][0]
  220. node1_y = path[i][1]
  221. node2_x = path[i - 1][0]
  222. node2_y = path[i - 1][1]
  223. pathLen += math.sqrt((node1_x - node2_x)
  224. ** 2 + (node1_y - node2_y) ** 2)
  225. return pathLen
  226. @staticmethod
  227. def line_cost(node1, node2):
  228. return math.sqrt((node1.x - node2.x) ** 2 + (node1.y - node2.y) ** 2)
  229. @staticmethod
  230. def get_nearest_list_index(nodes, rnd):
  231. # 遍历所有节点 计算采样点和节点的距离
  232. dList = [(node.x - rnd[0]) ** 2
  233. + (node.y - rnd[1]) ** 2 for node in nodes]
  234. # 获得最近的距离所对应的索引
  235. minIndex = dList.index(min(dList))
  236. return minIndex
  237. def get_new_node(self, theta, n_ind, nearestNode):
  238. newNode = copy.deepcopy(nearestNode)
  239. # 坐标
  240. newNode.x += self.expand_dis * math.cos(theta)
  241. newNode.y += self.expand_dis * math.sin(theta)
  242. # 代价
  243. newNode.cost += self.expand_dis
  244. # 父亲节点
  245. newNode.parent = n_ind
  246. return newNode
  247. def is_near_goal(self, node):
  248. # 计算距离
  249. d = self.line_cost(node, self.goal)
  250. if d < self.expand_dis:
  251. return True
  252. return False
  253. def rewire(self, newNode, nearInds):
  254. n_node = len(self.node_list)
  255. for i in nearInds:
  256. nearNode = self.node_list[i]
  257. d = math.sqrt((nearNode.x - newNode.x) ** 2
  258. + (nearNode.y - newNode.y) ** 2)
  259. s_cost = newNode.cost + d
  260. if nearNode.cost > s_cost:
  261. theta = math.atan2(newNode.y - nearNode.y,
  262. newNode.x - nearNode.x)
  263. if self.check_collision(nearNode, theta, d):
  264. nearNode.parent = n_node - 1
  265. nearNode.cost = s_cost
  266. @staticmethod
  267. def distance_squared_point_to_segment(v, w, p):
  268. # Return minimum distance between line segment vw and point p
  269. if np.array_equal(v, w):
  270. return (p - v).dot(p - v) # v == w case
  271. l2 = (w - v).dot(w - v) # i.e. |w-v|^2 - avoid a sqrt
  272. # Consider the line extending the segment,
  273. # parameterized as v + t (w - v).
  274. # We find projection of point p onto the line.
  275. # It falls where t = [(p-v) . (w-v)] / |w-v|^2
  276. # We clamp t from [0,1] to handle points outside the segment vw.
  277. t = max(0, min(1, (p - v).dot(w - v) / l2))
  278. projection = v + t * (w - v) # Projection falls on the segment
  279. return (p - projection).dot(p - projection)
  280. def check_segment_collision(self, x1, y1, x2, y2):
  281. # 遍历所有的障碍物
  282. for (ox, oy, size) in self.obstacle_list:
  283. dd = self.distance_squared_point_to_segment(
  284. np.array([x1, y1]),
  285. np.array([x2, y2]),
  286. np.array([ox, oy]))
  287. if dd <= size ** 2:
  288. return False # collision
  289. return True
  290. def check_collision(self, nearNode, theta, d):
  291. tmpNode = copy.deepcopy(nearNode)
  292. end_x = tmpNode.x + math.cos(theta) * d
  293. end_y = tmpNode.y + math.sin(theta) * d
  294. return self.check_segment_collision(tmpNode.x, tmpNode.y, end_x, end_y)
  295. def get_final_course(self, lastIndex):
  296. path = [[self.goal.x, self.goal.y]]
  297. while self.node_list[lastIndex].parent is not None:
  298. node = self.node_list[lastIndex]
  299. path.append([node.x, node.y])
  300. lastIndex = node.parent
  301. path.append([self.start.x, self.start.y])
  302. return path
  303. def draw_graph_informed_RRTStar(self, xCenter=None, cBest=None, cMin=None, e_theta=None, rnd=None, path=None):
  304. plt.clf()
  305. # for stopping simulation with the esc key.
  306. plt.gcf().canvas.mpl_connect(
  307. 'key_release_event',
  308. lambda event: [exit(0) if event.key == 'escape' else None])
  309. if rnd is not None:
  310. plt.plot(rnd[0], rnd[1], "^k")
  311. if cBest != float('inf'):
  312. self.plot_ellipse(xCenter, cBest, cMin, e_theta)
  313. for node in self.node_list:
  314. if node.parent is not None:
  315. if node.x or node.y is not None:
  316. plt.plot([node.x, self.node_list[node.parent].x], [
  317. node.y, self.node_list[node.parent].y], "-g")
  318. for (ox, oy, size) in self.obstacle_list:
  319. plt.plot(ox, oy, "ok", ms=30 * size)
  320. if path is not None:
  321. plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')
  322. plt.plot(self.start.x, self.start.y, "xr")
  323. plt.plot(self.goal.x, self.goal.y, "xr")
  324. plt.axis([-2, 18, -2, 15])
  325. plt.grid(True)
  326. plt.pause(0.01)
  327. @staticmethod
  328. def plot_ellipse(xCenter, cBest, cMin, e_theta): # pragma: no cover
  329. a = math.sqrt(cBest ** 2 - cMin ** 2) / 2.0
  330. b = cBest / 2.0
  331. angle = math.pi / 2.0 - e_theta
  332. cx = xCenter[0]
  333. cy = xCenter[1]
  334. t = np.arange(0, 2 * math.pi + 0.1, 0.1)
  335. x = [a * math.cos(it) for it in t]
  336. y = [b * math.sin(it) for it in t]
  337. rot = Rot.from_euler('z', -angle).as_matrix()[0:2, 0:2]
  338. fx = rot @ np.array([x, y])
  339. px = np.array(fx[0, :] + cx).flatten()
  340. py = np.array(fx[1, :] + cy).flatten()
  341. plt.plot(cx, cy, "xc")
  342. plt.plot(px, py, "--c")
  343. def draw_graph(self, rnd=None, path=None):
  344. plt.clf()
  345. # for stopping simulation with the esc key.
  346. plt.gcf().canvas.mpl_connect(
  347. 'key_release_event',
  348. lambda event: [exit(0) if event.key == 'escape' else None])
  349. if rnd is not None:
  350. plt.plot(rnd.x, rnd.y, "^k")
  351. for node in self.node_list:
  352. if node.parent is not None:
  353. if node.x or node.y is not None:
  354. plt.plot([node.x, self.node_list[node.parent].x], [
  355. node.y, self.node_list[node.parent].y], "-g")
  356. for (ox, oy, size) in self.obstacle_list:
  357. # self.plot_circle(ox, oy, size)
  358. plt.plot(ox, oy, "ok", ms=30 * size)
  359. plt.plot(self.start.x, self.start.y, "xr")
  360. plt.plot(self.goal.x, self.goal.y, "xr")
  361. if path is not None:
  362. plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')
  363. plt.axis([-2, 18, -2, 15])
  364. plt.grid(True)
  365. plt.pause(0.01)
  366. class Node:
  367. def __init__(self, x, y):
  368. self.x = x
  369. self.y = y
  370. self.cost = 0.0
  371. self.parent = None
  372. def main():
  373. print("Start rrt planning")
  374. # create obstacles
  375. # obstacleList = [
  376. # (3, 3, 1.5),
  377. # (12, 2, 3),
  378. # (3, 9, 2),
  379. # (9, 11, 2),
  380. # ]
  381. # 设置障碍物 (圆点、半径)
  382. obstacleList = [(5, 5, 1), (3, 6, 2), (3, 8, 2), (3, 10, 2), (7, 5, 2),
  383. (9, 5, 2), (8, 10, 1)]
  384. # Set params
  385. # 采样范围 设置的障碍物 最大迭代次数
  386. rrt = RRT(randArea=[-2, 18], obstacleList=obstacleList, maxIter=200)
  387. # 传入的是起点和终点
  388. path = rrt.rrt_planning(start=[0, 0], goal=[15, 12], animation=show_animation)
  389. # path = rrt.rrt_star_planning(start=[0, 0], goal=[15, 12], animation=show_animation)
  390. # path = rrt.informed_rrt_star_planning(start=[0, 0], goal=[15, 12], animation=show_animation)
  391. print("Done!!")
  392. if show_animation and path:
  393. plt.show()
  394. if __name__ == '__main__':
  395. main()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/808622
推荐阅读
相关标签
  

闽ICP备14008679号