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 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