SRBA: Sparser Relative Bundle Adjustment
srba/impl/determine_kf2kf_edges_to_create.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 
00012 namespace srba {
00013 
00014 
00016 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS>
00017 void RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::determine_kf2kf_edges_to_create(
00018     const TKeyFrameID               new_kf_id,
00019     const typename traits_t::new_kf_observations_t   & obs,
00020     std::vector<TNewEdgeInfo> &new_k2k_edge_ids )
00021 {
00022     // (1) Invoke edge creation policy
00023     // ------------------------------------------------
00024     new_k2k_edge_ids.clear();
00025     if (rba_state.keyframes.size()==1)
00026         return; // If this is the first KF, there's no other one to which we can connect! -> Return an empty set of edges.
00027     
00028     edge_creation_policy.template eval<traits_t,rba_engine_t>(new_kf_id,obs,new_k2k_edge_ids, *this,parameters.ecp);
00029 
00030     // (2) Common part: 
00031     // try to figure out the initial relative poses of those kf2kf edges whose relative pose was not guessed by the ECP.
00032     // ----------------------------------------------------------
00033 
00034     // For each edge in "new_k2k_edge_ids"
00035     const size_t nNEI = new_k2k_edge_ids.size();
00036     for (size_t i=0;i<nNEI;i++)
00037     {
00038         TNewEdgeInfo & nei = new_k2k_edge_ids[i];
00039         if (nei.has_approx_init_val)
00040             continue; // This edge already has an initial guess
00041         
00042         // New edges are: FROM (old KF) ==> TO (new KF)
00043         // In loop closures, neither "nei.to" nor "nei.from" are the latest KF, both may be existing center KFs:
00044         k2k_edge_t & nei_edge = rba_state.k2k_edges[nei.id];
00045         const bool nei_edge_does_not_touch_cur_kf = (nei_edge.to!=new_kf_id) && (nei_edge.from!=new_kf_id);
00046         
00047         // Method #1: look at last kf's kf2kf edges for an initial guess to ease optimization:
00048         if ( !nei_edge_does_not_touch_cur_kf && rba_state.last_timestep_touched_kfs.count(nei_edge.from) != 0 )
00049         {
00050             const pose_t *rel_pose = get_kf_relative_pose(new_kf_id-1, nei_edge.from);
00051             if (rel_pose)
00052             {
00053                 // Found: reuse this relative pose as a good initial guess for the estimation
00054                 const bool edge_dir_to_newkf  = (nei_edge.to==new_kf_id);
00055                 if (edge_dir_to_newkf)
00056                      nei_edge.inv_pose = - (*rel_pose); // Note the "-" inverse operator, it is important
00057                 else nei_edge.inv_pose =    *rel_pose;
00058                 nei.has_approx_init_val = true;
00059             }
00060         }
00061 
00062         // Method #2: use relative pose from sensor model implementation, if provided:
00063         // (1st attempt) Direct relative pose between the two KFs at each end of the new edge.
00064         if (!nei.has_approx_init_val)
00065         {
00066             // Landmarks in this new KF are in `obs`: const typename traits_t::new_kf_observations_t   & obs
00067             // Landmarks in the old reference KF are in: `other_k2f_edges`
00068             TKeyFrameID last_kf_id, other_kf_id;  // Required to tell if we have to take observations from "k2f_edges" or from the latest "obs":
00069             if (nei_edge_does_not_touch_cur_kf)
00070             {  // Arbitrarily pick "last" and "other" as "from" and "to":
00071                 last_kf_id   = nei_edge.from;
00072                 other_kf_id  = nei_edge.to;
00073             }
00074             else
00075             {  // Pick the latest and the "other" kf
00076                 last_kf_id  = new_kf_id;
00077                 other_kf_id = (nei_edge.to==new_kf_id) ? nei_edge.from : nei_edge.to;
00078             }
00079 
00080             const std::deque<k2f_edge_t*>  & other_k2f_edges   = rba_state.keyframes[other_kf_id].adjacent_k2f_edges;
00081 
00082             // Make two lists of equal length with corresponding observations 
00083             // (i.e. new_kf_obs[i] and old_kf_obs[i] correspond to observations of the same landmark from different poses)
00084             typename mrpt::aligned_containers<typename obs_t::obs_data_t>::vector_t  new_kf_obs, old_kf_obs;
00085 
00086             {
00087                 new_kf_obs.reserve(obs.size());  // maximum potential size: all observed features match against one old KF
00088                 old_kf_obs.reserve(obs.size());
00089 
00090                 // Temporary construction: associative container with all observed LMs in this new KF:
00091                 std::map<TLandmarkID,size_t> newkf_obs_feats;
00092                 const std::deque<k2f_edge_t*>  * last_k2f_edges   = NULL;
00093                 if (nei_edge_does_not_touch_cur_kf)
00094                 {
00095                     last_k2f_edges   = &rba_state.keyframes[last_kf_id].adjacent_k2f_edges;
00096                     for (size_t i=0;i<last_k2f_edges->size();i++) {
00097                         const TLandmarkID lm_id = (*last_k2f_edges)[i]->obs.obs.feat_id;
00098                         newkf_obs_feats[ lm_id ] = i;
00099                     }
00100                 }
00101                 else {
00102                     for (size_t i=0;i<obs.size();i++)
00103                         newkf_obs_feats[ obs[i].obs.feat_id ] = i;
00104                 }
00105 
00106                 // Search in `other_k2f_edges`:
00107                 for (size_t i=0;i<other_k2f_edges.size();i++) 
00108                 {
00109                     const TLandmarkID lm_id = other_k2f_edges[i]->obs.obs.feat_id;
00110                     std::map<TLandmarkID,size_t>::const_iterator it_id = newkf_obs_feats.find(lm_id);
00111                     if (it_id == newkf_obs_feats.end()) 
00112                         continue; // No matching feature
00113                     // Yes, we have a match:
00114                     old_kf_obs.push_back( other_k2f_edges[i]->obs.obs.obs_data );
00115                     if (nei_edge_does_not_touch_cur_kf)
00116                          new_kf_obs.push_back( (*last_k2f_edges)[it_id->second]->obs.obs.obs_data );
00117                     else new_kf_obs.push_back( obs[it_id->second].obs.obs_data );
00118                 }
00119             }
00120 
00121 
00122             // (1st attempt) Run matcher:
00123             pose_t pose_new_kf_wrt_old_kf;
00124             m_profiler.enter("define_new_keyframe.determine_edges.lm_matcher");
00125             bool found_ok = srba::observations::landmark_matcher<obs_t>::find_relative_pose(new_kf_obs, old_kf_obs, parameters.sensor,pose_new_kf_wrt_old_kf);
00126             m_profiler.leave("define_new_keyframe.determine_edges.lm_matcher");
00127 
00128             // (2nd attempt) Run matcher between another set of KFs, only possible in the case of a loop closure:
00129             if (!found_ok && nei.loopclosure_observer_kf!=SRBA_INVALID_KEYFRAMEID && nei.loopclosure_base_kf!=SRBA_INVALID_KEYFRAMEID)
00130             {
00131                 // We may have up to 4 KFs involved here: 
00132                 // - nei.loopclosure_observer_kf
00133                 // - nei.loopclosure_base_kf
00134                 // - nei_edge.to
00135                 // - nei_edge.from
00136                 // Any of the latter 2 *might* coincide with the former 2 KFs.
00137                 // We already tried to find a match between "to" and "from" without luck, 
00138                 // so let's try now with the former pair.
00139                 last_kf_id  = nei.loopclosure_observer_kf;
00140                 other_kf_id = nei.loopclosure_base_kf;
00141 
00142                 const std::deque<k2f_edge_t*>  & other2_k2f_edges   = rba_state.keyframes[other_kf_id].adjacent_k2f_edges;
00143                 const bool last_kf_is_new_kf = (last_kf_id==new_kf_id);
00144 
00145                 // Temporary construction: associative container with all observed LMs in this new KF:
00146                 new_kf_obs.clear();
00147                 old_kf_obs.clear();
00148                 std::map<TLandmarkID,size_t> newkf_obs_feats;
00149                 const std::deque<k2f_edge_t*>  * last_k2f_edges   = NULL;
00150                 if (!last_kf_is_new_kf)
00151                 {
00152                     last_k2f_edges   = &rba_state.keyframes[last_kf_id].adjacent_k2f_edges;
00153                     for (size_t i=0;i<last_k2f_edges->size();i++) {
00154                         const TLandmarkID lm_id = (*last_k2f_edges)[i]->obs.obs.feat_id;
00155                         newkf_obs_feats[ lm_id ] = i;
00156                     }
00157                 }
00158                 else {
00159                     for (size_t i=0;i<obs.size();i++)
00160                         newkf_obs_feats[ obs[i].obs.feat_id ] = i;
00161                 }
00162 
00163                 // Search in `other2_k2f_edges`:
00164                 for (size_t i=0;i<other2_k2f_edges.size();i++) 
00165                 {
00166                     const TLandmarkID lm_id = other2_k2f_edges[i]->obs.obs.feat_id;
00167                     std::map<TLandmarkID,size_t>::const_iterator it_id = newkf_obs_feats.find(lm_id);
00168                     if (it_id == newkf_obs_feats.end()) 
00169                         continue; // No matching feature
00170                     // Yes, we have a match:
00171                     old_kf_obs.push_back( other2_k2f_edges[i]->obs.obs.obs_data );
00172                     if (!last_kf_is_new_kf)
00173                          new_kf_obs.push_back( (*last_k2f_edges)[it_id->second]->obs.obs.obs_data );
00174                     else new_kf_obs.push_back( obs[it_id->second].obs.obs_data );
00175                 }
00176 
00177                 // Run matcher:
00178                 m_profiler.enter("define_new_keyframe.determine_edges.lm_matcher");
00179                 found_ok = srba::observations::landmark_matcher<obs_t>::find_relative_pose(new_kf_obs, old_kf_obs, parameters.sensor,pose_new_kf_wrt_old_kf);
00180                 m_profiler.leave("define_new_keyframe.determine_edges.lm_matcher");
00181             } // end 2nd attempt
00182 
00183             if (found_ok)
00184             {
00185                 // Take into account the sensor pose wrt the KF: Rotate/translate if the sensor is not at the robot origin of coordinates: 
00186                 mrpt::poses::CPose3D sensor_pose;
00187                 RBA_OPTIONS::sensor_pose_on_robot_t::template robot2sensor<mrpt::poses::CPose3D>(mrpt::poses::CPose3D(), sensor_pose, this->parameters.sensor_pose);
00188                 pose_new_kf_wrt_old_kf = pose_t( (sensor_pose + pose_new_kf_wrt_old_kf)+ (-sensor_pose) );
00189 
00190                 const bool edge_dir_to_newkf  = (nei_edge.to==new_kf_id);
00191                 nei.has_approx_init_val = true;
00192 
00193                 // Found: reuse this relative pose as a good initial guess for the estimation
00194                 if (!nei_edge_does_not_touch_cur_kf)
00195                 { // Found relative pose is directly for the two KFs at each end of the new kf2kf edge:
00196                     if (edge_dir_to_newkf)
00197                          nei_edge.inv_pose = - pose_new_kf_wrt_old_kf;
00198                     else nei_edge.inv_pose =   pose_new_kf_wrt_old_kf;
00199                 }
00200                 else
00201                 {   // Found relative pose is for the "2nd attempt" approach in loop closures, so 
00202                     // we must now transform "pose_new_kf_wrt_old_kf" into "nei_edge.inv_pose":
00203                     //
00204                     // loopclosure_observer_kf  <============   loopclosure_base_kf
00205                     //       ^              pose_new_kf_wrt_old_kf           ^
00206                     //       |                                               |
00207                     //       | pose_observer_wrt_local                       | pose_base_wrt_remote
00208                     //       |                                               |
00209                     //       |                 nei_edge.inv_pose             |
00210                     //       +--- TO or FROM  <======?======>  FROM or TO ---+
00211                     //           local_kf_id                  remote_kf_id
00212                     //
00213                     ASSERT_(nei.loopclosure_observer_kf!=SRBA_INVALID_KEYFRAMEID && nei.loopclosure_base_kf!=SRBA_INVALID_KEYFRAMEID);
00214 
00215                     const pose_t default_identity_pose;
00216 
00217                     const pose_t *pose_observer_wrt_to   = (nei.loopclosure_observer_kf==nei_edge.to)   ? &default_identity_pose : get_kf_relative_pose(nei.loopclosure_observer_kf, nei_edge.to);
00218                     const pose_t *pose_base_wrt_to       = (nei.loopclosure_base_kf==nei_edge.to)       ? &default_identity_pose : get_kf_relative_pose(nei.loopclosure_base_kf    , nei_edge.to);
00219                     const pose_t *pose_observer_wrt_from = (nei.loopclosure_observer_kf==nei_edge.from) ? &default_identity_pose : get_kf_relative_pose(nei.loopclosure_observer_kf, nei_edge.from);
00220                     const pose_t *pose_base_wrt_from     = (nei.loopclosure_base_kf==nei_edge.from)     ? &default_identity_pose : get_kf_relative_pose(nei.loopclosure_base_kf    , nei_edge.from);
00221                     
00222                     const bool observer_is_near_to = (pose_observer_wrt_to || pose_base_wrt_from) || !(pose_observer_wrt_from || pose_base_wrt_to);
00223                     const TKeyFrameID local_kf_id  =  observer_is_near_to ? nei_edge.to   : nei_edge.from;
00224                     const TKeyFrameID remote_kf_id =  observer_is_near_to ? nei_edge.from : nei_edge.to;
00225 
00226                     const pose_t *pose_observer_wrt_local   = observer_is_near_to ? 
00227                         (pose_observer_wrt_to ? pose_observer_wrt_to : &default_identity_pose)
00228                         : 
00229                         (pose_observer_wrt_from ? pose_observer_wrt_from : &default_identity_pose);
00230                     const pose_t *pose_base_wrt_remote   = observer_is_near_to ? 
00231                         (pose_base_wrt_from ? pose_base_wrt_from : &default_identity_pose)
00232                         : 
00233                         (pose_base_wrt_to ? pose_base_wrt_to : &default_identity_pose);
00234                     
00235                     // Pose transforms (from the graph of poses in the ASCII art above):
00236                     //
00237                     // Nwr = inv(BwR) * LwR * OwL
00238                     // BwR * Nwr = LwR * OwL
00239                     // BwR * Nwr * inv(OwL) = LwR
00240                     //
00241                     const pose_t pose_local_wrt_remote  =  ((*pose_base_wrt_remote)+(pose_new_kf_wrt_old_kf))+(-(*pose_observer_wrt_local));
00242 
00243                     if (edge_dir_to_newkf)
00244                          nei_edge.inv_pose = - pose_local_wrt_remote;
00245                     else nei_edge.inv_pose =   pose_local_wrt_remote;
00246                 }
00247             }
00248 
00249             if (!nei.has_approx_init_val)
00250             { // Otherwise: we cannot provide any reasonable initial value, which may degrade performance...
00251                 VERBOSE_LEVEL_COLOR(2,mrpt::system::CONCOL_RED) << "[determine_kf2kf_edges_to_create] Could not provide initial value to relative pose " <<nei_edge.from << "<=>" << nei_edge.to << "\n";
00252                 VERBOSE_LEVEL_COLOR_POST();
00253             }
00254         }
00255     } // end for each nei
00256 
00257     // save for the next timestep:
00258     rba_state.last_timestep_touched_kfs.clear();
00259     for (size_t i=0;i<nNEI;i++) {
00260         const k2k_edge_t & nei_edge = rba_state.k2k_edges[new_k2k_edge_ids[i].id];
00261         rba_state.last_timestep_touched_kfs.insert( nei_edge.from );
00262         rba_state.last_timestep_touched_kfs.insert( nei_edge.to );
00263     }
00264 
00265 
00266 }
00267 
00268 } // End of namespaces
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends