utils3D.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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):
  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 < 0.1:
  35. return initparams.env.goal + 1
  36. else:
  37. return x
  38. return x
  39. def isinside(initparams, x):
  40. '''see if inside obstacle'''
  41. for i in initparams.env.blocks:
  42. if i[0] <= x[0] < i[3] and i[1] <= x[1] < i[4] and i[2] <= x[2] < i[5]:
  43. return True
  44. return False
  45. def isinbound(i, x):
  46. if i[0] <= x[0] < i[3] and i[1] <= x[1] < i[4] and i[2] <= x[2] < i[5]:
  47. return True
  48. return False
  49. # def isCollide(initparams, x, y):
  50. # '''see if line intersects obstacle'''
  51. # ray = getRay(x, y)
  52. # dist = getDist(x, y)
  53. # if not isinbound(initparams.env.boundary, y):
  54. # return True
  55. # for i in getAABB(initparams.env.blocks):
  56. # shot = pyrr.geometric_tests.ray_intersect_aabb(ray, i)
  57. # if shot is not None:
  58. # dist_wall = getDist(x, shot)
  59. # if dist_wall <= dist: # collide
  60. # return True
  61. # for i in initparams.env.balls:
  62. # shot = pyrr.geometric_tests.ray_intersect_sphere(ray, i)
  63. # if shot != []:
  64. # dists_ball = [getDist(x, j) for j in shot]
  65. # if all(dists_ball <= dist): # collide
  66. # return True
  67. # return False
  68. def lineSphere(p0, p1, ball):
  69. # https://cseweb.ucsd.edu/classes/sp19/cse291-d/Files/CSE291_13_CollisionDetection.pdf
  70. c, r = ball[0:3], ball[-1]
  71. line = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]]
  72. d1 = [c[0] - p0[0], c[1] - p0[1], c[2] - p0[2]]
  73. t = (1 / (line[0] * line[0] + line[1] * line[1] + line[2] * line[2])) * (
  74. line[0] * d1[0] + line[1] * d1[1] + line[2] * d1[2])
  75. if t <= 0:
  76. if (d1[0] * d1[0] + d1[1] * d1[1] + d1[2] * d1[2]) <= r ** 2: return True
  77. elif t >= 1:
  78. d2 = [c[0] - p1[0], c[1] - p1[1], c[2] - p1[2]]
  79. if (d2[0] * d2[0] + d2[1] * d2[1] + d2[2] * d2[2]) <= r ** 2: return True
  80. elif 0 < t < 1:
  81. x = [p0[0] + t * line[0], p0[1] + t * line[1], p0[2] + t * line[2]]
  82. k = [c[0] - x[0], c[1] - x[1], c[2] - x[2]]
  83. if (k[0] * k[0] + k[1] * k[1] + k[2] * k[2]) <= r ** 2: return True
  84. return False
  85. def lineAABB(p0, p1, dist, aabb):
  86. # https://www.gamasutra.com/view/feature/131790/simple_intersection_tests_for_games.php?print=1
  87. # aabb should have the attributes of P, E as center point and extents
  88. mid = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2, (p0[2] + p1[2]) / 2] # mid point
  89. I = [(p1[0] - p0[0]) / dist, (p1[1] - p0[1]) / dist, (p1[2] - p0[2]) / dist] # unit direction
  90. hl = dist / 2 # radius
  91. T = [aabb.P[0] - mid[0], aabb.P[1] - mid[1], aabb.P[2] - mid[2]]
  92. # do any of the principal axis form a separting axis?
  93. if abs(T[0]) > (aabb.E[0] + hl * abs(I[0])): return False
  94. if abs(T[1]) > (aabb.E[1] + hl * abs(I[1])): return False
  95. if abs(T[2]) > (aabb.E[2] + hl * abs(I[2])): return False
  96. # I.cross(x axis) ?
  97. r = aabb.E[1] * abs(I[2]) + aabb.E[2] * abs(I[1])
  98. if abs(T[1] * I[2] - T[2] * I[1]) > r: return False
  99. # I.cross(y axis) ?
  100. r = aabb.E[0] * abs(I[2]) + aabb.E[2] * abs(I[0])
  101. if abs(T[2] * I[0] - T[0] * I[2]) > r: return False
  102. # I.cross(z axis) ?
  103. r = aabb.E[0] * abs(I[1]) + aabb.E[1] * abs(I[0])
  104. if abs(T[0] * I[1] - T[1] * I[0]) > r: return False
  105. return True
  106. def lineOBB(p0, p1, dist, obb):
  107. # transform points to obb frame
  108. res = obb.T@np.column_stack([np.array([p0,p1]),[1,1]]).T
  109. # record old position and set the position to origin
  110. oldP, obb.P= obb.P, [0,0,0]
  111. # calculate segment-AABB testing
  112. ans = lineAABB(res[0:3,0],res[0:3,1],dist,obb)
  113. # reset the position
  114. obb.P = oldP
  115. return ans
  116. def isCollide(initparams, x, child, dist=None):
  117. '''see if line intersects obstacle'''
  118. '''specified for expansion in A* 3D lookup table'''
  119. if dist==None:
  120. dist = getDist(x, child)
  121. # check in bound
  122. if not isinbound(initparams.env.boundary, child):
  123. return True, dist
  124. # check collision in AABB
  125. for i in range(len(initparams.env.AABB)):
  126. if lineAABB(x, child, dist, initparams.env.AABB[i]):
  127. return True, dist
  128. # check collision in ball
  129. for i in initparams.env.balls:
  130. if lineSphere(x, child, i):
  131. return True, dist
  132. # check collision with obb
  133. for i in initparams.env.OBB:
  134. if lineOBB(x, child, dist, i):
  135. return True, dist
  136. return False, dist
  137. def nearest(initparams, x):
  138. V = np.array(initparams.V)
  139. if initparams.i == 0:
  140. return initparams.V[0]
  141. xr = repmat(x, len(V), 1)
  142. dists = np.linalg.norm(xr - V, axis=1)
  143. return tuple(initparams.V[np.argmin(dists)])
  144. def steer(initparams, x, y):
  145. dist, step = getDist(y, x), initparams.stepsize
  146. increment = ((y[0] - x[0]) / dist * step, (y[1] - x[1]) / dist * step, (y[2] - x[2]) / dist * step)
  147. xnew = (x[0] + increment[0], x[1] + increment[1], x[2] + increment[2])
  148. # direc = (y - x) / np.linalg.norm(y - x)
  149. # xnew = x + initparams.stepsize * direc
  150. return xnew
  151. def near(initparams, x):
  152. cardV = len(initparams.V)
  153. eta = initparams.eta
  154. gamma = initparams.gamma
  155. r = min(gamma * (np.log(cardV) / cardV), eta)
  156. if initparams.done:
  157. r = 1
  158. V = np.array(initparams.V)
  159. if initparams.i == 0:
  160. return [initparams.V[0]]
  161. xr = repmat(x, len(V), 1)
  162. inside = np.linalg.norm(xr - V, axis=1) < r
  163. nearpoints = V[inside]
  164. return np.array(nearpoints)
  165. def cost(initparams, x):
  166. '''here use the additive recursive cost function'''
  167. if all(x == tuple(initparams.env.start)):
  168. return 0
  169. xparent = initparams.Parent[x]
  170. return cost(initparams, xparent) + getDist(x, xparent)
  171. def path(initparams, Path=[], dist=0):
  172. x = tuple(initparams.env.goal)
  173. while x != tuple(initparams.env.start):
  174. x2 = initparams.Parent[x]
  175. Path.append(np.array([x, x2]))
  176. dist += getDist(x, x2)
  177. x = x2
  178. return Path, dist
  179. def hash3D(x):
  180. return str(x[0]) + ' ' + str(x[1]) + ' ' + str(x[2])
  181. def dehash(x):
  182. return np.array([float(i) for i in x.split(' ')])
  183. class edgeset(object):
  184. def __init__(self):
  185. self.E = {}
  186. def add_edge(self, edge):
  187. x, y = edge[0], edge[1]
  188. if x in self.E:
  189. self.E[x].add(y)
  190. else:
  191. self.E[x] = set()
  192. self.E[x].add(y)
  193. def remove_edge(self, edge):
  194. x, y = edge[0], edge[1]
  195. self.E[x].remove(y)
  196. def get_edge(self):
  197. edges = []
  198. for v in self.E:
  199. for n in self.E[v]:
  200. # if (n,v) not in edges:
  201. edges.append((v, n))
  202. return edges