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 #define OBS_SUPER_VERBOSE 0 00015 00016 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS> 00017 size_t RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::add_observation( 00018 const TKeyFrameID observing_kf_id, 00019 const typename observation_traits<obs_t>::observation_t & new_obs, 00020 const array_landmark_t * fixed_relative_position, 00021 const array_landmark_t * unknown_relative_position_init_val 00022 ) 00023 { 00024 m_profiler.enter("add_observation"); 00025 00026 ASSERT_( !( fixed_relative_position!=NULL && unknown_relative_position_init_val!=NULL) ) // Both can't be !=NULL at once. 00027 00028 const bool is_1st_time_seen = ( new_obs.feat_id>=rba_state.all_lms.size() || rba_state.all_lms[new_obs.feat_id].rfp==NULL ); 00029 00030 const bool is_fixed = 00031 // This is the first observation of a fixed landmark: 00032 (fixed_relative_position!=NULL) 00033 || // or if it was observed before, get its feature type from stored structure: 00034 (!is_1st_time_seen && rba_state.all_lms[new_obs.feat_id].has_known_pos ); 00035 00036 // Append all new observation to raw vector: 00037 // --------------------------------------------------------------------- 00038 const size_t new_obs_idx = rba_state.all_observations.size(); // O(1) 00039 00040 rba_state.all_observations.push_back(k2f_edge_t()); // Create new k2f_edge -- O(1) 00041 rba_state.all_observations_Jacob_validity.push_back(1); // Also grow this vector (its content now are irrelevant, they'll be updated in optimization) 00042 char * const jacob_valid_bit = &(*rba_state.all_observations_Jacob_validity.rbegin()); 00043 00044 // Get a ref. to observation info, filled in below: 00045 k2f_edge_t & new_k2f_edge = *rba_state.all_observations.rbegin(); 00046 00047 // New landmark? Update LMs structures if this is the 1st time we see this landmark: 00048 // ----------------------------------------------------------------------- 00049 if (is_1st_time_seen) 00050 { 00051 if (is_fixed) 00052 { // LM with KNOWN relative position. 00053 // ---------------------------------- 00054 TRelativeLandmarkPos new_rfp; 00055 new_rfp.id_frame_base = observing_kf_id; // The current KF at creation time becomes the relative coordinate base. 00056 new_rfp.pos = *fixed_relative_position; 00057 00058 // O(1) insertion if the obs.feat_id is the largest until now: 00059 typename TRelativeLandmarkPosMap::iterator it_new = rba_state.known_lms.insert( 00060 rba_state.known_lms.end(), 00061 typename TRelativeLandmarkPosMap::value_type(new_obs.feat_id, new_rfp ) 00062 ); 00063 00064 // Add to list of all LMs: Amortized O(1) 00065 if (new_obs.feat_id >= rba_state.all_lms.size()) rba_state.all_lms.resize(new_obs.feat_id+1); 00066 rba_state.all_lms[new_obs.feat_id] = typename landmark_traits<landmark_t>::TLandmarkEntry(true /*known pos.*/, &it_new->second); 00067 } 00068 else 00069 { // LM with UNKNOWN relative position. 00070 // ---------------------------------- 00071 // O(1) insertion if the feat_ID is the largest until now: 00072 00073 TRelativeLandmarkPos new_rfp; 00074 new_rfp.id_frame_base = observing_kf_id; // The current KF at creation time becomes the relative coordinate base. 00075 if (unknown_relative_position_init_val) 00076 new_rfp.pos = *unknown_relative_position_init_val; 00077 else { 00078 // Default landmark position: Invoke sensor's inverse model. 00079 sensor_model_t::inverse_sensor_model(new_rfp.pos,new_obs.obs_data,this->parameters.sensor); 00080 00081 // Take into account the sensor pose wrt the KF: 00082 RBA_OPTIONS::sensor_pose_on_robot_t::template sensor2robot_point<landmark_t>(new_rfp.pos, this->parameters.sensor_pose ); 00083 } 00084 00085 typename TRelativeLandmarkPosMap::iterator it_new = rba_state.unknown_lms.insert( 00086 rba_state.unknown_lms.end(), 00087 typename TRelativeLandmarkPosMap::value_type( new_obs.feat_id, new_rfp ) 00088 ); 00089 00090 // Add to list of all LMs: 00091 if (new_obs.feat_id >= rba_state.all_lms.size()) rba_state.all_lms.resize(new_obs.feat_id+1); 00092 rba_state.all_lms[new_obs.feat_id] = typename landmark_traits<landmark_t>::TLandmarkEntry(false /*unknown pos.*/, &it_new->second); 00093 00094 // Expand Jacobian dh_df to accomodate a new column for a new unknown: 00095 // ("Remap indices" in dh_df for each column are the feature IDs of those feature with unknown positions) 00096 rba_state.lin_system.dh_df.appendCol( new_obs.feat_id ); 00097 } 00098 } 00099 00100 // Maintain a pointer to the relative position wrt its base keyframe: 00101 TRelativeLandmarkPos *lm_rel_pos = rba_state.all_lms[new_obs.feat_id].rfp; 00102 // and get the ID of the base keyframe for the observed landmark: 00103 const TKeyFrameID base_id = lm_rel_pos->id_frame_base; 00104 00105 // Fill in kf-to-feature edge data: 00106 // ------------------------------------ 00107 new_k2f_edge.obs.kf_id = observing_kf_id; 00108 new_k2f_edge.obs.obs = new_obs; 00109 new_k2f_edge.obs.obs.obs_data.getAsArray(new_k2f_edge.obs.obs_arr); // Save the observation data as an array now, only once, and reuse it from now on in optimizations, etc. 00110 new_k2f_edge.is_first_obs_of_unknown = is_1st_time_seen && !is_fixed; 00111 new_k2f_edge.feat_has_known_rel_pos = is_fixed; 00112 new_k2f_edge.feat_rel_pos = lm_rel_pos; 00113 00114 00115 // Update keyframe incident edge list: 00116 // --------------------------------------------------------------------- 00117 ASSERTDEB_( observing_kf_id < rba_state.keyframes.size() ) 00118 00119 keyframe_info &kf_info = rba_state.keyframes[ observing_kf_id ]; // O(1) 00120 00121 // Incident edges: 00122 kf_info.adjacent_k2f_edges.push_back( &new_k2f_edge ); // Amortized O(1) 00123 00124 00125 // Update linear system: 00126 00127 // If the observed feat has a known rel. pos., only dh_dAp; otherwise, both dh_dAp and dh_df 00128 // --------------------------------------------------------------------- 00129 // Add a new (block) row for this new observation (row index = "new_obs_idx" defined above) 00130 // We must create a block for each edge in between the observing and the ref. base id. 00131 // Note: no error checking here in find's for efficiency... 00132 m_profiler.enter("add_observation.jacobs.sym"); 00133 00134 // =========================== 00135 // Jacob 1/2: dh_dAp 00136 // =========================== 00137 bool graph_says_ignore_this_obs = false; 00138 00139 if (base_id!=new_k2f_edge.obs.kf_id) // If this is a feat with unknown rel.pos. and it's its first observation, the dh_dAp part of the Jacobian is empty. 00140 { 00141 #if OBS_SUPER_VERBOSE 00142 std::cout << "Jacobian dh_dAp for obs #"<<new_obs_idx << " has these edges [obs_kf="<<observing_kf_id<< " base_kf="<< base_id<<"]:\n"; 00143 #endif 00144 00145 // Since "all_edges" is symmetric, only the (i,j), i>j entries are stored: 00146 const TKeyFrameID from = std::max(observing_kf_id, base_id); 00147 const TKeyFrameID to = std::min(observing_kf_id, base_id); 00148 00149 const bool all_edges_inverse = (from!=observing_kf_id); 00150 00151 typename rba_problem_state_t::TSpanningTree::all_edges_maps_t::const_iterator it_map = rba_state.spanning_tree.sym.all_edges.find(from); // Was: observing 00152 ASSERTMSG_(it_map != rba_state.spanning_tree.sym.all_edges.end(), mrpt::format("No ST.all_edges found for observing_id=%u, base_id=%u", static_cast<unsigned int>(observing_kf_id), static_cast<unsigned int>(base_id) ) ) 00153 00154 typename std::map<TKeyFrameID, typename rba_problem_state_t::k2k_edge_vector_t >::const_iterator it_obs_ed = it_map->second.find(to); 00155 //ASSERTMSG_(it_obs_ed != it_map->second.end(), mrpt::format("No spanning-tree found from KF #%u to KF #%u, base of observation of landmark #%u", static_cast<unsigned int>(observing_kf_id),static_cast<unsigned int>(base_id),static_cast<unsigned int>(new_obs.feat_id) )) 00156 00157 if (it_obs_ed != it_map->second.end()) 00158 { 00159 const typename rba_problem_state_t::k2k_edge_vector_t & obs_edges = it_obs_ed->second; 00160 ASSERT_(!obs_edges.empty()) 00161 00162 TKeyFrameID curKF = observing_kf_id; // The backward running index towards "base_id" 00163 for (size_t j=0;j<obs_edges.size();j++) 00164 { 00165 const size_t i = all_edges_inverse ? (obs_edges.size()-j-1) : j; 00166 00167 ASSERT_(curKF!=to) 00168 00169 const size_t edge_id = obs_edges[i]->id; 00170 00171 // Normal direction: from -> to, so "to"=="curKF" (we go backwards) 00172 const bool normal_dir = (obs_edges[i]->to==curKF); 00173 00174 #if OBS_SUPER_VERBOSE 00175 std::cout << " * edge #"<<edge_id<< ": "<<obs_edges[i]->from <<" => "<<obs_edges[i]->to << " (inverse: " << (normal_dir ? "no":"yes") << ")\n"; 00176 #endif 00177 00178 // Get sparse block column: 00179 typename TSparseBlocksJacobians_dh_dAp::col_t & col = rba_state.lin_system.dh_dAp.getCol(edge_id); 00180 00181 // Create new entry: O(1) optimized insert if obs_idx is the largest index (as it will normally be): 00182 typename TSparseBlocksJacobians_dh_dAp::col_t::iterator it_new_entry = col.insert( 00183 col.end(), 00184 typename TSparseBlocksJacobians_dh_dAp::col_t::value_type( new_obs_idx, typename TSparseBlocksJacobians_dh_dAp::TEntry() ) 00185 ); 00186 00187 typename TSparseBlocksJacobians_dh_dAp::TEntry & entry = it_new_entry->second; 00188 00189 entry.sym.obs_idx = new_obs_idx; 00190 entry.sym.is_valid = jacob_valid_bit; 00191 entry.sym.edge_normal_dir = normal_dir; 00192 entry.sym.kf_d = curKF; 00193 entry.sym.kf_base = base_id; 00194 entry.sym.feat_rel_pos = lm_rel_pos; 00195 entry.sym.k2k_edge_id = edge_id; 00196 //entry.sym.rel_poses_path = &obs_edges; // Path OBSERVING_KF -> BASE_KF 00197 00198 // Pointers to placeholders of future numeric results of the spanning tree: 00199 entry.sym.rel_pose_base_from_d1 = & rba_state.spanning_tree.num[curKF][base_id]; 00200 entry.sym.rel_pose_d1_from_obs = 00201 (curKF==observing_kf_id) ? 00202 NULL // Use special value "NULL" when the CPose is fixed to the origin. 00203 : 00204 & rba_state.spanning_tree.num[observing_kf_id][curKF]; 00205 00206 // next node after this edge is: 00207 curKF = normal_dir ? obs_edges[i]->from : obs_edges[i]->to; 00208 } 00209 } 00210 else 00211 { 00212 // Ignore this obs... 00213 graph_says_ignore_this_obs=true; 00214 } 00215 } 00216 00217 // =========================== 00218 // Jacob 2/2: dh_df 00219 // =========================== 00220 if (!is_fixed && !graph_says_ignore_this_obs) // Only for features with unknown rel.pos. 00221 { 00222 // "Remap indices" in dh_df for each column are the feature IDs of those feature with unknown positions. 00223 const size_t remapIdx = new_k2f_edge.obs.obs.feat_id; 00224 00225 const mrpt::utils::map_as_vector<size_t,size_t> &dh_df_remap = rba_state.lin_system.dh_df.getColInverseRemappedIndices(); 00226 const mrpt::utils::map_as_vector<size_t,size_t>::const_iterator it_idx = dh_df_remap.find(remapIdx); // O(1) in mrpt::utils::map_as_vector() 00227 ASSERT_(it_idx!=dh_df_remap.end()) 00228 00229 const size_t col_idx = it_idx->second; 00230 00231 #if OBS_SUPER_VERBOSE 00232 cout << "dh_df: col_idx=" << col_idx << " feat_id=" << remapIdx << " obs_idx=" << new_obs_idx << endl; 00233 #endif 00234 // Get sparse block column: 00235 typename TSparseBlocksJacobians_dh_df::col_t & col = rba_state.lin_system.dh_df.getCol(col_idx); 00236 00237 // Create new entry: O(1) optimized insert if obs_idx is the largest index (as it will normally be): 00238 typename TSparseBlocksJacobians_dh_df::col_t::iterator it_new_entry = col.insert( 00239 col.end(), 00240 typename TSparseBlocksJacobians_dh_df::col_t::value_type( new_obs_idx, typename TSparseBlocksJacobians_dh_df::TEntry() ) 00241 ); 00242 00243 typename TSparseBlocksJacobians_dh_df::TEntry & entry = it_new_entry->second; 00244 00245 entry.sym.obs_idx = new_obs_idx; 00246 entry.sym.is_valid = jacob_valid_bit; 00247 00248 // Pointer to relative position: 00249 entry.sym.feat_rel_pos = lm_rel_pos; 00250 00251 // Pointers to placeholders of future numeric results of the spanning tree: 00252 entry.sym.rel_pose_base_from_obs = 00253 (new_k2f_edge.is_first_obs_of_unknown) ? 00254 NULL // Use special value "NULL" when the CPose is fixed to the origin. 00255 : 00256 & rba_state.spanning_tree.num[observing_kf_id][base_id]; 00257 } 00258 00259 m_profiler.leave("add_observation.jacobs.sym"); 00260 00261 m_profiler.leave("add_observation"); 00262 00263 return new_obs_idx; 00264 } 00265 00266 } // end NS