diff --git a/include/core/factor/factor_relative_pose_2d.h b/include/core/factor/factor_relative_pose_2d.h
index 4a608feb01c29b413b19608987767853eecf5407..d156213cd912cb8b82bef159804e184cac5018f6 100644
--- a/include/core/factor/factor_relative_pose_2d.h
+++ b/include/core/factor/factor_relative_pose_2d.h
@@ -142,11 +142,11 @@ class FactorRelativePose2d : public FactorAnalytic
          *
          **/
         void evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd>>& _st_vector,
-                                       std::vector<Eigen::Map<Eigen::MatrixRowXd> >& jacobians,
-                                       const std::vector<bool>& _compute_jacobian) const override;
+                               std::vector<Eigen::Map<Eigen::MatrixRowXd> >& jacobians,
+                               const std::vector<bool>& _compute_jacobian) const override;
         void evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd>>& _st_vector,
-                                       std::vector<Eigen::MatrixXd>& jacobians,
-                                       const std::vector<bool>& _compute_jacobian) const override;
+                               std::vector<Eigen::MatrixXd>& jacobians,
+                               const std::vector<bool>& _compute_jacobian) const override;
 
         /** \brief Returns the pure jacobians (without measurement noise) evaluated in the state blocks values
          * \param jacobians is an output vector of mapped eigen matrices that sould contain the jacobians w.r.t each state block
@@ -175,8 +175,8 @@ inline Eigen::VectorXd FactorRelativePose2d::evaluateResiduals(
 }
 
 inline void FactorRelativePose2d::evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd> >& _st_vector,
-                                                        std::vector<Eigen::Map<Eigen::MatrixRowXd> >& jacobians,
-                                                        const std::vector<bool>& _compute_jacobian) const
+                                                    std::vector<Eigen::Map<Eigen::MatrixRowXd> >& jacobians,
+                                                    const std::vector<bool>& _compute_jacobian) const
 {
     assert(jacobians.size() == 4);
     assert(_st_vector.size() == 4);
@@ -211,8 +211,8 @@ inline void FactorRelativePose2d::evaluateJacobians(const std::vector<Eigen::Map
 }
 
 inline void FactorRelativePose2d::evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd>>& _st_vector,
-                                                        std::vector<Eigen::MatrixXd>& jacobians,
-                                                        const std::vector<bool>& _compute_jacobian) const
+                                                    std::vector<Eigen::MatrixXd>& jacobians,
+                                                    const std::vector<bool>& _compute_jacobian) const
 {
     assert(jacobians.size() == 4);
     assert(_st_vector.size() == 4);
diff --git a/include/core/factor/factor_relative_pose_2d_with_extrinsics.h b/include/core/factor/factor_relative_pose_2d_with_extrinsics.h
index fb89f046ed7d967cc428bb8355dc4cf760da40b8..4455b0960e7fb5b22f1b88c1106d2e07aa036550 100644
--- a/include/core/factor/factor_relative_pose_2d_with_extrinsics.h
+++ b/include/core/factor/factor_relative_pose_2d_with_extrinsics.h
@@ -65,6 +65,8 @@ class FactorRelativePose2dWithExtrinsics : public FactorAutodiff<FactorRelativeP
                          const T* const _p_sensor,
                          const T* const _o_sensor,
                          T* _residuals) const;
+
+        Eigen::Vector3d residual() const;
 };
 
 inline FactorRelativePose2dWithExtrinsics::FactorRelativePose2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr,
@@ -171,7 +173,7 @@ inline bool FactorRelativePose2dWithExtrinsics::operator ()(const T* const _p_re
         // w_R_r2 = R(o_target)
         // ----------------------------------------
         // s1_p_s2 = s1_R_r1*(r1_R_w*(w_p_r2 - w_p_r1) - r1_p_s1)
-        
+
         expected_measurement.head(2) = Eigen::Rotation2D<T>(-o_sensor)*(-p_sensor + Eigen::Rotation2D<T>(-o_ref)*(-p_ref + p_target));
         expected_measurement(2) = o_target - o_ref;
     }
@@ -204,6 +206,34 @@ inline bool FactorRelativePose2dWithExtrinsics::operator ()(const T* const _p_re
     return true;
 }
 
+inline Eigen::Vector3d FactorRelativePose2dWithExtrinsics::residual() const
+{
+    Eigen::Vector3d res;
+    Eigen::VectorXd p_sensor, o_sensor, p_ref, o_ref, p_target, o_target;
+    p_sensor = getCapture()->getSensorP()->getState();
+    o_sensor = getCapture()->getSensorO()->getState();
+    // FRAME CASE
+    if (not getFrameOtherList().empty())
+    {
+        p_ref    = getFrameOther()->getP()->getState();
+        o_ref    = getFrameOther()->getO()->getState();
+        p_target = getCapture()->getFrame()->getP()->getState();
+        o_target = getCapture()->getFrame()->getO()->getState();
+    }
+    // LANDMARK CASE
+    else if (not getLandmarkOtherList().empty())
+    {
+        p_ref    = getCapture()->getFrame()->getP()->getState();
+        o_ref    = getCapture()->getFrame()->getO()->getState();
+        p_target = getLandmarkOther()->getP()->getState();
+        o_target = getLandmarkOther()->getO()->getState();
+    }
+
+    operator() (p_ref.data(), o_ref.data(), p_target.data(), o_target.data(), p_sensor.data(), o_sensor.data(), res.data());
+
+    return res;
+}
+
 } // namespace wolf
 
 #endif
diff --git a/include/core/factor/factor_relative_pose_3d.h b/include/core/factor/factor_relative_pose_3d.h
index 6003c446732399f1abe6c4127dfb257543c82c2c..3e6ebceb5354aed9f5ac67c0c5fb561ade536c89 100644
--- a/include/core/factor/factor_relative_pose_3d.h
+++ b/include/core/factor/factor_relative_pose_3d.h
@@ -19,12 +19,6 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
-/*
- * factor_relative_pose_3d.h
- *
- *  Created on: Oct 7, 2016
- *      Author: jsola
- */
 
 #ifndef FACTOR_RELATIVE_POSE_3D_H_
 #define FACTOR_RELATIVE_POSE_3D_H_
@@ -48,20 +42,27 @@ class FactorRelativePose3d : public FactorAutodiff<FactorRelativePose3d,6,3,4,3,
                              const FactorTopology& _top,
                              FactorStatus _status = FAC_ACTIVE);
 
+        FactorRelativePose3d(const FeatureBasePtr& _ftr_current_ptr,
+                             const LandmarkBasePtr& _landmark_ptr,
+                             const ProcessorBasePtr& _processor_ptr,
+                             bool _apply_loss_function,
+                             const FactorTopology& _top,
+                             FactorStatus _status = FAC_ACTIVE);
+
         ~FactorRelativePose3d() override = default;
 
         template<typename T>
