SRBA: Sparser Relative Bundle Adjustment
srba/ecps/local_areas_fixed_size.h
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 &params) 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 &params)
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends