def selectTarget(self, init_ogm, coverage, robot_pose, origin, resolution, force_random=False): target = [-1, -1] ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the # following tools: ogm_limits, Brushfire field, OGM skeleton, # topological nodes. # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries(init_ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, resolution, brush, ogm_limits) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker( vis_nodes, 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) # Random point if self.method == 'random' or force_random == True: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) ######################################################################## return target
def ogm_limit_calculation(ogm, resolution, origin, find_step=20, search_step=20, max_points=10, map_size=[]): (ogm_lp, previous_ogm_limits) = OgmOperations.findLimitPoints( ogm, origin, resolution, find_step, search_step, max_points, map_size) if ogm_lp is None: return None else: ogm_lm = np.array(ogm_lp, dtype=np.float64) for n in ogm_lm: n[0] = n[0] * resolution + origin['x'] n[1] = n[1] * resolution + origin['y'] return np.array(np.concatenate((ogm_lm[:, :2], ogm_lp), axis=1), dtype=np.float64), previous_ogm_limits
def selectTarget(self, init_ogm, ros_ogm, coverage, robot_pose, origin, \ resolution, force_random = False): target = [-1, -1] ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the # following tools: ogm_limits, Brushfire field, OGM skeleton, # topological nodes. # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries( init_ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, \ origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, \ resolution, brush, ogm_limits) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(\ vis_nodes,\ 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) # Random point if self.method == 'random' or force_random == True: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) # Custom point elif self.method == 'cost_based': self.path_planning.setMap(ros_ogm) g_robot_pose = [robot_pose['x_px'] - int(origin['x'] / resolution),\ robot_pose['y_px'] - int(origin['y'] / resolution)] # Remove all covered nodes from the candidate list nodes = np.array(nodes) uncovered_idxs = np.array( [coverage[n[0], n[1]] == 0 for n in nodes]) goals = nodes[uncovered_idxs] # Initialize costs w_dist = np.full(len(goals), np.inf) w_turn = np.full(len(goals), np.inf) w_topo = np.full(len(goals), np.inf) w_cove = np.full(len(goals), np.inf) for idx, node in zip(range(len(goals)), goals): subgoals = np.array( self.path_planning.createPath(g_robot_pose, node, resolution)) # If path is empty then skip to the next iteration if subgoals.size == 0: continue # subgoals should contain the robot pose, so we don't need to diff it explicitly subgoal_vectors = np.diff(subgoals, axis=0) # Distance cost dists = [math.hypot(v[0], v[1]) for v in subgoal_vectors] w_dist[idx] = np.sum(dists) # Turning cost w_turn[idx] = 0 theta = robot_pose['th'] for v in subgoal_vectors[:-1]: st_x, st_y = v theta2 = math.atan2(st_y - g_robot_pose[1], st_x - g_robot_pose[0]) w_turn[idx] += abs(theta2 - theta) theta = theta2 # Coverage cost w_cove[idx] = sum(coverage[x][y] for x, y in subgoal_vectors) # Topology cost w_topo[idx] = brush[node[0], node[1]] # Normalize weights w_dist = (w_dist - min(w_dist)) / (max(w_dist) - min(w_dist)) w_turn = (w_turn - min(w_turn)) / (max(w_turn) - min(w_turn)) w_cove = (w_cove - min(w_cove)) / (max(w_cove) - min(w_cove)) w_topo = (w_topo - min(w_topo)) / (max(w_topo) - min(w_topo)) # Cost weights c_topo = 4 c_dist = 3 c_cove = 2 c_turn = 1 # Calculate combination cost (final) cost = c_dist * w_dist + c_turn * w_turn + c_cove * w_cove + c_topo * w_topo min_dist, min_idx = min(zip(cost, range(len(cost)))) target = nodes[min_idx] # Check if next target exists and if it exists, check if is close to previous if target is None: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) else: target_dist = math.hypot(target[0] - self.previous_target[0], target[1] - self.previous_target[1]) if target_dist <= 5: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) ######################################################################## self.previous_target = target return target
def selectTarget(self, init_ogm, coverage, robot_pose, origin, \ resolution, force_random = False): target = [-1, -1] ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the # following tools: ogm_limits, Brushfire field, OGM skeleton, # topological nodes. # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries( init_ogm, origin, resolution) #print(ogm_limits) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, \ origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, \ resolution, brush, ogm_limits) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(\ vis_nodes,\ 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) # Random point #if statement that makes the target selection random in case the remaining nodes are two if len(nodes) <= 2: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) return target pose_global_x = int(robot_pose['x_px'] - origin['x'] / resolution) pose_global_y = int(robot_pose['y_px'] - origin['y'] / resolution) #the folowing variables receive the values read by the sonar sensor sonar_left = self.sonar.sonar_left_range sonar_right = self.sonar.sonar_right_range sonar_front = self.sonar.sonar_front_range #a total sum is calculated for the front,right and left sonar sensors sonar_sum = sonar_left + sonar_right + sonar_front numrows = ogm.shape[1] numcols = ogm.shape[0] #if statement used in case the robot is in a tight spot and determines the target in the direction there is maximum space if sonar_sum < 1.2: target = self.sonar_avoidance(pose_global_x, pose_global_y, ogm, numcols, numrows) return target #in case of a time out or a failure in path planning if self.method == 'random' or force_random == True: target = [ self.node2_index_x, self.node2_index_y ] #sets the current target as the previously calculated second best-scored target if self.timeout_happened == 1: target = self.sonar_avoidance(pose_global_x, pose_global_y, ogm, numcols, numrows) self.timeout_happened = 0 return target self.timeout_happened = 1 return target ######################################################################## sum_min = 10000000 dist_min = 10000000 node_index_x = 0 #x value of the node with the lowest total cost node_index_y = 0 #y value of the node with the lowest total cost topo_cost = [] #list of topological costs for each node dist_cost = [] #list of distance costs for each node cov_cost = [] #list of coverage costs for each node #using the brushfire array in order to calculate topology cost for each node for n in nodes: sum_temp = 0 index = n[1] + 1 while brush[n[0], index] != 0: sum_temp += 1 index += 1 if index == numrows - 1: #numrows break index = n[1] - 1 while brush[n[0], index] != 0: sum_temp += 1 index -= 1 if index == 0: break index = n[0] + 1 while brush[index, n[1]] != 0: sum_temp += 1 index += 1 if index == numcols - 1: #numcols break index = n[0] - 1 while brush[index, n[1]] != 0: sum_temp += 1 index -= 1 if index == 0: break topo_cost.append(sum_temp / 4) #using the coverage array in order to calculate coverage cost for each node numrows = len(coverage) numcols = len(coverage[0]) for n in nodes: total_sum = 0 sum_temp = 0 index = n[1] + 1 if index >= numrows or n[0] >= numcols: total_sum = 5000 cov_cost.append(total_sum) continue while coverage[n[0], index] != 100: sum_temp += 1 index += 1 if index >= numcols - 1: #numrows-1: break total_sum += sum_temp * coverage[n[0], index] / 100 index = n[1] - 1 sum_temp = 0 while coverage[n[0], index] != 100: sum_temp += 1 index -= 1 if index == 0: break total_sum += sum_temp * coverage[n[0], index] / 100 index = n[0] + 1 sum_temp = 0 if index >= numcols or n[1] >= numrows: total_sum = 5000 cov_cost.append(total_sum) continue while coverage[index, n[1]] != 100: sum_temp += 1 index += 1 if index >= numrows - 1: #numcols-1 break total_sum += sum_temp * coverage[index, n[1]] / 100 index = n[0] - 1 sum_temp = 0 while coverage[index, n[1]] != 100: sum_temp += 1 index -= 1 if index == 0: break total_sum += sum_temp * coverage[index, n[1]] / 100 if total_sum == 0: total_sum = 5000 cov_cost.append(total_sum) pose_global_x = int(robot_pose['x_px'] - origin['x'] / resolution) pose_global_y = int(robot_pose['y_px'] - origin['y'] / resolution) #eucledean distance between the robot pose and each node dist = math.sqrt( math.pow(pose_global_x - n[0], 2) + math.pow(pose_global_y - n[1], 2)) dist_cost.append(dist) maxi = 0 for i in range(0, len(cov_cost)): if cov_cost[i] != 5000 and cov_cost[i] > maxi: maxi = cov_cost[i] for i in range(0, len(cov_cost)): if cov_cost[i] == 5000: cov_cost[i] = maxi * 1.2 #lists to store the normalized costs for each node topo_cost_norm = [] dist_cost_norm = [] cov_cost_norm = [] final_cost = [] min_final_cost = 1000000 for i in range(0, len(dist_cost)): topo_cost_norm.append((np.float(topo_cost[i] - min(topo_cost)) / np.float(max(topo_cost) - min(topo_cost)))) dist_cost_norm.append((dist_cost[i] - min(dist_cost)) / (max(dist_cost) - min(dist_cost))) if np.float(max(cov_cost) - min(cov_cost)) != 0: cov_cost_norm.append((np.float(cov_cost[i] - min(cov_cost)) / np.float(max(cov_cost) - min(cov_cost)))) if max(cov_cost) != min(cov_cost): final_cost.append( 4 * topo_cost_norm[i] + 2 * dist_cost_norm[i] + 4 * cov_cost_norm[i] ) #optimal factor values in order to determine the best node to approach else: final_cost.append( 6 * topo_cost_norm[i] + 4 * dist_cost_norm[i] ) # exception if statement for the case coverage array cannot be used yet if (final_cost[i] < min_final_cost): min_final_cost = final_cost[i] self.node2_index_x = node_index_x #storing the second best node to approach in case of path planning failure or time out self.node2_index_y = node_index_y # node_index_x = nodes[i][0] node_index_y = nodes[i][1] target = [node_index_x, node_index_y] #in case current target is the same with the previous , sonar avoidance function is used to modify the robot pose if target == self.previous_target: target = self.sonar_avoidance(pose_global_x, pose_global_y, ogm, numcols, numrows) self.previous_target = target return target
def selectTarget(self, ogm, coverage, robot_pose, origin, resolution, force_random=False): ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the following tools: ogm_limits, Brushfire field, # OGM skeleton, topological nodes. # Find only the useful boundaries of OGM. Only there calculations have meaning. ogm_limits = OgmOperations.findUsefulBoundaries(ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = topology.skeletonizationCffi(ogm, origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = topology.topologicalNodes(ogm, skeleton, coverage, brush) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [[n[0] * resolution + origin['x'], n[1] * resolution + origin['y']] for n in nodes] RvizHandler.printMarker( vis_nodes, 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) try: tinit = time.time() except: tinit = None if self.method == 'random' or force_random: target = self.selectRandomTarget(ogm, coverage, brush) elif self.method_is_cost_based(): g_robot_pose = self.robot_perception.getGlobalCoordinates([robot_pose['x_px'], robot_pose['y_px']]) self.path_planning.setMap(self.robot_perception.getRosMap()) class MapContainer: def __init__(self): self.skeleton = skeleton self.coverage = coverage self.ogm = ogm self.brush = brush self.nodes = [node for node in nodes if TargetSelection.is_good(brush, ogm, coverage, node)] self.robot_px = [robot_pose['x_px'] - origin['x'] / resolution, robot_pose['y_px'] - origin['y'] / resolution] self.theta = robot_pose['th'] @staticmethod def create_path(path_target): return self.path_planning.createPath(g_robot_pose, path_target, resolution) target = self.select_by_cost(MapContainer()) else: assert False, "Invalid target_selector method." if tinit is not None: Print.art_print("Target method {} time: {}".format(self.method, time.time() - tinit), Print.ORANGE) return target
def selectTarget(self, init_ogm, coverage, robot_pose, origin, \ resolution, force_random = False): target = [-1, -1] ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the # following tools: ogm_limits, Brushfire field, OGM skeleton, # topological nodes. # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries( init_ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, \ origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, \ resolution, brush, ogm_limits) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(\ vis_nodes,\ 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) # Check at autonomous_expl.yaml if target_selector = 'random' or 'smart' if self.method == 'random' or force_random == True: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) elif self.method == 'smart' and force_random == False: tinit = time.time() # Get the robot pose in pixels [rx, ry] = [\ robot_pose['x_px'] - \ origin['x'] / resolution,\ robot_pose['y_px'] - \ origin['y'] / resolution\ ] g_robot_pose = [rx, ry] # If nodes > 25 the calculation time-cost is very big # In order to avoid time-reset we perform sampling on # the nodes list and take a half-sized sample for i in range(0, len(nodes)): nodes[i].append(i) if (len(nodes) > 25): nodes = random.sample(nodes, int(len(nodes) / 2)) # Initialize weigths w_dist = [0] * len(nodes) w_rot = [robot_pose['th']] * len(nodes) w_cov = [0] * len(nodes) w_topo = [0] * len(nodes) # Calculate weights for every node in the topological graph for i in range(0, len(nodes)): # If path planning fails then give extreme values to weights path = self.path_planning.createPath(g_robot_pose, nodes[i], resolution) if not path: w_dist[i] = sys.maxsize w_rot[i] = sys.maxsize w_cov[i] = sys.maxsize w_topo[i] = sys.maxsize else: for j in range(1, len(path)): # Distance cost w_dist[i] += math.hypot(path[j][0] - path[j - 1][0], path[j][1] - path[j - 1][1]) # Rotational cost w_rot[i] += abs( math.atan2(path[j][0] - path[j - 1][0], path[j][1] - path[j - 1][1])) # Coverage cost w_cov[i] += coverage[int(path[j][0])][int( path[j][1])] / (len(path)) # Add the coverage cost of 0-th node of the path w_cov[i] += coverage[int(path[0][0])][int( path[0][1])] / (len(path)) # Topological cost # This metric depicts wether the target node # is placed in open space or near an obstacle # We want the metric to be reliable so we also check node's neighbour cells w_topo[i] += brush[nodes[i][0]][nodes[i][1]] w_topo[i] += brush[nodes[i][0] - 1][nodes[i][1]] w_topo[i] += brush[nodes[i][0] + 1][nodes[i][1]] w_topo[i] += brush[nodes[i][0]][nodes[i][-1]] w_topo[i] += brush[nodes[i][0]][nodes[i][+1]] w_topo[i] += brush[nodes[i][0] - 1][nodes[i][1] - 1] w_topo[i] += brush[nodes[i][0] + 1][nodes[i][1] + 1] w_topo[i] = w_topo[i] / 7 # Normalize weights between 0-1 for i in range(0, len(nodes)): w_dist[i] = 1 - (w_dist[i] - min(w_dist)) / (max(w_dist) - min(w_dist)) w_rot[i] = 1 - (w_rot[i] - min(w_rot)) / (max(w_rot) - min(w_rot)) w_cov[i] = 1 - (w_cov[i] - min(w_cov)) / (max(w_cov) - min(w_cov)) w_topo[i] = 1 - (w_topo[i] - min(w_topo)) / (max(w_topo) - min(w_topo)) # Set cost values # We set each cost's priority based on experimental results # from "Cost-Based Target Selection Techniques Towards Full Space # Exploration and Coverage for USAR Applications # in a Priori Unknown Environments" publication C1 = w_topo C2 = w_dist C3 = [1] * len(nodes) for i in range(0, len(nodes)): C3[i] -= w_cov[i] C4 = w_rot # Priority Weight C_PW = [0] * len(nodes) # Smoothing Factor C_SF = [0] * len(nodes) # Target's Final Priority C_FP = [0] * len(nodes) for i in range(0, len(nodes)): C_PW[i] = 2**3 * (1 - C1[i]) / .5 + 2**2 * ( 1 - C2[i]) / .5 + 2**1 * (1 - C3[i]) / .5 + 2**0 * ( 1 - C4[i]) / .5 C_SF[i] = (2**3 * (1 - C1[i]) + 2**2 * (1 - C2[i]) + 2**1 * (1 - C3[i]) + 2**0 * (1 - C4[i])) / (2**4 - 1) C_FP[i] = C_PW[i] * C_SF[i] # Select the node with the smallest C_FP value val, idx = min((val, idx) for (idx, val) in enumerate(C_FP)) Print.art_print( "Select smart target time: " + str(time.time() - tinit), Print.BLUE) Print.art_print("Selected Target - Node: " + str(nodes[idx][2]), Print.BLUE) target = nodes[idx] else: Print.art_print( "[ERROR] target_selector at autonomous_expl.yaml must be either 'random' or 'smart'", Print.RED) ######################################################################## return target
def selectTarget(self, ogm, coverage, robot_pose, origin, \ resolution, force_random = False): # Random point if self.method == 'random' or force_random == True: init_ogm = ogm # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries( init_ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, \ origin, resolution, ogm_limits) Print.art_print( "Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, \ resolution, brush, ogm_limits) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(\ vis_nodes,\ 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) return [False, target] tinit = time.time() g_robot_pose = [robot_pose['x_px'] - int(origin['x'] / resolution), \ robot_pose['y_px'] - int(origin['y'] / resolution)] # Calculate coverage frontier with sobel filters tinit = time.time() cov_dx = scipy.ndimage.sobel(coverage, 0) cov_dy = scipy.ndimage.sobel(coverage, 1) cov_frontier = np.hypot(cov_dx, cov_dy) cov_frontier *= 100 / np.max(cov_frontier) cov_frontier = 100 * (cov_frontier > 80) Print.art_print("Sobel filters time: " + str(time.time() - tinit), Print.BLUE) # Remove the edges that correspond to obstacles instead of frontiers (in a 5x5 radius) kern = 5 tinit = time.time() i_rng = np.matlib.repmat( np.arange(-(kern / 2), kern / 2 + 1).reshape(kern, 1), 1, kern) j_rng = np.matlib.repmat(np.arange(-(kern / 2), kern / 2 + 1), kern, 1) for i in range((kern / 2), cov_frontier.shape[0] - (kern / 2)): for j in range((kern / 2), cov_frontier.shape[1] - (kern / 2)): if cov_frontier[i, j] == 100: if np.any(ogm[i + i_rng, j + j_rng] > 99): cov_frontier[i, j] = 0 Print.art_print("Frontier trimming time: " + str(time.time() - tinit), Print.BLUE) # Save coverage frontier as image (for debugging purposes) # scipy.misc.imsave('test.png', np.rot90(cov_frontier)) # Frontier detection/grouping tinit = time.time() labeled_frontiers, num_frontiers = scipy.ndimage.label( cov_frontier, np.ones((3, 3))) Print.art_print("Frontier grouping time: " + str(time.time() - tinit), Print.BLUE) goals = np.full((num_frontiers, 2), -1) w_dist = np.full(len(goals), -1) w_turn = np.full(len(goals), -1) w_size = np.full(len(goals), -1) w_obst = np.full(len(goals), -1) # Calculate the centroid and its cost, for each frontier for i in range(1, num_frontiers + 1): points = np.where(labeled_frontiers == i) # Discard small groupings (we chose 20 as a treshold arbitrarily) group_length = points[0].size if group_length < 20: labeled_frontiers[points] = 0 continue sum_x = np.sum(points[0]) sum_y = np.sum(points[1]) centroid = np.array([sum_x / group_length, sum_y / group_length]).reshape(2, 1) # Find the point on the frontier nearest (2-norm) to the centroid, and use it as goal nearest_idx = np.linalg.norm(np.array(points) - centroid, axis=0).argmin() print ogm[int(points[0][nearest_idx]), int(points[1][nearest_idx])] goals[i - 1, :] = np.array( [points[0][nearest_idx], points[1][nearest_idx]]) # Save centroids for later visualisation (for debugging purposes) labeled_frontiers[int(goals[i - 1, 0]) + i_rng, int(goals[i - 1, 1]) + j_rng] = i # Calculate size of obstacles between robot and goal line_pxls = list(bresenham(int(goals[i-1,0]), int(goals[i-1,1]),\ g_robot_pose[0], g_robot_pose[1])) ogm_line = list(map(lambda pxl: ogm[pxl[0], pxl[1]], line_pxls)) N_occupied = len(list(filter(lambda x: x > 25, ogm_line))) N_line = len(line_pxls) w_obst[i - 1] = float(N_occupied) / N_line # print('Occupied = '+str(N_occupied)) # print('Full Line = '+str(N_line)) # ipdb.set_trace() # Manhattan distance w_dist[i - 1] = scipy.spatial.distance.cityblock( goals[i - 1, :], g_robot_pose) # Missalignment theta = np.arctan2(goals[i - 1, 1] - g_robot_pose[1], goals[i - 1, 0] - g_robot_pose[0]) w_turn[i - 1] = (theta - robot_pose['th']) if w_turn[i - 1] > np.pi: w_turn[i - 1] -= 2 * np.pi elif w_turn[i - 1] < -np.pi: w_turn[i - 1] += 2 * np.pi # We don't care about the missalignment direction so we abs() it w_turn[i - 1] = np.abs(w_turn[i - 1]) # Frontier size w_size[i - 1] = group_length # Save frontier groupings as an image (for debugging purposes) cmap = plt.cm.jet norm = plt.Normalize(vmin=labeled_frontiers.min(), vmax=labeled_frontiers.max()) image = cmap(norm(labeled_frontiers)) plt.imsave('frontiers.png', np.rot90(image)) # Remove invalid goals and weights valids = w_dist != -1 goals = goals[valids] w_dist = w_dist[valids] w_turn = w_turn[valids] w_size = w_size[valids] w_obst = w_obst[valids] # Normalize weights w_dist = (w_dist - min(w_dist)) / (max(w_dist) - min(w_dist)) w_turn = (w_turn - min(w_turn)) / (max(w_turn) - min(w_turn)) w_size = (w_size - min(w_size)) / (max(w_size) - min(w_size)) # Cancel turn weight for frontiers that have obstacles w_turn[np.where(w_obst != 0)] = 1 # Goal cost function c_dist = 3 c_turn = 2 c_size = 1 c_obst = 4 costs = c_dist * w_dist + c_turn * w_turn + c_size * w_size + c_obst * w_obst min_idx = costs.argmin() Print.art_print("Target selection time: " + str(time.time() - tinit), Print.ORANGE) print costs print goals ## Safety Distance from obstacles # Goal Coordinates grid_size = 20 [found_obstacle, closest_obstacle, min_dist] = self.detectObstacles(grid_size, ogm, goals[min_idx]) if found_obstacle == False: return [False, goals[min_idx]] # Calculate new goal: dist_from_obstacles = 10 normal_vector = goals[min_idx] - closest_obstacle normal_vector = normal_vector / np.hypot(normal_vector[0], normal_vector[1]) new_goal = closest_obstacle + dist_from_obstacles * normal_vector # Check new goal for nearby obstacles [found_obstacle, closest_obstacle, min_dist_new] = \ self.detectObstacles(grid_size, ogm, new_goal.round()) # Return if min_dist < 7: # return the target with max min_dist if min_dist_new > min_dist: return [True, new_goal.round(), goals[min_idx]] else: return [False, goals[min_idx]] else: return [False, goals[min_idx]]
def selectTarget(self, init_ogm, coverage, robot_pose, origin, \ resolution, force_random = False ): target = [-1, -1] ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the # following tools: ogm_limits, Brushfire field, OGM skeleton, # topological nodes. # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries( init_ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, \ origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, \ resolution, brush, ogm_limits) for i in range(0, len(nodes)): print " node " + str(nodes[i]) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(\ vis_nodes,\ 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) if force_random == True: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) force_random = False print " force random: CANNOT CREATE PATH TO SELECTED POINT" return target else: # get robot's position #g_robot_pose = [robot_pose['x_px'] - int(origin['x'] / resolution),\ # robot_pose['y_px'] - int(origin['y'] / resolution)] [rx , ry] = [robot_pose['x_px'] - int(origin['x'] / resolution),\ robot_pose['y_px'] - int(origin['y'] / resolution)] # find all the x and y distances between robot and goals dis_x = [rx - target[0] for target in nodes] dis_y = [ry - target[1] for target in nodes] # calculate the euclidean distance dist = [math.hypot(dis[0], dis[1]) for dis in zip(dis_x, dis_y)] # calculate the manhattan distance between the robot and all nodes #dist = [scipy.spatial.distance.cityblock(nodes[i], g_robot_pose) for i in range(0,len(nodes)) ] # target is the closest node min_dist, min_idx = min(zip(dist, range(len(dist)))) goal = nodes[min_idx] target = goal print "TARGET " + str(target) + " TARGET IDX " + str(min_idx) ######################################################################## return target
def targetSelection(self, initOgm, costmap, origin, resolution, robotPose): rospy.loginfo("-----------------------------------------") rospy.loginfo("[Target Select Node] Robot_Pose[x, y, th] = [%f, %f, %f]", \ robotPose['x'], robotPose['y'], robotPose['th']) rospy.loginfo("[Target Select Node] OGM_Origin = [%i, %i]", origin['x'], origin['y']) rospy.loginfo("[Target Select Node] OGM_Size = [%u, %u]", initOgm.shape[0], initOgm.shape[1]) ogmLimits = {} ogmLimits['min_x'] = -1 ogmLimits['max_x'] = -1 ogmLimits['min_y'] = -1 ogmLimits['max_y'] = -1 # Find only the useful boundaries of OGM. Only there calculations have meaning ogmLimits = OgmOperations.findUsefulBoundaries(initOgm, origin, resolution) print(ogmLimits) while ogmLimits['min_x'] < 0 or ogmLimits['max_x'] < 0 or \ ogmLimits['min_y'] < 0 or ogmLimits['max_y'] < 0: rospy.logwarn("[Main Node] Negative OGM limits. Retrying...") ogmLimits = OgmOperations.findUsefulBoundaries( initOgm, origin, resolution) ogmLimits['min_x'] = abs(int(ogmLimits['min_x'])) ogmLimits['min_y'] = abs(int(ogmLimits['min_y'])) ogmLimits['max_x'] = abs(int(ogmLimits['max_x'])) ogmLimits['max_y'] = abs(int(ogmLimits['max_y'])) rospy.loginfo("[Target Select] OGM_Limits[x] = [%i, %i]", \ ogmLimits['min_x'], ogmLimits['max_x']) rospy.loginfo("[Target Select] OGM_Limits[y] = [%i, %i]", \ ogmLimits['min_y'], ogmLimits['max_y']) # Blur the OGM to erase discontinuities due to laser rays #ogm = OgmOperations.blurUnoccupiedOgm(initOgm, ogmLimits) ogm = initOgm # for i in range(len(ogm)): # for j in range(len(ogm)): # if ogm[i][j] == 100: # rospy.loginfo('i,j = [%d, %d]', i, j) # # Calculate Brushfire field #itime = time.time() #brush = self.brush.obstaclesBrushfireCffi(ogm, ogmLimits) #rospy.loginfo("[Target Select] Brush ready! Elapsed time = %fsec", time.time() - itime) #obst = self.brush.coverageLimitsBrushfire2(initOgm,ogm,robotPose,origin, resolution ) rospy.loginfo("Calculating brush2....") # brush = self.brush.obstaclesBrushfireCffi(ogm,ogmLimits) brush2 = self.brush.coverageLimitsBrushfire2(ogm, ogm, robotPose, origin, resolution) #goals = self.brush.closestUncoveredBrushfire(ogm, ogm, brush, robotPose, origin, resolution ) #robotPosePx = [] #robotPosePx[0] = robotPose['x']/resolution #robotPosePy[1] = robotPose['y']/resolution print 'size of brush2 :' print len(brush2) min_dist = 10**24 store_goal = () # rospy.loginfo("finding the difference between the two sets...") # brush2.difference(visited) #max_dist = random.randrange(1,10) #rospy.loginfo("max_dist for this it is: %d ", max_dist) throw = set() for goal in brush2: goal = list(goal) for i in range(-10, 11): if int(goal[0] / resolution - origin['x'] / resolution) + i >= len(ogm): break if ogm[int(goal[0]/resolution - origin['x']/resolution) + i]\ [int(goal[1]/resolution - origin['y']/resolution) ] == 100: goal = tuple(goal) throw.add(goal) break for goal in brush2: goal = list(goal) for j in range(-10, 11): if int(goal[1] / resolution - origin['y'] / resolution) + j >= len(ogm[0]): break if ogm[int(goal[0]/resolution - origin['x']/resolution)]\ [int(goal[1]/resolution - origin['y']/resolution) + j] == 100: goal = tuple(goal) throw.add(goal) break print 'size of throw :' print len(throw) brush2.difference_update(throw) print 'size of brush2 after update :' print len(brush2) distance_map = dict() for goal in brush2: dist = math.hypot(goal[0] - robotPose['x'], goal[1] - robotPose['y']) distance_map[goal] = dist sorted_dist_map = sorted(distance_map.items(), key=operator.itemgetter(1)) sorted_goal_list = list() for key, value in sorted(distance_map.iteritems(), key=lambda (k, v): (v, k)): sorted_goal_list.append(key) pass #print "%s: %s" % (key, value) # for key in distance_map: # if distance_map[key] > random.randrange(1,5): # goal = key # break # sorted_goal_list_sampled = sorted_goal_list[0:len(sorted_goal_list):10] #print sorted_goal_list_top_10 # stored_goal = list() # dist = 1000 # for goal in distance_map: # if distance_map[goal] < dist: # dist = distance_map[goal] # stored_goal = goal # # rospy.loginfo('The stored goal is = [%f,%f]!!' ,stored_goal[0], stored_goal[1]) # rospy.loginfo('The distance is %f!!', distance_map[stored_goal]) # rospy.loginfo('The gain is %f!!', topo_gain[stored_goal]) # #rand_target = random.choice(distance_map.keys()) # #goal = rand_target ind = random.randrange(0, min(4, len(sorted_goal_list))) print 'ind is' print ind goal = sorted_goal_list[ind] print 'the dist is' print distance_map[goal] goal = list(goal) goal[0] = goal[0] + random.uniform(-0.5, 0.5) goal[1] = goal[1] + random.uniform(-0.5, 0.5) print goal self.target = goal #for goal in brush2: # print sorted_distance_map[goal] return self.target rospy.loginfo("goal AFTER unifrom is: goal = [%f,%f]", store_goal[0], store_goal[1])
def selectTarget(self, init_ogm, coverage, robot_pose, origin, \ resolution, force_random = False): target = [-1, -1] ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the # following tools: ogm_limits, Brushfire field, OGM skeleton, # topological nodes. # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries(init_ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, \ origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, \ resolution, brush, ogm_limits) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(\ vis_nodes,\ 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) # Random point if self.method == 'random' or force_random == True: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) ######################################################################## # Challenge 6. Smart point selection demands autonomous_expl.yaml->target_selector: 'smart' # Smart point selection if self.method == 'smart' and force_random == False: nextTarget = self.selectSmartTarget(coverage, brush, robot_pose, resolution, origin, nodes) # Check if selectSmartTarget found a target if nextTarget is not None: # Check if the next target is the same as the previous dist = math.hypot( nextTarget[0] - self.previous_target[0], nextTarget[1] - self.previous_target[1]) if dist > 5: target = nextTarget else: target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) else: # No target found. Choose a random target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) self.previous_target = target return target
def selectTarget(self, init_ogm, coverage, robot_pose, origin, resolution, g_robot_pose, previous_limits=[], force_random=False): # Initialize Target target = [-1, -1] if self.running_time() > 15: print('\x1b[37;1m' + str('15 Minutes Constraint Passed!!!') + '\x1b[0m') # Find only the useful boundaries of OGM start = time() ogm_limits = OgmOperations.findUsefulBoundaries(init_ogm, origin, resolution, print_result=True, step=20) if self.debug: print('\x1b[34;1m' + str('Target Selection: OGM Boundaries ') + str(ogm_limits) + str(' in ') + str(time() - start) + str(' seconds.') + '\x1b[0m') # Blur the OGM to erase discontinuities due to laser rays start = time() ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) if self.debug: print('\x1b[34;1m' + str('Target Selection: OGM Blurred in ') + str(time() - start) + str(' seconds.') + '\x1b[0m') # Calculate Brushfire field start = time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) if self.debug: print('\x1b[34;1m' + str('Target Selection: Brush in ') + str(time() - start) + str(' seconds.') + '\x1b[0m') # Calculate Robot Position [rx, ry] = [ robot_pose['x_px'] - origin['x'] / resolution, robot_pose['y_px'] - origin['y'] / resolution ] # Calculate Skeletonization start = time() skeleton = self.topology.skeletonizationCffi(ogm, origin, resolution, ogm_limits) if self.debug: print('\x1b[34;1m' + str('Target Selection: Skeletonization in ') + str(time() - start) + str(' seconds.') + '\x1b[0m') # Find Topological Graph start = time() potential_targets = self.topology.topologicalNodes( ogm, skeleton, coverage, brush, final_num_of_nodes=25, erase_distance=100, step=15) if self.debug: print('\x1b[34;1m' + str('Target Selection: Topological Graph in ') + str(time() - start) + str(' seconds.') + '\x1b[0m') print('\x1b[34;1m' + str("The Potential Targets to be Checked are ") + str(len(potential_targets)) + '\x1b[0m') if len(potential_targets) == 0: print('\x1b[32;1m' + str('\n------------------------------------------') + str("Finished Space Exploration!!! ") + str('------------------------------------------\n') + '\x1b[0m') sleep(10000) # Visualization of Topological Graph vis__potential_targets = [] for n in potential_targets: vis__potential_targets.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(vis__potential_targets, 1, 0, "map", "art_topological_nodes", [0.3, 0.4, 0.7, 0.5], 0.1) # Check if we have given values to Gains if not self.initialize_gains: self.set_gain() # Random Point Selection if Needed if self.method == 'random' or force_random: # Get Distance from Potential Targets distance = np.zeros((len(potential_targets), 1), dtype=np.float32) for idx, target in enumerate(potential_targets): distance[idx] = hypot(rx - target[0], ry - target[1]) distance *= 255.0 / distance.max() path = self.selectRandomTarget(ogm, coverage, brush, ogm_limits, potential_targets, distance, resolution, g_robot_pose) if path is not None: return path else: return [] # Sent Potential Targets for Color Evaluation (if Flag is Enable) if self.color_evaluation_flag: start_color = time() while not self.sent_potential_targets_for_color_evaluation( potential_targets, resolution, origin): pass # Initialize Arrays for Target Selection id = np.array(range(0, len(potential_targets))).reshape(-1, 1) brushfire = np.zeros((len(potential_targets), 1), dtype=np.float32) distance = np.zeros((len(potential_targets), 1), dtype=np.float32) color = np.zeros((len(potential_targets), 1), dtype=np.float32) corners = np.zeros((len(potential_targets), 1), dtype=np.float32) score = np.zeros((len(potential_targets), 1), dtype=np.float32) # Calculate Distance and Brush Evaluation start = time() for idx, target in enumerate(potential_targets): distance[idx] = hypot(rx - target[0], ry - target[1]) brushfire[idx] = brush[target[0], target[1]] if self.debug: print('\x1b[35;1m' + str('Distance and Brush Evaluation Calculated in ') + str(time() - start) + str(' seconds.') + '\x1b[0m') # Wait for Color Evaluation to be Completed if self.color_evaluation_flag: while not self.targets_color_evaluated: pass color = np.array(self.color_evaluation).reshape(-1, 1) corners = np.array(self.corner_evaluation, dtype=np.float64).reshape(-1, 1) # Reset Flag for Next Run self.targets_color_evaluated = False if self.debug: print('\x1b[35;1m' + str('Color Evaluation Calculated in ') + str(time() - start_color) + str(' seconds.') + '\x1b[0m') # Normalize Evaluation Arrays to [0, 255] distance *= 255.0 / distance.max() brushfire *= 255.0 / brushfire.max() if self.color_evaluation_flag: # color max is 640*320 = 204800 color *= 255.0 / color.max() color = 255.0 - color corners *= 255.0 / corners.max() # Calculate Score to Choose Best Target # Final Array = [ Id. | [X] | [Y] | Color | Brush | Dist. | Num. of Corners | Score ] # 0 1 2 3 4 5 6 7 # Max is: 255 + 255 - 0 - 0 = +510 # Min is: 0 + 0 - 255 - 255 = -510 evaluation = np.concatenate((id, potential_targets, color, brushfire, distance, corners, score), axis=1) for e in evaluation: # Choose Gains According to Type of Exploration (Default is Exploration) if self.map_discovery_purpose == 'coverage': e[7] = self.g_color * e[3] + self.g_brush * e[ 4] - self.g_distance * e[5] - self.g_corner * e[6] elif self.map_discovery_purpose == 'combined': # Gains Change over Time self.set_gain() e[7] = self.g_color * e[3] + self.g_brush * e[ 4] - self.g_distance * e[5] - self.g_corner * e[6] else: e[7] = self.g_color * e[3] + self.g_brush * e[ 4] - self.g_distance * e[5] - self.g_corner * e[6] # Normalize Score to [0, 255] and Sort According to Best Score (Increasingly) evaluation[:, 7] = self.rescale( evaluation[:, 7], (-self.g_distance * 255.0 - self.g_corner * 255.0), (self.g_color * 255.0 + self.g_brush * 255.0), 0.0, 255.0) evaluation = evaluation[evaluation[:, 7].argsort()] # Best Target is in the Bottom of Evaluation Table Now target = [ evaluation[[len(potential_targets) - 1], [1]], evaluation[[len(potential_targets) - 1], [2]] ] # Print The Score of the Target Selected if len(previous_limits) != 0: if not previous_limits['min_x'] < target[0] < previous_limits['max_x'] and not\ previous_limits['min_y'] < target[1] < previous_limits['max_y']: print('\x1b[38;1m' + str('Selected Target was inside Explored Area.') + '\x1b[0m') print('\x1b[35;1m' + str('Selected Target was ') + str(int(evaluation.item( (len(potential_targets) - 1), 0))) + str(' with score of ') + str(evaluation.item( (len(potential_targets) - 1), 7)) + str('.') + '\x1b[0m') return self.path_planning.createPath(g_robot_pose, target, resolution)
def selectTarget(self, init_ogm, coverage, robot_pose, origin, \ resolution, force_random = False): target = [-1, -1] ######################### NOTE: QUESTION ############################## # Implement a smart way to select the next target. You have the # following tools: ogm_limits, Brushfire field, OGM skeleton, # topological nodes. # Find only the useful boundaries of OGM. Only there calculations # have meaning ogm_limits = OgmOperations.findUsefulBoundaries( init_ogm, origin, resolution) # Blur the OGM to erase discontinuities due to laser rays ogm = OgmOperations.blurUnoccupiedOgm(init_ogm, ogm_limits) # Calculate Brushfire field tinit = time.time() brush = self.brush.obstaclesBrushfireCffi(ogm, ogm_limits) Print.art_print("Brush time: " + str(time.time() - tinit), Print.ORANGE) # Calculate skeletonization tinit = time.time() skeleton = self.topo.skeletonizationCffi(ogm, \ origin, resolution, ogm_limits) Print.art_print("Skeletonization time: " + str(time.time() - tinit), Print.ORANGE) # Find topological graph tinit = time.time() nodes = self.topo.topologicalNodes(ogm, skeleton, coverage, origin, \ resolution, brush, ogm_limits) Print.art_print("Topo nodes time: " + str(time.time() - tinit), Print.ORANGE) # Visualization of topological nodes vis_nodes = [] for n in nodes: vis_nodes.append([ n[0] * resolution + origin['x'], n[1] * resolution + origin['y'] ]) RvizHandler.printMarker(\ vis_nodes,\ 1, # Type: Arrow 0, # Action: Add "map", # Frame "art_topological_nodes", # Namespace [0.3, 0.4, 0.7, 0.5], # Color RGBA 0.1 # Scale ) # SMART TARGET SELECTION USING: # 1. Brush-fire field # 2. OGM Skeleton # 3. Topological Nodes # 4. Coverage field # 5. OGM Limits # Next subtarget is selected based on a # weighted-calculated score for each node. The score # is calculated using normalized values of the brush # field and the number of branches. The weight values # are defined experimentaly through the tuning method. temp_score = 0 max_score = 0 best_node = nodes[0] # the max-min boundaries are set arbitarily BRUSH_MAX = 17 BRUSH_MIN = 1 BRUSH_WEIGHT = 2.5 BRANCH_MAX = 10 BRANCH_MIN = 0 BRANCH_WEIGHT = 2.5 DISTANCE_MIN = 0 DISTANCE_MAX = 40 DISTANCE_WEIGHT = 0.5 for n in nodes: # Use brushfire to increase temp_score temp_score = (brush[n[0]][n[1]] - BRUSH_MIN) / (BRUSH_MAX - BRUSH_MIN) * BRUSH_WEIGHT # Use OGM Skeleton to find potential # branches following the target branches = 0 for i in range(-1, 2): for j in range(-1, 2): if (i != 0 or j != 0): branches += skeleton[n[0] + i][n[1] + j] # Use OGM-Skeleton to increase temp_score (select a goal with more future options) temp_score += (branches - BRANCH_MIN) / (BRANCH_MAX - BRUSH_MIN) * BRANCH_WEIGHT # Use OGM-Limits to decrease temp_score # the goal closest to OGM limits is best exploration option distance = math.sqrt((ogm_limits['max_x'] - n[0])**2 + (ogm_limits['max_y'] - n[1])**2) temp_score -= (distance - DISTANCE_MIN) / ( DISTANCE_MAX - DISTANCE_MIN) * DISTANCE_WEIGHT # If temp_score is higher than current max # score, then max score is updated and current node # becomes next goal - target if temp_score > max_score: max_score = temp_score best_node = n final_x = best_node[0] final_y = best_node[1] target = [final_x, final_y] # Random point # if self.method == 'random' or force_random == True: # target = self.selectRandomTarget(ogm, coverage, brush, ogm_limits) ######################################################################## return target