Advertisement
Guest User

Untitled

a guest
Dec 5th, 2019
217
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.67 KB | None | 0 0
  1. # Author Aled
  2. import numpy as np
  3. import rospy
  4. from helper.array_help import convolve_func
  5.  
  6. from Probability_Models.sensor_model import SensorModel
  7. from scipy.stats import multivariate_normal
  8. from nav_msgs.msg import OccupancyGrid
  9. from geometry_msgs.msg import PoseStamped
  10. from time import sleep
  11.  
  12. class ProbabilityMap:
  13. """
  14. For generating a probability map of the area as to where a person could be, updating the probabilities of the map will
  15. cause the map to be published to /prob_map
  16.  
  17. NOT DESIGNED FOR CONCURRENCY
  18. """
  19.  
  20. def __init__(self, occupancy_map, occupancy_map_info, publish=False):
  21. """Author Aled
  22. :param occupancy_map: a 2D array of a map with -1 being unseen 0 being no wall and 1 being a wall
  23. :type occupancy_map: numpy.ndarray
  24. :param publish: if set to true the object will publish the map to "prob_map" on every single update
  25. :type publish: bool
  26. """
  27. if publish:
  28. self._prob_map_msg = OccupancyGrid()
  29. self._prob_map_msg.info = occupancy_map_info
  30. self.publisher = rospy.Publisher('/prob_map', OccupancyGrid, queue_size=1)
  31. sleep(10)
  32. else:
  33. self.publisher = None
  34.  
  35. print "initialising sensor model"
  36. self.sensor_model = SensorModel()
  37.  
  38. # this should be refactored so that the actual map is passed in as a whole as this would
  39. # make things easier
  40. actual_map = OccupancyGrid()
  41. actual_map.info = occupancy_map_info
  42. actual_map.data = list(occupancy_map.reshape(-1,))
  43.  
  44. self.sensor_model.set_map(actual_map)
  45. print "generating map"
  46. self.probability_map = self._generate_prob_map(occupancy_map)
  47. print "map generated"
  48.  
  49.  
  50. self._publish_map()
  51.  
  52.  
  53. # this might be a performance bottleneck
  54. def _normalise(self):
  55. """Author Aled
  56. normalises the probability map so that all the probabilities sum to 1"""
  57.  
  58. total = (self.probability_map != - 1).sum()
  59. f = np.vectorize(lambda x: (x * 100) / total if x != -1 else x)
  60. # I don't know how efficient this is for memory and the garbage collector may be doing a lot of work
  61. self.probability_map = f(self.probability_map)
  62.  
  63.  
  64. def _generate_prob_map(self, occupancy_map):
  65. """Author Aled
  66. Generate's a probability map and populates it
  67.  
  68. :param occupancy_map: a 2D array of a map with -1 being unseen 0 being no wall and 1 being a wall
  69. :type occupancy_map: numpy.ndarray
  70. :return: a probability map
  71. :type: numpy.ndarray
  72.  
  73. """
  74. probability_map = occupancy_map.copy()
  75. # replaces any blocks which are a wall or unknown with 0 probability any other are set to 1.0
  76. probability_map = convolve_func(probability_map, wall_mask, (19, 19))
  77. return probability_map
  78.  
  79.  
  80. def _publish_map(self):
  81. """Author Aled
  82.  
  83. This simply publishes the probability map to /prob_map
  84. :return:
  85. """
  86. if self.publisher is not None:
  87. self._prob_map_msg.data = self.probability_map.flatten().astype(int).tolist()
  88. self._prob_map_msg.header.frame_id = "map"
  89. #print self._prob_map_msg.data
  90. self.publisher.publish(self._prob_map_msg)
  91. print "I've published the map!!!"
  92.  
  93. def update(self,scan,robot_pose):
  94. """Author Aled
  95.  
  96. :param scan:
  97. :param robot_pose:
  98. :return:
  99. """
  100. self.sensor_model_update(scan,robot_pose)
  101. self._publish_map()
  102.  
  103. def _add_gaussian(self,x,y,negative=False,filter_size=9,scale=50, cov=2.0):
  104. """Author Aled
  105.  
  106.  
  107. It will add a gaussian filter to the given x and y co-ordinates. It's intended use is for when the laser
  108. find an unexpected object at predicted co-ordinates x,y. This should be used in conjunction with the camera.
  109.  
  110.  
  111. if negative is set to true then the gaussian will be subtracted rather than added to the map
  112. :param x:
  113. :param y:
  114. :return:
  115. """
  116. # I should probably only calculate the filter once to save extra computation
  117. # the filter size should always be an odd number
  118.  
  119. filter_ = np.linspace(0, filter_size, filter_size, endpoint=False)
  120. filter_ = multivariate_normal.pdf(filter_, mean=(filter_size/2), cov=cov).reshape(1,filter_size)
  121. filter_ = filter_.T.dot( filter_)
  122. filter_ *= scale
  123.  
  124.  
  125. half_filter = int(filter_size / 2)
  126.  
  127. for i, array in enumerate(filter_):
  128. for j, p in enumerate(array):
  129. # if it is out the map then do not give it a probability
  130. if self.probability_map[y + (i - half_filter)][x + (j - half_filter)] == -1:
  131. continue
  132. #print "BEFORE"
  133. #print self.probability_map[y + (i - half_filter)][x +(j - half_filter)]
  134. #print "AFTER"
  135. if negative:
  136. self.probability_map[y + (i - half_filter)][x +(j - half_filter)] -= p
  137. if self.probability_map[y + (i - half_filter)][x +(j - half_filter)] < 0:
  138. self.probability_map[y + (i - half_filter)][x +(j - half_filter)] = 0
  139. else:
  140. self.probability_map[y + (i - half_filter)][x +(j - half_filter)] += p
  141. if self.probability_map[y + (i - half_filter)][x +(j - half_filter)] > 100:
  142. self.probability_map[y + (i - half_filter)][x +(j - half_filter)] = 100
  143. #print self.probability_map[y + (i - half_filter)][x +(j - half_filter)]
  144.  
  145.  
  146.  
  147. def get_highest_probability(self):
  148. return self.probability_map.max()
  149.  
  150. def get_most_likely_pose(self):
  151. # should probably apply gaussian smoothing here
  152. (y,x) = get_most_likely(self.probability_map)
  153.  
  154. map_width = self._prob_map_msg.info.width
  155. map_height = self._prob_map_msg.info.height
  156. map_origin_x = self._prob_map_msg.info.origin.position.x
  157. map_origin_y = self._prob_map_msg.info.origin.position.y
  158. map_resolution = self._prob_map_msg.info.resolution
  159.  
  160. ox = (x * map_resolution) + map_origin_x
  161. oy = (y * map_resolution) + map_origin_y
  162.  
  163. #ox = ((x - (map_width/2) -0.5) * map_resolution) + map_origin_x
  164. #oy = ((y - (map_height/2) -0.5) * map_resolution ) + map_origin_y
  165.  
  166.  
  167.  
  168. p = PoseStamped()
  169. p.header.frame_id = "map"
  170. p.pose.position.x = ox
  171. p.pose.position.y = oy
  172. return p
  173.  
  174.  
  175. # this is a performance bottleneck
  176. def apply_gausian_smoothing(self):
  177. pass
  178.  
  179. def sensor_model_update(self, scan, robot_pose):
  180. """
  181. Author: Aled
  182.  
  183. The probability map will be updated with gaussians added to co-ordinates where there is an unexpected obstacle
  184.  
  185. :param scan:
  186. :param robot_pose:
  187. :return:
  188. """
  189. DECAY_RATE = 5
  190.  
  191.  
  192. # reduce the probability of places seen by the laser first by way of a decay rate
  193. laser_view = self.sensor_model.get_laser_area(scan,robot_pose)
  194. for point_x, point_y in laser_view:
  195. self._add_gaussian(point_x,point_y,negative=True,scale = 25)
  196.  
  197. collision_points = self.sensor_model.get_collision_points(scan, robot_pose)
  198. print collision_points
  199. for (x,y) in collision_points:
  200. self._add_gaussian(x,y,scale = 200)
  201.  
  202.  
  203.  
  204. def get_most_likely(a):
  205. """AUthor Xiwen"""
  206. target=[]
  207. idx = np.argmax(a, axis=1)
  208. pro = np.amax(a, axis=1)
  209. for x in range(len(pro)):
  210. if pro[x]==np.amax(pro):
  211. y=idx[x]
  212. target.append([x,y])
  213. return target[0]
  214.  
  215.  
  216.  
  217. def numpy_conv(map,filter,padding="VALID"):
  218. new_map=init_map(map)
  219. H, W = new_map.shape
  220. filter_size = filter.shape[0]
  221. # default np.floor
  222. filter_center = int(filter_size / 2.0)
  223. filter_center_ceil = int(np.ceil(filter_size / 2.0))
  224.  
  225. # SAME:input=output
  226. if padding == "SAME":
  227. padding_new_map = np.zeros([H + filter_center_ceil, W + filter_center_ceil], np.float32)
  228. padding_new_map[filter_center:-filter_center, filter_center:-filter_center] = inputs
  229. new_map = padding_new_map
  230. #defind a equal size sapce as the new_map,the 0 outside will be cut off later
  231.  
  232. result = np.zeros((new_map.shape))
  233. #update input,change HW
  234.  
  235. H, W = new_map.shape
  236. #print("new size",H,W)
  237.  
  238. for r in range(filter_center,H -filter_center):
  239. for c in range(filter_center,W -filter_center ):
  240.  
  241. cur_input = new_map[r -filter_center :r +filter_center_ceil,
  242. c - filter_center:c + filter_center_ceil]
  243.  
  244. cur_output = cur_input * filter
  245.  
  246. conv_sum = np.sum(cur_output)
  247.  
  248. result[r, c] = conv_sum
  249. # cut off the 0 outside
  250.  
  251. final_result = result[filter_center:result.shape[0] - filter_center,
  252. filter_center:result.shape[1] -filter_center]
  253. returnfinal_result
  254.  
  255.  
  256.  
  257. def wall_mask(a):
  258. """Author Aled
  259. Applies a mask to try and exclude areas that are too close to a wall
  260. Only works straight after you've converted the occupancy map to a probability map
  261.  
  262. :param a: 2D sub array to apply a mask to
  263. :type a: np.ndarray
  264. :return: a single float value of probability
  265. :type: float
  266. """
  267.  
  268. if -1 in a:
  269. return -1
  270. elif a.max() >= 50:
  271. return -1
  272. else:
  273. return 1
  274.  
  275.  
  276.  
  277. def wall_to_person_prob(x):
  278. """Author Aled
  279. maps a value in the occupancy map to the probability of a person being there
  280.  
  281. :param x: probability of wall
  282. :type x: float
  283. :return: probability of a person being there
  284. :type float
  285. """
  286. if x == -1:
  287. return -1
  288.  
  289. elif x < 50:
  290. return 1.0
  291.  
  292. else:
  293. return 0.0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement