SRBA: Sparser Relative Bundle Adjustment
srba/impl/add-observations.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 #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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends