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 #include <mrpt/math/jacobians.h> 00013 #include <mrpt/poses/SE_traits.h> 00014 #include <srba/landmark_jacob_families.h> 00015 00016 namespace srba { 00017 00018 #ifndef SRBA_USE_NUMERIC_JACOBIANS 00019 # define SRBA_USE_NUMERIC_JACOBIANS 0 00020 #endif 00021 #ifndef SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS 00022 # define SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS 0 00023 #endif 00024 00025 #define SRBA_COMPUTE_NUMERIC_JACOBIANS (SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS || SRBA_USE_NUMERIC_JACOBIANS) 00026 #define SRBA_COMPUTE_ANALYTIC_JACOBIANS (SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS || !SRBA_USE_NUMERIC_JACOBIANS) 00027 00028 #define DEBUG_JACOBIANS_SUPER_VERBOSE 0 00029 #define DEBUG_NOT_UPDATED_ENTRIES 0 // Extremely slow, just for debug during development! This checks that all the expected Jacobians are actually updated 00030 00031 00032 namespace internal { 00037 template <landmark_jacob_family_t LM_JACOB_FAMILY> 00038 struct recompute_all_Jacobians_dh_df; 00039 00040 // Specialization for "normal SLAM" (SLAM with real landmarks) 00041 template <> struct recompute_all_Jacobians_dh_df<jacob_point_landmark> { 00042 template <class RBAENGINE,class LSTJACOBCOLS,class LSTPOSES> 00043 static size_t eval( 00044 RBAENGINE &rba, 00045 LSTJACOBCOLS &lst_JacobCols_df, // std::vector<typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t*> 00046 LSTPOSES * out_list_of_required_num_poses ) // std::vector<const typename RBAENGINE::kf2kf_pose_traits<RBAENGINE::kf2kf_pose_t>::pose_flag_t*> 00047 { 00048 const size_t nUnknowns_k2f = lst_JacobCols_df.size(); 00049 size_t nJacobs = 0; 00050 for (size_t i=0;i<nUnknowns_k2f;i++) 00051 { 00052 // For each column, process each nonzero block: 00053 typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t *col = lst_JacobCols_df[i]; 00054 00055 for (typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t::iterator it=col->begin();it!=col->end();++it) 00056 { 00057 const size_t obs_idx = it->first; 00058 typename RBAENGINE::TSparseBlocksJacobians_dh_df::TEntry & jacob_entry = it->second; 00059 rba.compute_jacobian_dh_df( 00060 jacob_entry, 00061 rba.get_rba_state().all_observations[obs_idx], 00062 out_list_of_required_num_poses ); 00063 nJacobs++; 00064 } 00065 } 00066 return nJacobs; 00067 } 00068 00069 }; 00070 00071 // Specialization for relative graph-SLAM (no real landmarks) 00072 template <> struct recompute_all_Jacobians_dh_df<jacob_relpose_landmark> { 00073 template <class RBAENGINE,class LSTJACOBCOLS,class LSTPOSES> 00074 static size_t eval( 00075 RBAENGINE &rba, 00076 LSTJACOBCOLS &lst_JacobCols_df, // std::vector<typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t*> 00077 LSTPOSES * out_list_of_required_num_poses ) // std::vector<const typename RBAENGINE::kf2kf_pose_traits<RBAENGINE::kf2kf_pose_t>::pose_flag_t*> 00078 { 00079 MRPT_UNUSED_PARAM(rba); MRPT_UNUSED_PARAM(lst_JacobCols_df); 00080 MRPT_UNUSED_PARAM(out_list_of_required_num_poses); 00081 // Nothing to do: this will never be actually called. 00082 return 0; 00083 } 00084 }; 00085 } // end "internal" ns 00086 00087 00089 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS> 00090 void RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::numeric_dh_dAp(const array_pose_t &x, const TNumeric_dh_dAp_params& params, array_obs_t &y) 00091 { 00092 pose_t incr(mrpt::poses::UNINITIALIZED_POSE); 00093 mrpt::poses::SE_traits<pose_t::rotation_dimensions>::pseudo_exp(x,incr); 00094 00095 pose_t base_from_obs(mrpt::poses::UNINITIALIZED_POSE); 00096 if (!params.is_inverse_dir) 00097 { 00098 if (params.pose_d1_wrt_obs) // "A" in papers 00099 base_from_obs.composeFrom(*params.pose_d1_wrt_obs,incr); 00100 else base_from_obs = incr; 00101 base_from_obs.composeFrom(base_from_obs,params.pose_base_wrt_d1); 00102 } 00103 else 00104 { 00105 // Changes due to the inverse pose: 00106 // D becomes D' = p_d^{d+1} (+) D 00107 ASSERT_(params.k2k_edge_id<params.k2k_edges.size()) 00108 const pose_t & p_d_d1 = params.k2k_edges[params.k2k_edge_id].inv_pose; 00109 00110 pose_t pose_base_wrt_d_prime(mrpt::poses::UNINITIALIZED_POSE); // D' in papers 00111 pose_base_wrt_d_prime.composeFrom( p_d_d1, params.pose_base_wrt_d1 ); 00112 00113 pose_t p_d_d1_mod(mrpt::poses::UNINITIALIZED_POSE); 00114 p_d_d1_mod.composeFrom(incr, p_d_d1); 00115 p_d_d1_mod.inverse(); 00116 00117 // total pose: base from obs = pose_d1_wrt_obs (+) inv(p_d_d1_mod) (+) D' 00118 if (params.pose_d1_wrt_obs) // "A" in papers 00119 base_from_obs = *params.pose_d1_wrt_obs + p_d_d1_mod + pose_base_wrt_d_prime; 00120 else base_from_obs = p_d_d1_mod + pose_base_wrt_d_prime; 00121 } 00122 00123 // pose_robot2sensor(): pose wrt sensor = pose_wrt_robot (-) sensor_pose_on_the_robot 00124 typename options::internal::resulting_pose_t<typename RBA_OPTIONS::sensor_pose_on_robot_t,REL_POSE_DIMS>::pose_t base_pose_wrt_sensor(mrpt::poses::UNINITIALIZED_POSE); 00125 RBA_OPTIONS::sensor_pose_on_robot_t::pose_robot2sensor( base_from_obs, base_pose_wrt_sensor, params.sensor_pose ); 00126 00127 // Generate observation: 00128 array_obs_t z_zero; 00129 z_zero.setZero(); 00130 00131 sensor_model_t::observe_error(y,z_zero,base_pose_wrt_sensor,params.xji_i, params.sensor_params); 00132 y=-y; // because the method above evals: "z_zero - h(x)" 00133 } 00134 00135 #if DEBUG_NOT_UPDATED_ENTRIES 00136 00137 struct TNumSTData 00138 { 00139 TKeyFrameID from, to; 00140 }; 00141 00142 TNumSTData check_num_st_entry_exists( 00143 const pose_flag_t * entry, 00144 const TRBA_Problem_state::TSpanningTree & st) 00145 { 00146 for (TRelativePosesForEachTarget::const_iterator it_tree=st.num.begin();it_tree!=st.num.end();++it_tree) 00147 { 00148 const TKeyFrameID id_from = it_tree->first; 00149 const frameid2pose_map_t & tree = it_tree->second; 00150 00151 for (frameid2pose_map_t::const_iterator it=tree.begin();it!=tree.end();++it) 00152 { 00153 const TKeyFrameID id_to = it->first; 00154 const pose_flag_t & e = it->second; 00155 if (&e == entry) 00156 { 00157 TNumSTData d; 00158 d.from = id_from; 00159 d.to = id_to; 00160 return d; 00161 } 00162 } 00163 } 00164 ASSERT_(false) 00165 } 00166 00167 #endif 00168 00169 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS> 00170 void RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::numeric_dh_df(const array_landmark_t &x, const TNumeric_dh_df_params& params, array_obs_t &y) 00171 { 00172 static const pose_t my_aux_null_pose; 00173 00174 const array_landmark_t x_local = params.xji_i + x; 00175 const pose_t * pos_cam = params.pose_base_wrt_obs!=NULL ? params.pose_base_wrt_obs : &my_aux_null_pose; 00176 00177 // pose_robot2sensor(): pose wrt sensor = pose_wrt_robot (-) sensor_pose_on_the_robot 00178 typename options::internal::resulting_pose_t<typename RBA_OPTIONS::sensor_pose_on_robot_t,REL_POSE_DIMS>::pose_t base_pose_wrt_sensor(mrpt::poses::UNINITIALIZED_POSE); 00179 RBA_OPTIONS::sensor_pose_on_robot_t::pose_robot2sensor( *pos_cam, base_pose_wrt_sensor, params.sensor_pose ); 00180 00181 // Generate observation: 00182 array_obs_t z_zero; 00183 z_zero.setZero(); 00184 sensor_model_t::observe_error(y,z_zero,base_pose_wrt_sensor,x_local, params.sensor_params); 00185 y=-y; // because the method above evals: "z_zero - h(x)" 00186 } 00187 00188 00190 template <landmark_jacob_family_t JACOB_FAMILY, size_t POINT_DIMS, size_t POSE_DIMS, class RBA_ENGINE_T> 00191 struct compute_jacobian_dAepsDx_deps; 00192 00193 // ==================================================================== 00194 // j,i lm_id,base_id 00195 // \partial h \partial h 00196 // l obs_frame_id 00197 // dh_dp = ------------------ = --------------------------------- 00198 // d+1 cur_id 00199 // \partial p \partial p 00200 // d stp.next_node 00201 // 00202 // See tech report: 00203 // "A tutorial on SE(3) transformation parameterizations and 00204 // on-manifold optimization", Jose-Luis Blanco. 00205 // ==================================================================== 00206 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS> 00207 void RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::compute_jacobian_dh_dp( 00208 typename TSparseBlocksJacobians_dh_dAp::TEntry &jacob, 00209 const k2f_edge_t & observation, 00210 const k2k_edges_deque_t &k2k_edges, 00211 std::vector<const pose_flag_t*> *out_list_of_required_num_poses) const 00212 { 00213 ASSERT_(observation.obs.kf_id!=jacob.sym.kf_base) 00214 00215 if (! *jacob.sym.is_valid ) 00216 return; // Another block of the same Jacobian row said this observation was invalid for some reason. 00217 00218 // And x^{j,i}_l = pose_of_i_wrt_l (+) x^{j,i}_i 00219 00220 // Handle the special case when d==obs, so rel_pose_d1_from_obs==NULL, and its pose is the origin: 00221 const pose_flag_t * pose_d1_wrt_obs = jacob.sym.rel_pose_d1_from_obs; // "A" in papers 00222 const pose_flag_t & pose_base_wrt_d1 = *jacob.sym.rel_pose_base_from_d1; 00223 const bool is_inverse_edge_jacobian = !jacob.sym.edge_normal_dir; // If edge points in the opposite direction than as assumed in mathematical derivation. 00224 00225 if (out_list_of_required_num_poses) 00226 { 00227 if (jacob.sym.rel_pose_d1_from_obs) 00228 out_list_of_required_num_poses->push_back(jacob.sym.rel_pose_d1_from_obs); 00229 out_list_of_required_num_poses->push_back(jacob.sym.rel_pose_base_from_d1); 00230 } 00231 00232 // make sure the numeric spanning tree is working and updating all that we need: 00233 #if DEBUG_NOT_UPDATED_ENTRIES 00234 TNumSTData d1 = check_num_st_entry_exists(&pose_base_wrt_d1, rba_state.spanning_tree); 00235 #endif 00236 00237 if (!pose_base_wrt_d1.updated) 00238 { 00239 std::cerr << " kf_d+1: " << jacob.sym.kf_d << ", base_id: "<< jacob.sym.kf_base << std::endl; 00240 rba_state.spanning_tree.save_as_dot_file("_debug_jacob_error_all_STs.dot"); 00241 ASSERT_(pose_base_wrt_d1.updated) 00242 } 00243 00244 if (pose_d1_wrt_obs) 00245 { 00246 #if DEBUG_NOT_UPDATED_ENTRIES 00247 TNumSTData d2 = check_num_st_entry_exists(pose_d1_wrt_obs, rba_state.spanning_tree); 00248 #endif 00249 if (!pose_d1_wrt_obs->updated) 00250 { 00251 std::cerr << " kf_d+1: " << jacob.sym.kf_d << ", obs_frame_id: "<< observation.obs.kf_id << std::endl; 00252 rba_state.spanning_tree.save_as_dot_file("_debug_jacob_error_all_STs.dot"); 00253 } 00254 ASSERT_(pose_d1_wrt_obs->updated) 00255 } 00256 00257 00258 // i<-l = d <- obs/l (+) base/i <- d 00259 pose_t pose_i_wrt_l(mrpt::poses::UNINITIALIZED_POSE); 00260 if (pose_d1_wrt_obs!=NULL) 00261 pose_i_wrt_l.composeFrom( pose_d1_wrt_obs->pose, pose_base_wrt_d1.pose); 00262 else pose_i_wrt_l = pose_base_wrt_d1.pose; 00263 00264 // First, we need x^{j,i}_i: 00265 // LM parameters in: jacob.sym.feat_rel_pos->pos[0:N-1] 00266 const array_landmark_t &xji_i = jacob.sym.feat_rel_pos->pos; 00267 00268 // xji_l = pose_i_wrt_l (+) xji_i 00269 array_landmark_t xji_l = xji_i; // 00270 landmark_t::composePosePoint(xji_l, pose_i_wrt_l); 00271 00272 #if DEBUG_JACOBIANS_SUPER_VERBOSE // Debug: 00273 { 00274 const TKeyFrameID obs_frame_id = observation.obs.kf_id; 00275 const TKeyFrameID base_id = jacob.sym.kf_base; 00276 const TKeyFrameID d_plus_1_id = jacob.sym.kf_d; 00277 cout << "compute_jacobian_dh_dp: " 00278 << " obs_frame_id: " << obs_frame_id 00279 << " base_id: " << base_id 00280 << " kf_d+1: " << d_plus_1_id 00281 << " k2k_edge_id: " << jacob.sym.k2k_edge_id 00282 << " is_inverse: " << is_inverse_edge_jacobian << endl 00283 << " pose_base_wrt_d1 (D): "<<pose_base_wrt_d1.pose << endl; 00284 if (pose_d1_wrt_obs) cout << " pose_d1_wrt_obs (A): "<< pose_d1_wrt_obs->pose<< endl; 00285 // Observations from the "base_id" of a fixed,known landmark are NOT considered observations for optimization: 00286 //mrpt::system::pause(); 00287 } 00288 #endif 00289 00290 #if SRBA_COMPUTE_NUMERIC_JACOBIANS 00291 // Numeric jacobians 00292 typename TSparseBlocksJacobians_dh_dAp::matrix_t num_jacob; 00293 00294 array_pose_t x; 00295 x.setZero(); // Evaluate Jacobian at manifold incr around origin 00296 array_pose_t x_incrs; 00297 x_incrs.setConstant(1e-4); 00298 00299 const TNumeric_dh_dAp_params num_params(jacob.sym.k2k_edge_id,&pose_d1_wrt_obs->pose, pose_base_wrt_d1.pose,jacob.sym.feat_rel_pos->pos, is_inverse_edge_jacobian,k2k_edges,this->parameters.sensor,this->parameters.sensor_pose); 00300 00301 mrpt::math::jacobians::jacob_numeric_estimate(x,&numeric_dh_dAp,x_incrs,num_params,num_jacob); 00302 00303 #endif // SRBA_COMPUTE_NUMERIC_JACOBIANS 00304 00305 00306 #if SRBA_COMPUTE_ANALYTIC_JACOBIANS 00307 // d h(x^{j,i}_l) d h(x') d x^{j,i}_l 00308 // --------------- = --------- * ---------------- 00309 // d p^{d+1}_d d x' d epsilon 00310 // 00311 // With: x' = x^{j,i}_l 00312 00313 // First jacobian: (uses xji_l) 00314 // ----------------------------- 00315 Eigen::Matrix<double,OBS_DIMS,LM_DIMS> dh_dx; 00316 00317 // Converts a point relative to the robot coordinate frame (P) into a point relative to the sensor (RES = P \ominus POSE_IN_ROBOT ) 00318 RBA_OPTIONS::sensor_pose_on_robot_t::template point_robot2sensor<landmark_t,array_landmark_t>(xji_l,xji_l,this->parameters.sensor_pose ); 00319 00320 // Invoke sensor model: 00321 if (!sensor_model_t::eval_jacob_dh_dx(dh_dx,xji_l, this->parameters.sensor)) 00322 { 00323 // Invalid Jacobian: 00324 *jacob.sym.is_valid = 0; 00325 jacob.num.setZero(); 00326 return; 00327 } 00328 00329 // take into account the possible displacement of the sensor wrt the keyframe: 00330 RBA_OPTIONS::sensor_pose_on_robot_t::jacob_dh_dx_rotate( dh_dx, this->parameters.sensor_pose ); 00331 00332 // Second Jacobian: (uses xji_i) 00333 // ------------------------------ 00334 compute_jacobian_dAepsDx_deps<landmark_t::jacob_family,LM_DIMS,REL_POSE_DIMS,rba_engine_t>::eval(jacob.num,dh_dx,is_inverse_edge_jacobian,xji_i, pose_d1_wrt_obs, pose_base_wrt_d1,jacob.sym,k2k_edges,rba_state.all_observations); 00335 00336 #endif // SRBA_COMPUTE_ANALYTIC_JACOBIANS 00337 00338 00339 #if SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS 00340 // Check jacob.num vs. num_jacob 00341 const double MAX_REL_ERROR = 0.1; 00342 if ((jacob.num-num_jacob).array().abs().maxCoeff()>MAX_REL_ERROR*num_jacob.array().maxCoeff()) 00343 { 00344 std::cerr << "NUMERIC VS. ANALYTIC JACOBIAN dh_dAp FAILED:" 00345 << "\njacob.num:\n" << jacob.num 00346 << "\nnum_jacob:\n" << num_jacob 00347 << "\nDiff:\n" << jacob.num-num_jacob << endl << endl; 00348 } 00349 #endif 00350 00351 #if SRBA_USE_NUMERIC_JACOBIANS 00352 jacob.num = num_jacob; 00353 #endif 00354 } 00355 00356 // ==================================================================== 00357 // Auxiliary sub-Jacobian used in compute_jacobian_dh_dp() 00358 // These are specializations of the template, for each of the cases: 00359 //template <size_t , size_t , class MATRIX, class POINT> 00360 // ==================================================================== 00361 00362 // Case: 3D point, SE(3) poses: 00363 template <class RBA_ENGINE_T> 00364 struct compute_jacobian_dAepsDx_deps<jacob_point_landmark /* Jacobian family: this LM is a point */, 3 /*POINT_DIMS*/,6 /*POSE_DIMS*/,RBA_ENGINE_T> 00365 { 00366 template <class MATRIX, class MATRIX_DH_DX,class POINT,class pose_flag_t,class JACOB_SYM_T,class K2K_EDGES_T,class OBS_VECTOR> 00367 static void eval( 00368 MATRIX & jacob, 00369 const MATRIX_DH_DX & dh_dx, 00370 const bool is_inverse_edge_jacobian, 00371 const POINT & xji_i, 00372 const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes 00373 const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes 00374 const JACOB_SYM_T & jacob_sym, 00375 const K2K_EDGES_T & k2k_edges, 00376 const OBS_VECTOR & all_obs 00377 ) 00378 { 00379 MRPT_UNUSED_PARAM(all_obs); 00380 // See section 10.3.7 of technical report on SE(3) poses [http://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf] 00381 if (!is_inverse_edge_jacobian) 00382 { // Normal formulation: unknown is pose "d+1 -> d" 00383 00384 // This is "D" in my handwritten notes: 00385 // pose_i_wrt_dplus1 -> pose_base_wrt_d1 00386 00387 const mrpt::math::CMatrixDouble33 & ROTD = pose_base_wrt_d1.pose.getRotationMatrix(); 00388 00389 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0,0,0,0): 00390 Eigen::Matrix<double,RBA_ENGINE_T::OBS_DIMS,3> H_ROTA; 00391 if (pose_d1_wrt_obs!=NULL) 00392 { 00393 // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs 00394 00395 // 3x3 term: H*R(A) 00396 H_ROTA = dh_dx * pose_d1_wrt_obs->pose.getRotationMatrix(); 00397 } 00398 else 00399 { 00400 // 3x3 term: H*R(A) 00401 H_ROTA = dh_dx; 00402 } 00403 00404 // First 2x3 block: 00405 jacob.block(0,0,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA; 00406 00407 // Second 2x3 block: 00408 // compute aux vector "v": 00409 Eigen::Matrix<double,3,1> v; 00410 v[0] = -pose_base_wrt_d1.pose.x() - xji_i[0]*ROTD.coeff(0,0) - xji_i[1]*ROTD.coeff(0,1) - xji_i[2]*ROTD.coeff(0,2); 00411 v[1] = -pose_base_wrt_d1.pose.y() - xji_i[0]*ROTD.coeff(1,0) - xji_i[1]*ROTD.coeff(1,1) - xji_i[2]*ROTD.coeff(1,2); 00412 v[2] = -pose_base_wrt_d1.pose.z() - xji_i[0]*ROTD.coeff(2,0) - xji_i[1]*ROTD.coeff(2,1) - xji_i[2]*ROTD.coeff(2,2); 00413 00414 Eigen::Matrix<double,3,3> aux; 00415 00416 aux.coeffRef(0,0)=0; 00417 aux.coeffRef(1,1)=0; 00418 aux.coeffRef(2,2)=0; 00419 00420 aux.coeffRef(1,2)=-v[0]; 00421 aux.coeffRef(2,1)= v[0]; 00422 aux.coeffRef(2,0)=-v[1]; 00423 aux.coeffRef(0,2)= v[1]; 00424 aux.coeffRef(0,1)=-v[2]; 00425 aux.coeffRef(1,0)= v[2]; 00426 00427 jacob.block(0,3,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA * aux; 00428 } 00429 else 00430 { // Inverse formulation: unknown is pose "d -> d+1" 00431 00432 // Changes due to the inverse pose: 00433 // D becomes D' = p_d^{d+1} (+) D 00434 00435 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size()) 00436 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id].inv_pose; 00437 00438 typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE); 00439 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose ); 00440 00441 const mrpt::math::CMatrixDouble33 & ROTD = pose_base_wrt_d1_prime.getRotationMatrix(); 00442 00443 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0,0,0,0): 00444 Eigen::Matrix<double,RBA_ENGINE_T::OBS_DIMS,3> H_ROTA; 00445 if (pose_d1_wrt_obs!=NULL) 00446 { 00447 // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs 00448 00449 // In inverse edges, A (which is "pose_d1_wrt_obs") becomes A * (p_d_d1)^-1 => 00450 // So: ROT_A' = ROT_A * ROT_d_d1^t 00451 00452 // 3x3 term: H*R(A') 00453 H_ROTA = dh_dx * pose_d1_wrt_obs->pose.getRotationMatrix() * p_d_d1.getRotationMatrix().transpose(); 00454 } 00455 else 00456 { 00457 // Was in the normal edge: H_ROTA = dh_dx; 00458 00459 // 3x3 term: H*R(A') 00460 H_ROTA = dh_dx * p_d_d1.getRotationMatrix().transpose(); 00461 } 00462 00463 // First 2x3 block: 00464 jacob.block(0,0,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA; 00465 00466 // Second 2x3 block: 00467 // compute aux vector "v": 00468 Eigen::Matrix<double,3,1> v; 00469 v[0] = -pose_base_wrt_d1_prime.x() - xji_i[0]*ROTD.coeff(0,0) - xji_i[1]*ROTD.coeff(0,1) - xji_i[2]*ROTD.coeff(0,2); 00470 v[1] = -pose_base_wrt_d1_prime.y() - xji_i[0]*ROTD.coeff(1,0) - xji_i[1]*ROTD.coeff(1,1) - xji_i[2]*ROTD.coeff(1,2); 00471 v[2] = -pose_base_wrt_d1_prime.z() - xji_i[0]*ROTD.coeff(2,0) - xji_i[1]*ROTD.coeff(2,1) - xji_i[2]*ROTD.coeff(2,2); 00472 00473 Eigen::Matrix<double,3,3> aux; 00474 00475 aux.coeffRef(0,0)=0; 00476 aux.coeffRef(1,1)=0; 00477 aux.coeffRef(2,2)=0; 00478 00479 aux.coeffRef(1,2)=-v[0]; 00480 aux.coeffRef(2,1)= v[0]; 00481 aux.coeffRef(2,0)=-v[1]; 00482 aux.coeffRef(0,2)= v[1]; 00483 aux.coeffRef(0,1)=-v[2]; 00484 aux.coeffRef(1,0)= v[2]; 00485 00486 jacob.block(0,3,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA * aux; 00487 00488 // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon 00489 jacob = -jacob; 00490 00491 } // end inverse edge case 00492 00493 } 00494 }; // end of specialization of "compute_jacobian_dAepsDx_deps" 00495 00496 00500 template <size_t POINT_DIMS, class RBA_ENGINE_T> 00501 struct compute_jacobian_dAepsDx_deps_SE2 00502 { 00503 template <class MATRIX, class MATRIX_DH_DX,class POINT,class pose_flag_t,class JACOB_SYM_T,class K2K_EDGES_T,class OBS_VECTOR> 00504 static void eval( 00505 MATRIX & jacob, 00506 const MATRIX_DH_DX & dh_dx, 00507 const bool is_inverse_edge_jacobian, 00508 const POINT & xji_i, 00509 const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes 00510 const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes 00511 const JACOB_SYM_T & jacob_sym, 00512 const K2K_EDGES_T & k2k_edges, 00513 const OBS_VECTOR & all_obs 00514 ) 00515 { 00516 MRPT_UNUSED_PARAM(all_obs); 00517 MRPT_COMPILE_TIME_ASSERT(POINT_DIMS==2 || POINT_DIMS==3) 00518 00519 if (!is_inverse_edge_jacobian) 00520 { // Normal formulation: unknown is pose "d+1 -> d" 00521 00522 const double Xd=pose_base_wrt_d1.pose.x(); 00523 const double Yd=pose_base_wrt_d1.pose.y(); 00524 00525 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00526 double PHIa=0; // Xa, Ya: Are not really needed, since they don't appear in the Jacobian. 00527 mrpt::poses::CPose2D AD(mrpt::poses::UNINITIALIZED_POSE); // AD = A(+)D 00528 if (pose_d1_wrt_obs!=NULL) 00529 { 00530 // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs 00531 //Xa = pose_d1_wrt_obs->pose.x(); Ya = pose_d1_wrt_obs->pose.y(); // Not needed 00532 PHIa = pose_d1_wrt_obs->pose.phi(); 00533 AD.composeFrom(pose_d1_wrt_obs->pose,pose_base_wrt_d1.pose); 00534 } 00535 else 00536 { 00537 AD = pose_base_wrt_d1.pose; // A=0 -> A(+)D is simply D 00538 } 00539 00540 // d(P (+) x) / dP, P = A*D 00541 Eigen::Matrix<double,POINT_DIMS/* 2 or 3*/,3 /* x,y,phi*/> dPx_P; 00542 const double ccos_ad = cos(AD.phi()); 00543 const double ssin_ad = sin(AD.phi()); 00544 dPx_P(0,0) = 1; dPx_P(0,1) = 0; dPx_P(0,2) = -xji_i[0]*ssin_ad - xji_i[1]*ccos_ad; 00545 dPx_P(1,0) = 0; dPx_P(1,1) = 1; dPx_P(1,2) = xji_i[0]*ccos_ad - xji_i[1]*ssin_ad; 00546 if (POINT_DIMS==3) { 00547 dPx_P(2,0) = 0; dPx_P(2,1) = 0; dPx_P(2,2) = 1; 00548 } 00549 00550 // d(A*exp(eps)*D) / deps 00551 Eigen::Matrix<double,3,3> dAD_deps; 00552 const double ccos_a = cos(PHIa); 00553 const double ssin_a = sin(PHIa); 00554 dAD_deps(0,0) = ccos_a; dAD_deps(0,1) = -ssin_a; 00555 dAD_deps(1,0) = ssin_a; dAD_deps(1,1) = ccos_a; 00556 dAD_deps(2,0) = 0; dAD_deps(2,1) = 0; 00557 00558 dAD_deps(0,2) = -ssin_a*Xd - ccos_a*Yd; 00559 dAD_deps(1,2) = ccos_a*Xd - ssin_a*Yd; 00560 dAD_deps(2,2) = 1; 00561 00562 // Chain rule: 00563 jacob.noalias() = dh_dx * dPx_P * dAD_deps; 00564 } 00565 else 00566 { // Inverse formulation: unknown is pose "d -> d+1" 00567 00568 // Changes due to the inverse pose: 00569 // D becomes D' = p_d^{d+1} (+) D 00570 // and A (which is "pose_d1_wrt_obs") becomes A' = A (+) (p_d_d1)^-1 00571 00572 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size()) 00573 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id].inv_pose; 00574 00575 typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE); 00576 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose ); 00577 00578 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00579 const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1; 00580 00581 typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ? 00582 (pose_d1_wrt_obs->pose + p_d_d1_inv) 00583 : 00584 p_d_d1_inv; 00585 00586 mrpt::poses::CPose2D AD(mrpt::poses::UNINITIALIZED_POSE); // AD = A(+)D 00587 AD.composeFrom(A_prime,pose_base_wrt_d1_prime); 00588 00589 const double Xd=pose_base_wrt_d1_prime.x(); 00590 const double Yd=pose_base_wrt_d1_prime.y(); 00591 00592 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00593 const double PHIa=A_prime.phi(); 00594 00595 00596 // d(P (+) x) / dP, P = A*D 00597 Eigen::Matrix<double,POINT_DIMS/* 2 or 3*/,3 /* x,y,phi*/> dPx_P; 00598 const double ccos_ad = cos(AD.phi()); 00599 const double ssin_ad = sin(AD.phi()); 00600 dPx_P(0,0) = 1; dPx_P(0,1) = 0; dPx_P(0,2) = -xji_i[0]*ssin_ad - xji_i[1]*ccos_ad; 00601 dPx_P(1,0) = 0; dPx_P(1,1) = 1; dPx_P(1,2) = xji_i[0]*ccos_ad - xji_i[1]*ssin_ad; 00602 if (POINT_DIMS==3) { 00603 dPx_P(2,0) = 0; dPx_P(2,1) = 0; dPx_P(2,2) = 1; 00604 } 00605 00606 // d(A*exp(eps)*D) / deps 00607 Eigen::Matrix<double,3,3> dAD_deps; 00608 const double ccos_a = cos(PHIa); 00609 const double ssin_a = sin(PHIa); 00610 dAD_deps(0,0) = ccos_a; dAD_deps(0,1) = -ssin_a; 00611 dAD_deps(1,0) = ssin_a; dAD_deps(1,1) = ccos_a; 00612 dAD_deps(2,0) = 0; dAD_deps(2,1) = 0; 00613 00614 dAD_deps(0,2) = -ssin_a*Xd - ccos_a*Yd; 00615 dAD_deps(1,2) = ccos_a*Xd - ssin_a*Yd; 00616 dAD_deps(2,2) = 1; 00617 00618 // Chain rule: 00619 jacob.noalias() = dh_dx * dPx_P * dAD_deps; 00620 00621 // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon 00622 jacob = -jacob; 00623 00624 } // end inverse edge case 00625 00626 } 00627 }; // end of "compute_jacobian_dAepsDx_deps_SE2" 00628 00629 // Case: 2D point, SE(2) poses: (derived from generic SE2 implementation above) 00630 template <class RBA_ENGINE_T> 00631 struct compute_jacobian_dAepsDx_deps<jacob_point_landmark /* Jacobian family: this LM is a point */, 2 /*POINT_DIMS*/,3 /*POSE_DIMS*/,RBA_ENGINE_T> 00632 : public compute_jacobian_dAepsDx_deps_SE2<2 /*POINT_DIMS*/,RBA_ENGINE_T> 00633 { 00634 }; 00635 00636 // Case: 3D point, SE(2) poses: (derived from generic SE2 implementation above) 00637 template <class RBA_ENGINE_T> 00638 struct compute_jacobian_dAepsDx_deps<jacob_point_landmark /* Jacobian family: this LM is a point */, 3 /*POINT_DIMS*/,3 /*POSE_DIMS*/,RBA_ENGINE_T> 00639 : public compute_jacobian_dAepsDx_deps_SE2<3 /*POINT_DIMS*/,RBA_ENGINE_T> 00640 { 00641 }; 00642 00643 // Case: SE(2) relative-poses, SE(2) poses: 00644 template <class RBA_ENGINE_T> 00645 struct compute_jacobian_dAepsDx_deps<jacob_relpose_landmark /* Jacobian family: this LM is a relative pose (graph-slam) */, 3 /*POINT_DIMS*/,3 /*POSE_DIMS*/,RBA_ENGINE_T> 00646 { 00647 template <class MATRIX, class MATRIX_DH_DX,class POINT,class pose_flag_t,class JACOB_SYM_T,class K2K_EDGES_T,class OBS_VECTOR> 00648 static void eval( 00649 MATRIX & jacob, 00650 const MATRIX_DH_DX & dh_dx, 00651 const bool is_inverse_edge_jacobian, 00652 const POINT & xji_i, 00653 const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes 00654 const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes 00655 const JACOB_SYM_T & jacob_sym, 00656 const K2K_EDGES_T & k2k_edges, 00657 const OBS_VECTOR & all_obs 00658 ) 00659 { 00660 MRPT_UNUSED_PARAM(xji_i); 00661 MRPT_UNUSED_PARAM(all_obs); 00662 double Xd,Yd,PHIa; 00663 mrpt::poses::CPose2D base_wrt_obs(mrpt::poses::UNINITIALIZED_POSE); // A(+)D 00664 00665 if (!is_inverse_edge_jacobian) 00666 { // Normal formulation: unknown is pose "d+1 -> d" 00667 00668 Xd=pose_base_wrt_d1.pose.x(); 00669 Yd=pose_base_wrt_d1.pose.y(); 00670 00671 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00672 PHIa=0; // Xa, Ya: Are not really needed, since they don't appear in the Jacobian. 00673 if (pose_d1_wrt_obs!=NULL) 00674 { 00675 // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs 00676 PHIa = pose_d1_wrt_obs->pose.phi(); 00677 base_wrt_obs.composeFrom(pose_d1_wrt_obs->pose, pose_base_wrt_d1.pose); // A (+) D 00678 } 00679 else 00680 { 00681 base_wrt_obs = pose_base_wrt_d1.pose; // A (+) D 00682 } 00683 } 00684 else 00685 { // Inverse formulation: unknown is pose "d -> d+1" 00686 00687 // Changes due to the inverse pose: 00688 // D becomes D' = p_d^{d+1} (+) D 00689 // and A (which is "pose_d1_wrt_obs") becomes A' = A (+) (p_d_d1)^-1 00690 00691 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size()) 00692 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id].inv_pose; 00693 00694 typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE); 00695 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose ); 00696 00697 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00698 const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1; 00699 00700 typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ? 00701 (pose_d1_wrt_obs->pose + p_d_d1_inv) 00702 : 00703 p_d_d1_inv; 00704 00705 Xd=pose_base_wrt_d1_prime.x(); 00706 Yd=pose_base_wrt_d1_prime.y(); 00707 00708 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00709 PHIa=A_prime.phi(); 00710 00711 base_wrt_obs.composeFrom(A_prime, pose_base_wrt_d1_prime); // A (+) D 00712 } 00713 00714 //const mrpt::poses::CPose2D base_wrt_obs_inv = -base_wrt_obs; 00715 const double PHIad = base_wrt_obs.phi(); 00716 00717 const double ccos_ad = cos(PHIad), ssin_ad=sin(PHIad); 00718 const double ccos_a = cos(PHIa), ssin_a=sin(PHIa); 00719 00720 Eigen::Matrix<double,3,3> J0; // -d(\ominus p)_dp, with p=A*D 00721 J0(0,0)= ccos_ad; J0(0,1)= ssin_ad; J0(0,2)= 0; 00722 J0(1,0)=-ssin_ad; J0(1,1)= ccos_ad; J0(1,2)= 0; 00723 J0(2,0)=0; J0(2,1)=0; J0(2,2)= 1; 00724 00725 Eigen::Matrix<double,3,3> J1; // dAD_dA; 00726 J1(0,0)=1; J1(0,1)=0; J1(0,2)= -Xd*ssin_a-Yd*ccos_a; 00727 J1(1,0)=0; J1(1,1)=1; J1(1,2)= Xd*ccos_a-Yd*ssin_a; 00728 J1(2,0)=0; J1(2,1)=0; J1(2,2)= 1; 00729 00730 Eigen::Matrix<double,3,3> J2; // dAe_de 00731 J2(0,0)=ccos_a; J2(0,1)=-ssin_a; J2(0,2)=0; 00732 J2(1,0)=ssin_a; J2(1,1)= ccos_a; J2(1,2)=0; 00733 J2(2,0)=0; J2(2,1)=0; J2(2,2)=1; 00734 00735 // Chain rule: 00736 jacob.noalias() = dh_dx * J0* J1 * J2; 00737 00738 if (is_inverse_edge_jacobian) 00739 { 00740 // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon 00741 jacob = -jacob; 00742 } 00743 } 00744 }; // end of "compute_jacobian_dAepsDx_deps", Case: SE(2) relative-poses, SE(2) poses: 00745 00746 // Case: SE(3) relative-poses, SE(3) poses: 00747 template <class RBA_ENGINE_T> 00748 struct compute_jacobian_dAepsDx_deps<jacob_relpose_landmark /* Jacobian family: this LM is a relative pose (graph-slam) */, 6 /*POINT_DIMS*/,6 /*POSE_DIMS*/,RBA_ENGINE_T> 00749 { 00750 template <class MATRIX, class MATRIX_DH_DX,class POINT,class pose_flag_t,class JACOB_SYM_T,class K2K_EDGES_T,class OBS_VECTOR> 00751 static void eval( 00752 MATRIX & jacob, 00753 const MATRIX_DH_DX & dh_dx, 00754 const bool is_inverse_edge_jacobian, 00755 const POINT & xji_i, 00756 const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes 00757 const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes 00758 const JACOB_SYM_T & jacob_sym, 00759 const K2K_EDGES_T & k2k_edges, 00760 const OBS_VECTOR & all_obs 00761 ) 00762 { 00763 MRPT_UNUSED_PARAM(xji_i); 00764 MRPT_UNUSED_PARAM(all_obs); 00765 // 00766 // d ps-log(p^obs_base) d ps-log(p) d A*e^eps*D 00767 // -------------------- = --------------- * ----------------- 00768 // d eps^d+1_d d p d eps 00769 // 6x6 6x12 12x6 00770 // 00771 // ^: (1) ^: (2) 00772 // See section 10.3.7 of technical report on SE(3) poses [http://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf] 00773 mrpt::math::CMatrixDouble33 ROTA; // A.rotationMatrix 00774 typename RBA_ENGINE_T::pose_t D(mrpt::poses::UNINITIALIZED_POSE); 00775 typename RBA_ENGINE_T::pose_t base_wrt_obs(mrpt::poses::UNINITIALIZED_POSE); // A(+)D 00776 00777 if (!is_inverse_edge_jacobian) 00778 { // Normal formulation: unknown is pose "d+1 -> d" 00779 D = pose_base_wrt_d1.pose; 00780 00781 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00782 if (pose_d1_wrt_obs!=NULL) 00783 { 00784 // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs 00785 base_wrt_obs.composeFrom(pose_d1_wrt_obs->pose, D); // A (+) D 00786 ROTA = pose_d1_wrt_obs->pose.getRotationMatrix(); 00787 } 00788 else 00789 { 00790 base_wrt_obs = D; // A (+) D 00791 ROTA.setIdentity(); 00792 } 00793 } 00794 else 00795 { // Inverse formulation: unknown is pose "d -> d+1" 00796 00797 // Changes due to the inverse pose: 00798 // D becomes D' = p_d^{d+1} (+) D 00799 // and A (which is "pose_d1_wrt_obs") becomes A' = A (+) (p_d_d1)^-1 00800 00801 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size()) 00802 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id].inv_pose; 00803 00804 typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE); 00805 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose ); 00806 00807 // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0): 00808 const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1; 00809 00810 typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ? 00811 (pose_d1_wrt_obs->pose + p_d_d1_inv) 00812 : 00813 p_d_d1_inv; 00814 00815 ROTA=A_prime.getRotationMatrix(); 00816 00817 D=pose_base_wrt_d1_prime; 00818 00819 base_wrt_obs.composeFrom(A_prime, pose_base_wrt_d1_prime); // A (+) D 00820 } 00821 00822 const mrpt::math::CMatrixDouble44 & HM_D = D.getHomogeneousMatrixVal(); // [ROT(D) | Trans(D) ] 00823 00824 00825 // (1): Common part: d_Ln(R)_dR: 00826 // 00827 const mrpt::math::CMatrixDouble33 & ROT_AplusB = base_wrt_obs.getRotationMatrix(); // The rotation matrix. 00828 Eigen::Matrix<double,6,12> dLnRelPose_deps; 00829 dLnRelPose_deps.setZero(); 00830 dLnRelPose_deps.block<3,3>(0,9).setIdentity(); 00831 { 00832 mrpt::math::CMatrixFixedNumeric<double,3,9> dLnRot_dRot(mrpt::math::UNINITIALIZED_MATRIX); 00833 mrpt::poses::CPose3D::ln_rot_jacob( ROT_AplusB, dLnRot_dRot); 00834 dLnRelPose_deps.block<3,9>(3,0) = dLnRot_dRot; 00835 } 00836 00837 // (2): d A*e^eps*D / d eps = 00838 // 00839 // [ 0_{3x3} -R(A)*[dc1]_x ] 00840 // [ 0_{3x3} -R(A)*[dc2]_x ] 00841 // [ 0_{3x3} -R(A)*[dc3]_x ] 00842 // [ R(A) -R(A)*[dt]_x ] 00843 // 00844 Eigen::Matrix<double,12,6> dAeD_de; 00845 // Blocks: (1-3,1) 00846 dAeD_de.block<9,3>(0,0).setZero(); 00847 // Block (4,1) 00848 dAeD_de.block<3,3>(9,0) = (ROTA * HM_D.block<3,3>(0,0)).transpose(); 00849 00850 //cout << "========= ROTA ========= :\n" << ROTA << endl; 00851 //cout << "========= HM_D ========= :\n" << HM_D << endl; 00852 00853 // Blocks (1-4,2) 00854 for (int i=0;i<4;i++) 00855 { 00856 EIGEN_ALIGN16 const double aux_vals[] = { 00857 .0, -HM_D(2,i), HM_D(1,i), 00858 HM_D(2,i), .0, -HM_D(0,i), 00859 -HM_D(1,i), HM_D(0,i), .0 }; 00860 dAeD_de.block<3,3>(i*3,3) = -ROTA * mrpt::math::CMatrixDouble33(aux_vals); 00861 } 00862 00863 // (3): Apply chain rule: 00864 // 00865 jacob.noalias() = dLnRelPose_deps * dAeD_de; 00866 00867 if (is_inverse_edge_jacobian) 00868 { 00869 // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon 00870 jacob = -jacob; 00871 } 00872 } 00873 }; // end of "compute_jacobian_dAepsDx_deps", Case: SE(3) relative-poses, SE(3) poses: 00874 00875 00876 00877 // ==================================================================== 00878 // j,i lm_id,base_id 00879 // \partial h \partial h 00880 // l obs_frame_id 00881 // dh_df = ------------------ = --------------------------------- 00882 // 00883 // \partial f \partial f 00884 // 00885 // Note: f=relative position of landmark with respect to its base kf 00886 // ==================================================================== 00887 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS> 00888 void RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::compute_jacobian_dh_df( 00889 typename TSparseBlocksJacobians_dh_df::TEntry &jacob, 00890 const k2f_edge_t & observation, 00891 std::vector<const pose_flag_t*> *out_list_of_required_num_poses) const 00892 { 00893 MRPT_UNUSED_PARAM(observation); 00894 if (! *jacob.sym.is_valid ) 00895 return; // Another block of the same Jacobian row said this observation was invalid for some reason. 00896 00897 // Handle the special case when obs==base, for which rel_pose_base_from_obs==NULL 00898 const pose_flag_t * rel_pose_base_from_obs = jacob.sym.rel_pose_base_from_obs; 00899 00900 if (out_list_of_required_num_poses && rel_pose_base_from_obs) 00901 out_list_of_required_num_poses->push_back(jacob.sym.rel_pose_base_from_obs); 00902 00903 // make sure the numeric spanning tree is working and updating all that we need: 00904 if (rel_pose_base_from_obs) 00905 { 00906 #if DEBUG_NOT_UPDATED_ENTRIES 00907 TNumSTData d1 = check_num_st_entry_exists(rel_pose_base_from_obs, rba_state.spanning_tree); 00908 #endif 00909 if (!rel_pose_base_from_obs->updated) 00910 { 00911 #if DEBUG_NOT_UPDATED_ENTRIES 00912 cout << "not updated ST entry for: from=" << d1.from << ", to=" << d1.to << endl; 00913 #endif 00914 rba_state.spanning_tree.save_as_dot_file("_debug_jacob_error_all_STs.dot"); 00915 ASSERT_(rel_pose_base_from_obs->updated) 00916 } 00917 } 00918 00919 // First, we need x^{j,i}_i: 00920 //const mrpt::math::TPoint3D & xji_i = jacob.sym.feat_rel_pos->pos; 00921 const array_landmark_t &xji_i = jacob.sym.feat_rel_pos->pos; 00922 00923 array_landmark_t xji_l = xji_i; // 00924 00925 if (rel_pose_base_from_obs!=NULL) 00926 { 00927 #if 0 00928 cout << "dh_df(ft_id="<< observation.obs.obs.feat_id << ", obs_kf="<< observation.obs.kf_id << "): o2b=" << *rel_pose_base_from_obs << endl; 00929 #endif 00930 // xji_l = rel_pose_base_from_obs (+) xji_i 00931 //rel_pose_base_from_obs->pose.composePoint(xji_i, xji_l); 00932 landmark_t::composePosePoint(xji_l, rel_pose_base_from_obs->pose); 00933 } 00934 else 00935 { 00936 // I'm observing from the same base key-frame: xji_l = xji_i (already done above) 00937 } 00938 00939 00940 #if SRBA_COMPUTE_NUMERIC_JACOBIANS 00941 // Numeric jacobians 00942 typename TSparseBlocksJacobians_dh_df::matrix_t num_jacob; 00943 00944 array_landmark_t x; 00945 x.setZero(); // Evaluate Jacobian at incr around origin 00946 array_landmark_t x_incrs; 00947 x_incrs.setConstant(1e-3); 00948 00949 const TNumeric_dh_df_params num_params(&rel_pose_base_from_obs->pose,jacob.sym.feat_rel_pos->pos,this->parameters.sensor,this->parameters.sensor_pose); 00950 00951 mrpt::math::jacobians::jacob_numeric_estimate(x,&numeric_dh_df,x_incrs,num_params,num_jacob); 00952 00953 #endif // SRBA_COMPUTE_NUMERIC_JACOBIANS 00954 00955 00956 #if SRBA_COMPUTE_ANALYTIC_JACOBIANS 00957 // d h(x^{j,i}_l) d h(x') d x' 00958 // --------------- = --------- * ---------- 00959 // d f d x' d x 00960 // 00961 // With: x' = x^{j,i}_l 00962 00963 // First jacobian: (uses xji_l) 00964 // ----------------------------- 00965 Eigen::Matrix<double,OBS_DIMS,LM_DIMS> dh_dx; 00966 00967 // Converts a point relative to the robot coordinate frame (P) into a point relative to the sensor (RES = P \ominus POSE_IN_ROBOT ) 00968 RBA_OPTIONS::sensor_pose_on_robot_t::template point_robot2sensor<landmark_t,array_landmark_t>(xji_l,xji_l,this->parameters.sensor_pose ); 00969 00970 // Invoke sensor model: 00971 if (!sensor_model_t::eval_jacob_dh_dx(dh_dx,xji_l, this->parameters.sensor)) 00972 { 00973 // Invalid Jacobian: 00974 *jacob.sym.is_valid = 0; 00975 jacob.num.setZero(); 00976 return; 00977 } 00978 00979 // take into account the possible displacement of the sensor wrt the keyframe: 00980 RBA_OPTIONS::sensor_pose_on_robot_t::jacob_dh_dx_rotate( dh_dx, this->parameters.sensor_pose ); 00981 00982 // Second Jacobian: Simply the 2x2 or 3x3 rotation matrix of base wrt observing 00983 // ------------------------------ 00984 if (rel_pose_base_from_obs!=NULL) 00985 { 00986 mrpt::math::CMatrixFixedNumeric<double,LM_DIMS,LM_DIMS> R(mrpt::math::UNINITIALIZED_MATRIX); 00987 rel_pose_base_from_obs->pose.getRotationMatrix(R); 00988 jacob.num.noalias() = dh_dx * R; 00989 } 00990 else 00991 { 00992 // if observing from the same base kf, we're done: 00993 jacob.num.noalias() = dh_dx; 00994 } 00995 #endif // SRBA_COMPUTE_ANALYTIC_JACOBIANS 00996 00997 00998 #if SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS 00999 // Check jacob.num vs. num_jacob 01000 const double MAX_REL_ERROR = 0.1; 01001 if ((jacob.num-num_jacob).array().abs().maxCoeff()>MAX_REL_ERROR*num_jacob.array().maxCoeff()) 01002 { 01003 std::cerr << "NUMERIC VS. ANALYTIC JACOBIAN dh_df FAILED:" 01004 << "\njacob.num:\n" << jacob.num 01005 << "\nnum_jacob:\n" << num_jacob 01006 << "\nDiff:\n" << jacob.num-num_jacob << endl << endl; 01007 } 01008 #endif 01009 01010 #if SRBA_USE_NUMERIC_JACOBIANS 01011 jacob.num = num_jacob; 01012 #endif 01013 } 01014 01015 01016 // ------------------------------------------------------------------------ 01017 // prepare_Jacobians_required_tree_roots() 01018 // ------------------------------------------------------------------------ 01019 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS> 01020 void RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::prepare_Jacobians_required_tree_roots( 01021 std::set<TKeyFrameID> & lst, 01022 const std::vector<typename TSparseBlocksJacobians_dh_dAp::col_t*> &lst_JacobCols_dAp, 01023 const std::vector<typename TSparseBlocksJacobians_dh_df::col_t*> &lst_JacobCols_df ) 01024 { 01025 lst.clear(); 01026 01027 const size_t nUnknowns_k2k = lst_JacobCols_dAp.size(); 01028 const size_t nUnknowns_k2f = lst_JacobCols_df.size(); 01029 01030 // k2k edges ------------------------------------------------------ 01031 for (size_t i=0;i<nUnknowns_k2k;i++) 01032 { 01033 // For each column, process each nonzero block: 01034 const typename TSparseBlocksJacobians_dh_dAp::col_t *col = lst_JacobCols_dAp[i]; 01035 01036 for (typename TSparseBlocksJacobians_dh_dAp::col_t::const_iterator it=col->begin();it!=col->end();++it) 01037 { 01038 // For each dh_dAp block we need: 01039 // * (d+1) -> obs 01040 // * (d+1) -> base 01041 01042 const size_t obs_idx = it->first; 01043 const typename TSparseBlocksJacobians_dh_dAp::TEntry & jacob_entry = it->second; 01044 01045 const TKeyFrameID obs_id = rba_state.all_observations[obs_idx].obs.kf_id; 01046 const TKeyFrameID d1_id = jacob_entry.sym.kf_d; 01047 const TKeyFrameID base_id = jacob_entry.sym.kf_base; 01048 01049 add_edge_ij_to_list_needed_roots(lst, d1_id , obs_id ); 01050 add_edge_ij_to_list_needed_roots(lst, base_id, d1_id ); 01051 } 01052 } 01053 01054 // k2f edges ------------------------------------------------------ 01055 for (size_t i=0;i<nUnknowns_k2f;i++) 01056 { 01057 // For each column, process each nonzero block: 01058 const typename TSparseBlocksJacobians_dh_df::col_t *col = lst_JacobCols_df[i]; 01059 01060 for (typename TSparseBlocksJacobians_dh_df::col_t::const_iterator it=col->begin();it!=col->end();++it) 01061 { 01062 // For each dh_df block we need: 01063 // * obs -> base 01064 const size_t obs_idx = it->first; 01065 const k2f_edge_t &k2f = 01066 rba_state.all_observations[obs_idx]; 01067 01068 ASSERT_(k2f.feat_rel_pos) 01069 01070 const TKeyFrameID obs_id = k2f.obs.kf_id; 01071 const TKeyFrameID base_id = k2f.feat_rel_pos->id_frame_base; 01072 01073 add_edge_ij_to_list_needed_roots(lst, base_id, obs_id ); 01074 } 01075 } 01076 } 01077 01078 01079 // ------------------------------------------------------------------------ 01080 // recompute_all_Jacobians(): Re-evaluate all Jacobians numerically 01081 // ------------------------------------------------------------------------ 01082 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS> 01083 size_t RbaEngine<KF2KF_POSE_TYPE,LM_TYPE,OBS_TYPE,RBA_OPTIONS>::recompute_all_Jacobians( 01084 std::vector<typename TSparseBlocksJacobians_dh_dAp::col_t*> &lst_JacobCols_dAp, 01085 std::vector<typename TSparseBlocksJacobians_dh_df::col_t*> &lst_JacobCols_df, 01086 std::vector<const typename kf2kf_pose_traits<kf2kf_pose_t>::pose_flag_t*> * out_list_of_required_num_poses ) 01087 { 01088 size_t nJacobs=0; 01089 if (out_list_of_required_num_poses) out_list_of_required_num_poses->clear(); 01090 01091 const size_t nUnknowns_k2k = lst_JacobCols_dAp.size(); 01092 01093 // k2k edges ------------------------------------------------------ 01094 for (size_t i=0;i<nUnknowns_k2k;i++) 01095 { 01096 // For each column, process each nonzero block: 01097 typename TSparseBlocksJacobians_dh_dAp::col_t *col = lst_JacobCols_dAp[i]; 01098 01099 for (typename TSparseBlocksJacobians_dh_dAp::col_t::iterator it=col->begin();it!=col->end();++it) 01100 { 01101 const size_t obs_idx = it->first; 01102 typename TSparseBlocksJacobians_dh_dAp::TEntry & jacob_entry = it->second; 01103 compute_jacobian_dh_dp( 01104 jacob_entry, 01105 rba_state.all_observations[obs_idx], 01106 rba_state.k2k_edges, 01107 out_list_of_required_num_poses ); 01108 nJacobs++; 01109 } 01110 } 01111 01112 // k2f edges ------------------------------------------------------ 01113 // Only if we are in landmarks-based SLAM, not in graph-SLAM: 01114 nJacobs += internal::recompute_all_Jacobians_dh_df<landmark_t::jacob_family>::eval(*this, lst_JacobCols_df,out_list_of_required_num_poses); 01115 01116 return nJacobs; 01117 } // end of recompute_all_Jacobians() 01118 01119 01120 } // end of namespace