utils3D.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import numpy as np
  2. from numpy.matlib import repmat
  3. import pyrr as pyrr
  4. import os
  5. import sys
  6. sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../Sampling-based Planning/")
  7. from rrt_3D.plot_util3D import visualization
  8. def getRay(x, y):
  9. direc = [y[0] - x[0], y[1] - x[1], y[2] - x[2]]
  10. return np.array([x, direc])
  11. def getAABB(blocks):
  12. AABB = []
  13. for i in blocks:
  14. AABB.append(np.array([np.add(i[0:3], -0), np.add(i[3:6], 0)])) # make AABBs alittle bit of larger
  15. return AABB
  16. def getDist(pos1, pos2):
  17. return np.sqrt(sum([(pos1[0] - pos2[0]) ** 2, (pos1[1] - pos2[1]) ** 2, (pos1[2] - pos2[2]) ** 2]))
  18. ''' The following utils can be used for rrt or rrt*,
  19. required param initparams should have
  20. env, environement generated from env3D
  21. V, node set
  22. E, edge set
  23. i, nodes added
  24. maxiter, maximum iteration allowed
  25. stepsize, leaf growth restriction
  26. '''
  27. def sampleFree(initparams, bias = 0.1):
  28. '''biased sampling'''
  29. x = np.random.uniform(initparams.env.boundary[0:3], initparams.env.boundary[3:6])
  30. i = np.random.random()
  31. if isinside(initparams, x):
  32. return sampleFree(initparams)
  33. else:
  34. if i < bias:
  35. return initparams.env.goal + 1
  36. else:
  37. return x
  38. return x
  39. # ---------------------- Collision checking algorithms
  40. def isinside(initparams, x):
  41. '''see if inside obstacle'''
  42. for i in initparams.env.blocks:
  43. if i[0] <= x[0] < i[3] and i[1] <= x[1] < i[4] and i[2] <= x[2] < i[5]:
  44. return True
  45. return False
  46. def isinbound(i, x):
  47. if i[0] <= x[0] < i[3] and i[1] <= x[1] < i[4] and i[2] <= x[2] < i[5]:
  48. return True
  49. return False
  50. def lineSphere(p0, p1, ball):
  51. # https://cseweb.ucsd.edu/classes/sp19/cse291-d/Files/CSE291_13_CollisionDetection.pdf
  52. c, r = ball[0:3], ball[-1]
  53. line = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]]
  54. d1 = [c[0] - p0[0], c[1] - p0[1], c[2] - p0[2]]
  55. t = (1 / (line[0] * line[0] + line[1] * line[1] + line[2] * line[2])) * (
  56. line[0] * d1[0] + line[1] * d1[1] + line[2] * d1[2])
  57. if t <= 0:
  58. if (d1[0] * d1[0] + d1[1] * d1[1] + d1[2] * d1[2]) <= r ** 2: return True
  59. elif t >= 1:
  60. d2 = [c[0] - p1[0], c[1] - p1[1], c[2] - p1[2]]
  61. if (d2[0] * d2[0] + d2[1] * d2[1] + d2[2] * d2[2]) <= r ** 2: return True
  62. elif 0 < t < 1:
  63. x = [p0[0] + t * line[0], p0[1] + t * line[1], p0[2] + t * line[2]]
  64. k = [c[0] - x[0], c[1] - x[1], c[2] - x[2]]
  65. if (k[0] * k[0] + k[1] * k[1] + k[2] * k[2]) <= r ** 2: return True
  66. return False
  67. def lineAABB(p0, p1, dist, aabb):
  68. # https://www.gamasutra.com/view/feature/131790/simple_intersection_tests_for_games.php?print=1
  69. # aabb should have the attributes of P, E as center point and extents
  70. mid = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2, (p0[2] + p1[2]) / 2] # mid point
  71. I = [(p1[0] - p0[0]) / dist, (p1[1] - p0[1]) / dist, (p1[2] - p0[2]) / dist] # unit direction
  72. hl = dist / 2 # radius
  73. T = [aabb.P[0] - mid[0], aabb.P[1] - mid[1], aabb.P[2] - mid[2]]
  74. # do any of the principal axis form a separting axis?
  75. if abs(T[0]) > (aabb.E[0] + hl * abs(I[0])): return False
  76. if abs(T[1]) > (aabb.E[1] + hl * abs(I[1])): return False
  77. if abs(T[2]) > (aabb.E[2] + hl * abs(I[2])): return False
  78. # I.cross(x axis) ?
  79. r = aabb.E[1] * abs(I[2]) + aabb.E[2] * abs(I[1])
  80. if abs(T[1] * I[2] - T[2] * I[1]) > r: return False
  81. # I.cross(y axis) ?
  82. r = aabb.E[0] * abs(I[2]) + aabb.E[2] * abs(I[0])
  83. if abs(T[2] * I[0] - T[0] * I[2]) > r: return False
  84. # I.cross(z axis) ?
  85. r = aabb.E[0] * abs(I[1]) + aabb.E[1] * abs(I[0])
  86. if abs(T[0] * I[1] - T[1] * I[0]) > r: return False
  87. return True
  88. def lineOBB(p0, p1, dist, obb):
  89. # transform points to obb frame
  90. res = obb.T@np.column_stack([np.array([p0,p1]),[1,1]]).T
  91. # record old position and set the position to origin
  92. oldP, obb.P= obb.P, [0,0,0]
  93. # calculate segment-AABB testing
  94. ans = lineAABB(res[0:3,0],res[0:3,1],dist,obb)
  95. # reset the position
  96. obb.P = oldP
  97. return ans
  98. def isCollide(initparams, x, child, dist=None):
  99. '''see if line intersects obstacle'''
  100. '''specified for expansion in A* 3D lookup table'''
  101. if dist==None:
  102. dist = getDist(x, child)
  103. # check in bound
  104. if not isinbound(initparams.env.boundary, child):
  105. return True, dist
  106. # check collision in AABB
  107. for i in range(len(initparams.env.AABB)):
  108. if lineAABB(x, child, dist, initparams.env.AABB[i]):
  109. return True, dist
  110. # check collision in ball
  111. for i in initparams.env.balls:
  112. if lineSphere(x, child, i):
  113. return True, dist
  114. # check collision with obb
  115. for i in initparams.env.OBB:
  116. if lineOBB(x, child, dist, i):
  117. return True, dist
  118. return False, dist
  119. # ---------------------- leaf node extending algorithms
  120. def nearest(initparams, x):
  121. V = np.array(initparams.V)
  122. if initparams.i == 0:
  123. return initparams.V[0]
  124. xr = repmat(x, len(V), 1)
  125. dists = np.linalg.norm(xr - V, axis=1)
  126. return tuple(initparams.V[np.argmin(dists)])
  127. def near(initparams, x):
  128. x = np.array(x)
  129. V = np.array(initparams.V)
  130. cardV = len(initparams.V)
  131. eta = initparams.eta
  132. gamma = initparams.gamma
  133. r = min(gamma * (np.log(cardV) / cardV), eta)
  134. if initparams.done:
  135. r = 1
  136. if initparams.i == 0:
  137. return [initparams.V[0]]
  138. xr = repmat(x, len(V), 1)
  139. inside = np.linalg.norm(xr - V, axis=1) < r
  140. nearpoints = V[inside]
  141. return np.array(nearpoints)
  142. def steer(initparams, x, y):
  143. dist, step = getDist(y, x), initparams.stepsize
  144. increment = ((y[0] - x[0]) / dist * step, (y[1] - x[1]) / dist * step, (y[2] - x[2]) / dist * step)
  145. xnew = (x[0] + increment[0], x[1] + increment[1], x[2] + increment[2])
  146. # direc = (y - x) / np.linalg.norm(y - x)
  147. # xnew = x + initparams.stepsize * direc
  148. return xnew
  149. def cost(initparams, x):
  150. '''here use the additive recursive cost function'''
  151. if x == initparams.x0:
  152. return 0
  153. return cost(initparams, initparams.Parent[x]) + getDist(x, initparams.Parent[x])
  154. def path(initparams, Path=[], dist=0):
  155. x = tuple(initparams.env.goal)
  156. while x != tuple(initparams.env.start):
  157. x2 = initparams.Parent[x]
  158. Path.append(np.array([x, x2]))
  159. dist += getDist(x, x2)
  160. x = x2
  161. return Path, dist
  162. class edgeset(object):
  163. def __init__(self):
  164. self.E = {}
  165. def add_edge(self, edge):
  166. x, y = edge[0], edge[1]
  167. if x in self.E:
  168. self.E[x].add(y)
  169. else:
  170. self.E[x] = set()
  171. self.E[x].add(y)
  172. def remove_edge(self, edge):
  173. x, y = edge[0], edge[1]
  174. self.E[x].remove(y)
  175. def get_edge(self):
  176. edges = []
  177. for v in self.E:
  178. for n in self.E[v]:
  179. # if (n,v) not in edges:
  180. edges.append((v, n))
  181. return edges