SRBA: Sparser Relative Bundle Adjustment
|
00001 /* +---------------------------------------------------------------------------+ 00002 | Mobile Robot Programming Toolkit (MRPT) | 00003 | http://www.mrpt.org/ | 00004 | | 00005 | Copyright (c) 2005-2015, Individual contributors, see AUTHORS file | 00006 | See: http://www.mrpt.org/Authors - All rights reserved. | 00007 | Released under BSD License. See details in http://www.mrpt.org/License | 00008 +---------------------------------------------------------------------------+ */ 00009 00010 #pragma once 00011 #include <mrpt/utils/CConfigFileBase.h> // MRPT_LOAD_CONFIG_VAR 00012 00013 namespace srba { 00014 namespace ecps { 00015 00022 struct local_areas_fixed_size 00023 { 00024 struct parameters_t 00025 { 00026 size_t submap_size; 00027 size_t min_obs_to_loop_closure; 00028 00030 parameters_t() : 00031 submap_size ( 15 ), 00032 min_obs_to_loop_closure ( 4 ) 00033 { } 00034 00036 void loadFromConfigFile(const mrpt::utils::CConfigFileBase & source,const std::string & section) 00037 { 00038 MRPT_LOAD_CONFIG_VAR(submap_size,uint64_t,source,section) 00039 MRPT_LOAD_CONFIG_VAR(min_obs_to_loop_closure,uint64_t,source,section) 00040 } 00041 00043 void saveToConfigFile(mrpt::utils::CConfigFileBase & out,const std::string & section) const 00044 { 00045 out.write(section,"submap_size",static_cast<uint64_t>(submap_size), /* text width */ 30, 30, "Max. local optimization distance"); 00046 out.write(section,"min_obs_to_loop_closure",static_cast<uint64_t>(min_obs_to_loop_closure), /* text width */ 30, 30, "Min. num. of covisible observations to add a loop closure edge"); 00047 } 00048 }; 00049 00051 TKeyFrameID get_center_kf_for_kf(const TKeyFrameID kf_id, const parameters_t ¶ms) const 00052 { 00053 const size_t SUBMAP_SIZE = params.submap_size; // In # of KFs 00054 return SUBMAP_SIZE*(kf_id/SUBMAP_SIZE); 00055 } 00056 00060 template <class traits_t,class rba_engine_t> 00061 void eval( 00062 const TKeyFrameID new_kf_id, 00063 const typename traits_t::new_kf_observations_t & obs, 00064 std::vector<TNewEdgeInfo> &new_k2k_edge_ids, 00065 rba_engine_t & rba_engine, 00066 const parameters_t ¶ms) 00067 { 00068 using namespace std; 00069 ASSERT_(new_kf_id>=1) // We can run an ECP only if we have 2 KFs in the map 00070 00071 const size_t MINIMUM_OBS_TO_LOOP_CLOSURE = params.min_obs_to_loop_closure; 00072 const TKeyFrameID current_center_kf_id = get_center_kf_for_kf(new_kf_id, params); 00073 const topo_dist_t min_dist_for_loop_closure = rba_engine.parameters.srba.max_tree_depth + 1; // By definition of loop closure in the SRBA framework 00074 00075 // Go thru all observations and for those already-seen LMs, check the distance between their base KFs and (i_id): 00076 // Make a list of base KFs of my new observations, ordered in descending order by # of shared observations: 00077 base_sorted_lst_t obs_for_each_base_sorted; 00078 srba::internal::make_ordered_list_base_kfs<traits_t,typename rba_engine_t::rba_problem_state_t>(obs, rba_engine.get_rba_state(), obs_for_each_base_sorted); 00079 00080 // Make vote list for each central KF: 00081 map<TKeyFrameID,size_t> obs_for_each_area; 00082 map<TKeyFrameID,bool> base_is_center_for_all_obs_in_area; // Detect whether the base KF for observations is the area center or not (needed to determine exact worst-case topological distances) 00083 map<TKeyFrameID,map<TKeyFrameID,size_t> > obs_for_base_KF_grouped_by_area; 00084 for (base_sorted_lst_t::const_iterator it=obs_for_each_base_sorted.begin();it!=obs_for_each_base_sorted.end();++it) 00085 { 00086 const size_t num_obs_this_base = it->first; 00087 const TKeyFrameID base_id = it->second; 00088 00089 const TKeyFrameID this_localmap_center = get_center_kf_for_kf(base_id, params); 00090 obs_for_each_area[this_localmap_center] += num_obs_this_base; 00091 obs_for_base_KF_grouped_by_area[this_localmap_center][base_id] += num_obs_this_base; 00092 00093 // Fist time this area is observed? 00094 if (base_is_center_for_all_obs_in_area.find(this_localmap_center)==base_is_center_for_all_obs_in_area.end()) 00095 base_is_center_for_all_obs_in_area[this_localmap_center] = true; 00096 // Filter: 00097 if (base_id!=this_localmap_center) base_is_center_for_all_obs_in_area[this_localmap_center] = false; 00098 } 00099 00100 // Sort submaps by votes: 00101 base_sorted_lst_t obs_for_each_area_sorted; 00102 for (map<TKeyFrameID,size_t>::const_iterator it=obs_for_each_area.begin();it!=obs_for_each_area.end();++it) 00103 obs_for_each_area_sorted.insert( make_pair(it->second,it->first) ); 00104 00105 // Within each submap, sort by the most voted base KF, so we can detect the most connected KF in the case of a loop closure: 00106 map<TKeyFrameID,base_sorted_lst_t> obs_for_base_KF_grouped_by_area_sorted; 00107 for (map<TKeyFrameID,map<TKeyFrameID,size_t> >::const_iterator it=obs_for_base_KF_grouped_by_area.begin();it!=obs_for_base_KF_grouped_by_area.end();++it) 00108 { 00109 base_sorted_lst_t &bsl = obs_for_base_KF_grouped_by_area_sorted[it->first]; 00110 for (map<TKeyFrameID,size_t>::const_iterator it2=it->second.begin();it2!=it->second.end();++it2) 00111 bsl.insert( make_pair(it2->second,it2->first) ); 00112 } 00113 00114 // First: always create one edge: 00115 // Regular KFs: new KF ==> current_center_kf_id 00116 // New area center: new KF (=current_center_kf_id) ==> center of previous 00117 { 00118 if (current_center_kf_id == new_kf_id) { 00119 // We are about to start an empty, new area: link with the most connected area (in the general code above) 00120 } 00121 else { 00122 // Connect to the local area center: 00123 TNewEdgeInfo nei; 00124 nei.id = rba_engine.create_kf2kf_edge(new_kf_id, TPairKeyFrameID( current_center_kf_id, new_kf_id), obs); 00125 nei.has_approx_init_val = false; // By default: Will need to estimate this one 00126 00127 // Add to list of newly created kf2kf edges: 00128 new_k2k_edge_ids.push_back(nei); 00129 } 00130 } 00131 00132 // Go thru candidate areas for loop closures: 00133 for (base_sorted_lst_t::const_iterator it=obs_for_each_area_sorted.begin();it!=obs_for_each_area_sorted.end();++it) 00134 { 00135 const size_t num_obs_this_base = it->first; 00136 const TKeyFrameID remote_center_kf_id = it->second; 00137 const bool is_strongest_connected_edge = (it==obs_for_each_area_sorted.begin()); // Is this the first one? 00138 00139 //VERBOSE_LEVEL(2) << "[edge_creation_policy] Consider: area central kf#"<< remote_center_kf_id << " with #obs:"<< num_obs_this_base << endl; 00140 00141 // Create edges to all these central KFs if they're too far: 00142 00143 // Find the distance between "remote_center_kf_id" <=> "new_kf_id" 00144 const TKeyFrameID from_id = current_center_kf_id; //new_kf_id; 00145 const TKeyFrameID to_id = remote_center_kf_id; 00146 if (from_id==to_id) 00147 continue; // We are observing a LM within our local submap; it is fine. 00148 00149 typename rba_engine_t::rba_problem_state_t::TSpanningTree::next_edge_maps_t::const_iterator it_from = rba_engine.get_rba_state().spanning_tree.sym.next_edge.find(from_id); 00150 00151 topo_dist_t found_distance = numeric_limits<topo_dist_t>::max(); 00152 00153 if (it_from != rba_engine.get_rba_state().spanning_tree.sym.next_edge.end()) 00154 { 00155 const map<TKeyFrameID,TSpanTreeEntry> &from_Ds = it_from->second; 00156 map<TKeyFrameID,TSpanTreeEntry>::const_iterator it_to_dist = from_Ds.find(to_id); 00157 00158 if (it_to_dist != from_Ds.end()) 00159 found_distance = it_to_dist->second.distance; 00160 } 00161 else 00162 { 00163 // The new KF doesn't still have any edge created to it, that's why we didn't found any spanning tree for it. 00164 // Since this means that the KF is aisolated from the rest of the world, leave the topological distance to infinity. 00165 } 00166 00167 // We may have to add the 2 edges: 00168 // OBSERVER_KF ==(1)==> CENTER1->CENTER2 ===(2)==> BASE_KF 00169 // to determine the exact topological distance to the base of the currently observed LMs and whether a loop closure actually happened. 00170 topo_dist_t dist_extra_edges = 2; 00171 if (current_center_kf_id == new_kf_id) dist_extra_edges--; 00172 if (base_is_center_for_all_obs_in_area[remote_center_kf_id]) dist_extra_edges--; 00173 00174 if ( found_distance >= min_dist_for_loop_closure - dist_extra_edges ) // Note: DO NOT sum `dist_extra_edges` to the left side of the equation, since found_distance may be numeric_limits::max<>!! 00175 { 00176 if (num_obs_this_base>=MINIMUM_OBS_TO_LOOP_CLOSURE) 00177 { 00178 // The KF is TOO FAR: We will need to create an additional edge: 00179 TNewEdgeInfo nei; 00180 00181 nei.id = rba_engine.create_kf2kf_edge(from_id, TPairKeyFrameID( to_id, from_id), obs); 00182 nei.has_approx_init_val = false; // By default: Will need to estimate this one 00183 00184 // Fill these loop closure helper fields: 00185 nei.loopclosure_observer_kf = new_kf_id; 00186 { 00187 // Take the KF id of the strongest connection: 00188 const base_sorted_lst_t & bsl = obs_for_base_KF_grouped_by_area_sorted[remote_center_kf_id]; 00189 ASSERT_(!bsl.empty()); 00190 nei.loopclosure_base_kf = bsl.begin()->second; 00191 } 00192 new_k2k_edge_ids.push_back(nei); 00193 } 00194 else { 00195 //VERBOSE_LEVEL(1) << "[edge_creation_policy] Skipped extra edge " << remote_center_kf_id <<"->"<<new_kf_id << " with #obs: "<< num_obs_this_base << " and already_connected="<< (already_connected?"TRUE":"FALSE") << endl; 00196 } 00197 } 00198 } 00199 00200 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) )) 00201 00202 // Debug: 00203 if (new_k2k_edge_ids.size()>1) // && m_verbose_level>=1) 00204 { 00205 mrpt::system::setConsoleColor(mrpt::system::CONCOL_GREEN); 00206 cout << "\n[edge_creation_policy] Loop closure detected for KF#"<< new_kf_id << ", edges: "; 00207 for (size_t j=0;j<new_k2k_edge_ids.size();j++) 00208 cout << rba_engine.get_rba_state().k2k_edges[new_k2k_edge_ids[j].id].from <<"->"<<rba_engine.get_rba_state().k2k_edges[new_k2k_edge_ids[j].id].to<<", "; 00209 cout << endl; 00210 mrpt::system::setConsoleColor(mrpt::system::CONCOL_NORMAL); 00211 } 00212 00213 } // end eval<>() 00214 00215 }; // end of struct 00216 00217 } } // End of namespaces