-                bool operator ()(const T* const _p_current,
-                                 const T* const _q_current,
-                                 const T* const _p_past,
-                                 const T* const _q_past,
+                bool operator ()(const T* const _p_target,
+                                 const T* const _q_target,
+                                 const T* const _p_ref,
+                                 const T* const _q_ref,
                                  T* _residuals) const;
 
         template<typename T>
-                bool expectation(const T* const _p_current,
-                                 const T* const _q_current,
-                                 const T* const _p_past,
-                                 const T* const _q_past,
+                bool expectation(const T* const _p_target,
+                                 const T* const _q_target,
+                                 const T* const _p_ref,
+                                 const T* const _q_ref,
                                  T* _expectation_dp,
                                  T* _expectation_dq) const;
 
@@ -118,39 +119,64 @@ inline FactorRelativePose3d::FactorRelativePose3d(const FeatureBasePtr& _ftr_cur
     MatrixSizeCheck<6,6>::check(_ftr_current_ptr->getMeasurementSquareRootInformationUpper());
 }
 
+inline FactorRelativePose3d::FactorRelativePose3d(const FeatureBasePtr& _ftr_current_ptr,
+                                                  const LandmarkBasePtr& _lmk_ptr,
+                                                  const ProcessorBasePtr& _processor_ptr,
+                                                  bool _apply_loss_function,
+                                                  const FactorTopology& _top,
+                                                  FactorStatus _status) :
+        FactorAutodiff<FactorRelativePose3d, 6, 3, 4, 3, 4>("FactorRelativePose3d",     // type
+                                                            _top, // topology
+                                                            _ftr_current_ptr,   // feature
+                                                            nullptr,            // frame other
+                                                            nullptr,            // capture other
+                                                            nullptr,            // feature other
+                                                            _lmk_ptr,           // landmark other
+                                                            _processor_ptr,     // processor
+                                                            _apply_loss_function,
+                                                            _status,
+                                                            _lmk_ptr->getP(), // landmark P
+                                                            _lmk_ptr->getO(), // landmark Q
+                                                            _ftr_current_ptr->getFrame()->getP(), // current frame P
+                                                            _ftr_current_ptr->getFrame()->getO()) // current frame Q
+{
+    MatrixSizeCheck<7,1>::check(_ftr_current_ptr->getMeasurement());
+    MatrixSizeCheck<6,6>::check(_ftr_current_ptr->getMeasurementSquareRootInformationUpper());
+}
+
 template<typename T>
-inline bool FactorRelativePose3d::expectation(const T* const _p_current,
-                                      const T* const _q_current,
-                                      const T* const _p_past,
-                                      const T* const _q_past,
-                                      T* _expectation_dp,
-                                      T* _expectation_dq) const
+inline bool FactorRelativePose3d::expectation(const T* const _p_target,
+                                              const T* const _q_target,
+                                              const T* const _p_ref,
+                                              const T* const _q_ref,
+                                              T* _expectation_dp,
+                                              T* _expectation_dq) const
 {
-    Eigen::Map<const Eigen::Matrix<T,3,1> > p_current(_p_current);
-    Eigen::Map<const Eigen::Quaternion<T> > q_current(_q_current);
-    Eigen::Map<const Eigen::Matrix<T,3,1> > p_past(_p_past);
-    Eigen::Map<const Eigen::Quaternion<T> > q_past(_q_past);
+    Eigen::Map<const Eigen::Matrix<T,3,1> > p_target(_p_target);
+    Eigen::Map<const Eigen::Quaternion<T> > q_target(_q_target);
+    Eigen::Map<const Eigen::Matrix<T,3,1> > p_ref(_p_ref);
+    Eigen::Map<const Eigen::Quaternion<T> > q_ref(_q_ref);
     Eigen::Map<Eigen::Matrix<T,3,1> > expectation_dp(_expectation_dp);
     Eigen::Map<Eigen::Quaternion<T> > expectation_dq(_expectation_dq);
 
-//     std::cout << "p_current: " << p_current(0) << " "
-//                                << p_current(1) << " "
-//                                << p_current(2) << "\n";
-//     std::cout << "q_current: " << q_current.coeffs()(0) << " "
-//                                << q_current.coeffs()(1) << " "
-//                                << q_current.coeffs()(2) << " "
-//                                << q_current.coeffs()(3) << "\n";
-//     std::cout << "p_past: " << p_past(0) << " "
-//                             << p_past(1) << " "
-//                             << p_past(2) << "\n";
-//     std::cout << "q_past: " << q_past.coeffs()(0) << " "
-//                             << q_past.coeffs()(1) << " "
-//                             << q_past.coeffs()(2) << " "
-//                             << q_past.coeffs()(3) << "\n";
+//     std::cout << "p_target: " << p_target(0) << " "
+//                               << p_target(1) << " "
+//                               << p_target(2) << "\n";
+//     std::cout << "q_target: " << q_target.coeffs()(0) << " "
+//                               << q_target.coeffs()(1) << " "
+//                               << q_target.coeffs()(2) << " "
+//                               << q_target.coeffs()(3) << "\n";
+//     std::cout << "p_ref: " << p_ref(0) << " "
+//                            << p_ref(1) << " "
+//                            << p_ref(2) << "\n";
+//     std::cout << "q_ref: " << q_ref.coeffs()(0) << " "
+//                            << q_ref.coeffs()(1) << " "
+//                            << q_ref.coeffs()(2) << " "
+//                            << q_ref.coeffs()(3) << "\n";
 
     // estimate motion increment, dp, dq;
-    expectation_dp = q_past.conjugate() * (p_current - p_past);
-    expectation_dq = q_past.conjugate() * q_current;
+    expectation_dp = q_ref.conjugate() * (p_target - p_ref);
+    expectation_dq = q_ref.conjugate() * q_target;
 
 //    std::cout << "exp_p: " << expectation_dp(0) << " "
 //                           << expectation_dp(1) << " "
@@ -168,29 +194,31 @@ inline Eigen::VectorXd FactorRelativePose3d::expectation() const
     Eigen::VectorXd exp(7);
     auto frm_current = getFeature()->getFrame();
     auto frm_past = getFrameOther();
-    const Eigen::VectorXd frame_current_pos  = frm_current->getP()->getState();
-    const Eigen::VectorXd frame_current_ori  = frm_current->getO()->getState();
-    const Eigen::VectorXd frame_past_pos     = frm_past->getP()->getState();
-    const Eigen::VectorXd frame_past_ori     = frm_past->getO()->getState();
-
-//    std::cout << "frame_current_pos: " << frm_current->getP()->getState().transpose() << std::endl;
-//    std::cout << "frame_past_pos: " << frm_past->getP()->getState().transpose() << std::endl;
-
-    expectation(frame_current_pos.data(),
-                frame_current_ori.data(),
-                frame_past_pos.data(),
-                frame_past_ori.data(),
+    auto lmk = getLandmarkOther();
+
+    const Eigen::VectorXd target_pos  = ( frm_past ? frm_current->getP()->getState() : lmk->getP()->getState());
+    const Eigen::VectorXd target_ori  = ( frm_past ? frm_current->getO()->getState() : lmk->getO()->getState());
+    const Eigen::VectorXd ref_pos     = ( frm_past ? frm_past->getP()->getState() : frm_current->getP()->getState());
+    const Eigen::VectorXd ref_ori     = ( frm_past ? frm_past->getO()->getState() : frm_current->getO()->getState());
+
+//    std::cout << "target_pos: " << frm_current->getP()->getState().transpose() << std::endl;
+//    std::cout << "ref_pos: " << frm_past->getP()->getState().transpose() << std::endl;
+
+    expectation(target_pos.data(),
+                target_ori.data(),
+                ref_pos.data(),
+                ref_ori.data(),
                 exp.data(),
                 exp.data()+3);
     return exp;
 }
 
 template<typename T>
-inline bool FactorRelativePose3d::operator ()(const T* const _p_current,
-                                      const T* const _q_current,
-                                      const T* const _p_past,
-                                      const T* const _q_past,
-                                      T* _residuals) const
+inline bool FactorRelativePose3d::operator ()(const T* const _p_target,
+                                              const T* const _q_target,
+                                              const T* const _p_ref,
+                                              const T* const _q_ref,
+                                              T* _residuals) const
 {
 
     /* Residual expression
@@ -232,7 +260,7 @@ inline bool FactorRelativePose3d::operator ()(const T* const _p_current,
     Eigen::Map<Eigen::Matrix<T,6,1> > residuals(_residuals);
 
     Eigen::Matrix<T, Eigen::Dynamic, 1> expected(7) ;
-    expectation(_p_current, _q_current, _p_past, _q_past, expected.data(), expected.data()+3);
+    expectation(_p_target, _q_target, _p_ref, _q_ref, expected.data(), expected.data()+3);
 
     // measured motion increment, dp_m, dq_m
     // Eigen::Matrix<T,3,1> dp_m = getMeasurement().head<3>().cast<T>();
diff --git a/include/core/factor/factor_relative_pose_3d_with_extrinsics.h b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
index a72d9683a8f9818e6dc242a67e2770a29fa1692e..0204203f330403b3821dcc784706989b4f7c1c08 100644
--- a/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
+++ b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
@@ -211,9 +211,9 @@ inline bool FactorRelativePose3dWithExtrinsics::operator ()(const T* const _p_re
 inline Eigen::Vector6d FactorRelativePose3dWithExtrinsics::residual() const
 {
     Eigen::Vector6d res;
-    Eigen::VectorXd p_camera, o_camera, p_ref, o_ref, p_target, o_target;
-    p_camera = getCapture()->getSensorP()->getState();
-    o_camera = getCapture()->getSensorO()->getState();
+    Eigen::VectorXd p_sensor, o_sensor, p_ref, o_ref, p_target, o_target;
+    p_sensor = getCapture()->getSensorP()->getState();
+    o_sensor = getCapture()->getSensorO()->getState();
     // FRAME CASE
     if (not getFrameOtherList().empty())
     {
@@ -231,7 +231,7 @@ inline Eigen::Vector6d FactorRelativePose3dWithExtrinsics::residual() const
         o_target = getLandmarkOther()->getO()->getState();
     }
 
-    operator() (p_ref.data(), o_ref.data(), p_target.data(), o_target.data(), p_camera.data(), o_camera.data(), res.data());
+    operator() (p_ref.data(), o_ref.data(), p_target.data(), o_target.data(), p_sensor.data(), o_sensor.data(), res.data());
 
     return res;
 }
diff --git a/include/core/factor/factor_relative_position_2d.h b/include/core/factor/factor_relative_position_2d.h
index 8c52033c6add806a4d809857477063558e85317f..8a2c7a0e52a7b58f8cda42586c56abff31793e79 100644
--- a/include/core/factor/factor_relative_position_2d.h
+++ b/include/core/factor/factor_relative_position_2d.h
@@ -255,6 +255,4 @@ inline void FactorRelativePosition2d::evaluatePureJacobians(std::vector<Eigen::M
                      cos(getStateBlockPtrVector()[1]->getState()(0))).finished();
 }
 
-} // namespace wolf
-
-#endif
+} // namespace wolf
\ No newline at end of file
diff --git a/include/core/factor/factor_relative_position_2d_with_extrinsics copy.h b/include/core/factor/factor_relative_position_2d_with_extrinsics.h
similarity index 99%
rename from include/core/factor/factor_relative_position_2d_with_extrinsics copy.h
rename to include/core/factor/factor_relative_position_2d_with_extrinsics.h
index 5e62f6e0079852d738930493b23112c9ded14df5..fd36867f4a93e08fe671f0491a07276ed00b6624 100644
--- a/include/core/factor/factor_relative_position_2d_with_extrinsics copy.h	
+++ b/include/core/factor/factor_relative_position_2d_with_extrinsics.h
@@ -125,6 +125,4 @@ inline bool FactorRelativePosition2dWithExtrinsics::operator ()(const T* const _
     return true;
 }
 
-} // namespace wolf
-
-#endif
+} // namespace wolf
\ No newline at end of file
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 6fa50395a7ee2af50d290b32e543b18bac534869..1aaf3bc58cca2d9157c998e5fd1d5d0c1cf90e56 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -173,6 +173,12 @@ wolf_add_gtest(gtest_factor_relative_pose_3d gtest_factor_relative_pose_3d.cpp)
 # FactorRelativePose3dWithExtrinsics class test
 wolf_add_gtest(gtest_factor_relative_pose_3d_with_extrinsics gtest_factor_relative_pose_3d_with_extrinsics.cpp)
 
+# FactorRelativePosition2d class test
+wolf_add_gtest(gtest_factor_relative_position_2d gtest_factor_relative_position_2d.cpp)
+
+# FactorRelativePosition2dWithExtrinsics class test
+wolf_add_gtest(gtest_factor_relative_position_2d_with_extrinsics gtest_factor_relative_position_2d_with_extrinsics.cpp)
+
 # FactorVelocityLocalDirection3d class test
 wolf_add_gtest(gtest_factor_velocity_local_direction_3d gtest_factor_velocity_local_direction_3d.cpp)
 
diff --git a/test/gtest_factor_relative_pose_2d.cpp b/test/gtest_factor_relative_pose_2d.cpp
index cc4030a9e51617399f2b256047f24eb30a93f5df..e317161887bc11dfbb4937b47e9ceb5b4c2e6c5e 100644
--- a/test/gtest_factor_relative_pose_2d.cpp
+++ b/test/gtest_factor_relative_pose_2d.cpp
@@ -25,6 +25,8 @@
 #include "core/factor/factor_relative_pose_2d.h"
 #include "core/capture/capture_odom_2d.h"
 #include "core/math/rotations.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 
 
 using namespace Eigen;
@@ -32,6 +34,12 @@ using namespace wolf;
 using std::cout;
 using std::endl;
 
+
+int N = 1e2;
+
+// Vectors
+Vector3d delta, x_0, x_1, x_f, x_l;
+
 // Input odometry data and covariance
 Matrix3d data_cov = 0.1*Matrix3d::Identity();
 
@@ -43,93 +51,164 @@ SolverCeres solver(problem_ptr);
 FrameBasePtr frm0 = problem_ptr->emplaceFrame(0.0, Vector3d::Zero());
 FrameBasePtr frm1 = problem_ptr->emplaceFrame(1.0, Vector3d::Zero());
 
+// Landmark
+LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                          "LandmarkPose2d",
+                                                          std::make_shared<StatePoint2d>(Vector2d::Zero()),
+                                                          std::make_shared<StateAngle>(Vector1d::Zero()));
+
 // Capture from frm1 to frm0
 auto cap1 = CaptureBase::emplace<CaptureOdom2d>(frm1, 1, nullptr, Vector3d::Zero(), data_cov);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePose2dPtr fac = nullptr;
 
+void generateRandomProblemFrame()
+{
+    // Random delta
+    delta = Vector3d::Random() * 1e2;
+    delta(2) = pi2pi(delta(2));
+
+    // Random frame 0 pose
+    x_0 = Vector3d::Random() * 1e2;
+    x_0(2) = pi2pi(x_0(2));
+
+    // Frame 1 pose
+    x_1(2) = pi2pi(x_0(2) + delta(2));
+    x_1.head<2>() = x_0.head<2>() + Rotation2Dd(x_0(2)) * delta.head<2>();
+
+    // Set states
+    frm0->setState(x_0);
+    frm1->setState(x_1);
+    cap1->setData(delta);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose2d>(fea, fea, frm0, nullptr, false, TOP_MOTION); // create and add
+}
+
+void generateRandomProblemLandmark()
+{
+    // Random delta
+    delta = Vector3d::Random() * 1e2;
+    delta(2) = pi2pi(delta(2));
+
+    // Random frame 0 pose
+    x_f = Vector3d::Random() * 1e2;
+    x_f(2) = pi2pi(x_f(2));
+
+    // landmark pose
+    x_l(2) = pi2pi(x_f(2) + delta(2));
+    x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * delta.head<2>();
+
+    // Set states
+    frm1->setState(x_f);
+    lmk->setState(x_l);
+    cap1->setData(delta);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose2d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose2d>(fea, fea, lmk, nullptr, false, TOP_LMK); // create and add
+}
+
+////////////// TESTS /////////////////////////////////////////////////////////////////////
 TEST(FactorRelativePose2d, check_tree)
 {
     ASSERT_TRUE(problem_ptr->check(0));
 }
 
-TEST(FactorRelativePose2d, fix_0_solve)
+// FRAME TO FRAME =========================================================================
+TEST(FactorRelativePose2d, frame_solve_frame1)
 {
-    for (int i = 0; i < 1e3; i++)
+    for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
-
-        // x1 groundtruth
-        Vector3d x1;
-        x1.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * delta.head<2>();
-        x1(2) = pi2pi(x0(2) + delta(2));
-
-        // Set states and measurement
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
+        // random setup
+        generateRandomProblemFrame();
 
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2d>(fea1, fea1, frm0, nullptr, false, TOP_MOTION);
-
-        // Fix frm0, perturb frm1
+        // Perturb frm1, fix the rest
         frm0->fix();
         frm1->unfix();
-        frm1->perturb(5);
+        frm1->perturb(1);
 
         // solve for frm1
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x1, 1e-6);
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_1, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
-TEST(FactorRelativePose2d, fix_1_solve)
+TEST(FactorRelativePose2d, frame_solve_frame0)
 {
-    for (int i = 0; i < 1e3; i++)
+    for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
+        // random setup
+        generateRandomProblemFrame();
+        
+        // Perturb frm0, fix the rest
+        frm1->fix();
+        frm0->unfix();
+        frm0->perturb(1);
 
-        // x1 groundtruth
-        Vector3d x1;
-        x1.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * delta.head<2>();
-        x1(2) = pi2pi(x0(2) + delta(2));
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        // Set states and measurement
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
+        ASSERT_POSE2d_APPROX(frm0->getStateVector(), x_0, 1e-3);
 
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2d>(fea1, fea1, frm0, nullptr, false, TOP_MOTION);
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
 
-        // Fix frm1, perturb frm0
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePose2d, landmark_solve_lmk)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb landmark, fix the rest
         frm1->fix();
-        frm0->unfix();
-        frm0->perturb(5);
+        lmk->unfix();
+        lmk->perturb(1);
+
+        // solve for landmark
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(lmk->getStateVector(), x_l, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePose2d, landmark_solve_frame)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb frm0, fix the rest
+        frm1->unfix();
+        lmk->fix();
+        frm1->perturb(1);
 
         // solve for frm0
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(frm0->getStateVector(), x0, 1e-6);
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_f, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
diff --git a/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp b/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp
index 76744a478fddc50146f3bea9b3598803cad93b46..a10190e0fceb62db586e3a8d87b844a067d72969 100644
--- a/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp
+++ b/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp
@@ -37,7 +37,10 @@ using std::endl;
 
 std::string wolf_root = _WOLF_ROOT_DIR;
 
-int N = 1;//1e2
+int N = 1e2;
+
+// Vectors
+Vector3d delta, x_0, x_1, x_f, x_l, x_s;
 
 // Input odometry data and covariance
 Matrix3d data_cov = 0.1*Matrix3d::Identity();
@@ -47,7 +50,7 @@ ProblemPtr problem_ptr = Problem::create("PO", 2);
 SolverCeres solver(problem_ptr);
 
 // Sensor
-auto sensor_odom2d = problem_ptr->installSensor("SensorOdom2d", "odom", Vector3d(0,0,0), wolf_root + "/test/yaml/sensor_odom_2d.yaml");
+auto sensor = problem_ptr->installSensor("SensorOdom2d", "odom", Vector3d(0,0,0), wolf_root + "/test/yaml/sensor_odom_2d.yaml");
 
 // Two frames
 FrameBasePtr frm0 = problem_ptr->emplaceFrame(0, Vector3d::Zero() );
@@ -60,7 +63,71 @@ LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(),
                                                           std::make_shared<StateAngle>(Vector1d::Zero()));
 
 // Capture
-auto cap1 = CaptureBase::emplace<CaptureOdom2d>(frm1, 1, sensor_odom2d, Vector3d::Zero(), data_cov);
+auto cap1 = CaptureBase::emplace<CaptureOdom2d>(frm1, 1, sensor, Vector3d::Zero(), data_cov);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePose2dWithExtrinsicsPtr fac = nullptr;
+
+void generateRandomProblemFrame()
+{
+    // Random delta
+    delta = Vector3d::Random() * 1e2;
+    delta(2) = pi2pi(delta(2));
+
+    // Random frame 0 pose
+    x_0 = Vector3d::Random() * 1e2;
+    x_0(2) = pi2pi(x_0(2));
+
+    // Random extrinsics
+    x_s = Vector3d::Random() * 1e2;
+    x_s(2) = pi2pi(x_s(2));
+
+    // Frame 1 pose
+    x_1(2) = pi2pi(x_0(2) + delta(2));
+    x_1.head<2>() = x_0.head<2>() + Rotation2Dd(x_0(2)) * (x_s.head<2>() + Rotation2Dd(x_s(2)) * delta.head<2>()) - Rotation2Dd(x_1(2)) * x_s.head<2>();
+
+    // Set states
+    frm0->setState(x_0);
+    frm1->setState(x_1);
+    cap1->setData(delta);
+    sensor->setState(x_s);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea, fea, frm0, nullptr, false, TOP_MOTION); // create and add
+}
+
+void generateRandomProblemLandmark()
+{
+    // Random delta
+    delta = Vector3d::Random() * 1e2;
+    delta(2) = pi2pi(delta(2));
+
+    // Random frame pose
+    x_f = Vector3d::Random() * 1e2;
+    x_f(2) = pi2pi(x_f(2));
+
+    // Random extrinsics
+    x_s = Vector3d::Random() * 1e2;
+    x_s(2) = pi2pi(x_s(2));
+
+    // landmark pose
+    x_l(2) = pi2pi(x_f(2) + delta(2));
+    x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (x_s.head<2>() + Rotation2Dd(x_s(2)) * delta.head<2>());
+
+    // Set states
+    frm1->setState(x_f);
+    lmk->setState(x_l);
+    cap1->setData(delta);
+    sensor->setState(x_s);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose2d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea, fea, lmk, nullptr, false, TOP_LMK); // create and add
+}
+
+////////////// TESTS /////////////////////////////////////////////////////////////////////
 
 TEST(FactorRelativePose2dWithExtrinsics, check_tree)
 {
@@ -72,47 +139,26 @@ TEST(FactorRelativePose2dWithExtrinsics, frame_solve_frame1)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // x1 groundtruth
-        Vector3d x1;
-        x1(2) = pi2pi(x0(2) + delta(2));
-        x1.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()) - Rotation2Dd(x1(2)) * xs.head<2>();
-
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
+        // random setup
+        generateRandomProblemFrame();
 
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb frm1, fix the rest
         frm0->fix();
         frm1->unfix();
-        sensor_odom2d->getP()->fix();
-        sensor_odom2d->getO()->fix();
-        frm1->perturb(5);
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm1->perturb(1);
 
         // solve for frm1
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x1, 1e-6);
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_1, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
@@ -120,95 +166,53 @@ TEST(FactorRelativePose2dWithExtrinsics, frame_solve_frame0)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // x1 groundtruth
-        Vector3d x1;
-        x1(2) = pi2pi(x0(2) + delta(2));
-        x1.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()) - Rotation2Dd(x1(2)) * xs.head<2>();
-
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
-
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb frm0, fix the rest
         frm1->fix();
         frm0->unfix();
-        sensor_odom2d->getP()->fix();
-        sensor_odom2d->getO()->fix();
-        frm0->perturb(5);
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm0->perturb(1);
 
         // solve for frm0
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(frm0->getStateVector(), x0, 1e-6);
+        ASSERT_POSE2d_APPROX(frm0->getStateVector(), x_0, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
+// JV: The position extrinsics in case of pair of frames is not observable
 TEST(FactorRelativePose2dWithExtrinsics, frame_solve_extrinsics_p)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // x1 groundtruth
-        Vector3d x1;
-        x1(2) = pi2pi(x0(2) + delta(2));
-        x1.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()) - Rotation2Dd(x1(2)) * xs.head<2>();
-
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
-
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb sensor P, fix the rest
         frm1->fix();
         frm0->fix();
-        sensor_odom2d->getP()->unfix();
-        sensor_odom2d->getO()->fix();
-        sensor_odom2d->getP()->perturb(1);
+        sensor->getP()->unfix();
+        sensor->getO()->fix();
+        sensor->getP()->perturb(1);
 
         // solve for frm0
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
 
-        ASSERT_POSE2d_APPROX(sensor_odom2d->getStateVector(), xs, 1e-6);
+        //ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
@@ -216,49 +220,26 @@ TEST(FactorRelativePose2dWithExtrinsics, frame_solve_extrinsics_o)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // x1 groundtruth
-        Vector3d x1;
-        x1(2) = pi2pi(x0(2) + delta(2));
-        x1.head<2>() = x0.head<2>() + Rotation2Dd(x0(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>()) - Rotation2Dd(x1(2)) * xs.head<2>();
-
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
-
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb sensor O, fix the rest
         frm1->fix();
         frm0->fix();
-        sensor_odom2d->getP()->fix();
-        sensor_odom2d->getO()->unfix();
-        sensor_odom2d->getO()->perturb(.2);
-
-        //std::cout << "Sensor O (perturbed): " << sensor_odom2d->getO()->getState().transpose() << std::endl;
+        sensor->getP()->fix();
+        sensor->getO()->unfix();
+        sensor->getO()->perturb(.2);
 
         // solve for frm0
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(sensor_odom2d->getStateVector(), xs, 1e-6);
+        ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
@@ -267,47 +248,26 @@ TEST(FactorRelativePose2dWithExtrinsics, landmark_solve_lmk)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random frame pose
-        Vector3d x_f = Vector3d::Random() * 10;
-        pi2pi(x_f(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // landmark pose
-        Vector3d x_l;
-        x_l(2) = pi2pi(x_f(2) + delta(2));
-        x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>());
-
-        // Set states
-        frm1->setState(x_f);
-        lmk->setState(x_l);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
-
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb landmark, fix the rest
         frm1->fix();
         lmk->unfix();
-        sensor_odom2d->getP()->fix();
-        sensor_odom2d->getO()->fix();
-        lmk->perturb(5);
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        lmk->perturb(1);
 
         // solve for landmark
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(lmk->getStateVector(), x_l, 1e-6);
+        ASSERT_POSE2d_APPROX(lmk->getStateVector(), x_l, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
@@ -315,47 +275,26 @@ TEST(FactorRelativePose2dWithExtrinsics, landmark_solve_frame)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random frame pose
-        Vector3d x_f = Vector3d::Random() * 10;
-        pi2pi(x_f(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // landmark pose
-        Vector3d x_l;
-        x_l(2) = pi2pi(x_f(2) + delta(2));
-        x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>());
-
-        // Set states
-        frm1->setState(x_f);
-        lmk->setState(x_l);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
-
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb frm0, fix the rest
         frm1->unfix();
         lmk->fix();
-        sensor_odom2d->getP()->fix();
-        sensor_odom2d->getO()->fix();
-        frm1->perturb(5);
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm1->perturb(1);
 
         // solve for frm0
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_f, 1e-6);
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_f, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
@@ -363,47 +302,26 @@ TEST(FactorRelativePose2dWithExtrinsics, landmark_extrinsics_p_solve)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random frame pose
-        Vector3d x_f = Vector3d::Random() * 10;
-        pi2pi(x_f(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // landmark pose
-        Vector3d x_l;
-        x_l(2) = pi2pi(x_f(2) + delta(2));
-        x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>());
-
-        // Set states
-        frm1->setState(x_f);
-        lmk->setState(x_l);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
-
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb sensor P, fix the rest
         frm1->fix();
         lmk->fix();
-        sensor_odom2d->getP()->unfix();
-        sensor_odom2d->getO()->fix();
-        sensor_odom2d->getP()->perturb(1);
+        sensor->getP()->unfix();
+        sensor->getO()->fix();
+        sensor->getP()->perturb(1);
 
         // solve for frm0
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(sensor_odom2d->getStateVector(), xs, 1e-6);
+        ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
@@ -411,49 +329,28 @@ TEST(FactorRelativePose2dWithExtrinsics, landmark_extrinsics_o_solve)
 {
     for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random frame pose
-        Vector3d x_f = Vector3d::Random() * 10;
-        pi2pi(x_f(2));
-
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
-
-        // landmark pose
-        Vector3d x_l;
-        x_l(2) = pi2pi(x_f(2) + delta(2));
-        x_l.head<2>() = x_f.head<2>() + Rotation2Dd(x_f(2)) * (xs.head<2>() + Rotation2Dd(xs(2)) * delta.head<2>());
-
-        // Set states
-        frm1->setState(x_f);
-        lmk->setState(x_l);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
-
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, lmk, nullptr, false, TOP_LMK); // create and add
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
         // Perturb sensor O, fix the rest
         frm1->fix();
         lmk->fix();
-        sensor_odom2d->getP()->fix();
-        sensor_odom2d->getO()->unfix();
-        sensor_odom2d->getO()->perturb(.2);
+        sensor->getP()->fix();
+        sensor->getO()->unfix();
+        sensor->getO()->perturb(.2);
 
-        //std::cout << "Sensor O (perturbed): " << sensor_odom2d->getO()->getState().transpose() << std::endl;
+        //std::cout << "Sensor O (perturbed): " << sensor->getO()->getState().transpose() << std::endl;
 
         // solve for frm0
-        std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(sensor_odom2d->getStateVector(), xs, 1e-6);
+        ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
diff --git a/test/gtest_factor_relative_pose_3d.cpp b/test/gtest_factor_relative_pose_3d.cpp
index 9369c090efecad4b74435214230fca2b3b01f959..00c75bec4aeec1df11a17fbd39eea2f06ca8577e 100644
--- a/test/gtest_factor_relative_pose_3d.cpp
+++ b/test/gtest_factor_relative_pose_3d.cpp
@@ -30,7 +30,9 @@
 
 #include "core/ceres_wrapper/solver_ceres.h"
 #include "core/factor/factor_relative_pose_3d.h"
-#include "core/capture/capture_motion.h"
+#include "core/capture/capture_odom_3d.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_quaternion.h"
 
 
 using namespace Eigen;
@@ -38,72 +40,189 @@ using namespace wolf;
 using std::cout;
 using std::endl;
 
-Vector7d data2delta(Vector6d _data)
-{
-    return (Vector7d() << _data.head<3>() , v2q(_data.tail<3>()).coeffs()).finished();
-}
+std::string wolf_root = _WOLF_ROOT_DIR;
 
-// Input odometry data and covariance
-Vector6d data(Vector6d::Random());
-Vector7d delta = data2delta(data);
-Vector6d data_var((Vector6d() << 0.2,0.2,0.2,0.2,0.2,0.2).finished());
-Matrix6d data_cov = data_var.asDiagonal();
+int N = 1e2;
 
-// perturbated priors
-Vector7d x0 = data2delta(Vector6d::Random()*0.25);
-Vector7d x1 = data2delta(data + Vector6d::Random()*0.25);
+// Vectors
+Vector7d delta, x_0, x_1, x_f, x_l;
+
+// Input odometry data and covariance
+Matrix6d data_cov = (Vector6d() << 1e-3, 1e-3, 1e-3, 1e-6, 1e-6, 1e-6).finished().asDiagonal();
 
 // Problem and solver
 ProblemPtr problem_ptr = Problem::create("PO", 3);
+
 SolverCeres solver(problem_ptr);
 
 // Two frames
-FrameBasePtr frm0 = problem_ptr->emplaceFrame(TimeStamp(0), problem_ptr->stateZero());
-FrameBasePtr frm1 = problem_ptr->emplaceFrame(TimeStamp(1), delta);
-
-// Capture, feature and factor from frm1 to frm0
-auto cap1 = CaptureBase::emplace<CaptureMotion>(frm1, "CaptureOdom3d", 1, nullptr, delta, nullptr);
-auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom3d", delta, data_cov);
-FactorRelativePose3dPtr ctr1 = FactorBase::emplace<FactorRelativePose3d>(fea1, fea1, frm0, nullptr, false, TOP_MOTION); // create and add
+FrameBasePtr frm0 = problem_ptr->emplaceFrame(0, (Vector7d() << 0,0,0,0,0,0,1).finished() );
+FrameBasePtr frm1 = problem_ptr->emplaceFrame(1, (Vector7d() << 0,0,0,0,0,0,1).finished() );
+
+// Landmark
+LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                        "LandmarkPose3d",
+                                                        std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                        std::make_shared<StateQuaternion>(Quaterniond::Identity().coeffs()));
+
+// Capture
+auto cap1 = CaptureBase::emplace<CaptureOdom3d>(frm1, 1, nullptr, Vector7d::Zero(), data_cov);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePose3dPtr fac = nullptr;
 
 ////////////////////////////////////////////////////////
 
-TEST(FactorRelativePose3d, check_tree)
+void generateRandomProblemFrame()
 {
-    ASSERT_TRUE(problem_ptr->check(0));
+    // Random delta
+    delta = Vector7d::Random() * 1e2;
+    delta.tail<4>().normalize();
+    auto q_delta = Quaterniond(delta.tail<4>());
+
+    // Random frame 0 pose
+    x_0 = Vector7d::Random() * 1e2;
+    x_0.tail<4>().normalize();
+    auto q_0 = Quaterniond(x_0.tail<4>());
+
+    // Frame 1 pose
+    x_1.head<3>() = x_0.head<3>() + q_0 * delta.head<3>();
+    x_1.tail<4>() = (q_0 * q_delta).coeffs();
+
+    // Set states
+    frm0->setState(x_0);
+    frm1->setState(x_1);
+    cap1->setData(delta);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom3d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose3d>(fea, fea, frm0, nullptr, false, TOP_MOTION); // create and add
 }
 
-TEST(FactorRelativePose3d, expectation)
+void generateRandomProblemLandmark()
 {
-    ASSERT_MATRIX_APPROX(ctr1->expectation() , delta, 1e-6);
+    // Random delta
+    delta = Vector7d::Random() * 10;
+    delta.tail<4>().normalize();
+    auto q_delta = Quaterniond(delta.tail<4>());
+
+    // Random frame pose
+    x_f = Vector7d::Random() * 10;
+    x_f.tail<4>().normalize();
+    auto q_f = Quaterniond(x_f.tail<4>());
+
+    // Landmark pose
+    x_l.head<3>() = x_f.head<3>() + q_f * delta.head<3>();
+    x_l.tail<4>() = (q_f * q_delta).coeffs();
+
+    // Set states
+    frm1->setState(x_f);
+    lmk->setState(x_l);
+    cap1->setData(delta);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose3d>(fea, fea, lmk, nullptr, false, TOP_LMK); // create and add
 }
 
-TEST(FactorRelativePose3d, fix_0_solve)
+TEST(FactorRelativePose3d, check_tree)
+{
+    ASSERT_TRUE(problem_ptr->check(0));
+}
+
+// FRAME TO FRAME =========================================================================
+TEST(FactorRelativePose3d, frame_solve_frame1)
 {
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
 
-    // Fix frame 0, perturb frm1
-    frm0->fix();
-    frm1->unfix();
-    frm1->setState(x1);
+        // Perturb frm1, fix the rest
+        frm0->fix();
+        frm1->unfix();
+        frm1->perturb(1);
 
-    // solve for frm1
-    std::string report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+        // solve for frm1
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-    ASSERT_MATRIX_APPROX(frm1->getStateVector(), delta, 1e-6);
+        ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_1, 1e-3);
 
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
 }
 
-TEST(FactorRelativePose3d, fix_1_solve)
+TEST(FactorRelativePose3d, frame_solve_frame0)
 {
-    // Fix frame 1, perturb frm0
-    frm0->unfix();
-    frm1->fix();
-    frm0->setState(x0);
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
+        
+        // Perturb frm0, fix the rest
+        frm1->fix();
+        frm0->unfix();
+        frm0->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(frm0->getStateVector(), x_0, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
 
-    // solve for frm0
-    std::string brief_report = solver.solve(SolverManager::ReportVerbosity::BRIEF);
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePose3d, landmark_solve_lmk)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb landmark, fix the rest
+        frm1->fix();
+        lmk->unfix();
+        lmk->perturb(1);
+
+        // solve for landmark
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(lmk->getStateVector(), x_l, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
 
-    ASSERT_MATRIX_APPROX(frm0->getStateVector(), problem_ptr->stateZero().vector("PO"), 1e-6);
+TEST(FactorRelativePose3d, landmark_solve_frame)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb frm0, fix the rest
+        frm1->unfix();
+        lmk->fix();
+        frm1->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_f, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
 }
 
 int main(int argc, char **argv)
diff --git a/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp b/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp
index 54f4fc527a9d470442b2a1d6352ef064af6c21d4..2e99b1d2b765998d11c7eba96f7241e29977a7c4 100644
--- a/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp
+++ b/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp
@@ -1,378 +1,371 @@
-    //--------LICENSE_START--------
-    //
-    // Copyright (C) 2020,2021,2022 Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
-    // Authors: Joan Solà Ortega (jsola@iri.upc.edu)
-    // All rights reserved.
-    //
-    // This file is part of WOLF
-    // WOLF is free software: you can redistribute it and/or modify
-    // it under the terms of the GNU Lesser General Public License as published by
-    // the Free Software Foundation, either version 3 of the License, or
-    // at your option) any later version.
-    //
-    // This program is distributed in the hope that it will be useful,
-    // but WITHOUT ANY WARRANTY; without even the implied warranty of
-    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    // GNU Lesser General Public License for more details.
-    //
-    // You should have received a copy of the GNU Lesser General Public License
-    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
-    //
-    //--------LICENSE_END--------
-    #include "core/utils/utils_gtest.h"
-
-    #include "core/ceres_wrapper/solver_ceres.h"
-    #include "core/factor/factor_relative_pose_3d_with_extrinsics.h"
-    #include "core/capture/capture_odom_3d.h"
-    #include "core/sensor/sensor_odom_3d.h"
-    #include "core/math/rotations.h"
-    #include "core/state_block/state_block_derived.h"
-    #include "core/state_block/state_quaternion.h"
-
-
-    using namespace Eigen;
-    using namespace wolf;
-    using std::cout;
-    using std::endl;
-
-    std::string wolf_root = _WOLF_ROOT_DIR;
-
-    int N = 1e2;
-
-    // Vectors
-    Vector7d delta, x_0, x_1, x_f, x_l, x_s;
-
-    // Input odometry data and covariance
-    Matrix6d data_cov = (Vector6d() << 1e-3, 1e-3, 1e-3, 1e-6, 1e-6, 1e-6).finished().asDiagonal();
-
-    // Problem and solver
-    ProblemPtr problem_ptr = Problem::create("PO", 3);
-
-    SolverCeres solver(problem_ptr);
-
-    // Sensor
-    auto sensor_odom3d = problem_ptr->installSensor("SensorOdom3d", "odom", (Vector7d() << 0,0,0,0,0,0,1).finished(), wolf_root + "/test/yaml/sensor_odom_3d.yaml");
-
-    // Two frames
-    FrameBasePtr frm0 = problem_ptr->emplaceFrame(0, (Vector7d() << 0,0,0,0,0,0,1).finished() );
-    FrameBasePtr frm1 = problem_ptr->emplaceFrame(1, (Vector7d() << 0,0,0,0,0,0,1).finished() );
-
-    // Landmark
-    LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
-                                                            "LandmarkPose3d",
-                                                            std::make_shared<StatePoint3d>(Vector3d::Zero()),
-                                                            std::make_shared<StateQuaternion>(Quaterniond::Identity().coeffs()));
-
-    // Capture
-    auto cap1 = CaptureBase::emplace<CaptureOdom3d>(frm1, 1, sensor_odom3d, Vector7d::Zero(), data_cov);
-    // Feature
-    FeatureBasePtr fea = nullptr;
-    // Factor
-    FactorRelativePose3dWithExtrinsicsPtr fac = nullptr;
-
-    void generateRandomProblemFrame()
+//--------LICENSE_START--------
+//
+// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
+// Authors: Joan Solà Ortega (jsola@iri.upc.edu)
+// All rights reserved.
+//
+// This file is part of WOLF
+// WOLF is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//--------LICENSE_END--------
+#include "core/utils/utils_gtest.h"
+
+#include "core/ceres_wrapper/solver_ceres.h"
+#include "core/factor/factor_relative_pose_3d_with_extrinsics.h"
+#include "core/capture/capture_odom_3d.h"
+#include "core/sensor/sensor_odom_3d.h"
+#include "core/math/rotations.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_quaternion.h"
+
+
+using namespace Eigen;
+using namespace wolf;
+using std::cout;
+using std::endl;
+
+std::string wolf_root = _WOLF_ROOT_DIR;
+
+int N = 1e2;
+
+// Vectors
+Vector7d delta, x_0, x_1, x_f, x_l, x_s;
+
+// Input odometry data and covariance
+Matrix6d data_cov = (Vector6d() << 1e-3, 1e-3, 1e-3, 1e-6, 1e-6, 1e-6).finished().asDiagonal();
+
+// Problem and solver
+ProblemPtr problem_ptr = Problem::create("PO", 3);
+
+SolverCeres solver(problem_ptr);
+
+// Sensor
+auto sensor = problem_ptr->installSensor("SensorOdom3d", "odom", (Vector7d() << 0,0,0,0,0,0,1).finished(), wolf_root + "/test/yaml/sensor_odom_3d.yaml");
+
+// Two frames
+FrameBasePtr frm0 = problem_ptr->emplaceFrame(0, (Vector7d() << 0,0,0,0,0,0,1).finished() );
+FrameBasePtr frm1 = problem_ptr->emplaceFrame(1, (Vector7d() << 0,0,0,0,0,0,1).finished() );
+
+// Landmark
+LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                        "LandmarkPose3d",
+                                                        std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                        std::make_shared<StateQuaternion>(Quaterniond::Identity().coeffs()));
+
+// Capture
+auto cap1 = CaptureBase::emplace<CaptureOdom3d>(frm1, 1, sensor, Vector7d::Zero(), data_cov);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePose3dWithExtrinsicsPtr fac = nullptr;
+
+void generateRandomProblemFrame()
+{
+    // Random delta
+    delta = Vector7d::Random() * 1e2;
+    delta.tail<4>().normalize();
+    auto q_delta = Quaterniond(delta.tail<4>());
+
+    // Random frame 0 pose
+    x_0 = Vector7d::Random() * 1e2;
+    x_0.tail<4>().normalize();
+    auto q_0 = Quaterniond(x_0.tail<4>());
+
+    // Random extrinsics
+    x_s = Vector7d::Random() * 1e2;
+    x_s.tail<4>().normalize();
+    auto q_s = Quaterniond(x_s.tail<4>());
+
+    // Frame 1 pose
+    auto q_1 = q_0 * q_s * q_delta * q_s.conjugate();
+    x_1.head<3>() = x_0.head<3>() + q_0 * (x_s.head<3>() + q_s * delta.head<3>()) - q_1 *  x_s.head<3>();
+    x_1.tail<4>() = q_1.coeffs();
+
+    // WOLF_INFO("x_0:   ", x_0.transpose());
+    // WOLF_INFO("x_s:   ", x_s.transpose());
+    // WOLF_INFO("delta: ", delta.transpose());
+    // WOLF_INFO("x_1:   ", x_1.transpose());
+
+    // Set states
+    frm0->setState(x_0);
+    frm1->setState(x_1);
+    cap1->setData(delta);
+    sensor->setState(x_s);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom3d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(fea, fea, frm0, nullptr, false, TOP_MOTION); // create and add
+}
+
+void generateRandomProblemLandmark()
+{
+    // Random delta
+    delta = Vector7d::Random() * 10;
+    delta.tail<4>().normalize();
+    auto q_delta = Quaterniond(delta.tail<4>());
+
+    // Random frame pose
+    x_f = Vector7d::Random() * 10;
+    x_f.tail<4>().normalize();
+    auto q_f = Quaterniond(x_f.tail<4>());
+
+    // Random extrinsics
+    x_s = Vector7d::Random() * 10;
+    x_s.tail<4>().normalize();
+    auto q_s = Quaterniond(x_s.tail<4>());
+
+    // Landmark pose
+    x_l.head<3>() = x_f.head<3>() + q_f * (x_s.head<3>() + q_s * delta.head<3>());
+    x_l.tail<4>() = (q_f * q_s * q_delta).coeffs();
+
+    // Set states
+    frm1->setState(x_f);
+    lmk->setState(x_l);
+    cap1->setData(delta);
+    sensor->setState(x_s);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(fea, fea, lmk, nullptr, false, TOP_LMK); // create and add
+}
+
+TEST(FactorRelativePose3dWithExtrinsics, check_tree)
+{
+    ASSERT_TRUE(problem_ptr->check(0));
+}
+
+// FRAME TO FRAME =========================================================================
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame1)
+{
+    for (int i = 0; i < N; i++)
     {
-        // Random delta
-        delta = Vector7d::Random() * 1e2;
-        delta.tail<4>().normalize();
-        auto q_delta = Quaterniond(delta.tail<4>());
-
-        // Random frame 0 pose
-        x_0 = Vector7d::Random() * 1e2;
-        x_0.tail<4>().normalize();
-        auto q_0 = Quaterniond(x_0.tail<4>());
-
-        // Random extrinsics
-        x_s = Vector7d::Random() * 1e2;
-        x_s.tail<4>().normalize();
-        auto q_s = Quaterniond(x_s.tail<4>());
-
-        // Random frame 1 pose
-        auto q_1 = q_0 * q_s * q_delta * q_s.conjugate();
-        x_1.head<3>() = x_0.head<3>() + q_0 * (x_s.head<3>() + q_s * delta.head<3>()) - q_1 *  x_s.head<3>();
-        x_1.tail<4>() = q_1.coeffs();
-
-        // WOLF_INFO("x_0:   ", x_0.transpose());
-        // WOLF_INFO("x_s:   ", x_s.transpose());
-        // WOLF_INFO("delta: ", delta.transpose());
-        // WOLF_INFO("x_1:   ", x_1.transpose());
-
-        // Set states
-        frm0->setState(x_0);
-        frm1->setState(x_1);
-        cap1->setData(delta);
-        sensor_odom3d->setState(x_s);
-
-        // feature & factor with delta measurement
-        fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom3d", delta, data_cov);
-        fac = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(fea, fea, frm0, nullptr, false, TOP_MOTION); // create and add
-    }
+        // random setup
+        generateRandomProblemFrame();
 
-    void generateRandomProblemLandmark()
-    {
-        // Random delta
-        delta = Vector7d::Random() * 10;
-        delta.tail<4>().normalize();
-        auto q_delta = Quaterniond(delta.tail<4>());
-
-        // Random frame pose
-        x_f = Vector7d::Random() * 10;
-        x_f.tail<4>().normalize();
-        auto q_f = Quaterniond(x_f.tail<4>());
-
-        // Random extrinsics
-        x_s = Vector7d::Random() * 10;
-        x_s.tail<4>().normalize();
-        auto q_s = Quaterniond(x_s.tail<4>());
-
-        // landmark pose
-        x_l.head<3>() = x_f.head<3>() + q_f * (x_s.head<3>() + q_s * delta.head<3>());
-        x_l.tail<4>() = (q_f * q_s * q_delta).coeffs();
-
-        // Set states
-        frm1->setState(x_f);
-        lmk->setState(x_l);
-        cap1->setData(delta);
-        sensor_odom3d->setState(x_s);
-
-        // feature & factor with delta measurement
-        fea = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPose", delta, data_cov);
-        fac = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(fea, fea, lmk, nullptr, false, TOP_LMK); // create and add
-    }
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
 
-    TEST(FactorRelativePose3dWithExtrinsics, check_tree)
-    {
-        ASSERT_TRUE(problem_ptr->check(0));
-    }
+        // Perturb frm1, fix the rest
+        frm0->fix();
+        frm1->unfix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm1->perturb(1);
 
-    // FRAME TO FRAME =========================================================================
-    TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame1)
-    {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemFrame();
+        // solve for frm1
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+        ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_1, 1e-3);
 
-            // Perturb frm1, fix the rest
-            frm0->fix();
-            frm1->unfix();
-            sensor_odom3d->getP()->fix();
-            sensor_odom3d->getO()->fix();
-            frm1->perturb(1);
-
-            // solve for frm1
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO(report);
-
-            ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_1, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame0)
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame0)
+{
+    for (int i = 0; i < N; i++)
     {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemFrame();
-            
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
-
-            // Perturb frm0, fix the rest
-            frm1->fix();
-            frm0->unfix();
-            sensor_odom3d->getP()->fix();
-            sensor_odom3d->getO()->fix();
-            frm0->perturb(1);
-
-            // solve for frm0
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO(report);
-
-            ASSERT_POSE3d_APPROX(frm0->getStateVector(), x_0, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb frm0, fix the rest
+        frm1->fix();
+        frm0->unfix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm0->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(frm0->getStateVector(), x_0, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    // JV: The position extrinsics in case of pair of frames apears to be not very observable
-    TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_p)
+// JV: The position extrinsics in case of pair of frames is not observable
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_p)
+{
+    for (int i = 0; i < N; i++)
     {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemFrame();
-            
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
-
-            // Perturb sensor P, fix the rest
-            frm1->fix();
-            frm0->fix();
-            sensor_odom3d->getP()->unfix();
-            sensor_odom3d->getO()->fix();
-            //WOLF_INFO("sensor P before perturbing: ", sensor_odom3d->getP()->getState().transpose());
-            sensor_odom3d->getP()->perturb(1);
-            //WOLF_INFO("sensor P after perturbing: ", sensor_odom3d->getP()->getState().transpose());
-
-            // solve for frm0
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO("sensor P after solving: ", sensor_odom3d->getP()->getState().transpose());
-            //WOLF_INFO(report);
-
-            //ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb sensor P, fix the rest
+        frm1->fix();
+        frm0->fix();
+        sensor->getP()->unfix();
+        sensor->getO()->fix();
+        sensor->getP()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        //ASSERT_POSE3d_APPROX(sensor->getStateVector(), x_s, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_o)
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_o)
+{
+    for (int i = 0; i < N; i++)
     {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemFrame();
-            
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
-
-            // Perturb sensor O, fix the rest
-            frm1->fix();
-            frm0->fix();
-            sensor_odom3d->getP()->fix();
-            sensor_odom3d->getO()->unfix();
-            sensor_odom3d->getO()->perturb(.2);
-
-            //std::cout << "Sensor O (perturbed): " << sensor_odom3d->getO()->getState().transpose() << std::endl;
-
-            // solve for frm0
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO(report);
-
-            ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb sensor O, fix the rest
+        frm1->fix();
+        frm0->fix();
+        sensor->getP()->fix();
+        sensor->getO()->unfix();
+        sensor->getO()->perturb(.2);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(sensor->getStateVector(), x_s, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    // FRAME TO LANDMARK =========================================================================
-    TEST(FactorRelativePose3dWithExtrinsics, landmark_solve_lmk)
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePose3dWithExtrinsics, landmark_solve_lmk)
+{
+    for (int i = 0; i < N; i++)
     {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemLandmark();
-            
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
-
-            // Perturb landmark, fix the rest
-            frm1->fix();
-            lmk->unfix();
-            sensor_odom3d->getP()->fix();
-            sensor_odom3d->getO()->fix();
-            lmk->perturb(1);
-
-            // solve for landmark
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO(report);
-
-            ASSERT_POSE3d_APPROX(lmk->getStateVector(), x_l, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb landmark, fix the rest
+        frm1->fix();
+        lmk->unfix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        lmk->perturb(1);
+
+        // solve for landmark
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(lmk->getStateVector(), x_l, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    TEST(FactorRelativePose3dWithExtrinsics, landmark_solve_frame)
+TEST(FactorRelativePose3dWithExtrinsics, landmark_solve_frame)
+{
+    for (int i = 0; i < N; i++)
     {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemLandmark();
-            
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
-
-            // Perturb frm0, fix the rest
-            frm1->unfix();
-            lmk->fix();
-            sensor_odom3d->getP()->fix();
-            sensor_odom3d->getO()->fix();
-            frm1->perturb(1);
-
-            // solve for frm0
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO(report);
-
-            ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_f, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb frm0, fix the rest
+        frm1->unfix();
+        lmk->fix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm1->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_f, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    TEST(FactorRelativePose3dWithExtrinsics, landmark_extrinsics_p_solve)
+TEST(FactorRelativePose3dWithExtrinsics, landmark_extrinsics_p_solve)
+{
+    for (int i = 0; i < N; i++)
     {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemLandmark();
-            
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
-
-            // Perturb sensor P, fix the rest
-            frm1->fix();
-            lmk->fix();
-            sensor_odom3d->getP()->unfix();
-            sensor_odom3d->getO()->fix();
-            sensor_odom3d->getP()->perturb(1);
-
-            // solve for frm0
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO(report);
-
-            ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb sensor P, fix the rest
+        frm1->fix();
+        lmk->fix();
+        sensor->getP()->unfix();
+        sensor->getO()->fix();
+        sensor->getP()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(sensor->getStateVector(), x_s, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    TEST(FactorRelativePose3dWithExtrinsics, landmark_extrinsics_o_solve)
+TEST(FactorRelativePose3dWithExtrinsics, landmark_extrinsics_o_solve)
+{
+    for (int i = 0; i < N; i++)
     {
-        for (int i = 0; i < N; i++)
-        {
-            // random setup
-            generateRandomProblemLandmark();
-            
-            ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
-
-            // Perturb sensor O, fix the rest
-            frm1->fix();
-            lmk->fix();
-            sensor_odom3d->getP()->fix();
-            sensor_odom3d->getO()->unfix();
-            sensor_odom3d->getO()->perturb(.2);
-
-            //std::cout << "Sensor O (perturbed): " << sensor_odom3d->getO()->getState().transpose() << std::endl;
-
-            // solve for frm0
-            std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
-            //WOLF_INFO(report);
-
-            ASSERT_POSE3d_APPROX(sensor_odom3d->getStateVector(), x_s, 1e-3);
-
-            // remove feature (and factor) for the next loop
-            fea->remove();
-        }
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb sensor O, fix the rest
+        frm1->fix();
+        lmk->fix();
+        sensor->getP()->fix();
+        sensor->getO()->unfix();
+        sensor->getO()->perturb(.2);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(sensor->getStateVector(), x_s, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
     }
+}
 
-    int main(int argc, char **argv)
-    {
-    testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
-    }
+int main(int argc, char **argv)
+{
+testing::InitGoogleTest(&argc, argv);
+return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_factor_relative_position_2d.cpp b/test/gtest_factor_relative_position_2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e91d0fb436646ea92401d64e5bde466e5989c6d9
--- /dev/null
+++ b/test/gtest_factor_relative_position_2d.cpp
@@ -0,0 +1,268 @@
+//--------LICENSE_START--------
+//
+// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
+// Authors: Joan Solà Ortega (jsola@iri.upc.edu)
+// All rights reserved.
+//
+// This file is part of WOLF
+// WOLF is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//--------LICENSE_END--------
+#include "core/utils/utils_gtest.h"
+
+#include "core/ceres_wrapper/solver_ceres.h"
+#include "core/factor/factor_relative_position_2d.h"
+#include "core/capture/capture_void.h"
+#include "core/sensor/sensor_odom_2d.h"
+#include "core/math/rotations.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
+
+
+using namespace Eigen;
+using namespace wolf;
+using std::cout;
+using std::endl;
+
+std::string wolf_root = _WOLF_ROOT_DIR;
+
+int N = 1;//1e2
+
+// Vectors
+Vector3d x_0, x_f, x_1;
+Vector2d delta, x_l;
+
+// Input odometry data and covariance
+Matrix2d data_cov = 0.1*Matrix2d::Identity();
+
+// Problem and solver
+ProblemPtr problem_ptr = Problem::create("PO", 2);
+SolverCeres solver(problem_ptr);
+
+// Frames
+FrameBasePtr frm0 = problem_ptr->emplaceFrame(0, Vector3d::Zero() );
+FrameBasePtr frm1 = problem_ptr->emplaceFrame(1, Vector3d::Zero() );
+
+// Landmark
+LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                          "LandmarkPosition2d",
+                                                          std::make_shared<StatePoint2d>(Vector2d::Zero()),
+                                                          nullptr);
+
+// Capture
+auto cap = CaptureBase::emplace<CaptureVoid>(frm1, 1, nullptr);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePosition2dPtr fac = nullptr;
+
+void generateRandomProblemFrame()
+{
+    // Random delta
+    delta = Vector2d::Random() * 1e2;
+
+    // Random frame 0 pose
+    x_0 = Vector3d::Random() * 1e2;
+    x_0(2) = pi2pi(x_0(2));
+
+    // Frame 1 position
+    x_1.head<2>() = x_0.head<2>() + Rotation2Dd(x_0(2)) * delta;
+
+    // Set states
+    frm0->setState(x_0);
+    frm1->setState(x_1);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap, "FeatureFramePosition2d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePosition2d>(fea, fea, frm0, nullptr, false, TOP_MOTION);
+}
+
+void generateRandomProblemLandmark()
+{
+    // Random delta
+    delta = Vector2d::Random() * 1e2;
+
+    // Random frame pose
+    x_f = Vector3d::Random() * 1e2;
+    x_f(2) = pi2pi(x_f(2));
+
+    // landmark position
+    x_l = x_f.head<2>() + Rotation2Dd(x_f(2)) * delta;
+
+    // Set states
+    frm1->setState(x_f);
+    lmk->setState(x_l);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap, "FeatureLandmarkPosition2d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePosition2d>(fea, fea, lmk, nullptr, false, TOP_LMK);
+}
+
+////////////// TESTS /////////////////////////////////////////////////////////////////////
+
+TEST(FactorRelativePosition2d, check_tree)
+{
+    ASSERT_TRUE(problem_ptr->check(0));
+}
+
+// FRAME TO FRAME =========================================================================
+TEST(FactorRelativePosition2d, frame_solve_frame1_p)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
+
+        // Perturb frm1, fix the rest
+        frm0->fix();
+        frm1->getP()->unfix();
+        frm1->getO()->fix();
+        frm1->getP()->perturb(1);
+
+        // solve for frm1
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_1, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2d, frame_solve_frame0_p)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
+        
+        // Perturb frm0, fix the rest
+        frm1->fix();
+        frm0->getP()->unfix();
+        frm0->getO()->fix();
+        frm0->getP()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(frm0->getStateVector(), x_0, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2d, frame_solve_frame0_o)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
+        
+        // Perturb frm0, fix the rest
+        frm1->fix();
+        frm0->getP()->fix();
+        frm0->getO()->unfix();
+        frm0->getO()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(frm0->getStateVector(), x_0, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePosition2d, landmark_solve_lmk)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb landmark, fix the rest
+        frm1->fix();
+        lmk->unfix();
+        lmk->perturb(1);
+
+        // solve for landmark
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_MATRIX_APPROX(lmk->getStateVector(), x_l, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2d, landmark_solve_frame_p)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb frm0, fix the rest
+        frm1->getP()->unfix();
+        frm1->getO()->fix();
+        lmk->fix();
+        frm1->getP()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_f, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2d, landmark_solve_frame_o)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb frm0, fix the rest
+        frm1->getP()->fix();
+        frm1->getO()->unfix();
+        lmk->fix();
+        frm1->getO()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_f, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_factor_relative_position_2d_with_extrinsics.cpp b/test/gtest_factor_relative_position_2d_with_extrinsics.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..87bb8f07080f83760be2fe1896904c8f64e2bd92
--- /dev/null
+++ b/test/gtest_factor_relative_position_2d_with_extrinsics.cpp
@@ -0,0 +1,239 @@
+//--------LICENSE_START--------
+//
+// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
+// Authors: Joan Solà Ortega (jsola@iri.upc.edu)
+// All rights reserved.
+//
+// This file is part of WOLF
+// WOLF is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//--------LICENSE_END--------
+#include "core/utils/utils_gtest.h"
+
+#include "core/ceres_wrapper/solver_ceres.h"
+#include "core/factor/factor_relative_position_2d_with_extrinsics.h"
+#include "core/capture/capture_void.h"
+#include "core/sensor/sensor_odom_2d.h"
+#include "core/math/rotations.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
+
+
+using namespace Eigen;
+using namespace wolf;
+using std::cout;
+using std::endl;
+
+std::string wolf_root = _WOLF_ROOT_DIR;
+
+int N = 1;//1e2
+
+// Vectors
+Vector3d x_f, x_s;
+Vector2d delta, x_l;
+
+// Input odometry data and covariance
+Matrix2d data_cov = 0.1*Matrix2d::Identity();
+
+// Problem and solver
+ProblemPtr problem_ptr = Problem::create("PO", 2);
+SolverCeres solver(problem_ptr);
+
+// Sensor
+auto sensor = problem_ptr->installSensor("SensorOdom2d", "odom", Vector3d(0,0,0), wolf_root + "/test/yaml/sensor_odom_2d.yaml");
+
+// Frame
+FrameBasePtr frm = problem_ptr->emplaceFrame(0, Vector3d::Zero() );
+
+// Landmark
+LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                          "LandmarkPosition2d",
+                                                          std::make_shared<StatePoint2d>(Vector2d::Zero()),
+                                                          nullptr);
+
+// Capture
+auto cap = CaptureBase::emplace<CaptureVoid>(frm, 1, sensor);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePosition2dWithExtrinsicsPtr fac = nullptr;
+
+void generateRandomProblemLandmark()
+{
+    // Random delta
+    delta = Vector2d::Random() * 1e2;
+
+    // Random frame pose
+    x_f = Vector3d::Random() * 1e2;
+    x_f(2) = pi2pi(x_f(2));
+
+    // Random extrinsics
+    x_s = Vector3d::Random() * 1e2;
+    x_s(2) = pi2pi(x_s(2));
+
+    // landmark position
+    x_l = x_f.head<2>() + Rotation2Dd(x_f(2)) * (x_s.head<2>() + Rotation2Dd(x_s(2)) * delta);
+
+    // Set states
+    frm->setState(x_f);
+    lmk->setState(x_l);
+    sensor->setState(x_s);
+
+    // feature & factor with delta measurement
+    fea = FeatureBase::emplace<FeatureBase>(cap, "FeatureLandmarkPosition2d", delta, data_cov);
+    fac = FactorBase::emplace<FactorRelativePosition2dWithExtrinsics>(fea, fea, lmk, nullptr, false, TOP_LMK);
+}
+
+////////////// TESTS /////////////////////////////////////////////////////////////////////
+
+TEST(FactorRelativePosition2dWithExtrinsics, check_tree)
+{
+    ASSERT_TRUE(problem_ptr->check(0));
+}
+
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePosition2dWithExtrinsics, landmark_solve_lmk)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb landmark, fix the rest
+        frm->fix();
+        lmk->unfix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        lmk->perturb(1);
+
+        // solve for landmark
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_MATRIX_APPROX(lmk->getStateVector(), x_l, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2dWithExtrinsics, landmark_solve_frame_p)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb frm0, fix the rest
+        frm->getP()->unfix();
+        frm->getO()->fix();
+        lmk->fix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm->getP()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(frm->getStateVector(), x_f, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2dWithExtrinsics, landmark_solve_frame_o)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb frm0, fix the rest
+        frm->getP()->fix();
+        frm->getO()->unfix();
+        lmk->fix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm->getO()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(frm->getStateVector(), x_f, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2dWithExtrinsics, landmark_extrinsics_p_solve)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb sensor P, fix the rest
+        frm->fix();
+        lmk->fix();
+        sensor->getP()->unfix();
+        sensor->getO()->fix();
+        sensor->getP()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePosition2dWithExtrinsics, landmark_extrinsics_o_solve)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb sensor O, fix the rest
+        frm->fix();
+        lmk->fix();
+        sensor->getP()->fix();
+        sensor->getO()->unfix();
+        sensor->getO()->perturb(.2);
+
+        //std::cout << "Sensor O (perturbed): " << sensor->getO()->getState().transpose() << std::endl;
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}