16 namespace mrpt {
namespace srba {
18 #ifndef SRBA_USE_NUMERIC_JACOBIANS
19 # define SRBA_USE_NUMERIC_JACOBIANS 0
21 #ifndef SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS
22 # define SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS 0
25 #define SRBA_COMPUTE_NUMERIC_JACOBIANS (SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS || SRBA_USE_NUMERIC_JACOBIANS)
26 #define SRBA_COMPUTE_ANALYTIC_JACOBIANS (SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS || !SRBA_USE_NUMERIC_JACOBIANS)
28 #define DEBUG_JACOBIANS_SUPER_VERBOSE 0
29 #define DEBUG_NOT_UPDATED_ENTRIES 0 // Extremely slow, just for debug during development! This checks that all the expected Jacobians are actually updated
37 template <landmark_jacob_family_t LM_JACOB_FAMILY>
42 template <
class RBAENGINE,
class LSTJACOBCOLS,
class LSTPOSES>
45 LSTJACOBCOLS &lst_JacobCols_df,
46 LSTPOSES * out_list_of_required_num_poses )
48 const size_t nUnknowns_k2f = lst_JacobCols_df.size();
50 for (
size_t i=0;i<nUnknowns_k2f;i++)
53 typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t *col = lst_JacobCols_df[i];
57 const size_t obs_idx = it->first;
58 typename RBAENGINE::TSparseBlocksJacobians_dh_df::TEntry & jacob_entry = it->second;
59 rba.compute_jacobian_dh_df(
61 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
64 rba.get_rba_state().all_observations[obs_idx],
65 out_list_of_required_num_poses );
76 template <
class RBAENGINE,
class LSTJACOBCOLS,
class LSTPOSES>
79 LSTJACOBCOLS &lst_JacobCols_df,
80 LSTPOSES * out_list_of_required_num_poses )
92 template <
class KF2KF_POSE_TYPE,
class LM_TYPE,
class OBS_TYPE,
class RBA_OPTIONS>
96 mrpt::poses::SE_traits<pose_t::rotation_dimensions>::pseudo_exp(x,incr);
103 else base_from_obs = incr;
112 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
123 p_d_d1_mod.composeFrom(incr, p_d_d1);
124 p_d_d1_mod.inverse();
128 base_from_obs = *params.
pose_d1_wrt_obs + p_d_d1_mod + pose_base_wrt_d_prime;
129 else base_from_obs = p_d_d1_mod + pose_base_wrt_d_prime;
134 RBA_OPTIONS::sensor_pose_on_robot_t::pose_robot2sensor( base_from_obs, base_pose_wrt_sensor, params.
sensor_pose );
140 sensor_model_t::observe_error(y,z_zero,base_pose_wrt_sensor,params.
xji_i, params.
sensor_params);
144 #if DEBUG_NOT_UPDATED_ENTRIES
151 TNumSTData check_num_st_entry_exists(
152 const pose_flag_t * entry,
153 const TRBA_Problem_state::TSpanningTree & st)
158 const frameid2pose_map_t & tree = it_tree->second;
163 const pose_flag_t & e = it->second;
178 template <
class KF2KF_POSE_TYPE,
class LM_TYPE,
class OBS_TYPE,
class RBA_OPTIONS>
181 static const pose_t my_aux_null_pose;
188 RBA_OPTIONS::sensor_pose_on_robot_t::pose_robot2sensor( *pos_cam, base_pose_wrt_sensor, params.
sensor_pose );
193 sensor_model_t::observe_error(y,z_zero,base_pose_wrt_sensor,x_local, params.
sensor_params);
199 template <landmark_jacob_family_t JACOB_FAMILY,
size_t POINT_DIMS,
size_t POSE_DIMS,
class RBA_ENGINE_T>
215 template <
class KF2KF_POSE_TYPE,
class LM_TYPE,
class OBS_TYPE,
class RBA_OPTIONS>
217 typename TSparseBlocksJacobians_dh_dAp::TEntry &jacob,
220 std::vector<const pose_flag_t*> *out_list_of_required_num_poses)
const
224 if (! *jacob.sym.is_valid )
230 const pose_flag_t * pose_d1_wrt_obs = jacob.sym.rel_pose_d1_from_obs;
231 const pose_flag_t & pose_base_wrt_d1 = *jacob.sym.rel_pose_base_from_d1;
232 const bool is_inverse_edge_jacobian = !jacob.sym.edge_normal_dir;
234 if (out_list_of_required_num_poses)
236 if (jacob.sym.rel_pose_d1_from_obs)
237 out_list_of_required_num_poses->push_back(jacob.sym.rel_pose_d1_from_obs);
238 out_list_of_required_num_poses->push_back(jacob.sym.rel_pose_base_from_d1);
242 #if DEBUG_NOT_UPDATED_ENTRIES
243 TNumSTData d1 = check_num_st_entry_exists(&pose_base_wrt_d1, rba_state.spanning_tree);
246 if (!pose_base_wrt_d1.updated)
248 std::cerr <<
" kf_d+1: " << jacob.sym.kf_d <<
", base_id: "<< jacob.sym.kf_base << std::endl;
249 rba_state.spanning_tree.save_as_dot_file(
"_debug_jacob_error_all_STs.dot");
250 ASSERT_(pose_base_wrt_d1.updated)
255 #if DEBUG_NOT_UPDATED_ENTRIES
256 TNumSTData d2 = check_num_st_entry_exists(pose_d1_wrt_obs, rba_state.spanning_tree);
258 if (!pose_d1_wrt_obs->updated)
260 std::cerr <<
" kf_d+1: " << jacob.sym.kf_d <<
", obs_frame_id: "<< observation.
obs.
kf_id << std::endl;
261 rba_state.spanning_tree.save_as_dot_file(
"_debug_jacob_error_all_STs.dot");
263 ASSERT_(pose_d1_wrt_obs->updated)
269 if (pose_d1_wrt_obs!=NULL)
270 pose_i_wrt_l.composeFrom( pose_d1_wrt_obs->pose, pose_base_wrt_d1.pose);
271 else pose_i_wrt_l = pose_base_wrt_d1.pose;
279 LM_TYPE::composePosePoint(xji_l, pose_i_wrt_l);
281 #if DEBUG_JACOBIANS_SUPER_VERBOSE // Debug:
286 cout <<
"compute_jacobian_dh_dp: "
287 <<
" obs_frame_id: " << obs_frame_id
288 <<
" base_id: " << base_id
289 <<
" kf_d+1: " << d_plus_1_id
290 <<
" k2k_edge_id: " << jacob.sym.k2k_edge_id
291 <<
" is_inverse: " << is_inverse_edge_jacobian << endl
292 <<
" pose_base_wrt_d1 (D): "<<pose_base_wrt_d1.pose << endl;
293 if (pose_d1_wrt_obs) cout <<
" pose_d1_wrt_obs (A): "<< pose_d1_wrt_obs->pose<< endl;
299 #if SRBA_COMPUTE_NUMERIC_JACOBIANS
301 typename TSparseBlocksJacobians_dh_dAp::matrix_t num_jacob;
306 x_incrs.setConstant(1e-4);
308 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);
312 #endif // SRBA_COMPUTE_NUMERIC_JACOBIANS
315 #if SRBA_COMPUTE_ANALYTIC_JACOBIANS
324 Eigen::Matrix<double,OBS_DIMS,LM_DIMS> dh_dx;
327 RBA_OPTIONS::sensor_pose_on_robot_t::template point_robot2sensor<LM_TYPE,array_landmark_t>(xji_l,xji_l,this->parameters.sensor_pose );
330 if (!sensor_model_t::eval_jacob_dh_dx(dh_dx,xji_l, this->parameters.sensor))
333 *jacob.sym.is_valid = 0;
339 RBA_OPTIONS::sensor_pose_on_robot_t::jacob_dh_dx_rotate( dh_dx, this->parameters.sensor_pose );
343 compute_jacobian_dAepsDx_deps<LM_TYPE::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);
345 #endif // SRBA_COMPUTE_ANALYTIC_JACOBIANS
348 #if SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS
350 const double MAX_REL_ERROR = 0.1;
351 if ((jacob.num-num_jacob).array().abs().maxCoeff()>MAX_REL_ERROR*num_jacob.array().maxCoeff())
353 std::cerr <<
"NUMERIC VS. ANALYTIC JACOBIAN dh_dAp FAILED:"
354 <<
"\njacob.num:\n" << jacob.num
355 <<
"\nnum_jacob:\n" << num_jacob
356 <<
"\nDiff:\n" << jacob.num-num_jacob << endl << endl;
360 #if SRBA_USE_NUMERIC_JACOBIANS
361 jacob.num = num_jacob;
372 template <
class RBA_ENGINE_T>
375 template <
class MATRIX,
class MATRIX_DH_DX,
class POINT,
class pose_flag_t,
class JACOB_SYM_T,
class K2K_EDGES_T,
class OBS_VECTOR>
378 const MATRIX_DH_DX & dh_dx,
379 const bool is_inverse_edge_jacobian,
381 const pose_flag_t * pose_d1_wrt_obs,
382 const pose_flag_t & pose_base_wrt_d1,
383 const JACOB_SYM_T & jacob_sym,
384 const K2K_EDGES_T & k2k_edges,
385 const OBS_VECTOR & all_obs
390 if (!is_inverse_edge_jacobian)
399 Eigen::Matrix<double,RBA_ENGINE_T::OBS_DIMS,3> H_ROTA;
400 if (pose_d1_wrt_obs!=NULL)
405 H_ROTA = dh_dx * pose_d1_wrt_obs->pose.getRotationMatrix();
414 jacob.block(0,0,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA;
418 Eigen::Matrix<double,3,1> v;
419 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);
420 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);
421 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);
423 Eigen::Matrix<double,3,3> aux;
429 aux.coeffRef(1,2)=-v[0];
430 aux.coeffRef(2,1)= v[0];
431 aux.coeffRef(2,0)=-v[1];
432 aux.coeffRef(0,2)= v[1];
433 aux.coeffRef(0,1)=-v[2];
434 aux.coeffRef(1,0)= v[2];
436 jacob.block(0,3,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA * aux;
444 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size())
445 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id]
446 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
454 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
459 Eigen::Matrix<double,RBA_ENGINE_T::OBS_DIMS,3> H_ROTA;
460 if (pose_d1_wrt_obs!=NULL)
468 H_ROTA = dh_dx * pose_d1_wrt_obs->pose.getRotationMatrix() * p_d_d1.getRotationMatrix().transpose();
475 H_ROTA = dh_dx * p_d_d1.getRotationMatrix().transpose();
479 jacob.block(0,0,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA;
483 Eigen::Matrix<double,3,1> v;
484 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);
485 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);
486 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);
488 Eigen::Matrix<double,3,3> aux;
494 aux.coeffRef(1,2)=-v[0];
495 aux.coeffRef(2,1)= v[0];
496 aux.coeffRef(2,0)=-v[1];
497 aux.coeffRef(0,2)= v[1];
498 aux.coeffRef(0,1)=-v[2];
499 aux.coeffRef(1,0)= v[2];
501 jacob.block(0,3,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA * aux;
515 template <
size_t POINT_DIMS,
class RBA_ENGINE_T>
518 template <
class MATRIX,
class MATRIX_DH_DX,
class POINT,
class pose_flag_t,
class JACOB_SYM_T,
class K2K_EDGES_T,
class OBS_VECTOR>
521 const MATRIX_DH_DX & dh_dx,
522 const bool is_inverse_edge_jacobian,
524 const pose_flag_t * pose_d1_wrt_obs,
525 const pose_flag_t & pose_base_wrt_d1,
526 const JACOB_SYM_T & jacob_sym,
527 const K2K_EDGES_T & k2k_edges,
528 const OBS_VECTOR & all_obs
534 if (!is_inverse_edge_jacobian)
537 const double Xd=pose_base_wrt_d1.pose.x();
538 const double Yd=pose_base_wrt_d1.pose.y();
543 if (pose_d1_wrt_obs!=NULL)
547 PHIa = pose_d1_wrt_obs->pose.phi();
548 AD.
composeFrom(pose_d1_wrt_obs->pose,pose_base_wrt_d1.pose);
552 AD = pose_base_wrt_d1.pose;
556 Eigen::Matrix<double,POINT_DIMS,3 > dPx_P;
557 const double ccos_ad = cos(AD.
phi());
558 const double ssin_ad = sin(AD.
phi());
559 dPx_P(0,0) = 1; dPx_P(0,1) = 0; dPx_P(0,2) = -xji_i[0]*ssin_ad - xji_i[1]*ccos_ad;
560 dPx_P(1,0) = 0; dPx_P(1,1) = 1; dPx_P(1,2) = xji_i[0]*ccos_ad - xji_i[1]*ssin_ad;
562 dPx_P(2,0) = 0; dPx_P(2,1) = 0; dPx_P(2,2) = 1;
566 Eigen::Matrix<double,3,3> dAD_deps;
567 const double ccos_a = cos(PHIa);
568 const double ssin_a = sin(PHIa);
569 dAD_deps(0,0) = ccos_a; dAD_deps(0,1) = -ssin_a;
570 dAD_deps(1,0) = ssin_a; dAD_deps(1,1) = ccos_a;
571 dAD_deps(2,0) = 0; dAD_deps(2,1) = 0;
573 dAD_deps(0,2) = -ssin_a*Xd - ccos_a*Yd;
574 dAD_deps(1,2) = ccos_a*Xd - ssin_a*Yd;
578 jacob.noalias() = dh_dx * dPx_P * dAD_deps;
587 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size())
588 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id]
589 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
597 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
600 const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1;
602 typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ?
603 (pose_d1_wrt_obs->pose + p_d_d1_inv)
610 const double Xd=pose_base_wrt_d1_prime.x();
611 const double Yd=pose_base_wrt_d1_prime.y();
614 const double PHIa=A_prime.phi();
618 Eigen::Matrix<double,POINT_DIMS,3 > dPx_P;
619 const double ccos_ad = cos(AD.
phi());
620 const double ssin_ad = sin(AD.
phi());
621 dPx_P(0,0) = 1; dPx_P(0,1) = 0; dPx_P(0,2) = -xji_i[0]*ssin_ad - xji_i[1]*ccos_ad;
622 dPx_P(1,0) = 0; dPx_P(1,1) = 1; dPx_P(1,2) = xji_i[0]*ccos_ad - xji_i[1]*ssin_ad;
624 dPx_P(2,0) = 0; dPx_P(2,1) = 0; dPx_P(2,2) = 1;
628 Eigen::Matrix<double,3,3> dAD_deps;
629 const double ccos_a = cos(PHIa);
630 const double ssin_a = sin(PHIa);
631 dAD_deps(0,0) = ccos_a; dAD_deps(0,1) = -ssin_a;
632 dAD_deps(1,0) = ssin_a; dAD_deps(1,1) = ccos_a;
633 dAD_deps(2,0) = 0; dAD_deps(2,1) = 0;
635 dAD_deps(0,2) = -ssin_a*Xd - ccos_a*Yd;
636 dAD_deps(1,2) = ccos_a*Xd - ssin_a*Yd;
640 jacob.noalias() = dh_dx * dPx_P * dAD_deps;
651 template <
class RBA_ENGINE_T>
658 template <
class RBA_ENGINE_T>
665 template <
class RBA_ENGINE_T>
668 template <
class MATRIX,
class MATRIX_DH_DX,
class POINT,
class pose_flag_t,
class JACOB_SYM_T,
class K2K_EDGES_T,
class OBS_VECTOR>
671 const MATRIX_DH_DX & dh_dx,
672 const bool is_inverse_edge_jacobian,
674 const pose_flag_t * pose_d1_wrt_obs,
675 const pose_flag_t & pose_base_wrt_d1,
676 const JACOB_SYM_T & jacob_sym,
677 const K2K_EDGES_T & k2k_edges,
678 const OBS_VECTOR & all_obs
686 if (!is_inverse_edge_jacobian)
689 Xd=pose_base_wrt_d1.pose.x();
690 Yd=pose_base_wrt_d1.pose.y();
694 if (pose_d1_wrt_obs!=NULL)
697 PHIa = pose_d1_wrt_obs->pose.phi();
698 base_wrt_obs.
composeFrom(pose_d1_wrt_obs->pose, pose_base_wrt_d1.pose);
702 base_wrt_obs = pose_base_wrt_d1.pose;
712 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size())
713 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id]
714 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
722 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
725 const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1;
727 typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ?
728 (pose_d1_wrt_obs->pose + p_d_d1_inv)
732 Xd=pose_base_wrt_d1_prime.x();
733 Yd=pose_base_wrt_d1_prime.y();
738 base_wrt_obs.
composeFrom(A_prime, pose_base_wrt_d1_prime);
742 const double PHIad = base_wrt_obs.
phi();
744 const double ccos_ad = cos(PHIad), ssin_ad=sin(PHIad);
745 const double ccos_a = cos(PHIa), ssin_a=sin(PHIa);
747 Eigen::Matrix<double,3,3> J0;
748 J0(0,0)= ccos_ad; J0(0,1)= ssin_ad; J0(0,2)= 0;
749 J0(1,0)=-ssin_ad; J0(1,1)= ccos_ad; J0(1,2)= 0;
750 J0(2,0)=0; J0(2,1)=0; J0(2,2)= 1;
752 Eigen::Matrix<double,3,3> J1;
753 J1(0,0)=1; J1(0,1)=0; J1(0,2)= -Xd*ssin_a-Yd*ccos_a;
754 J1(1,0)=0; J1(1,1)=1; J1(1,2)= Xd*ccos_a-Yd*ssin_a;
755 J1(2,0)=0; J1(2,1)=0; J1(2,2)= 1;
757 Eigen::Matrix<double,3,3> J2;
758 J2(0,0)=ccos_a; J2(0,1)=-ssin_a; J2(0,2)=0;
759 J2(1,0)=ssin_a; J2(1,1)= ccos_a; J2(1,2)=0;
760 J2(2,0)=0; J2(2,1)=0; J2(2,2)=1;
763 jacob.noalias() = dh_dx * J0* J1 * J2;
765 if (is_inverse_edge_jacobian)
774 template <
class RBA_ENGINE_T>
777 template <
class MATRIX,
class MATRIX_DH_DX,
class POINT,
class pose_flag_t,
class JACOB_SYM_T,
class K2K_EDGES_T,
class OBS_VECTOR>
780 const MATRIX_DH_DX & dh_dx,
781 const bool is_inverse_edge_jacobian,
783 const pose_flag_t * pose_d1_wrt_obs,
784 const pose_flag_t & pose_base_wrt_d1,
785 const JACOB_SYM_T & jacob_sym,
786 const K2K_EDGES_T & k2k_edges,
787 const OBS_VECTOR & all_obs
804 if (!is_inverse_edge_jacobian)
806 D = pose_base_wrt_d1.pose;
809 if (pose_d1_wrt_obs!=NULL)
812 base_wrt_obs.composeFrom(pose_d1_wrt_obs->pose, D);
813 ROTA = pose_d1_wrt_obs->pose.getRotationMatrix();
828 ASSERT_(jacob_sym.k2k_edge_id<k2k_edges.size())
829 const typename RBA_ENGINE_T::pose_t & p_d_d1 = k2k_edges[jacob_sym.k2k_edge_id]
830 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
838 pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
841 const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1;
843 typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ?
844 (pose_d1_wrt_obs->pose + p_d_d1_inv)
848 ROTA=A_prime.getRotationMatrix();
850 D=pose_base_wrt_d1_prime;
852 base_wrt_obs.composeFrom(A_prime, pose_base_wrt_d1_prime);
861 Eigen::Matrix<double,6,12> dLnRelPose_deps;
862 dLnRelPose_deps.setZero();
863 dLnRelPose_deps.block<3,3>(0,9).setIdentity();
867 dLnRelPose_deps.block<3,9>(3,0) = dLnRot_dRot;
877 Eigen::Matrix<double,12,6> dAeD_de;
879 dAeD_de.block<9,3>(0,0).setZero();
881 dAeD_de.block<3,3>(9,0) = (ROTA * HM_D.block<3,3>(0,0)).transpose();
887 for (
int i=0;i<4;i++)
889 EIGEN_ALIGN16
const double aux_vals[] = {
890 .0, -HM_D(2,i), HM_D(1,i),
891 HM_D(2,i), .0, -HM_D(0,i),
892 -HM_D(1,i), HM_D(0,i), .0 };
898 jacob.noalias() = dLnRelPose_deps * dAeD_de;
900 if (is_inverse_edge_jacobian)
920 template <
class KF2KF_POSE_TYPE,
class LM_TYPE,
class OBS_TYPE,
class RBA_OPTIONS>
922 typename TSparseBlocksJacobians_dh_df::TEntry &jacob,
924 std::vector<const pose_flag_t*> *out_list_of_required_num_poses)
const
927 if (! *jacob.sym.is_valid )
931 const pose_flag_t * rel_pose_base_from_obs = jacob.sym.rel_pose_base_from_obs;
933 if (out_list_of_required_num_poses && rel_pose_base_from_obs)
934 out_list_of_required_num_poses->push_back(jacob.sym.rel_pose_base_from_obs);
937 if (rel_pose_base_from_obs)
939 #if DEBUG_NOT_UPDATED_ENTRIES
940 TNumSTData d1 = check_num_st_entry_exists(rel_pose_base_from_obs, rba_state.spanning_tree);
942 if (!rel_pose_base_from_obs->updated)
944 #if DEBUG_NOT_UPDATED_ENTRIES
945 cout <<
"not updated ST entry for: from=" << d1.from <<
", to=" << d1.to << endl;
947 rba_state.spanning_tree.save_as_dot_file(
"_debug_jacob_error_all_STs.dot");
948 ASSERT_(rel_pose_base_from_obs->updated)
958 if (rel_pose_base_from_obs!=NULL)
961 cout <<
"dh_df(ft_id="<< observation.
obs.
obs.
feat_id <<
", obs_kf="<< observation.
obs.
kf_id <<
"): o2b=" << *rel_pose_base_from_obs << endl;
965 LM_TYPE::composePosePoint(xji_l, rel_pose_base_from_obs->pose);
973 #if SRBA_COMPUTE_NUMERIC_JACOBIANS
975 typename TSparseBlocksJacobians_dh_df::matrix_t num_jacob;
980 x_incrs.setConstant(1e-3);
982 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);
986 #endif // SRBA_COMPUTE_NUMERIC_JACOBIANS
989 #if SRBA_COMPUTE_ANALYTIC_JACOBIANS
998 Eigen::Matrix<double,OBS_DIMS,LM_DIMS> dh_dx;
1001 RBA_OPTIONS::sensor_pose_on_robot_t::template point_robot2sensor<LM_TYPE,array_landmark_t>(xji_l,xji_l,this->parameters.sensor_pose );
1004 if (!sensor_model_t::eval_jacob_dh_dx(dh_dx,xji_l, this->parameters.sensor))
1007 *jacob.sym.is_valid = 0;
1008 jacob.num.setZero();
1013 RBA_OPTIONS::sensor_pose_on_robot_t::jacob_dh_dx_rotate( dh_dx, this->parameters.sensor_pose );
1017 if (rel_pose_base_from_obs!=NULL)
1020 rel_pose_base_from_obs->pose.getRotationMatrix(R);
1021 jacob.num.noalias() = dh_dx * R;
1026 jacob.num.noalias() = dh_dx;
1028 #endif // SRBA_COMPUTE_ANALYTIC_JACOBIANS
1031 #if SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS
1033 const double MAX_REL_ERROR = 0.1;
1034 if ((jacob.num-num_jacob).array().abs().maxCoeff()>MAX_REL_ERROR*num_jacob.array().maxCoeff())
1036 std::cerr <<
"NUMERIC VS. ANALYTIC JACOBIAN dh_df FAILED:"
1037 <<
"\njacob.num:\n" << jacob.num
1038 <<
"\nnum_jacob:\n" << num_jacob
1039 <<
"\nDiff:\n" << jacob.num-num_jacob << endl << endl;
1043 #if SRBA_USE_NUMERIC_JACOBIANS
1044 jacob.num = num_jacob;
1052 template <
class KF2KF_POSE_TYPE,
class LM_TYPE,
class OBS_TYPE,
class RBA_OPTIONS>
1054 std::set<TKeyFrameID> & lst,
1055 const std::vector<typename TSparseBlocksJacobians_dh_dAp::col_t*> &lst_JacobCols_dAp,
1056 const std::vector<typename TSparseBlocksJacobians_dh_df::col_t*> &lst_JacobCols_df )
1060 const size_t nUnknowns_k2k = lst_JacobCols_dAp.size();
1061 const size_t nUnknowns_k2f = lst_JacobCols_df.size();
1064 for (
size_t i=0;i<nUnknowns_k2k;i++)
1067 const typename TSparseBlocksJacobians_dh_dAp::col_t *col = lst_JacobCols_dAp[i];
1075 const size_t obs_idx = it->first;
1076 const typename TSparseBlocksJacobians_dh_dAp::TEntry & jacob_entry = it->second;
1078 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
1079 const TKeyFrameID obs_id = rba_state.all_observations[obs_idx]->obs.kf_id;
1081 const TKeyFrameID obs_id = rba_state.all_observations[obs_idx].obs.kf_id;
1084 const TKeyFrameID base_id = jacob_entry.sym.kf_base;
1086 add_edge_ij_to_list_needed_roots(lst, d1_id , obs_id );
1087 add_edge_ij_to_list_needed_roots(lst, base_id, d1_id );
1092 for (
size_t i=0;i<nUnknowns_k2f;i++)
1095 const typename TSparseBlocksJacobians_dh_df::col_t *col = lst_JacobCols_df[i];
1101 const size_t obs_idx = it->first;
1103 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
1106 rba_state.all_observations[obs_idx];
1113 add_edge_ij_to_list_needed_roots(lst, base_id, obs_id );
1122 template <
class KF2KF_POSE_TYPE,
class LM_TYPE,
class OBS_TYPE,
class RBA_OPTIONS>
1124 std::vector<typename TSparseBlocksJacobians_dh_dAp::col_t*> &lst_JacobCols_dAp,
1125 std::vector<typename TSparseBlocksJacobians_dh_df::col_t*> &lst_JacobCols_df,
1129 if (out_list_of_required_num_poses) out_list_of_required_num_poses->clear();
1131 const size_t nUnknowns_k2k = lst_JacobCols_dAp.size();
1134 for (
size_t i=0;i<nUnknowns_k2k;i++)
1137 typename TSparseBlocksJacobians_dh_dAp::col_t *col = lst_JacobCols_dAp[i];
1141 const size_t obs_idx = it->first;
1142 typename TSparseBlocksJacobians_dh_dAp::TEntry & jacob_entry = it->second;
1143 compute_jacobian_dh_dp(
1145 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
1146 *rba_state.all_observations[obs_idx],
1148 rba_state.all_observations[obs_idx],
1150 rba_state.k2k_edges,
1151 out_list_of_required_num_poses );
Auxiliary sub-jacobian used in compute_jacobian_dh_dp() (it's a static method within specializations ...
static size_t eval(RBAENGINE &rba, LSTJACOBCOLS &lst_JacobCols_df, LSTPOSES *out_list_of_required_num_poses)
void compute_jacobian_dh_dp(typename TSparseBlocksJacobians_dh_dAp::TEntry &jacob, const k2f_edge_t &observation, const k2k_edges_deque_t &k2k_edges, std::vector< const pose_flag_t * > *out_list_of_required_num_poses) const
==================================================================== j,i lm_id,base_id h h l obs_fr...
obs_traits_t::observation_t obs
Observation data.
TLandmarkID feat_id
Observed what.
Typedefs for determining whether the result of combining a KF pose (+) a sensor pose leads to a SE(2)...
const double & phi() const
Get the phi angle of the 2D pose (in radians)
const RBA_OPTIONS::sensor_pose_on_robot_t::parameters_t & sensor_pose
const bool is_inverse_dir
rba_problem_state_t::k2k_edges_deque_t k2k_edges_deque_t
A list (deque) of KF-to-KF edges (unknown relative poses).
const array_landmark_t & xji_i
size_t recompute_all_Jacobians(std::vector< typename TSparseBlocksJacobians_dh_dAp::col_t * > &lst_JacobCols_dAp, std::vector< typename TSparseBlocksJacobians_dh_df::col_t * > &lst_JacobCols_df, std::vector< const pose_flag_t * > *out_list_of_required_num_poses=NULL)
Re-evaluate all Jacobians numerically using their symbolic info.
const Scalar * const_iterator
kf2kf_pose_traits_t::pose_flag_t pose_flag_t
KF2KF_POSE_TYPE::pose_t pose_t
The type of relative poses (e.g. mrpt::poses::CPose3D)
Auxiliary template for evaluating the dh_df part in recompute_all_Jacobians().
static void eval(MATRIX &jacob, const MATRIX_DH_DX &dh_dx, const bool is_inverse_edge_jacobian, const POINT &xji_i, const pose_flag_t *pose_d1_wrt_obs, const pose_flag_t &pose_base_wrt_d1, const JACOB_SYM_T &jacob_sym, const K2K_EDGES_T &k2k_edges, const OBS_VECTOR &all_obs)
const pose_t * pose_d1_wrt_obs
static void numeric_dh_df(const array_landmark_t &x, const TNumeric_dh_df_params ¶ms, array_obs_t &y)
Auxiliary method for numeric Jacobian: numerically evaluates the new observation "y" for a small incr...
lm_traits_t::TRelativeLandmarkPos * feat_rel_pos
Pointer to the known/unknown rel.pos. (always!=NULL)
const OBS_TYPE::TObservationParams & sensor_params
A numeric matrix of compile-time fixed size.
#define MRPT_UNUSED_PARAM(a)
Can be used to avoid "not used parameters" warnings from the compiler.
The argument "POSE_TRAITS" can be any of those defined in srba/models/kf2kf_poses.h (typically, either kf2kf_poses::SE3 or kf2kf_poses::SE2).
const k2k_edges_deque_t & k2k_edges
const pose_t & pose_base_wrt_d1
static void numeric_dh_dAp(const array_pose_t &x, const TNumeric_dh_dAp_params ¶ms, array_obs_t &y)
Auxiliary method for numeric Jacobian: numerically evaluates the new observation "y" for a small incr...
#define MRPT_COMPILE_TIME_ASSERT(f)
static void eval(MATRIX &jacob, const MATRIX_DH_DX &dh_dx, const bool is_inverse_edge_jacobian, const POINT &xji_i, const pose_flag_t *pose_d1_wrt_obs, const pose_flag_t &pose_base_wrt_d1, const JACOB_SYM_T &jacob_sym, const K2K_EDGES_T &k2k_edges, const OBS_VECTOR &all_obs)
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
void compute_jacobian_dh_df(typename TSparseBlocksJacobians_dh_df::TEntry &jacob, const k2f_edge_t &observation, std::vector< const pose_flag_t * > *out_list_of_required_num_poses) const
==================================================================== j,i lm_id,base_id h h l obs_fr...
A class used to store a 2D pose.
TKeyFrameID kf_id
Observed from.
const pose_t * pose_base_wrt_obs
const RBA_OPTIONS::sensor_pose_on_robot_t::parameters_t & sensor_pose
void composeFrom(const CPose2D &A, const CPose2D &B)
Makes .
A partial specialization of CArrayNumeric for double numbers.
Case: 2D or 3D points, SE(2) poses Both cases are grouped because a SE(2) pose doesn't transform the ...
void jacob_numeric_estimate(const VECTORLIKE &x, void(*functor)(const VECTORLIKE &x, const USERPARAM &y, VECTORLIKE3 &out), const VECTORLIKE2 &increments, const USERPARAM &userParam, MATRIXLIKE &out_Jacobian)
Numerical estimation of the Jacobian of a user-supplied function - this template redirects to mrpt::m...
"fake landmarks" used for relative pose observations
void prepare_Jacobians_required_tree_roots(std::set< TKeyFrameID > &kfs_num_spantrees_to_update, const std::vector< typename TSparseBlocksJacobians_dh_dAp::col_t * > &lst_JacobCols_dAp, const std::vector< typename TSparseBlocksJacobians_dh_df::col_t * > &lst_JacobCols_df)
Prepare the list of all required KF roots whose spanning trees need numeric updates with each optimiz...
static void ln_rot_jacob(const mrpt::math::CMatrixDouble33 &R, mrpt::math::CMatrixFixedNumeric< double, 3, 9 > &M)
Static function to compute the Jacobian of the SO(3) Logarithm function, evaluated at a given 3x3 rot...
const OBS_TYPE::TObservationParams & sensor_params
Keyframe-to-feature edge: observation data stored for each keyframe.
const array_landmark_t & xji_i
static size_t eval(RBAENGINE &rba, LSTJACOBCOLS &lst_JacobCols_df, LSTPOSES *out_list_of_required_num_poses)
static void eval(MATRIX &jacob, const MATRIX_DH_DX &dh_dx, const bool is_inverse_edge_jacobian, const POINT &xji_i, const pose_flag_t *pose_d1_wrt_obs, const pose_flag_t &pose_base_wrt_d1, const JACOB_SYM_T &jacob_sym, const K2K_EDGES_T &k2k_edges, const OBS_VECTOR &all_obs)
static void eval(MATRIX &jacob, const MATRIX_DH_DX &dh_dx, const bool is_inverse_edge_jacobian, const POINT &xji_i, const pose_flag_t *pose_d1_wrt_obs, const pose_flag_t &pose_base_wrt_d1, const JACOB_SYM_T &jacob_sym, const K2K_EDGES_T &k2k_edges, const OBS_VECTOR &all_obs)
uint64_t TKeyFrameID
Numeric IDs for key-frames (KFs)