@@ -97,7 +97,6 @@ class PoseGraphSLAM
9797 // Get the optimized pose at node i. This function is thread-safe
9898 const Matrix4d getNodePose ( int i ) const ; // < this gives the pose from the optimization variable
9999 bool nodePoseExists ( int i ) const ; // < returns if ith node pose exist
100- bool nodePoseExists__nolock ( int i ) const ; // < returns if ith node pose exist
101100 // bool getNodePose( int i, Matrix4d& ) const; //TODO: removal
102101 int nNodes () const ;
103102 void getAllNodePose ( vector<Matrix4d>& vec_w_T_ci ) const ;
@@ -402,3 +401,201 @@ class SixDOFErrorWithSwitchingConstraints
402401
403402 double weight;
404403};
404+
405+
406+ // output is in degrees
407+ template <typename T>
408+ Matrix<T,3 ,1 > R2ypr ( const Matrix<T,3 ,3 >& R)
409+ {
410+ Matrix<T,3 ,1 > n = R.col (0 );
411+ Matrix<T,3 ,1 > o = R.col (1 );
412+ Matrix<T,3 ,1 > a = R.col (2 );
413+
414+ Matrix<T,3 ,1 > ypr (3 );
415+ T y = atan2 (n (1 ), n (0 ));
416+ T p = atan2 (-n (2 ), n (0 ) * cos (y) + n (1 ) * sin (y));
417+ T r = atan2 (a (0 ) * sin (y) - a (1 ) * cos (y), -o (0 ) * sin (y) + o (1 ) * cos (y));
418+ ypr (0 ) = y;
419+ ypr (1 ) = p;
420+ ypr (2 ) = r;
421+
422+ return ypr / T (M_PI) * T (180.0 );
423+ }
424+
425+
426+
427+
428+
429+ // In this residue function, internally we rely on the
430+ // Euler angle representation and not Quaternions as before.
431+ class FourDOFError
432+ {
433+ public:
434+ FourDOFError ( const Matrix4d& _observed__c1_T_c2, const double _weight=1.0 ) : observed__c1_T_c2( _observed__c1_T_c2 )
435+ {
436+ // Convert c1_T_c2 to Euler Angle representation
437+ PoseManipUtils::eigenmat_to_rawyprt ( _observed__c1_T_c2, observed_c1_ypr_c2, observed_c1_t_c2 );
438+ observed_c1_q_c2 = Quaterniond ( _observed__c1_T_c2.topLeftCorner <3 ,3 >() );
439+
440+ weight = _weight;
441+ }
442+
443+ // q1, t1 : w_T_c1
444+ // q2, t2 : w_T_c2
445+ template <typename T>
446+ bool operator () ( const T* const q1, const T* const t1,
447+ const T* const q2, const T* const t2,
448+ T* residue_ptr ) const
449+ {
450+ // Eigen:
451+ // Note the order of the arguments: the real w coefficient first,
452+ // while internally the coefficients are stored in the following order: [x, y, z, w]
453+
454+ // q1,t1 --> w_T_c1
455+ Eigen::Map<const Eigen::Matrix<T,3 ,1 > > p_1 ( t1 );
456+ Eigen::Map<const Eigen::Quaternion<T> > q_1 ( q1 );
457+
458+ // q2,t2 --> w_T_c2
459+ Eigen::Map<const Eigen::Matrix<T,3 ,1 > > p_2 ( t2 );
460+ Eigen::Map<const Eigen::Quaternion<T> > q_2 ( q2 );
461+
462+ // relative transforms between the 2 frames
463+ Quaternion<T> q_1_inverse = q_1.conjugate ();
464+ Quaternion<T> q_12_estimated = q_1_inverse * q_2;
465+ Matrix<T,3 ,1 > p_12_estimated = q_1_inverse * (p_2 - p_1);
466+
467+ // compute error between orientations estimates
468+ Quaternion<T> delta_q = q_12_estimated.conjugate () * observed_c1_q_c2.cast <T>();
469+ Matrix<T,3 ,1 > delta_t = q_12_estimated.conjugate () * ( observed_c1_t_c2.cast <T>() - p_12_estimated );
470+
471+ // penalty - A
472+ Eigen::Map<Matrix<T,4 ,1 > > residuals ( residue_ptr );
473+ // residuals.block(0,0, 3,1) = delta_t; // translation error
474+ residuals (0 ) = delta_t (0 );
475+ residuals (1 ) = delta_t (1 );
476+ residuals (2 ) = delta_t (2 );
477+
478+
479+ // convert Quaternion to euler angle using rotation matrix as intermediate
480+ Matrix<T,3 ,3 > delta_Rot = delta_q.toRotationMatrix ();
481+ Matrix<T,3 ,1 > delta_ypr = R2ypr ( delta_Rot );
482+ residuals (3 ) = T (4 .) * delta_ypr (0 );
483+ // residuals(4) = T(10.0);
484+ // residuals(5) = T(10.0);
485+
486+ residuals *= T (weight);
487+
488+ return true ;
489+
490+ }
491+
492+
493+ static ceres::CostFunction* Create ( const Matrix4d& _observed__c1_T_c2, const double weight=1.0 )
494+ {
495+ return ( new ceres::AutoDiffCostFunction<FourDOFError,4 ,4 ,3 ,4 ,3 >
496+ (
497+ new FourDOFError (_observed__c1_T_c2, weight )
498+ )
499+ );
500+ }
501+
502+
503+ private:
504+
505+
506+ Matrix4d observed__c1_T_c2;
507+ Quaterniond observed_c1_q_c2;
508+ Vector3d observed_c1_ypr_c2;
509+ Vector3d observed_c1_t_c2;
510+
511+ double weight;
512+ };
513+
514+
515+ // In this residue function, internally we rely on the
516+ // Euler angle representation and not Quaternions as before.
517+ class FourDOFErrorWithSwitchingConstraints
518+ {
519+ public:
520+ FourDOFErrorWithSwitchingConstraints ( const Matrix4d& _observed__c1_T_c2, const double _weight=1.0 ) : observed__c1_T_c2( _observed__c1_T_c2 )
521+ {
522+ // Convert c1_T_c2 to Euler Angle representation
523+ PoseManipUtils::eigenmat_to_rawyprt ( _observed__c1_T_c2, observed_c1_ypr_c2, observed_c1_t_c2 );
524+ observed_c1_q_c2 = Quaterniond ( _observed__c1_T_c2.topLeftCorner <3 ,3 >() );
525+
526+ weight = _weight;
527+ }
528+
529+ // q1, t1 : w_T_c1
530+ // q2, t2 : w_T_c2
531+ template <typename T>
532+ bool operator () ( const T* const q1, const T* const t1,
533+ const T* const q2, const T* const t2,
534+ const T* const switching_var,
535+ T* residue_ptr ) const
536+ {
537+ // Eigen:
538+ // Note the order of the arguments: the real w coefficient first,
539+ // while internally the coefficients are stored in the following order: [x, y, z, w]
540+
541+ // q1,t1 --> w_T_c1
542+ Eigen::Map<const Eigen::Matrix<T,3 ,1 > > p_1 ( t1 );
543+ Eigen::Map<const Eigen::Quaternion<T> > q_1 ( q1 );
544+
545+ // q2,t2 --> w_T_c2
546+ Eigen::Map<const Eigen::Matrix<T,3 ,1 > > p_2 ( t2 );
547+ Eigen::Map<const Eigen::Quaternion<T> > q_2 ( q2 );
548+
549+ // relative transforms between the 2 frames
550+ Quaternion<T> q_1_inverse = q_1.conjugate ();
551+ Quaternion<T> q_12_estimated = q_1_inverse * q_2;
552+ Matrix<T,3 ,1 > p_12_estimated = q_1_inverse * (p_2 - p_1);
553+
554+ // compute error between orientations estimates
555+ Quaternion<T> delta_q = q_12_estimated.conjugate () * observed_c1_q_c2.cast <T>();
556+ Matrix<T,3 ,1 > delta_t = q_12_estimated.conjugate () * ( observed_c1_t_c2.cast <T>() - p_12_estimated );
557+
558+ // penalty - A
559+ Eigen::Map<Matrix<T,5 ,1 > > residuals ( residue_ptr );
560+ // residuals.block(0,0, 3,1) = delta_t; // translation error
561+ residuals (0 ) = delta_t (0 );
562+ residuals (1 ) = delta_t (1 );
563+ residuals (2 ) = delta_t (2 );
564+
565+ residuals (4 ) = T (1.0 ) * ( T (1.0 ) - switching_var[0 ] ); // switching constraint penalty
566+
567+ // convert Quaternion to euler angle using rotation matrix as intermediate
568+ Matrix<T,3 ,3 > delta_Rot = delta_q.toRotationMatrix ();
569+ Matrix<T,3 ,1 > delta_ypr = R2ypr ( delta_Rot );
570+ residuals (3 ) = T (4 .) * delta_ypr (0 );
571+ // residuals(4) = T(10.0);
572+ // residuals(5) = T(10.0);
573+
574+
575+ T s = switching_var[0 ];
576+ residuals *= s; // * T(weight);
577+ return true ;
578+
579+ }
580+
581+
582+ static ceres::CostFunction* Create ( const Matrix4d& _observed__c1_T_c2, const double weight=1.0 )
583+ {
584+ return ( new ceres::AutoDiffCostFunction<FourDOFErrorWithSwitchingConstraints,5 ,4 ,3 ,4 ,3 ,1 >
585+ (
586+ new FourDOFErrorWithSwitchingConstraints (_observed__c1_T_c2, weight )
587+ )
588+ );
589+ }
590+
591+
592+ private:
593+
594+
595+ Matrix4d observed__c1_T_c2;
596+ Quaterniond observed_c1_q_c2;
597+ Vector3d observed_c1_ypr_c2;
598+ Vector3d observed_c1_t_c2;
599+
600+ double weight;
601+ };
0 commit comments