Main MRPT website > C++ reference
MRPT logo
edge_creation_policy.h
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2015, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +---------------------------------------------------------------------------+ */
9 
10 #pragma once
11 
12 namespace mrpt { namespace srba {
13 
14 /** Determines and creates the new kf2fk edges given the set of new observations: */
15 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS>
17  const TKeyFrameID new_kf_id,
18  const typename traits_t::new_kf_observations_t & obs,
19  std::vector<TNewEdgeInfo> &new_k2k_edge_ids )
20 {
21  using namespace std;
22  switch (parameters.srba.edge_creation_policy)
23  {
24  // -----------------------------------------------------------
25  // Policy: Linear graph
26  // -----------------------------------------------------------
27  case ecpLinearGraph:
28  {
29  // The simplest policy: Always add a single edge (n-1) => (n)
30  const pose_t init_inv_pose;
31 
32  TNewEdgeInfo nei;
33  nei.has_aprox_init_val = true; // In a linear graph it's a reasonable approx. to make each KF start at the last KF pose, which is what means a null pose init val.
34 
35  nei.id = this->create_kf2kf_edge(new_kf_id, TPairKeyFrameID( new_kf_id-1, new_kf_id), obs, init_inv_pose);
36 
37  new_k2k_edge_ids.push_back(nei);
38  }
39  break;
40 
41  // -----------------------------------------------------------
42  // Policy: Submapping-like policy as in ICRA-2013 paper
43  // -----------------------------------------------------------
44  case ecpICRA2013:
45  {
46  ASSERT_(new_kf_id>=1)
47 
48  const size_t MINIMUM_OBS_TO_LOOP_CLOSURE = parameters.srba.min_obs_to_loop_closure;
49  const size_t SUBMAP_SIZE = parameters.srba.submap_size; // In # of KFs
50  const TKeyFrameID cur_localmap_center = SUBMAP_SIZE*((new_kf_id-1)/SUBMAP_SIZE);
51 
52  // For normal KFs, just connect it to its local area central KF:
53  if (0!=(new_kf_id)%SUBMAP_SIZE)
54  {
55  TNewEdgeInfo nei;
56  nei.has_aprox_init_val = false; // Filled in below
57 
58  nei.id = this->create_kf2kf_edge(new_kf_id, TPairKeyFrameID( cur_localmap_center, new_kf_id), obs );
59 
60  if (0==((new_kf_id-1)%SUBMAP_SIZE))
61  {
62  // This is the first KF after a new center, so if we add an edge to it we must be very close:
63 
64  this->rba_state.k2k_edges[nei.id]
65 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
66  ->
67 #else
68  .
69 #endif
70  inv_pose = pose_t();
71  }
72  else
73  if (nei.id>=1)
74  {
75  // Idea: the new KF should be close to the last one.
76 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
77  this->rba_state.k2k_edges[nei.id]->inv_pose = this->rba_state.k2k_edges[nei.id-1]->inv_pose;
78 #else
79  this->rba_state.k2k_edges[nei.id].inv_pose = this->rba_state.k2k_edges[nei.id-1].inv_pose;
80 #endif
81  }
82 
83  new_k2k_edge_ids.push_back(nei);
84 
85  VERBOSE_LEVEL(2) << "[edge_creation_policy] Created edge #"<< nei.id << ": "<< cur_localmap_center<<"->"<< new_kf_id << endl;
86  }
87  else
88  {
89  // Only for "submap centers", so we force the edges from the rest to the center to be optimizable, since
90  // many obs from different areas become dependent on those edges
91 
92  // Go thru all observations and for those already-seen LMs, check the distance between their base KFs and (i_id):
93  // Make a list of base KFs of my new observations, ordered in descending order by # of shared observations:
94  base_sorted_lst_t obs_for_each_base_sorted;
95  make_ordered_list_base_kfs(obs, obs_for_each_base_sorted);
96 
97  // Make vote list for each central KF:
98  map<TKeyFrameID,size_t> obs_for_each_area;
99  for (base_sorted_lst_t::const_iterator it=obs_for_each_base_sorted.begin();it!=obs_for_each_base_sorted.end();++it)
100  {
101  const size_t num_obs_this_base = it->first;
102  const TKeyFrameID base_id = it->second;
103 
104  const TKeyFrameID this_localmap_center = SUBMAP_SIZE*(base_id/SUBMAP_SIZE);
105 
106  obs_for_each_area[this_localmap_center] += num_obs_this_base;
107  }
108 
109  // Sort by votes:
110  base_sorted_lst_t obs_for_each_area_sorted;
111  for (map<TKeyFrameID,size_t>::const_iterator it=obs_for_each_area.begin();it!=obs_for_each_area.end();++it)
112  obs_for_each_area_sorted.insert( make_pair(it->second,it->first) );
113 
114  // Go thru candidate areas:
115  for (base_sorted_lst_t::const_iterator it=obs_for_each_area_sorted.begin();it!=obs_for_each_area_sorted.end();++it)
116  {
117  const size_t num_obs_this_base = it->first;
118  const TKeyFrameID central_kf_id = it->second;
119 
120  VERBOSE_LEVEL(2) << "[edge_creation_policy] Consider: area central kf#"<< central_kf_id << " with #obs:"<< num_obs_this_base << endl;
121 
122  // Create edges to all these central KFs if they're too far:
123 
124  // Find the distance between "central_kf_id" <=> "new_kf_id"
125  const TKeyFrameID from_id = new_kf_id;
126  const TKeyFrameID to_id = central_kf_id;
127 
128  typename rba_problem_state_t::TSpanningTree::next_edge_maps_t::const_iterator it_from = rba_state.spanning_tree.sym.next_edge.find(from_id);
129 
130  topo_dist_t found_distance = numeric_limits<topo_dist_t>::max();
131 
132  if (it_from != rba_state.spanning_tree.sym.next_edge.end())
133  {
134  const map<TKeyFrameID,TSpanTreeEntry> &from_Ds = it_from->second;
135  map<TKeyFrameID,TSpanTreeEntry>::const_iterator it_to_dist = from_Ds.find(to_id);
136 
137  if (it_to_dist != from_Ds.end())
138  found_distance = it_to_dist->second.distance;
139  }
140  else
141  {
142  // The new KF doesn't still have any edge created to it, that's why we didn't found any spanning tree for it.
143  // Since this means that the KF is aisolated from the rest of the world, leave the topological distance to infinity.
144  }
145 
146  if ( found_distance>=parameters.srba.max_optimize_depth)
147  {
148  if (num_obs_this_base>=MINIMUM_OBS_TO_LOOP_CLOSURE)
149  {
150  // The KF is TOO FAR: We will need to create an additional edge:
151  TNewEdgeInfo nei;
152 
153  nei.id = this->create_kf2kf_edge(new_kf_id, TPairKeyFrameID( central_kf_id, new_kf_id), obs);
154  nei.has_aprox_init_val = false; // Will need to estimate this one
155 
156 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
157  this->rba_state.k2k_edges[nei.id]->inv_pose = this->rba_state.k2k_edges[nei.id-1]->inv_pose;
158 #else
159  this->rba_state.k2k_edges[nei.id].inv_pose = this->rba_state.k2k_edges[nei.id-1].inv_pose;
160 #endif
161  new_k2k_edge_ids.push_back(nei);
162 
163  VERBOSE_LEVEL(2) << "[edge_creation_policy] Created extra edge #"<< nei.id << ": "<< central_kf_id <<"->"<<new_kf_id << " with #obs: "<< num_obs_this_base<< endl;
164  }
165  else
166  {
167  VERBOSE_LEVEL(1) << "[edge_creation_policy] Skipped extra edge " << central_kf_id <<"->"<<new_kf_id << " with #obs: "<< num_obs_this_base << " for too few shared obs!" << endl;
168  }
169  }
170  }
171 
172  // At least we must create 1 edge!
173  if (new_k2k_edge_ids.empty())
174  {
175  // Try linking to a "non-central" KF but at least having the min. # of desired shared observations:
176  if (!obs_for_each_base_sorted.empty())
177  {
178  const size_t most_connected_nObs = obs_for_each_base_sorted.begin()->first;
179  const TKeyFrameID most_connected_kf_id = obs_for_each_base_sorted.begin()->second;
180  if (most_connected_nObs>=MINIMUM_OBS_TO_LOOP_CLOSURE)
181  {
182  TNewEdgeInfo nei;
183 
184  nei.id = this->create_kf2kf_edge(new_kf_id, TPairKeyFrameID( most_connected_kf_id, new_kf_id), obs);
185  nei.has_aprox_init_val = false; // Will need to estimate this one
186  new_k2k_edge_ids.push_back(nei);
187 
188  VERBOSE_LEVEL(0) << "[edge_creation_policy] Created edge of last resort #"<< nei.id << ": "<< most_connected_kf_id <<"->"<<new_kf_id << " with #obs: "<< most_connected_nObs<< endl;
189  }
190  }
191  }
192 
193  // Recheck: if even with the last attempt we don't have any edge, it's bad:
194  ASSERTMSG_(new_k2k_edge_ids.size()>=1, mrpt::format("Error for new KF#%u: no suitable linking KF found with a minimum of %u common observation: the node becomes isolated of the graph!", static_cast<unsigned int>(new_kf_id),static_cast<unsigned int>(MINIMUM_OBS_TO_LOOP_CLOSURE) ))
195 
196  // Debug:
197  if (new_k2k_edge_ids.size()>1 && m_verbose_level>=1)
198  {
199  cout << "\n[edge_creation_policy] Loop closure detected for KF#"<< new_kf_id << ", edges: ";
200  for (size_t j=0;j<new_k2k_edge_ids.size();j++)
201  {
202 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
203  cout << rba_state.k2k_edges[new_k2k_edge_ids[j].id]->from <<"->"<<rba_state.k2k_edges[new_k2k_edge_ids[j].id]->to<<", ";
204 #else
205  cout << rba_state.k2k_edges[new_k2k_edge_ids[j].id].from <<"->"<<rba_state.k2k_edges[new_k2k_edge_ids[j].id].to<<", ";
206 #endif
207  }
208  cout << endl;
209  }
210 
211  } // for each obs.
212 
213  }
214  break;
215 
216  // -----------------------------------------------------------
217  // Policy: Star Graph
218  // KFs have "global coordinates" (wrt KF 0), features are
219  // relative to their base KFs.
220  // -----------------------------------------------------------
221  case ecpStarGraph:
222  {
223  TNewEdgeInfo nei;
224 
225  const bool this_is_first = this->rba_state.k2k_edges.empty();
226 
227  // Edge: #0 -> NEW_KF
228  nei.id = this->create_kf2kf_edge(new_kf_id, TPairKeyFrameID(0, new_kf_id), obs);
229 
230  // Idea: the new KF should be close to the last one.
231  if (!this_is_first) {
232  nei.has_aprox_init_val = true; // Use pose of the last edge since the new one should be close.
233 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
234  this->rba_state.k2k_edges[nei.id]->inv_pose = this->rba_state.k2k_edges[nei.id-1]->inv_pose;
235 #else
236  this->rba_state.k2k_edges[nei.id].inv_pose = this->rba_state.k2k_edges[nei.id-1].inv_pose;
237 #endif
238  }
239  else {
240  nei.has_aprox_init_val = false;
241  }
242 
243  new_k2k_edge_ids.push_back(nei);
244  }
245  break;
246 
247  default:
248  THROW_EXCEPTION("Unknown value for 'parameters.edge_creation_policy'!")
249  };
250 
251 } // end of RbaEngine::determine_kf2kf_edges_to_create
252 
253 
254 /** (Aux method) Make a list of base KFs of my new observations, ordered in descending order by # of shared observations: */
255 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS>
257  const typename traits_t::new_kf_observations_t & obs,
258  base_sorted_lst_t & obs_for_each_base_sorted,
259  std::map<TKeyFrameID,size_t> *out_obs_for_each_base ) const
260 {
261  using namespace std;
262  // Make a first pass to make a sorted list of base KFs, ordered by # of observations so we prefer edges to
263  // strongly connected base KFs:
264  map<TKeyFrameID,size_t> obs_for_each_base;
265 
266  for (typename traits_t::new_kf_observations_t::const_iterator itObs=obs.begin();itObs!=obs.end();++itObs)
267  {
268  const TLandmarkID lm_id = itObs->obs.feat_id;
269  if (lm_id>=rba_state.all_lms.size()) continue; // It's a new LM
270 
271  const typename landmark_traits<LM_TYPE>::TLandmarkEntry &lme = rba_state.all_lms[lm_id];
272  if (!lme.rfp) continue; // It's a new LM.
273 
274  const TKeyFrameID base_id = lme.rfp->id_frame_base;
275  obs_for_each_base[base_id]++; // vote for this.
276  }
277 
278  // Sort map<> by values:
279  for (map<TKeyFrameID,size_t>::const_iterator it=obs_for_each_base.begin();it!=obs_for_each_base.end();++it)
280  obs_for_each_base_sorted.insert( make_pair(it->second,it->first) );
281 
282  if (out_obs_for_each_base)
283  out_obs_for_each_base->swap(obs_for_each_base);
284 }
285 
286 
287 } } // end namespaces
virtual void edge_creation_policy(const TKeyFrameID new_kf_id, const typename traits_t::new_kf_observations_t &obs, std::vector< TNewEdgeInfo > &new_k2k_edge_ids)
Implements the edge-creation policy, by default depending on "parameters.edge_creation_policy" if the...
uint64_t TLandmarkID
Numeric IDs for landmarks.
Definition: srba_types.h:32
#define THROW_EXCEPTION(msg)
The sub-map method introduced in the ICRA2013 paper.
Definition: srba_types.h:166
STL namespace.
const Scalar * const_iterator
Definition: eigen_plugins.h:24
KF2KF_POSE_TYPE::pose_t pose_t
The type of relative poses (e.g. mrpt::poses::CPose3D)
Definition: RbaEngine.h:87
bool has_aprox_init_val
Whether the edge was assigned an approximated initial value.
Definition: srba_types.h:201
uint64_t topo_dist_t
Unsigned integral type for topological distances in a graph/tree.
Definition: srba_types.h:33
std::deque< new_kf_observation_t > new_kf_observations_t
A set of all the observations made from a new KF, as provided by the user.
Definition: srba_types.h:422
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Each keyframe is only connected to its predecessor (no loop closures)
Definition: srba_types.h:168
void make_ordered_list_base_kfs(const typename traits_t::new_kf_observations_t &obs, base_sorted_lst_t &obs_for_each_base_sorted, std::map< TKeyFrameID, size_t > *out_obs_for_each_base=NULL) const
Make a list of base KFs of my new observations, ordered in descending order by # of shared observatio...
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
std::pair< TKeyFrameID, TKeyFrameID > TPairKeyFrameID
Used to represent the IDs of a directed edge (first –> second)
Definition: srba_types.h:34
The argument "LM_TRAITS" can be any of those defined in srba/models/landmarks.h (typically, either landmarks::Euclidean3D or landmarks::Euclidean2D).
Definition: srba_types.h:100
#define VERBOSE_LEVEL(_LEVEL)
Definition: RbaEngine.h:25
#define ASSERT_(f)
size_t id
The new edge ID.
Definition: srba_types.h:198
Used in TNewKeyFrameInfo.
Definition: srba_types.h:196
std::multimap< size_t, TKeyFrameID, std::greater< size_t > > base_sorted_lst_t
Definition: RbaEngine.h:475
All keyframes are connected to the first one (it can be used to emulate global coordinates) ...
Definition: srba_types.h:170
#define ASSERTMSG_(f, __ERROR_MSG)
uint64_t TKeyFrameID
Numeric IDs for key-frames (KFs)
Definition: srba_types.h:31



Page generated by Doxygen 1.8.9.1 for MRPT 1.3.0 SVN: at Sun Sep 13 03:55:12 UTC 2015