Swarm-SLAM  1.0.0
C-SLAM Framework
loop_closure_sparse_matching.py
Go to the documentation of this file.
1 import numpy as np
2 from cslam.nns_matching import NearestNeighborsMatching
3 from cslam.lidar_pr.scancontext_matching import ScanContextMatching
4 from cslam.algebraic_connectivity_maximization import AlgebraicConnectivityMaximization, EdgeInterRobot
5 
7  """Sparse matching for loop closure detection
8  Matches global descriptors to generate loop closure candidates
9  Then candidates are selected such that we respect the communication budget
10  """
11 
12  def __init__(self, params):
13  """ Initialization of loop closure matching
14 
15  Args:
16  params (dict): ROS 2 parameters
17  """
18  # Extract params
19  self.paramsparams = params
20  # Initialize matching structs
21  if self.paramsparams["frontend.sensor_type"] == "lidar":
22  self.local_nnsmlocal_nnsm = ScanContextMatching()
23  else:
24  self.local_nnsmlocal_nnsm = NearestNeighborsMatching()
25  self.other_robots_nnsmother_robots_nnsm = {}
26  for i in range(self.paramsparams['max_nb_robots']):
27  if i != self.paramsparams['robot_id']:
28  if self.paramsparams["frontend.sensor_type"] == "lidar":
29  self.other_robots_nnsmother_robots_nnsm[i] = ScanContextMatching()
30  else:
31  self.other_robots_nnsmother_robots_nnsm[i] = NearestNeighborsMatching()
32  # Initialize candidate selection algorithm
34  self.paramsparams['robot_id'], self.paramsparams['max_nb_robots'], extra_params=self.paramsparams)
35 
36  def add_local_global_descriptor(self, embedding, keyframe_id):
37  """ Add a local keyframe for matching
38 
39  Args:
40  embedding (np.array): global descriptor
41  id (int): keyframe id
42  """
43  matches = []
44  self.local_nnsmlocal_nnsm.add_item(embedding, keyframe_id)
45  for i in range(self.paramsparams['max_nb_robots']):
46  if i != self.paramsparams['robot_id']:
47  kf, similarity = self.other_robots_nnsmother_robots_nnsm[i].search_best(embedding)
48  if kf is not None:
49  if similarity >= self.paramsparams['frontend.similarity_threshold']:
50  match = EdgeInterRobot(self.paramsparams['robot_id'], keyframe_id, i, kf,
51  similarity)
52  self.candidate_selectorcandidate_selector.add_match(match)
53  matches.append(match)
54  return matches
55 
57  """ Add keyframe global descriptor info from other robot
58 
59  Args:
60  msg (cslam_common_interfaces.msg.GlobalDescriptor): global descriptor info
61  """
62  self.other_robots_nnsmother_robots_nnsm[msg.robot_id].add_item(
63  np.asarray(msg.descriptor), msg.keyframe_id)
64 
65  match = None
66  kf, similarity = self.local_nnsmlocal_nnsm.search_best(np.asarray(msg.descriptor))
67  if kf is not None:
68  if similarity >= self.paramsparams['frontend.similarity_threshold']:
69  match = EdgeInterRobot(self.paramsparams['robot_id'], kf, msg.robot_id,
70  msg.keyframe_id, similarity)
71  self.candidate_selectorcandidate_selector.add_match(match)
72  return match
73 
74  def match_local_loop_closures(self, descriptor, kf_id):
75  kfs, similarities = self.local_nnsmlocal_nnsm.search(descriptor,
76  k=self.paramsparams['frontend.nb_best_matches'])
77 
78  if len(kfs) > 0 and kfs[0] == kf_id:
79  kfs, similarities = kfs[1:], similarities[1:]
80  if len(kfs) == 0:
81  return None, None
82 
83  for kf, similarity in zip(kfs, similarities):
84  if abs(kf -
85  kf_id) < self.paramsparams['frontend.intra_loop_min_inbetween_keyframes']:
86  continue
87 
88  if similarity < self.paramsparams['frontend.similarity_threshold']:
89  continue
90 
91  return kf, kfs
92  return None, None
93 
95  number_of_candidates,
96  is_neighbor_in_range,
97  greedy_initialization=True):
98  """Select inter-robot loop closure candidates according to budget
99 
100  Args:
101  number_of_candidates (int): inter-robot loop closure budget,
102  is_neighbor_in_range: dict(int, bool): indicates which other robots are in communication range
103  greedy_initialization: bool: use greedy initialization for selection
104 
105  Returns:
106  list(EdgeInterRobot): selected edges
107  """
108  return self.candidate_selectorcandidate_selector.select_candidates(
109  number_of_candidates, is_neighbor_in_range,
110  greedy_initialization)
def select_candidates(self, number_of_candidates, is_neighbor_in_range, greedy_initialization=True)