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