Main MRPT website > C++ reference
MRPT logo
srba/include/mrpt/srba/impl/jacobians.h
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2015, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +---------------------------------------------------------------------------+ */
9 
10 #pragma once
11 
12 #include <mrpt/math/jacobians.h>
13 #include <mrpt/poses/SE_traits.h>
15 
16 namespace mrpt { namespace srba {
17 
18 #ifndef SRBA_USE_NUMERIC_JACOBIANS
19 # define SRBA_USE_NUMERIC_JACOBIANS 0
20 #endif
21 #ifndef SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS
22 # define SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS 0
23 #endif
24 
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)
27 
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
30 
31 
32 namespace internal {
33  /** Auxiliary template for evaluating the dh_df part in \a recompute_all_Jacobians().
34  * The extra complexity of adding this auxiliary template with specializations is required to
35  * avoid the compiler trying to evaluate the jacobians dh_df in relative SLAM problems, where
36  * the Jacobian does not exist. */
37  template <landmark_jacob_family_t LM_JACOB_FAMILY>
39 
40  // Specialization for "normal SLAM" (SLAM with real landmarks)
42  template <class RBAENGINE,class LSTJACOBCOLS,class LSTPOSES>
43  static size_t eval(
44  RBAENGINE &rba,
45  LSTJACOBCOLS &lst_JacobCols_df, // std::vector<typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t*>
46  LSTPOSES * out_list_of_required_num_poses ) // std::vector<const typename RBAENGINE::kf2kf_pose_traits<RBAENGINE::KF2KF_POSE_TYPE>::pose_flag_t*>
47  {
48  const size_t nUnknowns_k2f = lst_JacobCols_df.size();
49  size_t nJacobs = 0;
50  for (size_t i=0;i<nUnknowns_k2f;i++)
51  {
52  // For each column, process each nonzero block:
53  typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t *col = lst_JacobCols_df[i];
54 
55  for (typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t::iterator it=col->begin();it!=col->end();++it)
56  {
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(
60  jacob_entry,
61  #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
62  *
63  #endif
64  rba.get_rba_state().all_observations[obs_idx],
65  out_list_of_required_num_poses );
66  nJacobs++;
67  }
68  }
69  return nJacobs;
70  }
71 
72  };
73 
74  // Specialization for relative graph-SLAM (no real landmarks)
76  template <class RBAENGINE,class LSTJACOBCOLS,class LSTPOSES>
77  static size_t eval(
78  RBAENGINE &rba,
79  LSTJACOBCOLS &lst_JacobCols_df, // std::vector<typename RBAENGINE::TSparseBlocksJacobians_dh_df::col_t*>
80  LSTPOSES * out_list_of_required_num_poses ) // std::vector<const typename RBAENGINE::kf2kf_pose_traits<RBAENGINE::KF2KF_POSE_TYPE>::pose_flag_t*>
81  {
82  MRPT_UNUSED_PARAM(rba); MRPT_UNUSED_PARAM(lst_JacobCols_df);
83  MRPT_UNUSED_PARAM(out_list_of_required_num_poses);
84  // Nothing to do: this will never be actually called.
85  return 0;
86  }
87  };
88 } // end "internal" ns
89 
90 
91 /** Numeric implementation of the partial Jacobian dh_dAp */
92 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS>
94 {
96  mrpt::poses::SE_traits<pose_t::rotation_dimensions>::pseudo_exp(x,incr);
97 
99  if (!params.is_inverse_dir)
100  {
101  if (params.pose_d1_wrt_obs) // "A" in papers
102  base_from_obs.composeFrom(*params.pose_d1_wrt_obs,incr);
103  else base_from_obs = incr;
104  base_from_obs.composeFrom(base_from_obs,params.pose_base_wrt_d1);
105  }
106  else
107  {
108  // Changes due to the inverse pose:
109  // D becomes D' = p_d^{d+1} (+) D
110  ASSERT_(params.k2k_edge_id<params.k2k_edges.size())
111  const pose_t & p_d_d1 = params.k2k_edges[params.k2k_edge_id]
112 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
113  ->
114 #else
115  .
116 #endif
117  inv_pose;
118 
119  pose_t pose_base_wrt_d_prime(mrpt::poses::UNINITIALIZED_POSE); // D' in papers
120  pose_base_wrt_d_prime.composeFrom( p_d_d1, params.pose_base_wrt_d1 );
121 
123  p_d_d1_mod.composeFrom(incr, p_d_d1);
124  p_d_d1_mod.inverse();
125 
126  // total pose: base from obs = pose_d1_wrt_obs (+) inv(p_d_d1_mod) (+) D'
127  if (params.pose_d1_wrt_obs) // "A" in papers
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;
130  }
131 
132  // pose_robot2sensor(): pose wrt sensor = pose_wrt_robot (-) sensor_pose_on_the_robot
134  RBA_OPTIONS::sensor_pose_on_robot_t::pose_robot2sensor( base_from_obs, base_pose_wrt_sensor, params.sensor_pose );
135 
136  // Generate observation:
137  array_obs_t z_zero;
138  z_zero.setZero();
139 
140  sensor_model_t::observe_error(y,z_zero,base_pose_wrt_sensor,params.xji_i, params.sensor_params);
141  y=-y; // because the method above evals: "z_zero - h(x)"
142 }
143 
144 #if DEBUG_NOT_UPDATED_ENTRIES
145 
146 struct TNumSTData
147 {
148  TKeyFrameID from, to;
149 };
150 
151 TNumSTData check_num_st_entry_exists(
152  const pose_flag_t * entry,
153  const TRBA_Problem_state::TSpanningTree & st)
154 {
155  for (TRelativePosesForEachTarget::const_iterator it_tree=st.num.begin();it_tree!=st.num.end();++it_tree)
156  {
157  const TKeyFrameID id_from = it_tree->first;
158  const frameid2pose_map_t & tree = it_tree->second;
159 
160  for (frameid2pose_map_t::const_iterator it=tree.begin();it!=tree.end();++it)
161  {
162  const TKeyFrameID id_to = it->first;
163  const pose_flag_t & e = it->second;
164  if (&e == entry)
165  {
166  TNumSTData d;
167  d.from = id_from;
168  d.to = id_to;
169  return d;
170  }
171  }
172  }
173  ASSERT_(false)
174 }
175 
176 #endif
177 
178 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS>
180 {
181  static const pose_t my_aux_null_pose;
182 
183  const array_landmark_t x_local = params.xji_i + x;
184  const pose_t * pos_cam = params.pose_base_wrt_obs!=NULL ? params.pose_base_wrt_obs : &my_aux_null_pose;
185 
186  // pose_robot2sensor(): pose wrt sensor = pose_wrt_robot (-) sensor_pose_on_the_robot
188  RBA_OPTIONS::sensor_pose_on_robot_t::pose_robot2sensor( *pos_cam, base_pose_wrt_sensor, params.sensor_pose );
189 
190  // Generate observation:
191  array_obs_t z_zero;
192  z_zero.setZero();
193  sensor_model_t::observe_error(y,z_zero,base_pose_wrt_sensor,x_local, params.sensor_params);
194  y=-y; // because the method above evals: "z_zero - h(x)"
195 }
196 
197 
198 /** Auxiliary sub-jacobian used in compute_jacobian_dh_dp() (it's a static method within specializations of this struct) */
199 template <landmark_jacob_family_t JACOB_FAMILY, size_t POINT_DIMS, size_t POSE_DIMS, class RBA_ENGINE_T>
201 
202 // ====================================================================
203 // j,i lm_id,base_id
204 // \partial h \partial h
205 // l obs_frame_id
206 // dh_dp = ------------------ = ---------------------------------
207 // d+1 cur_id
208 // \partial p \partial p
209 // d stp.next_node
210 //
211 // See tech report:
212 // "A tutorial on SE(3) transformation parameterizations and
213 // on-manifold optimization", Jose-Luis Blanco.
214 // ====================================================================
215 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS>
217  typename TSparseBlocksJacobians_dh_dAp::TEntry &jacob,
218  const k2f_edge_t & observation,
219  const k2k_edges_deque_t &k2k_edges,
220  std::vector<const pose_flag_t*> *out_list_of_required_num_poses) const
221 {
222  ASSERT_(observation.obs.kf_id!=jacob.sym.kf_base)
223 
224  if (! *jacob.sym.is_valid )
225  return; // Another block of the same Jacobian row said this observation was invalid for some reason.
226 
227  // And x^{j,i}_l = pose_of_i_wrt_l (+) x^{j,i}_i
228 
229  // Handle the special case when d==obs, so rel_pose_d1_from_obs==NULL, and its pose is the origin:
230  const pose_flag_t * pose_d1_wrt_obs = jacob.sym.rel_pose_d1_from_obs; // "A" in papers
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; // If edge points in the opposite direction than as assumed in mathematical derivation.
233 
234  if (out_list_of_required_num_poses)
235  {
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);
239  }
240 
241  // make sure the numeric spanning tree is working and updating all that we need:
242 #if DEBUG_NOT_UPDATED_ENTRIES
243  TNumSTData d1 = check_num_st_entry_exists(&pose_base_wrt_d1, rba_state.spanning_tree);
244 #endif
245 
246  if (!pose_base_wrt_d1.updated)
247  {
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)
251  }
252 
253  if (pose_d1_wrt_obs)
254  {
255 #if DEBUG_NOT_UPDATED_ENTRIES
256  TNumSTData d2 = check_num_st_entry_exists(pose_d1_wrt_obs, rba_state.spanning_tree);
257 #endif
258  if (!pose_d1_wrt_obs->updated)
259  {
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");
262  }
263  ASSERT_(pose_d1_wrt_obs->updated)
264  }
265 
266 
267  // i<-l = d <- obs/l (+) base/i <- d
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;
272 
273  // First, we need x^{j,i}_i:
274  // LM parameters in: jacob.sym.feat_rel_pos->pos[0:N-1]
275  const array_landmark_t &xji_i = jacob.sym.feat_rel_pos->pos;
276 
277  // xji_l = pose_i_wrt_l (+) xji_i
278  array_landmark_t xji_l = xji_i; //
279  LM_TYPE::composePosePoint(xji_l, pose_i_wrt_l);
280 
281 #if DEBUG_JACOBIANS_SUPER_VERBOSE // Debug:
282  {
283  const TKeyFrameID obs_frame_id = observation.obs.kf_id;
284  const TKeyFrameID base_id = jacob.sym.kf_base;
285  const TKeyFrameID d_plus_1_id = jacob.sym.kf_d;
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;
294  // Observations from the "base_id" of a fixed,known landmark are NOT considered observations for optimization:
295  //mrpt::system::pause();
296  }
297 #endif
298 
299 #if SRBA_COMPUTE_NUMERIC_JACOBIANS
300  // Numeric jacobians
301  typename TSparseBlocksJacobians_dh_dAp::matrix_t num_jacob;
302 
303  array_pose_t x;
304  x.setZero(); // Evaluate Jacobian at manifold incr around origin
305  array_pose_t x_incrs;
306  x_incrs.setConstant(1e-4);
307 
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);
309 
310  mrpt::math::jacobians::jacob_numeric_estimate(x,&numeric_dh_dAp,x_incrs,num_params,num_jacob);
311 
312 #endif // SRBA_COMPUTE_NUMERIC_JACOBIANS
313 
314 
315 #if SRBA_COMPUTE_ANALYTIC_JACOBIANS
316  // d h(x^{j,i}_l) d h(x') d x^{j,i}_l
317  // --------------- = --------- * ----------------
318  // d p^{d+1}_d d x' d epsilon
319  //
320  // With: x' = x^{j,i}_l
321 
322  // First jacobian: (uses xji_l)
323  // -----------------------------
324  Eigen::Matrix<double,OBS_DIMS,LM_DIMS> dh_dx;
325 
326  // Converts a point relative to the robot coordinate frame (P) into a point relative to the sensor (RES = P \ominus POSE_IN_ROBOT )
327  RBA_OPTIONS::sensor_pose_on_robot_t::template point_robot2sensor<LM_TYPE,array_landmark_t>(xji_l,xji_l,this->parameters.sensor_pose );
328 
329  // Invoke sensor model:
330  if (!sensor_model_t::eval_jacob_dh_dx(dh_dx,xji_l, this->parameters.sensor))
331  {
332  // Invalid Jacobian:
333  *jacob.sym.is_valid = 0;
334  jacob.num.setZero();
335  return;
336  }
337 
338  // take into account the possible displacement of the sensor wrt the keyframe:
339  RBA_OPTIONS::sensor_pose_on_robot_t::jacob_dh_dx_rotate( dh_dx, this->parameters.sensor_pose );
340 
341  // Second Jacobian: (uses xji_i)
342  // ------------------------------
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);
344 
345 #endif // SRBA_COMPUTE_ANALYTIC_JACOBIANS
346 
347 
348 #if SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS
349  // Check jacob.num vs. num_jacob
350  const double MAX_REL_ERROR = 0.1;
351  if ((jacob.num-num_jacob).array().abs().maxCoeff()>MAX_REL_ERROR*num_jacob.array().maxCoeff())
352  {
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;
357  }
358 #endif
359 
360 #if SRBA_USE_NUMERIC_JACOBIANS
361  jacob.num = num_jacob;
362 #endif
363 }
364 
365 // ====================================================================
366 // Auxiliary sub-Jacobian used in compute_jacobian_dh_dp()
367 // These are specializations of the template, for each of the cases:
368 //template <size_t , size_t , class MATRIX, class POINT>
369 // ====================================================================
370 
371 // Case: 3D point, SE(3) poses:
372 template <class RBA_ENGINE_T>
373 struct compute_jacobian_dAepsDx_deps<jacob_point_landmark /* Jacobian family: this LM is a point */, 3 /*POINT_DIMS*/,6 /*POSE_DIMS*/,RBA_ENGINE_T>
374 {
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>
376  static void eval(
377  MATRIX & jacob,
378  const MATRIX_DH_DX & dh_dx,
379  const bool is_inverse_edge_jacobian,
380  const POINT & xji_i,
381  const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes
382  const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes
383  const JACOB_SYM_T & jacob_sym,
384  const K2K_EDGES_T & k2k_edges,
385  const OBS_VECTOR & all_obs
386  )
387  {
388  MRPT_UNUSED_PARAM(all_obs);
389  // See section 10.3.7 of technical report on SE(3) poses [http://mapir.isa.uma.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf]
390  if (!is_inverse_edge_jacobian)
391  { // Normal formulation: unknown is pose "d+1 -> d"
392 
393  // This is "D" in my handwritten notes:
394  // pose_i_wrt_dplus1 -> pose_base_wrt_d1
395 
396  const mrpt::math::CMatrixDouble33 & ROTD = pose_base_wrt_d1.pose.getRotationMatrix();
397 
398  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0,0,0,0):
399  Eigen::Matrix<double,RBA_ENGINE_T::OBS_DIMS,3> H_ROTA;
400  if (pose_d1_wrt_obs!=NULL)
401  {
402  // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs
403 
404  // 3x3 term: H*R(A)
405  H_ROTA = dh_dx * pose_d1_wrt_obs->pose.getRotationMatrix();
406  }
407  else
408  {
409  // 3x3 term: H*R(A)
410  H_ROTA = dh_dx;
411  }
412 
413  // First 2x3 block:
414  jacob.block(0,0,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA;
415 
416  // Second 2x3 block:
417  // compute aux vector "v":
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);
422 
423  Eigen::Matrix<double,3,3> aux;
424 
425  aux.coeffRef(0,0)=0;
426  aux.coeffRef(1,1)=0;
427  aux.coeffRef(2,2)=0;
428 
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];
435 
436  jacob.block(0,3,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA * aux;
437  }
438  else
439  { // Inverse formulation: unknown is pose "d -> d+1"
440 
441  // Changes due to the inverse pose:
442  // D becomes D' = p_d^{d+1} (+) D
443 
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
447  ->
448 #else
449  .
450 #endif
451  inv_pose;
452 
453  typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE);
454  pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
455 
456  const mrpt::math::CMatrixDouble33 & ROTD = pose_base_wrt_d1_prime.getRotationMatrix();
457 
458  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0,0,0,0):
459  Eigen::Matrix<double,RBA_ENGINE_T::OBS_DIMS,3> H_ROTA;
460  if (pose_d1_wrt_obs!=NULL)
461  {
462  // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs
463 
464  // In inverse edges, A (which is "pose_d1_wrt_obs") becomes A * (p_d_d1)^-1 =>
465  // So: ROT_A' = ROT_A * ROT_d_d1^t
466 
467  // 3x3 term: H*R(A')
468  H_ROTA = dh_dx * pose_d1_wrt_obs->pose.getRotationMatrix() * p_d_d1.getRotationMatrix().transpose();
469  }
470  else
471  {
472  // Was in the normal edge: H_ROTA = dh_dx;
473 
474  // 3x3 term: H*R(A')
475  H_ROTA = dh_dx * p_d_d1.getRotationMatrix().transpose();
476  }
477 
478  // First 2x3 block:
479  jacob.block(0,0,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA;
480 
481  // Second 2x3 block:
482  // compute aux vector "v":
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);
487 
488  Eigen::Matrix<double,3,3> aux;
489 
490  aux.coeffRef(0,0)=0;
491  aux.coeffRef(1,1)=0;
492  aux.coeffRef(2,2)=0;
493 
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];
500 
501  jacob.block(0,3,RBA_ENGINE_T::OBS_DIMS,3).noalias() = H_ROTA * aux;
502 
503  // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon
504  jacob = -jacob;
505 
506  } // end inverse edge case
507 
508  }
509 }; // end of specialization of "compute_jacobian_dAepsDx_deps"
510 
511 
512 /** Case: 2D or 3D points, SE(2) poses
513  * Both cases are grouped because a SE(2) pose doesn't transform the "z" of 3D points, so both sets of Jacobians are almost identical.
514  */
515 template <size_t POINT_DIMS, class RBA_ENGINE_T>
517 {
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>
519  static void eval(
520  MATRIX & jacob,
521  const MATRIX_DH_DX & dh_dx,
522  const bool is_inverse_edge_jacobian,
523  const POINT & xji_i,
524  const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes
525  const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes
526  const JACOB_SYM_T & jacob_sym,
527  const K2K_EDGES_T & k2k_edges,
528  const OBS_VECTOR & all_obs
529  )
530  {
531  MRPT_UNUSED_PARAM(all_obs);
532  MRPT_COMPILE_TIME_ASSERT(POINT_DIMS==2 || POINT_DIMS==3)
533 
534  if (!is_inverse_edge_jacobian)
535  { // Normal formulation: unknown is pose "d+1 -> d"
536 
537  const double Xd=pose_base_wrt_d1.pose.x();
538  const double Yd=pose_base_wrt_d1.pose.y();
539 
540  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
541  double PHIa=0; // Xa, Ya: Are not really needed, since they don't appear in the Jacobian.
543  if (pose_d1_wrt_obs!=NULL)
544  {
545  // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs
546  //Xa = pose_d1_wrt_obs->pose.x(); Ya = pose_d1_wrt_obs->pose.y(); // Not needed
547  PHIa = pose_d1_wrt_obs->pose.phi();
548  AD.composeFrom(pose_d1_wrt_obs->pose,pose_base_wrt_d1.pose);
549  }
550  else
551  {
552  AD = pose_base_wrt_d1.pose; // A=0 -> A(+)D is simply D
553  }
554 
555  // d(P (+) x) / dP, P = A*D
556  Eigen::Matrix<double,POINT_DIMS/* 2 or 3*/,3 /* x,y,phi*/> 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;
561  if (POINT_DIMS==3) {
562  dPx_P(2,0) = 0; dPx_P(2,1) = 0; dPx_P(2,2) = 1;
563  }
564 
565  // d(A*exp(eps)*D) / deps
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;
572 
573  dAD_deps(0,2) = -ssin_a*Xd - ccos_a*Yd;
574  dAD_deps(1,2) = ccos_a*Xd - ssin_a*Yd;
575  dAD_deps(2,2) = 1;
576 
577  // Chain rule:
578  jacob.noalias() = dh_dx * dPx_P * dAD_deps;
579  }
580  else
581  { // Inverse formulation: unknown is pose "d -> d+1"
582 
583  // Changes due to the inverse pose:
584  // D becomes D' = p_d^{d+1} (+) D
585  // and A (which is "pose_d1_wrt_obs") becomes A' = A (+) (p_d_d1)^-1
586 
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
590  ->
591 #else
592  .
593 #endif
594  inv_pose;
595 
596  typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE);
597  pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
598 
599  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
600  const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1;
601 
602  typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ?
603  (pose_d1_wrt_obs->pose + p_d_d1_inv)
604  :
605  p_d_d1_inv;
606 
608  AD.composeFrom(A_prime,pose_base_wrt_d1_prime);
609 
610  const double Xd=pose_base_wrt_d1_prime.x();
611  const double Yd=pose_base_wrt_d1_prime.y();
612 
613  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
614  const double PHIa=A_prime.phi();
615 
616 
617  // d(P (+) x) / dP, P = A*D
618  Eigen::Matrix<double,POINT_DIMS/* 2 or 3*/,3 /* x,y,phi*/> 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;
623  if (POINT_DIMS==3) {
624  dPx_P(2,0) = 0; dPx_P(2,1) = 0; dPx_P(2,2) = 1;
625  }
626 
627  // d(A*exp(eps)*D) / deps
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;
634 
635  dAD_deps(0,2) = -ssin_a*Xd - ccos_a*Yd;
636  dAD_deps(1,2) = ccos_a*Xd - ssin_a*Yd;
637  dAD_deps(2,2) = 1;
638 
639  // Chain rule:
640  jacob.noalias() = dh_dx * dPx_P * dAD_deps;
641 
642  // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon
643  jacob = -jacob;
644 
645  } // end inverse edge case
646 
647  }
648 }; // end of "compute_jacobian_dAepsDx_deps_SE2"
649 
650 // Case: 2D point, SE(2) poses: (derived from generic SE2 implementation above)
651 template <class RBA_ENGINE_T>
652 struct compute_jacobian_dAepsDx_deps<jacob_point_landmark /* Jacobian family: this LM is a point */, 2 /*POINT_DIMS*/,3 /*POSE_DIMS*/,RBA_ENGINE_T>
653  : public compute_jacobian_dAepsDx_deps_SE2<2 /*POINT_DIMS*/,RBA_ENGINE_T>
654 {
655 };
656 
657 // Case: 3D point, SE(2) poses: (derived from generic SE2 implementation above)
658 template <class RBA_ENGINE_T>
659 struct compute_jacobian_dAepsDx_deps<jacob_point_landmark /* Jacobian family: this LM is a point */, 3 /*POINT_DIMS*/,3 /*POSE_DIMS*/,RBA_ENGINE_T>
660  : public compute_jacobian_dAepsDx_deps_SE2<3 /*POINT_DIMS*/,RBA_ENGINE_T>
661 {
662 };
663 
664 // Case: SE(2) relative-poses, SE(2) poses:
665 template <class RBA_ENGINE_T>
666 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>
667 {
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>
669  static void eval(
670  MATRIX & jacob,
671  const MATRIX_DH_DX & dh_dx,
672  const bool is_inverse_edge_jacobian,
673  const POINT & xji_i,
674  const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes
675  const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes
676  const JACOB_SYM_T & jacob_sym,
677  const K2K_EDGES_T & k2k_edges,
678  const OBS_VECTOR & all_obs
679  )
680  {
681  MRPT_UNUSED_PARAM(xji_i);
682  MRPT_UNUSED_PARAM(all_obs);
683  double Xd,Yd,PHIa;
685 
686  if (!is_inverse_edge_jacobian)
687  { // Normal formulation: unknown is pose "d+1 -> d"
688 
689  Xd=pose_base_wrt_d1.pose.x();
690  Yd=pose_base_wrt_d1.pose.y();
691 
692  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
693  PHIa=0; // Xa, Ya: Are not really needed, since they don't appear in the Jacobian.
694  if (pose_d1_wrt_obs!=NULL)
695  {
696  // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs
697  PHIa = pose_d1_wrt_obs->pose.phi();
698  base_wrt_obs.composeFrom(pose_d1_wrt_obs->pose, pose_base_wrt_d1.pose); // A (+) D
699  }
700  else
701  {
702  base_wrt_obs = pose_base_wrt_d1.pose; // A (+) D
703  }
704  }
705  else
706  { // Inverse formulation: unknown is pose "d -> d+1"
707 
708  // Changes due to the inverse pose:
709  // D becomes D' = p_d^{d+1} (+) D
710  // and A (which is "pose_d1_wrt_obs") becomes A' = A (+) (p_d_d1)^-1
711 
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
715  ->
716 #else
717  .
718 #endif
719  inv_pose;
720 
721  typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE);
722  pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
723 
724  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
725  const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1;
726 
727  typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ?
728  (pose_d1_wrt_obs->pose + p_d_d1_inv)
729  :
730  p_d_d1_inv;
731 
732  Xd=pose_base_wrt_d1_prime.x();
733  Yd=pose_base_wrt_d1_prime.y();
734 
735  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
736  PHIa=A_prime.phi();
737 
738  base_wrt_obs.composeFrom(A_prime, pose_base_wrt_d1_prime); // A (+) D
739  }
740 
741  //const mrpt::poses::CPose2D base_wrt_obs_inv = -base_wrt_obs;
742  const double PHIad = base_wrt_obs.phi();
743 
744  const double ccos_ad = cos(PHIad), ssin_ad=sin(PHIad);
745  const double ccos_a = cos(PHIa), ssin_a=sin(PHIa);
746 
747  Eigen::Matrix<double,3,3> J0; // -d(\ominus p)_dp, with p=A*D
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;
751 
752  Eigen::Matrix<double,3,3> J1; // dAD_dA;
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;
756 
757  Eigen::Matrix<double,3,3> J2; // dAe_de
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;
761 
762  // Chain rule:
763  jacob.noalias() = dh_dx * J0* J1 * J2;
764 
765  if (is_inverse_edge_jacobian)
766  {
767  // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon
768  jacob = -jacob;
769  }
770  }
771 }; // end of "compute_jacobian_dAepsDx_deps", Case: SE(2) relative-poses, SE(2) poses:
772 
773 // Case: SE(3) relative-poses, SE(3) poses:
774 template <class RBA_ENGINE_T>
775 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>
776 {
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>
778  static void eval(
779  MATRIX & jacob,
780  const MATRIX_DH_DX & dh_dx,
781  const bool is_inverse_edge_jacobian,
782  const POINT & xji_i,
783  const pose_flag_t * pose_d1_wrt_obs, // "A" in handwritten notes
784  const pose_flag_t & pose_base_wrt_d1, // "D" in handwritten notes
785  const JACOB_SYM_T & jacob_sym,
786  const K2K_EDGES_T & k2k_edges,
787  const OBS_VECTOR & all_obs
788  )
789  {
790  MRPT_UNUSED_PARAM(xji_i);
791  MRPT_UNUSED_PARAM(all_obs);
792  //
793  // d ps-log(p^obs_base) d ps-log(p) d A*e^eps*D
794  // -------------------- = --------------- * -----------------
795  // d eps^d+1_d d p d eps
796  // 6x6 6x12 12x6
797  //
798  // ^: (1) ^: (2)
799  // See section 10.3.7 of technical report on SE(3) poses [http://mapir.isa.uma.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf]
800  mrpt::math::CMatrixDouble33 ROTA; // A.rotationMatrix
801  typename RBA_ENGINE_T::pose_t D(mrpt::poses::UNINITIALIZED_POSE);
802  typename RBA_ENGINE_T::pose_t base_wrt_obs(mrpt::poses::UNINITIALIZED_POSE); // A(+)D
803 
804  if (!is_inverse_edge_jacobian)
805  { // Normal formulation: unknown is pose "d+1 -> d"
806  D = pose_base_wrt_d1.pose;
807 
808  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
809  if (pose_d1_wrt_obs!=NULL)
810  {
811  // pose_d_plus_1_wrt_l -> pose_d1_wrt_obs
812  base_wrt_obs.composeFrom(pose_d1_wrt_obs->pose, D); // A (+) D
813  ROTA = pose_d1_wrt_obs->pose.getRotationMatrix();
814  }
815  else
816  {
817  base_wrt_obs = D; // A (+) D
818  ROTA.setIdentity();
819  }
820  }
821  else
822  { // Inverse formulation: unknown is pose "d -> d+1"
823 
824  // Changes due to the inverse pose:
825  // D becomes D' = p_d^{d+1} (+) D
826  // and A (which is "pose_d1_wrt_obs") becomes A' = A (+) (p_d_d1)^-1
827 
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
831  ->
832 #else
833  .
834 #endif
835  inv_pose;
836 
837  typename RBA_ENGINE_T::pose_t pose_base_wrt_d1_prime(mrpt::poses::UNINITIALIZED_POSE);
838  pose_base_wrt_d1_prime.composeFrom( p_d_d1 , pose_base_wrt_d1.pose );
839 
840  // We need to handle the special case where "d+1"=="l", so A=Pose(0,0,0):
841  const typename RBA_ENGINE_T::pose_t p_d_d1_inv = -p_d_d1;
842 
843  typename RBA_ENGINE_T::pose_t A_prime = (pose_d1_wrt_obs!=NULL) ?
844  (pose_d1_wrt_obs->pose + p_d_d1_inv)
845  :
846  p_d_d1_inv;
847 
848  ROTA=A_prime.getRotationMatrix();
849 
850  D=pose_base_wrt_d1_prime;
851 
852  base_wrt_obs.composeFrom(A_prime, pose_base_wrt_d1_prime); // A (+) D
853  }
854 
855  const mrpt::math::CMatrixDouble44 & HM_D = D.getHomogeneousMatrixVal(); // [ROT(D) | Trans(D) ]
856 
857 
858  // (1): Common part: d_Ln(R)_dR:
859  //
860  const mrpt::math::CMatrixDouble33 & ROT_AplusB = base_wrt_obs.getRotationMatrix(); // The rotation matrix.
861  Eigen::Matrix<double,6,12> dLnRelPose_deps;
862  dLnRelPose_deps.setZero();
863  dLnRelPose_deps.block<3,3>(0,9).setIdentity();
864  {
866  mrpt::poses::CPose3D::ln_rot_jacob( ROT_AplusB, dLnRot_dRot);
867  dLnRelPose_deps.block<3,9>(3,0) = dLnRot_dRot;
868  }
869 
870  // (2): d A*e^eps*D / d eps =
871  //
872  // [ 0_{3x3} -R(A)*[dc1]_x ]
873  // [ 0_{3x3} -R(A)*[dc2]_x ]
874  // [ 0_{3x3} -R(A)*[dc3]_x ]
875  // [ R(A) -R(A)*[dt]_x ]
876  //
877  Eigen::Matrix<double,12,6> dAeD_de;
878  // Blocks: (1-3,1)
879  dAeD_de.block<9,3>(0,0).setZero();
880  // Block (4,1)
881  dAeD_de.block<3,3>(9,0) = (ROTA * HM_D.block<3,3>(0,0)).transpose();
882 
883  //cout << "========= ROTA ========= :\n" << ROTA << endl;
884  //cout << "========= HM_D ========= :\n" << HM_D << endl;
885 
886  // Blocks (1-4,2)
887  for (int i=0;i<4;i++)
888  {
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 };
893  dAeD_de.block<3,3>(i*3,3) = -ROTA * mrpt::math::CMatrixDouble33(aux_vals);
894  }
895 
896  // (3): Apply chain rule:
897  //
898  jacob.noalias() = dLnRelPose_deps * dAeD_de;
899 
900  if (is_inverse_edge_jacobian)
901  {
902  // And this comes from: d exp(-epsilon)/d epsilon = - d exp(epsilon)/d epsilon
903  jacob = -jacob;
904  }
905  }
906 }; // end of "compute_jacobian_dAepsDx_deps", Case: SE(3) relative-poses, SE(3) poses:
907 
908 
909 
910 // ====================================================================
911 // j,i lm_id,base_id
912 // \partial h \partial h
913 // l obs_frame_id
914 // dh_df = ------------------ = ---------------------------------
915 //
916 // \partial f \partial f
917 //
918 // Note: f=relative position of landmark with respect to its base kf
919 // ====================================================================
920 template <class KF2KF_POSE_TYPE,class LM_TYPE,class OBS_TYPE,class RBA_OPTIONS>
922  typename TSparseBlocksJacobians_dh_df::TEntry &jacob,
923  const k2f_edge_t & observation,
924  std::vector<const pose_flag_t*> *out_list_of_required_num_poses) const
925 {
926  MRPT_UNUSED_PARAM(observation);
927  if (! *jacob.sym.is_valid )
928  return; // Another block of the same Jacobian row said this observation was invalid for some reason.
929 
930  // Handle the special case when obs==base, for which rel_pose_base_from_obs==NULL
931  const pose_flag_t * rel_pose_base_from_obs = jacob.sym.rel_pose_base_from_obs;
932 
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);
935 
936  // make sure the numeric spanning tree is working and updating all that we need:
937  if (rel_pose_base_from_obs)
938  {
939 #if DEBUG_NOT_UPDATED_ENTRIES
940  TNumSTData d1 = check_num_st_entry_exists(rel_pose_base_from_obs, rba_state.spanning_tree);
941 #endif
942  if (!rel_pose_base_from_obs->updated)
943  {
944 #if DEBUG_NOT_UPDATED_ENTRIES
945  cout << "not updated ST entry for: from=" << d1.from << ", to=" << d1.to << endl;
946 #endif
947  rba_state.spanning_tree.save_as_dot_file("_debug_jacob_error_all_STs.dot");
948  ASSERT_(rel_pose_base_from_obs->updated)
949  }
950  }
951 
952  // First, we need x^{j,i}_i:
953  //const mrpt::math::TPoint3D & xji_i = jacob.sym.feat_rel_pos->pos;
954  const array_landmark_t &xji_i = jacob.sym.feat_rel_pos->pos;
955 
956  array_landmark_t xji_l = xji_i; //
957 
958  if (rel_pose_base_from_obs!=NULL)
959  {
960 #if 0
961  cout << "dh_df(ft_id="<< observation.obs.obs.feat_id << ", obs_kf="<< observation.obs.kf_id << "): o2b=" << *rel_pose_base_from_obs << endl;
962 #endif
963  // xji_l = rel_pose_base_from_obs (+) xji_i
964  //rel_pose_base_from_obs->pose.composePoint(xji_i, xji_l);
965  LM_TYPE::composePosePoint(xji_l, rel_pose_base_from_obs->pose);
966  }
967  else
968  {
969  // I'm observing from the same base key-frame: xji_l = xji_i (already done above)
970  }
971 
972 
973 #if SRBA_COMPUTE_NUMERIC_JACOBIANS
974  // Numeric jacobians
975  typename TSparseBlocksJacobians_dh_df::matrix_t num_jacob;
976 
978  x.setZero(); // Evaluate Jacobian at incr around origin
979  array_landmark_t x_incrs;
980  x_incrs.setConstant(1e-3);
981 
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);
983 
984  mrpt::math::jacobians::jacob_numeric_estimate(x,&numeric_dh_df,x_incrs,num_params,num_jacob);
985 
986 #endif // SRBA_COMPUTE_NUMERIC_JACOBIANS
987 
988 
989 #if SRBA_COMPUTE_ANALYTIC_JACOBIANS
990  // d h(x^{j,i}_l) d h(x') d x'
991  // --------------- = --------- * ----------
992  // d f d x' d x
993  //
994  // With: x' = x^{j,i}_l
995 
996  // First jacobian: (uses xji_l)
997  // -----------------------------
998  Eigen::Matrix<double,OBS_DIMS,LM_DIMS> dh_dx;
999 
1000  // Converts a point relative to the robot coordinate frame (P) into a point relative to the sensor (RES = P \ominus POSE_IN_ROBOT )
1001  RBA_OPTIONS::sensor_pose_on_robot_t::template point_robot2sensor<LM_TYPE,array_landmark_t>(xji_l,xji_l,this->parameters.sensor_pose );
1002 
1003  // Invoke sensor model:
1004  if (!sensor_model_t::eval_jacob_dh_dx(dh_dx,xji_l, this->parameters.sensor))
1005  {
1006  // Invalid Jacobian:
1007  *jacob.sym.is_valid = 0;
1008  jacob.num.setZero();
1009  return;
1010  }
1011 
1012  // take into account the possible displacement of the sensor wrt the keyframe:
1013  RBA_OPTIONS::sensor_pose_on_robot_t::jacob_dh_dx_rotate( dh_dx, this->parameters.sensor_pose );
1014 
1015  // Second Jacobian: Simply the 2x2 or 3x3 rotation matrix of base wrt observing
1016  // ------------------------------
1017  if (rel_pose_base_from_obs!=NULL)
1018  {
1020  rel_pose_base_from_obs->pose.getRotationMatrix(R);
1021  jacob.num.noalias() = dh_dx * R;
1022  }
1023  else
1024  {
1025  // if observing from the same base kf, we're done:
1026  jacob.num.noalias() = dh_dx;
1027  }
1028 #endif // SRBA_COMPUTE_ANALYTIC_JACOBIANS
1029 
1030 
1031 #if SRBA_VERIFY_AGAINST_NUMERIC_JACOBIANS
1032  // Check jacob.num vs. num_jacob
1033  const double MAX_REL_ERROR = 0.1;
1034  if ((jacob.num-num_jacob).array().abs().maxCoeff()>MAX_REL_ERROR*num_jacob.array().maxCoeff())
1035  {
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;
1040  }
1041 #endif
1042 
1043 #if SRBA_USE_NUMERIC_JACOBIANS
1044  jacob.num = num_jacob;
1045 #endif
1046 }
1047 
1048 
1049 // ------------------------------------------------------------------------
1050 // prepare_Jacobians_required_tree_roots()
1051 // ------------------------------------------------------------------------
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 )
1057 {
1058  lst.clear();
1059 
1060  const size_t nUnknowns_k2k = lst_JacobCols_dAp.size();
1061  const size_t nUnknowns_k2f = lst_JacobCols_df.size();
1062 
1063  // k2k edges ------------------------------------------------------
1064  for (size_t i=0;i<nUnknowns_k2k;i++)
1065  {
1066  // For each column, process each nonzero block:
1067  const typename TSparseBlocksJacobians_dh_dAp::col_t *col = lst_JacobCols_dAp[i];
1068 
1069  for (typename TSparseBlocksJacobians_dh_dAp::col_t::const_iterator it=col->begin();it!=col->end();++it)
1070  {
1071  // For each dh_dAp block we need:
1072  // * (d+1) -> obs
1073  // * (d+1) -> base
1074 
1075  const size_t obs_idx = it->first;
1076  const typename TSparseBlocksJacobians_dh_dAp::TEntry & jacob_entry = it->second;
1077 
1078 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
1079  const TKeyFrameID obs_id = rba_state.all_observations[obs_idx]->obs.kf_id;
1080 #else
1081  const TKeyFrameID obs_id = rba_state.all_observations[obs_idx].obs.kf_id;
1082 #endif
1083  const TKeyFrameID d1_id = jacob_entry.sym.kf_d;
1084  const TKeyFrameID base_id = jacob_entry.sym.kf_base;
1085 
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 );
1088  }
1089  }
1090 
1091  // k2f edges ------------------------------------------------------
1092  for (size_t i=0;i<nUnknowns_k2f;i++)
1093  {
1094  // For each column, process each nonzero block:
1095  const typename TSparseBlocksJacobians_dh_df::col_t *col = lst_JacobCols_df[i];
1096 
1097  for (typename TSparseBlocksJacobians_dh_df::col_t::const_iterator it=col->begin();it!=col->end();++it)
1098  {
1099  // For each dh_df block we need:
1100  // * obs -> base
1101  const size_t obs_idx = it->first;
1102  const k2f_edge_t &k2f =
1103 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
1104  *
1105 #endif
1106  rba_state.all_observations[obs_idx];
1107 
1108  ASSERT_(k2f.feat_rel_pos)
1109 
1110  const TKeyFrameID obs_id = k2f.obs.kf_id;
1111  const TKeyFrameID base_id = k2f.feat_rel_pos->id_frame_base;
1112 
1113  add_edge_ij_to_list_needed_roots(lst, base_id, obs_id );
1114  }
1115  }
1116 }
1117 
1118 
1119 // ------------------------------------------------------------------------
1120 // recompute_all_Jacobians(): Re-evaluate all Jacobians numerically
1121 // ------------------------------------------------------------------------
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,
1126  std::vector<const typename kf2kf_pose_traits<KF2KF_POSE_TYPE>::pose_flag_t*> * out_list_of_required_num_poses )
1127 {
1128  size_t nJacobs=0;
1129  if (out_list_of_required_num_poses) out_list_of_required_num_poses->clear();
1130 
1131  const size_t nUnknowns_k2k = lst_JacobCols_dAp.size();
1132 
1133  // k2k edges ------------------------------------------------------
1134  for (size_t i=0;i<nUnknowns_k2k;i++)
1135  {
1136  // For each column, process each nonzero block:
1137  typename TSparseBlocksJacobians_dh_dAp::col_t *col = lst_JacobCols_dAp[i];
1138 
1139  for (typename TSparseBlocksJacobians_dh_dAp::col_t::iterator it=col->begin();it!=col->end();++it)
1140  {
1141  const size_t obs_idx = it->first;
1142  typename TSparseBlocksJacobians_dh_dAp::TEntry & jacob_entry = it->second;
1143  compute_jacobian_dh_dp(
1144  jacob_entry,
1145 #ifdef SRBA_WORKAROUND_MSVC9_DEQUE_BUG
1146  *rba_state.all_observations[obs_idx],
1147 #else
1148  rba_state.all_observations[obs_idx],
1149 #endif
1150  rba_state.k2k_edges,
1151  out_list_of_required_num_poses );
1152  nJacobs++;
1153  }
1154  }
1155 
1156  // k2f edges ------------------------------------------------------
1157  // Only if we are in landmarks-based SLAM, not in graph-SLAM:
1158  nJacobs += internal::recompute_all_Jacobians_dh_df<LM_TYPE::jacob_family>::eval(*this, lst_JacobCols_df,out_list_of_required_num_poses);
1159 
1160  return nJacobs;
1161 } // end of recompute_all_Jacobians()
1162 
1163 
1164 } } // end of namespace
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.
Definition: srba_types.h:433
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)
Definition: CPose2D.h:84
const RBA_OPTIONS::sensor_pose_on_robot_t::parameters_t & sensor_pose
Definition: RbaEngine.h:717
Scalar * iterator
Definition: eigen_plugins.h:23
rba_problem_state_t::k2k_edges_deque_t k2k_edges_deque_t
A list (deque) of KF-to-KF edges (unknown relative poses).
Definition: RbaEngine.h:92
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
Definition: eigen_plugins.h:24
kf2kf_pose_traits_t::pose_flag_t pose_flag_t
Definition: RbaEngine.h:94
KF2KF_POSE_TYPE::pose_t pose_t
The type of relative poses (e.g. mrpt::poses::CPose3D)
Definition: RbaEngine.h:87
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)
static void numeric_dh_df(const array_landmark_t &x, const TNumeric_dh_df_params &params, 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)
Definition: srba_types.h:447
const OBS_TYPE::TObservationParams & sensor_params
Definition: RbaEngine.h:737
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).
Definition: srba_types.h:50
static void numeric_dh_dAp(const array_pose_t &x, const TNumeric_dh_dAp_params &params, 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.
Definition: CPose2D.h:36
const RBA_OPTIONS::sensor_pose_on_robot_t::parameters_t & sensor_pose
Definition: RbaEngine.h:738
void composeFrom(const CPose2D &A, const CPose2D &B)
Makes .
#define ASSERT_(f)
A partial specialization of CArrayNumeric for double numbers.
Definition: CArrayNumeric.h:74
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
Definition: RbaEngine.h:716
Keyframe-to-feature edge: observation data stored for each keyframe.
Definition: srba_types.h:442
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)
Definition: srba_types.h:31



Page generated by Doxygen 1.8.9.1 for MRPT 1.3.0 SVN: at Sun Sep 13 03:55:12 UTC 2015