diff --git a/.clang-format b/.clang-format
index bc951c7539c78c4fa00efde0030dcf12b126686a..8f284dd854c7257bc8e4af2bb9e9a056acf81d6c 100644
--- a/.clang-format
+++ b/.clang-format
@@ -6,7 +6,7 @@ AccessModifierOffset: -2
 AlignAfterOpenBracket: Align
 AlignConsecutiveAssignments: true
 AlignConsecutiveDeclarations: true
-AlignEscapedNewlines: Left
+AlignEscapedNewlines: Right
 AlignOperands: true
 AlignTrailingComments: true
 AllowAllParametersOfDeclarationOnNextLine: false
@@ -19,7 +19,7 @@ AlwaysBreakAfterDefinitionReturnType: None
 AlwaysBreakAfterReturnType: None
 AlwaysBreakBeforeMultilineStrings: true
 AlwaysBreakTemplateDeclarations: true
-BinPackArguments: true
+BinPackArguments: false
 BinPackParameters: false
 BreakBeforeBraces: Custom
 BraceWrapping:
diff --git a/.gitignore b/.gitignore
index 2a19e53e62aaa9c251b2f92657392e589597f1d6..c81d93b330db1ee8455e4c073e979746edbc3728 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ build_debug/
 build_release/
 build-debug/
 build-release/
+build-test/
 lib/
 .idea/
 ./Wolf.user
@@ -34,7 +35,5 @@ src/examples/map_apriltag_save.yaml
 
 \.vscode/
 .clangd
-wolfcore.found
-/wolf.found
 .ccls*
 compile_commands.json
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8337cb1148e9f8669aecc9a6ab7558c5a08446b9..b6b0a3b5c9c447bf5402a403714351831a9cf342 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,3 +1,13 @@
+workflow:
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "web"'
+    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
+      when: never
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+    - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
+      when: never
+    - if: '$CI_COMMIT_BRANCH'
+
 stages:
   - license
   - build_and_test
@@ -89,7 +99,7 @@ stages:
   - cd build
   - cmake -DCMAKE_BUILD_TYPE=release -DBUILD_DEMOS=ON -DBUILD_TESTS=ON ..
   - make -j$(nproc)
-  - ctest -j$(nproc)
+  - ctest -j$(nproc) --output-on-failure
   # run demos
   - ../bin/hello_wolf
   - ../bin/hello_wolf_autoconf
@@ -126,6 +136,8 @@ deploy_imu:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH != "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     DEPLOY_CI_ROS: "false"
@@ -138,6 +150,8 @@ deploy_gnss:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH != "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     GNSSUTILS_BRANCH: $GNSSUTILS_BRANCH
@@ -151,6 +165,8 @@ deploy_vision:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH != "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     DEPLOY_CI_ROS: "false"
@@ -163,6 +179,8 @@ deploy_laser:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH != "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     LASERSCANUTILS_BRANCH: $LASERSCANUTILS_BRANCH
@@ -176,6 +194,8 @@ deploy_apriltag:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH != "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     WOLF_VISION_BRANCH: $WOLF_VISION_BRANCH
@@ -189,6 +209,8 @@ deploy_bodydynamics:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH != "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     WOLF_IMU_BRANCH: $WOLF_IMU_BRANCH
@@ -203,6 +225,8 @@ deploy_imu_main:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH == "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: main
     DEPLOY_CI_ROS: "false"
@@ -215,6 +239,8 @@ deploy_gnss_main:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH == "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: main
     GNSSUTILS_BRANCH: $GNSSUTILS_BRANCH
@@ -228,6 +254,8 @@ deploy_vision_main:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH == "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: main
     DEPLOY_CI_ROS: "false"
@@ -240,6 +268,8 @@ deploy_laser_main:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH == "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: main
     LASERSCANUTILS_BRANCH: $LASERSCANUTILS_BRANCH
@@ -253,6 +283,8 @@ deploy_apriltag_main:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH == "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: main
     WOLF_VISION_BRANCH: main
@@ -266,6 +298,8 @@ deploy_bodydynamics_main:
   stage: deploy_plugins
   rules: 
     - if: $CI_COMMIT_BRANCH == "main"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: main
     WOLF_IMU_BRANCH: main
@@ -286,6 +320,8 @@ deploy_wolf_ros_node:
   stage: deploy_ros
   rules: 
     - if: $CI_COMMIT_BRANCH != "main" && $DEPLOY_CI_ROS == "true"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     WOLF_IMU_BRANCH: $WOLF_IMU_BRANCH
@@ -311,6 +347,8 @@ deploy_wolf_ros_node_main:
   stage: deploy_ros
   rules: 
     - if: $CI_COMMIT_BRANCH == "main" && $DEPLOY_CI_ROS == "true"
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
   variables:
     WOLF_CORE_BRANCH: $CI_COMMIT_BRANCH
     WOLF_IMU_BRANCH: main
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e69a3926a1a15adc529a45d8c37c7aa3a5437ff5..897854374187e8d3fb517b1ec1bf5f849c719f51 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,7 +16,7 @@ SET(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY FALSE)
 SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
 SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
 SET(LIB_INSTALL_DIR lib)
-SET(INCLUDE_INSTALL_DIR include/iri-algorithms/wolf)
+SET(INCLUDE_INSTALL_DIR include/wolf)
 
 # Build type
 IF (NOT CMAKE_BUILD_TYPE)
@@ -33,7 +33,7 @@ include(CheckCXXCompilerFlag)
 CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
 if(COMPILER_SUPPORTS_CXX14)
   message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has C++14 support.")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
+  set(CMAKE_CXX_STANDARD 14)
 else()
   message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler.")
 endif()
@@ -49,6 +49,10 @@ IF(NOT BUILD_TESTS)
   OPTION(BUILD_TESTS "Build Unit tests" ON)
 ENDIF(NOT BUILD_TESTS)
 
+IF(NOT BUILD_GMOCK)
+  OPTION(BUILD_GMOCK "Build GMock" OFF)
+ENDIF(NOT BUILD_GMOCK)
+
 IF(NOT BUILD_DEMOS)
   OPTION(BUILD_DEMOS "Build Demos" ON)
 ENDIF(NOT BUILD_DEMOS)
@@ -80,6 +84,7 @@ option(_WOLF_TRACE "Enable wolf tracing macro" ON)
 FIND_PACKAGE(Threads REQUIRED MODULE)
 FIND_PACKAGE(Ceres REQUIRED CONFIG)
 FIND_PACKAGE(Eigen3 3.3 REQUIRED CONFIG)
+FIND_PACKAGE(spdlog REQUIRED CONFIG)
 if(${EIGEN3_VERSION_STRING} VERSION_LESS 3.3)
   message(FATAL_ERROR "Wolf requires Eigen >= 3.3. Found Eigen ${EIGEN3_VERSION_STRING}")
 endif()
@@ -89,7 +94,7 @@ FIND_PACKAGE(yaml-cpp REQUIRED CONFIG)
 set(_WOLF_ROOT_DIR ${CMAKE_SOURCE_DIR})
 set(_WOLF_LIB_DIR ${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR})
 # Define the directory where will be the configured config.h
-SET(WOLF_CONFIG_DIR ${PROJECT_BINARY_DIR}/conf/core/internal)
+SET(WOLF_CONFIG_DIR ${PROJECT_BINARY_DIR}/conf/${PROJECT_NAME}/internal)
 
 # Create the specified output directory if it does not exist.
 IF(NOT EXISTS "${WOLF_CONFIG_DIR}")
@@ -100,168 +105,162 @@ IF(EXISTS "${WOLF_CONFIG_DIR}" AND NOT IS_DIRECTORY "${WOLF_CONFIG_DIR}")
   message(FATAL_ERROR "Bug: Specified CONFIG_DIR: "
     "${WOLF_CONFIG_DIR} exists, but is not a directory.")
 ENDIF()
+
 # Configure config.h
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/internal/config.h.in "${WOLF_CONFIG_DIR}/config.h")
-message(STATUS "WOLF CONFIG DIRECTORY ${WOLF_CONFIG_DIR}")
-message(STATUS "WOLF CONFIG FILE      ${WOLF_CONFIG_DIR}/config.h")
-include_directories("${PROJECT_BINARY_DIR}/conf")
-
-# include spdlog (logging library)
-FIND_PATH(SPDLOG_INCLUDE_DIR spdlog.h /usr/local/include/spdlog /usr/include/spdlog)
-IF (SPDLOG_INCLUDE_DIR)
-  # INCLUDE_DIRECTORIES(${SPDLOG_INCLUDE_DIR})
-  MESSAGE(STATUS "Found spdlog: ${SPDLOG_INCLUDE_DIR}")
-ELSE (SPDLOG_INCLUDE_DIR)
- MESSAGE(FATAL_ERROR "Could not find spdlog")
-ENDIF (SPDLOG_INCLUDE_DIR)
-
-# ============ INCLUDES ============ 
-INCLUDE_DIRECTORIES("include") # In this same project
 
 # ============ HEADERS ============ 
 SET(HDRS_CAPTURE
-  include/core/capture/capture_base.h
-  include/core/capture/capture_motion.h
-  include/core/capture/capture_odom_2d.h
-  include/core/capture/capture_odom_3d.h
-  include/core/capture/capture_pose.h
-  include/core/capture/capture_void.h
-  include/core/capture/capture_diff_drive.h
+  include/${PROJECT_NAME}/capture/capture_base.h
+  include/${PROJECT_NAME}/capture/capture_diff_drive.h
+  include/${PROJECT_NAME}/capture/capture_landmarks_external.h
+  include/${PROJECT_NAME}/capture/capture_motion.h
+  include/${PROJECT_NAME}/capture/capture_odom_2d.h
+  include/${PROJECT_NAME}/capture/capture_odom_3d.h
+  include/${PROJECT_NAME}/capture/capture_pose.h
+  include/${PROJECT_NAME}/capture/capture_void.h
   )
 SET(HDRS_COMMON
-  include/core/common/factory.h
-  include/core/common/node_base.h
-  include/core/common/time_stamp.h
-  include/core/common/wolf.h
-  include/core/common/params_base.h
+  include/${PROJECT_NAME}/common/factory.h
+  include/${PROJECT_NAME}/common/node_base.h
+  include/${PROJECT_NAME}/common/time_stamp.h
+  include/${PROJECT_NAME}/common/wolf.h
+  include/${PROJECT_NAME}/common/params_base.h
   )
 SET(HDRS_FACTOR
-  include/core/factor/factor_analytic.h
-  include/core/factor/factor_autodiff.h
-  include/core/factor/factor_base.h
-  include/core/factor/factor_block_absolute.h
-  include/core/factor/factor_block_difference.h
-  include/core/factor/factor_diff_drive.h
-  include/core/factor/factor_distance_3d.h
-  include/core/factor/factor_pose_2d.h
-  include/core/factor/factor_pose_3d.h
-  include/core/factor/factor_quaternion_absolute.h
-  include/core/factor/factor_relative_pose_2d.h
-  include/core/factor/factor_relative_pose_2d_with_extrinsics.h
-  include/core/factor/factor_relative_pose_3d.h
-  include/core/factor/factor_pose_3d_with_extrinsics.h
-  include/core/factor/factor_relative_pose_3d_with_extrinsics.h
-  include/core/factor/factor_velocity_local_direction_3d.h
+  include/${PROJECT_NAME}/factor/factor_analytic.h
+  include/${PROJECT_NAME}/factor/factor_autodiff.h
+  include/${PROJECT_NAME}/factor/factor_base.h
+  include/${PROJECT_NAME}/factor/factor_block_absolute.h
+  include/${PROJECT_NAME}/factor/factor_block_difference.h
+  include/${PROJECT_NAME}/factor/factor_diff_drive.h
+  include/${PROJECT_NAME}/factor/factor_distance_3d.h
+  include/${PROJECT_NAME}/factor/factor_pose_2d.h
+  include/${PROJECT_NAME}/factor/factor_pose_3d.h
+  include/${PROJECT_NAME}/factor/factor_quaternion_absolute.h
+  include/${PROJECT_NAME}/factor/factor_pose_3d_with_extrinsics.h
+  include/${PROJECT_NAME}/factor/factor_relative_pose_2d.h
+  include/${PROJECT_NAME}/factor/factor_relative_pose_2d_with_extrinsics.h
+  include/${PROJECT_NAME}/factor/factor_relative_pose_3d.h
+  include/${PROJECT_NAME}/factor/factor_relative_pose_3d_with_extrinsics.h
+  include/${PROJECT_NAME}/factor/factor_relative_position_2d.h
+  include/${PROJECT_NAME}/factor/factor_relative_position_2d_with_extrinsics.h
+  include/${PROJECT_NAME}/factor/factor_relative_position_3d.h
+  include/${PROJECT_NAME}/factor/factor_relative_position_3d_with_extrinsics.h
+  include/${PROJECT_NAME}/factor/factor_velocity_local_direction_3d.h
   )
 SET(HDRS_FEATURE
-  include/core/feature/feature_base.h
-  include/core/feature/feature_diff_drive.h
-  include/core/feature/feature_match.h
-  include/core/feature/feature_motion.h
-  include/core/feature/feature_odom_2d.h
-  include/core/feature/feature_pose.h
+  include/${PROJECT_NAME}/feature/feature_base.h
+  include/${PROJECT_NAME}/feature/feature_diff_drive.h
+  include/${PROJECT_NAME}/feature/feature_match.h
+  include/${PROJECT_NAME}/feature/feature_motion.h
+  include/${PROJECT_NAME}/feature/feature_odom_2d.h
+  include/${PROJECT_NAME}/feature/feature_pose.h
   )
 SET(HDRS_FRAME
-  include/core/frame/frame_base.h
+  include/${PROJECT_NAME}/frame/frame_base.h
   )
 SET(HDRS_HARDWARE
-  include/core/hardware/hardware_base.h
+  include/${PROJECT_NAME}/hardware/hardware_base.h
   )
 SET(HDRS_LANDMARK
-  include/core/landmark/landmark_base.h
-  include/core/landmark/landmark_match.h
+  include/${PROJECT_NAME}/landmark/landmark_base.h
+  include/${PROJECT_NAME}/landmark/landmark_match.h
   )
 SET(HDRS_MATH
-  include/core/math/SE2.h
-  include/core/math/SE3.h
-  include/core/math/rotations.h
-  include/core/math/covariance.h
+  include/${PROJECT_NAME}/math/SE2.h
+  include/${PROJECT_NAME}/math/SE3.h
+  include/${PROJECT_NAME}/math/rotations.h
+  include/${PROJECT_NAME}/math/covariance.h
   )
 SET(HDRS_MAP
-  include/core/map/factory_map.h
-  include/core/map/map_base.h
+  include/${PROJECT_NAME}/map/factory_map.h
+  include/${PROJECT_NAME}/map/map_base.h
   )
 SET(HDRS_PROBLEM
-  include/core/problem/problem.h
+  include/${PROJECT_NAME}/problem/problem.h
   )
 SET(HDRS_PROCESSOR
-  include/core/processor/motion_buffer.h
-  include/core/processor/motion_provider.h
-  include/core/processor/processor_base.h
-  include/core/processor/processor_diff_drive.h
-  include/core/processor/processor_fixed_wing_model.h
-  include/core/processor/factory_processor.h
-  include/core/processor/processor_loop_closure.h
-  include/core/processor/processor_motion.h
-  include/core/processor/processor_odom_2d.h
-  include/core/processor/processor_odom_3d.h
-  include/core/processor/processor_pose.h
-  include/core/processor/processor_tracker.h
-  include/core/processor/processor_tracker_feature.h
-  include/core/processor/processor_tracker_landmark.h
-  include/core/processor/track_matrix.h
+  include/${PROJECT_NAME}/processor/motion_buffer.h
+  include/${PROJECT_NAME}/processor/motion_provider.h
+  include/${PROJECT_NAME}/processor/processor_base.h
+  include/${PROJECT_NAME}/processor/processor_diff_drive.h
+  include/${PROJECT_NAME}/processor/processor_fixed_wing_model.h
+  include/${PROJECT_NAME}/processor/factory_processor.h
+  include/${PROJECT_NAME}/processor/processor_landmark_external.h
+  include/${PROJECT_NAME}/processor/processor_loop_closure.h
+  include/${PROJECT_NAME}/processor/processor_motion.h
+  include/${PROJECT_NAME}/processor/processor_odom_2d.h
+  include/${PROJECT_NAME}/processor/processor_odom_3d.h
+  include/${PROJECT_NAME}/processor/processor_pose.h
+  include/${PROJECT_NAME}/processor/processor_tracker.h
+  include/${PROJECT_NAME}/processor/processor_tracker_feature.h
+  include/${PROJECT_NAME}/processor/processor_tracker_landmark.h
+  include/${PROJECT_NAME}/processor/track_matrix.h
   )
 SET(HDRS_SENSOR
-  include/core/sensor/sensor_base.h
-  include/core/sensor/sensor_diff_drive.h
-  include/core/sensor/factory_sensor.h
-  include/core/sensor/sensor_motion_model.h
-  include/core/sensor/sensor_odom_2d.h
-  include/core/sensor/sensor_odom_3d.h
-  include/core/sensor/sensor_pose.h
+  include/${PROJECT_NAME}/sensor/sensor_base.h
+  include/${PROJECT_NAME}/sensor/sensor_diff_drive.h
+  include/${PROJECT_NAME}/sensor/factory_sensor.h
+  include/${PROJECT_NAME}/sensor/sensor_motion_model.h
+  include/${PROJECT_NAME}/sensor/sensor_odom_2d.h
+  include/${PROJECT_NAME}/sensor/sensor_odom_3d.h
+  include/${PROJECT_NAME}/sensor/sensor_pose.h
   )
 SET(HDRS_SOLVER
-  include/core/solver/solver_manager.h
-  include/core/solver/factory_solver.h
+  include/${PROJECT_NAME}/solver/solver_manager.h
+  include/${PROJECT_NAME}/solver/factory_solver.h
   )
 SET(HDRS_STATE_BLOCK
-  include/core/state_block/factory_state_block.h
-  include/core/state_block/has_state_blocks.h
-  include/core/state_block/local_parametrization_angle.h
-  include/core/state_block/local_parametrization_base.h
-  include/core/state_block/local_parametrization_homogeneous.h
-  include/core/state_block/local_parametrization_quaternion.h
-  include/core/state_block/state_angle.h
-  include/core/state_block/state_block.h
-  include/core/state_block/state_composite.h
-  include/core/state_block/state_homogeneous_3d.h
-  include/core/state_block/state_quaternion.h
+  include/${PROJECT_NAME}/state_block/factory_state_block.h
+  include/${PROJECT_NAME}/state_block/has_state_blocks.h
+  include/${PROJECT_NAME}/state_block/local_parametrization_angle.h
+  include/${PROJECT_NAME}/state_block/local_parametrization_base.h
+  include/${PROJECT_NAME}/state_block/local_parametrization_homogeneous.h
+  include/${PROJECT_NAME}/state_block/local_parametrization_quaternion.h
+  include/${PROJECT_NAME}/state_block/state_angle.h
+  include/${PROJECT_NAME}/state_block/state_block.h
+  include/${PROJECT_NAME}/state_block/state_block_derived.h
+  include/${PROJECT_NAME}/state_block/state_composite.h
+  include/${PROJECT_NAME}/state_block/state_homogeneous_3d.h
+  include/${PROJECT_NAME}/state_block/state_quaternion.h
   )
 SET(HDRS_TRAJECTORY
-  include/core/trajectory/trajectory_base.h
+  include/${PROJECT_NAME}/trajectory/trajectory_base.h
   )
 SET(HDRS_TREE_MANAGER
-  include/core/tree_manager/factory_tree_manager.h
-  include/core/tree_manager/tree_manager_base.h
-  include/core/tree_manager/tree_manager_sliding_window.h
-  include/core/tree_manager/tree_manager_sliding_window_dual_rate.h
+  include/${PROJECT_NAME}/tree_manager/factory_tree_manager.h
+  include/${PROJECT_NAME}/tree_manager/tree_manager_base.h
+  include/${PROJECT_NAME}/tree_manager/tree_manager_sliding_window.h
+  include/${PROJECT_NAME}/tree_manager/tree_manager_sliding_window_dual_rate.h
   )
 SET(HDRS_UTILS
-  include/core/utils/check_log.h
-  include/core/utils/converter.h
-  include/core/utils/eigen_assert.h
-  include/core/utils/graph_search.h
-  include/core/utils/loader.h
-  include/core/utils/logging.h
-  include/core/utils/params_server.h
-  include/core/utils/singleton.h
-  include/core/utils/utils_gtest.h
-  include/core/utils/converter_utils.h
+  include/${PROJECT_NAME}/utils/check_log.h
+  include/${PROJECT_NAME}/utils/converter.h
+  include/${PROJECT_NAME}/utils/eigen_assert.h
+  include/${PROJECT_NAME}/utils/graph_search.h
+  include/${PROJECT_NAME}/utils/loader.h
+  include/${PROJECT_NAME}/utils/logging.h
+  include/${PROJECT_NAME}/utils/params_server.h
+  include/${PROJECT_NAME}/utils/singleton.h
+  include/${PROJECT_NAME}/utils/utils_gtest.h
+  include/${PROJECT_NAME}/utils/converter_utils.h
   )
 SET(HDRS_YAML
-  include/core/yaml/parser_yaml.h
-  include/core/yaml/yaml_conversion.h
+  include/${PROJECT_NAME}/yaml/parser_yaml.h
+  include/${PROJECT_NAME}/yaml/yaml_conversion.h
   )
   
 # ============ SOURCES ============ 
 SET(SRCS_CAPTURE
   src/capture/capture_base.cpp
+  src/capture/capture_diff_drive.cpp
+  src/capture/capture_landmarks_external.cpp
   src/capture/capture_motion.cpp
   src/capture/capture_odom_2d.cpp
   src/capture/capture_odom_3d.cpp
   src/capture/capture_pose.cpp
   src/capture/capture_void.cpp
-  src/capture/capture_diff_drive.cpp
   )
 SET(SRCS_COMMON
   src/common/node_base.cpp
@@ -299,6 +298,7 @@ SET(SRCS_PROCESSOR
   src/processor/processor_base.cpp
   src/processor/processor_diff_drive.cpp
   src/processor/processor_fixed_wing_model.cpp
+  src/processor/processor_landmark_external.cpp
   src/processor/processor_loop_closure.cpp
   src/processor/processor_motion.cpp
   src/processor/processor_odom_2d.cpp
@@ -326,6 +326,7 @@ SET(SRCS_STATE_BLOCK
   src/state_block/local_parametrization_homogeneous.cpp
   src/state_block/local_parametrization_quaternion.cpp
   src/state_block/state_block.cpp
+  src/state_block/state_block_derived.cpp
   src/state_block/state_composite.cpp
   )
 SET(SRCS_TRAJECTORY
@@ -354,13 +355,13 @@ SET(SRCS_YAML
 IF (Ceres_FOUND)
     SET(HDRS_CERES_WRAPPER
       #ceres_wrapper/qr_manager.h
-      include/core/ceres_wrapper/cost_function_wrapper.h
-      include/core/ceres_wrapper/create_numeric_diff_cost_function.h
-      include/core/ceres_wrapper/local_parametrization_wrapper.h
-      include/core/ceres_wrapper/iteration_update_callback.h
-      include/core/ceres_wrapper/solver_ceres.h
-      include/core/solver/solver_manager.h
-      include/core/solver_suitesparse/sparse_utils.h
+      include/${PROJECT_NAME}/ceres_wrapper/cost_function_wrapper.h
+      include/${PROJECT_NAME}/ceres_wrapper/create_numeric_diff_cost_function.h
+      include/${PROJECT_NAME}/ceres_wrapper/local_parametrization_wrapper.h
+      include/${PROJECT_NAME}/ceres_wrapper/iteration_update_callback.h
+      include/${PROJECT_NAME}/ceres_wrapper/solver_ceres.h
+      include/${PROJECT_NAME}/solver/solver_manager.h
+      include/${PROJECT_NAME}/solver_suitesparse/sparse_utils.h
       )
     SET(SRCS_CERES_WRAPPER
       #ceres_wrapper/qr_manager.cpp
@@ -413,6 +414,7 @@ endif()
 TARGET_LINK_LIBRARIES(${PLUGIN_NAME} PUBLIC ${CMAKE_THREAD_LIBS_INIT} dl)
 TARGET_LINK_LIBRARIES(${PLUGIN_NAME} PUBLIC yaml-cpp)
 TARGET_LINK_LIBRARIES(${PLUGIN_NAME} PUBLIC Eigen3::Eigen)
+TARGET_LINK_LIBRARIES(${PLUGIN_NAME} PUBLIC spdlog::spdlog)
 IF (Ceres_FOUND)
     TARGET_LINK_LIBRARIES(${PLUGIN_NAME} PUBLIC ceres)
 ENDIF(Ceres_FOUND)
@@ -457,9 +459,9 @@ install(
   ${LIB_INSTALL_DIR}/${PLUGIN_NAME}/cmake
 )
 
-# Specifies include directories to use when compiling the plugin target
-# This way, include_directories does not need to be called in plugins depending on this one
-target_include_directories(${PLUGIN_NAME} INTERFACE
+target_include_directories(${PLUGIN_NAME} PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/conf>
   $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>
 )
 
@@ -493,7 +495,7 @@ INSTALL(FILES ${HDRS_SENSOR}
 INSTALL(FILES ${HDRS_SOLVER}
   DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/solver)
 #INSTALL(FILES ${HDRS_SOLVER_SUITESPARSE}
-#  DESTINATION ${INCLUDE_INSTALL_DIR}/core/solver_suitesparse)
+#  DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/solver_suitesparse)
 INSTALL(FILES ${HDRS_STATE_BLOCK}
   DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/state_block)
 INSTALL(FILES ${HDRS_TRAJECTORY}
@@ -505,12 +507,9 @@ INSTALL(FILES ${HDRS_UTILS}
 INSTALL(FILES ${HDRS_YAML}
   DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/yaml)
 
-INSTALL(FILES "${WOLF_CONFIG_DIR}/config.h"
+INSTALL(FILES ${WOLF_CONFIG_DIR}/config.h
   DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/internal)
 
-
-INSTALL(DIRECTORY ${SPDLOG_INCLUDE_DIRS} DESTINATION "include/iri-algorithms/")
-
 export(PACKAGE ${PLUGIN_NAME})
 
 FIND_PACKAGE(Doxygen MODULE)
diff --git a/Testing/Temporary/.gitignore b/Testing/Temporary/.gitignore
deleted file mode 100644
index 6ccdef7c09c93207bc46508bb9fd8c873e94e6c9..0000000000000000000000000000000000000000
--- a/Testing/Temporary/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/CTestCostData.txt
-/LastTest.log
diff --git a/cmake_modules/wolfcoreConfig.cmake.in b/cmake_modules/wolfcoreConfig.cmake.in
index 735adef5984b0989b98947873a15e22a910172b4..749bc679d1abdfe403c3bfd3bb4115d5c2c19600 100644
--- a/cmake_modules/wolfcoreConfig.cmake.in
+++ b/cmake_modules/wolfcoreConfig.cmake.in
@@ -8,6 +8,7 @@ FIND_DEPENDENCY(Threads REQUIRED)
 FIND_DEPENDENCY(Ceres REQUIRED)
 FIND_DEPENDENCY(Eigen3 3.3 REQUIRED)
 FIND_DEPENDENCY(yaml-cpp REQUIRED)
+FIND_DEPENDENCY(spdlog REQUIRED)
 
 include("${CMAKE_CURRENT_LIST_DIR}/@PLUGIN_NAME@Targets.cmake")
 
diff --git a/demos/demo_analytic_autodiff_factor.cpp b/demos/demo_analytic_autodiff_factor.cpp
index 29a27f5d9c1662a6e6ab7bd4c9d7c02515476c4e..707bb45b02d2880cf461bd161d3f3137f9897737 100644
--- a/demos/demo_analytic_autodiff_factor.cpp
+++ b/demos/demo_analytic_autodiff_factor.cpp
@@ -80,7 +80,7 @@ int main(int argc, char** argv)
     // Wolf problem
     ProblemPtr wolf_problem_autodiff = new Problem(FRM_PO_2d);
     ProblemPtr wolf_problem_analytic = new Problem(FRM_PO_2d);
-    SensorBasePtr sensor = new SensorBase("ODOM 2d", std::make_shared<StateBlock>(Eigen::VectorXd::Zero(2)), std::make_shared<StateBlock>(Eigen::VectorXd::Zero(1)), std::make_shared<StateBlock>(Eigen::VectorXd::Zero(2)), 2);
+    SensorBasePtr sensor = new SensorBase("ODOM 2d", std::make_shared<StatePoint2d>(Eigen::VectorXd::Zero(2)), std::make_shared<StateAngle>(0), std::make_shared<StateParams2>(Eigen::VectorXd::Zero(2)), 2);
 
     // Ceres wrapper
     SolverCeres* ceres_manager_autodiff = new SolverCeres(wolf_problem_autodiff, ceres_options);
@@ -147,8 +147,8 @@ int main(int argc, char** argv)
                     bNum.clear();
 
                     // add frame to problem
-                    FrameBasePtr vertex_frame_ptr_autodiff = new FrameBase(TimeStamp(0), std::make_shared<StateBlock>(vertex_pose.head(2)), std::make_shared<StateBlock>(vertex_pose.tail(1)));
-                    FrameBasePtr vertex_frame_ptr_analytic = new FrameBase(TimeStamp(0), std::make_shared<StateBlock>(vertex_pose.head(2)), std::make_shared<StateBlock>(vertex_pose.tail(1)));
+                    FrameBasePtr vertex_frame_ptr_autodiff = new FrameBase(TimeStamp(0), std::make_shared<StatePoint2d>(vertex_pose.head(2)), std::make_shared<StateAngle>(vertex_pose.tail(1)));
+                    FrameBasePtr vertex_frame_ptr_analytic = new FrameBase(TimeStamp(0), std::make_shared<StatePoint2d>(vertex_pose.head(2)), std::make_shared<StateAngle>(vertex_pose.tail(1)));
                     wolf_problem_autodiff->getTrajectory()->addFrame(vertex_frame_ptr_autodiff);
                     wolf_problem_analytic->getTrajectory()->addFrame(vertex_frame_ptr_analytic);
                     // store
diff --git a/demos/demo_wolf_imported_graph.cpp b/demos/demo_wolf_imported_graph.cpp
index bc8982ca2ce696719aab35105f059639d6c72f23..ed612ffcad7f3e1299f8e02d434c61969705b718 100644
--- a/demos/demo_wolf_imported_graph.cpp
+++ b/demos/demo_wolf_imported_graph.cpp
@@ -84,7 +84,7 @@ int main(int argc, char** argv)
     // Wolf problem
     ProblemPtr wolf_problem_full = new Problem(FRM_PO_2d);
     ProblemPtr wolf_problem_prun = new Problem(FRM_PO_2d);
-    SensorBasePtr sensor = new SensorBase("ODOM 2d", std::make_shared<StateBlock>(Eigen::VectorXd::Zero(2)), std::make_shared<StateBlock>(Eigen::VectorXd::Zero(1)), std::make_shared<StateBlock>(Eigen::VectorXd::Zero(2)), 2);
+    SensorBasePtr sensor = new SensorBase("ODOM 2d", std::make_shared<StatePoint2d>(Eigen::VectorXd::Zero(2)), std::make_shared<StateAngle>(Eigen::VectorXd::Zero(1)), std::make_shared<StateParams2>(Eigen::VectorXd::Zero(2)), 2);
 
     Eigen::SparseMatrix<double> Lambda(0,0);
 
@@ -151,8 +151,8 @@ int main(int argc, char** argv)
                     bNum.clear();
 
                     // add frame to problem
-                    FrameBasePtr vertex_frame_ptr_full = new FrameBase(TimeStamp(0), std::make_shared<StateBlock>(vertex_pose.head(2)), std::make_shared<StateBlock>(vertex_pose.tail(1)));
-                    FrameBasePtr vertex_frame_ptr_prun = new FrameBase(TimeStamp(0), std::make_shared<StateBlock>(vertex_pose.head(2)), std::make_shared<StateBlock>(vertex_pose.tail(1)));
+                    FrameBasePtr vertex_frame_ptr_full = new FrameBase(TimeStamp(0), std::make_shared<StatePoint2d>(vertex_pose.head(2)), std::make_shared<StateAngle>(vertex_pose.tail(1)));
+                    FrameBasePtr vertex_frame_ptr_prun = new FrameBase(TimeStamp(0), std::make_shared<StatePoint2d>(vertex_pose.head(2)), std::make_shared<StateAngle>(vertex_pose.tail(1)));
                     wolf_problem_full->getTrajectory()->addFrame(vertex_frame_ptr_full);
                     wolf_problem_prun->getTrajectory()->addFrame(vertex_frame_ptr_prun);
                     // store
diff --git a/demos/hello_wolf/landmark_point_2d.cpp b/demos/hello_wolf/landmark_point_2d.cpp
index c2c11187eedd016c49116f84ee14141067d7fa47..2eee6e3b8d43638a010b7959d700b7d1b4fa05a0 100644
--- a/demos/hello_wolf/landmark_point_2d.cpp
+++ b/demos/hello_wolf/landmark_point_2d.cpp
@@ -27,12 +27,13 @@
  */
 
 #include "landmark_point_2d.h"
+#include "core/state_block/state_block_derived.h"
 
 namespace wolf
 {
 
 LandmarkPoint2d::LandmarkPoint2d(int _id, const Eigen::Vector2d& _xy) :
-        LandmarkBase("LandmarkPoint2d", std::make_shared<StateBlock>(_xy))
+        LandmarkBase("LandmarkPoint2d", std::make_shared<StatePoint2d>(_xy))
 {
     setId(_id);
 }
diff --git a/demos/hello_wolf/sensor_range_bearing.cpp b/demos/hello_wolf/sensor_range_bearing.cpp
index 7073082e34be71b45e60b7671211e1617432a124..affd707e29a7eacadee870b584ff586920049b17 100644
--- a/demos/hello_wolf/sensor_range_bearing.cpp
+++ b/demos/hello_wolf/sensor_range_bearing.cpp
@@ -28,6 +28,7 @@
 
 #include "sensor_range_bearing.h"
 #include "core/state_block/state_angle.h"
+#include "core/state_block/state_block_derived.h"
 
 namespace wolf
 {
@@ -36,7 +37,7 @@ WOLF_PTR_TYPEDEFS(SensorRangeBearing);
 
 SensorRangeBearing::SensorRangeBearing(const Eigen::VectorXd& _extrinsics, const Eigen::Vector2d& _noise_std) :
         SensorBase("SensorRangeBearing",
-                   std::make_shared<StateBlock>(_extrinsics.head<2>(), true),
+                   std::make_shared<StatePoint2d>(_extrinsics.head<2>(), true),
                    std::make_shared<StateAngle>(_extrinsics(2), true),
                    nullptr,
                    _noise_std)
diff --git a/demos/solver/test_SPQR.cpp b/demos/solver/test_SPQR.cpp
index b5f159c731741dabe4ed00bbe8eaa0e7968be7f3..56df88821b3441b9e1526f713741ffbfc49acedc 100644
--- a/demos/solver/test_SPQR.cpp
+++ b/demos/solver/test_SPQR.cpp
@@ -27,8 +27,8 @@
  */
 
 #include <iostream>
-#include <eigen3/Eigen/SPQRSupport>
-#include <eigen3/Eigen/CholmodSupport>
+#include <Eigen/SPQRSupport>
+#include <Eigen/CholmodSupport>
 #include "SuiteSparseQR.hpp"
 
 using namespace Eigen;
diff --git a/demos/solver/test_ccolamd.cpp b/demos/solver/test_ccolamd.cpp
index 98f496f26b5de691a38384d42b3349d62fc8b20e..126fa7f869f203188a3c589bf68fced8f4e593ff 100644
--- a/demos/solver/test_ccolamd.cpp
+++ b/demos/solver/test_ccolamd.cpp
@@ -43,9 +43,9 @@
 #include "solver/ccolamd_ordering.h"
 
 // eigen includes
-#include <eigen3/Eigen/OrderingMethods>
-#include <eigen3/Eigen/CholmodSupport>
-#include <eigen3/Eigen/SparseLU>
+#include <Eigen/OrderingMethods>
+#include <Eigen/CholmodSupport>
+#include <Eigen/SparseLU>
 
 using namespace Eigen;
 using namespace wolf;
diff --git a/demos/solver/test_ccolamd_blocks.cpp b/demos/solver/test_ccolamd_blocks.cpp
index 14bb174286235f063a375cca4e28f1e4dfeca537..2d82633432d7620f108b8ef8532c4d08f0a17b9f 100644
--- a/demos/solver/test_ccolamd_blocks.cpp
+++ b/demos/solver/test_ccolamd_blocks.cpp
@@ -37,9 +37,9 @@
 #include <queue>
 
 // eigen includes
-#include <eigen3/Eigen/OrderingMethods>
-#include <eigen3/Eigen/CholmodSupport>
-#include <eigen3/Eigen/SparseLU>
+#include <Eigen/OrderingMethods>
+#include <Eigen/CholmodSupport>
+#include <Eigen/SparseLU>
 
 // ccolamd
 #include "solver/ccolamd_ordering.h"
diff --git a/demos/solver/test_iQR.cpp b/demos/solver/test_iQR.cpp
index 53a37cef1a62ec1ffb281a6fdbd741fc73cb59f8..ba4ae194a00c6e9c8a78affc1a54fc02b1f52bb1 100644
--- a/demos/solver/test_iQR.cpp
+++ b/demos/solver/test_iQR.cpp
@@ -44,9 +44,9 @@
 #include <queue>
 
 // eigen includes
-#include <eigen3/Eigen/OrderingMethods>
-#include <eigen3/Eigen/SparseQR>
-#include <eigen3/Eigen/SPQRSupport>
+#include <Eigen/OrderingMethods>
+#include <Eigen/SparseQR>
+#include <Eigen/SPQRSupport>
 
 // ccolamd
 #include "solver/ccolamd_ordering.h"
diff --git a/demos/solver/test_iQR_wolf.cpp b/demos/solver/test_iQR_wolf.cpp
index a8df237237669b54aa837d32f35bf8c156fa33eb..4ffc4a0bffada8742a20dd9d00b220c7544be156 100644
--- a/demos/solver/test_iQR_wolf.cpp
+++ b/demos/solver/test_iQR_wolf.cpp
@@ -38,8 +38,8 @@
 #include <queue>
 
 // eigen includes
-#include <eigen3/Eigen/OrderingMethods>
-#include <eigen3/Eigen/SparseQR>
+#include <Eigen/OrderingMethods>
+#include <Eigen/SparseQR>
 #include <Eigen/SPQRSupport>
 
 // ccolamd
diff --git a/demos/solver/test_iQR_wolf2.cpp b/demos/solver/test_iQR_wolf2.cpp
index e634dac6f2de62d79296525dd6ca2631b4041108..d85e2cdebeacd0bbc1d39220cacd562f018e291b 100644
--- a/demos/solver/test_iQR_wolf2.cpp
+++ b/demos/solver/test_iQR_wolf2.cpp
@@ -181,10 +181,10 @@ int main(int argc, char *argv[])
     Eigen::Vector4d laser_1_pose, laser_2_pose; //xyz + theta
     laser_1_pose << 1.2, 0, 0, 0; //laser 1
     laser_2_pose << -1.2, 0, 0, M_PI; //laser 2
-    SensorOdom2D odom_sensor(std::make_shared<StateBlock>(odom_pose.head(2)), std::make_shared<StateBlock>(odom_pose.tail(1)), odom_std_factor, odom_std_factor);
-    SensorGPSFix gps_sensor(std::make_shared<StateBlock>(gps_pose.head(2)), std::make_shared<StateBlock>(gps_pose.tail(1)), gps_std);
-    SensorLaser2D laser_1_sensor(std::make_shared<StateBlock>(laser_1_pose.head(2)), std::make_shared<StateBlock>(laser_1_pose.tail(1)), laserscanutils::LaserScanParams({M_PI/2,-M_PI/2, -M_PI/720,0.01,0.2,100,0.01,0.01}));
-    SensorLaser2D laser_2_sensor(std::make_shared<StateBlock>(laser_2_pose.head(2)), std::make_shared<StateBlock>(laser_2_pose.tail(1)), laserscanutils::LaserScanParams({M_PI/2,-M_PI/2, -M_PI/720,0.01,0.2,100,0.01,0.01}));
+    SensorOdom2D odom_sensor(std::make_shared<StatePoint2d>(odom_pose.head(2)), std::make_shared<StateAngle>(odom_pose.tail(1)), odom_std_factor, odom_std_factor);
+    SensorGPSFix gps_sensor(std::make_shared<StatePoint2d>(gps_pose.head(2)), std::make_shared<StateAngle>(gps_pose.tail(1)), gps_std);
+    SensorLaser2D laser_1_sensor(std::make_shared<StatePoint2d>(laser_1_pose.head(2)), std::make_shared<StateAngle>(laser_1_pose.tail(1)), laserscanutils::LaserScanParams({M_PI/2,-M_PI/2, -M_PI/720,0.01,0.2,100,0.01,0.01}));
+    SensorLaser2D laser_2_sensor(std::make_shared<StatePoint2d>(laser_2_pose.head(2)), std::make_shared<StateAngle>(laser_2_pose.tail(1)), laserscanutils::LaserScanParams({M_PI/2,-M_PI/2, -M_PI/720,0.01,0.2,100,0.01,0.01}));
 
     // Initial pose
     pose_odom << 2, 8, 0;
diff --git a/demos/solver/test_incremental_ccolamd_blocks.cpp b/demos/solver/test_incremental_ccolamd_blocks.cpp
index 740407a9a560f0efdbdb8267ee7925c22fbf12bf..f0eabf66cde8d920265f3af6a5b4e8a994fa5858 100644
--- a/demos/solver/test_incremental_ccolamd_blocks.cpp
+++ b/demos/solver/test_incremental_ccolamd_blocks.cpp
@@ -37,9 +37,9 @@
 #include <queue>
 
 // eigen includes
-#include <eigen3/Eigen/OrderingMethods>
-#include <eigen3/Eigen/CholmodSupport>
-#include <eigen3/Eigen/SparseLU>
+#include <Eigen/OrderingMethods>
+#include <Eigen/CholmodSupport>
+#include <Eigen/SparseLU>
 
 // ccolamd
 #include "solver/ccolamd_ordering.h"
diff --git a/demos/solver/test_permutations.cpp b/demos/solver/test_permutations.cpp
index 0765d8ae77996a72b027a62380804243aa0aedfa..6724ea83f167bbacbfceed245a01b7c5cfdeea60 100644
--- a/demos/solver/test_permutations.cpp
+++ b/demos/solver/test_permutations.cpp
@@ -37,7 +37,7 @@
 #include <queue>
 
 // eigen includes
-#include <eigen3/Eigen/OrderingMethods>
+#include <Eigen/OrderingMethods>
 
 using namespace Eigen;
 
diff --git a/doc/doxygen.conf b/doc/doxygen.conf
index f0009526c6f9fcb8a61ae84916690a62190e32c6..29dd647045c69da17bfd41a11bd4d047216b2583 100644
--- a/doc/doxygen.conf
+++ b/doc/doxygen.conf
@@ -757,7 +757,8 @@ WARN_LOGFILE           =
 
 INPUT                  = ../doc/main.dox \
                          ../src \
-                         ../include/core
+                         ../include/core \
+                         conf/core/internal
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/include/core/capture/capture_landmarks_external.h b/include/core/capture/capture_landmarks_external.h
new file mode 100644
index 0000000000000000000000000000000000000000..27af193e1f73de46ec3f934c0f89b0ff0f2a9750
--- /dev/null
+++ b/include/core/capture/capture_landmarks_external.h
@@ -0,0 +1,61 @@
+//--------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--------
+#pragma once
+
+//Wolf includes
+#include "core/capture/capture_base.h"
+
+namespace wolf {
+
+struct LandmarkDetection
+{
+    int id;                      // id of landmark
+    Eigen::VectorXd measure;     // either pose or position
+    Eigen::MatrixXd covariance;  // covariance of the measure
+    double quality;              // [0, 1] quality of the detection
+};
+
+WOLF_PTR_TYPEDEFS(CaptureLandmarksExternal);
+
+//class CaptureLandmarksExternal
+class CaptureLandmarksExternal : public CaptureBase
+{
+    protected:
+        std::vector<LandmarkDetection> detections_;
+
+    public:
+
+        CaptureLandmarksExternal(const TimeStamp& _ts, 
+                                 SensorBasePtr _sensor_ptr, 
+                                 const std::vector<int>& _ids = {},
+                                 const std::vector<Eigen::VectorXd>& _detections = {}, 
+                                 const std::vector<Eigen::MatrixXd>& _covs = {}, 
+                                 const std::vector<double>& _qualities = {});
+
+        ~CaptureLandmarksExternal() override;
+
+        std::vector<LandmarkDetection> getDetections() const {return detections_;};
+
+        void addDetection(const int& _id, const Eigen::VectorXd& _detection, const Eigen::MatrixXd& _cov, const double& quality);
+};
+
+} //namespace wolf
\ No newline at end of file
diff --git a/include/core/ceres_wrapper/sparse_utils.h b/include/core/ceres_wrapper/sparse_utils.h
index e0d70f26f95c2d5dfd10131e48103586c52fd20d..132a4aba706160bb30e1f3099f3b546dbe2f4bda 100644
--- a/include/core/ceres_wrapper/sparse_utils.h
+++ b/include/core/ceres_wrapper/sparse_utils.h
@@ -30,7 +30,7 @@
 #define SPARSE_UTILS_H_
 
 // eigen includes
-//#include <eigen3/Eigen/Sparse>
+//#include <Eigen/Sparse>
 
 namespace wolf
 {
diff --git a/include/core/common/factory.h b/include/core/common/factory.h
index 790ec306644c12e607a4657859bedc8e360284aa..03be899855188f350530c1e721a68c275121e6b1 100644
--- a/include/core/common/factory.h
+++ b/include/core/common/factory.h
@@ -305,6 +305,7 @@ class Factory
     public:
         static bool registerCreator(const std::string& _type, CreatorCallback createFn);
         static bool unregisterCreator(const std::string& _type);
+        static bool isCreatorRegistered(const std::string& _type);
         static TypeBasePtr create(const std::string& _type, TypeInput... _input);
         std::string getClass() const;
         static void printAddress();
@@ -356,6 +357,12 @@ inline bool Factory<TypeBase, TypeInput...>::unregisterCreator(const std::string
     return get().callbacks_.erase(_type) == 1;
 }
 
+template<class TypeBase, typename... TypeInput>
+inline bool Factory<TypeBase, TypeInput...>::isCreatorRegistered(const std::string& _type)
+{
+    return get().callbacks_.count(_type) == 1;
+}
+
 template<class TypeBase, typename... TypeInput>
 inline typename Factory<TypeBase, TypeInput...>::TypeBasePtr Factory<TypeBase, TypeInput...>::create(const std::string& _type, TypeInput... _input)
 {
diff --git a/include/core/factor/factor_base.h b/include/core/factor/factor_base.h
index 1894148de2ea576614b14d6de81a4f138c4e037c..0ca91c9c6bb5db6577e268e9f159da0f09a89a4e 100644
--- a/include/core/factor/factor_base.h
+++ b/include/core/factor/factor_base.h
@@ -123,6 +123,31 @@ class FactorBase : public NodeBase, public std::enable_shared_from_this<FactorBa
             return topology_;
         }
 
+        std::string getTopologyString() const
+        {
+            switch (topology_)
+            {
+            case TOP_GEOM:
+                return "GEOM";
+                break;
+            case TOP_ABS:
+                return "ABS";
+                break;
+            case TOP_LMK:
+                return "LMK";
+                break;
+            case TOP_LOOP:
+                return "LOOP";
+                break;
+            case TOP_OTHER:
+                return "OTHER";
+                break;
+            default:
+                return "UNDEFINED";
+            }
+        }
+
+
         /** \brief Evaluate the factor given the input parameters and returning the residuals and jacobians
         **/
         virtual bool evaluate(double const* const* _parameters, double* _residuals, double** _jacobians) const = 0;
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 b0eea0f2b8f91f00a9a2bc8a7bda6ce1be66dc45..f0f70a5c15341c48f03e1b36ce86eb6e1e1415db 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,
@@ -132,31 +134,49 @@ inline bool FactorRelativePose2dWithExtrinsics::operator ()(const T* const _p_re
     Eigen::Map<const Eigen::Matrix<T,2,1> > p_ref(_p_ref);
     Eigen::Map<const Eigen::Matrix<T,2,1> > p_target(_p_target);
     Eigen::Map<const Eigen::Matrix<T,2,1> > p_sensor(_p_sensor);
-    T o_ref = _o_ref[0];
-    T o_target = _o_target[0];
-    T o_sensor = _o_sensor[0];
+    const T& o_ref = _o_ref[0];
+    const T& o_target = _o_target[0];
+    const T& o_sensor = _o_sensor[0];
 
     Eigen::Matrix<T, 3, 1> expected_measurement;
     Eigen::Matrix<T, 3, 1> er; // error
 
-    // Expected measurement
-    // r1_p_r1_s1 = ps
-    // r2_p_r2_s2 = ps
-    // r1_R_s1 = R(os)
-    // r2_R_s2 = R(os)
-    // w_R_r1 = R(o1)
-    // w_R_r2 = R(o2)
-    // ----------------------------------------
-
-    // s1_p_s1_s2 = s1_R_r1*r1_p_s1_r1 +
-    //              s1_R_r1*r1_R_w*w_p_r1_w +
-    //              s1_R_r1*r1_R_w*w_p_w_r2 +
-    //              s1_R_r1*r1_R_w*w_R_r2*r2_p_r2_s2
-
-    // s1_p_s1_s2 = s1_R_r1*(r1_p_s1_r1 + r1_R_w*(w_p_r1_w + w_p_w_r2 + w_R_r2*r2_p_r2_s2))
-
-    expected_measurement.head(2) = Eigen::Rotation2D<T>(-o_sensor)*(-p_sensor + Eigen::Rotation2D<T>(-o_ref)*(-p_ref + p_target + Eigen::Rotation2D<T>(o_target)*p_sensor));
-    expected_measurement(2) = o_target - o_ref;
+    // FRAME CASE
+    if (not getFrameOtherList().empty())
+    {
+        // Expected measurement
+        // r1_p_s1 = p_sensor
+        // r2_p_s2 = p_sensor
+        // r1_R_s1 = R(o_sensor)
+        // r2_R_s2 = R(o_sensor)
+        // w_p_r1 = p_ref
+        // w_p_r2 = p_target
+        // w_R_r1 = R(o_ref)
+        // w_R_r2 = R(o_target)
+        // ----------------------------------------
+        // s1_p_s2 = s1_R_r1*(r1_R_w*(w_p_r2 + w_R_r2*r2_p_s2 - 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 + Eigen::Rotation2D<T>(o_target)*p_sensor));
+        expected_measurement(2) = o_target - o_ref;
+    }
+    // LANDMARK CASE
+    else if (not getLandmarkOtherList().empty())
+    {
+        // Expected measurement
+        // r1_p_s1 = p_sensor
+        // r2_p_s2 = p_sensor
+        // r1_R_s1 = R(o_sensor)
+        // r2_R_s2 = R(o_sensor)
+        // w_p_r1 = p_ref
+        // w_p_r2 = p_target
+        // w_R_r1 = R(o_ref)
+        // 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 - o_sensor;
+    }
 
     // Error
     er = expected_measurement - getMeasurement().cast<T>();
@@ -186,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..4ea300d5c13d35aed9ff1c60ba57c822cd4386c6 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,24 +42,31 @@ 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;
 
-        Eigen::VectorXd expectation() const;
+        Eigen::Vector7d expectation() const;
 
     private:
         template<typename T>
@@ -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) << " "
@@ -163,34 +189,38 @@ inline bool FactorRelativePose3d::expectation(const T* const _p_current,
     return true;
 }
 
-inline Eigen::VectorXd FactorRelativePose3d::expectation() const
+inline Eigen::Vector7d FactorRelativePose3d::expectation() const
 {
-    Eigen::VectorXd exp(7);
+    Eigen::Vector7d exp;
     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: " << target_pos.transpose() << std::endl;
+//    std::cout << "target_ori: " << target_ori.coeffs().transpose() << std::endl;
+//    std::cout << "ref_pos: " << ref_pos.transpose() << std::endl;
+//    std::cout << "ref_ori: " << ref_ori.coeffs().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 +262,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 19245f6bcf630f737e4a01aa78b4a11030d8264a..66468bbcafb2b4480df5077ca6b2a16d6ff61839 100644
--- a/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
+++ b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
@@ -58,7 +58,7 @@ class FactorRelativePose3dWithExtrinsics : public FactorAutodiff<FactorRelativeP
 
         /** \brief Class Destructor
          */
-        ~FactorRelativePose3dWithExtrinsics() override;
+        ~FactorRelativePose3dWithExtrinsics() override = default;
  
         /** brief : compute the residual from the state blocks being iterated by the solver.
          **/
@@ -116,6 +116,8 @@ inline FactorRelativePose3dWithExtrinsics::FactorRelativePose3dWithExtrinsics(co
                    _feature_ptr->getCapture()->getSensorP(),
                    _feature_ptr->getCapture()->getSensorO())
 {
+    assert(_feature_ptr->getCapture()->getSensorP() != nullptr && "No extrinsics found!");
+    assert(_feature_ptr->getCapture()->getSensorO() != nullptr && "No extrinsics found!");
 }
 
 inline FactorRelativePose3dWithExtrinsics::FactorRelativePose3dWithExtrinsics(const FeatureBasePtr& _feature_ptr,
@@ -143,45 +145,59 @@ inline FactorRelativePose3dWithExtrinsics::FactorRelativePose3dWithExtrinsics(co
 {
 }
 
-inline FactorRelativePose3dWithExtrinsics::~FactorRelativePose3dWithExtrinsics()
-{
-    //
-}
-
 template<typename T>
 inline bool FactorRelativePose3dWithExtrinsics::operator ()(const T* const _p_ref,
-                                                     const T* const _o_ref,
-                                                     const T* const _p_target,
-                                                     const T* const _o_target,
-                                                     const T* const _p_sensor,
-                                                     const T* const _o_sensor,
-                                                     T* _residuals) const
+                                                            const T* const _o_ref,
+                                                            const T* const _p_target,
+                                                            const T* const _o_target,
+                                                            const T* const _p_sensor,
+                                                            const T* const _o_sensor,
+                                                            T* _residuals) const
 {
     // Maps
-    Eigen::Map<const Eigen::Matrix<T,3,1>> p_r_c(_p_sensor);
-    Eigen::Map<const Eigen::Quaternion<T>> q_r_c(_o_sensor);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> p_r_s(_p_sensor);
+    Eigen::Map<const Eigen::Quaternion<T>> q_r_s(_o_sensor);
     Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_r(_p_ref);
     Eigen::Map<const Eigen::Quaternion<T>> q_w_r(_o_ref);
-    Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_l(_p_target);
-    Eigen::Map<const Eigen::Quaternion<T>> q_w_l(_o_target);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_t(_p_target);
+    Eigen::Map<const Eigen::Quaternion<T>> q_w_t(_o_target);
     Eigen::Map<Eigen::Matrix<T,6,1>> residuals(_residuals);
 
+    // WOLF_INFO("p_sensor: ", p_r_s.transpose());
+    // WOLF_INFO("o_sensor: ", q_r_s.coeffs().transpose());
+    // WOLF_INFO("p_ref:    ", p_w_r.transpose());
+    // WOLF_INFO("o_ref:    ", q_w_r.coeffs().transpose());
+    // WOLF_INFO("p_target: ", p_w_t.transpose());
+    // WOLF_INFO("o_target: ", q_w_t.coeffs().transpose());
+
     // Expected measurement
-    Eigen::Quaternion<T> q_c_w = (q_w_r * q_r_c).conjugate();
-    Eigen::Quaternion<T> q_c_l = q_c_w * q_w_l;
-    Eigen::Matrix<T,3,1> p_c_l = q_c_w * (-(p_w_r + q_w_r * p_r_c) + p_w_l);
+    Eigen::Quaternion<T> expected_q;
+    Eigen::Matrix<T,3,1> expected_p;
+    // FRAME CASE
+    if (not getFrameOtherList().empty())
+    {
+        expected_p = q_r_s.conjugate() * (q_w_r.conjugate() * (p_w_t + q_w_t * p_r_s - p_w_r) - p_r_s);
+        expected_q = q_r_s.conjugate() * q_w_r.conjugate() * q_w_t * q_r_s;
+    }
+    // LANDMARK CASE
+    else if (not getLandmarkOtherList().empty())
+    {
+        expected_p = q_r_s.conjugate() * (q_w_r.conjugate() * (p_w_t - p_w_r) - p_r_s);
+        expected_q = q_r_s.conjugate() * q_w_r.conjugate() * q_w_t;
+    }
 
     // Measurement
-    Eigen::Vector3d      p_c_l_meas(getMeasurement().head<3>());
+    Eigen::Vector3d      p_meas(getMeasurement().head<3>());
     Eigen::Quaterniond   q_c_l_meas(getMeasurement().data() + 3 );
     Eigen::Quaternion<T> q_l_c_meas = q_c_l_meas.conjugate().cast<T>();
     //Eigen::Matrix<T,3,1> p_l_c_meas = -q_l_c_meas * p_c_l_meas.cast<T>();
 
     // Error
     Eigen::Matrix<T, 6, 1> err;
-    err.head(3) = q_l_c_meas * (p_c_l_meas.cast<T>() - p_c_l);
-    //err.tail(3) = wolf::log_q(q_l_c_meas * q_c_l);  // true error function for which the covariance should be computed
-    err.tail(3) = T(2)*(q_l_c_meas * q_c_l).vec();  // 1rst order approximation of sin function ( 2*sin(aa/2) ~ aa )
+    // err.head(3) = q_l_c_meas * (p_meas.cast<T>() - expected_p);
+    err.head(3) = (p_meas - expected_p);
+    err.tail(3) = wolf::log_q(q_l_c_meas * expected_q);  // true error function for which the covariance should be computed
+    //err.tail(3) = T(2)*(q_l_c_meas * expected_q).vec();  // 1rst order approximation of sin function ( 2*sin(aa/2) ~ aa )
 
     // Residual
     residuals = getMeasurementSquareRootInformationUpper().cast<T>() * err;
@@ -192,15 +208,27 @@ inline bool FactorRelativePose3dWithExtrinsics::operator ()(const T* const _p_re
 inline Eigen::Vector6d FactorRelativePose3dWithExtrinsics::residual() const
 {
     Eigen::Vector6d res;
-    double * p_camera, * o_camera, * p_ref, * o_ref, * p_target, * o_target;
-    p_camera = getCapture()->getSensorP()->getState().data();
-    o_camera = getCapture()->getSensorO()->getState().data();
-    p_ref    = getCapture()->getFrame()->getP()->getState().data();
-    o_ref    = getCapture()->getFrame()->getO()->getState().data();
-    p_target = getLandmarkOther()->getP()->getState().data();
-    o_target = getLandmarkOther()->getO()->getState().data();
-
-    operator() (p_camera, o_camera, p_ref, o_ref, p_target, o_target, res.data());
+    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;
 }
diff --git a/include/core/factor/factor_relative_position_2d.h b/include/core/factor/factor_relative_position_2d.h
new file mode 100644
index 0000000000000000000000000000000000000000..8a2c7a0e52a7b58f8cda42586c56abff31793e79
--- /dev/null
+++ b/include/core/factor/factor_relative_position_2d.h
@@ -0,0 +1,258 @@
+//--------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--------
+#pragma once
+
+//Wolf includes
+#include "core/factor/factor_analytic.h"
+#include "core/landmark/landmark_base.h"
+#include "core/math/rotations.h"
+#include <Eigen/StdVector>
+
+namespace wolf {
+    
+WOLF_PTR_TYPEDEFS(FactorRelativePosition2d);
+    
+//class
+class FactorRelativePosition2d : public FactorAnalytic
+{
+    public:
+
+        /** \brief Constructor of category FAC_FRAME
+         **/
+        FactorRelativePosition2d(const FeatureBasePtr& _ftr_ptr,
+                                 const FrameBasePtr& _frame_other_ptr,
+                                 const ProcessorBasePtr& _processor_ptr,
+                                 bool _apply_loss_function,
+                                 const FactorTopology& _top,
+                                 FactorStatus _status = FAC_ACTIVE) :
+            FactorAnalytic("FactorRelativePosition2d",
+                           _top,
+                           _ftr_ptr,
+                           _frame_other_ptr,
+                           nullptr,
+                           nullptr,
+                           nullptr,
+                           _processor_ptr,
+                           _apply_loss_function,
+                           _status,
+                           _frame_other_ptr->getP(),
+                           _frame_other_ptr->getO(),
+                           _ftr_ptr->getFrame()->getP())
+        {
+            //
+        }
+
+        /** \brief Constructor of category FAC_FEATURE
+         **/
+        FactorRelativePosition2d(const FeatureBasePtr& _ftr_ptr,
+                                 const FeatureBasePtr& _ftr_other_ptr,
+                                 const ProcessorBasePtr& _processor_ptr,
+                                 bool _apply_loss_function,
+                                 const FactorTopology& _top,
+                                 FactorStatus _status = FAC_ACTIVE) :
+            FactorAnalytic("FactorRelativePosition2d",
+                           _top,
+                           _ftr_ptr,
+                           nullptr,
+                           nullptr,
+                           _ftr_other_ptr,
+                           nullptr,
+                           _processor_ptr,
+                           _apply_loss_function,
+                           _status,
+                           _ftr_ptr->getFrame()->getP(),
+                           _ftr_ptr->getFrame()->getO(),
+                           _ftr_other_ptr->getFrame()->getP() )
+        {
+            //
+        }
+
+        /** \brief Constructor of category FAC_LANDMARK
+         **/
+        FactorRelativePosition2d(const FeatureBasePtr& _ftr_ptr,
+                                 const LandmarkBasePtr& _landmark_other_ptr,
+                                 const ProcessorBasePtr& _processor_ptr,
+                                 bool _apply_loss_function,
+                                 const FactorTopology& _top,
+                                 FactorStatus _status = FAC_ACTIVE) :
+            FactorAnalytic("FactorRelativePosition2d",
+                           _top,
+                           _ftr_ptr,
+                           nullptr,
+                           nullptr,
+                           nullptr,
+                           _landmark_other_ptr,
+                           _processor_ptr,
+                           _apply_loss_function,
+                           _status,
+                           _ftr_ptr->getFrame()->getP(),
+                           _ftr_ptr->getFrame()->getO(),
+                           _landmark_other_ptr->getP())
+        {
+            //
+        }
+
+        ~FactorRelativePosition2d() override = default;
+
+        /** \brief Returns the factor residual size
+         **/
+        unsigned int getSize() const override
+        {
+            return 2;
+        }
+
+        /** \brief Returns the residual evaluated in the states provided
+         *
+         * Returns the residual evaluated in the states provided in a std::vector of mapped Eigen::VectorXd
+         **/
+        Eigen::VectorXd evaluateResiduals(
+                const std::vector<Eigen::Map<const Eigen::VectorXd> >& _st_vector) const override;
+
+        /** \brief Returns the jacobians evaluated in the states provided
+         *
+         * Returns the jacobians evaluated in the states provided in std::vector of mapped Eigen::VectorXd.
+         * IMPORTANT: only fill the jacobians of the state blocks specified in _compute_jacobian.
+         *
+         * \param _st_vector is a vector containing the mapped eigen vectors of all state blocks involved in the factor
+         * \param jacobians is an output vector of mapped eigen matrices that sould contain the jacobians w.r.t each state block
+         * \param _compute_jacobian is a vector that specifies whether the ith jacobian sould be computed or not
+         *
+         **/
+        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;
+        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;
+
+        /** \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
+         *
+         **/
+        void evaluatePureJacobians(std::vector<Eigen::MatrixXd>& jacobians) const override;
+
+};
+
+/// IMPLEMENTATION ///
+
+inline Eigen::VectorXd FactorRelativePosition2d::evaluateResiduals(
+        const std::vector<Eigen::Map<const Eigen::VectorXd> >& _st_vector) const
+{
+    Eigen::VectorXd residual(2);
+    Eigen::VectorXd expected_measurement(2);
+    // Expected measurement
+    Eigen::Matrix2d R = Eigen::Rotation2D<double>(-_st_vector[1](0)).matrix();
+    expected_measurement = R * (_st_vector[2] - _st_vector[0]); // rotar menys l'angle de primer (-_o1)
+    // Residual
+    residual = expected_measurement - getMeasurement();
+    residual = getMeasurementSquareRootInformationUpper() * residual;
+    return residual;
+}
+
+inline void FactorRelativePosition2d::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
+{
+    assert(jacobians.size() == 3);
+    assert(_st_vector.size() == 3);
+    assert(_compute_jacobian.size() == 3);
+
+    if (_compute_jacobian[0])
+    {
+        jacobians[0] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) <<
+                        -cos(_st_vector[1](0)), -sin(_st_vector[1](0)),
+                        sin(_st_vector[1](0)), -cos(_st_vector[1](0))).finished();
+    }
+    if (_compute_jacobian[1])
+    {
+        jacobians[1] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,1) <<
+                        -(_st_vector[2](0) - _st_vector[0](0)) * sin(_st_vector[1](0)) + 
+                         (_st_vector[2](1) - _st_vector[0](1)) * cos(_st_vector[1](0)),
+                        -(_st_vector[2](0) - _st_vector[0](0)) * cos(_st_vector[1](0)) - 
+                         (_st_vector[2](1) - _st_vector[0](1)) * sin(_st_vector[1](0))).finished();
+    }
+    if (_compute_jacobian[2])
+    {
+        jacobians[2] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) <<
+                        cos(_st_vector[1](0)), sin(_st_vector[1](0)),
+                        -sin(_st_vector[1](0)), cos(_st_vector[1](0))).finished();
+    }
+}
+
+inline void FactorRelativePosition2d::evaluateJacobians(const std::vector<Eigen::Map<const Eigen::VectorXd>>& _st_vector,
+                                                        std::vector<Eigen::MatrixXd>& jacobians,
+                                                        const std::vector<bool>& _compute_jacobian) const
+{
+    assert(jacobians.size() == 3);
+    assert(_st_vector.size() == 3);
+    assert(_compute_jacobian.size() == 3);
+
+    if (_compute_jacobian[0])
+    {
+        jacobians[0] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) <<
+                        -cos(_st_vector[1](0)), -sin(_st_vector[1](0)),
+                        sin(_st_vector[1](0)), -cos(_st_vector[1](0))).finished();
+    }
+    if (_compute_jacobian[1])
+    {
+        jacobians[1] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,1) <<
+                        -(_st_vector[2](0) - _st_vector[0](0)) * sin(_st_vector[1](0)) + 
+                         (_st_vector[2](1) - _st_vector[0](1)) * cos(_st_vector[1](0)),
+                        -(_st_vector[2](0) - _st_vector[0](0)) * cos(_st_vector[1](0)) - 
+                         (_st_vector[2](1) - _st_vector[0](1)) * sin(_st_vector[1](0))).finished();
+    }
+    if (_compute_jacobian[2])
+    {
+        jacobians[2] = getMeasurementSquareRootInformationUpper() * (Eigen::MatrixRowXd(2,2) <<
+                        cos(_st_vector[1](0)), sin(_st_vector[1](0)),
+                        -sin(_st_vector[1](0)), cos(_st_vector[1](0))).finished();
+    }
+}
+
+inline void FactorRelativePosition2d::evaluatePureJacobians(std::vector<Eigen::MatrixXd>& jacobians) const
+{
+    assert(jacobians.size() == 3);
+
+    jacobians[0] = (Eigen::MatrixXd(2,2) <<
+                    -cos(getStateBlockPtrVector()[1]->getState()(0)), 
+                    -sin(getStateBlockPtrVector()[1]->getState()(0)),
+                     sin(getStateBlockPtrVector()[1]->getState()(0)), 
+                    -cos(getStateBlockPtrVector()[1]->getState()(0))).finished();
+
+    jacobians[1] = (Eigen::MatrixXd(2,1) <<
+                    -(getStateBlockPtrVector()[2]->getState()(0)
+                        - getStateBlockPtrVector()[0]->getState()(0)) * sin(getStateBlockPtrVector()[1]->getState()(0))
+                        + (getStateBlockPtrVector()[2]->getState()(1)
+                        - getStateBlockPtrVector()[0]->getState()(1)) * cos(getStateBlockPtrVector()[1]->getState()(0)),
+                    -(getStateBlockPtrVector()[2]->getState()(0)
+                        - getStateBlockPtrVector()[0]->getState()(0)) * cos(getStateBlockPtrVector()[1]->getState()(0))
+                        - (getStateBlockPtrVector()[2]->getState()(1)
+                        - getStateBlockPtrVector()[0]->getState()(1)) * sin(getStateBlockPtrVector()[1]->getState()(0))).finished();
+
+    jacobians[2] = (Eigen::MatrixXd(2,2) <<
+                     cos(getStateBlockPtrVector()[1]->getState()(0)),
+                     sin(getStateBlockPtrVector()[1]->getState()(0)),
+                    -sin(getStateBlockPtrVector()[1]->getState()(0)),
+                     cos(getStateBlockPtrVector()[1]->getState()(0))).finished();
+}
+
+} // namespace wolf
\ No newline at end of file
diff --git a/include/core/factor/factor_relative_position_2d_with_extrinsics.h b/include/core/factor/factor_relative_position_2d_with_extrinsics.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd36867f4a93e08fe671f0491a07276ed00b6624
--- /dev/null
+++ b/include/core/factor/factor_relative_position_2d_with_extrinsics.h
@@ -0,0 +1,128 @@
+//--------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--------
+#pragma once
+
+//Wolf includes
+#include "core/factor/factor_autodiff.h"
+#include "core/frame/frame_base.h"
+#include "core/math/rotations.h"
+
+//#include "ceres/jet.h"
+
+namespace wolf {
+
+WOLF_PTR_TYPEDEFS(FactorRelativePosition2dWithExtrinsics);
+
+//class
+class FactorRelativePosition2dWithExtrinsics : public FactorAutodiff<FactorRelativePosition2dWithExtrinsics, 2, 2, 1, 2, 2, 1>
+{
+    public:
+
+        /** \brief Class constructor Frame-Landmark
+         */
+        FactorRelativePosition2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr,
+                                               const LandmarkBasePtr& _lmk_other_ptr,
+                                               const ProcessorBasePtr& _processor_ptr,
+                                               bool _apply_loss_function,
+                                               const FactorTopology& _top = TOP_LMK,
+                                               FactorStatus _status = FAC_ACTIVE);
+
+        ~FactorRelativePosition2dWithExtrinsics() override = default;
+
+        template<typename T>
+        bool operator ()(const T* const _p_ref,
+                         const T* const _o_ref,
+                         const T* const _p_target,
+                         const T* const _p_sensor,
+                         const T* const _o_sensor,
+                         T* _residuals) const;
+};
+
+inline FactorRelativePosition2dWithExtrinsics::FactorRelativePosition2dWithExtrinsics(const FeatureBasePtr& _ftr_ptr,
+                                                                                      const LandmarkBasePtr& _lmk_other_ptr,
+                                                                                      const ProcessorBasePtr& _processor_ptr,
+                                                                                      bool _apply_loss_function,
+                                                                                      const FactorTopology& _top,
+                                                                                      FactorStatus _status) :
+     FactorAutodiff("FactorRelativePosition2dWithExtrinsics",
+                    _top,
+                    _ftr_ptr,
+                    nullptr, nullptr, nullptr, _lmk_other_ptr,
+                    _processor_ptr,
+                    _apply_loss_function,
+                    _status,
+                    _ftr_ptr->getFrame()->getP(),
+                    _ftr_ptr->getFrame()->getO(),
+                    _lmk_other_ptr->getP(),
+                    _ftr_ptr->getCapture()->getSensorP(),
+                    _ftr_ptr->getCapture()->getSensorO())
+{
+    assert(_ftr_ptr->getCapture()->getSensorP() != nullptr && "No extrinsics found!");
+    assert(_ftr_ptr->getCapture()->getSensorO() != nullptr && "No extrinsics found!");
+    //
+}
+
+template<typename T>
+inline bool FactorRelativePosition2dWithExtrinsics::operator ()(const T* const _p_ref,
+                                                                const T* const _o_ref,
+                                                                const T* const _p_target,
+                                                                const T* const _p_sensor,
+                                                                const T* const _o_sensor,
+                                                                T* _residuals) const
+{
+    // MAPS
+    Eigen::Map<Eigen::Matrix<T,2,1> > res(_residuals);
+    Eigen::Map<const Eigen::Matrix<T,2,1> > p_ref(_p_ref);
+    Eigen::Map<const Eigen::Matrix<T,2,1> > p_target(_p_target);
+    Eigen::Map<const Eigen::Matrix<T,2,1> > p_sensor(_p_sensor);
+    T o_ref = _o_ref[0];
+    T o_sensor = _o_sensor[0];
+
+    // Expected measurement
+    Eigen::Matrix<T, 2, 1> expected_measurement = Eigen::Rotation2D<T>(-o_sensor) * 
+                                                  (-p_sensor + Eigen::Rotation2D<T>(-o_ref) * (p_target - p_ref));
+
+    // Residuals
+    res = getMeasurementSquareRootInformationUpper() * (expected_measurement - getMeasurement());
+
+    ////////////////////////////////////////////////////////
+    // print Jacobian. Uncomment this as you wish (remember to uncomment #include "ceres/jet.h" above):
+//    using ceres::Jet;
+//    Eigen::MatrixXs J(3,6);
+//    J.row(0) = ((Jet<Scalar, 6>)(er(0))).v;
+//    J.row(1) = ((Jet<Scalar, 6>)(er(1))).v;
+//    J.row(2) = ((Jet<Scalar, 6>)(er(2))).v;
+//    J.row(0) = ((Jet<Scalar, 6>)(res(0))).v;
+//    J.row(1) = ((Jet<Scalar, 6>)(res(1))).v;
+//    J.row(2) = ((Jet<Scalar, 6>)(res(2))).v;
+//    if (sizeof(er(0)) != sizeof(double))
+//    {
+//        std::cout << "FactorRelativePosition2dWithExtrinsics::Jacobian(c" << id() << ") = \n " << J << std::endl;
+//        std::cout << "FactorRelativePosition2dWithExtrinsics::Weighted Jacobian(c" << id() << ") = \n " << J << std::endl;
+//        std::cout << "FactorRelativePosition2dWithExtrinsics::Sqrt Info(c" << id() << ") = \n" << getMeasurementSquareRootInformationUpper() << std::endl;
+//    }
+    ////////////////////////////////////////////////////////
+
+    return true;
+}
+
+} // namespace wolf
\ No newline at end of file
diff --git a/include/core/factor/factor_relative_position_3d.h b/include/core/factor/factor_relative_position_3d.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d76dcb3406059f37218d0ace894ec1b3f9bc474
--- /dev/null
+++ b/include/core/factor/factor_relative_position_3d.h
@@ -0,0 +1,154 @@
+//--------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--------
+
+#pragma once
+
+#include "core/factor/factor_autodiff.h"
+
+namespace wolf
+{
+
+WOLF_PTR_TYPEDEFS(FactorRelativePosition3d);
+    
+//class
+class FactorRelativePosition3d : public FactorAutodiff<FactorRelativePosition3d,3,3,4,3>
+{
+    public:
+        FactorRelativePosition3d(const FeatureBasePtr& _ftr_current_ptr,
+                                 const FrameBasePtr& _frame_past_ptr,
+                                 const ProcessorBasePtr& _processor_ptr,
+                                 bool _apply_loss_function,
+                                 const FactorTopology& _top,
+                                 FactorStatus _status = FAC_ACTIVE);
+
+        FactorRelativePosition3d(const FeatureBasePtr& _ftr_current_ptr,
+                                 const LandmarkBasePtr& _landmark_ptr,
+                                 const ProcessorBasePtr& _processor_ptr,
+                                 bool _apply_loss_function,
+                                 const FactorTopology& _top,
+                                 FactorStatus _status = FAC_ACTIVE);
+
+        ~FactorRelativePosition3d() override = default;
+
+        template<typename T>
+        bool operator ()(const T* const _p_ref,
+                         const T* const _q_ref,
+                         const T* const _p_target,
+                         T* _residuals) const;
+
+    private:
+        template<typename T>
+        void printRes(const Eigen::Matrix<T, 3, 1>& r) const;
+};
+
+template<typename T>
+inline void FactorRelativePosition3d::printRes(const Eigen::Matrix<T, 3, 1>& r) const
+{
+    std::cout << "Jet residual = " << std::endl;
+    std::cout << r(0) << std::endl;
+    std::cout << r(1) << std::endl;
+    std::cout << r(2) << std::endl;
+}
+
+template<>
+inline void FactorRelativePosition3d::printRes (const  Eigen::Matrix<double,3,1> & r) const
+{
+    std::cout << "Scalar residual = " << std::endl;
+    std::cout << r.transpose() << std::endl;
+}
+
+inline FactorRelativePosition3d::FactorRelativePosition3d(const FeatureBasePtr& _ftr_current_ptr,
+                                                          const FrameBasePtr& _frame_past_ptr,
+                                                          const ProcessorBasePtr& _processor_ptr,
+                                                          bool _apply_loss_function,
+                                                          const FactorTopology& _top,
+                                                          FactorStatus _status) :
+        FactorAutodiff("FactorRelativePosition3d",     // type
+                       _top, // topology
+                       _ftr_current_ptr,   // feature
+                       _frame_past_ptr,    // frame other
+                       nullptr,            // capture other
+                       nullptr,            // feature other
+                       nullptr,            // landmark other
+                       _processor_ptr,     // processor
+                       _apply_loss_function,
+                       _status,
+                       _frame_past_ptr->getP(), // past frame P
+                       _frame_past_ptr->getO(), // past frame Q
+                       _ftr_current_ptr->getFrame()->getP()) // current frame P
+{
+    MatrixSizeCheck<3,1>::check(_ftr_current_ptr->getMeasurement());
+    MatrixSizeCheck<3,3>::check(_ftr_current_ptr->getMeasurementSquareRootInformationUpper());
+}
+
+inline FactorRelativePosition3d::FactorRelativePosition3d(const FeatureBasePtr& _ftr_current_ptr,
+                                                  const LandmarkBasePtr& _lmk_ptr,
+                                                  const ProcessorBasePtr& _processor_ptr,
+                                                  bool _apply_loss_function,
+                                                  const FactorTopology& _top,
+                                                  FactorStatus _status) :
+        FactorAutodiff("FactorRelativePosition3d",     // 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,
+                       _ftr_current_ptr->getFrame()->getP(), // frame P
+                       _ftr_current_ptr->getFrame()->getO(), // frame Q
+                       _lmk_ptr->getP())                     // landmark P
+{
+    MatrixSizeCheck<3,1>::check(_ftr_current_ptr->getMeasurement());
+    MatrixSizeCheck<3,3>::check(_ftr_current_ptr->getMeasurementSquareRootInformationUpper());
+}
+
+template<typename T>
+inline bool FactorRelativePosition3d::operator ()(const T* const _p_ref,
+                                                  const T* const _q_ref,
+                                                  const T* const _p_target,
+                                                  T* _residuals) const
+{
+    Eigen::Map<const Eigen::Matrix<T,3,1> > p_target(_p_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> > residuals(_residuals);
+
+//     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";
+//     std::cout << "p_target: " << p_target(0) << " "
+//                               << p_target(1) << " "
+//                               << p_target(2) << "\n";
+
+    residuals = getMeasurementSquareRootInformationUpper() * (getMeasurement() - (q_ref.conjugate() * (p_target - p_ref)));
+
+    return true;
+}
+
+} /* namespace wolf */
\ No newline at end of file
diff --git a/include/core/factor/factor_relative_position_3d_with_extrinsics.h b/include/core/factor/factor_relative_position_3d_with_extrinsics.h
new file mode 100644
index 0000000000000000000000000000000000000000..f892ac25a4adcb6c31f43f76b4fc3022a62b56ae
--- /dev/null
+++ b/include/core/factor/factor_relative_position_3d_with_extrinsics.h
@@ -0,0 +1,149 @@
+//--------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--------
+#pragma once
+
+//Wolf includes
+#include "core/factor/factor_autodiff.h"
+#include "core/frame/frame_base.h"
+#include "core/math/rotations.h"
+
+//#include "ceres/jet.h"
+
+namespace wolf {
+
+WOLF_PTR_TYPEDEFS(FactorRelativePosition3dWithExtrinsics);
+
+//class
+class FactorRelativePosition3dWithExtrinsics : public FactorAutodiff<FactorRelativePosition3dWithExtrinsics, 3, 3, 4, 3, 3, 4>
+{
+    public:
+
+        /** \brief Class constructor Frame-Landmark
+         */
+        FactorRelativePosition3dWithExtrinsics(const FeatureBasePtr& _ftr_ptr,
+                                               const LandmarkBasePtr& _lmk_other_ptr,
+                                               const ProcessorBasePtr& _processor_ptr,
+                                               bool _apply_loss_function,
+                                               const FactorTopology& _top = TOP_LMK,
+                                               FactorStatus _status = FAC_ACTIVE);
+
+        ~FactorRelativePosition3dWithExtrinsics() override = default;
+
+        template<typename T>
+        bool operator ()(const T* const _p_ref,
+                         const T* const _o_ref,
+                         const T* const _p_target,
+                         const T* const _p_sensor,
+                         const T* const _o_sensor,
+                         T* _residuals) const;
+
+        Eigen::Vector3d residual() const;
+        double cost() const;
+};
+
+inline FactorRelativePosition3dWithExtrinsics::FactorRelativePosition3dWithExtrinsics(const FeatureBasePtr& _ftr_ptr,
+                                                                                      const LandmarkBasePtr& _lmk_other_ptr,
+                                                                                      const ProcessorBasePtr& _processor_ptr,
+                                                                                      bool _apply_loss_function,
+                                                                                      const FactorTopology& _top,
+                                                                                      FactorStatus _status) :
+     FactorAutodiff("FactorRelativePosition3dWithExtrinsics",
+                    _top,
+                    _ftr_ptr,
+                    nullptr, nullptr, nullptr, _lmk_other_ptr,
+                    _processor_ptr,
+                    _apply_loss_function,
+                    _status,
+                    _ftr_ptr->getFrame()->getP(),
+                    _ftr_ptr->getFrame()->getO(),
+                    _lmk_other_ptr->getP(),
+                    _ftr_ptr->getCapture()->getSensorP(),
+                    _ftr_ptr->getCapture()->getSensorO())
+{
+    assert(_ftr_ptr->getCapture()->getSensorP() != nullptr && "No extrinsics found!");
+    assert(_ftr_ptr->getCapture()->getSensorO() != nullptr && "No extrinsics found!");
+}
+
+template<typename T>
+inline bool FactorRelativePosition3dWithExtrinsics::operator ()(const T* const _p_ref,
+                                                                const T* const _o_ref,
+                                                                const T* const _p_target,
+                                                                const T* const _p_sensor,
+                                                                const T* const _o_sensor,
+                                                                T* _residuals) const
+{
+    // Maps
+    Eigen::Map<const Eigen::Matrix<T,3,1>> p_r_s(_p_sensor);
+    Eigen::Map<const Eigen::Quaternion<T>> q_r_s(_o_sensor);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_r(_p_ref);
+    Eigen::Map<const Eigen::Quaternion<T>> q_w_r(_o_ref);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> p_w_t(_p_target);
+    Eigen::Map<Eigen::Matrix<T,3,1>> residuals(_residuals);
+
+    // WOLF_INFO("p_sensor: ", p_r_s.transpose());
+    // WOLF_INFO("o_sensor: ", q_r_s.coeffs().transpose());
+    // WOLF_INFO("p_ref:    ", p_w_r.transpose());
+    // WOLF_INFO("o_ref:    ", q_w_r.coeffs().transpose());
+    // WOLF_INFO("p_target: ", p_w_t.transpose());
+
+    // Expected measurement
+    Eigen::Matrix<T,3,1> expected_p;
+    expected_p = q_r_s.conjugate() * (q_w_r.conjugate() * (p_w_t - p_w_r) - p_r_s);
+
+    // Residual
+    residuals = getMeasurementSquareRootInformationUpper().cast<T>() * (getMeasurement() - expected_p);
+
+    return true;
+}
+
+inline Eigen::Vector3d FactorRelativePosition3dWithExtrinsics::residual() const
+{
+    Eigen::Vector3d res;
+    Eigen::VectorXd p_sensor, o_sensor, p_ref, o_ref, p_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();
+    }
+    // LANDMARK CASE
+    else if (not getLandmarkOtherList().empty())
+    {
+        p_ref    = getCapture()->getFrame()->getP()->getState();
+        o_ref    = getCapture()->getFrame()->getO()->getState();
+        p_target = getLandmarkOther()->getP()->getState();
+    }
+
+    operator() (p_ref.data(), o_ref.data(), p_target.data(), p_sensor.data(), o_sensor.data(), res.data());
+
+    return res;
+}
+
+inline double FactorRelativePosition3dWithExtrinsics::cost() const
+{
+    return residual().squaredNorm();
+}
+
+} // namespace wolf
\ No newline at end of file
diff --git a/include/core/frame/frame_base.h b/include/core/frame/frame_base.h
index 8d64663585af0982d7ac3209a3fcb808c753080a..0f0271a1b12733b5e37dce59061af22c497e7daf 100644
--- a/include/core/frame/frame_base.h
+++ b/include/core/frame/frame_base.h
@@ -139,17 +139,17 @@ class FrameBase : public NodeBase, public HasStateBlocks, public std::enable_sha
         bool isConstrainedBy(const FactorBaseConstPtr& _factor) const;
 
         template <class C>
-        CaptureBaseConstPtr getCaptureOfType() const;
+        std::shared_ptr<const C> getCaptureOfType() const;
         template <class C>
-        CaptureBasePtr getCaptureOfType();
+        std::shared_ptr<C> getCaptureOfType();
 
         CaptureBaseConstPtr getCaptureOfType(const std::string& type) const;
         CaptureBasePtr getCaptureOfType(const std::string& type);
 
         template <class C>
-        CaptureBaseConstPtrList getCapturesOfType() const;
+        std::list<std::shared_ptr<const C>> getCapturesOfType() const;
         template <class C>
-        CaptureBasePtrList getCapturesOfType();
+        std::list<std::shared_ptr<C>> getCapturesOfType();
 
         CaptureBaseConstPtrList getCapturesOfType(const std::string& type) const;
         CaptureBasePtrList getCapturesOfType(const std::string& type);
@@ -290,40 +290,52 @@ inline void FrameBase::setTrajectory(TrajectoryBasePtr _trj_ptr)
 }
 
 template <class C>
-inline CaptureBaseConstPtr FrameBase::getCaptureOfType() const
+inline std::shared_ptr<const C> FrameBase::getCaptureOfType() const
 {
     for (auto capture_ptr : getCaptureList())
-        if (std::dynamic_pointer_cast<C>(capture_ptr) != nullptr)
-            return capture_ptr;
+    {
+        auto cap_derived = std::dynamic_pointer_cast<const C>(capture_ptr);
+        if (cap_derived)
+            return cap_derived;
+    }
     return nullptr;
 }
 
 template <class C>
-inline CaptureBasePtr FrameBase::getCaptureOfType()
+inline std::shared_ptr<C> FrameBase::getCaptureOfType()
 {
     for (auto capture_ptr : getCaptureList())
-        if (std::dynamic_pointer_cast<C>(capture_ptr) != nullptr)
-            return capture_ptr;
+    {
+        auto cap_derived = std::dynamic_pointer_cast<C>(capture_ptr);
+        if (cap_derived)
+            return cap_derived;
+    }
     return nullptr;
 }
 
 template <class C>
-inline CaptureBaseConstPtrList FrameBase::getCapturesOfType() const
+inline std::list<std::shared_ptr<const C>> FrameBase::getCapturesOfType() const
 {
-    CaptureBaseConstPtrList captures;
+    std::list<std::shared_ptr<const C>> captures;
     for (auto capture_ptr : getCaptureList())
-        if (std::dynamic_pointer_cast<C>(capture_ptr) != nullptr)
-            captures.push_back(capture_ptr);
+    {
+        auto cap_derived = std::dynamic_pointer_cast<const C>(capture_ptr);
+        if (cap_derived)
+            captures.push_back(cap_derived);
+    }
     return captures;
 }
 
 template <class C>
-inline CaptureBasePtrList FrameBase::getCapturesOfType()
+inline std::list<std::shared_ptr<C>> FrameBase::getCapturesOfType()
 {
-    CaptureBasePtrList captures;
+    std::list<std::shared_ptr<C>> captures;
     for (auto capture_ptr : getCaptureList())
-        if (std::dynamic_pointer_cast<C>(capture_ptr) != nullptr)
-            captures.push_back(capture_ptr);
+    {
+        auto cap_derived = std::dynamic_pointer_cast<C>(capture_ptr);
+        if (cap_derived)
+            captures.push_back(cap_derived);
+    }
     return captures;
 }
 
diff --git a/include/core/map/map_base.h b/include/core/map/map_base.h
index af09f86002c697ad026ad9942d218026808555c4..da7113fa583bd7838bda5ee9a96c610731814dcd 100644
--- a/include/core/map/map_base.h
+++ b/include/core/map/map_base.h
@@ -113,7 +113,9 @@ class MapBase : public NodeBase, public std::enable_shared_from_this<MapBase>
     public:
         LandmarkBaseConstPtrList getLandmarkList() const;
         LandmarkBasePtrList getLandmarkList();
-        
+        LandmarkBaseConstPtr getLandmark(const unsigned int& _id) const;
+        LandmarkBasePtr getLandmark(const unsigned int& _id);
+
         void load(const std::string& _map_file_yaml);
         void save(const std::string& _map_file_yaml, const std::string& _map_name = "Map automatically saved by Wolf");
 
diff --git a/include/core/math/SE2.h b/include/core/math/SE2.h
index 95631a8a6fb82ac22595b64d3e1d5b4fe7849cb4..39f67c0358fc1f3c127adc1ec96844d005011588 100644
--- a/include/core/math/SE2.h
+++ b/include/core/math/SE2.h
@@ -320,6 +320,13 @@ inline void compose(const VectorComposite& _x1,
     _c['O'] = Matrix1d( pi2pi(a1 + a2) ) ;
 }
 
+inline VectorComposite compose(const VectorComposite& x1, const VectorComposite& x2)
+{
+    VectorComposite c("PO", {2,1});
+    compose(x1, x2, c);
+    return c;
+}
+
 inline void compose(const VectorComposite& _x1,
              const VectorComposite& _x2,
              VectorComposite& _c,
diff --git a/include/core/math/SE3.h b/include/core/math/SE3.h
index 9eb2e5fd9b1c644742a7d32d055591231f10c7aa..7917935549619fbc58cc917e834d5ae735d90d41 100644
--- a/include/core/math/SE3.h
+++ b/include/core/math/SE3.h
@@ -108,7 +108,7 @@ inline void inverse(const MatrixBase<D1>& p, const QuaternionBase<D2>& q,
     MatrixSizeCheck<3, 1>::check(p);
     MatrixSizeCheck<3, 1>::check(ip);
 
-    ip = - q.conjugate() * p ;
+    ip = -(q.conjugate() * p) ;
     iq =   q.conjugate() ;
 }
 
@@ -135,6 +135,21 @@ inline Matrix<typename D::Scalar, 7, 1> inverse(const MatrixBase<D>& d)
     return id;
 }
 
+inline void inverse(const VectorComposite& v, VectorComposite& c)
+{
+    Map<const Quaternion<double> > qv( & v.at('O')(0) );
+    Map<Quaternion<double> > qc( & c['O'](0) );
+    inverse(v.at('P'), qv, c['P'], qc);
+}
+
+inline VectorComposite inverse(const VectorComposite& v)
+{
+    VectorComposite c("PO", {3,4});
+    inverse(v, c);
+    return c;
+}
+
+
 template<typename D1, typename D2, typename D4, typename D5, typename D7, typename D8>
 inline void compose(const MatrixBase<D1>& p1, const QuaternionBase<D2>& q1,
                     const MatrixBase<D4>& p2, const QuaternionBase<D5>& q2,
@@ -231,6 +246,13 @@ inline void compose(const VectorComposite& x1, const VectorComposite& x2, Vector
     compose(x1.at('P'), x1.at('O'), x2.at('P'), x2.at('O'), c['P'], c['O']);
 }
 
+inline VectorComposite compose(const VectorComposite& x1, const VectorComposite& x2)
+{
+    VectorComposite c("PO", {3,4});
+    compose(x1.at('P'), x1.at('O'), x2.at('P'), x2.at('O'), c['P'], c['O']);
+    return c;
+}
+
 inline void compose(const VectorComposite& x1,
                     const VectorComposite& x2,
                     VectorComposite& c,
diff --git a/include/core/problem/problem.h b/include/core/problem/problem.h
index 9dfb7cae3e62adac06662e9414c75764045148e2..e9134b5593e30d93aa514f49eecb597759b0cf18 100644
--- a/include/core/problem/problem.h
+++ b/include/core/problem/problem.h
@@ -86,7 +86,11 @@ class Problem : public std::enable_shared_from_this<Problem>
         StateStructure frame_structure_;
         PriorOptionsPtr prior_options_;
 
-    private: // CAUTION: THESE METHODS ARE PRIVATE, DO NOT MAKE THEM PUBLIC !!
+        std::atomic_bool transformed_;
+        VectorComposite  transformation_;
+        mutable std::mutex mut_transform_;
+
+      private:  // CAUTION: THESE METHODS ARE PRIVATE, DO NOT MAKE THEM PUBLIC !!
         Problem(const std::string& _frame_structure, SizeEigen _dim, MapBasePtr _map); // USE create() below !!
         void setup();
 
@@ -362,8 +366,16 @@ class Problem : public std::enable_shared_from_this<Problem>
 
 
         // All branches -------------------------------------------
-        // perturb states
+        /**
+         *  \brief perturb all states states with random noise
+         * following an uniform distribution in [ -amplitude, amplitude ]
+         */
         void perturb(double amplitude = 0.01);
+        // transform states
+        void transform(const VectorComposite& _transformation);
+        bool isTransformed() const;
+        void resetTransformed();
+        VectorComposite getTransformation() const;
 
         // Covariances
         void clearCovariance();
@@ -423,7 +435,7 @@ class Problem : public std::enable_shared_from_this<Problem>
         /**
          * \brief print wolf tree
          * \param depth :        levels to show ( 0: H, T, M : 1: H:S, T:F, M:L ; 2: H:S:p, T:F:C ; 3: T:F:C:f ; 4: T:F:C:f:c )
-         * \param constr_by:     show factors pointing to F, f and L.
+         * \param constr_by :    show factors pointing to F, C, f and L.
          * \param metric :       show metric info (status, time stamps, state vectors, measurements)
          * \param state_blocks : show state blocks
          */
diff --git a/include/core/processor/factory_processor.h b/include/core/processor/factory_processor.h
index 74f01419ea00bfd01842364b5f79e46df02b525f..fbd3261c29afceed19fbc21666e79dd996b945aa 100644
--- a/include/core/processor/factory_processor.h
+++ b/include/core/processor/factory_processor.h
@@ -37,6 +37,7 @@ struct ParamsProcessorBase;
 
 // wolf
 #include "core/common/factory.h"
+#include "core/utils/params_server.h"
 
 // std
 
diff --git a/include/core/processor/motion_buffer.h b/include/core/processor/motion_buffer.h
index 4763d0bc45d513611d34775754898da9290b7aec..7e0a1203e454c7b2ed02c9e056499ddb4900aee7 100644
--- a/include/core/processor/motion_buffer.h
+++ b/include/core/processor/motion_buffer.h
@@ -100,7 +100,7 @@ class MotionBuffer : public std::list<Motion>
         const Motion& getMotion(const TimeStamp& _ts) const;
         void getMotion(const TimeStamp& _ts, Motion& _motion) const;
         void split(const TimeStamp& _ts, MotionBuffer& _oldest_buffer);
-        void print(bool show_data = 0, bool show_delta = 0, bool show_delta_int = 0, bool show_jacs = 0);
+        void print(bool show_data = 0, bool show_delta = 0, bool show_delta_int = 0, bool show_jacs = 0, bool show_covs = 0);
 };
 
 } // namespace wolf
diff --git a/include/core/processor/motion_provider.h b/include/core/processor/motion_provider.h
index d7bd26d00df45c7f14d0ff5ae01966e271af03ef..c2602142571f2f8ba7283c5ac9ecea12f98f187e 100644
--- a/include/core/processor/motion_provider.h
+++ b/include/core/processor/motion_provider.h
@@ -86,8 +86,12 @@ class MotionProvider
 
     protected:
         StateStructure state_structure_; ///< The structure of the state vector (to retrieve state blocks from frames)
-        VectorComposite odometry_;
         ParamsMotionProviderPtr params_motion_provider_;
+
+    private:
+        VectorComposite odometry_;
+        mutable std::mutex mut_odometry_;
+
 };
 
 inline MotionProvider::MotionProvider(const StateStructure& _structure, ParamsMotionProviderPtr _params) :
@@ -97,11 +101,6 @@ inline MotionProvider::MotionProvider(const StateStructure& _structure, ParamsMo
     //
 }
 
-inline wolf::VectorComposite MotionProvider::getOdometry ( ) const
-{
-    return odometry_;
-}
-
 }
 
 /////  IMPLEMENTATION ///////
diff --git a/include/core/processor/processor_diff_drive.h b/include/core/processor/processor_diff_drive.h
index 20ed2330334aea821c7ca8bc6510f7438c555063..462e5ee22a47f00f874f861dcd4d0ddcd37d8c17 100644
--- a/include/core/processor/processor_diff_drive.h
+++ b/include/core/processor/processor_diff_drive.h
@@ -63,30 +63,30 @@ class ProcessorDiffDrive : public ProcessorOdom2d
 
     protected:
         // Motion integration
-        void computeCurrentDelta(const Eigen::VectorXd& _data,
-                                         const Eigen::MatrixXd& _data_cov,
-                                         const Eigen::VectorXd& _calib,
-                                         const double _dt,
-                                         Eigen::VectorXd& _delta,
-                                         Eigen::MatrixXd& _delta_cov,
-                                         Eigen::MatrixXd& _jacobian_calib) const override;
-        CaptureMotionPtr emplaceCapture(const FrameBasePtr& _frame_own,
-                                                const SensorBasePtr& _sensor,
-                                                const TimeStamp& _ts,
-                                                const VectorXd& _data,
-                                                const MatrixXd& _data_cov,
-                                                const VectorXd& _calib,
-                                                const VectorXd& _calib_preint,
-                                                const CaptureBasePtr& _capture_origin) override;
-        FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_own) override;
-        FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion,
-                                            CaptureBasePtr _capture_origin) override;
-        VectorXd getCalibration (const CaptureBaseConstPtr _capture = nullptr) const override;
-        void setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override;
+      void computeCurrentDelta(const Eigen::VectorXd& _data,
+                               const Eigen::MatrixXd& _data_cov,
+                               const Eigen::VectorXd& _calib,
+                               const double           _dt,
+                               Eigen::VectorXd&       _delta,
+                               Eigen::MatrixXd&       _delta_cov,
+                               Eigen::MatrixXd&       _jacobian_calib) const override;
+
+      CaptureMotionPtr emplaceCapture(const FrameBasePtr&   _frame_own,
+                                      const SensorBasePtr&  _sensor,
+                                      const TimeStamp&      _ts,
+                                      const VectorXd&       _data,
+                                      const MatrixXd&       _data_cov,
+                                      const VectorXd&       _calib,
+                                      const VectorXd&       _calib_preint,
+                                      const CaptureBasePtr& _capture_origin) override;
+      void emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) override;
+
+      VectorXd getCalibration(const CaptureBaseConstPtr _capture = nullptr) const override;
+      void     setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override;
 
     protected:
-        ParamsProcessorDiffDrivePtr params_prc_diff_drv_selfcal_;
-        double radians_per_tick_;
+      ParamsProcessorDiffDrivePtr params_prc_diff_drv_selfcal_;
+      double                      radians_per_tick_;
 
 };
 
diff --git a/include/core/processor/processor_landmark_external.h b/include/core/processor/processor_landmark_external.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c574a6e9a3a0a8c94254329dfaaa766be5f8db4
--- /dev/null
+++ b/include/core/processor/processor_landmark_external.h
@@ -0,0 +1,159 @@
+//--------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--------
+
+#pragma once
+
+#include "core/common/wolf.h"
+#include "core/processor/processor_tracker.h"
+#include "core/processor/track_matrix.h"
+
+namespace wolf
+{
+
+WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorLandmarkExternal);
+
+struct ParamsProcessorLandmarkExternal : public ParamsProcessorTracker
+{
+    bool use_orientation;                   ///< use orientation measure or not when emplacing factors
+    double match_dist_th;                   ///< for considering tracked detection: distance threshold to previous detection
+    unsigned int new_features_for_keyframe; ///< for keyframe voting: amount of new features with respect to origin (sufficient condition if more than min_features_for_keyframe)
+    double filter_quality_th;               ///< min quality to consider the detection
+    unsigned int filter_track_length_th;    ///< length of the track necessary to consider the detection
+    double time_span;                       ///< for keyframe voting: time span since last frame (sufficient condition if more than min_features_for_keyframe)
+
+    ParamsProcessorLandmarkExternal() = default;
+    ParamsProcessorLandmarkExternal(std::string _unique_name,
+                                       const wolf::ParamsServer & _server):
+        ParamsProcessorTracker(_unique_name, _server)
+    {
+        use_orientation         = _server.getParam<bool>        (prefix + _unique_name + "/use_orientation");
+        filter_quality_th       = _server.getParam<double>      (prefix + _unique_name + "/filter/quality_th");
+        filter_track_length_th  = _server.getParam<unsigned int>(prefix + _unique_name + "/filter/track_length_th");
+        match_dist_th           = _server.getParam<double>      (prefix + _unique_name + "/match_dist_th");
+        time_span               = _server.getParam<double>      (prefix + _unique_name + "/keyframe_vote/time_span");
+    }
+};
+
+WOLF_PTR_TYPEDEFS(ProcessorLandmarkExternal);
+
+//Class
+class ProcessorLandmarkExternal : public ProcessorTracker
+{
+    public:
+        ProcessorLandmarkExternal(ParamsProcessorLandmarkExternalPtr _params_tracker_feature);
+        ~ProcessorLandmarkExternal() override;
+
+        // Factory method for high level API
+        WOLF_PROCESSOR_CREATE(ProcessorLandmarkExternal, ParamsProcessorLandmarkExternal);
+
+        void configure(SensorBasePtr _sensor) override { };
+
+    protected:
+
+        ParamsProcessorLandmarkExternalPtr params_tfle_;
+        TrackMatrix track_matrix_;
+        std::set<SizeStd> lmks_ids_origin_;
+
+        /** Pre-process incoming Capture
+         *
+         * This is called by process() just after assigning incoming_ptr_ to a valid Capture.
+         *
+         * Extract the detections and fill unmatched_detections_incoming_ (FeaturePtrList)
+         */
+        void preProcess() override;
+
+
+        /** \brief Process known Features
+         * \return The number of successful matches.
+         *
+         * This function tracks features from last to incoming and starts new tracks for new (not tracked) features in incoming
+         */
+        unsigned int processKnown() override;
+
+        /**\brief Process new Features
+         *
+         */
+        unsigned int processNew(const int& _max_features) override { return 0;};
+
+        /** \brief Vote for KeyFrame generation
+         *
+         * If a KeyFrame criterion is validated, this function returns true,
+         * meaning that it wants to create a KeyFrame at the \b last Capture.
+         *
+         * WARNING! This function only votes! It does not create KeyFrames!
+         */
+        bool voteForKeyFrame() const override;
+        
+        /** \brief Emplaces a new factor for each known feature in Capture \b last
+         */
+        void establishFactors() override;
+        
+        /** \brief Emplaces a new factor
+         * \param _feature feature pointer
+         * \param _landmark pointer to the landmark
+         */
+        FactorBasePtr emplaceFactor(FeatureBasePtr _feature, LandmarkBasePtr _landmark);
+        
+        /** \brief Emplaces a landmark or modifies (if needed) a landmark
+         * \param _feature_ptr pointer to the Feature
+         */
+        LandmarkBasePtr emplaceLandmark(FeatureBasePtr _feature_ptr);
+
+        /** \brief Modifies (if needed) a landmark
+         * \param _landmark pointer to the landmark
+         * \param _feature pointer to the Feature.
+         */
+        void modifyLandmark(LandmarkBasePtr _landmark,
+                            FeatureBasePtr _feature);
+
+        /** Post-process
+         *
+         * This is called by process() after finishing the processing algorithm.
+         *
+         * It does nothing for now
+         */
+        void postProcess() override {};
+
+        void advanceDerived() override;
+        void resetDerived() override;
+
+        double detectionDistance(FeatureBasePtr _ftr1,
+                                 FeatureBasePtr _ftr2,
+                                 const VectorComposite& _pose1,
+                                 const VectorComposite& _pose2,
+                                 const VectorComposite& _pose_sen) const;
+};
+
+inline ProcessorLandmarkExternal::ProcessorLandmarkExternal(ParamsProcessorLandmarkExternalPtr _params_tfle) :
+        ProcessorTracker("ProcessorLandmarkExternal", "PO", 0, _params_tfle),
+        params_tfle_(_params_tfle),
+        lmks_ids_origin_()
+{
+    //
+}
+
+inline ProcessorLandmarkExternal::~ProcessorLandmarkExternal()
+{
+    //
+}
+
+} // namespace wolf
\ No newline at end of file
diff --git a/include/core/processor/processor_motion.h b/include/core/processor/processor_motion.h
index 7c0cf9b2c776be5808c853c54c5d5c3f0cca5ff7..c286d247e34676cbc687fff1ac8db2872b068980 100644
--- a/include/core/processor/processor_motion.h
+++ b/include/core/processor/processor_motion.h
@@ -30,7 +30,7 @@
 #define PROCESSOR_MOTION_H_
 
 // Wolf
-#include <core/processor/motion_provider.h>
+#include "core/processor/motion_provider.h"
 #include "core/capture/capture_motion.h"
 #include "core/processor/processor_base.h"
 #include "core/common/time_stamp.h"
@@ -167,12 +167,13 @@ class ProcessorMotion : public ProcessorBase, public MotionProvider
             RUNNING_WITH_KF_AFTER_ORIGIN
         } ProcessingStep ;
 
-    protected:
+      protected:
         ParamsProcessorMotionPtr params_motion_;
-        ProcessingStep processing_step_;        ///< State machine controlling the processing step
+        ProcessingStep           processing_step_; ///< State machine controlling the processing step
+        bool                     bootstrapping_;   ///< processor is bootstrapping
 
-    // This is the main public interface
-    public:
+        // This is the main public interface
+      public:
         ProcessorMotion(const std::string& _type,
                         std::string _state_structure,
                         int _dim,
@@ -270,6 +271,10 @@ class ProcessorMotion : public ProcessorBase, public MotionProvider
         bool voteForKeyFrame() const override;
 
         double updateDt();
+        /** \brief Make one step of motion integration
+         * 
+         * Integrate motiondata in incoming_ptr_ and store all results in the MotionBuffer in last_ptr_
+         */
         void integrateOneStep();
         void reintegrateBuffer(CaptureMotionPtr _capture_ptr) const;
         void splitBuffer(const wolf::CaptureMotionPtr& capture_source,
@@ -289,6 +294,11 @@ class ProcessorMotion : public ProcessorBase, public MotionProvider
          */
         virtual void preProcess(){ };
 
+        /**
+         * @brief Bootstrap the processor with some initialization steps
+         */
+        virtual void bootstrap(){};
+
         /** Post-process
          *
          * This is called by process() after finishing the processing algorithm.
@@ -471,16 +481,17 @@ class ProcessorMotion : public ProcessorBase, public MotionProvider
                                                 const VectorXd& _calib_preint,
                                                 const CaptureBasePtr& _capture_origin_ptr) = 0;
 
-        /** \brief emplace a feature corresponding to given capture and add the feature to this capture
-         * \param _capture_motion: the parent capture
-         */
-        virtual FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_own) = 0;
 
-        /** \brief emplace a factor and link it in the wolf tree
-         * \param _feature_motion: the parent feature
-         * \param _frame_origin: the frame constrained by this motion factor
+        /** \brief emplace the features and factors corresponding to given capture and link them to the capture
+         * \param _capture_own: the parent capture
+         * \param _capture_origin: the capture constrained by this motion factor
+         * 
+         * Typical factors to add for a ProcessorMotionDerived can be:
+         *   - A preintegrated motion factor -- this is the main factor
+         *   - A calibration drift factor -- only for dynamic sensor calibration parameters
+         *   - An instantaneous factor for observing non-typical states of the Frame -- useful for complex dynamic robot models
          */
-        virtual FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion, CaptureBasePtr _capture_origin) = 0;
+        virtual void emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) = 0;
 
         virtual void setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) = 0;
 
diff --git a/include/core/processor/processor_odom_2d.h b/include/core/processor/processor_odom_2d.h
index fea39c4d6808a7a798a83d7b8b99dfbb47abec9d..1612a4bf5ce75ff875b09017e9624d60d8ae6a3c 100644
--- a/include/core/processor/processor_odom_2d.h
+++ b/include/core/processor/processor_odom_2d.h
@@ -71,44 +71,42 @@ class ProcessorOdom2d : public ProcessorMotion
         bool voteForKeyFrame() const override;
 
     protected:
-        void computeCurrentDelta(const Eigen::VectorXd& _data,
-                                         const Eigen::MatrixXd& _data_cov,
-                                         const Eigen::VectorXd& _calib,
-                                         const double _dt,
-                                         Eigen::VectorXd& _delta,
-                                         Eigen::MatrixXd& _delta_cov,
-                                         Eigen::MatrixXd& _jacobian_calib) const override;
-        void deltaPlusDelta(const Eigen::VectorXd& _delta1,
-                                    const Eigen::VectorXd& _delta2,
-                                    const double _Dt2,
-                                    Eigen::VectorXd& _delta1_plus_delta2) const override;
-        void deltaPlusDelta(const Eigen::VectorXd& _delta1,
-                                    const Eigen::VectorXd& _delta2,
-                                    const double _Dt2,
-                                    Eigen::VectorXd& _delta1_plus_delta2,
-                                    Eigen::MatrixXd& _jacobian1,
-                                    Eigen::MatrixXd& _jacobian2) const override;
-        void statePlusDelta(const VectorComposite& _x,
-                                    const Eigen::VectorXd& _delta,
-                                    const double _Dt,
-                                    VectorComposite& _x_plus_delta) const override;
-        Eigen::VectorXd deltaZero() const override;
-        Eigen::VectorXd correctDelta(const Eigen::VectorXd& delta_preint,
-                                     const Eigen::VectorXd& delta_step) const override;
-
-        CaptureMotionPtr emplaceCapture(const FrameBasePtr& _frame_own,
-                                                const SensorBasePtr& _sensor,
-                                                const TimeStamp& _ts,
-                                                const VectorXd& _data,
-                                                const MatrixXd& _data_cov,
-                                                const VectorXd& _calib,
-                                                const VectorXd& _calib_preint,
-                                                const CaptureBasePtr& _capture_origin_ptr) override;
-        FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_motion) override;
-        FactorBasePtr emplaceFactor(FeatureBasePtr _feature,
-                                            CaptureBasePtr _capture_origin) override;
-        VectorXd getCalibration (const CaptureBaseConstPtr _capture) const override;
-        void setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override;
+      void            computeCurrentDelta(const Eigen::VectorXd& _data,
+                                          const Eigen::MatrixXd& _data_cov,
+                                          const Eigen::VectorXd& _calib,
+                                          const double           _dt,
+                                          Eigen::VectorXd&       _delta,
+                                          Eigen::MatrixXd&       _delta_cov,
+                                          Eigen::MatrixXd&       _jacobian_calib) const override;
+      void            deltaPlusDelta(const Eigen::VectorXd& _delta1,
+                                     const Eigen::VectorXd& _delta2,
+                                     const double           _Dt2,
+                                     Eigen::VectorXd&       _delta1_plus_delta2) const override;
+      void            deltaPlusDelta(const Eigen::VectorXd& _delta1,
+                                     const Eigen::VectorXd& _delta2,
+                                     const double           _Dt2,
+                                     Eigen::VectorXd&       _delta1_plus_delta2,
+                                     Eigen::MatrixXd&       _jacobian1,
+                                     Eigen::MatrixXd&       _jacobian2) const override;
+      void            statePlusDelta(const VectorComposite& _x,
+                                     const Eigen::VectorXd& _delta,
+                                     const double           _Dt,
+                                     VectorComposite&       _x_plus_delta) const override;
+      Eigen::VectorXd deltaZero() const override;
+      Eigen::VectorXd correctDelta(const Eigen::VectorXd& delta_preint,
+                                   const Eigen::VectorXd& delta_step) const override;
+
+      CaptureMotionPtr emplaceCapture(const FrameBasePtr&   _frame_own,
+                                      const SensorBasePtr&  _sensor,
+                                      const TimeStamp&      _ts,
+                                      const VectorXd&       _data,
+                                      const MatrixXd&       _data_cov,
+                                      const VectorXd&       _calib,
+                                      const VectorXd&       _calib_preint,
+                                      const CaptureBasePtr& _capture_origin_ptr) override;
+      void     emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) override;
+      VectorXd getCalibration(const CaptureBaseConstPtr _capture) const override;
+      void     setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override;
 
     protected:
         ParamsProcessorOdom2dPtr params_odom_2d_;
diff --git a/include/core/processor/processor_odom_3d.h b/include/core/processor/processor_odom_3d.h
index 02cdbd5ef4480ccd1a0ba28ab0db5c44886fa58c..fca115c8358bdc22bd60013d1f7f3fbc9f1828d2 100644
--- a/include/core/processor/processor_odom_3d.h
+++ b/include/core/processor/processor_odom_3d.h
@@ -90,47 +90,45 @@ class ProcessorOdom3d : public ProcessorMotion
 
     public:
         // Motion integration
-        void computeCurrentDelta(const Eigen::VectorXd& _data,
-                                         const Eigen::MatrixXd& _data_cov,
-                                         const Eigen::VectorXd& _calib,
-                                         const double _dt,
-                                         Eigen::VectorXd& _delta,
-                                         Eigen::MatrixXd& _delta_cov,
-                                         Eigen::MatrixXd& _jacobian_calib) const override;
-        void deltaPlusDelta(const Eigen::VectorXd& _delta1,
-                            const Eigen::VectorXd& _delta2,
-                            const double _Dt2,
-                            Eigen::VectorXd& _delta1_plus_delta2) const override;
-        void deltaPlusDelta(const Eigen::VectorXd& _delta1,
-                            const Eigen::VectorXd& _delta2,
-                            const double _Dt2,
-                            Eigen::VectorXd& _delta1_plus_delta2,
-                            Eigen::MatrixXd& _jacobian1,
-                            Eigen::MatrixXd& _jacobian2) const override;
-        void statePlusDelta(const VectorComposite& _x,
-                            const Eigen::VectorXd& _delta,
-                            const double _Dt,
-                            VectorComposite& _x_plus_delta) const override;
-        Eigen::VectorXd deltaZero() const override;
-        Eigen::VectorXd correctDelta(const Eigen::VectorXd& delta_preint,
-                                     const Eigen::VectorXd& delta_step) const override;
-
-        bool voteForKeyFrame() const override;
-        CaptureMotionPtr emplaceCapture(const FrameBasePtr& _frame_own,
-                                                const SensorBasePtr& _sensor,
-                                                const TimeStamp& _ts,
-                                                const VectorXd& _data,
-                                                const MatrixXd& _data_cov,
-                                                const VectorXd& _calib,
-                                                const VectorXd& _calib_preint,
-                                                const CaptureBasePtr& _capture_origin) override;
-
-        FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_motion) override;
-
-        FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion,
-                                            CaptureBasePtr _capture_origin) override;
-        VectorXd getCalibration (const CaptureBaseConstPtr _capture) const override;
-        void setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override;
+      void            computeCurrentDelta(const Eigen::VectorXd& _data,
+                                          const Eigen::MatrixXd& _data_cov,
+                                          const Eigen::VectorXd& _calib,
+                                          const double           _dt,
+                                          Eigen::VectorXd&       _delta,
+                                          Eigen::MatrixXd&       _delta_cov,
+                                          Eigen::MatrixXd&       _jacobian_calib) const override;
+      void            deltaPlusDelta(const Eigen::VectorXd& _delta1,
+                                     const Eigen::VectorXd& _delta2,
+                                     const double           _Dt2,
+                                     Eigen::VectorXd&       _delta1_plus_delta2) const override;
+      void            deltaPlusDelta(const Eigen::VectorXd& _delta1,
+                                     const Eigen::VectorXd& _delta2,
+                                     const double           _Dt2,
+                                     Eigen::VectorXd&       _delta1_plus_delta2,
+                                     Eigen::MatrixXd&       _jacobian1,
+                                     Eigen::MatrixXd&       _jacobian2) const override;
+      void            statePlusDelta(const VectorComposite& _x,
+                                     const Eigen::VectorXd& _delta,
+                                     const double           _Dt,
+                                     VectorComposite&       _x_plus_delta) const override;
+      Eigen::VectorXd deltaZero() const override;
+      Eigen::VectorXd correctDelta(const Eigen::VectorXd& delta_preint,
+                                   const Eigen::VectorXd& delta_step) const override;
+
+      bool             voteForKeyFrame() const override;
+
+      CaptureMotionPtr emplaceCapture(const FrameBasePtr&   _frame_own,
+                                      const SensorBasePtr&  _sensor,
+                                      const TimeStamp&      _ts,
+                                      const VectorXd&       _data,
+                                      const MatrixXd&       _data_cov,
+                                      const VectorXd&       _calib,
+                                      const VectorXd&       _calib_preint,
+                                      const CaptureBasePtr& _capture_origin) override;
+      void emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own) override;
+
+      VectorXd getCalibration(const CaptureBaseConstPtr _capture) const override;
+      void     setCalibration(const CaptureBasePtr _capture, const VectorXd& _calibration) override;
 
     protected:
         ParamsProcessorOdom3dPtr params_odom_3d_;
diff --git a/include/core/processor/processor_tracker_landmark.h b/include/core/processor/processor_tracker_landmark.h
index bbccccf950aa02f499d215a21f5488a724330435..ab109ffdbf4302cfeb517be5e184ae3499d77fa2 100644
--- a/include/core/processor/processor_tracker_landmark.h
+++ b/include/core/processor/processor_tracker_landmark.h
@@ -88,7 +88,7 @@ WOLF_PTR_TYPEDEFS(ProcessorTrackerLandmark);
  *     - createLandmark() : creates a Landmark using a new Feature                  <=== IMPLEMENT
  *     - findLandmarks() : find the new Landmarks again in \b incoming              <=== IMPLEMENT
  *   - establishFactors() : which calls the pure virtual:
- *     - createFactor() : create a Feature-Landmark factor of the correct derived type <=== IMPLEMENT
+ *     - emplaceFactor() : create a Feature-Landmark factor of the correct derived type <=== IMPLEMENT
  *
  * Should you need extra functionality for your derived types, you can overload these two methods,
  *
diff --git a/include/core/sensor/sensor_base.h b/include/core/sensor/sensor_base.h
index 8e6c6db7c6657037e7df0eddeea9b683635e5b71..a8572e7d66887ea60d6d38ed427f268c748662c2 100644
--- a/include/core/sensor/sensor_base.h
+++ b/include/core/sensor/sensor_base.h
@@ -58,15 +58,10 @@ static
 SensorBasePtr create(const std::string& _unique_name, const ParamsServer& _server)                                              \
 {                                                                                                                               \
     Eigen::VectorXd extrinsics = _server.template getParam<Eigen::VectorXd>("sensor/" + _unique_name + "/extrinsic/pose");      \
-                                                                                                                                \
     assert(extrinsics.size() == ExtrinsicsSize && "Bad extrinsics vector length.");                                             \
-                                                                                                                                \
     auto params = std::make_shared<ParamsSensorClass>(_unique_name, _server);                                                   \
-                                                                                                                                \
     auto sensor = std::make_shared<SensorClass>(extrinsics, params);                                                            \
-                                                                                                                                \
     sensor      ->setName(_unique_name);                                                                                        \
-                                                                                                                                \
     return sensor;                                                                                                              \
 }                                                                                                                               \
                                                                                                                                 \
@@ -74,13 +69,9 @@ static
 SensorBasePtr create(const std::string& _unique_name, const Eigen::VectorXd& _extrinsics, const ParamsSensorBasePtr _intrinsics)\
 {                                                                                                                               \
     assert(_extrinsics.size() == ExtrinsicsSize && "Bad extrinsics vector length.");                                            \
-                                                                                                                                \
     auto params = std::static_pointer_cast<ParamsSensorClass>(_intrinsics);                                                     \
-                                                                                                                                \
     auto sensor = std::make_shared<SensorClass>(_extrinsics, params);                                                           \
-                                                                                                                                \
     sensor      ->setName(_unique_name);                                                                                        \
-                                                                                                                                \
     return sensor;                                                                                                              \
 }                                                                                                                               \
 
diff --git a/include/core/sensor/sensor_pose.h b/include/core/sensor/sensor_pose.h
index e653fe458d39107c143da729a006c727df3959e3..1fbf559364af15d85a74f8b53a13f0c74355b188 100644
--- a/include/core/sensor/sensor_pose.h
+++ b/include/core/sensor/sensor_pose.h
@@ -39,8 +39,12 @@ WOLF_STRUCT_PTR_TYPEDEFS(ParamsSensorPose);
 
 struct ParamsSensorPose : public ParamsSensorBase
 {
-    double std_p = 1;  // m
-    double std_o = 1;  // rad
+    double std_px = 1;  // m
+    double std_py = 1;  // m
+    double std_pz = 1;  // m
+    double std_ox = 1;  // rad
+    double std_oy = 1;  // rad
+    double std_oz = 1;  // rad
     ParamsSensorPose()
     {
         //DEFINED FOR COMPATIBILITY PURPOSES. TO BE REMOVED IN THE FUTURE.
@@ -48,14 +52,56 @@ struct ParamsSensorPose : public ParamsSensorBase
     ParamsSensorPose(std::string _unique_name, const ParamsServer& _server):
         ParamsSensorBase(_unique_name, _server)
     {
-        std_p = _server.getParam<double>(prefix + _unique_name + "/std_p");
-        std_o  = _server.getParam<double>(prefix + _unique_name + "/std_o");
+        /* Here we allow isotropic specs for position and orientation
+         * and also particularizations for eaxh axis
+         * depending on how the info was ptovided in the YAML file.
+         * Combinations are possible. 
+         * 
+         * E.g. if the yaml file contains
+         * 
+         *  std_p : 1
+         * 
+         * then the result is std_px = 1, std_py = 1, std_pz = 1
+         * 
+         * If the yaml file contains (in any order)
+         * 
+         *  std_p : 1
+         *  std_pz : 0.1
+         * 
+         * then the result is std_px = 1, std_py = 1, std_pz = 0.1
+         */
+
+        // first the isotropic options
+        if (_server.hasParam(prefix + _unique_name + "/std_p"))
+            std_px = std_py = std_pz = _server.getParam<double>(prefix + _unique_name + "/std_p");
+        if (_server.hasParam(prefix + _unique_name + "/std_o"))
+            std_ox = std_oy = std_oz = _server.getParam<double>(prefix + _unique_name + "/std_o");
+
+        // then individual axes specifications
+        if (_server.hasParam(prefix + _unique_name + "/std_px"))
+            std_px = _server.getParam<double>(prefix + _unique_name + "/std_px");
+        if (_server.hasParam(prefix + _unique_name + "/std_py"))
+            std_py = _server.getParam<double>(prefix + _unique_name + "/std_py");
+        if (_server.hasParam(prefix + _unique_name + "/std_pz"))
+            std_pz = _server.getParam<double>(prefix + _unique_name + "/std_pz");
+
+        if (_server.hasParam(prefix + _unique_name + "/std_ox"))
+            std_ox = _server.getParam<double>(prefix + _unique_name + "/std_ox");
+        if (_server.hasParam(prefix + _unique_name + "/std_oy"))
+            std_oy = _server.getParam<double>(prefix + _unique_name + "/std_oy");
+        if (_server.hasParam(prefix + _unique_name + "/std_oz"))
+            std_oz = _server.getParam<double>(prefix + _unique_name + "/std_oz");
+
     }
     std::string print() const override
     {
       return ParamsSensorBase::print()           + "\n"
-        + "std_p: "    + std::to_string(std_p)   + "\n"
-        + "std_o: "    + std::to_string(std_o)  + "\n";
+        + "std_px: "    + std::to_string(std_px)   + "\n"
+        + "std_py: "    + std::to_string(std_py)   + "\n"
+        + "std_pz: "    + std::to_string(std_pz)   + "\n"
+        + "std_ox: "    + std::to_string(std_ox)  + "\n";
+        + "std_oy: "    + std::to_string(std_oy)  + "\n";
+        + "std_oz: "    + std::to_string(std_oz)  + "\n";
     }
         ~ParamsSensorPose() override = default;
 };
@@ -65,8 +111,7 @@ WOLF_PTR_TYPEDEFS(SensorPose);
 class SensorPose : public SensorBase
 {
     protected:
-        double std_p_; // standard deviation of translation measurements
-        double std_o_; // standard deviation of orientation measurements
+        ParamsSensorPosePtr params_pose_;
 
     public:
         SensorPose(const Eigen::VectorXd& _extrinsics_pq, const ParamsSensorPose& params);
@@ -75,20 +120,20 @@ class SensorPose : public SensorBase
 
         ~SensorPose() override;
 
-        double getStdP() const;
-        double getStdO() const;
+        // double getStdP() const;
+        // double getStdO() const;
 
 };
 
-inline double SensorPose::getStdP() const
-{
-    return std_p_;
-}
+// inline double SensorPose::getStdP() const
+// {
+//     return std_p_;
+// }
 
-inline double SensorPose::getStdO() const
-{
-    return std_o_;
-}
+// inline double SensorPose::getStdO() const
+// {
+//     return std_o_;
+// }
 
 // inline Matrix6d SensorPose::computeDataCov() const
 // {
diff --git a/include/core/solver_suitesparse/ccolamd_ordering.h b/include/core/solver_suitesparse/ccolamd_ordering.h
index 4683373559f7df440b834c01c7552628e4f4b7b1..38ec767eff5d4d4e21afee0050f26b840b769c76 100644
--- a/include/core/solver_suitesparse/ccolamd_ordering.h
+++ b/include/core/solver_suitesparse/ccolamd_ordering.h
@@ -33,9 +33,9 @@
 #include <iostream>
 
 // Eigen includes
-#include <eigen3/Eigen/OrderingMethods>
-#include <eigen3/Eigen/CholmodSupport>
-#include <eigen3/Eigen/SparseLU>
+#include <Eigen/OrderingMethods>
+#include <Eigen/CholmodSupport>
+#include <Eigen/SparseLU>
 
 // ccolamd
 #include "ccolamd.h"
diff --git a/include/core/solver_suitesparse/qr_solver.h b/include/core/solver_suitesparse/qr_solver.h
index 83c392894a0a63b06c9b86a8d8b31907a2803bf9..c5fc858227b6da93dbcfe8424a1bed48319f5228 100644
--- a/include/core/solver_suitesparse/qr_solver.h
+++ b/include/core/solver_suitesparse/qr_solver.h
@@ -46,8 +46,8 @@
 #include "solver/cost_function_sparse.h"
 
 // eigen includes
-#include <eigen3/Eigen/OrderingMethods>
-#include <eigen3/Eigen/SparseQR>
+#include <Eigen/OrderingMethods>
+#include <Eigen/SparseQR>
 #include <Eigen/StdVector>
 #include "core/factor/factor_pose_2d.h"
 
diff --git a/include/core/solver_suitesparse/sparse_utils.h b/include/core/solver_suitesparse/sparse_utils.h
index 393a2fb275507aa1e9b5b6c67172a6b589be4f9f..e9bc9f76e8a0de8a88f4b7c6f3a4fe202e2398a0 100644
--- a/include/core/solver_suitesparse/sparse_utils.h
+++ b/include/core/solver_suitesparse/sparse_utils.h
@@ -30,7 +30,7 @@
 #define SPARSE_UTILS_H_
 
 // eigen includes
-#include <eigen3/Eigen/Sparse>
+#include <Eigen/Sparse>
 
 namespace wolf
 {
diff --git a/include/core/state_block/factory_state_block.h b/include/core/state_block/factory_state_block.h
index c440584c0539625d93b4fa4bc5bbafaa1cad1461..5c90da7b1c297d89b9db00364008e696e7316cfa 100644
--- a/include/core/state_block/factory_state_block.h
+++ b/include/core/state_block/factory_state_block.h
@@ -136,30 +136,19 @@ inline std::string FactoryStateBlock::getClass() const
     return "FactoryStateBlock";
 }
 
-template<>
-inline StateBlockPtr FactoryStateBlock::create(const string& _type, const Eigen::VectorXd& _state, bool _fixed)
-{
-    typename CallbackMap::const_iterator creator_callback_it = get().callbacks_.find(_type);
-
-    if (creator_callback_it == get().callbacks_.end())
-        // not found: return StateBlock base
-        return std::make_shared<StateBlock>(_state, _fixed);
-
-    // Invoke the creation function
-    return (creator_callback_it->second)(_state, _fixed);
-}
-
-#define WOLF_REGISTER_STATEBLOCK(StateBlockType)                                            \
-  namespace{ const bool WOLF_UNUSED StateBlockType##Registered =                            \
-     FactoryStateBlock::registerCreator(#StateBlockType, StateBlockType::create); }   \
-
-#define WOLF_REGISTER_STATEBLOCK_WITH_KEY(Key, StateBlockType)                              \
-  namespace{ const bool WOLF_UNUSED StateBlockType##RegisteredWith##Key =                   \
-     FactoryStateBlock::registerCreator(#Key, StateBlockType::create); }              \
-
-
-
+#define WOLF_REGISTER_STATEBLOCK(StateBlockType)                                                                      \
+    namespace                                                                                                         \
+    {                                                                                                                 \
+    const bool WOLF_UNUSED StateBlockType##Registered =                                                               \
+        FactoryStateBlock::registerCreator(#StateBlockType, StateBlockType::create);                                  \
+    }
 
+#define WOLF_REGISTER_STATEBLOCK_WITH_KEY(Key, StateBlockType)                                                        \
+    namespace                                                                                                         \
+    {                                                                                                                 \
+    const bool WOLF_UNUSED StateBlockType##RegisteredWith##Key =                                                      \
+        FactoryStateBlock::registerCreator(#Key, StateBlockType::create);                                             \
+    }
 }
 
 
diff --git a/include/core/state_block/has_state_blocks.h b/include/core/state_block/has_state_blocks.h
index ac82297f5e36b5130151bd1b0464a110264b5fe7..f11047c17b751f327ec6c01177194170b0d39f4d 100644
--- a/include/core/state_block/has_state_blocks.h
+++ b/include/core/state_block/has_state_blocks.h
@@ -40,6 +40,8 @@ namespace wolf
 
 class HasStateBlocks
 {
+    friend Problem;
+
     public:
         HasStateBlocks();
         HasStateBlocks(const StateStructure& _structure) : structure_(_structure), state_block_map_() {}
@@ -80,10 +82,6 @@ class HasStateBlocks
         template<typename SB, typename ... Args>
         std::shared_ptr<SB> emplaceStateBlock(const char& _sb_type, ProblemPtr _problem, Args&&... _args_of_derived_state_block_constructor);
 
-        // Emplace base state blocks.
-        template<typename ... Args>
-        StateBlockPtr emplaceStateBlock(const char& _sb_type, ProblemPtr _problem, Args&&... _args_of_base_state_block_constructor);
-
         // Register/remove state blocks to/from wolf::Problem
         virtual void registerNewStateBlocks(ProblemPtr _problem);
         virtual void removeStateBlocks(ProblemPtr _problem);
@@ -99,12 +97,17 @@ class HasStateBlocks
         unsigned int getSize(const StateStructure& _structure="") const;
         unsigned int getLocalSize(const StateStructure& _structure="") const;
 
-        // Perturb state
-        void perturb(double amplitude = 0.01);
+        /**
+         *  \brief perturb all states states with random noise
+         * following an uniform distribution in [ -amplitude, amplitude ]
+         */
+        void perturb(double amplitude = 0.01);        
 
-    protected:
-        void printState(bool _metric, bool _state_blocks, std::ostream& _stream, std::string _tabs) const;
+      protected:
+        // transform state
+        void transform(const VectorComposite& _transformation);
 
+        void printState(bool _metric, bool _state_blocks, std::ostream& _stream, std::string _tabs) const;
 
     private:
         StateStructure structure_;
@@ -182,17 +185,6 @@ inline std::shared_ptr<SB> HasStateBlocks::emplaceStateBlock(const char& _sb_typ
     return sb;
 }
 
-template<typename ... Args>
-inline StateBlockPtr HasStateBlocks::emplaceStateBlock(const char& _sb_type, ProblemPtr _problem, Args&&... _args_of_base_state_block_constructor)
-{
-    assert(state_block_map_.count(_sb_type) == 0 && state_block_const_map_.count(_sb_type) == 0 && "Trying to add a state block with an existing type! Use setStateBlock instead.");
-    auto sb = std::make_shared<StateBlock>(std::forward<Args>(_args_of_base_state_block_constructor)...);
-
-    addStateBlock(_sb_type, sb, _problem);
-
-    return sb;
-}
-
 inline bool HasStateBlocks::setStateBlock(const char _sb_type, const StateBlockPtr& _sb)
 {
     assert (structure_.find(_sb_type) > 0 && "Cannot set a state block out of the state structure! Use addStateBlock instead.");
diff --git a/include/core/state_block/state_angle.h b/include/core/state_block/state_angle.h
index 3cc093665df69b32ed10d2d49921fdacedea7871..30a1c4dc3c5b16d0a5ddd238d7f3569e5abc8987 100644
--- a/include/core/state_block/state_angle.h
+++ b/include/core/state_block/state_angle.h
@@ -38,19 +38,28 @@ namespace wolf
 class StateAngle : public StateBlock
 {
     public:
-        StateAngle(double _angle = 0.0, bool _fixed = false);
+        StateAngle(double _angle = 0.0, bool _fixed = false, bool _transformable = true);
+        StateAngle(const Vector1d& _angle, bool _fixed = false, bool _transformable = true);
 
         ~StateAngle() override;
 
+        virtual void transform(const VectorComposite& _transformation) override;
+
         static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
 };
 
-inline StateAngle::StateAngle(double _angle, bool _fixed) :
-        StateBlock(1, _fixed, std::make_shared<LocalParametrizationAngle>())
+inline StateAngle::StateAngle(double _angle, bool _fixed, bool _transformable) :
+        StateBlock(1, _fixed, std::make_shared<LocalParametrizationAngle>(), _transformable)
 {
     state_(0) = pi2pi(_angle);
 }
 
+inline StateAngle::StateAngle(const Vector1d& _angle, bool _fixed, bool _transformable) :
+        StateBlock(1, _fixed, std::make_shared<LocalParametrizationAngle>(), _transformable)
+{
+    state_(0) = pi2pi(_angle(0));
+}
+
 inline StateAngle::~StateAngle()
 {
     //
@@ -62,6 +71,11 @@ inline StateBlockPtr StateAngle::create (const Eigen::VectorXd& _state, bool _fi
     return std::make_shared<StateAngle>(_state(0), _fixed);
 }
 
+inline void StateAngle::transform(const VectorComposite& _transformation)
+{
+    if (isTransformable()) setState(_transformation.at('O') + getState()); // 2D rotation is a sum of angles
+}
+
 } /* namespace wolf */
 
 #endif /* STATE_ANGLE_H_ */
diff --git a/include/core/state_block/state_block.h b/include/core/state_block/state_block.h
index 8f4b0468b8d9fdc519e11137a8dc491330ec3ac7..aa78dff969a29e4f5d3d583643c6e46a32980430 100644
--- a/include/core/state_block/state_block.h
+++ b/include/core/state_block/state_block.h
@@ -31,6 +31,7 @@ class LocalParametrizationBase;
 
 //Wolf includes
 #include "core/common/wolf.h"
+#include "core/state_block/state_composite.h"
 
 //std includes
 #include <iostream>
@@ -65,6 +66,8 @@ public:
         std::atomic_bool fix_updated_;          ///< Flag to indicate whether the status has been updated or not
         std::atomic_bool local_param_updated_;  ///< Flag to indicate whether the local_parameterization has been updated or not
 
+        bool transformable_;                    ///< Flag to indicate if the state block can be transformed to another reference frame
+
     public:
         /** \brief Constructor from size
          *
@@ -72,7 +75,7 @@ public:
          * \param _fixed Indicates this state is not estimated and thus acts as a fixed parameter
          * \param _local_param_ptr pointer to the local parametrization for the block
          */
-        StateBlock(const SizeEigen _size, bool _fixed = false, LocalParametrizationBasePtr _local_param_ptr = nullptr);
+        StateBlock(const SizeEigen _size, bool _fixed = false, LocalParametrizationBasePtr _local_param_ptr = nullptr, bool _transformable = true);
 
         /** \brief Constructor from vector
          * 
@@ -80,7 +83,7 @@ public:
          * \param _fixed Indicates this state is not estimated and thus acts as a fixed parameter
          * \param _local_param_ptr pointer to the local parametrization for the block
          **/
-        StateBlock(const Eigen::VectorXd& _state, bool _fixed = false, LocalParametrizationBasePtr _local_param_ptr = nullptr);
+        StateBlock(const Eigen::VectorXd& _state, bool _fixed = false, LocalParametrizationBasePtr _local_param_ptr = nullptr, bool _transformable = true);
 
         ///< Explicitly not copyable/movable
         StateBlock(const StateBlock& o) = delete;
@@ -173,19 +176,29 @@ public:
         virtual Eigen::VectorXd identity() const;
         Eigen::VectorXd zero()     const;
 
-        /** \brief perturb state
+        /**
+         *  \brief perturb all states states with random noise
+         * following an uniform distribution in [ -amplitude, amplitude ]
          */
         void perturb(double amplitude = 0.1);
 
+        bool isTransformable() const {
+            return transformable_;
+        }
+
+        void setTransformable(bool _trf = true) {transformable_ = _trf;}
+
+        void setNonTransformable() {transformable_ = false;}
+
+        virtual void transform(const VectorComposite& _transformation) = 0;
 
         void plus(const Eigen::VectorXd& _dv);
 
         bool isValid(double tolerance = Constants::EPS);
 
-        static StateBlockPtr create (const Eigen::VectorXd& _state, bool _fixed = false);
-
 };
 
+
 } // namespace wolf
 
 // IMPLEMENTATION
@@ -195,31 +208,30 @@ public:
 
 namespace wolf {
 
-inline StateBlock::StateBlock(const Eigen::VectorXd& _state, bool _fixed, LocalParametrizationBasePtr _local_param_ptr) :
-//        notifications_{Notification::ADD},
+inline StateBlock::StateBlock(const Eigen::VectorXd& _state, bool _fixed, LocalParametrizationBasePtr _local_param_ptr, bool _transformable) :
         fixed_(_fixed),
         state_size_(_state.size()),
         state_(_state),
         local_param_ptr_(_local_param_ptr),
         state_updated_(false),
         fix_updated_(false),
-        local_param_updated_(false)
+        local_param_updated_(false),
+        transformable_(_transformable)
 {
 //    std::cout << "constructed           +sb" << std::endl;
 }
 
-inline StateBlock::StateBlock(const SizeEigen _size, bool _fixed, LocalParametrizationBasePtr _local_param_ptr) :
-//        notifications_{Notification::ADD},
+inline StateBlock::StateBlock(const SizeEigen _size, bool _fixed, LocalParametrizationBasePtr _local_param_ptr, bool _transformable) :
         fixed_(_fixed),
         state_size_(_size),
         state_(Eigen::VectorXd::Zero(_size)),
         local_param_ptr_(_local_param_ptr),
         state_updated_(false),
         fix_updated_(false),
-        local_param_updated_(false)
+        local_param_updated_(false),
+        transformable_(_transformable)
 {
-    //
-//    std::cout << "constructed           +sb" << std::endl;
+    //    std::cout << "constructed           +sb" << std::endl;
 }
 
 inline StateBlock::~StateBlock()
@@ -338,10 +350,6 @@ inline Eigen::VectorXd StateBlock::zero()     const
     return identity();
 }
 
-inline StateBlockPtr StateBlock::create (const Eigen::VectorXd& _state, bool _fixed)
-{
-    return std::make_shared<StateBlock>(_state, _fixed);
-}
 inline bool StateBlock::isValid(double tolerance)
 {
     return local_param_ptr_ ? local_param_ptr_->isValid(state_, tolerance) : true;
diff --git a/include/core/state_block/state_block_derived.h b/include/core/state_block/state_block_derived.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d1377520098ae65e5a43b361f48d6061266de37
--- /dev/null
+++ b/include/core/state_block/state_block_derived.h
@@ -0,0 +1,160 @@
+//--------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--------
+#ifndef STATE_BLOCK_DERIVED_H_
+#define STATE_BLOCK_DERIVED_H_
+
+#include "core/state_block/state_block.h"
+
+namespace wolf
+{
+
+/**
+ * @brief State block for general parameters
+ *
+ * This state block cannot be transformed with a geometric frame transformation.
+ *
+ * This state block cannot have a local parametrization
+ *
+ * @tparam size the size of the state block's vector
+ */
+template <size_t size>
+class StateParams : public StateBlock
+{
+  public:
+    StateParams(const Eigen::VectorXd& _state, bool _fixed = false) : StateBlock(_state, _fixed, nullptr, false) {}
+    void transform(const VectorComposite& _transformation) override
+    {
+        // non transformable! --> do nothing
+    }
+    static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
+};
+
+typedef StateParams<1>  StateParams1;
+typedef StateParams<2>  StateParams2;
+typedef StateParams<3>  StateParams3;
+typedef StateParams<4>  StateParams4;
+typedef StateParams<5>  StateParams5;
+typedef StateParams<6>  StateParams6;
+typedef StateParams<7>  StateParams7;
+typedef StateParams<8>  StateParams8;
+typedef StateParams<9>  StateParams9;
+typedef StateParams<10> StateParams10;
+
+/**
+ * @brief State block for general 2D points and positions
+ *
+ * This state block can be transformed with a geometric frame transformation.
+ * This is controlled at construction time by parameter _transformable,
+ * or later via setTransformable(bool).
+ *
+ * This state block cannot have a local parametrization
+ */
+class StatePoint2d : public StateBlock
+{
+  public:
+    StatePoint2d(const Eigen::VectorXd& _state, bool _fixed = false, bool _transformable = true)
+        : StateBlock(_state, _fixed, nullptr, _transformable)
+    {
+    }
+    void transform(const VectorComposite& _transformation) override
+    {
+        if (transformable_) setState(_transformation.at('P') + Rotation2Dd(_transformation.at('O')(0)) * getState());
+    }
+    static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
+};
+
+/**
+ * @brief State block for general 2D vectors
+ *
+ * This state block can be transformed with a geometric frame transformation.
+ * This is controlled at construction time by parameter _transformable,
+ * or later via setTransformable(bool).
+ * Being a vector, the geometric frame transformation will rotate the vector.
+ *
+ * This state block cannot have a local parametrization
+ */
+class StateVector2d : public StateBlock
+{
+  public:
+    StateVector2d(const Eigen::VectorXd& _state, bool _fixed = false, bool _transformable = true)
+        : StateBlock(_state, _fixed, nullptr, _transformable)
+    {
+    }
+    void transform(const VectorComposite& _transformation) override
+    {
+        if (transformable_) setState(Rotation2Dd(_transformation.at('O')(0)) * getState());
+    }
+    static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
+};
+
+/**
+ * @brief State block for general 3D points and positions
+ *
+ * This state block can be transformed with a geometric frame transformation.
+ * This is controlled at construction time by parameter _transformable,
+ * or later via setTransformable(bool).
+ *
+ * This state block cannot have a local parametrization
+ */
+class StatePoint3d : public StateBlock
+{
+  public:
+    StatePoint3d(const Eigen::VectorXd& _state, bool _fixed = false, bool _transformable = true)
+        : StateBlock(_state, _fixed, nullptr, _transformable)
+    {
+    }
+    void transform(const VectorComposite& _transformation) override
+    {
+        if (transformable_)
+            setState(_transformation.at('P') + Quaterniond(_transformation.at('O').data()) * getState());
+    }
+    static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
+};
+
+/**
+ * @brief State block for general 3D vectors
+ *
+ * This state block can be transformed with a geometric frame transformation.
+ * This is controlled at construction time by parameter _transformable,
+ * or later via setTransformable(bool).
+ * Being a vector, the geometric frame transformation will rotate the vector.
+ *
+ * This state block cannot have a local parametrization
+ */
+class StateVector3d : public StateBlock
+{
+  public:
+    StateVector3d(const Eigen::VectorXd& _state, bool _fixed = false, bool _transformable = true)
+        : StateBlock(_state, _fixed, nullptr, _transformable)
+    {
+    }
+    void transform(const VectorComposite& _transformation) override
+    {
+        if (transformable_) setState(Quaterniond(_transformation.at('O').data()) * getState());
+    }
+    static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
+};
+
+
+}  // namespace wolf
+
+#endif  // STATE_BLOCK_DERIVED_H_
\ No newline at end of file
diff --git a/include/core/state_block/state_composite.h b/include/core/state_block/state_composite.h
index e3569b5b4ed30159e0593de789b3b7a6c3644387..8f1d6de1f98c9afa906640f29d4a065188c7a04f 100644
--- a/include/core/state_block/state_composite.h
+++ b/include/core/state_block/state_composite.h
@@ -246,7 +246,7 @@ class MatrixComposite : public std::unordered_map < char, std::unordered_map < c
          *   S["V"]["V"] S["V"]["W"]
          *   S["W"]["V"] S["W"]["W"]
          */
-        MatrixComposite propagate(const MatrixComposite & _Cov); // This performs Jac * this * Jac.tr
+        MatrixComposite propagate(const MatrixComposite & _Cov); // This performs this * _Cov * this.tr
 
         /**
          * \brief Matrix-vector product
@@ -294,10 +294,6 @@ class StateBlockComposite
         template<typename SB, typename ... Args>
         std::shared_ptr<SB>     emplace(const char& _sb_type, Args&&... _args_of_derived_state_block_constructor);
 
-        // Emplace base state blocks.
-        template<typename ... Args>
-        inline StateBlockPtr    emplace(const char& _sb_type, Args&&... _args_of_base_state_block_constructor);
-
         unsigned int            remove(const char& _sb_type);
         bool                    has(const char& _sb_type) const { return state_block_map_.count(_sb_type) > 0; }
         bool                    has(const StateBlockPtr& _sb) const;
@@ -318,7 +314,10 @@ class StateBlockComposite
         // Plus operator
         void                    plus(const VectorComposite& _dx);
 
-        // Perturb state with random noise
+        /**
+         *  \brief perturb all states states with random noise
+         * following an uniform distribution in [ -amplitude, amplitude ]
+         */
         void                    perturb(double amplitude = 0.01);
 
         // These act on all state blocks. Per-block action must be done through state_block.fix() or through extended API in derived classes of this.
@@ -367,18 +366,6 @@ inline std::shared_ptr<SB> wolf::StateBlockComposite::emplace(const char &_sb_ty
     return sb;
 }
 
-template<typename ... Args>
-inline StateBlockPtr wolf::StateBlockComposite::emplace(const char &_sb_type,
-                                                        Args &&... _args_of_base_state_block_constructor)
-{
-    assert(state_block_map_.count(_sb_type) == 0 && "Trying to add a state block with an existing type! Use setStateBlock instead.");
-    auto sb = std::make_shared<StateBlock>(std::forward<Args>(_args_of_base_state_block_constructor)...);
-
-    add(_sb_type, sb);
-
-    return sb;
-}
-
 }
 
 
diff --git a/include/core/state_block/state_homogeneous_3d.h b/include/core/state_block/state_homogeneous_3d.h
index 6ee8a85c28725805ff5a28fb2149950f7cb8e132..8af6059aa6badcfc91665307e92df8fd9379694b 100644
--- a/include/core/state_block/state_homogeneous_3d.h
+++ b/include/core/state_block/state_homogeneous_3d.h
@@ -37,25 +37,27 @@ namespace wolf {
 class StateHomogeneous3d : public StateBlock
 {
     public:
-        StateHomogeneous3d(bool _fixed = false);
-        StateHomogeneous3d(const Eigen::VectorXd _state, bool _fixed = false);
+        StateHomogeneous3d(bool _fixed = false, bool _transformable = true);
+        StateHomogeneous3d(const Eigen::VectorXd _state, bool _fixed = false, bool _transformable = true);
         ~StateHomogeneous3d() override;
 
         void setIdentity(bool _notify = true) override;
         Eigen::VectorXd identity() const override;
 
+        virtual void transform(const VectorComposite& _transformation) override;
+
         static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
 };
 
-inline StateHomogeneous3d::StateHomogeneous3d(const Eigen::VectorXd _state, bool _fixed) :
-        StateBlock(_state, _fixed)
+inline StateHomogeneous3d::StateHomogeneous3d(const Eigen::VectorXd _state, bool _fixed, bool _transformable) :
+        StateBlock(_state, _fixed, nullptr, _transformable)
 {
     assert(_state.size() == 4 && "Homogeneous 3d must be size 4.");
     local_param_ptr_ = std::make_shared<LocalParametrizationHomogeneous>();
 }
 
-inline StateHomogeneous3d::StateHomogeneous3d(bool _fixed) :
-        StateBlock(4, _fixed)
+inline StateHomogeneous3d::StateHomogeneous3d(bool _fixed, bool _transformable) :
+        StateBlock(4, _fixed, nullptr, _transformable)
 {
     local_param_ptr_ = std::make_shared<LocalParametrizationHomogeneous>();
     state_ << 0, 0, 0, 1; // Set origin
@@ -77,6 +79,12 @@ inline Eigen::VectorXd StateHomogeneous3d::identity() const
     return Eigen::Quaterniond::Identity().coeffs();
 }
 
+inline void StateHomogeneous3d::transform(const VectorComposite& _transformation)
+{
+    if (transformable_)
+        setState((Quaterniond(_transformation.at('O').data()) * Quaterniond(getState().data())).coeffs()); // TODO is this correct?????? probably not!!!
+}
+
 inline StateBlockPtr StateHomogeneous3d::create (const Eigen::VectorXd& _state, bool _fixed)
 {
     MatrixSizeCheck<4,1>::check(_state);
diff --git a/include/core/state_block/state_quaternion.h b/include/core/state_block/state_quaternion.h
index f22772908c411fba8a096fb764af40fe6b39316e..b73279f3f7347cf994f23ba8bde4d03e3db75b34 100644
--- a/include/core/state_block/state_quaternion.h
+++ b/include/core/state_block/state_quaternion.h
@@ -37,27 +37,29 @@ namespace wolf {
 class StateQuaternion : public StateBlock
 {
     public:
-        StateQuaternion(bool _fixed = false);
-        StateQuaternion(const Eigen::VectorXd& _state, bool _fixed = false);
-        StateQuaternion(const Eigen::Quaterniond& _quaternion, bool _fixed = false);
+        StateQuaternion(bool _fixed = false, bool _transformable = true);
+        StateQuaternion(const Eigen::VectorXd& _state, bool _fixed = false, bool _transformable = true);
+        StateQuaternion(const Eigen::Quaterniond& _quaternion, bool _fixed = false, bool _transformable = true);
         ~StateQuaternion() override;
 
         void setIdentity(bool _notify = true) override;
         Eigen::VectorXd identity() const override;
 
+        virtual void transform(const VectorComposite& _transformation) override;
+
         static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed);
 };
 
-inline StateQuaternion::StateQuaternion(const Eigen::Quaterniond& _quaternion, bool _fixed) :
-        StateBlock(_quaternion.coeffs(), _fixed, std::make_shared<LocalParametrizationQuaternion<DQ_LOCAL>>())
+inline StateQuaternion::StateQuaternion(const Eigen::Quaterniond& _quaternion, bool _fixed, bool _transformable) :
+        StateBlock(_quaternion.coeffs(), _fixed, std::make_shared<LocalParametrizationQuaternion<DQ_LOCAL>>(), _transformable)
 {
     // TODO: remove these lines after issue #381 is addressed
     assert(isValid(1e-5)  && "Quaternion unit norm is worse than 1e-5 tolerance!");
     state_.normalize();
 }
 
-inline StateQuaternion::StateQuaternion(const Eigen::VectorXd& _state, bool _fixed) :
-        StateBlock(_state, _fixed, std::make_shared<LocalParametrizationQuaternion<DQ_LOCAL>>())
+inline StateQuaternion::StateQuaternion(const Eigen::VectorXd& _state, bool _fixed, bool _transformable) :
+        StateBlock(_state, _fixed, std::make_shared<LocalParametrizationQuaternion<DQ_LOCAL>>(), _transformable)
 {
     assert(state_.size() == 4 && "The quaternion state vector must be of size 4");
 
@@ -66,8 +68,8 @@ inline StateQuaternion::StateQuaternion(const Eigen::VectorXd& _state, bool _fix
     state_.normalize();
 }
 
-inline StateQuaternion::StateQuaternion(bool _fixed) :
-        StateBlock(4, _fixed, std::make_shared<LocalParametrizationQuaternion<DQ_LOCAL>>())
+inline StateQuaternion::StateQuaternion(bool _fixed, bool _transformable) :
+        StateBlock(4, _fixed, std::make_shared<LocalParametrizationQuaternion<DQ_LOCAL>>(), _transformable)
 {
     state_ = Eigen::Quaterniond::Identity().coeffs();
     //
@@ -89,6 +91,12 @@ inline Eigen::VectorXd StateQuaternion::identity() const
     return Eigen::Quaterniond::Identity().coeffs();
 }
 
+inline void StateQuaternion::transform(const VectorComposite& _transformation)
+{
+    if (transformable_)
+        setState((Quaterniond(_transformation.at('O').data()) * Quaterniond(getState().data())).coeffs());
+}
+
 inline StateBlockPtr StateQuaternion::create (const Eigen::VectorXd& _state, bool _fixed)
 {
     MatrixSizeCheck<4,1>::check(_state);
diff --git a/include/core/utils/converter.h b/include/core/utils/converter.h
index 1c53906c6c624ef4e88ac95785fa1871b9b5c122..12a8eee4da7b95af04fc475ac219310f8cecfd20 100644
--- a/include/core/utils/converter.h
+++ b/include/core/utils/converter.h
@@ -28,8 +28,8 @@
 #include "core/state_block/state_composite.h"
 
 // Eigen
-#include <eigen3/Eigen/Dense>
-#include <eigen3/Eigen/Geometry>
+#include <Eigen/Dense>
+#include <Eigen/Geometry>
 
 // std
 #include <regex>
diff --git a/include/core/utils/params_server.h b/include/core/utils/params_server.h
index e5283ccd5b5f669e000fda1904245c99e58955ee..0cde0badc595350bacf33f466abf21ede7b6a67f 100644
--- a/include/core/utils/params_server.h
+++ b/include/core/utils/params_server.h
@@ -51,6 +51,8 @@ public:
 
     void addParams(std::map<std::string, std::string> _params);
 
+    bool hasParam(std::string _key) const;
+
    // template<typename T>
    // T getParam(std::string key, std::string def_value) const {
    //     if(params_.find(key) != params_.end()){
diff --git a/include/core/utils/utils_gtest.h b/include/core/utils/utils_gtest.h
index b89b9102ab64073b729744ec5d6c7d4d8098b143..9c46dd20c96535308e5f851e218641498787a631 100644
--- a/include/core/utils/utils_gtest.h
+++ b/include/core/utils/utils_gtest.h
@@ -19,16 +19,7 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
-/**
- * \file utils_gtest.h
- * \brief Some utils for gtest
- * \author Jeremie Deray
- *  Created on: 26/09/2016
- *  Eigen macros extension by: Joan Sola on 26/04/2017
- */
-
-#ifndef WOLF_UTILS_GTEST_H
-#define WOLF_UTILS_GTEST_H
+#pragma once
 
 #include <gtest/gtest.h>
 
@@ -144,24 +135,58 @@ TEST(Test, Foo)
                }, \
                C_expect, C_actual);
 
-#define EXPECT_QUATERNION_APPROX(C_expect, C_actual, precision) EXPECT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision)
+#define EXPECT_QUATERNION_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   return Eigen::Quaterniond(Eigen::Vector4d(lhs)).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs))) < precision; \
+               }, \
+               Quaterniond(C_expect).coeffs(), Quaterniond(C_actual).coeffs());
 
-#define ASSERT_QUATERNION_APPROX(C_expect, C_actual, precision) ASSERT_MATRIX_APPROX((C_expect).coeffs(), (C_actual).coeffs(), precision)
+#define ASSERT_QUATERNION_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   return Eigen::Quaterniond(Eigen::Vector4d(lhs)).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs))) < precision; \
+               }, \
+               Quaterniond(C_expect).coeffs(), Quaterniond(C_actual).coeffs());
 
-#define EXPECT_POSE2d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \
-                   MatrixXd er = lhs - rhs; \
+#define EXPECT_QUATERNION_VECTOR_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   return Eigen::Quaterniond(Eigen::Vector4d(lhs)).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs))) < precision; \
+               }, \
+               C_expect, C_actual);
+
+#define ASSERT_QUATERNION_VECTOR_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   return Eigen::Quaterniond(Eigen::Vector4d(lhs)).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs))) < precision; \
+               }, \
+               C_expect, C_actual);
+
+#define EXPECT_POSE2d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   assert(lhs.size() == 3 and rhs.size() == 3); \
+                   Eigen::VectorXd er = lhs - rhs; \
                    er(2) = pi2pi((double)er(2)); \
                    return er.isMuchSmallerThan(1, precision); \
                }, \
                C_expect, C_actual);
 
-#define ASSERT_POSE2d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::MatrixXd lhs, const Eigen::MatrixXd rhs) { \
-                   MatrixXd er = lhs - rhs; \
+#define ASSERT_POSE2d_APPROX(C_expect, C_actual, precision) ASSERT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   assert(lhs.size() == 3 and rhs.size() == 3); \
+                   Eigen::VectorXd er = lhs - rhs; \
                    er(2) = pi2pi((double)er(2)); \
                    return er.isMuchSmallerThan(1, precision); \
                }, \
                C_expect, C_actual);
 
-} // namespace testing
+#define EXPECT_POSE3d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   assert(lhs.size() == 7 and rhs.size() == 7); \
+                   Eigen::Vector4d er; \
+                   er.head(3) = lhs.head(3) - rhs.head(3); \
+                   er(3) = Eigen::Quaterniond(Eigen::Vector4d(lhs.tail(4))).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs.tail(4)))); \
+                   return er.isMuchSmallerThan(1, precision); \
+               }, \
+               C_expect, C_actual);
+
+#define ASSERT_POSE3d_APPROX(C_expect, C_actual, precision) EXPECT_PRED2([](const Eigen::VectorXd lhs, const Eigen::VectorXd rhs) { \
+                   assert(lhs.size() == 7 and rhs.size() == 7); \
+                   Eigen::Vector4d er; \
+                   er.head(3) = lhs.head(3) - rhs.head(3); \
+                   er(3) = Eigen::Quaterniond(Eigen::Vector4d(lhs.tail(4))).angularDistance(Eigen::Quaterniond(Eigen::Vector4d(rhs.tail(4)))); \
+                   return er.isMuchSmallerThan(1, precision); \
+               }, \
+               C_expect, C_actual);
 
-#endif /* WOLF_UTILS_GTEST_H */
+} // namespace testing
\ No newline at end of file
diff --git a/include/core/yaml/yaml_conversion.h b/include/core/yaml/yaml_conversion.h
index 76991419a0ec370174055479cbb9dfdb910e0356..8ae7e0384e8aae80cdffc161e4c48e74525df36b 100644
--- a/include/core/yaml/yaml_conversion.h
+++ b/include/core/yaml/yaml_conversion.h
@@ -33,8 +33,8 @@
 #include <yaml-cpp/yaml.h>
 
 // Eigen
-#include <eigen3/Eigen/Dense>
-#include <eigen3/Eigen/Geometry>
+#include <Eigen/Dense>
+#include <Eigen/Geometry>
 
 // stl
 #include <iostream>
diff --git a/prova.txt b/prova.txt
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/capture/capture_base.cpp b/src/capture/capture_base.cpp
index e6d4a1e660bd0664f2fa1196c2ab558840664263..a154cdbb4b62606dc5260310cbfd5b4052bda54c 100644
--- a/src/capture/capture_base.cpp
+++ b/src/capture/capture_base.cpp
@@ -158,13 +158,15 @@ FactorBasePtrList CaptureBase::getFactorList()
 void CaptureBase::getFactorList(FactorBaseConstPtrList& _fac_list) const
 {
     for (auto f_ptr : getFeatureList())
-        f_ptr->getFactorList(_fac_list);
+        if (not f_ptr->isRemoving())
+            f_ptr->getFactorList(_fac_list);
 }
 
 void CaptureBase::getFactorList(FactorBasePtrList& _fac_list)
 {
     for (auto f_ptr : getFeatureList())
-        f_ptr->getFactorList(_fac_list);
+        if (not f_ptr->isRemoving())
+            f_ptr->getFactorList(_fac_list);
 }
 
 FactorBasePtr CaptureBase::addConstrainedBy(FactorBasePtr _fac_ptr)
diff --git a/src/capture/capture_landmarks_external.cpp b/src/capture/capture_landmarks_external.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..154dcc83f488b39a91c69f0906a0f59e2fdd56d7
--- /dev/null
+++ b/src/capture/capture_landmarks_external.cpp
@@ -0,0 +1,53 @@
+//--------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/capture/capture_landmarks_external.h"
+
+namespace wolf{
+
+CaptureLandmarksExternal::CaptureLandmarksExternal(const TimeStamp& _ts, 
+                                                   SensorBasePtr _sensor_ptr, 
+                                                   const std::vector<int>& _ids,
+                                                   const std::vector<Eigen::VectorXd>& _detections, 
+                                                   const std::vector<Eigen::MatrixXd>& _covs, 
+                                                   const std::vector<double>& _qualities) :
+	CaptureBase("CaptureLandmarksExternal", _ts, _sensor_ptr)
+{
+    if (_ids.size() != _detections.size() or 
+        _ids.size() != _covs.size() or 
+        _ids.size() != _qualities.size())
+        throw std::runtime_error("CaptureLandmarksExternal constructor: '_ids', '_detections', '_covs', '_qualities' should have the same size.");
+    
+    for (auto i = 0; i < _detections.size(); i++)
+        addDetection(_ids.at(i), _detections.at(i), _covs.at(i), _qualities.at(i));
+}
+
+CaptureLandmarksExternal::~CaptureLandmarksExternal()
+{
+	//
+}
+
+void CaptureLandmarksExternal::addDetection(const int& _id, const Eigen::VectorXd& _detection, const Eigen::MatrixXd& _cov, const double& _quality)
+{
+    detections_.push_back(LandmarkDetection{_id, _detection, _cov, _quality});
+}
+
+} // namespace wolf
diff --git a/src/factor/factor_base.cpp b/src/factor/factor_base.cpp
index 9ac1675b8b9ddc388f85b7fd6d8d7e34a274e65b..9610063417bd9a283b01d90068abf51e655e2f68 100644
--- a/src/factor/factor_base.cpp
+++ b/src/factor/factor_base.cpp
@@ -141,42 +141,44 @@ void FactorBase::remove(bool viral_remove_empty_parent)
 CaptureBaseConstPtr FactorBase::getCapture() const
 {
     auto ftr = getFeature();
-    if (ftr != nullptr) return ftr->getCapture();
+    if (ftr != nullptr and not ftr->isRemoving()) 
+        return ftr->getCapture();
     else return nullptr;
 }
 
 CaptureBasePtr FactorBase::getCapture()
 {
     auto ftr = getFeature();
-    if (ftr != nullptr) return ftr->getCapture();
+    if (ftr != nullptr and not ftr->isRemoving()) 
+        return ftr->getCapture();
     else return nullptr;
 }
 
 FrameBaseConstPtr FactorBase::getFrame() const
 {
     auto cpt = getCapture();
-    if(cpt != nullptr) return cpt->getFrame();
+    if(cpt != nullptr and not cpt->isRemoving()) return cpt->getFrame();
     else return nullptr;
 }
 
 FrameBasePtr FactorBase::getFrame()
 {
     auto cpt = getCapture();
-    if(cpt != nullptr) return cpt->getFrame();
+    if(cpt != nullptr and not cpt->isRemoving()) return cpt->getFrame();
     else return nullptr;
 }
 
 SensorBaseConstPtr FactorBase::getSensor() const
 {
     auto cpt = getCapture();
-    if(cpt != nullptr) return cpt->getSensor();
+    if(cpt != nullptr and not cpt->isRemoving()) return cpt->getSensor();
     else return nullptr;
 }
 
 SensorBasePtr FactorBase::getSensor()
 {
     auto cpt = getCapture();
-    if(cpt != nullptr) return cpt->getSensor();
+    if(cpt != nullptr and not cpt->isRemoving()) return cpt->getSensor();
     else return nullptr;
 }
 
diff --git a/src/frame/frame_base.cpp b/src/frame/frame_base.cpp
index aae21b6eb2fa9d5552e8212259299b71e0e0c440..390d6d51019aea6a65208707b1b8671194dc060e 100644
--- a/src/frame/frame_base.cpp
+++ b/src/frame/frame_base.cpp
@@ -351,13 +351,15 @@ FactorBasePtrList FrameBase::getFactorList()
 void FrameBase::getFactorList(FactorBaseConstPtrList& _fac_list) const
 {
     for (auto c_ptr : getCaptureList())
-        c_ptr->getFactorList(_fac_list);
+        if (not c_ptr->isRemoving())
+            c_ptr->getFactorList(_fac_list);
 }
 
 void FrameBase::getFactorList(FactorBasePtrList& _fac_list)
 {
     for (auto c_ptr : getCaptureList())
-        c_ptr->getFactorList(_fac_list);
+        if (not c_ptr->isRemoving())
+            c_ptr->getFactorList(_fac_list);
 }
 
 bool FrameBase::hasCapture(const CaptureBaseConstPtr& _capture) const
diff --git a/src/landmark/landmark_base.cpp b/src/landmark/landmark_base.cpp
index e7acd4f0d90ef0883eea1af949928b0458e25a44..a47ecce3e0a77a90bc0a2fe97de18c7860ca472a 100644
--- a/src/landmark/landmark_base.cpp
+++ b/src/landmark/landmark_base.cpp
@@ -23,9 +23,10 @@
 #include "core/landmark/landmark_base.h"
 #include "core/factor/factor_base.h"
 #include "core/map/map_base.h"
-#include "core/state_block/state_block.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/state_block/state_angle.h"
 #include "core/state_block/state_quaternion.h"
+#include "core/state_block/factory_state_block.h"
 #include "core/common/factory.h"
 #include "core/yaml/yaml_conversion.h"
 
@@ -144,6 +145,7 @@ YAML::Node LandmarkBase::saveToYaml() const
         node["orientation"] = getO()->getState();
         node["orientation fixed"] = getO()->isFixed();
     }
+    node["transformable"] = getP()->isTransformable();
     return node;
 }
 
@@ -216,7 +218,7 @@ LandmarkBasePtr LandmarkBase::create(const YAML::Node& _node)
     Eigen::VectorXd pos         = _node["position"]         .as< Eigen::VectorXd  >();
     bool            pos_fixed   = _node["position fixed"]   .as< bool >();
 
-    StateBlockPtr pos_sb = std::make_shared<StateBlock>(pos, pos_fixed);
+    StateBlockPtr pos_sb = FactoryStateBlock::create("P", pos, pos_fixed);
     StateBlockPtr ori_sb = nullptr;
 
     if (_node["orientation"])
diff --git a/src/map/map_base.cpp b/src/map/map_base.cpp
index 66367305a0b9ad82e9ef5987c3bdacb9e9a1eeea..0c002934af723fd242babeb22cabd4de15823e9f 100644
--- a/src/map/map_base.cpp
+++ b/src/map/map_base.cpp
@@ -65,21 +65,46 @@ void MapBase::removeLandmark(LandmarkBasePtr _landmark_ptr)
     landmark_list_.remove(_landmark_ptr);
 }
 
-void MapBase::load(const std::string& _map_file_dot_yaml)
+LandmarkBaseConstPtr MapBase::getLandmark(const unsigned int& _id) const
 {
-    YAML::Node map = YAML::LoadFile(_map_file_dot_yaml);
+    auto lmk_it = std::find_if(landmark_list_.begin(),
+                               landmark_list_.end(),
+                               [&](LandmarkBaseConstPtr lmk)
+                               {
+                                   return lmk->id() == _id;
+                               }); // lambda function for the find_if
+
+    if (lmk_it == landmark_list_.end())
+        return nullptr;
+
+    return (*lmk_it);
+}
+
+LandmarkBasePtr MapBase::getLandmark(const unsigned int& _id)
+{
+    auto lmk_it = std::find_if(landmark_list_.begin(),
+                               landmark_list_.end(),
+                               [&](LandmarkBasePtr lmk)
+                               {
+                                   return lmk->id() == _id;
+                               }); // lambda function for the find_if
 
-    unsigned int nlandmarks = map["nlandmarks"].as<unsigned int>();
+    if (lmk_it == landmark_list_.end())
+        return nullptr;
 
-    assert(nlandmarks == map["landmarks"].size() && "Number of landmarks in map file does not match!");
+    return (*lmk_it);
+}
 
-    for (unsigned int i = 0; i < nlandmarks; i++)
+void MapBase::load(const std::string& _map_file_dot_yaml)
+{
+    YAML::Node map = YAML::LoadFile(_map_file_dot_yaml);
+
+    for (unsigned int i = 0; i < map["landmarks"].size(); i++)
     {
         YAML::Node lmk_node = map["landmarks"][i];
         LandmarkBasePtr lmk_ptr = FactoryLandmark::create(lmk_node["type"].as<std::string>(), lmk_node);
         lmk_ptr->link(shared_from_this());
     }
-
 }
 
 void MapBase::save(const std::string& _map_file_yaml, const std::string& _map_name)
@@ -87,10 +112,8 @@ void MapBase::save(const std::string& _map_file_yaml, const std::string& _map_na
     YAML::Emitter emitter;
 
     emitter << YAML::BeginMap;
-    emitter << "map name"   << _map_name;
-    emitter << "date-time" << dateTimeNow(); // Get date and time for archiving purposes
-
-    emitter << "nlandmarks" << getLandmarkList().size();
+    emitter << "map_name"   << _map_name;
+    emitter << "date_time" << dateTimeNow(); // Get date and time for archiving purposes
 
     emitter << "landmarks"  << YAML::BeginSeq;
 
diff --git a/src/problem/problem.cpp b/src/problem/problem.cpp
index 6fe2afb34530e550d8a361e56bcaae2c07938349..e95ce825308eefa1b4c3295e60e2abffd2cc4847 100644
--- a/src/problem/problem.cpp
+++ b/src/problem/problem.cpp
@@ -44,7 +44,8 @@ Problem::Problem(const std::string& _frame_structure, SizeEigen _dim, MapBasePtr
         map_ptr_(_map),
         motion_provider_map_(),
         frame_structure_(_frame_structure),
-        prior_options_(std::make_shared<PriorOptions>())
+        prior_options_(std::make_shared<PriorOptions>()),
+        transformed_(false)
 {
     dim_ = _dim;
     if (_frame_structure == "PO" and _dim == 2)
@@ -104,26 +105,6 @@ ProblemPtr Problem::autoSetup(ParamsServer &_server)
     if (plugins_path.back() != '/'){
         plugins_path += '/';  // only works for UNIX systems
     }
-    /*try
-    {
-        plugins_path = _server.getParam<std::string>("plugins_path");
-    }
-    catch (MissingValueException& e)
-    {
-      WOLF_WARN(e.what());
-      WOLF_WARN("Setting '/usr/local/lib/' as plugins path...");
-      plugins_path="/usr/local/lib/";
-    }*/
-    for (auto plugin_name : _server.getParam<std::vector<std::string>>("plugins"))
-    {
-      if (plugin_name == "core" or plugin_name == "wolf" or plugin_name == "") continue; // ignore plugin "core"
-
-      std::string plugin = plugins_path + "libwolf" + plugin_name + lib_extension;
-      WOLF_TRACE("Loading plugin " + plugin_name + " via " + plugin);
-      auto l = std::make_shared<LoaderRaw>(plugin);
-      l->load();
-      loaders.push_back(l);
-    }
 
     // load raw libs
     std::vector<std::string> raw_libs;
@@ -143,20 +124,35 @@ ProblemPtr Problem::autoSetup(ParamsServer &_server)
         loaders.push_back(l);
     }
 
-    // Install sensors and processors
+    // Install sensors and processors -- load plugins only if sensors and processor are not registered
     auto sensors        = _server.getParam<std::vector<std::map<std::string, std::string>>>("sensors");
-    for(auto sen : sensors)
-        problem->installSensor(sen["type"],
-                               sen["name"],
-                               _server);
-
-    auto processors     = _server.getParam<std::vector<std::map<std::string, std::string>>>("processors");
-    for(auto prc : processors)
-        problem->installProcessor(prc["type"],
-                                  prc["name"],
-                                  prc["sensor_name"],
-                                  _server);
-
+    for (auto sen : sensors)
+    {
+        if (not AutoConfFactorySensor::isCreatorRegistered(sen["type"]))
+        {
+            auto        plugin_name = sen["plugin"];
+            std::string plugin      = plugins_path + "libwolf" + plugin_name + lib_extension;
+            WOLF_TRACE("Loading plugin " + plugin_name + " via " + plugin);
+            auto l = std::make_shared<LoaderRaw>(plugin);
+            l->load();
+            loaders.push_back(l);
+        }
+        problem->installSensor(sen["type"], sen["name"], _server);
+    }
+    auto processors = _server.getParam<std::vector<std::map<std::string, std::string>>>("processors");
+    for (auto prc : processors)
+    {
+        if (not AutoConfFactoryProcessor::isCreatorRegistered(prc["type"]))
+        {
+            auto        plugin_name = prc["plugin"];
+            std::string plugin      = plugins_path + "libwolf" + plugin_name + lib_extension;
+            WOLF_TRACE("Loading plugin " + plugin_name + " via " + plugin);
+            auto l = std::make_shared<LoaderRaw>(plugin);
+            l->load();
+            loaders.push_back(l);
+        }
+        problem->installProcessor(prc["type"], prc["name"], prc["sensor_name"], _server);
+    }
 
     // Map (optional)
     std::string map_type, map_plugin;
@@ -176,6 +172,11 @@ ProblemPtr Problem::autoSetup(ParamsServer &_server)
     auto map = AutoConfFactoryMap::create(map_type, _server);
     map->setProblem(problem);
     problem->setMap(map);
+    // load map from file (optional)
+    if (_server.hasParam("map/filename"))
+    {
+        problem->loadMap(_server.getParam<std::string>("map/filename"));
+    }
 
     // Tree manager
     std::string tree_manager_type = _server.getParam<std::string>("problem/tree_manager/type");
@@ -586,7 +587,7 @@ VectorComposite Problem::getOdometry(const StateStructure& _structure) const
         }
     }
 
-    // check for empty blocks and fill them with the the prior, or with zeros in the worst case
+    // check for empty blocks and fill them with the prior, or with zeros in the worst case
 
     VectorComposite state_last;
 
@@ -1044,14 +1045,14 @@ void Problem::setPriorOptions(const std::string& _mode,
     assert((_mode == "nothing" || _mode == "initial_guess" || _mode == "fix" || _mode == "factor") && "wrong _mode value, it should be: 'nothing', 'initial_guess', 'fix' or 'factor'");
 
     // Store options (optionals depending on the mode)
-    WOLF_TRACE("prior mode:           ", _mode);
+    WOLF_DEBUG("prior mode:           ", _mode);
     prior_options_->mode = _mode;
 
     if (prior_options_->mode != "nothing")
     {
         assert(_state.includesStructure(frame_structure_) && "any missing key in prior state");
 
-        WOLF_TRACE("prior state:          ", _state);
+        WOLF_DEBUG("prior state:          ", _state);
         prior_options_->state = _state;
 
         if (prior_options_->mode == "factor")
@@ -1073,7 +1074,7 @@ void Problem::setPriorOptions(const std::string& _mode,
                 const auto& cov_blk = (sig_blk.array() * sig_blk.array()).matrix().asDiagonal();
                 Q.emplace(key,key,cov_blk);
             }
-            WOLF_TRACE("prior covariance:"    , Q);
+            WOLF_DEBUG("prior covariance:"    , Q);
             prior_options_->cov = Q;
         }
     }
@@ -1260,4 +1261,42 @@ void Problem::perturb(double amplitude)
         L->perturb(amplitude);
 }
 
+void Problem::transform(const VectorComposite& _transformation)
+{
+    std::lock_guard<std::mutex> lock_transform (mut_transform_); // Protect especially from SolverManager::update()
+
+    transformation_ = _transformation;
+    transformed_.store(true);
+
+    // Sensors
+    for (auto S : getHardware()->getSensorList())
+        S->transform(_transformation);
+
+    // Frames and Captures
+    for (auto F : getTrajectory()->getFrameMap())
+    {
+        F.second->transform(_transformation);
+        for (auto& C : F.second->getCaptureList())
+            C->transform(_transformation);
+    }
+
+    // Landmarks
+    for (auto L : getMap()->getLandmarkList())
+        L->transform(_transformation);
+}
+
+bool Problem::isTransformed() const
+{
+    return transformed_.load();
+}
+void Problem::resetTransformed()
+{
+    transformed_.store(false);
+}
+VectorComposite Problem::getTransformation() const
+{
+    std::lock_guard<std::mutex> lock(mut_transform_);
+    return transformation_;
+}
+
 } // namespace wolf
diff --git a/src/processor/motion_buffer.cpp b/src/processor/motion_buffer.cpp
index f61c1b1f9ef7ba4d4f3609fed41fedef53e3cbb8..6afd1c5c76c9c838e3072711b69ca199da0a48b9 100644
--- a/src/processor/motion_buffer.cpp
+++ b/src/processor/motion_buffer.cpp
@@ -144,14 +144,14 @@ void MotionBuffer::split(const TimeStamp& _ts, MotionBuffer& _buffer_part_before
 }
 
 
-void MotionBuffer::print(bool show_data, bool show_delta, bool show_delta_int, bool show_jacs)
+void MotionBuffer::print(bool show_data, bool show_delta, bool show_delta_int, bool show_jacs, bool show_covs)
 {
     using std::cout;
     using std::endl;
 
-    if (!show_data && !show_delta && !show_delta_int && !show_jacs)
+    if (!show_data && !show_delta && !show_delta_int && !show_jacs && !show_covs)
     {
-        cout << "Buffer state [" << this->size() << "] : <";
+        cout << "Buffer size: " << this->size() << " ; time stamps: <";
         for (Motion mot : *this)
             cout << " " << mot.ts_;
         cout << " >" << endl;
@@ -165,17 +165,20 @@ void MotionBuffer::print(bool show_data, bool show_delta, bool show_delta_int, b
             if (show_data)
             {
                 cout << "   data: " << mot.data_.transpose() << endl;
-                cout << "   data cov: \n" << mot.data_cov_ << endl;
+                if (show_covs)
+                    cout << "   data cov: \n" << mot.data_cov_ << endl;
             }
             if (show_delta)
             {
                 cout << "   delta: " << mot.delta_.transpose() << endl;
-                cout << "   delta cov: \n" << mot.delta_cov_ << endl;
+                if (show_covs)
+                    cout << "   delta cov: \n" << mot.delta_cov_ << endl;
             }
             if (show_delta_int)
             {
                 cout << "   delta integrated: " << mot.delta_integr_.transpose() << endl;
-                cout << "   delta integrated cov: \n" << mot.delta_integr_cov_ << endl;
+                if (show_covs)
+                    cout << "   delta integrated cov: \n" << mot.delta_integr_cov_ << endl;
             }
             if (show_jacs)
             {
diff --git a/src/processor/motion_provider.cpp b/src/processor/motion_provider.cpp
index e2723db61a38f9ee6b98a362eeafac8289875e03..d3a7db55582378db0621f029e8cdf16c7f677355 100644
--- a/src/processor/motion_provider.cpp
+++ b/src/processor/motion_provider.cpp
@@ -37,8 +37,18 @@ void MotionProvider::addToProblem(ProblemPtr _prb_ptr, MotionProviderPtr _motion
 
 void MotionProvider::setOdometry(const VectorComposite& _odom)
 {
+    std::lock_guard<std::mutex> lock(mut_odometry_);
+
     assert(_odom.includesStructure(state_structure_) && "MotionProvider::setOdometry(): any key missing in _odom.");
 
     for (auto key : state_structure_)
         odometry_[key] = _odom.at(key); //overwrite/insert only keys of the state_structure_
 }
+
+wolf::VectorComposite MotionProvider::getOdometry ( ) const
+{
+    std::lock_guard<std::mutex> lock(mut_odometry_);
+
+    return odometry_;
+}
+
diff --git a/src/processor/processor_diff_drive.cpp b/src/processor/processor_diff_drive.cpp
index db06d0078261369000aed77e8d8f4d290cd2a355..310fb5f107e46cd416a3dff9c7f9a84abd6fe334 100644
--- a/src/processor/processor_diff_drive.cpp
+++ b/src/processor/processor_diff_drive.cpp
@@ -166,28 +166,22 @@ CaptureMotionPtr ProcessorDiffDrive::emplaceCapture(const FrameBasePtr& _frame_o
     return cap_motion;
 }
 
-FeatureBasePtr ProcessorDiffDrive::emplaceFeature(CaptureMotionPtr _capture_motion)
+void ProcessorDiffDrive::emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own)
 {
-    auto key_feature_ptr = FeatureBase::emplace<FeatureDiffDrive>(_capture_motion,
-                                                               _capture_motion->getBuffer().back().delta_integr_,
-                                                               _capture_motion->getBuffer().back().delta_integr_cov_,
-                                                               _capture_motion->getCalibrationPreint(),
-                                                               _capture_motion->getBuffer().back().jacobian_calib_);
-
-    return key_feature_ptr;
+    auto feature = FeatureBase::emplace<FeatureDiffDrive>(_capture_own,
+                                                          _capture_own->getBuffer().back().delta_integr_,
+                                                          _capture_own->getBuffer().back().delta_integr_cov_,
+                                                          _capture_own->getCalibrationPreint(),
+                                                          _capture_own->getBuffer().back().jacobian_calib_);
+
+    auto ftr_motion = std::static_pointer_cast<FeatureMotion>(feature);
+    FactorBase::emplace<FactorDiffDrive>(ftr_motion,
+                                         ftr_motion,
+                                         _capture_origin,
+                                         shared_from_this(),
+                                         params_prc_diff_drv_selfcal_->apply_loss_function);
 }
 
-FactorBasePtr ProcessorDiffDrive::emplaceFactor(FeatureBasePtr _feature,
-                                                CaptureBasePtr _capture_origin)
-{
-    auto ftr_motion = std::static_pointer_cast<FeatureMotion>(_feature);
-    auto fac_odom = FactorBase::emplace<FactorDiffDrive>(ftr_motion,
-                                                         ftr_motion,
-                                                         _capture_origin,
-                                                         shared_from_this(),
-                                                         params_prc_diff_drv_selfcal_->apply_loss_function);
-    return fac_odom;
-}
 
 } /* namespace wolf */
 
diff --git a/src/processor/processor_landmark_external.cpp b/src/processor/processor_landmark_external.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2ac3ff66ae6d3ed22787b7b8166cf9c4c5b8965a
--- /dev/null
+++ b/src/processor/processor_landmark_external.cpp
@@ -0,0 +1,497 @@
+//--------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/processor/processor_landmark_external.h"
+#include "core/capture/capture_landmarks_external.h"
+#include "core/feature/feature_base.h"
+#include "core/factor/factor_relative_pose_2d_with_extrinsics.h"
+#include "core/factor/factor_relative_position_2d_with_extrinsics.h"
+#include "core/factor/factor_relative_pose_3d_with_extrinsics.h"
+#include "core/factor/factor_relative_position_3d_with_extrinsics.h"
+#include "core/landmark/landmark_base.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_quaternion.h"
+#include "core/state_block/state_angle.h"
+#include "core/math/SE2.h"
+#include "core/math/SE3.h"
+
+using namespace Eigen;
+
+namespace wolf
+{
+
+void ProcessorLandmarkExternal::preProcess()
+{
+    assert(new_features_incoming_.empty());
+
+    auto dim = getProblem()->getDim();
+    auto cap_landmarks = std::dynamic_pointer_cast<CaptureLandmarksExternal>(incoming_ptr_);
+    if (not cap_landmarks)
+        throw std::runtime_error("ProcessorLandmarkExternal::preProcess incoming_ptr_ should be of type 'CaptureLandmarksExternal'");
+
+    // Extract features from capture detections
+    auto landmark_detections = cap_landmarks->getDetections();
+    std::set<int> ids;
+    for (auto detection : landmark_detections)
+    {
+        WOLF_WARN_COND(ids.count(detection.id), "ProcessorLandmarkExternal::preProcess: detection with repeated id, discarding...");
+
+        // Filter by quality
+        if (detection.quality < params_tfle_->filter_quality_th or ids.count(detection.id))
+            continue;
+
+        // measure and covariance
+        VectorXd meas;
+        MatrixXd cov;
+        if (not params_tfle_->use_orientation)
+        {
+            assert(detection.measure.size() >= dim);
+            assert(detection.covariance.rows() >= dim and detection.covariance.rows() == detection.covariance.cols());
+
+            meas = detection.measure.head(dim);
+            cov  = detection.covariance.topLeftCorner(dim,dim);
+        }
+        else
+        {
+            assert(detection.measure.size() == (dim == 2 ? 3 : 7));
+            assert(detection.covariance.rows() == (dim == 2 ? 3 : 6) and detection.covariance.rows() == detection.covariance.cols());
+
+            meas = detection.measure;
+            cov  = detection.covariance;
+        }
+
+        // emplace feature
+        FeatureBasePtr ftr = FeatureBase::emplace<FeatureBase>(cap_landmarks,
+                                                               "FeatureLandmarkExternal",
+                                                               meas,
+                                                               cov);
+        ftr->setLandmarkId(detection.id);
+
+        if (detection.id != -1 and detection.id != 0)
+            ids.insert(detection.id);
+
+        //unmatched_detections_incoming_.push_back(ftr);
+
+        // add new feature
+        new_features_incoming_.push_back(ftr);
+    }
+    WOLF_INFO("ProcessorLandmarkExternal::preprocess: found ", new_features_incoming_.size(), " new features");
+}
+
+unsigned int ProcessorLandmarkExternal::processKnown()
+{
+    // clear list of known features
+    known_features_incoming_.clear();
+
+    // Track features from last_ptr_ to incoming_ptr_
+    if (last_ptr_)
+    {
+        WOLF_INFO("Searching ", known_features_last_.size(), " tracked features...");
+        auto pose_sen = getSensor()->getState("PO");
+        auto pose_in  = getProblem()->getState(last_ptr_->getTimeStamp(), "PO");
+        auto pose_out = getProblem()->getState(incoming_ptr_->getTimeStamp(), "PO");
+
+        for (auto feat_last : known_features_last_)
+        {
+            auto feat_candidate_it = new_features_incoming_.begin();
+            while (feat_candidate_it != new_features_incoming_.end())
+            {
+                if ((*feat_candidate_it)->landmarkId() == feat_last->landmarkId() and 
+                    detectionDistance(feat_last, (*feat_candidate_it), pose_in, pose_out, pose_sen) < params_tfle_->match_dist_th)
+                {
+                    // grow track
+                    track_matrix_.add(feat_last, *feat_candidate_it);
+
+                    // feature is known
+                    known_features_incoming_.push_back((*feat_candidate_it));
+
+                    // remove from unmatched
+                    feat_candidate_it = new_features_incoming_.erase(feat_candidate_it);
+                    break;
+                }
+                else
+                    feat_candidate_it++;
+            }
+        }
+        WOLF_INFO("Tracked ", known_features_incoming_.size(), " features.");
+    }
+
+    // Add new features (not tracked) as known features
+    WOLF_INFO_COND(not new_features_incoming_.empty(), "Adding new features ", new_features_incoming_.size());
+    while (not new_features_incoming_.empty())
+    {
+        auto ftr = new_features_incoming_.front();
+
+        // new track
+        track_matrix_.newTrack(ftr);
+
+        // feature is now known
+        known_features_incoming_.push_back(ftr);
+
+        // remove from unmatched
+        new_features_incoming_.pop_front();
+    }
+
+    // check all features have been emplaced
+    assert(std::all_of(known_features_incoming_.begin(), known_features_incoming_.end(), [](FeatureBasePtr f){return f->getCapture() != nullptr;}) &&
+           "any not linked feature returned by trackFeatures()");
+
+    return known_features_incoming_.size();
+}
+
+double ProcessorLandmarkExternal::detectionDistance(FeatureBasePtr _ftr1,
+                                                                  FeatureBasePtr _ftr2,
+                                                                  const VectorComposite& _pose1,
+                                                                  const VectorComposite& _pose2,
+                                                                  const VectorComposite& _pose_sen) const
+{
+    // Any not available info of poses, assume identity
+    if (not _pose1.includesStructure("PO") or not _pose2.includesStructure("PO") or not _pose_sen.includesStructure("PO"))
+    {
+        if (getProblem()->getDim() == 2)
+            return (_ftr1->getMeasurement().head<2>() - _ftr2->getMeasurement().head<2>()).norm();
+        else
+            return (_ftr1->getMeasurement().head<3>() - _ftr2->getMeasurement().head<3>()).norm();
+    }
+    else
+    {
+        if (getProblem()->getDim() == 2)
+        {
+            auto pose_s1 = SE2::compose(_pose1, _pose_sen);
+            auto pose_s2 = SE2::compose(_pose2, _pose_sen);
+            auto p1 = pose_s1.at('P') + Rotation2Dd(pose_s1.at('O')(0)) * _ftr1->getMeasurement().head<2>();
+            auto p2 = pose_s2.at('P') + Rotation2Dd(pose_s2.at('O')(0)) * _ftr2->getMeasurement().head<2>();
+            return (p1-p2).norm();
+        }
+        else
+        {
+            auto pose_s1 = SE3::compose(_pose1, _pose_sen);
+            auto pose_s2 = SE3::compose(_pose2, _pose_sen);
+            auto p1 = pose_s1.at('P') + Quaterniond(Vector4d(pose_s1.at('O'))) * _ftr1->getMeasurement().head<3>();
+            auto p2 = pose_s2.at('P') + Quaterniond(Vector4d(pose_s2.at('O'))) * _ftr2->getMeasurement().head<3>();
+            return (p1-p2).norm();
+        }
+    }
+}
+
+bool ProcessorLandmarkExternal::voteForKeyFrame() const
+{
+    auto track_ids_last = track_matrix_.trackIds(last_ptr_);
+
+    WOLF_INFO("Active feature tracks: " , track_ids_last.size() );
+    
+    // no tracks longer than filter_track_length_th
+    auto n_tracks = 0;
+    auto n_new_tracks = 0;
+    for (auto track_id : track_ids_last)
+    {
+        if (track_matrix_.trackSize(track_id) >= params_tfle_->filter_track_length_th)
+        {
+            n_tracks++;
+            if (not lmks_ids_origin_.count(track_matrix_.feature(track_id, last_ptr_)->landmarkId()))
+                n_new_tracks++;
+        }
+    }
+    
+    // Necessary condition: active valid tracks
+    bool vote_min_features = n_tracks >= params_tfle_->min_features_for_keyframe;
+    WOLF_INFO("vote_min_features: ", vote_min_features,
+              " - Active feature tracks longer than ", params_tfle_->filter_track_length_th, ": ", n_tracks,
+              " (should be equal or bigger than ", params_tfle_->min_features_for_keyframe, ")");
+
+    bool vote_new_features(true), vote_time_span(true);
+    if (vote_min_features and origin_ptr_)
+    {
+        // Sufficient condition: new valid tracks
+        vote_new_features = n_new_tracks >= params_tfle_->new_features_for_keyframe;
+        WOLF_INFO("vote_new_features: " , vote_new_features, 
+                  " - n_new_tracks = ", n_new_tracks,
+                  " (should be equal or bigger than ", params_tfle_->new_features_for_keyframe, ")");
+
+        // Sufficient condition: time span
+        vote_time_span = last_ptr_->getTimeStamp() - origin_ptr_->getTimeStamp() > params_tfle_->time_span;
+        WOLF_INFO("vote_time_span: " , vote_time_span, 
+                  " - time_span = ", last_ptr_->getTimeStamp() - origin_ptr_->getTimeStamp(), 
+                  " (should be bigger than ", params_tfle_->time_span, ")");
+    }
+
+    bool vote = vote_min_features and (vote_new_features or vote_time_span);
+
+    WOLF_INFO( (vote ? "Vote ": "Do NOT vote ") , "for KF" );
+
+    return vote;
+}
+
+void ProcessorLandmarkExternal::establishFactors()
+{
+    if (origin_ptr_ == last_ptr_)
+        return;
+
+    // reset n_tracks_origin_
+    lmks_ids_origin_.clear();
+
+    // will emplace a factor (and landmark if needed) for each known feature in last with long tracks (params_tfle_->filter_track_length_th)
+    FactorBasePtrList fac_list;
+    auto track_ids_last = track_matrix_.trackIds(last_ptr_);
+
+    for (auto track_id : track_ids_last)
+    {
+        // check track length
+        if (track_matrix_.trackSize(track_id) < params_tfle_->filter_track_length_th)
+            continue;
+
+        auto feature = track_matrix_.feature(track_id, last_ptr_);
+
+        // get landmark
+        LandmarkBasePtr lmk = getProblem()->getMap()->getLandmark(feature->landmarkId());
+        
+        // emplace landmark
+        if (not lmk)
+            lmk = emplaceLandmark(feature);
+
+        // modify landmark
+        modifyLandmark(lmk, feature);
+
+        // emplace factor
+        auto fac = emplaceFactor(feature, lmk);
+
+        lmks_ids_origin_.insert(lmk->id());
+
+        if (fac)
+            fac_list.push_back(fac);
+    }
+
+    WOLF_INFO("ProcessorLandmarkExternal::establishFactors: emplaced ", fac_list.size(), " factors!");
+}
+
+FactorBasePtr ProcessorLandmarkExternal::emplaceFactor(FeatureBasePtr _feature, LandmarkBasePtr _landmark)
+{
+    assert(_feature);
+    assert(_landmark);
+    
+    // 2D - Relative Position
+    if (getProblem()->getDim() == 2 and (_feature->getMeasurement().size() == 2 or not params_tfle_->use_orientation))
+    {
+        return FactorBase::emplace<FactorRelativePosition2dWithExtrinsics>(_feature, 
+                                                                           _feature,
+                                                                           _landmark,
+                                                                           shared_from_this(),
+                                                                           params_tfle_->apply_loss_function);
+    }
+    // 2D - Relative Pose
+    else if (getProblem()->getDim() == 2 and _feature->getMeasurement().size() == 3 and params_tfle_->use_orientation)
+    {
+        return FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(_feature,
+                                                                       _feature,
+                                                                       _landmark, 
+                                                                       shared_from_this(),
+                                                                       params_tfle_->apply_loss_function);
+    }
+    // 3D - Relative Position
+    else if (getProblem()->getDim() == 3 and (_feature->getMeasurement().size() == 3 or not params_tfle_->use_orientation))
+    {
+        return FactorBase::emplace<FactorRelativePosition3dWithExtrinsics>(_feature, 
+                                                                           _feature,
+                                                                           _landmark,
+                                                                           shared_from_this(),
+                                                                           params_tfle_->apply_loss_function);
+    }
+    // 3D - Relative Pose
+    else if (getProblem()->getDim() == 3 and _feature->getMeasurement().size() == 7 and params_tfle_->use_orientation)
+    {
+        return FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(_feature, 
+                                                                       _feature, 
+                                                                       _landmark, 
+                                                                       shared_from_this(),
+                                                                       params_tfle_->apply_loss_function);
+    }
+    else 
+        throw std::runtime_error("ProcessorLandmarkExternal::emplaceFactor unknown case");
+
+    // not reachable
+    return nullptr;
+
+}
+        
+LandmarkBasePtr ProcessorLandmarkExternal::emplaceLandmark(FeatureBasePtr _feature)
+{
+    assert(_feature);
+    assert(_feature->getCapture());
+    assert(_feature->getCapture()->getFrame());
+    assert(_feature->getCapture()->getSensorP());
+    assert(_feature->getCapture()->getSensorO());
+    assert(getProblem());
+    assert(getProblem()->getMap());
+
+    if (getProblem()->getMap()->getLandmark(_feature->landmarkId()) != nullptr)
+        throw std::runtime_error("ProcessorLandmarkExternal::emplaceLandmark: attempting to create an existing landmark");
+
+    LandmarkBasePtr lmk;
+
+    // 2D - Relative Position
+    if (getProblem()->getDim() == 2 and (_feature->getMeasurement().size() == 2 or not params_tfle_->use_orientation))
+    {
+        Vector2d frm_p = _feature->getCapture()->getFrame()->getP()->getState();
+        Vector2d sen_p = _feature->getCapture()->getSensorP()->getState();
+        double frm_o   = _feature->getCapture()->getFrame()->getO()->getState()(0);
+        double sen_o   = _feature->getCapture()->getSensorO()->getState()(0);
+        
+        Vector2d lmk_p = frm_p + Rotation2Dd(frm_o) * (sen_p + Rotation2Dd(sen_o) * _feature->getMeasurement());
+
+        lmk = LandmarkBase::emplace<LandmarkBase>(getProblem()->getMap(),
+                                                  "LandmarkRelativePosition2d",
+                                                  std::make_shared<StatePoint2d>(lmk_p), 
+                                                  nullptr);
+        lmk->setId(_feature->landmarkId());
+    }
+    // 2D - Relative Pose
+    else if (getProblem()->getDim() == 2 and _feature->getMeasurement().size() == 3 and params_tfle_->use_orientation)
+    {
+        Vector2d frm_p = _feature->getCapture()->getFrame()->getP()->getState();
+        Vector2d sen_p = _feature->getCapture()->getSensorP()->getState();
+        double frm_o   = _feature->getCapture()->getFrame()->getO()->getState()(0);
+        double sen_o   = _feature->getCapture()->getSensorO()->getState()(0);
+
+        Vector2d lmk_p = frm_p + Rotation2Dd(frm_o) * (sen_p + Rotation2Dd(sen_o) * _feature->getMeasurement().head<2>());
+        double lmk_o   = frm_o + sen_o + _feature->getMeasurement()(2);
+
+        lmk = LandmarkBase::emplace<LandmarkBase>(getProblem()->getMap(),
+                                                  "LandmarkRelativePose2d",
+                                                  std::make_shared<StatePoint2d>(lmk_p),
+                                                  std::make_shared<StateAngle>(lmk_o));
+        lmk->setId(_feature->landmarkId());
+    }
+    // 3D - Relative Position
+    else if (getProblem()->getDim() == 3 and (_feature->getMeasurement().size() == 3 or not params_tfle_->use_orientation))
+    {
+        Vector3d frm_p    = _feature->getCapture()->getFrame()->getP()->getState();
+        Vector3d sen_p    = _feature->getCapture()->getSensorP()->getState();
+        Quaterniond frm_o = Quaterniond(Vector4d(_feature->getCapture()->getFrame()->getO()->getState()));
+        Quaterniond sen_o = Quaterniond(Vector4d(_feature->getCapture()->getSensorO()->getState()));
+
+        Vector3d lmk_p = frm_p + frm_o * (sen_p + sen_o * _feature->getMeasurement());
+
+        lmk = LandmarkBase::emplace<LandmarkBase>(getProblem()->getMap(),
+                                                  "LandmarkRelativePosition3d",
+                                                  std::make_shared<StatePoint3d>(lmk_p),
+                                                  nullptr);
+        lmk->setId(_feature->landmarkId());
+    }
+    // 3D - Relative Pose
+    else if (getProblem()->getDim() == 3 and _feature->getMeasurement().size() == 7 and params_tfle_->use_orientation)
+    {
+        Vector3d frm_p    = _feature->getCapture()->getFrame()->getP()->getState();
+        Vector3d sen_p    = _feature->getCapture()->getSensorP()->getState();
+        Quaterniond frm_o = Quaterniond(Vector4d(_feature->getCapture()->getFrame()->getO()->getState()));
+        Quaterniond sen_o = Quaterniond(Vector4d(_feature->getCapture()->getSensorO()->getState()));
+
+        Vector3d lmk_p    = frm_p + frm_o * (sen_p + sen_o * _feature->getMeasurement().head<3>());
+        Quaterniond lmk_o = frm_o * sen_o * Quaterniond(_feature->getMeasurement().tail<4>());
+
+        lmk = LandmarkBase::emplace<LandmarkBase>(getProblem()->getMap(),
+                                                  "LandmarkRelativePose3d",
+                                                  std::make_shared<StatePoint3d>(lmk_p),
+                                                  std::make_shared<StateQuaternion>(lmk_o));
+        lmk->setId(_feature->landmarkId());
+    }
+    else 
+        throw std::runtime_error("ProcessorLandmarkExternal::emplaceLandmark unknown case");
+
+    return lmk;
+}
+
+void ProcessorLandmarkExternal::modifyLandmark(LandmarkBasePtr _landmark,
+                                               FeatureBasePtr _feature)
+{
+    assert(_feature);
+    assert(_feature->getCapture());
+    assert(_feature->getCapture()->getFrame());
+    assert(_feature->getCapture()->getSensorP());
+    assert(_feature->getCapture()->getSensorO());
+    assert(getProblem());
+    
+    if (not _landmark)
+        throw std::runtime_error("ProcessorLandmarkExternal::modifyLandmark: null landmark");
+
+    // 2D - Relative Position
+    if (getProblem()->getDim() == 2 and (_feature->getMeasurement().size() == 2 or not params_tfle_->use_orientation))
+    {
+        // nothing to do (we assume P already in landmark)
+        return;
+    }
+    // 2D - Relative Pose
+    else if (getProblem()->getDim() == 2 and _feature->getMeasurement().size() == 3 and params_tfle_->use_orientation)
+    {
+        // no orientation, add it
+        if (not _landmark->getO())
+        {
+            double frm_o = _feature->getCapture()->getFrame()->getO()->getState()(0);
+            double sen_o = _feature->getCapture()->getSensorO()->getState()(0);
+
+            double lmk_o = frm_o + sen_o + _feature->getMeasurement()(2);
+
+            _landmark->addStateBlock('O', std::make_shared<StateAngle>(lmk_o), getProblem());
+        }
+    }
+    // 3D - Relative Position
+    else if (getProblem()->getDim() == 3 and (_feature->getMeasurement().size() == 3 or not params_tfle_->use_orientation))
+    {
+        // nothing to do (we assume P already in landmark)
+        return;
+    }
+    // 3D - Relative Pose
+    else if (getProblem()->getDim() == 3 and _feature->getMeasurement().size() == 7 and params_tfle_->use_orientation)
+    {
+        // no orientation, add it
+        if (not _landmark->getO())
+        {
+            Quaterniond frm_o = Quaterniond(Vector4d(_feature->getCapture()->getFrame()->getO()->getState()));
+            Quaterniond sen_o = Quaterniond(Vector4d(_feature->getCapture()->getSensorO()->getState()));
+
+            Quaterniond lmk_o = frm_o * sen_o * Quaterniond(_feature->getMeasurement().tail<4>());
+
+            _landmark->addStateBlock('O', std::make_shared<StateQuaternion>(lmk_o), getProblem());
+        }
+    }
+    else 
+        throw std::runtime_error("ProcessorLandmarkExternal::modifyLandmark unknown case");
+}                                                          
+
+void ProcessorLandmarkExternal::advanceDerived()
+{
+    // Reset here the list of correspondences.
+    known_features_last_ = std::move(known_features_incoming_);
+}
+void ProcessorLandmarkExternal::resetDerived()
+{
+    // Reset here the list of correspondences.
+    known_features_last_ = std::move(known_features_incoming_);
+}
+
+} // namespace wolf
+
+// Register in the FactoryProcessor
+#include "core/processor/factory_processor.h"
+namespace wolf {
+WOLF_REGISTER_PROCESSOR(ProcessorLandmarkExternal)
+WOLF_REGISTER_PROCESSOR_AUTO(ProcessorLandmarkExternal)
+} // namespace wolf
diff --git a/src/processor/processor_motion.cpp b/src/processor/processor_motion.cpp
index 0ad4a754dd665d4533303f2e27569b0e7c151048..b1ed04c2be5814b217357383cc5feaafff1f64c4 100644
--- a/src/processor/processor_motion.cpp
+++ b/src/processor/processor_motion.cpp
@@ -47,6 +47,7 @@ ProcessorMotion::ProcessorMotion(const std::string& _type,
         MotionProvider(_state_structure, _params_motion),
         params_motion_(_params_motion),
         processing_step_(RUNNING_WITHOUT_KF),
+        bootstrapping_(false),
         x_size_(_state_size),
         data_size_(_data_size),
         delta_size_(_delta_size),
@@ -65,11 +66,13 @@ ProcessorMotion::ProcessorMotion(const std::string& _type,
         calib_preint_(_calib_size),
         jacobian_delta_preint_(delta_cov_size_, delta_cov_size_),
         jacobian_delta_(delta_cov_size_, delta_cov_size_),
-        jacobian_calib_(delta_cov_size_, calib_size_)
+        jacobian_calib_(delta_cov_size_, calib_size_),
+        jacobian_delta_calib_(delta_cov_size_, calib_size_)
 {   
     jacobian_delta_preint_          .setIdentity(delta_cov_size_,delta_cov_size_);                                    // dDp'/dDp, dDv'/dDv, all zeros
     jacobian_delta_                 .setIdentity(delta_cov_size_,delta_cov_size_);                                           //
     jacobian_calib_                 .setZero(delta_cov_size_,calib_size_);
+    jacobian_delta_calib_           .setZero(delta_cov_size_,calib_size_);
     unmeasured_perturbation_cov_ =
               params_motion_->unmeasured_perturbation_std
             * params_motion_->unmeasured_perturbation_std
@@ -116,8 +119,7 @@ void ProcessorMotion::mergeCaptures(CaptureMotionPtr cap_prev,
     // emplace new feature and factor (if origin has frame)
     if (cap_prev->getOriginCapture() and cap_prev->getOriginCapture()->getFrame())
     {
-        auto new_feature = emplaceFeature(cap_target);
-        emplaceFactor(new_feature, cap_prev->getOriginCapture());
+        emplaceFeaturesAndFactors(cap_prev->getOriginCapture(), cap_target);
     }
 }
 
@@ -192,7 +194,7 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
     if (keyframe_from_callback)
         buffer_frame_.removeUpTo( keyframe_from_callback->getTimeStamp() );
     
-    switch(processing_step_)
+    switch(processing_step_) // Things to do before integrating motion data
     {
         case FIRST_TIME_WITHOUT_KF :
         {
@@ -202,19 +204,19 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
         }
         case FIRST_TIME_WITH_KF_BEFORE_INCOMING :
         {
-            // cannot joint to the KF: create own origin
+            // cannot join to the KF: create own origin
             setOrigin(getProblem()->getState(getStateStructure()), _incoming_ptr->getTimeStamp());
             break;
         }
         case FIRST_TIME_WITH_KF_ON_INCOMING :
         {
-            // can joint to the KF
+            // can join to the KF
             setOrigin(keyframe_from_callback);
             break;
         }
         case FIRST_TIME_WITH_KF_AFTER_INCOMING :
         {
-            // cannot joint to the KF: create own origin
+            // cannot join to the KF: create own origin
             setOrigin(getProblem()->getState(getStateStructure()), _incoming_ptr->getTimeStamp());
             break;
         }
@@ -227,12 +229,14 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
     }
 
 
-    // integrate data
+    // integrate motion data
     // Done at this place because setPrior() needs 
     integrateOneStep();
 
+    // perform bootstrap steps (usually only IMU requires this)
+    if (bootstrapping_) bootstrap();
 
-    switch(processing_step_)
+    switch (processing_step_) // Things to do after integrating motion data
     {
         case RUNNING_WITH_KF_BEFORE_ORIGIN :
         {
@@ -268,11 +272,11 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
             TimeStamp timestamp_from_callback = keyframe_from_callback->getTimeStamp();
 
             // find the capture whose buffer is affected by the new keyframe
-            auto capture_existing   = findCaptureContainingTimeStamp(timestamp_from_callback); // k
+            auto capture_existing   = findCaptureContainingTimeStamp(timestamp_from_callback); 
 
             if (!capture_existing)
             {
-                WOLF_WARN("A KF before first motion capture (TS = ", timestamp_from_callback, "). ProcessorMotion cannot do anything.");
+                WOLF_WARN(getName(), ": Cannot join KF. The received KF (TS = ", timestamp_from_callback, ") is older than the first motion capture.");
                 break;
             }
 
@@ -307,11 +311,9 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
             // and give the part of the buffer before the new keyframe to the capture for the KF callback
             splitBuffer(capture_existing, timestamp_from_callback, capture_for_keyframe_callback);
 
-            // create motion feature and add it to the capture
-            auto feature_new = emplaceFeature(capture_for_keyframe_callback);
-
-            // create motion factor and add it to the feature, and constrain to the other capture (origin)
-            emplaceFactor(feature_new, capture_origin );
+            // // create motion feature and add it to the capture
+            // // create motion factor and add it to the feature, and constrain to the other capture (origin)
+            emplaceFeaturesAndFactors(capture_origin, capture_for_keyframe_callback);
 
             // modify existing feature and factor (if they exist in the existing capture)
             if (!capture_existing->getFeatureList().empty())
@@ -321,9 +323,7 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
 
                 assert(capture_existing->getFeatureList().empty());// there was only one feature!
 
-                auto new_feature_existing = emplaceFeature(capture_existing);
-
-                emplaceFactor(new_feature_existing, capture_for_keyframe_callback);
+                emplaceFeaturesAndFactors(capture_for_keyframe_callback, capture_existing);
             }
 
             break;
@@ -401,10 +401,8 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
             splitBuffer(capture_existing, timestamp_from_callback, capture_for_keyframe_callback);
 
             // create motion feature and add it to the capture
-            auto feature_for_keyframe_callback = emplaceFeature(capture_for_keyframe_callback);
-
             // create motion factor and add it to the feature, and constrain to the other capture (origin)
-            emplaceFactor(feature_for_keyframe_callback, capture_origin );
+            emplaceFeaturesAndFactors(capture_origin, capture_for_keyframe_callback);
 
             // reset processor origin
             origin_ptr_ = capture_for_keyframe_callback;
@@ -440,7 +438,7 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
          *    x : any capture
          *    o : origin capture
          *    l : last capture      -> we'll make a KF here
-         *    i : incoming capture
+         *    i : incoming capture  -> data has already been integrated into last capture
          *    n : new capture       ->
          *  --- : buffer history
          *
@@ -472,10 +470,8 @@ void ProcessorMotion::processCapture(CaptureBasePtr _incoming_ptr)
         keyframe            ->link(getProblem());
 
         // create motion feature and add it to the key_capture
-        auto key_feature    = emplaceFeature(last_ptr_);
-
         // create motion factor and link it to parent feature and other frame (which is origin's frame)
-        auto factor         = emplaceFactor(key_feature, origin_ptr_);
+        emplaceFeaturesAndFactors(origin_ptr_, last_ptr_);
 
         // create a new frame
         auto frame_new      = std::make_shared<FrameBase>(getTimeStamp(),
@@ -564,8 +560,6 @@ VectorComposite ProcessorMotion::getState(const StateStructure& _structure) cons
     auto calib_preint = last_ptr_->getCalibrationPreint();
 
     VectorComposite state;
-    //WOLF_INFO("processorMotion last timestamp: ", last_ptr_->getTimeStamp());
-    //WOLF_INFO("processorMotion origin timestamp: ", origin_ptr_->getTimeStamp());
     if ( hasCalibration())
     {
         // Get current calibration -- from origin capture
@@ -610,7 +604,6 @@ VectorComposite ProcessorMotion::getState(const StateStructure& _structure) cons
 
 
 
-// _x needs to have the size of the processor state
 VectorComposite ProcessorMotion::getState(const TimeStamp& _ts, const StateStructure& _structure) const
 {
     assert(_ts.ok());
@@ -763,6 +756,9 @@ void ProcessorMotion::setOrigin(FrameBasePtr _origin_frame)
 
 void ProcessorMotion::integrateOneStep()
 {
+    /* Integrate motiondata in incoming_ptr_ and store all results in the MotionBuffer in last_ptr_
+     */
+
     // Set dt
     dt_ = updateDt();
     assert(dt_ >= 0 && "Time interval _dt is negative!");
@@ -813,7 +809,9 @@ void ProcessorMotion::integrateOneStep()
                              jacobian_calib_);
 
     // integrate odometry
-    statePlusDelta(odometry_, delta_, dt_, odometry_);
+    VectorComposite odom;
+    statePlusDelta(getOdometry(), delta_, dt_, odom);
+    setOdometry(odom);
 }
 
 void ProcessorMotion::reintegrateBuffer(CaptureMotionPtr _capture_ptr) const
@@ -990,23 +988,23 @@ FrameBasePtr ProcessorMotion::computeProcessingStep()
             if (checkTimeTolerance(keyframe_from_callback,
                                    incoming_ptr_))
             {
-                WOLF_DEBUG("First time with a KF compatible.")
+                WOLF_DEBUG("PM ", getName(), ": First time with a KF compatible.")
                 processing_step_ = FIRST_TIME_WITH_KF_ON_INCOMING;
             }
             else if (keyframe_from_callback->getTimeStamp() < incoming_ptr_->getTimeStamp())
             {
-                WOLF_DEBUG("First time with a KF too old. It seems the prior has been set before receiving the first capture of this processor.")
+                WOLF_DEBUG("PM ", getName(), ": First time with a KF too old. It seems the prior has been set before receiving the first capture of this processor.")
                 processing_step_ = FIRST_TIME_WITH_KF_BEFORE_INCOMING;
             }
             else
             {
-                WOLF_DEBUG("First time with a KF newer than the first capture. It only can happen if prior mode is 'nothing'")
+                WOLF_DEBUG("PM ", getName(), ": First time with a KF newer than the first capture. It only can happen if prior mode is 'nothing'")
                 processing_step_ = FIRST_TIME_WITH_KF_AFTER_INCOMING;
             }
         }
         else
         {
-            WOLF_DEBUG("First time with a KF newer than the first capture. It only can happen if prior mode is 'nothing'")
+            WOLF_DEBUG("PM ", getName(), ": First time with a KF newer than the first capture. It only can happen if prior mode is 'nothing'")
             processing_step_ = FIRST_TIME_WITHOUT_KF;
         }
 
@@ -1052,7 +1050,7 @@ TimeStamp ProcessorMotion::getTimeStamp ( ) const
         origin_ptr_->isRemoving() or
         not last_ptr_)
     {
-        WOLF_DEBUG("Processor has no time stamp. Returning a non-valid timestamp equal to 0");
+        WOLF_DEBUG("PM proc \"", getName(), "\" has no time stamp. Returning a non-valid timestamp equal to 0");
         return TimeStamp::Invalid();
     }
 
@@ -1066,7 +1064,7 @@ void ProcessorMotion::printHeader(int _depth, bool _constr_by, bool _metric, boo
 {
     _stream << _tabs << "PrcM" << id() << " " << getType() << " \"" << getName() << "\"" << std::endl;
     if (getOrigin())
-        _stream << _tabs << "  " << "o: Cap" << getOrigin()->id() << " - " << "  Frm"
+        _stream << _tabs << "  " << "o: Cap" << getOrigin()->id() << " - " << " Frm"
                 << getOrigin()->getFrame()->id() << std::endl;
     if (getLast())
         _stream << _tabs << "  " << "l: Cap" << getLast()->id() << " - " << " Frm"
diff --git a/src/processor/processor_odom_2d.cpp b/src/processor/processor_odom_2d.cpp
index 98f55c0f0d6c5ff9af08d627c1ef4d67b351783e..69dfe4e6ea31ebea40d9b9205db212e40067a4dc 100644
--- a/src/processor/processor_odom_2d.cpp
+++ b/src/processor/processor_odom_2d.cpp
@@ -164,29 +164,17 @@ CaptureMotionPtr ProcessorOdom2d::emplaceCapture(const FrameBasePtr& _frame_own,
     return cap_motion;
 }
 
-FactorBasePtr ProcessorOdom2d::emplaceFactor(FeatureBasePtr _feature, CaptureBasePtr _capture_origin)
+void ProcessorOdom2d::emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own)
 {
-    auto fac_odom = FactorBase::emplace<FactorRelativePose2d>(_feature,
-                                                              _feature,
-                                                              _capture_origin->getFrame(),
-                                                              shared_from_this(),
-                                                              params_->apply_loss_function,
-                                                              TOP_MOTION);
-    return fac_odom;
-}
-
-FeatureBasePtr ProcessorOdom2d::emplaceFeature(CaptureMotionPtr _capture_motion)
-{
-    Eigen::MatrixXd covariance = _capture_motion->getBuffer().back().delta_integr_cov_;
+    Eigen::MatrixXd covariance = _capture_own->getBuffer().back().delta_integr_cov_;
     makePosDef(covariance);
 
-    FeatureBasePtr key_feature_ptr = FeatureBase::emplace<FeatureBase>(_capture_motion,
-                                                                       "ProcessorOdom2d",
-                                                                       _capture_motion->getBuffer().back().delta_integr_,
-                                                                       covariance);
-    return key_feature_ptr;
-}
+    FeatureBasePtr feature = FeatureBase::emplace<FeatureBase>(
+        _capture_own, "ProcessorOdom2d", _capture_own->getBuffer().back().delta_integr_, covariance);
 
+    FactorBase::emplace<FactorRelativePose2d>(
+        feature, feature, _capture_origin->getFrame(), shared_from_this(), params_->apply_loss_function, TOP_MOTION);
+}
 
 
 } /* namespace wolf */
diff --git a/src/processor/processor_odom_3d.cpp b/src/processor/processor_odom_3d.cpp
index 471dd627361266aad20356543345355a5d18bdbf..1870bb66b61021cb0c3b4851eaec97ca5b80d80b 100644
--- a/src/processor/processor_odom_3d.cpp
+++ b/src/processor/processor_odom_3d.cpp
@@ -55,9 +55,8 @@ void ProcessorOdom3d::computeCurrentDelta(const Eigen::VectorXd& _data,
     if (_data.size() == (long int)6)
     {
         // rotation in vector form
-        _delta.head<3>()    = _data.head<3>();
-        Eigen::Map<Eigen::Quaterniond> q(_delta.data() + 3);
-        q                   = v2q(_data.tail<3>());
+        _delta.head<3>() = _data.head<3>();
+        _delta.tail<4>() = v2q(_data.tail<3>()).coeffs();
     }
     else
     {
@@ -214,15 +213,6 @@ CaptureMotionPtr ProcessorOdom3d::emplaceCapture(const FrameBasePtr& _frame_own,
     return cap_motion;
 }
 
-FeatureBasePtr ProcessorOdom3d::emplaceFeature(CaptureMotionPtr _capture_motion)
-{
-    FeatureBasePtr key_feature_ptr = FeatureBase::emplace<FeatureBase>(_capture_motion,
-                                                                       "ProcessorOdom3d",
-                                                                       _capture_motion->getBuffer().back().delta_integr_,
-                                                                       _capture_motion->getBuffer().back().delta_integr_cov_);
-    return key_feature_ptr;
-}
-
 Eigen::VectorXd ProcessorOdom3d::correctDelta (const Eigen::VectorXd& delta_preint,
                                                const Eigen::VectorXd& delta_step) const
 {
@@ -231,15 +221,16 @@ Eigen::VectorXd ProcessorOdom3d::correctDelta (const Eigen::VectorXd& delta_prei
     return delta_corrected;
 }
 
-FactorBasePtr ProcessorOdom3d::emplaceFactor(FeatureBasePtr _feature_motion, CaptureBasePtr _capture_origin)
+
+void ProcessorOdom3d::emplaceFeaturesAndFactors(CaptureBasePtr _capture_origin, CaptureMotionPtr _capture_own)
 {
-    auto fac_odom = FactorBase::emplace<FactorRelativePose3d>(_feature_motion,
-                                                              _feature_motion,
-                                                              _capture_origin->getFrame(),
-                                                              shared_from_this(),
-                                                              params_->apply_loss_function,
-                                                              TOP_MOTION);
-    return fac_odom;
+    Eigen::MatrixXd covariance = _capture_own->getBuffer().back().delta_integr_cov_ + unmeasured_perturbation_cov_;
+
+    FeatureBasePtr feature = FeatureBase::emplace<FeatureBase>(
+        _capture_own, "ProcessorOdom3d", _capture_own->getBuffer().back().delta_integr_, covariance);
+        
+    FactorBase::emplace<FactorRelativePose3d>(
+        feature, feature, _capture_origin->getFrame(), shared_from_this(), params_->apply_loss_function, TOP_MOTION);
 }
 
 VectorXd ProcessorOdom3d::getCalibration (const CaptureBaseConstPtr _capture) const
diff --git a/src/processor/processor_tracker.cpp b/src/processor/processor_tracker.cpp
index b0e08b4b59240b359457565c95071d2b7c1385e6..9dc1fa5ab42f7dc88504b63977eb90611bc7bde2 100644
--- a/src/processor/processor_tracker.cpp
+++ b/src/processor/processor_tracker.cpp
@@ -90,8 +90,9 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             // TrackerLandmark: If we have been given a map, all landmarks in the map are known. Process them.
             processKnown();
 
-            // Update pointers
+            // Reset this
             resetDerived();
+            // Update pointers
             origin_ptr_     = incoming_ptr_;
             last_ptr_       = incoming_ptr_;
             incoming_ptr_   = nullptr;
@@ -114,13 +115,15 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             // Process info
             // TrackerFeature:  We only process new features in Last, here last = nullptr, so we do not have anything to do.
             // TrackerLandmark: If we have been given a map, all landmarks in the map are known. Process them.
-            processKnown();
+            if (not getProblem()->getMap()->getLandmarkList().empty())
+                processKnown();
 
             // Issue KF callback with new KF
             getProblem()->keyFrameCallback(keyframe, shared_from_this());
 
-            // Update pointers
+            // Reset this
             resetDerived();
+            // Update pointers
             origin_ptr_ = incoming_ptr_;
             last_ptr_   = incoming_ptr_;
             incoming_ptr_ = nullptr;
@@ -149,25 +152,28 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             WOLF_DEBUG( "PT ", getName(), " SECOND_TIME_WITHOUT_KEYFRAME" );
 
             // Make a NON KEY Frame to hold incoming capture
-            FrameBasePtr keyframe = std::make_shared<FrameBase>(incoming_ptr_->getTimeStamp(),
+            FrameBasePtr frame = std::make_shared<FrameBase>(incoming_ptr_->getTimeStamp(),
                                                                 getProblem()->getFrameStructure(),
                                                                 getProblem()->getState());
-            incoming_ptr_->link(keyframe);
+            incoming_ptr_->link(frame);
 
             // Process info
-            // TrackerLandmark: If we have been given a map, all landmarks in the map are known. Process them.
-            processKnown();
+            // If we have known landmarks or features. Process them.
+            if (not getProblem()->getMap()->getLandmarkList().empty() or not known_features_last_.empty())
+                processKnown();
+            
             // Both Trackers:  We have a last_ Capture with not enough features, so populate it.
             processNew(params_tracker_->max_new_features);
 
             // Establish factors
             establishFactors();
 
-            // Update pointers
+            // Reset this
             resetDerived();
+            // Update pointers
             origin_ptr_ = last_ptr_;
             last_ptr_   = incoming_ptr_;
-            last_frame_ptr_ = keyframe;
+            last_frame_ptr_ = frame;
             incoming_ptr_ = nullptr;
 
             break;
@@ -199,8 +205,9 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             // Establish factors
             establishFactors();
 
-            // Update pointers
+            // Reset this
             resetDerived();
+            // Update pointers
             origin_ptr_     = last_ptr_;
             last_ptr_       = incoming_ptr_;
             last_frame_ptr_ = frame;
@@ -237,8 +244,9 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
                                                                  getProblem()->getState(incoming_ptr_->getTimeStamp()));
                 incoming_ptr_   ->link(frame);
 
-                // Update pointers
+                // Reset this
                 resetDerived();
+                // Update pointers
                 origin_ptr_     = last_ptr_;
                 last_ptr_       = incoming_ptr_;
                 last_frame_ptr_ = frame;
@@ -249,9 +257,6 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             {
                 // We do not create a KF
 
-                // Advance this
-                advanceDerived();
-
                 // Replace last frame for a new NON KEY frame in incoming
                 FrameBasePtr frame = std::make_shared<FrameBase>(incoming_ptr_->getTimeStamp(),
                                                                  getProblem()->getFrameStructure(),
@@ -259,6 +264,8 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
                 incoming_ptr_->link(frame);
                 last_ptr_->unlink(); // unlink last (destroying the frame) instead of frame destruction that would implicitly destroy last
 
+                // Advance this
+                advanceDerived();
                 // Update pointers
                 last_ptr_       = incoming_ptr_;
                 last_frame_ptr_ = frame;
diff --git a/src/processor/processor_tracker_feature.cpp b/src/processor/processor_tracker_feature.cpp
index d55a2666cc00b3c8cdc53854ab43f7903f0b9d6e..c269ff41137f33cb6cfd14158b54228346782f28 100644
--- a/src/processor/processor_tracker_feature.cpp
+++ b/src/processor/processor_tracker_feature.cpp
@@ -200,11 +200,11 @@ void ProcessorTrackerFeature::establishFactors()
 
         auto fac_ptr  = emplaceFactor(feature_in_last, feature_in_origin);
 
-        assert(fac_ptr->getFeature() != nullptr && "not emplaced factor returned by emplaceFactor()");
+        assert((fac_ptr == nullptr or fac_ptr->getFeature() != nullptr) && "not emplaced factor returned by emplaceFactor()");
 
-        WOLF_DEBUG( "Factor: track: " , feature_in_last->trackId(),
-                    " origin: "       , feature_in_origin->id() ,
-                    " from last: "    , feature_in_last->id() );
+        WOLF_DEBUG_COND(fac_ptr, "New factor: track: " , feature_in_last->trackId(),
+                                 " origin: "       , feature_in_origin->id() ,
+                                 " from last: "    , feature_in_last->id() );
     }
 }
 
diff --git a/src/sensor/sensor_base.cpp b/src/sensor/sensor_base.cpp
index 996d48263c113555ba055422153bf10459bfb564..af06fd13624d22a6b0f83ab8b62f0769b1b0284f 100644
--- a/src/sensor/sensor_base.cpp
+++ b/src/sensor/sensor_base.cpp
@@ -56,14 +56,20 @@ SensorBase::SensorBase(const std::string& _type,
     noise_cov_.setZero();
 
     if (_p_ptr)
+    {
+        _p_ptr->setNonTransformable();
         addStateBlock('P', _p_ptr, _p_dyn);
+    }
 
     if (_o_ptr)
+    {
+        _o_ptr->setNonTransformable();
         addStateBlock('O', _o_ptr, _o_dyn);
-
+    }
     if (_intr_ptr)
+    {
         addStateBlock('I', _intr_ptr, _intr_dyn);
-
+    }
 }
 
 SensorBase::SensorBase(const std::string& _type,
@@ -87,11 +93,15 @@ SensorBase::SensorBase(const std::string& _type,
     setNoiseStd(_noise_std);
 
     if (_p_ptr)
+    {
+        _p_ptr->setNonTransformable();
         addStateBlock('P', _p_ptr, _p_dyn);
-
+    }
     if (_o_ptr)
+    {
+        _o_ptr->setNonTransformable();
         addStateBlock('O', _o_ptr, _o_dyn);
-
+    }
     if (_intr_ptr)
         addStateBlock('I', _intr_ptr, _intr_dyn);
 }
@@ -541,8 +551,18 @@ void SensorBase::printState (bool _metric, bool _state_blocks, std::ostream& _st
         {
             auto sb = getStateBlockDynamic(key);
             if (sb)
-                _stream << _tabs << "  " << key << " [" << (sb->isFixed() ? "Fix" : "Est") << "] = ( "
-                        << std::setprecision(3) << sb->getState().transpose() << " )" << " @ " << sb << std::endl;
+            {
+                _stream << _tabs << "  " << key << " [" << (sb->isFixed() ? "Fix" : "Est") << ", "
+                        << (isStateBlockDynamic(key) ? "Dyn" : "Sta") << "] = ( " << std::setprecision(3)
+                        << sb->getState().transpose() << " )";
+                if (params_prior_map_.count(key))
+                {
+                    auto ftr = params_prior_map_.at(key);
+                    _stream << " ; mean = ( " << ftr->getMeasurement().transpose() << " )";
+                    _stream << " , std = ( " << ftr->getMeasurementCovariance().diagonal().array().sqrt().transpose() << " )";
+                }
+                _stream << " @ " << sb << std::endl;
+            }
         }
     }
     else if (_metric)
@@ -557,7 +577,7 @@ void SensorBase::printState (bool _metric, bool _state_blocks, std::ostream& _st
         {
             auto sb = getStateBlockDynamic(key);
             if (sb)
-                _stream << "    " << key << " [" << (sb->isFixed() ? "Fix" : "Est") << "] @ " << sb;
+                _stream << "    " << key << " [" << (sb->isFixed() ? "Fix" : "Est") << ", " << (isStateBlockDynamic(key) ? "Dyn" : "Sta") << "] @ " << sb;
         }
         _stream << std::endl;
     }
diff --git a/src/sensor/sensor_diff_drive.cpp b/src/sensor/sensor_diff_drive.cpp
index 4620caea4e169aa3ef64d233e6883fdea53bed02..256def47937b9cd42e94b72b2169e4a3e72fc897 100644
--- a/src/sensor/sensor_diff_drive.cpp
+++ b/src/sensor/sensor_diff_drive.cpp
@@ -27,21 +27,23 @@
  */
 
 #include "core/sensor/sensor_diff_drive.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/state_block/state_angle.h"
 
 namespace wolf
 {
 
-SensorDiffDrive::SensorDiffDrive(const Eigen::VectorXd& _extrinsics,
-                                 ParamsSensorDiffDrivePtr _intrinsics) :
-        SensorBase("SensorDiffDrive",
-                   std::make_shared<StateBlock>(_extrinsics.head(2), true),
-                   std::make_shared<StateAngle>(_extrinsics(2), true),
-                   std::make_shared<StateBlock>(3, false), 2),
-                   params_diff_drive_(_intrinsics)
+SensorDiffDrive::SensorDiffDrive(const Eigen::VectorXd& _extrinsics, ParamsSensorDiffDrivePtr _intrinsics)
+    : SensorBase("SensorDiffDrive",
+                 std::make_shared<StatePoint2d>(_extrinsics.head(2), true),
+                 std::make_shared<StateAngle>(_extrinsics(2), true),
+                 std::make_shared<StateParams3>(
+                     Vector3d(_intrinsics->radius_left, _intrinsics->radius_right, _intrinsics->wheel_separation),
+                     false),
+                 2),
+      params_diff_drive_(_intrinsics)
 {
     radians_per_tick = 2.0*M_PI / params_diff_drive_->ticks_per_wheel_revolution;
-    getIntrinsic()->setState(Eigen::Vector3d(_intrinsics->radius_left,_intrinsics->radius_right,_intrinsics->wheel_separation));
     unfixIntrinsics();
 
     if(params_diff_drive_->set_intrinsics_prior)
@@ -53,7 +55,6 @@ SensorDiffDrive::SensorDiffDrive(const Eigen::VectorXd& _extrinsics,
     Eigen::Vector2d noise_sigma; noise_sigma << sigma_rev, sigma_rev;
 
     setNoiseStd(noise_sigma);
-   
 }
 
 SensorDiffDrive::~SensorDiffDrive()
diff --git a/src/sensor/sensor_odom_2d.cpp b/src/sensor/sensor_odom_2d.cpp
index 711545f66b48b99fcfaf891954db3ddba7b56d7a..9a4a9d1f1f41323e3209da09afcf968ef3dc69b4 100644
--- a/src/sensor/sensor_odom_2d.cpp
+++ b/src/sensor/sensor_odom_2d.cpp
@@ -20,17 +20,19 @@
 //
 //--------LICENSE_END--------
 #include "core/sensor/sensor_odom_2d.h"
-#include "core/state_block/state_block.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/state_block/state_angle.h"
 
 namespace wolf {
 
 SensorOdom2d::SensorOdom2d(Eigen::VectorXd _extrinsics, const ParamsSensorOdom2d& _intrinsics) :
-        SensorBase("SensorOdom2d", std::make_shared<StateBlock>(_extrinsics.head(2), true), std::make_shared<StateAngle>(_extrinsics(2), true), nullptr, 2),
+        SensorBase("SensorOdom2d", std::make_shared<StatePoint2d>(_extrinsics.head(2), true), std::make_shared<StateAngle>(_extrinsics(2), true), nullptr, 2),
         k_disp_to_disp_(_intrinsics.k_disp_to_disp),
         k_rot_to_rot_(_intrinsics.k_rot_to_rot)
 {
     assert(_extrinsics.size() == 3 && "Wrong extrinsics vector size! Should be 3 for 2d.");
+    getStateBlock('P')->setNonTransformable();
+    getStateBlock('O')->setNonTransformable();
     //
 }
 
diff --git a/src/sensor/sensor_odom_3d.cpp b/src/sensor/sensor_odom_3d.cpp
index b44561ff402bef4971b211cfa2eae43724457fc3..112dc72eb98a50dfdf9f34bbceba7d3994078758 100644
--- a/src/sensor/sensor_odom_3d.cpp
+++ b/src/sensor/sensor_odom_3d.cpp
@@ -28,14 +28,14 @@
 
 #include "core/sensor/sensor_odom_3d.h"
 
-#include "core/state_block/state_block.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/state_block/state_quaternion.h"
 #include "core/math/rotations.h"
 
 namespace wolf {
 
 SensorOdom3d::SensorOdom3d(const Eigen::VectorXd& _extrinsics_pq, const ParamsSensorOdom3d& _intrinsics) :
-                        SensorBase("SensorOdom3d", std::make_shared<StateBlock>(_extrinsics_pq.head(3), true), std::make_shared<StateQuaternion>(_extrinsics_pq.tail(4), true), nullptr, 6),
+                        SensorBase("SensorOdom3d", std::make_shared<StatePoint3d>(_extrinsics_pq.head(3), true), std::make_shared<StateQuaternion>(_extrinsics_pq.tail(4), true), nullptr, 6),
                         k_disp_to_disp_(_intrinsics.k_disp_to_disp),
                         k_disp_to_rot_(_intrinsics.k_disp_to_rot),
                         k_rot_to_rot_(_intrinsics.k_rot_to_rot),
@@ -43,6 +43,8 @@ SensorOdom3d::SensorOdom3d(const Eigen::VectorXd& _extrinsics_pq, const ParamsSe
                         min_rot_var_(_intrinsics.min_rot_var)
 {
     assert(_extrinsics_pq.size() == 7 && "Bad extrinsics vector size! Should be 7 for 3d.");
+    getStateBlock('P')->setNonTransformable();
+    getStateBlock('O')->setNonTransformable();
 
     noise_cov_ = (Eigen::Vector6d() << min_disp_var_, min_disp_var_, min_disp_var_, min_rot_var_, min_rot_var_, min_rot_var_).finished().asDiagonal();
     setNoiseCov(noise_cov_); // sets also noise_std_
diff --git a/src/sensor/sensor_pose.cpp b/src/sensor/sensor_pose.cpp
index 87f2880b8f3e58e1898470572e630375bd792d8c..c9c0c90db18737e6052e83a2050ee9fd3a1ed71f 100644
--- a/src/sensor/sensor_pose.cpp
+++ b/src/sensor/sensor_pose.cpp
@@ -28,21 +28,26 @@
 
 #include "core/sensor/sensor_pose.h"
 
-#include "core/state_block/state_block.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/state_block/state_quaternion.h"
 #include "core/math/rotations.h"
 
 namespace wolf {
 
 SensorPose::SensorPose(const Eigen::VectorXd& _extrinsics_pq, const ParamsSensorPose& _intrinsics) :
-                        SensorBase("SensorPose", std::make_shared<StateBlock>(_extrinsics_pq.head(3), true), std::make_shared<StateQuaternion>(_extrinsics_pq.tail(4), true), nullptr, 6),
-                        std_p_(_intrinsics.std_p),
-                        std_o_(_intrinsics.std_o)
+                        SensorBase("SensorPose", 
+                                   std::make_shared<StatePoint3d>(_extrinsics_pq.head(3), true), 
+                                   std::make_shared<StateQuaternion>(_extrinsics_pq.tail(4), true), 
+                                   nullptr, 
+                                   6)
 {
     assert(_extrinsics_pq.size() == 7 && "Bad extrinsics vector size! Should be 7 for 3d.");
 
-    noise_cov_ = (Eigen::Vector6d() << std_p_*std_p_, std_p_*std_p_, std_p_*std_p_, std_o_*std_o_, std_o_*std_o_, std_o_*std_o_).finished().asDiagonal();
-    setNoiseCov(noise_cov_); // sets also noise_std_
+    params_pose_ = std::make_shared<ParamsSensorPose>(_intrinsics);
+
+    noise_std_ << params_pose_->std_px, params_pose_->std_py, params_pose_->std_pz, params_pose_->std_ox, params_pose_->std_oy, params_pose_->std_oz;
+
+    setNoiseStd(noise_std_); // sets also noise_cov_
 }
 
 SensorPose::SensorPose(const Eigen::VectorXd& _extrinsics_pq, ParamsSensorPosePtr _intrinsics) :
diff --git a/src/solver/solver_manager.cpp b/src/solver/solver_manager.cpp
index c9d192a536151f0340a7fd1f8f9f5e88acf54c26..a9d3ee25ad0cc1e5d7c2a8991c221835fbcd4334 100644
--- a/src/solver/solver_manager.cpp
+++ b/src/solver/solver_manager.cpp
@@ -57,6 +57,9 @@ SolverManager::~SolverManager()
 
 void SolverManager::update()
 {
+    // lock mutex to prevent Problem::transform() during this update()
+    std::lock_guard<std::mutex> lock_transform (wolf_problem_->mut_transform_);
+
     // Consume notification maps
     std::map<StateBlockPtr,Notification> sb_notification_map;
     std::map<FactorBasePtr,Notification> fac_notification_map;
@@ -147,8 +150,9 @@ void SolverManager::update()
         state_ptr->resetFixUpdated();
         state_ptr->resetLocalParamUpdated();
     }
+    wolf_problem_->resetTransformed();
 
-    #ifdef _WOLF_DEBUG
+#ifdef _WOLF_DEBUG
         assert(check("after update()"));
     #endif
 }
@@ -188,7 +192,15 @@ std::string SolverManager::solve(const ReportVerbosity report_level)
     {
         // Avoid usuless copies
         if (!stateblock_statevector.first->isFixed())
-            stateblock_statevector.first->setState(stateblock_statevector.second, false); // false = do not raise the flag state_updated_
+        {
+            stateblock_statevector.first->setState(stateblock_statevector.second,
+                                                   false);  // false = do not raise the flag state_updated_
+
+            // Transform the whole set of state blocks if necessary
+            // (this may happen when Problem::transform() has been called during the execution of this solve())
+            if (wolf_problem_->isTransformed())
+                stateblock_statevector.first->transform(wolf_problem_->getTransformation());
+        }
     }
     auto duration_state = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start_state);
     acc_duration_state_ += duration_state;
diff --git a/src/state_block/has_state_blocks.cpp b/src/state_block/has_state_blocks.cpp
index 047469dc899a8973b7bdf07df318b2079c155ef9..91523db7c2ea51f7f47e680cfda5cac7cadacedf 100644
--- a/src/state_block/has_state_blocks.cpp
+++ b/src/state_block/has_state_blocks.cpp
@@ -76,6 +76,13 @@ void HasStateBlocks::perturb(double amplitude)
     }
 }
 
+void HasStateBlocks::transform(const VectorComposite& _transformation)
+{
+    for (auto& pair_key_sb : state_block_map_)
+        pair_key_sb.second->transform(_transformation);
+}
+
+
 void HasStateBlocks::printState (bool _metric, bool _state_blocks, std::ostream& _stream, std::string _tabs) const
 {
     if (_metric && _state_blocks)
diff --git a/src/state_block/state_block.cpp b/src/state_block/state_block.cpp
index 513d6d4627627a029e3ebe61bab550497d369634..5fe479daf2fdab0245b223f76075f05d7e2e4001 100644
--- a/src/state_block/state_block.cpp
+++ b/src/state_block/state_block.cpp
@@ -74,7 +74,6 @@ void StateBlock::plus(const Eigen::VectorXd &_dv)
 #include "core/state_block/state_homogeneous_3d.h"
 
 namespace wolf{
-WOLF_REGISTER_STATEBLOCK(StateBlock);
 WOLF_REGISTER_STATEBLOCK(StateQuaternion);
 WOLF_REGISTER_STATEBLOCK(StateAngle);
 WOLF_REGISTER_STATEBLOCK(StateHomogeneous3d);
diff --git a/src/state_block/state_block_derived.cpp b/src/state_block/state_block_derived.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c5bc71bbc0252beb8c951a6bc37d5f5a66884ea0
--- /dev/null
+++ b/src/state_block/state_block_derived.cpp
@@ -0,0 +1,108 @@
+//--------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/state_block/state_block_derived.h"
+#include "core/state_block/factory_state_block.h"
+
+namespace wolf
+{
+StateBlockPtr StatePoint2d::create(const Eigen::VectorXd& _state, bool _fixed)
+{
+    if (_state.size() == 2) return std::make_shared<StatePoint2d>(_state, _fixed);
+
+    throw std::length_error("Wrong vector size for Point2d.");
+}
+
+StateBlockPtr StatePoint3d::create(const Eigen::VectorXd& _state, bool _fixed)
+{
+    if (_state.size() == 3) return std::make_shared<StatePoint3d>(_state, _fixed);
+
+    throw std::length_error("Wrong vector size for Point3d.");
+}
+
+StateBlockPtr StateVector2d::create(const Eigen::VectorXd& _state, bool _fixed)
+{
+    if (_state.size() == 2) return std::make_shared<StateVector2d>(_state, _fixed);
+
+    throw std::length_error("Wrong vector size for Vector2d.");
+}
+
+StateBlockPtr StateVector3d::create(const Eigen::VectorXd& _state, bool _fixed)
+{
+    if (_state.size() == 3) return std::make_shared<StateVector3d>(_state, _fixed);
+
+    throw std::length_error("Wrong vector size for Vector3d.");
+}
+
+StateBlockPtr create_point(const Eigen::VectorXd& _state, bool _fixed)
+{
+    if (_state.size() == 2)
+        return std::make_shared<StatePoint2d>(_state, _fixed);
+    else if (_state.size() == 3)
+        return std::make_shared<StatePoint3d>(_state, _fixed);
+
+    throw std::length_error("Wrong vector size for Point.");
+}
+
+StateBlockPtr create_vector(const Eigen::VectorXd& _state, bool _fixed)
+{
+    if (_state.size() == 2)
+        return std::make_shared<StateVector2d>(_state, _fixed);
+    else if (_state.size() == 3)
+        return std::make_shared<StateVector3d>(_state, _fixed);
+
+    throw std::length_error("Wrong vector size for Vector.");
+}
+
+template <size_t size>
+StateBlockPtr StateParams<size>::create(const Eigen::VectorXd& _state, bool _fixed)
+{
+    if (_state.size() == size) return std::make_shared<StateParams<size>>(_state, _fixed);
+
+    throw std::length_error("Wrong vector size for Params.");
+}
+
+namespace
+{
+const bool __attribute__((used)) create_pointRegisteredWithP =
+    FactoryStateBlock::registerCreator("P", wolf::create_point);
+const bool __attribute__((used)) create_vectorRegisteredWithP =
+    FactoryStateBlock::registerCreator("V", wolf::create_vector);
+}  // namespace
+
+WOLF_REGISTER_STATEBLOCK(StatePoint2d);
+WOLF_REGISTER_STATEBLOCK(StateVector2d);
+WOLF_REGISTER_STATEBLOCK(StatePoint3d);
+WOLF_REGISTER_STATEBLOCK(StateVector3d);
+
+WOLF_REGISTER_STATEBLOCK(StateParams1);
+WOLF_REGISTER_STATEBLOCK(StateParams2);
+WOLF_REGISTER_STATEBLOCK(StateParams3);
+WOLF_REGISTER_STATEBLOCK(StateParams4);
+WOLF_REGISTER_STATEBLOCK(StateParams5);
+WOLF_REGISTER_STATEBLOCK(StateParams6);
+WOLF_REGISTER_STATEBLOCK(StateParams7);
+WOLF_REGISTER_STATEBLOCK(StateParams8);
+WOLF_REGISTER_STATEBLOCK(StateParams9);
+WOLF_REGISTER_STATEBLOCK(StateParams10);
+
+}  // namespace wolf
\ No newline at end of file
diff --git a/src/trajectory/trajectory_base.cpp b/src/trajectory/trajectory_base.cpp
index 24de3de294a6e65ea409d345a31a23393904ae50..244d6a505d66143823e6a67456c33972ff86a588 100644
--- a/src/trajectory/trajectory_base.cpp
+++ b/src/trajectory/trajectory_base.cpp
@@ -54,13 +54,15 @@ void TrajectoryBase::removeFrame(FrameBasePtr _frame_ptr)
 void TrajectoryBase::getFactorList(FactorBaseConstPtrList & _fac_list) const
 {
 	for(auto fr_pair: frame_map_)
-		fr_pair.second->getFactorList(_fac_list);
+        if (not fr_pair.second->isRemoving())
+	    	fr_pair.second->getFactorList(_fac_list);
 }
 
 void TrajectoryBase::getFactorList(FactorBasePtrList & _fac_list)
 {
 	for(auto fr_pair: frame_map_)
-		fr_pair.second->getFactorList(_fac_list);
+        if (not fr_pair.second->isRemoving())
+    		fr_pair.second->getFactorList(_fac_list);
 }
 
 TimeStamp TrajectoryBase::closestTimeStampToTimeStamp(const TimeStamp& _ts) const
diff --git a/src/utils/converter_utils.cpp b/src/utils/converter_utils.cpp
index f77008e1bb528118d955789e15a914d329d804bc..b5412684689c739d9b5824c9ff2866a07d43e48b 100644
--- a/src/utils/converter_utils.cpp
+++ b/src/utils/converter_utils.cpp
@@ -23,8 +23,8 @@
 
 // Eigen
 #include <array>
-#include <eigen3/Eigen/Dense>
-#include <eigen3/Eigen/Geometry>
+#include <Eigen/Dense>
+#include <Eigen/Geometry>
 
 // STD
 #include <iostream>
diff --git a/src/utils/params_server.cpp b/src/utils/params_server.cpp
index ce4a79743d24302907e85117b812458ed4de6c83..df5cd7677ddc448918c4c5aec0c74fddef0073e2 100644
--- a/src/utils/params_server.cpp
+++ b/src/utils/params_server.cpp
@@ -47,3 +47,8 @@ void ParamsServer::addParams(std::map<std::string, std::string> _params)
 {
     params_.insert(_params.begin(), _params.end());
 }
+
+bool ParamsServer::hasParam(std::string _key) const
+{
+    return (params_.count(_key) != 0);
+}
diff --git a/src/yaml/sensor_pose_yaml.cpp b/src/yaml/sensor_pose_yaml.cpp
index bfbe151c43315c21e5f636e2632fa9ceb614c9d4..4ce97752fc8d5a59aed7c7b0a4b0c3f89de07405 100644
--- a/src/yaml/sensor_pose_yaml.cpp
+++ b/src/yaml/sensor_pose_yaml.cpp
@@ -49,8 +49,12 @@ static ParamsSensorBasePtr createParamsSensorPose(const std::string & _filename_
     {
         ParamsSensorPosePtr params = std::make_shared<ParamsSensorPose>();
 
-        params->std_p = config["std_p"].as<double>();
-        params->std_o = config["std_o"].as<double>();
+        params->std_px = config["std_p"].as<double>();
+        params->std_py = config["std_p"].as<double>();
+        params->std_pz = config["std_p"].as<double>();
+        params->std_ox = config["std_o"].as<double>();
+        params->std_oy = config["std_o"].as<double>();
+        params->std_oz = config["std_o"].as<double>();
 
         return params;
     }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 6fa50395a7ee2af50d290b32e543b18bac534869..299594805cb46a551145613baf30481f4e7aed22 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -173,6 +173,18 @@ 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)
+
+# FactorRelativePosition3d class test
+wolf_add_gtest(gtest_factor_relative_position_3d gtest_factor_relative_position_3d.cpp)
+
+# FactorRelativePosition3dWithExtrinsics class test
+wolf_add_gtest(gtest_factor_relative_position_3d_with_extrinsics gtest_factor_relative_position_3d_with_extrinsics.cpp)
+
 # FactorVelocityLocalDirection3d class test
 wolf_add_gtest(gtest_factor_velocity_local_direction_3d gtest_factor_velocity_local_direction_3d.cpp)
 
@@ -191,6 +203,9 @@ wolf_add_gtest(gtest_processor_fixed_wing_model gtest_processor_fixed_wing_model
 # ProcessorDiffDrive class test
 wolf_add_gtest(gtest_processor_diff_drive gtest_processor_diff_drive.cpp)
 
+# ProcessorLandmarkExternal class test
+wolf_add_gtest(gtest_processor_landmark_external gtest_processor_landmark_external.cpp)
+
 # ProcessorLoopClosure class test
 wolf_add_gtest(gtest_processor_loop_closure gtest_processor_loop_closure.cpp)
 
diff --git a/test/dummy/processor_tracker_landmark_dummy.cpp b/test/dummy/processor_tracker_landmark_dummy.cpp
index 54525c8682b4c80d95e3e38b2ef34784e4a84cfe..b72b6a3e677d29889cf80a14a81fc32d163944b2 100644
--- a/test/dummy/processor_tracker_landmark_dummy.cpp
+++ b/test/dummy/processor_tracker_landmark_dummy.cpp
@@ -29,6 +29,8 @@
 #include "processor_tracker_landmark_dummy.h"
 #include "core/landmark/landmark_base.h"
 #include "factor_landmark_dummy.h"
+#include "core/state_block/state_angle.h"
+#include "core/state_block/state_block_derived.h"
 
 namespace wolf
 {
@@ -124,8 +126,8 @@ LandmarkBasePtr ProcessorTrackerLandmarkDummy::emplaceLandmark(FeatureBasePtr _f
     //std::cout << "ProcessorTrackerLandmarkDummy::emplaceLandmark" << std::endl;
     return LandmarkBase::emplace<LandmarkBase>(getProblem()->getMap(),
                                                "LandmarkBase",
-                                               std::make_shared<StateBlock>(2),
-                                               std::make_shared<StateBlock>(1));
+                                               std::make_shared<StatePoint2d>(Vector2d::Zero()),
+                                               std::make_shared<StateAngle>(0));
 }
 
 FactorBasePtr ProcessorTrackerLandmarkDummy::emplaceFactor(FeatureBasePtr _feature_ptr,
diff --git a/test/gtest_SE3.cpp b/test/gtest_SE3.cpp
index be07e98047c8074979d036074f2df6ee490e9a90..614d6f7f4cc04430d4a595209a94039bbbacd5cb 100644
--- a/test/gtest_SE3.cpp
+++ b/test/gtest_SE3.cpp
@@ -19,19 +19,10 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
-/**
- * \file gtest_SE3.cpp
- *
- *  Created on: Feb 2, 2019
- *      \author: jsola
- */
-
 
 #include "core/math/SE3.h"
 #include "core/utils/utils_gtest.h"
 
-
-
 using namespace Eigen;
 using namespace wolf;
 using namespace SE3;
@@ -103,6 +94,55 @@ TEST(SE3, log_of_power)
     ASSERT_MATRIX_APPROX(tau3, log(pose3), 1e-8);
 }
 
+TEST(SE3, inverse)
+{
+    Vector3d p; p.setRandom();
+    Vector4d qvec; qvec.setRandom().normalized();
+    Quaterniond q(qvec);
+
+    Vector3d pi_true = -(q.conjugate() * p);
+    Quaterniond qi_true = q.conjugate();
+
+    // separate API
+    Vector3d pi_out;
+    Quaterniond qi_out;
+
+    inverse(p, q, pi_out, qi_out);
+    ASSERT_MATRIX_APPROX(pi_out, pi_true, 1e-8);
+    ASSERT_MATRIX_APPROX(qi_out.coeffs(), qi_true.coeffs(), 1e-8);
+
+    // Vector7d API
+    Vector7d pose; pose << p, q.coeffs();
+    Vector7d posei_true; posei_true << pi_true, qi_true.coeffs();
+    Vector7d posei_out;
+    inverse(pose, posei_out);
+    ASSERT_MATRIX_APPROX(posei_out, posei_true, 1e-8);
+
+    posei_out = inverse(pose);
+    ASSERT_MATRIX_APPROX(posei_out, posei_true, 1e-8);
+}
+
+TEST(SE3, inverseComposite)
+{
+    Vector3d p; p.setRandom();
+    Vector4d qvec; qvec.setRandom().normalized();
+    VectorComposite pose_vc("PO", {p, qvec});
+    Quaterniond q(qvec);
+
+    // ground truth
+    Vector3d pi_true = -(q.conjugate() * p);
+    Quaterniond qi_true = q.conjugate();
+
+    VectorComposite pose_vc_out("PO", {3, 4});
+    inverse(pose_vc, pose_vc_out);
+    ASSERT_MATRIX_APPROX(pose_vc_out.at('P'), pi_true, 1e-8);
+    ASSERT_MATRIX_APPROX(pose_vc_out.at('O'), qi_true.coeffs(), 1e-8);
+
+    VectorComposite pose_vc_out_bis = inverse(pose_vc);
+    ASSERT_MATRIX_APPROX(pose_vc_out_bis.at('P'), pi_true, 1e-8);
+    ASSERT_MATRIX_APPROX(pose_vc_out_bis.at('O'), qi_true.coeffs(), 1e-8);
+}
+
 TEST(SE3, composeBlocks)
 {
     Vector3d p1, p2, pc;
@@ -144,7 +184,6 @@ TEST(SE3, composeEigenVectors)
 
     compose(p1, q1, p2, q2, pc, qc); // tested in composeVectorBlocks
 
-
     Vector7d x1; x1 << p1, q1.coeffs();
     Vector7d x2; x2 << p2, q2.coeffs();
     Vector7d xc, xc_true; xc_true << pc, qc.coeffs();
@@ -176,6 +215,10 @@ TEST(SE3, composeVectorComposite)
 
     ASSERT_MATRIX_APPROX(xc.at('P'), pc, 1e-8);
     ASSERT_MATRIX_APPROX(xc.at('O'), qc.coeffs(), 1e-8);
+
+    VectorComposite vc = compose(x1, x2);
+    ASSERT_MATRIX_APPROX(vc.at('P'), pc, 1e-8);
+    ASSERT_MATRIX_APPROX(vc.at('O'), qc.coeffs(), 1e-8);
 }
 
 TEST(SE3, composeVectorComposite_Jacobians)
@@ -233,7 +276,6 @@ TEST(SE3, exp_0_Composite)
 
     ASSERT_MATRIX_APPROX(x.at('P'), Vector3d::Zero(), 1e-8);
     ASSERT_MATRIX_APPROX(x.at('O'), Quaterniond::Identity().coeffs(), 1e-8);
-
 }
 
 TEST(SE3, plus_0_Composite)
@@ -397,6 +439,7 @@ TEST(SE3, interpolate_third)
     ASSERT_MATRIX_APPROX(p_pre, p_gt, 1e-8);
 }
 
+
 TEST(SE3, toSE3_I)
 {
     Vector7d pose; pose << 0,0,0, 0,0,0,1;
diff --git a/test/gtest_capture_base.cpp b/test/gtest_capture_base.cpp
index aa36078108a936109484bd4a8d761c258105d236..aaf3aa4fbaca881a24b520ecb85795d5fb0f9b7d 100644
--- a/test/gtest_capture_base.cpp
+++ b/test/gtest_capture_base.cpp
@@ -29,6 +29,7 @@
 #include "core/utils/utils_gtest.h"
 
 #include "core/capture/capture_base.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/state_block/state_angle.h"
 
 using namespace wolf;
@@ -58,9 +59,9 @@ TEST(CaptureBase, ConstructorWithSensor)
 
 TEST(CaptureBase, Static_sensor_params)
 {
-    StateBlockPtr p(std::make_shared<StateBlock>(2));
-    StateBlockPtr o(std::make_shared<StateAngle>() );
-    StateBlockPtr i(std::make_shared<StateBlock>(2));
+    StateBlockPtr p(std::make_shared<StatePoint2d>(Vector2d::Zero()));
+    StateBlockPtr o(std::make_shared<StateAngle>(0) );
+    StateBlockPtr i(std::make_shared<StateParams2>(Vector2d::Zero()));
     SensorBasePtr S(std::make_shared<SensorBase>("DUMMY", p, o, i, 2));
     CaptureBasePtr C(std::make_shared<CaptureBase>("DUMMY", 1.5, S)); // timestamp = 1.5
 
@@ -77,10 +78,10 @@ TEST(CaptureBase, Static_sensor_params)
 
 TEST(CaptureBase, Dynamic_sensor_params)
 {
-    StateBlockPtr p(std::make_shared<StateBlock>(2));
-    StateBlockPtr o(std::make_shared<StateAngle>() );
-    StateBlockPtr i(std::make_shared<StateBlock>(2));
-    SensorBasePtr S(std::make_shared<SensorBase>("DUMMY", std::make_shared<StateBlock>(2), std::make_shared<StateAngle>(), std::make_shared<StateBlock>(2), 2, true, true, true)); // last 3 'true' mark dynamic
+    StateBlockPtr p(std::make_shared<StatePoint2d>(Vector2d::Zero()));
+    StateBlockPtr o(std::make_shared<StateAngle>(0) );
+    StateBlockPtr i(std::make_shared<StateParams2>(Vector2d::Zero()));
+    SensorBasePtr S(std::make_shared<SensorBase>("DUMMY", std::make_shared<StatePoint2d>(Vector2d::Zero()), std::make_shared<StateAngle>(0), std::make_shared<StateParams2>(Vector2d::Zero()), 2, true, true, true)); // last 3 'true' mark dynamic
     CaptureBasePtr C(std::make_shared<CaptureBase>("DUMMY", 1.5, S, p, o, i)); // timestamp = 1.5
 
     // query sensor blocks -- need KFs to find some Capture with the params
diff --git a/test/gtest_emplace.cpp b/test/gtest_emplace.cpp
index 0aa294035d9f712b706883636e46cd1e4ae62593..956f6eb4e76f44b333ea5253a0023c0ec4fadac5 100644
--- a/test/gtest_emplace.cpp
+++ b/test/gtest_emplace.cpp
@@ -28,7 +28,8 @@
 
 #include "core/utils/utils_gtest.h"
 
-
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 #include "core/problem/problem.h"
 #include "core/sensor/sensor_base.h"
 #include "core/sensor/sensor_odom_2d.h"
@@ -64,7 +65,7 @@ TEST(Emplace, Frame)
     ProblemPtr P = Problem::create("POV", 3);
 
     ASSERT_NE(P->getTrajectory(), nullptr);
-    FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StateBlock>(2,true), std::make_shared<StateBlock>(2,true));
+    FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StatePoint2d>(Vector2d::Zero(),true), std::make_shared<StateAngle>(2,true));
     ASSERT_EQ(P, P->getTrajectory()->getFirstFrame()->getTrajectory()->getProblem());
 }
 
@@ -89,7 +90,7 @@ TEST(Emplace, Capture)
     ProblemPtr P = Problem::create("POV", 3);
 
     ASSERT_NE(P->getTrajectory(), nullptr);
-    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StateBlock>(2,true), std::make_shared<StateBlock>(2,true));
+    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StatePoint2d>(Vector2d::Zero(),true), std::make_shared<StateAngle>(0,true));
     ASSERT_EQ(P, P->getTrajectory()->getFirstFrame()->getTrajectory()->getProblem());
 
     auto cpt = CaptureBase::emplace<CaptureBase>(frm, "Dummy", TimeStamp(0), nullptr, nullptr, nullptr, nullptr);
@@ -103,7 +104,7 @@ TEST(Emplace, Feature)
     ProblemPtr P = Problem::create("POV", 3);
 
     ASSERT_NE(P->getTrajectory(), nullptr);
-    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StateBlock>(2,true), std::make_shared<StateBlock>(2,true));
+    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StatePoint2d>(Vector2d::Zero(),true), std::make_shared<StateAngle>(2,true));
     ASSERT_EQ(P, P->getTrajectory()->getFirstFrame()->getTrajectory()->getProblem());
 
     auto cpt = CaptureBase::emplace<CaptureBase>(frm, "Dummy", TimeStamp(0), nullptr, nullptr, nullptr, nullptr);
@@ -121,7 +122,7 @@ TEST(Emplace, Factor)
     ProblemPtr P = Problem::create("POV", 3);
 
     ASSERT_NE(P->getTrajectory(), nullptr);
-    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StateBlock>(2,true), std::make_shared<StateBlock>(2,true));
+    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StatePoint2d>(Vector2d::Zero(),true), std::make_shared<StateAngle>(2,true));
     ASSERT_EQ(P, P->getTrajectory()->getFirstFrame()->getTrajectory()->getProblem());
 
     auto cpt = CaptureBase::emplace<CaptureBase>(frm, "Dummy", TimeStamp(0), nullptr, nullptr, nullptr, nullptr);
@@ -141,7 +142,7 @@ TEST(Emplace, EmplaceDerived)
 {
     ProblemPtr P = Problem::create("POV", 3);
 
-    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StateBlock>(2,true), std::make_shared<StateBlock>(2,true));
+    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StatePoint2d>(Vector2d::Zero(),true), std::make_shared<StateAngle>(2,true));
     auto sen = SensorBase::emplace<SensorOdom2d>(P->getHardware(), Eigen::VectorXd(3), ParamsSensorOdom2d());
     auto cov = Eigen::MatrixXd::Identity(2,2);
     auto cpt = CaptureBase::emplace<CaptureOdom2d>(frm, TimeStamp(0), sen, Eigen::VectorXd(2), cov, nullptr);
@@ -163,7 +164,7 @@ TEST(Emplace, ReturnDerived)
     ProblemPtr P = Problem::create("POV", 3);
 
     ASSERT_NE(P->getTrajectory(), nullptr);
-    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StateBlock>(2,true), std::make_shared<StateBlock>(2,true));
+    auto frm = FrameBase::emplace<FrameBase>(P->getTrajectory(),  TimeStamp(0), std::make_shared<StatePoint2d>(Vector2d::Zero(),true), std::make_shared<StateAngle>(2,true));
     auto cpt = CaptureBase::emplace<CaptureBase>(frm, "Dummy", TimeStamp(0), nullptr, nullptr, nullptr, nullptr);
     auto cov = Eigen::MatrixXd::Identity(2,2);
     auto ftr = FeatureBase::emplace<FeatureOdom2d>(cpt, Eigen::VectorXd(2), cov);
diff --git a/test/gtest_factor_autodiff.cpp b/test/gtest_factor_autodiff.cpp
index 7a1a330916e5ba4a5e7c28f784bd6bfd21278da7..a2f8dfaf9f6bfe3a08ae392937b186377476e28e 100644
--- a/test/gtest_factor_autodiff.cpp
+++ b/test/gtest_factor_autodiff.cpp
@@ -30,6 +30,7 @@
 #include "dummy/factor_dummy_zero_1.h"
 #include "dummy/factor_dummy_zero_12.h"
 
+#include "core/state_block/state_block_derived.h"
 #include "core/factor/factor_relative_pose_2d.h"
 #include "core/utils/utils_gtest.h"
 #include "core/sensor/sensor_odom_2d.h"
@@ -44,7 +45,7 @@ using namespace Eigen;
 
 TEST(FactorAutodiff, AutodiffDummy1)
 {
-    StateBlockPtr sb = std::make_shared<StateBlock>(Eigen::Vector1d::Random());
+    StateBlockPtr sb = std::make_shared<StateParams1>(Eigen::Vector1d::Random());
     FeatureBasePtr ftr = std::make_shared<FeatureBase>("Feature", Vector2d(1,2), Matrix2d::Identity());
 
     auto fac = std::make_shared<FactorDummyZero1>(ftr, sb);
@@ -61,18 +62,18 @@ TEST(FactorAutodiff, AutodiffDummy1)
 
 TEST(FactorAutodiff, AutodiffDummy12)
 {
-    StateBlockPtr sb1 = std::make_shared<StateBlock>(Eigen::Vector1d::Random());
-    StateBlockPtr sb2 = std::make_shared<StateBlock>(Eigen::Vector2d::Random());
-    StateBlockPtr sb3 = std::make_shared<StateBlock>(Eigen::Vector3d::Random());
-    StateBlockPtr sb4 = std::make_shared<StateBlock>(Eigen::Vector4d::Random());
-    StateBlockPtr sb5 = std::make_shared<StateBlock>(Eigen::Vector5d::Random());
-    StateBlockPtr sb6 = std::make_shared<StateBlock>(Eigen::Vector6d::Random());
-    StateBlockPtr sb7 = std::make_shared<StateBlock>(Eigen::Vector7d::Random());
-    StateBlockPtr sb8 = std::make_shared<StateBlock>(Eigen::Vector8d::Random());
-    StateBlockPtr sb9 = std::make_shared<StateBlock>(Eigen::Vector9d::Random());
-    StateBlockPtr sb10 = std::make_shared<StateBlock>(Eigen::Vector10d::Random());
-    StateBlockPtr sb11 = std::make_shared<StateBlock>(Eigen::VectorXd::Random(11));
-    StateBlockPtr sb12 = std::make_shared<StateBlock>(Eigen::VectorXd::Random(12));
+    StateBlockPtr sb1 = std::make_shared<StateParams1>(Eigen::Vector1d::Random());
+    StateBlockPtr sb2 = std::make_shared<StateParams2>(Eigen::Vector2d::Random());
+    StateBlockPtr sb3 = std::make_shared<StateParams3>(Eigen::Vector3d::Random());
+    StateBlockPtr sb4 = std::make_shared<StateParams4>(Eigen::Vector4d::Random());
+    StateBlockPtr sb5 = std::make_shared<StateParams5>(Eigen::Vector5d::Random());
+    StateBlockPtr sb6 = std::make_shared<StateParams6>(Eigen::Vector6d::Random());
+    StateBlockPtr sb7 = std::make_shared<StateParams7>(Eigen::Vector7d::Random());
+    StateBlockPtr sb8 = std::make_shared<StateParams8>(Eigen::Vector8d::Random());
+    StateBlockPtr sb9 = std::make_shared<StateParams9>(Eigen::Vector9d::Random());
+    StateBlockPtr sb10 = std::make_shared<StateParams10>(Eigen::Vector10d::Random());
+    StateBlockPtr sb11 = std::make_shared<StateParams<11>>(Eigen::VectorXd::Random(11));
+    StateBlockPtr sb12 = std::make_shared<StateParams<12>>(Eigen::VectorXd::Random(12));
 
     std::vector<const double*> states_ptr({sb1->getStateData(),sb2->getStateData(),sb3->getStateData(),sb4->getStateData(),sb5->getStateData(),sb6->getStateData(),sb7->getStateData(),sb8->getStateData(),sb9->getStateData(),sb10->getStateData(),sb11->getStateData(),sb12->getStateData()});
     std::vector<Eigen::MatrixXd> J;
diff --git a/test/gtest_factor_diff_drive.cpp b/test/gtest_factor_diff_drive.cpp
index c37bed03e30da209b9b644d49b92011afeeb5f93..2cbbc7e4c732a9c42959cac3d3a01cc769154d5a 100644
--- a/test/gtest_factor_diff_drive.cpp
+++ b/test/gtest_factor_diff_drive.cpp
@@ -105,16 +105,16 @@ class ProcessorDiffDrivePublic : public ProcessorDiffDrive
             return Base::emplaceCapture(_frame_own, _sensor, _ts, _data, _data_cov, _calib, _calib_preint, _capture_origin);
         }
 
-        FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_own) override
-        {
-            return Base::emplaceFeature(_capture_own);
-        }
-
-        FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion,
-                                            CaptureBasePtr _capture_origin) override
-        {
-            return Base::emplaceFactor(_feature_motion, _capture_origin);
-        }
+        // FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_own) override
+        // {
+        //     return Base::emplaceFeature(_capture_own);
+        // }
+
+        // FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion,
+        //                                     CaptureBasePtr _capture_origin) override
+        // {
+        //     return Base::emplaceFactor(_feature_motion, _capture_origin);
+        // }
 
         ParamsProcessorDiffDrivePtr getParams()
         {
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 ee2bc076da1c48876250197c2fa1179ede489306..b5b4c9d0f36318408b0e056a7bdc3b17def589e7 100644
--- a/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp
+++ b/test/gtest_factor_relative_pose_2d_with_extrinsics.cpp
@@ -25,8 +25,9 @@
 #include "core/factor/factor_relative_pose_2d_with_extrinsics.h"
 #include "core/capture/capture_odom_2d.h"
 #include "core/sensor/sensor_odom_2d.h"
-#include "core/processor/processor_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;
@@ -36,6 +37,11 @@ using std::endl;
 
 std::string wolf_root = _WOLF_ROOT_DIR;
 
+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();
 
@@ -44,212 +50,307 @@ 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 processor_odom2d = problem_ptr->installProcessor("ProcessorOdom2d", "odom", sensor_odom2d, std::make_shared<ParamsProcessorOdom2d>());
+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() );
 FrameBasePtr frm1 = problem_ptr->emplaceFrame(1, Vector3d::Zero() );
 
-// Capture from frm1 to frm0
-auto cap1 = CaptureBase::emplace<CaptureOdom2d>(frm1, 1, sensor_odom2d, Vector3d::Zero(), data_cov);
+// Landmark
+LandmarkBasePtr lmk = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                          "LandmarkPose2d",
+                                                          std::make_shared<StatePoint2d>(Vector2d::Zero()),
+                                                          std::make_shared<StateAngle>(Vector1d::Zero()));
 
-TEST(FactorRelativePose2dWithExtrinsics, check_tree)
+// Capture
+auto cap1 = CaptureBase::emplace<CaptureOdom2d>(frm1, 1, sensor, Vector3d::Zero(), data_cov);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePose2dWithExtrinsicsPtr fac = nullptr;
+
+void generateRandomProblemFrame()
 {
-    ASSERT_TRUE(problem_ptr->check(0));
+    // 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
 }
 
-TEST(FactorRelativePose2dWithExtrinsics, fix_0_solve)
+void generateRandomProblemLandmark()
 {
-    for (int i = 0; i < 1e2; i++)
-    {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
-
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
+    // 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) + x_s(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
+}
 
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
+////////////// TESTS /////////////////////////////////////////////////////////////////////
 
-        // 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>();
+TEST(FactorRelativePose2dWithExtrinsics, check_tree)
+{
+    ASSERT_TRUE(problem_ptr->check(0));
+}
 
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
+// FRAME TO FRAME =========================================================================
+TEST(FactorRelativePose2dWithExtrinsics, frame_solve_frame1)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
 
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, 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();
     }
 }
 
-TEST(FactorRelativePose2dWithExtrinsics, fix_1_solve)
+TEST(FactorRelativePose2dWithExtrinsics, frame_solve_frame0)
 {
-    for (int i = 0; i < 1e2; i++)
+    for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
+        // Perturb frm0, fix the rest
+        frm1->fix();
+        frm0->unfix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm0->perturb(1);
 
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        // 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>();
+        ASSERT_POSE2d_APPROX(frm0->getStateVector(), x_0, 1e-3);
 
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
 
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, false, TOP_MOTION); // create and add
+// 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 setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
-        // Perturb frm0, fix the rest
+        // Perturb sensor P, fix the rest
         frm1->fix();
-        frm0->unfix();
-        sensor_odom2d->getP()->fix();
-        sensor_odom2d->getO()->fix();
-        frm0->perturb(5);
+        frm0->fix();
+        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(frm0->getStateVector(), x0, 1e-6);
+        //ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
-TEST(FactorRelativePose2dWithExtrinsics, extrinsics_p_solve)
+TEST(FactorRelativePose2dWithExtrinsics, frame_solve_extrinsics_o)
 {
-    for (int i = 0; i < 1e2; i++)
+    for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
+        // random setup
+        generateRandomProblemFrame();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
+        // Perturb sensor O, fix the rest
+        frm1->fix();
+        frm0->fix();
+        sensor->getP()->fix();
+        sensor->getO()->unfix();
+        sensor->getO()->perturb(.2);
 
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        // 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>();
+        ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
 
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
 
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, false, TOP_MOTION); // create and add
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePose2dWithExtrinsics, landmark_solve_lmk)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
-        // Perturb sensor P, fix the rest
+        // Perturb landmark, fix the rest
         frm1->fix();
-        frm0->fix();
-        sensor_odom2d->getP()->unfix();
-        sensor_odom2d->getO()->fix();
-        sensor_odom2d->getP()->perturb(1);
+        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_POSE2d_APPROX(lmk->getStateVector(), x_l, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
+
+TEST(FactorRelativePose2dWithExtrinsics, landmark_solve_frame)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), 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::BRIEF);
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        ASSERT_POSE2d_APPROX(sensor_odom2d->getStateVector(), xs, 1e-6);
+        ASSERT_POSE2d_APPROX(frm1->getStateVector(), x_f, 1e-3);
 
         // remove feature (and factor) for the next loop
-        fea1->remove();
+        fea->remove();
     }
 }
 
-TEST(FactorRelativePose2dWithExtrinsics, extrinsics_o_solve)
+TEST(FactorRelativePose2dWithExtrinsics, landmark_extrinsics_p_solve)
 {
-    for (int i = 0; i < 1e2; i++)
+    for (int i = 0; i < N; i++)
     {
-        // Random delta
-        Vector3d delta = Vector3d::Random() * 10;
-        pi2pi(delta(2));
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(3), Constants::EPS);
 
-        // Random initial pose
-        Vector3d x0 = Vector3d::Random() * 10;
-        pi2pi(x0(2));
+        // Perturb sensor P, fix the rest
+        frm1->fix();
+        lmk->fix();
+        sensor->getP()->unfix();
+        sensor->getO()->fix();
+        sensor->getP()->perturb(1);
 
-        // Random extrinsics
-        Vector3d xs = Vector3d::Random();
-        pi2pi(xs(2));
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-        // 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>();
+        ASSERT_POSE2d_APPROX(sensor->getStateVector(), x_s, 1e-3);
 
-        // Set states
-        frm0->setState(x0);
-        frm1->setState(x1);
-        cap1->setData(delta);
-        sensor_odom2d->setState(xs);
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
+}
 
-        // feature & factor with delta measurement
-        auto fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureOdom2d", delta, data_cov);
-        FactorBase::emplace<FactorRelativePose2dWithExtrinsics>(fea1, fea1, frm0, processor_odom2d, false, TOP_MOTION); // create and add
+TEST(FactorRelativePose2dWithExtrinsics, landmark_extrinsics_o_solve)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        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);
+        lmk->fix();
+        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..3b7f25c8fba43f0d56f5146fd2d5168e83733631 100644
--- a/test/gtest_factor_relative_pose_3d.cpp
+++ b/test/gtest_factor_relative_pose_3d.cpp
@@ -19,18 +19,14 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
-/**
- * \file gtest_factor_relative_pose_3d.cpp
- *
- *  Created on: Mar 30, 2017
- *      \author: jsola
- */
 
 #include "core/utils/utils_gtest.h"
 
 #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 +34,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 0f89a11003bd0e297ed6b3df283b6aba536ca5ea..2e99b1d2b765998d11c7eba96f7241e29977a7c4 100644
--- a/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp
+++ b/test/gtest_factor_relative_pose_3d_with_extrinsics.cpp
@@ -19,307 +19,353 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
-#include <core/factor/factor_relative_pose_3d_with_extrinsics.h>
-#include <core/utils/utils_gtest.h>
+#include "core/utils/utils_gtest.h"
 
-#include "core/common/wolf.h"
-#include "core/utils/logging.h"
-
-#include "core/state_block/state_quaternion.h"
 #include "core/ceres_wrapper/solver_ceres.h"
-#include "core/capture/capture_pose.h"
-#include "core/feature/feature_pose.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::static_pointer_cast;
-
-
-
-// Use the following in case you want to initialize tests with predefines variables or methods.
-class FactorRelativePose3dWithExtrinsics_class : public testing::Test{
-    public:
-        Vector3d    pos_camera,   pos_robot,   pos_landmark;
-        Vector3d    euler_camera, euler_robot, euler_landmark;
-        Quaterniond quat_camera,  quat_robot,  quat_landmark;
-        Vector4d    vquat_camera, vquat_robot, vquat_landmark; // quaternions as vectors
-        Vector7d    pose_camera,  pose_robot,  pose_landmark;
-
-        ProblemPtr     problem;
-        SolverCeresPtr solver;
-
-        SensorBasePtr   S;
-        FrameBasePtr    F1;
-        CapturePosePtr  c1;
-        FeaturePosePtr  f1;
-        LandmarkBasePtr lmk1;
-        FactorRelativePose3dWithExtrinsicsPtr fac;
-
-        Eigen::Matrix6d meas_cov;
-
-        void SetUp() override
-        {
-            // configuration
-
-             /* We have three poses to take into account:
-             *  - pose of the sensor (extrinsincs)
-             *  - pose of the landmark
-             *  - pose of the robot (Keyframe)
-             *
-             * The measurement provides the pose of the landmark wrt sensor's current pose in the world.
-             * Camera's current pose in World is the composition of the robot pose with the sensor extrinsics.
-             *
-             * The robot has a sensor looking forward
-             *   S: pos = (0,0,0), ori = (0, 0, 0)
-             *
-             * There is a point at the origin
-             *   P: pos = (0,0,0)
-             *
-             * Therefore, P projects exactly at the origin of the sensor,
-             *   f: p = (0,0)
-             *
-             * We form a Wolf tree with 1 frames F1, 1 capture C1,
-             * 1 feature f1 (measurement), 1 landmark l and 1 relative kf landmark constraint c1:
-             *
-             *   Frame F1, Capture C1, feature f1, landmark l1, constraint c1
-             *
-             * The frame pose F1, and the sensor pose S
-             * in the robot frame are variables subject to optimization
-             *
-             * We perform a number of tests based on this configuration.*/
-
-
-            // sensor is at origin of robot and looking forward
-            // robot is at (0,0,0)
-            // landmark is right in front of sensor. Its orientation wrt sensor is identity.
-            pos_camera      << 0,0,0;
-            pos_robot       << 0,0,0; //robot is at origin
-            pos_landmark    << 0,1,0;
-            euler_camera    << 0,0,0;
-            //euler_camera    << -M_PI_2, 0, -M_PI_2;
-            euler_robot     << 0,0,0;
-            euler_landmark  << 0,0,0;
-            quat_camera     = e2q(euler_camera);
-            quat_robot      = e2q(euler_robot);
-            quat_landmark   = e2q(euler_landmark);
-            vquat_camera    = quat_camera.coeffs();
-            vquat_robot     = quat_robot.coeffs();
-            vquat_landmark  = quat_landmark.coeffs();
-            pose_camera     << pos_camera, vquat_camera;
-            pose_robot      << pos_robot, vquat_robot;
-            pose_landmark   << pos_landmark, vquat_landmark;
-
-            // Build problem
-            problem = Problem::create("PO", 3);
-            solver = std::make_shared<SolverCeres>(problem);
-
-            // Create sensor to be able to initialize (a camera for instance)
-            S = SensorBase::emplace<SensorBase>(problem->getHardware(), "SensorPose", 
-                                                std::make_shared<StateBlock>(pos_camera, true), 
-                                                std::make_shared<StateQuaternion>(vquat_camera, true), 
-                                                std::make_shared<StateBlock>(Vector4d::Zero(), false),  // fixed
-                                                Vector1d::Zero());
-
-            // ParamsSensorCameraPtr params_camera = std::make_shared<ParamsSensorCamera>();
-            // S      = problem->installSensor("SensorCamera", "canonical", pose_camera, params_camera);
-            // camera = std::static_pointer_cast<SensorCamera>(S);
-
-
-            // F1 is be origin KF
-            VectorComposite x0(pose_robot, "PO", {3,4});
-            VectorComposite s0("PO", {Vector3d(1,1,1), Vector3d(1,1,1)});
-            F1 = problem->setPriorFactor(x0, s0, 0.0);
-
-
-            // the sensor is at origin as well as the robot. The measurement matches with the pose of the tag wrt sensor (and also wrt robot and world)
-            meas_cov = Eigen::Matrix6d::Identity();
-            meas_cov.topLeftCorner(3,3)     *= 1e-2;
-            meas_cov.bottomRightCorner(3,3) *= 1e-3;
-
-            //emplace feature and landmark
-            c1 = std::static_pointer_cast<CapturePose>(CaptureBase::emplace<CapturePose>(F1, 0, S, pose_landmark, meas_cov));
-            f1 = std::static_pointer_cast<FeaturePose>(FeatureBase::emplace<FeaturePose>(c1, pose_landmark, meas_cov));
-            lmk1 = LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkPose",  
-                                                       std::make_shared<StateBlock>(pos_landmark), 
-                                                       std::make_shared<StateQuaternion>(Quaterniond(vquat_landmark)));
-        }
-};
-
-
-TEST_F(FactorRelativePose3dWithExtrinsics_class, Constructor)
-{
-    auto factor = std::make_shared<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                       lmk1,
-                                                                       nullptr,
-                                                                       false);
+using std::cout;
+using std::endl;
 
-    ASSERT_TRUE(factor->getType() == "FactorRelativePose3dWithExtrinsics");
-}
+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()));
 
-/////////////////////////////////////////////
-// Tree not ok with this gtest implementation
-/////////////////////////////////////////////
-TEST_F(FactorRelativePose3dWithExtrinsics_class, Check_tree)
+// Capture
+auto cap1 = CaptureBase::emplace<CaptureOdom3d>(frm1, 1, sensor, Vector7d::Zero(), data_cov);
+// Feature
+FeatureBasePtr fea = nullptr;
+// Factor
+FactorRelativePose3dWithExtrinsicsPtr fac = nullptr;
+
+void generateRandomProblemFrame()
 {
-    ASSERT_TRUE(problem->check(1));
-
-    auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                          f1,
-                                                                          lmk1,
-                                                                          nullptr,
-                                                                          false);
-    ASSERT_TRUE(problem->check(1));
+    // 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
 }
 
-TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_F1_P_perturbated)
+void generateRandomProblemLandmark()
 {
-    auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                          f1,
-                                                                          lmk1,
-                                                                          nullptr,
-                                                                          false);
-
-    // unfix F1, perturbate state
-    F1->unfix();
-    F1->getP()->perturb();
-
-    std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport
-    ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 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>());
+
+    // 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_F(FactorRelativePose3dWithExtrinsics_class, solve_F1_O_perturbated)
+TEST(FactorRelativePose3dWithExtrinsics, check_tree)
 {
-    auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                          f1,
-                                                                          lmk1,
-                                                                          nullptr,
-                                                                          false);
-
-    // unfix F1, perturbate state
-    F1->unfix();
-    F1->getO()->perturb();
-
-    std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport
-    ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6);
+    ASSERT_TRUE(problem_ptr->check(0));
 }
 
-TEST_F(FactorRelativePose3dWithExtrinsics_class, Check_initialization)
+// FRAME TO FRAME =========================================================================
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame1)
 {
-    auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                          f1,
-                                                                          lmk1,
-                                                                          nullptr,
-                                                                          false);
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
+
+        ASSERT_MATRIX_APPROX(fac->residual(), VectorXd::Zero(6), Constants::EPS);
+
+        // Perturb frm1, fix the rest
+        frm0->fix();
+        frm1->unfix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm1->perturb(1);
+
+        // solve for frm1
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
 
-    ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6);
-    ASSERT_MATRIX_APPROX(f1->getMeasurement(), pose_landmark, 1e-6);
-    ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), pose_landmark, 1e-6);
+        ASSERT_POSE3d_APPROX(frm1->getStateVector(), x_1, 1e-3);
 
+        // remove feature (and factor) for the next loop
+        fea->remove();
+    }
 }
 
-TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_L1_P_perturbated)
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_frame0)
 {
-    auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                          f1,
-                                                                          lmk1,
-                                                                          nullptr,
-                                                                          false);
-
-    // unfix lmk1, perturbate state
-    lmk1->unfix();
-    lmk1->getP()->perturb();
-
-    std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport
-    ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6);
-    ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), pose_landmark, 1e-6);
+    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->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();
+    }
 }
 
-TEST_F(FactorRelativePose3dWithExtrinsics_class, solve_L1_O_perturbated)
+// JV: The position extrinsics in case of pair of frames is not observable
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_p)
 {
-    auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                          f1,
-                                                                          lmk1,
-                                                                          nullptr,
-                                                                          false);
+    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->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();
+    }
+}
 
-    // unfix F1, perturbate state
-    lmk1->unfix();
-    lmk1->getO()->perturb();
+TEST(FactorRelativePose3dWithExtrinsics, frame_solve_extrinsics_o)
+{
+    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->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();
+    }
+}
 
-    std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport
-    ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), pose_robot, 1e-6);
-    ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), pose_landmark, 1e-6);
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePose3dWithExtrinsics, landmark_solve_lmk)
+{
+    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->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)
+{
+    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->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_F(FactorRelativePose3dWithExtrinsics_class, solve_L1_PO_perturbated)
+TEST(FactorRelativePose3dWithExtrinsics, landmark_extrinsics_p_solve)
 {
-    // Change setup
-    Vector3d p_w_r, p_r_c, p_c_l, p_w_l;
-    Quaterniond q_w_r, q_r_c, q_c_l, q_w_l;
-    p_w_r << 1, 2, 3;
-    p_r_c << 4, 5, 6;
-    p_c_l << 7, 8, 9;
-    q_w_r.coeffs() << 1, 2, 3, 4; q_w_r.normalize();
-    q_r_c.coeffs() << 4, 5, 6, 7; q_r_c.normalize();
-    q_c_l.coeffs() << 7, 8, 9, 0; q_c_l.normalize();
-
-    q_w_l = q_w_r * q_r_c * q_c_l;
-    p_w_l = p_w_r + q_w_r * (p_r_c + q_r_c * p_c_l);
-
-    // Change feature (remove and emplace)
-    Vector7d meas;
-    meas << p_c_l, q_c_l.coeffs();
-    f1->remove();
-    f1 = std::static_pointer_cast<FeaturePose>(FeatureBase::emplace<FeaturePose>(c1, meas, meas_cov));
-
-    // emplace factor
-    auto factor = FactorBase::emplace<FactorRelativePose3dWithExtrinsics>(f1,
-                                                                          f1,
-                                                                          lmk1,
-                                                                          nullptr,
-                                                                          false);
-
-    // Change Landmark
-    lmk1->getP()->setState(p_w_l);
-    lmk1->getO()->setState(q_w_l.coeffs());
-    ASSERT_TRUE(lmk1->getP()->stateUpdated());
-    ASSERT_TRUE(lmk1->getO()->stateUpdated());
-
-    // Change Frame
-    F1->getP()->setState(p_w_r);
-    F1->getO()->setState(q_w_r.coeffs());
-    F1->fix();
-    ASSERT_TRUE(F1->getP()->stateUpdated());
-    ASSERT_TRUE(F1->getO()->stateUpdated());
-
-    // Change sensor extrinsics
-    S->getP()->setState(p_r_c);
-    S->getO()->setState(q_r_c.coeffs());
-    ASSERT_TRUE(S->getP()->stateUpdated());
-    ASSERT_TRUE(S->getO()->stateUpdated());
-
-    Vector7d t_w_r, t_w_l;
-    t_w_r << p_w_r, q_w_r.coeffs();
-    t_w_l << p_w_l, q_w_l.coeffs();
-    ASSERT_MATRIX_APPROX(F1->getState().vector("PO"), t_w_r, 1e-6);
-    ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO"), t_w_l, 1e-6);
-
-    // unfix LMK, perturbate state
-    lmk1->unfix();
-    lmk1->perturb();
-
-    std::string report = solver->solve(SolverManager::ReportVerbosity::QUIET); // 0: nothing, 1: BriefReport, 2: FullReport
-    ASSERT_MATRIX_APPROX(F1->getState().vector("PO").transpose(), t_w_r.transpose(), 1e-6);
-    ASSERT_MATRIX_APPROX(lmk1->getState().vector("PO").transpose(), t_w_l.transpose(), 1e-6);
+    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->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)
+{
+    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->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();
+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();
+}
diff --git a/test/gtest_factor_relative_position_3d.cpp b/test/gtest_factor_relative_position_3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e928a49baf2f61b7c23eb663830a7a0d63c78a47
--- /dev/null
+++ b/test/gtest_factor_relative_position_3d.cpp
@@ -0,0 +1,280 @@
+//--------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_3d.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;
+using namespace wolf;
+using std::cout;
+using std::endl;
+
+std::string wolf_root = _WOLF_ROOT_DIR;
+
+int N = 1e2;
+
+// Vectors
+Vector7d x_f;
+Vector3d delta1, delta2, delta3, x_1, x_2, x_3;
+
+// Input odometry data and covariance
+Matrix3d data_cov = (Vector3d() << 1e-3, 1e-3, 1e-3).finished().asDiagonal();
+
+// Problem and solver
+ProblemPtr problem_ptr = Problem::create("PO", 3);
+
+SolverCeres solver(problem_ptr);
+
+// Two frames
+FrameBasePtr frm  = 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() );
+FrameBasePtr frm2 = problem_ptr->emplaceFrame(2, (Vector7d() << 0,0,0,0,0,0,1).finished() );
+FrameBasePtr frm3 = problem_ptr->emplaceFrame(3, (Vector7d() << 0,0,0,0,0,0,1).finished() );
+
+// Landmarks
+LandmarkBasePtr lmk1 = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                           "LandmarkPosition3d",
+                                                           std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                           nullptr);
+
+LandmarkBasePtr lmk2 = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                           "LandmarkPosition3d",
+                                                           std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                           nullptr);
+
+LandmarkBasePtr lmk3 = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                           "LandmarkPosition3d",
+                                                           std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                           nullptr);
+
+// Captures
+CaptureBasePtr cap1, cap2, cap3;
+// Features
+FeatureBasePtr fea1, fea2, fea3;
+// Factors
+FactorRelativePosition3dPtr fac1, fac2, fac3;
+
+////////////////////////////////////////////////////////
+void generateRandomProblemFrame()
+{
+    // Random delta
+    delta1 = Vector3d::Random() * 1e2;
+    delta2 = Vector3d::Random() * 1e2;
+    delta3 = Vector3d::Random() * 1e2;
+
+    // Random frame pose
+    x_f = Vector7d::Random() * 1e2;
+    x_f.tail<4>().normalize();
+    auto q_f = Quaterniond(x_f.tail<4>());
+
+    // Frames position
+    x_1 = x_f.head<3>() + q_f * delta1;
+    x_2 = x_f.head<3>() + q_f * delta2;
+    x_3 = x_f.head<3>() + q_f * delta3;
+
+    // Set states
+    frm->setState(x_f);
+    frm1->getP()->setState(x_1);
+    frm2->getP()->setState(x_2);
+    frm3->getP()->setState(x_3);
+
+    // capture & feature & factor with delta measurementCaptureBase(const std::string& _type,
+    cap1 = CaptureBase::emplace<CaptureBase>(frm1, "CaptureFramePosition3d", 1);
+    cap2 = CaptureBase::emplace<CaptureBase>(frm2, "CaptureFramePosition3d", 2);
+    cap3 = CaptureBase::emplace<CaptureBase>(frm3, "CaptureFramePosition3d", 3);
+    fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureFramePosition3d", delta1, data_cov);
+    fea2 = FeatureBase::emplace<FeatureBase>(cap2, "FeatureFramePosition3d", delta2, data_cov);
+    fea3 = FeatureBase::emplace<FeatureBase>(cap3, "FeatureFramePosition3d", delta3, data_cov);
+    fac1 = FactorBase::emplace<FactorRelativePosition3d>(fea1, fea1, frm, nullptr, false, TOP_MOTION);
+    fac2 = FactorBase::emplace<FactorRelativePosition3d>(fea2, fea2, frm, nullptr, false, TOP_MOTION);
+    fac3 = FactorBase::emplace<FactorRelativePosition3d>(fea3, fea3, frm, nullptr, false, TOP_MOTION);
+}
+
+void generateRandomProblemLandmark()
+{
+    // Random delta
+    delta1 = Vector3d::Random() * 1e2;
+    delta2 = Vector3d::Random() * 1e2;
+    delta3 = Vector3d::Random() * 1e2;
+
+    // Random frame pose
+    x_f = Vector7d::Random() * 1e2;
+    x_f.tail<4>().normalize();
+    auto q_f = Quaterniond(x_f.tail<4>());
+
+    // Landmarks positions
+    x_1 = x_f.head<3>() + q_f * delta1;
+    x_2 = x_f.head<3>() + q_f * delta2;
+    x_3 = x_f.head<3>() + q_f * delta3;
+
+    // Set states
+    frm->setState(x_f);
+    lmk1->setState(x_1);
+    lmk2->setState(x_2);
+    lmk3->setState(x_3);
+
+    // feature & factor with delta measurement
+    cap1 = CaptureBase::emplace<CaptureBase>(frm, "CaptureLandmarkPosition3d", 1);
+    cap2 = CaptureBase::emplace<CaptureBase>(frm, "CaptureLandmarkPosition3d", 1);
+    cap3 = CaptureBase::emplace<CaptureBase>(frm, "CaptureLandmarkPosition3d", 1);
+    fea1 = FeatureBase::emplace<FeatureBase>(cap1, "FeatureLandmarkPosition3d", delta1, data_cov);
+    fea2 = FeatureBase::emplace<FeatureBase>(cap2, "FeatureLandmarkPosition3d", delta2, data_cov);
+    fea3 = FeatureBase::emplace<FeatureBase>(cap3, "FeatureLandmarkPosition3d", delta3, data_cov);
+    fac1 = FactorBase::emplace<FactorRelativePosition3d>(fea1, fea1, lmk1, nullptr, false, TOP_LMK);
+    fac2 = FactorBase::emplace<FactorRelativePosition3d>(fea2, fea2, lmk2, nullptr, false, TOP_LMK);
+    fac3 = FactorBase::emplace<FactorRelativePosition3d>(fea3, fea3, lmk3, nullptr, false, TOP_LMK);
+}
+
+TEST(FactorRelativePosition3d, check_tree)
+{
+    ASSERT_TRUE(problem_ptr->check(0));
+}
+
+// FRAME TO FRAME =========================================================================
+TEST(FactorRelativePosition3d, frame_solve_frame)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
+
+        // Perturb frm, fix the rest
+        frm->unfix();
+        frm1->fix();
+        frm2->fix();
+        frm3->fix();
+        frm->perturb(1);
+
+        // solve for frm1
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(frm->getStateVector(), x_f, 1e-3);
+
+        // remove captures (and features and factors) for the next loop
+        cap1->remove();
+        cap2->remove();
+        cap3->remove();
+    }
+}
+
+TEST(FactorRelativePosition3d, frame_solve_frames)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemFrame();
+        
+        // Perturb frm0, fix the rest
+        frm->fix();
+        frm1->unfix();
+        frm2->unfix();
+        frm3->unfix();
+        frm1->getP()->perturb(1);
+        frm2->getP()->perturb(1);
+        frm3->getP()->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_MATRIX_APPROX(frm1->getStateVector("P"), x_1, 1e-3);
+        ASSERT_MATRIX_APPROX(frm2->getStateVector("P"), x_2, 1e-3);
+        ASSERT_MATRIX_APPROX(frm3->getStateVector("P"), x_3, 1e-3);
+
+        // remove captures (and features and factors) for the next loop
+        cap1->remove();
+        cap2->remove();
+        cap3->remove();
+    }
+}
+
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePosition3d, landmark_solve_frame)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb frm0, fix the rest
+        frm->unfix();
+        lmk1->fix();
+        lmk2->fix();
+        lmk3->fix();
+        frm->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(frm->getStateVector(), x_f, 1e-3);
+
+        // remove captures (and features and factors) for the next loop
+        cap1->remove();
+        cap2->remove();
+        cap3->remove();
+    }
+}
+
+TEST(FactorRelativePosition3d, landmark_solve_lmks)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        // Perturb landmark, fix the rest
+        frm->fix();
+        lmk1->unfix();
+        lmk2->unfix();
+        lmk3->unfix();
+        lmk1->perturb(1);
+        lmk2->perturb(1);
+        lmk3->perturb(1);
+
+        // solve for landmark
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_MATRIX_APPROX(lmk1->getStateVector("P"), x_1, 1e-3);
+        ASSERT_MATRIX_APPROX(lmk2->getStateVector("P"), x_2, 1e-3);
+        ASSERT_MATRIX_APPROX(lmk3->getStateVector("P"), x_3, 1e-3);
+
+        // remove captures (and features and factors) for the next loop
+        cap1->remove();
+        cap2->remove();
+        cap3->remove();
+    }
+}
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_factor_relative_position_3d_with_extrinsics.cpp b/test/gtest_factor_relative_position_3d_with_extrinsics.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3917e5d85f021c0f950b18fa7ab4d191fb835379
--- /dev/null
+++ b/test/gtest_factor_relative_position_3d_with_extrinsics.cpp
@@ -0,0 +1,267 @@
+//--------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_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 x_f, x_s;
+Vector3d delta1, delta2, delta3, x_l1, x_l2, x_l3;
+
+// Input odometry data and covariance
+Matrix3d data_cov = (Vector3d() << 1e-3, 1e-3, 1e-3).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");
+
+// Frame
+FrameBasePtr frm = problem_ptr->emplaceFrame(0, (Vector7d() << 0,0,0,0,0,0,1).finished() );
+
+// Landmarks
+LandmarkBasePtr lmk1 = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                           "LandmarkPosition3d",
+                                                           std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                           nullptr);
+
+LandmarkBasePtr lmk2 = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                           "LandmarkPosition3d",
+                                                           std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                           nullptr);
+
+LandmarkBasePtr lmk3 = LandmarkBase::emplace<LandmarkBase>(problem_ptr->getMap(), 
+                                                           "LandmarkPosition3d",
+                                                           std::make_shared<StatePoint3d>(Vector3d::Zero()),
+                                                           nullptr);
+
+// Capture
+auto cap = CaptureBase::emplace<CaptureOdom3d>(frm, 1, sensor, Vector3d::Zero(), data_cov);
+// Features
+FeatureBasePtr fea1, fea2, fea3;
+// Factors
+FactorRelativePosition3dWithExtrinsicsPtr fac1, fac2, fac3;
+
+void generateRandomProblemLandmark()
+{
+    // Random deltas
+    delta1 = Vector3d::Random() * 10;
+    delta2 = Vector3d::Random() * 10;
+    delta3 = Vector3d::Random() * 10;
+
+    // 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>());
+
+    // Landmarks poses
+    x_l1 = x_f.head<3>() + q_f * (x_s.head<3>() + q_s * delta1);
+    x_l2 = x_f.head<3>() + q_f * (x_s.head<3>() + q_s * delta2);
+    x_l3 = x_f.head<3>() + q_f * (x_s.head<3>() + q_s * delta3);
+
+    // Set states
+    frm->setState(x_f);
+    lmk1->setState(x_l1);
+    lmk2->setState(x_l2);
+    lmk3->setState(x_l3);
+    sensor->setState(x_s);
+
+    // features & factors with deltas measurements
+    fea1 = FeatureBase::emplace<FeatureBase>(cap, "FeatureLandmarkPose", delta1, data_cov);
+    fea2 = FeatureBase::emplace<FeatureBase>(cap, "FeatureLandmarkPose", delta2, data_cov);
+    fea3 = FeatureBase::emplace<FeatureBase>(cap, "FeatureLandmarkPose", delta3, data_cov);
+    fac1 = FactorBase::emplace<FactorRelativePosition3dWithExtrinsics>(fea1, fea1, lmk1, nullptr, false, TOP_LMK);
+    fac2 = FactorBase::emplace<FactorRelativePosition3dWithExtrinsics>(fea2, fea2, lmk2, nullptr, false, TOP_LMK);
+    fac3 = FactorBase::emplace<FactorRelativePosition3dWithExtrinsics>(fea3, fea3, lmk3, nullptr, false, TOP_LMK);
+}
+
+TEST(FactorRelativePosition3dWithExtrinsics, check_tree)
+{
+    ASSERT_TRUE(problem_ptr->check(0));
+}
+
+// FRAME TO LANDMARK =========================================================================
+TEST(FactorRelativePosition3dWithExtrinsics, landmark_solve_lmks)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac1->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac2->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac3->residual(), VectorXd::Zero(3), Constants::EPS);
+
+        // Perturb landmark, fix the rest
+        frm->fix();
+        lmk1->unfix();
+        lmk2->unfix();
+        lmk3->unfix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        lmk1->perturb(1);
+        lmk2->perturb(1);
+        lmk3->perturb(1);
+
+        // solve for landmark
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_MATRIX_APPROX(lmk1->getStateVector(), x_l1, 1e-3);
+        ASSERT_MATRIX_APPROX(lmk2->getStateVector(), x_l2, 1e-3);
+        ASSERT_MATRIX_APPROX(lmk3->getStateVector(), x_l3, 1e-3);
+
+        // remove features (and factors) for the next loop
+        fea1->remove();
+        fea2->remove();
+        fea3->remove();
+    }
+}
+
+TEST(FactorRelativePosition3dWithExtrinsics, landmark_solve_frame)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac1->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac2->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac3->residual(), VectorXd::Zero(3), Constants::EPS);
+
+        // Perturb frm0, fix the rest
+        frm->unfix();
+        lmk1->fix();
+        lmk2->fix();
+        lmk3->fix();
+        sensor->getP()->fix();
+        sensor->getO()->fix();
+        frm->perturb(1);
+
+        // solve for frm0
+        std::string report = solver.solve(SolverManager::ReportVerbosity::FULL);
+        //WOLF_INFO(report);
+
+        ASSERT_POSE3d_APPROX(frm->getStateVector(), x_f, 1e-3);
+
+        // remove feature (and factor) for the next loop
+        fea1->remove();
+        fea2->remove();
+        fea3->remove();
+    }
+}
+
+TEST(FactorRelativePosition3dWithExtrinsics, landmark_extrinsics_p_solve)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac1->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac2->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac3->residual(), VectorXd::Zero(3), Constants::EPS);
+
+        // Perturb sensor P, fix the rest
+        frm->fix();
+        lmk1->fix();
+        lmk2->fix();
+        lmk3->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
+        fea1->remove();
+        fea2->remove();
+        fea3->remove();
+    }
+}
+
+TEST(FactorRelativePosition3dWithExtrinsics, landmark_extrinsics_o_solve)
+{
+    for (int i = 0; i < N; i++)
+    {
+        // random setup
+        generateRandomProblemLandmark();
+        
+        ASSERT_MATRIX_APPROX(fac1->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac2->residual(), VectorXd::Zero(3), Constants::EPS);
+        ASSERT_MATRIX_APPROX(fac3->residual(), VectorXd::Zero(3), Constants::EPS);
+
+        // Perturb sensor O, fix the rest
+        frm->fix();
+        lmk1->fix();
+        lmk2->fix();
+        lmk3->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
+        fea1->remove();
+        fea2->remove();
+        fea3->remove();
+    }
+}
+
+int main(int argc, char **argv)
+{
+testing::InitGoogleTest(&argc, argv);
+return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_factory.cpp b/test/gtest_factory.cpp
index f46ee75069a4611a1fdb45be399c852b527517b0..fb269fe8784ec08d3c62edf569e98a7da83d9e03 100644
--- a/test/gtest_factory.cpp
+++ b/test/gtest_factory.cpp
@@ -44,6 +44,19 @@ TEST(TestFactory, DummyObjectFactory)
   DummyObjectDerived obj_derived = DummyObjectDerived("AnotherCoolDummyObject", server);
 }
 
+TEST(TestFactory, isCreatorRegistered)
+{
+    ParserYaml   parser = ParserYaml("test/yaml/params_basic.yaml", wolf_root);
+    ParamsServer server = ParamsServer(parser.getParams());
+
+    bool object_creator_registered = FactoryDummyObject::isCreatorRegistered("DummyObjectDerived");
+
+    ASSERT_TRUE(object_creator_registered);
+
+    // FORCE LOADING
+    DummyObjectDerived obj_derived = DummyObjectDerived("AnotherCoolDummyObject", server);
+}
+
 int main(int argc, char **argv)
 {
   testing::InitGoogleTest(&argc, argv);
diff --git a/test/gtest_factory_state_block.cpp b/test/gtest_factory_state_block.cpp
index 6332a69928a558cfdede9125a73fd7ab7f835338..9822acd60d37ae94d4b73ef45a30ab5207eccbd0 100644
--- a/test/gtest_factory_state_block.cpp
+++ b/test/gtest_factory_state_block.cpp
@@ -31,79 +31,21 @@
 #include "core/common/wolf.h"
 #include "core/state_block/factory_state_block.h"
 #include "core/sensor/factory_sensor.h"
-
+#include "core/state_block/state_block_derived.h"
 
 
 using namespace wolf;
 
-/*
-// You may use this to make some methods of Foo public
-WOLF_PTR_TYPEDEFS(FooPublic);
-class FooPublic : public Foo
-{
-    // You may use this to make some methods of Foo public
-}
-
-class TestInit : public testing::Test
-{
-    public:
-        // You may use this to initialize stuff
-        // Combine it with TEST_F(FooTest, testName) { }
-        void SetUp() override
-        {
-            // Init all you want here
-            // e.g. FooPublic foo;
-        }
-        void TearDown() override {} // you can delete this if unused
-};
-
-TEST_F(TestInit, testName)
-{
-    // Use class TestInit
-}
-*/
-
-//TEST(FactoryStateBlock, get_getClass)
-//{
-//    const auto& f = FactoryStateBlock::get();
-//
-//    const std::string& s = f.getClass();
-//
-//    ASSERT_EQ(s, "FactoryStateBlock");
-//}
 
-TEST(FactoryStateBlock, creator_default)
+TEST(FactoryStateBlock, creator_non_registered_key)
 {
-    auto sbp = FactoryStateBlock::create("P", Eigen::Vector3d(1,2,3), false);
-    auto sbv = FactoryStateBlock::create("V", Eigen::Vector2d(4,5), false);
-    auto sbw = FactoryStateBlock::create("W", Eigen::Vector1d(6), false);
-
-    ASSERT_MATRIX_APPROX(Eigen::Vector3d(1,2,3) , sbp->getState(), 1e-20);
-    ASSERT_MATRIX_APPROX(Eigen::Vector2d(4,5)   , sbv->getState(), 1e-20);
-    ASSERT_MATRIX_APPROX(Eigen::Vector1d(6)     , sbw->getState(), 1e-20);
-
-    ASSERT_FALSE(sbp->hasLocalParametrization());
-    ASSERT_FALSE(sbv->hasLocalParametrization());
-    ASSERT_FALSE(sbw->hasLocalParametrization());
+    // non registered -> throw
+    ASSERT_THROW(auto sba = FactoryStateBlock::create("A", Eigen::Vector1d(6), false), std::runtime_error);
 }
 
 TEST(FactoryStateBlock, creator_StateBlock)
 {
-    auto sbp = FactoryStateBlock::create("StateBlock", Eigen::Vector3d(1,2,3), false);
-    auto sbv = FactoryStateBlock::create("StateBlock", Eigen::Vector2d(4,5), true);
-    auto sbw = FactoryStateBlock::create("StateBlock", Eigen::Vector1d(6), false);
-
-    ASSERT_MATRIX_APPROX(Eigen::Vector3d(1,2,3) , sbp->getState(), 1e-20);
-    ASSERT_MATRIX_APPROX(Eigen::Vector2d(4,5)   , sbv->getState(), 1e-20);
-    ASSERT_MATRIX_APPROX(Eigen::Vector1d(6)     , sbw->getState(), 1e-20);
-
-    ASSERT_FALSE(sbp->isFixed());
-    ASSERT_TRUE (sbv->isFixed());
-    ASSERT_FALSE(sbw->isFixed());
-
-    ASSERT_FALSE(sbp->hasLocalParametrization());
-    ASSERT_FALSE(sbv->hasLocalParametrization());
-    ASSERT_FALSE(sbw->hasLocalParametrization());
+    ASSERT_THROW(auto sbp = FactoryStateBlock::create("StateBlock", Eigen::Vector3d(1,2,3), false), std::runtime_error);
 }
 
 TEST(FactoryStateBlock, creator_Quaternion)
@@ -165,6 +107,109 @@ TEST(FactoryStateBlock, creator_O_is_wrong_size)
     ASSERT_THROW(auto sba = FactoryStateBlock::create("O", Eigen::Vector2d(1,2), false) , std::length_error);
 }
 
+TEST(FactoryStateBlock, creator_Point)
+{
+    auto sba = FactoryStateBlock::create("StatePoint2d", Eigen::Vector2d(1,2), false);
+
+    ASSERT_EQ(sba->getSize()     , 2);
+    ASSERT_EQ(sba->getLocalSize(), 2);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    sba = FactoryStateBlock::create("StatePoint3d", Eigen::Vector3d(1,2,3), false);
+
+    ASSERT_EQ(sba->getSize()     , 3);
+    ASSERT_EQ(sba->getLocalSize(), 3);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    // fails
+    ASSERT_THROW(sba = FactoryStateBlock::create("StatePoint2d", Vector1d(1), false) , std::length_error);
+}
+
+TEST(FactoryStateBlock, creator_P)
+{
+    auto sba = FactoryStateBlock::create("P", Eigen::Vector2d(1,2), false);
+
+    ASSERT_EQ(sba->getSize()     , 2);
+    ASSERT_EQ(sba->getLocalSize(), 2);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    sba = FactoryStateBlock::create("P", Eigen::Vector3d(1,2,3), false);
+
+    ASSERT_EQ(sba->getSize()     , 3);
+    ASSERT_EQ(sba->getLocalSize(), 3);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    // fails
+    ASSERT_THROW(sba = FactoryStateBlock::create("P", Vector1d(1), false) , std::length_error);
+}
+
+TEST(FactoryStateBlock, creator_Vector)
+{
+    auto sba = FactoryStateBlock::create("StateVector2d", Eigen::Vector2d(1,2), false);
+
+    ASSERT_EQ(sba->getSize()     , 2);
+    ASSERT_EQ(sba->getLocalSize(), 2);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    sba = FactoryStateBlock::create("StateVector3d", Eigen::Vector3d(1,2,3), false);
+
+    ASSERT_EQ(sba->getSize()     , 3);
+    ASSERT_EQ(sba->getLocalSize(), 3);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    // fails
+    ASSERT_THROW(sba = FactoryStateBlock::create("StatePoint2d", Vector1d(1), false) , std::length_error);
+}
+
+TEST(FactoryStateBlock, creator_V)
+{
+    auto sba = FactoryStateBlock::create("V", Eigen::Vector2d(1,2), false);
+
+    ASSERT_EQ(sba->getSize()     , 2);
+    ASSERT_EQ(sba->getLocalSize(), 2);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    sba = FactoryStateBlock::create("V", Eigen::Vector3d(1,2,3), false);
+
+    ASSERT_EQ(sba->getSize()     , 3);
+    ASSERT_EQ(sba->getLocalSize(), 3);
+    ASSERT_FALSE(sba->hasLocalParametrization());
+
+    // fails
+    ASSERT_THROW(sba = FactoryStateBlock::create("V", Vector1d(1), false) , std::length_error);
+}
+
+TEST(FactoryStateBlock, creator_Params)
+{
+    auto sb1  = FactoryStateBlock::create("StateParams1", Eigen::Vector1d::Ones(), false);
+    auto sb2  = FactoryStateBlock::create("StateParams2", Eigen::Vector2d::Ones(), false);
+    auto sb3  = FactoryStateBlock::create("StateParams3", Eigen::Vector3d::Ones(), false);
+    auto sb4  = FactoryStateBlock::create("StateParams4", Eigen::Vector4d::Ones(), false);
+    auto sb5  = FactoryStateBlock::create("StateParams5", Eigen::Vector5d::Ones(), false);
+    auto sb6  = FactoryStateBlock::create("StateParams6", Eigen::Vector6d::Ones(), false);
+    auto sb7  = FactoryStateBlock::create("StateParams7", Eigen::Vector7d::Ones(), false);
+    auto sb8  = FactoryStateBlock::create("StateParams8", Eigen::Vector8d::Ones(), false);
+    auto sb9  = FactoryStateBlock::create("StateParams9", Eigen::Vector9d::Ones(), false);
+    auto sb10 = FactoryStateBlock::create("StateParams10", Eigen::Vector10d::Ones(), false);
+
+    ASSERT_EQ(sb1->getSize(), 1);
+    ASSERT_EQ(sb2->getSize(), 2);
+    ASSERT_EQ(sb3->getSize(), 3);
+    ASSERT_EQ(sb4->getSize(), 4);
+    ASSERT_EQ(sb5->getSize(), 5);
+    ASSERT_EQ(sb6->getSize(), 6);
+    ASSERT_EQ(sb7->getSize(), 7);
+    ASSERT_EQ(sb8->getSize(), 8);
+    ASSERT_EQ(sb9->getSize(), 9);
+    ASSERT_EQ(sb10->getSize(), 10);
+
+    ASSERT_EQ(sb1->getLocalSize(), 1);
+    ASSERT_FALSE(sb1->hasLocalParametrization());
+
+    // fails
+    ASSERT_THROW(auto sba = FactoryStateBlock::create("StateParams2", Vector1d(1), false) , std::length_error);
+}
+
 int main(int argc, char **argv)
 {
     testing::InitGoogleTest(&argc, argv);
diff --git a/test/gtest_frame_base.cpp b/test/gtest_frame_base.cpp
index eb61f798d41b2aec792b864dffb39cb1cdb7d098..64dbd6d85484e2ac6ed270b470f674107d146cb6 100644
--- a/test/gtest_frame_base.cpp
+++ b/test/gtest_frame_base.cpp
@@ -33,6 +33,8 @@
 #include "core/sensor/sensor_odom_2d.h"
 #include "core/processor/processor_odom_2d.h"
 #include "core/capture/capture_motion.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 
 #include <iostream>
 
@@ -42,7 +44,7 @@ using namespace wolf;
 
 TEST(FrameBase, GettersAndSetters)
 {
-    FrameBasePtr F = make_shared<FrameBase>(1, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
+    FrameBasePtr F = make_shared<FrameBase>(1, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
 
     // getters
     ASSERT_EQ(F->id(), (unsigned int) 1);
@@ -55,7 +57,7 @@ TEST(FrameBase, GettersAndSetters)
 
 TEST(FrameBase, StateBlocks)
 {
-    FrameBasePtr F = make_shared<FrameBase>(1, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
+    FrameBasePtr F = make_shared<FrameBase>(1, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
 
     ASSERT_EQ(F->getStateBlockMap().size(),(unsigned int) 2);
     ASSERT_EQ(F->getP()->getState().size(),(unsigned int) 2);
@@ -65,7 +67,7 @@ TEST(FrameBase, StateBlocks)
 
 TEST(FrameBase, LinksBasic)
 {
-    FrameBasePtr F = make_shared<FrameBase>(1, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
+    FrameBasePtr F = make_shared<FrameBase>(1, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
 
     ASSERT_FALSE(F->getTrajectory());
     ASSERT_FALSE(F->getProblem());
@@ -85,8 +87,8 @@ TEST(FrameBase, LinksToTree)
     intrinsics_odo.k_disp_to_disp = 1;
     intrinsics_odo.k_rot_to_rot = 1;
     auto S = SensorBase::emplace<SensorOdom2d>(P->getHardware(), Vector3d::Zero(), intrinsics_odo);
-    auto F1 = FrameBase::emplace<FrameBase>(T, 1, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F2 = FrameBase::emplace<FrameBase>(T, 2, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
+    auto F1 = FrameBase::emplace<FrameBase>(T, 1, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F2 = FrameBase::emplace<FrameBase>(T, 2, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
     auto C = CaptureBase::emplace<CaptureMotion>(F1, "CaptureMotion", 1, S, Vector3d::Zero(), nullptr);
     WOLF_INFO("F2->getCaptureList().size() ", F2->getCaptureList().size());
     auto p = std::make_shared<ProcessorOdom2d>(std::make_shared<ParamsProcessorOdom2d>());
@@ -139,16 +141,16 @@ TEST(FrameBase, Frames)
     intrinsics_odo.k_disp_to_disp = 1;
     intrinsics_odo.k_rot_to_rot = 1;
     auto S = SensorBase::emplace<SensorOdom2d>(P->getHardware(), Vector3d::Zero(), intrinsics_odo);
-    auto F0 = FrameBase::emplace<FrameBase>(T, 0, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F1 = FrameBase::emplace<FrameBase>(T, 1, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F2 = FrameBase::emplace<FrameBase>(T, 2, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F3 = FrameBase::emplace<FrameBase>(T, 3, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F4 = FrameBase::emplace<FrameBase>(T, 4, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F5 = FrameBase::emplace<FrameBase>(T, 5, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F6 = FrameBase::emplace<FrameBase>(T, 6, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F7 = FrameBase::emplace<FrameBase>(T, 7, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F8 = FrameBase::emplace<FrameBase>(T, 8, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
-    auto F9 = FrameBase::emplace<FrameBase>(T, 9, make_shared<StateBlock>(2), make_shared<StateBlock>(1));
+    auto F0 = FrameBase::emplace<FrameBase>(T, 0, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F1 = FrameBase::emplace<FrameBase>(T, 1, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F2 = FrameBase::emplace<FrameBase>(T, 2, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F3 = FrameBase::emplace<FrameBase>(T, 3, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F4 = FrameBase::emplace<FrameBase>(T, 4, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F5 = FrameBase::emplace<FrameBase>(T, 5, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F6 = FrameBase::emplace<FrameBase>(T, 6, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F7 = FrameBase::emplace<FrameBase>(T, 7, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F8 = FrameBase::emplace<FrameBase>(T, 8, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
+    auto F9 = FrameBase::emplace<FrameBase>(T, 9, make_shared<StatePoint2d>(Vector2d::Zero()), make_shared<StateAngle>(0));
 
     // tree is now consistent
     ASSERT_TRUE(P->check(0));
@@ -396,8 +398,8 @@ TEST(FrameBase, Frames)
 TEST(FrameBase, GetSetState)
 {
     // Create PQV_3d state blocks
-    StateBlockPtr sbp = make_shared<StateBlock>(3);
-    StateBlockPtr sbv = make_shared<StateBlock>(3);
+    StateBlockPtr sbp = make_shared<StatePoint3d>(Vector3d::Zero());
+    StateBlockPtr sbv = make_shared<StateVector3d>(Vector3d::Zero());
     StateQuaternionPtr sbq = make_shared<StateQuaternion>();
 
     // Create frame
diff --git a/test/gtest_has_state_blocks.cpp b/test/gtest_has_state_blocks.cpp
index e60bc875310148a2fc0a127884b4fb1b3b77ab6a..2b02be288217cca57703ab98b5b9c7a40f32d505 100644
--- a/test/gtest_has_state_blocks.cpp
+++ b/test/gtest_has_state_blocks.cpp
@@ -34,6 +34,8 @@
 #include "core/sensor/sensor_base.h"
 #include "core/landmark/landmark_base.h"
 #include "core/state_block/state_quaternion.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 
 using namespace wolf;
 
@@ -55,12 +57,12 @@ class HasStateBlocksTest : public testing::Test
         {
             problem = Problem::create("POV", 3);
 
-            sbp0 = std::make_shared<StateBlock>(3); // size 3
+            sbp0 = std::make_shared<StatePoint3d>(Vector3d::Zero()); // size 3
             sbo0 = std::make_shared<StateQuaternion>();
-            sbv0 = std::make_shared<StateBlock>(3); // size 3
-            sbp1 = std::make_shared<StateBlock>(3); // size 3
+            sbv0 = std::make_shared<StateVector3d>(Vector3d::Zero()); // size 3
+            sbp1 = std::make_shared<StatePoint3d>(Vector3d::Zero()); // size 3
             sbo1 = std::make_shared<StateQuaternion>();
-            sbv1 = std::make_shared<StateBlock>(3); // size 3
+            sbv1 = std::make_shared<StateVector3d>(Vector3d::Zero()); // size 3
 
             F0 = std::make_shared<FrameBase>(0.0, sbp0, sbo0); // non KF
             F1 = std::make_shared<FrameBase>(1.0, nullptr);    // non KF
diff --git a/test/gtest_map_yaml.cpp b/test/gtest_map_yaml.cpp
index 5c4b2b7893837316736ab7a69a49f1b08c169b9b..f0957a5abd90436d96d6e3a4e2788ad2d5c54faa 100644
--- a/test/gtest_map_yaml.cpp
+++ b/test/gtest_map_yaml.cpp
@@ -31,7 +31,8 @@
 #include "core/problem/problem.h"
 #include "core/map/map_base.h"
 #include "core/landmark/landmark_base.h"
-#include "core/state_block/state_block.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 #include "core/state_block/state_quaternion.h"
 #include "core/state_block/local_parametrization_quaternion.h"
 #include "core/yaml/yaml_conversion.h"
@@ -39,6 +40,9 @@
 #include <iostream>
 using namespace wolf;
 
+std::string wolf_root = _WOLF_ROOT_DIR;
+std::string map_path  = wolf_root + "/test/yaml/maps";
+
 TEST(MapYaml, save_2d)
 {
     ProblemPtr problem = Problem::create("PO", 2);
@@ -51,19 +55,16 @@ TEST(MapYaml, save_2d)
     o2 << 2;
     o3 << 3;
 
-    StateBlockPtr p1_sb = std::make_shared<StateBlock>(p1);
-    StateBlockPtr p2_sb = std::make_shared<StateBlock>(p2);
-    StateBlockPtr o2_sb = std::make_shared<StateBlock>(o2);
-    StateBlockPtr p3_sb = std::make_shared<StateBlock>(p3, true);
-    StateBlockPtr o3_sb = std::make_shared<StateBlock>(o3, true);
+    StateBlockPtr p1_sb = std::make_shared<StatePoint2d>(p1);
+    StateBlockPtr p2_sb = std::make_shared<StatePoint2d>(p2);
+    StateBlockPtr o2_sb = std::make_shared<StateAngle>(o2(0));
+    StateBlockPtr p3_sb = std::make_shared<StatePoint2d>(p3, true);
+    StateBlockPtr o3_sb = std::make_shared<StateAngle>(o3(0), true);
 
     LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p1_sb);
     LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p2_sb, o2_sb);
     LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p3_sb, o3_sb);
 
-    std::string wolf_root = _WOLF_ROOT_DIR;
-    std::string map_path  = wolf_root + "/test/yaml";
-
     // Check Problem functions
     std::string filename  = map_path + "/map_2d_problem.yaml";
     std::cout << "Saving map to file: " << filename << std::endl;
@@ -79,89 +80,54 @@ TEST(MapYaml, load_2d)
 {
     ProblemPtr problem = Problem::create("PO", 2);
 
-    std::string wolf_root = _WOLF_ROOT_DIR;
-    std::string map_path  = wolf_root + "/test/yaml";
-
-    // Check Problem functions
-    std::string filename  = map_path + "/map_2d_problem.yaml";
-    std::cout << "Loading map to file: " << filename << std::endl;
-    problem->loadMap(filename);
-
-    ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3);
+    std::vector<std::string> maps{"map_2d_map", "map_2d_problem", "map_2d_manual"};
 
-    for (auto lmk : problem->getMap()->getLandmarkList())
+    for (auto map : maps)
     {
-        if (lmk->id() == 1)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() == nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<1,2).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
-        }
-        else if (lmk->id() == 2)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<3,4).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<2).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
-            ASSERT_FALSE(lmk->getO()->isFixed());
-        }
-        else if (lmk->id() == 3)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<5,6).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<3).finished(), 1e-12);
-            ASSERT_TRUE(lmk->getP()->isFixed());
-            ASSERT_TRUE(lmk->getO()->isFixed());
-        }
-    }
-
-    // empty the map
-    {
-        auto lmk_list = problem->getMap()->getLandmarkList();
-        for (auto lmk : lmk_list)
-            lmk->remove();
-    }
-    ASSERT_TRUE(problem->getMap()->getLandmarkList().empty());
+        std::string filename  = map_path + "/" + map + ".yaml";
+        std::cout << "Loading map to file: " << filename << std::endl;
+        problem->loadMap(filename);
 
-    // Check Map functions
-    filename  = map_path + "/map_2d_map.yaml";
-    std::cout << "Loading map to file: " << filename << std::endl;
-    problem->getMap()->load(filename);
+        ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3);
 
-    ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3);
-
-    for (auto lmk : problem->getMap()->getLandmarkList())
-    {
-        if (lmk->id() == 1)
+        for (auto lmk : problem->getMap()->getLandmarkList())
         {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() == nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<1,2).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
+            if (lmk->id() == 1)
+            {
+                ASSERT_TRUE(lmk->getP() != nullptr);
+                ASSERT_TRUE(lmk->getO() == nullptr);
+                ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<1,2).finished(), 1e-12);
+                ASSERT_FALSE(lmk->getP()->isFixed());
+            }
+            else if (lmk->id() == 2)
+            {
+                ASSERT_TRUE(lmk->getP() != nullptr);
+                ASSERT_TRUE(lmk->getO() != nullptr);
+                ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<3,4).finished(), 1e-12);
+                ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<2).finished(), 1e-12);
+                ASSERT_FALSE(lmk->getP()->isFixed());
+                ASSERT_FALSE(lmk->getO()->isFixed());
+            }
+            else if (lmk->id() == 3)
+            {
+                ASSERT_TRUE(lmk->getP() != nullptr);
+                ASSERT_TRUE(lmk->getO() != nullptr);
+                ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<5,6).finished(), 1e-12);
+                ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<3).finished(), 1e-12);
+                ASSERT_TRUE(lmk->getP()->isFixed());
+                ASSERT_TRUE(lmk->getO()->isFixed());
+            }
         }
-        else if (lmk->id() == 2)
+        // empty the map
         {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<3,4).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<2).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
-            ASSERT_FALSE(lmk->getO()->isFixed());
-        }
-        else if (lmk->id() == 3)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector2d()<<5,6).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector1d()<<3).finished(), 1e-12);
-            ASSERT_TRUE(lmk->getP()->isFixed());
-            ASSERT_TRUE(lmk->getO()->isFixed());
+            auto lmk_list = problem->getMap()->getLandmarkList();
+            for (auto lmk : lmk_list)
+                lmk->remove();
         }
+        ASSERT_TRUE(problem->getMap()->getLandmarkList().empty());
     }
 }
+
 TEST(MapYaml, save_3d)
 {
     ProblemPtr problem = Problem::create("PO", 3);
@@ -174,19 +140,16 @@ TEST(MapYaml, save_3d)
     q2 << 0, 1, 0, 0;
     q3 << 0, 0, 1, 0;
 
-    auto p1_sb = std::make_shared<StateBlock>(p1);
-    auto p2_sb = std::make_shared<StateBlock>(p2);
+    auto p1_sb = std::make_shared<StatePoint2d>(p1);
+    auto p2_sb = std::make_shared<StatePoint2d>(p2);
     auto q2_sb = std::make_shared<StateQuaternion>(q2);
-    auto p3_sb = std::make_shared<StateBlock>(p3, true);
+    auto p3_sb = std::make_shared<StatePoint2d>(p3, true);
     auto q3_sb = std::make_shared<StateQuaternion>(q3, true);
 
     LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p1_sb);
     LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p2_sb, q2_sb);
     LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", p3_sb, q3_sb);
 
-    std::string wolf_root = _WOLF_ROOT_DIR;
-    std::string map_path  = wolf_root + "/test/yaml";
-
     // Check Problem functions
     std::string filename  = map_path + "/map_3d_problem.yaml";
     std::cout << "Saving map to file: " << filename << std::endl;
@@ -202,99 +165,60 @@ TEST(MapYaml, load_3d)
 {
     ProblemPtr problem = Problem::create("PO", 3);
 
-    std::string wolf_root = _WOLF_ROOT_DIR;
-    std::string map_path  = wolf_root + "/test/yaml";
-
-    // Check Problem functions
-    std::string filename  = map_path + "/map_3d_problem.yaml";
-    std::cout << "Loading map to file: " << filename << std::endl;
-    problem->loadMap(filename);
-
-    ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3);
-
-    for (auto lmk : problem->getMap()->getLandmarkList())
-    {
-        if (lmk->id() == 1)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() == nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<1,2,3).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
-        }
-        else if (lmk->id() == 2)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<4,5,6).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,1,0,0).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
-            ASSERT_FALSE(lmk->getO()->isFixed());
-            ASSERT_TRUE(lmk->getO()->hasLocalParametrization());
-        }
-        else if (lmk->id() == 3)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<7,8,9).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,0,1,0).finished(), 1e-12);
-            ASSERT_TRUE(lmk->getP()->isFixed());
-            ASSERT_TRUE(lmk->getO()->isFixed());
-            ASSERT_TRUE(lmk->getO()->hasLocalParametrization());
-        }
-    }
+    std::vector<std::string> maps{"map_3d_map", "map_3d_problem", "map_3d_manual"};
 
-    // empty the map
+    for (auto map : maps)
     {
-        auto lmk_list = problem->getMap()->getLandmarkList();
-        for (auto lmk : lmk_list)
-            lmk->remove();
-    }
-    ASSERT_TRUE(problem->getMap()->getLandmarkList().empty());
+        std::string filename  = map_path + "/" + map + ".yaml";
 
-    // Check Map functions
-    filename  = map_path + "/map_3d_map.yaml";
-    std::cout << "Loading map to file: " << filename << std::endl;
-    problem->getMap()->load(filename);
+        std::cout << "Loading map to file: " << filename << std::endl;
+        problem->loadMap(filename);
 
-    ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3);
+        ASSERT_EQ(problem->getMap()->getLandmarkList().size(), 3);
 
-    for (auto lmk : problem->getMap()->getLandmarkList())
-    {
-        if (lmk->id() == 1)
-        {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() == nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<1,2,3).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
-        }
-        else if (lmk->id() == 2)
+        for (auto lmk : problem->getMap()->getLandmarkList())
         {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<4,5,6).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,1,0,0).finished(), 1e-12);
-            ASSERT_FALSE(lmk->getP()->isFixed());
-            ASSERT_FALSE(lmk->getO()->isFixed());
-            ASSERT_TRUE(lmk->getO()->hasLocalParametrization());
+            if (lmk->id() == 1)
+            {
+                ASSERT_TRUE(lmk->getP() != nullptr);
+                ASSERT_TRUE(lmk->getO() == nullptr);
+                ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<1,2,3).finished(), 1e-12);
+                ASSERT_FALSE(lmk->getP()->isFixed());
+            }
+            else if (lmk->id() == 2)
+            {
+                ASSERT_TRUE(lmk->getP() != nullptr);
+                ASSERT_TRUE(lmk->getO() != nullptr);
+                ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<4,5,6).finished(), 1e-12);
+                ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,1,0,0).finished(), 1e-12);
+                ASSERT_FALSE(lmk->getP()->isFixed());
+                ASSERT_FALSE(lmk->getO()->isFixed());
+                ASSERT_TRUE(lmk->getO()->hasLocalParametrization());
+            }
+            else if (lmk->id() == 3)
+            {
+                ASSERT_TRUE(lmk->getP() != nullptr);
+                ASSERT_TRUE(lmk->getO() != nullptr);
+                ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<7,8,9).finished(), 1e-12);
+                ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,0,1,0).finished(), 1e-12);
+                ASSERT_TRUE(lmk->getP()->isFixed());
+                ASSERT_TRUE(lmk->getO()->isFixed());
+                ASSERT_TRUE(lmk->getO()->hasLocalParametrization());
+            }
         }
-        else if (lmk->id() == 3)
+
+        // empty the map
         {
-            ASSERT_TRUE(lmk->getP() != nullptr);
-            ASSERT_TRUE(lmk->getO() != nullptr);
-            ASSERT_MATRIX_APPROX(lmk->getP()->getState(), (Eigen::Vector3d()<<7,8,9).finished(), 1e-12);
-            ASSERT_MATRIX_APPROX(lmk->getO()->getState(), (Eigen::Vector4d()<<0,0,1,0).finished(), 1e-12);
-            ASSERT_TRUE(lmk->getP()->isFixed());
-            ASSERT_TRUE(lmk->getO()->isFixed());
-            ASSERT_TRUE(lmk->getO()->hasLocalParametrization());
+            auto lmk_list = problem->getMap()->getLandmarkList();
+            for (auto lmk : lmk_list)
+                lmk->remove();
         }
+        ASSERT_TRUE(problem->getMap()->getLandmarkList().empty());
     }
 }
 
 TEST(MapYaml, remove_map_files)
 {
-    std::string wolf_root = _WOLF_ROOT_DIR;
-    std::string map_path  = wolf_root + "/test/yaml";
-
     std::string filename  = map_path + "/map_2d_problem.yaml";
     ASSERT_TRUE(std::remove(filename.c_str()) == 0);
     filename  = map_path + "/map_2d_map.yaml";
diff --git a/test/gtest_problem.cpp b/test/gtest_problem.cpp
index 522246910cc6bd3944f6924a832fbf028cb2b75c..2d852b31748b9ec2320bcf10f89f042687206c68 100644
--- a/test/gtest_problem.cpp
+++ b/test/gtest_problem.cpp
@@ -43,6 +43,10 @@
 #include "core/capture/capture_diff_drive.h"
 #include "core/factor/factor_diff_drive.h"
 #include "core/state_block/state_quaternion.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
+#include "core/landmark/landmark_base.h"
+#include "core/math/SE3.h"
 
 
 #include <iostream>
@@ -388,7 +392,7 @@ TEST(Problem, perturb)
 
         for (int j = 0; j< 2 ; j++)
         {
-            auto sb     = std::make_shared<StateBlock>(Vector3d(1,1,1));
+            auto sb     = std::make_shared<StatePoint3d>(Vector3d(1,1,1));
             auto cap    = CaptureBase::emplace<CaptureBase>(F, "CaptureBase", t, sensor, nullptr, nullptr, sb);
         }
         i++;
@@ -396,8 +400,8 @@ TEST(Problem, perturb)
 
     for (int l = 0; l < 2; l++)
     {
-        auto sb1    = std::make_shared<StateBlock>(Vector2d(1,2));
-        auto sb2    = std::make_shared<StateBlock>(Vector1d(3));
+        auto sb1    = std::make_shared<StatePoint2d>(Vector2d(1,2));
+        auto sb2    = std::make_shared<StateAngle>(3);
         auto L      = LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", sb1, sb2);
         if (l==0) L->fix();
     }
@@ -479,7 +483,7 @@ TEST(Problem, check)
 
         for (int j = 0; j< 2 ; j++)
         {
-            auto sb     = std::make_shared<StateBlock>(Vector3d(1,1,1));
+            auto sb     = std::make_shared<StatePoint3d>(Vector3d(1,1,1));
             auto cap    = CaptureBase::emplace<CaptureDiffDrive>(F, t, sensor, Vector2d(1,2), Matrix2d::Identity(), nullptr, nullptr, nullptr, sb);
 
             for (int k = 0; k<2; k++)
@@ -495,8 +499,8 @@ TEST(Problem, check)
 
     for (int l = 0; l < 2; l++)
     {
-        auto sb1    = std::make_shared<StateBlock>(Vector2d(1,2));
-        auto sb2    = std::make_shared<StateBlock>(Vector1d(3));
+        auto sb1    = std::make_shared<StatePoint2d>(Vector2d(1,2));
+        auto sb2    = std::make_shared<StateAngle>(3);
         auto L      = LandmarkBase::emplace<LandmarkBase>(problem->getMap(), "LandmarkBase", sb1, sb2);
         if (l==0) L->fix();
     }
@@ -612,10 +616,128 @@ TEST(Problem, getState)
 
 }
 
+TEST(Problem, transform)
+{
+    std::string wolf_root = _WOLF_ROOT_DIR;
+
+    auto parser = ParserYaml("test/yaml/params_problem_autosetup.yaml", wolf_root);
+    auto server = ParamsServer(parser.getParams());
+
+    auto P = Problem::autoSetup(server);
+
+    auto S = P->getHardware()->getSensorList().front();
+
+    // Add dynamic sensor params
+    Vector3d param_sensor = Vector3d(3, 2, 1);
+    S->addStateBlock('b', std::make_shared<StateParams3>(param_sensor, false), true); // Estimated ; Dynamic
+
+    Vector6d data;
+    data << 0, 0.2, 0, 0, 0, 0;  // advance on Y
+    auto C = std::make_shared<CaptureOdom3d>(0.0, S, Vector6d::Zero(), 0.01 * Matrix6d::Identity());
+
+    // Move until we get one keyframe
+    for (TimeStamp t = 0.0; t <= 3.0; t += 0.2)
+    {
+        C->setTimeStamp(t);
+        if (t > 0.0) C->setData(data);  // first time data is zero!
+        C->process();
+    }
+
+    // Add dynamic sensor params to captures
+    for (auto t_f : P->getTrajectory()->getFrameMap())
+    {
+        for (auto c : t_f.second->getCapturesOf(S))
+        {
+            c->addStateBlock('b', std::make_shared<StateParams3>(S->getStateBlock('b')->getState()), P);
+        }
+    }
+
+    // Add a couple of lmks
+    LandmarkBase::emplace<LandmarkBase>(P->getMap(),  //
+                                        "Point3d",
+                                        std::make_shared<StatePoint3d>(Vector3d(1, 2, 3)));
+    LandmarkBase::emplace<LandmarkBase>(P->getMap(),
+                                        "Pose3d",
+                                        std::make_shared<StatePoint3d>(Vector3d(1, 2, 3)),
+                                        std::make_shared<StateQuaternion>(Vector4d(0, 0, 0, 1)));
+
+    // P->print(2, 0, 1, 1);  // reference
+    
+    ///////////////////////////
+    // Define transformations
+
+    // translation
+    Quaterniond q_w1_w0 = Quaterniond::Identity();                            // no rotation
+    VectorComposite pose_w1_w0("PO", {Vector3d(1, 0, 0), q_w1_w0.coeffs()});  // translation X: 1m
+
+    // rotation
+    Quaterniond q_w2_w1(AngleAxisd(M_PI_2, Vector3d(1, 0, 0)));               // rotation X: 90deg
+    VectorComposite pose_w2_w1("PO", {Vector3d(0, 0, 0), q_w2_w1.coeffs()}); // no translation, 
+    
+    // rotation + translation
+    Quaterniond q_w3_w2(AngleAxisd(-M_PI_2, Vector3d(1, 0, 0)));              // rotation X: -90deg
+    VectorComposite pose_w3_w2("PO", {Vector3d(1, 0, 0), q_w3_w2.coeffs()});  // translation X: 1m
+
+    // State of the problem in the initial frame "w0"
+    VectorComposite pose_w0_r0 = P->getTrajectory()->getFirstFrame()->getState();
+    VectorComposite pose_w0_r1 = P->getTrajectory()->getLastFrame()->getState();
+    Vector3d p_w0_l0 = P->getMap()->getLandmarkList().front()->getState("P").vector("P");  // Point3d
+    VectorComposite pose_w0_l1 = P->getMap()->getLandmarkList().back()->getState();
+
+    //////////////////////
+    // Apply successive changes of reference frame w0->w1->w2->w3 and compare with transformed states
+    //////////////////////
+
+    //////////////////////
+    // w0->w1: Translation
+    P->transform(pose_w1_w0);
+    // Expected values
+    VectorComposite pose_w1_r0 = SE3::compose(pose_w1_w0, pose_w0_r0);
+    VectorComposite pose_w1_r1 = SE3::compose(pose_w1_w0, pose_w0_r1);
+    Vector3d p_w1_l0 = pose_w1_w0.at('P') + q_w1_w0 * p_w0_l0;
+    VectorComposite pose_w1_l1 = SE3::compose(pose_w1_w0, pose_w0_l1);
+    ASSERT_MATRIX_APPROX(P->getTrajectory()->getFirstFrame()->getState().vector("PO"), pose_w1_r0.vector("PO"), 1e-10);
+    ASSERT_MATRIX_APPROX(P->getTrajectory()->getLastFrame()->getState().vector("PO"), pose_w1_r1.vector("PO"), 1e-10);
+    ASSERT_MATRIX_APPROX(P->getMap()->getLandmarkList().front()->getState("P").vector("P"), p_w1_l0, 1e-10);
+    ASSERT_MATRIX_APPROX(P->getMap()->getLandmarkList().back()->getState().vector("PO"), pose_w1_l1.vector("PO"), 1e-10);
+    // P->print(2, 0, 1, 1);
+
+    //////////////////////
+    // w1->w2: Rotation
+    P->transform(pose_w2_w1);
+    // Expected values
+    VectorComposite pose_w2_r0 = SE3::compose(pose_w2_w1, pose_w1_r0);
+    VectorComposite pose_w2_r1 = SE3::compose(pose_w2_w1, pose_w1_r1);
+    Vector3d p_w2_l0 = pose_w2_w1.at('P') + q_w2_w1 * p_w1_l0;
+    VectorComposite pose_w2_l1 = SE3::compose(pose_w2_w1, pose_w1_l1);
+    ASSERT_MATRIX_APPROX(P->getTrajectory()->getFirstFrame()->getState().vector("PO"), pose_w2_r0.vector("PO"), 1e-10);
+    ASSERT_MATRIX_APPROX(P->getTrajectory()->getLastFrame()->getState().vector("PO"), pose_w2_r1.vector("PO"), 1e-10);
+    ASSERT_MATRIX_APPROX(P->getMap()->getLandmarkList().front()->getState("P").vector("P"), p_w2_l0, 1e-10);
+    ASSERT_MATRIX_APPROX(P->getMap()->getLandmarkList().back()->getState().vector("PO"), pose_w2_l1.vector("PO"), 1e-10);
+    // P->print(2, 0, 1, 1);
+
+    //////////////////////
+    // w2->w3: Translation + Rotation
+    P->transform(pose_w3_w2);
+    // Expected values
+    VectorComposite pose_w3_r0 = SE3::compose(pose_w3_w2, pose_w2_r0);
+    VectorComposite pose_w3_r1 = SE3::compose(pose_w3_w2, pose_w2_r1);
+    Vector3d p_w3_l0 = pose_w3_w2.at('P') + q_w3_w2 * p_w2_l0;
+    VectorComposite pose_w3_l1 = SE3::compose(pose_w3_w2, pose_w2_l1);
+    ASSERT_MATRIX_APPROX(P->getTrajectory()->getFirstFrame()->getState().vector("PO"), pose_w3_r0.vector("PO"), 1e-10);
+    ASSERT_MATRIX_APPROX(P->getTrajectory()->getLastFrame()->getState().vector("PO"), pose_w3_r1.vector("PO"), 1e-10);
+    ASSERT_MATRIX_APPROX(P->getMap()->getLandmarkList().front()->getState("P").vector("P"), p_w3_l0, 1e-10);
+    ASSERT_MATRIX_APPROX(P->getMap()->getLandmarkList().back()->getState().vector("PO"), pose_w3_l1.vector("PO"), 1e-10);
+    // P->print(2, 0, 1, 1);
+
+    // sensor parameters should not be transformed
+    ASSERT_MATRIX_APPROX(S->getStateBlock('b')->getState(), param_sensor, 1e-10);
+
+}
 
 int main(int argc, char **argv)
 {
-  testing::InitGoogleTest(&argc, argv);
-//  ::testing::GTEST_FLAG(filter) = "Problem.emplaceFrame_factory";
-  return RUN_ALL_TESTS();
+    testing::InitGoogleTest(&argc, argv);
+    //  ::testing::GTEST_FLAG(filter) = "Problem.emplaceFrame_factory";
+    return RUN_ALL_TESTS();
 }
diff --git a/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp b/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp
index 7e1e8624ec6c9d10711875ca85fe256adcdf2ba6..1b287910cdb0ed08015ffd1831159af6325d88bd 100644
--- a/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp
+++ b/test/gtest_processor_and_factor_pose_3d_with_extrinsics.cpp
@@ -127,8 +127,12 @@ class FactorPose3dWithExtrinsicsBase_Test : public testing::Test
 
         // pose sensor and proc (to get extrinsics in the prb)
         auto intr_sp = std::make_shared<ParamsSensorPose>();
-        intr_sp->std_p = 1;
-        intr_sp->std_o = 1;
+        intr_sp->std_px = 1;
+        intr_sp->std_py = 1;
+        intr_sp->std_pz = 1;
+        intr_sp->std_ox = 1;
+        intr_sp->std_oy = 1;
+        intr_sp->std_oz = 1;
         Vector7d extr; extr << b_p_bm_, b_q_m_.coeffs();
         sensor_pose_ = problem_->installSensor("SensorPose", "pose", extr, intr_sp);
         auto params_proc = std::make_shared<ParamsProcessorPose>();
diff --git a/test/gtest_processor_base.cpp b/test/gtest_processor_base.cpp
index 89341815653b03ed0d189b7d91c8e1c759f9bd9f..5840118ff7c4b4ee12ed7413f2ae3e64e896ffd0 100644
--- a/test/gtest_processor_base.cpp
+++ b/test/gtest_processor_base.cpp
@@ -36,6 +36,8 @@
 #include "core/capture/capture_void.h"
 
 #include "core/problem/problem.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 
 // STL
 #include <iterator>
@@ -104,9 +106,9 @@ TEST(ProcessorBase, KeyFrameCallback)
     // Install tracker (sensor and processor)
     auto sens_trk = SensorBase::emplace<SensorBase>(problem->getHardware(),
                                                     "SensorTrackerDummy",
-                                                    std::make_shared<StateBlock>(Eigen::VectorXd::Zero(2)),
-                                                    std::make_shared<StateBlock>(Eigen::VectorXd::Zero(1)),
-                                                    std::make_shared<StateBlock>(Eigen::VectorXd::Zero(2)), 2);
+                                                    std::make_shared<StatePoint2d>(Eigen::VectorXd::Zero(2)),
+                                                    std::make_shared<StateAngle>(0),
+                                                    std::make_shared<StateParams2>(Eigen::VectorXd::Zero(2)), 2);
     auto proc_trk_params = make_shared<ParamsProcessorTrackerFeatureDummy>();
     proc_trk_params->time_tolerance = dt/2;
     auto proc_trk = problem->installProcessor("ProcessorTrackerFeatureDummy",  "dummy", sens_trk, proc_trk_params);
diff --git a/test/gtest_processor_diff_drive.cpp b/test/gtest_processor_diff_drive.cpp
index 0fcf734675b5b4f3f4fa5d3a46e0b6f5d5fa0cc0..9389febe08bac0e5d0e23caff425140a6eacebd2 100644
--- a/test/gtest_processor_diff_drive.cpp
+++ b/test/gtest_processor_diff_drive.cpp
@@ -104,16 +104,16 @@ class ProcessorDiffDrivePublic : public ProcessorDiffDrive
             return Base::emplaceCapture(_frame_own, _sensor, _ts, _data, _data_cov, _calib, _calib_preint, _capture_origin);
         }
 
-        FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_own) override
-        {
-            return Base::emplaceFeature(_capture_own);
-        }
-
-        FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion,
-                                            CaptureBasePtr _capture_origin) override
-        {
-            return Base::emplaceFactor(_feature_motion, _capture_origin);
-        }
+        // FeatureBasePtr emplaceFeature(CaptureMotionPtr _capture_own) override
+        // {
+        //     return Base::emplaceFeature(_capture_own);
+        // }
+
+        // FactorBasePtr emplaceFactor(FeatureBasePtr _feature_motion,
+        //                                     CaptureBasePtr _capture_origin) override
+        // {
+        //     return Base::emplaceFactor(_feature_motion, _capture_origin);
+        // }
 
         ParamsProcessorDiffDrivePtr getParams()
         {
diff --git a/test/gtest_processor_fixed_wing_model.cpp b/test/gtest_processor_fixed_wing_model.cpp
index b2bcb1990ccd266756c7f106478b79aadff3cc88..4f7500423edb371503bafc23169b12775ff3d5c5 100644
--- a/test/gtest_processor_fixed_wing_model.cpp
+++ b/test/gtest_processor_fixed_wing_model.cpp
@@ -20,15 +20,17 @@
 //
 //--------LICENSE_END--------
 
+#include "core/processor/processor_fixed_wing_model.h"
+
 #include "core/utils/utils_gtest.h"
 #include "core/problem/problem.h"
 #include "core/ceres_wrapper/solver_ceres.h"
 #include "core/state_block/state_quaternion.h"
+#include "core/state_block/state_block_derived.h"
 
 // STL
 #include <iterator>
 #include <iostream>
-#include "../include/core/processor/processor_fixed_wing_model.h"
 
 using namespace wolf;
 using namespace Eigen;
@@ -52,7 +54,7 @@ class ProcessorFixWingModelTest : public testing::Test
             // Emplace sensor
             sensor = SensorBase::emplace<SensorBase>(problem->getHardware(),
                                                      "SensorBase",
-                                                     std::make_shared<StateBlock>(Vector3d::Zero()),
+                                                     std::make_shared<StatePoint3d>(Vector3d::Zero()),
                                                      std::make_shared<StateQuaternion>((Vector4d() << 0,0,0,1).finished()),
                                                      nullptr,
                                                      2);
diff --git a/test/gtest_processor_landmark_external.cpp b/test/gtest_processor_landmark_external.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..201e8f61a26fe70aac63d99365668ea3cc2968c0
--- /dev/null
+++ b/test/gtest_processor_landmark_external.cpp
@@ -0,0 +1,526 @@
+//--------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/problem/problem.h"
+#include "core/capture/capture_landmarks_external.h"
+#include "core/landmark/landmark_base.h"
+#include "core/sensor/sensor_odom_2d.h"
+#include "core/sensor/sensor_odom_3d.h"
+#include "core/processor/processor_odom_2d.h"
+#include "core/processor/processor_odom_3d.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
+#include "core/state_block/state_quaternion.h"
+#include "core/ceres_wrapper/solver_ceres.h"
+
+#include "core/processor/processor_landmark_external.h"
+
+// STL
+#include <iterator>
+#include <iostream>
+
+using namespace wolf;
+using namespace Eigen;
+
+class ProcessorLandmarkExternalTest : public testing::Test
+{
+    protected:
+        int dim;
+        bool orientation;
+        TimeStamp t;
+        double dt;
+
+        ProblemPtr problem;
+        SolverManagerPtr solver;
+        SensorBasePtr sensor, sensor_odom;
+        ProcessorLandmarkExternalPtr processor;
+        ProcessorMotionPtr processor_motion;
+        std::vector<LandmarkBasePtr> landmarks;
+
+        // Groundtruth states
+        VectorComposite state_robot, state_sensor;
+        std::vector<VectorComposite> state_landmarks;
+
+        virtual void SetUp() {};
+        void initProblem(int _dim,
+                         bool _orientation,
+                         double _quality_th, 
+                         double _dist_th, 
+                         unsigned int _track_length_th,
+                         double _time_span,
+                         bool _add_landmarks);
+        void randomStep();
+        CaptureLandmarksExternalPtr computeCaptureLandmarks() const;
+        void testConfiguration(int dim, 
+                               bool orientation, 
+                               double quality_th, 
+                               double dist_th, 
+                               int track_length, 
+                               double time_span, 
+                               bool add_landmarks);
+        void assertVectorComposite(const VectorComposite& v1, const VectorComposite& v2) const;
+};
+
+void ProcessorLandmarkExternalTest::initProblem(int _dim, 
+                                                bool _orientation,
+                                                double _quality_th, 
+                                                double _dist_th, 
+                                                unsigned int _track_length_th,
+                                                double _time_span,
+                                                bool _add_landmarks)
+{
+    dim = _dim;
+    orientation = _orientation;
+    t = TimeStamp(0);
+    dt = 0.1;
+
+    problem = Problem::create("PO", dim);
+    solver = std::make_shared<SolverCeres>(problem);
+
+    // Sensors
+    if (dim == 2)
+    {
+        sensor = SensorBase::emplace<SensorBase>(problem->getHardware(),
+                                                 "SensorBase",
+                                                 std::make_shared<StatePoint2d>(Vector2d::Random()),
+                                                 std::make_shared<StateAngle>(Vector1d::Random() * M_PI),
+                                                 nullptr,
+                                                 2);
+        sensor_odom = SensorBase::emplace<SensorOdom2d>(problem->getHardware(),
+                                                        Vector3d::Zero(),
+                                                        ParamsSensorOdom2d());
+    
+    }
+    else
+    {
+        sensor = SensorBase::emplace<SensorBase>(problem->getHardware(),
+                                                 "SensorBase",
+                                                 std::make_shared<StatePoint3d>(Vector3d::Random()),
+                                                 std::make_shared<StateQuaternion>(Vector4d::Random().normalized()),
+                                                 nullptr,
+                                                 3);
+        sensor_odom = SensorBase::emplace<SensorOdom3d>(problem->getHardware(),
+                                                        (Vector7d() << 0,0,0,0,0,0,1).finished(),
+                                                        ParamsSensorOdom3d());
+    }
+
+    // Processors
+    ParamsProcessorLandmarkExternalPtr params = std::make_shared<ParamsProcessorLandmarkExternal>();
+    params->time_tolerance = dt/2;
+    params->max_new_features = -1;
+    params->voting_active = true;
+    params->apply_loss_function = false;
+    params->use_orientation = orientation;
+    params->filter_quality_th = _quality_th;
+    params->match_dist_th = _dist_th;
+    params->min_features_for_keyframe = 1;
+    params->new_features_for_keyframe = 1000;
+    params->filter_track_length_th = _track_length_th;
+    params->time_span = _time_span;
+    processor = ProcessorBase::emplace<ProcessorLandmarkExternal>(sensor, params);
+
+    if (dim == 2)
+    {
+        auto params_odom = std::make_shared<ParamsProcessorOdom2d>();
+        params_odom->state_getter = true;
+        params_odom->voting_active = false;
+        processor_motion = std::static_pointer_cast<ProcessorMotion>(ProcessorBase::emplace<ProcessorOdom2d>(sensor_odom, 
+                                                                                                             params_odom));
+    }
+    else
+    {
+        auto params_odom = std::make_shared<ParamsProcessorOdom3d>();
+        params_odom->state_getter = true;
+        params_odom->voting_active = false;
+        processor_motion = std::static_pointer_cast<ProcessorMotion>(ProcessorBase::emplace<ProcessorOdom3d>(sensor_odom, 
+                                                                                                             params_odom));
+    }
+
+    // Emplace frame
+    auto F0 = problem->emplaceFrame(t, (dim == 2 ? VectorXd::Zero(3) : (VectorXd(7) << 0,0,0,0,0,0,1).finished()));
+    F0->fix();
+    processor->keyFrameCallback(F0);
+    processor_motion->setOrigin(F0);
+
+    // Emplace 3 random landmarks
+    for (auto i = 0; i < 3; i++)
+    {
+        LandmarkBasePtr lmk;
+        if (dim == 2)
+            lmk = LandmarkBase::emplace<LandmarkBase>(_add_landmarks ? problem->getMap() : nullptr, 
+                                                      "LandmarkExternal",
+                                                      std::make_shared<StatePoint2d>(Vector2d::Random() * 10),
+                                                      (orientation ? 
+                                                        std::make_shared<StateAngle>(Vector1d::Random() * M_PI) : 
+                                                        nullptr));
+    
+        else 
+            lmk = LandmarkBase::emplace<LandmarkBase>(_add_landmarks ? problem->getMap() : nullptr, 
+                                                      "LandmarkExternal",
+                                                      std::make_shared<StatePoint3d>(Vector3d::Random() * 10),
+                                                      (orientation ? 
+                                                        std::make_shared<StateQuaternion>(Vector4d::Random().normalized()) : 
+                                                        nullptr));
+        lmk->setId(i);
+        landmarks.push_back(lmk);
+        state_landmarks.push_back(lmk->getState());
+    }
+
+    // Store groundtruth poses
+    state_robot  = F0->getState("PO");
+    state_sensor = sensor->getState("PO");
+}
+
+void ProcessorLandmarkExternalTest::randomStep()
+{
+    // compute delta
+    VectorXd delta;
+    MatrixXd delta_cov;
+    if (dim == 2)
+    {
+        delta = Vector2d::Random() * 0.1;
+        delta_cov = Matrix2d::Identity() * 0.1;
+    }
+    else
+    {
+        delta = Vector7d::Random() * 0.1;
+        delta.tail<4>().normalize();
+        delta_cov = Matrix6d::Identity() * 0.1;
+    }
+
+    // Process odometry
+    auto cap_odom = std::make_shared<CaptureMotion>("CaptureOdom", t, sensor_odom, delta, delta_cov, nullptr);
+    cap_odom->process();
+
+    // update groundtruth pose with random movement
+    state_robot = processor_motion->getState("PO");
+}
+
+CaptureLandmarksExternalPtr ProcessorLandmarkExternalTest::computeCaptureLandmarks() const
+{
+    // Detections
+    auto cap = std::make_shared<CaptureLandmarksExternal>(t, sensor);
+    for (auto i = 0; i < landmarks.size(); i++)
+    {
+        // compute detection
+        VectorXd meas(orientation?(dim==2?3:7):(dim==2?2:3));
+        if (dim == 2)
+        {
+            meas.head(2) = Rotation2Dd(-state_sensor.at('O')(0))*(Rotation2Dd(-state_robot.at('O')(0))*(state_landmarks.at(i).at('P') - state_robot.at('P')) - state_sensor.at('P'));
+            if (orientation)
+                meas(2) = state_landmarks.at(i).at('O')(0) - state_robot.at('O')(0) - state_sensor.at('O')(0);
+        }
+        else
+        {
+            auto q_sensor = Quaterniond(Vector4d(state_sensor.at('O')));
+            auto q_robot  = Quaterniond(Vector4d(state_robot.at('O')));
+
+            meas.head(3) = q_sensor.conjugate() * (q_robot.conjugate() * (state_landmarks.at(i).at('P') - state_robot.at('P')) - state_sensor.at('P'));
+            if (orientation)
+                meas.tail(4) = (q_sensor.conjugate() * q_robot.conjugate() * Quaterniond(Vector4d(state_landmarks.at(i).at('O')))).coeffs();
+        }
+        // cov
+        MatrixXd cov = MatrixXd::Identity(meas.size(), meas.size());
+        if (orientation and dim != 2)
+            cov = MatrixXd::Identity(6, 6);
+
+        // quality
+        double quality = (double) i / (double) (landmarks.size()-1); // increasing from 0 to 1
+
+        // add detection
+        cap->addDetection(landmarks.at(i)->id(), meas, cov, quality );
+    }
+
+    return cap;
+}
+
+void ProcessorLandmarkExternalTest::testConfiguration(int dim, 
+                                                                    bool orientation, 
+                                                                    double quality_th, 
+                                                                    double dist_th, 
+                                                                    int track_length, 
+                                                                    double time_span, 
+                                                                    bool add_landmarks)
+{
+    initProblem(dim, orientation, quality_th, dist_th, track_length, time_span, add_landmarks);  
+
+    ASSERT_TRUE(problem->check());
+
+    for (auto i = 0; i<10; i++)
+    {
+        WOLF_INFO("\n================= STEP ", i, " t = ", t, " =================");
+
+        // detection of landmarks
+        auto cap = computeCaptureLandmarks();
+        ASSERT_TRUE(problem->check());
+
+        // process detections
+        cap->process();
+        ASSERT_TRUE(problem->check());
+        problem->print(4,1,1,1);
+
+        // CHECKS
+        FactorBasePtrList fac_list;
+        problem->getTrajectory()->getFactorList(fac_list);
+        // should emplace KF in last?
+        bool any_active_track = quality_th <=1 and i >= track_length-1;
+        bool should_emplace_KF = t-dt > time_span and any_active_track;
+        WOLF_INFO("should emplace KF: ", should_emplace_KF, " t-dt = ", t-dt, " time_span = ", time_span, " track_length-1 = ", track_length-1);
+
+        if (should_emplace_KF)
+        {   
+            // voted for keyframe
+            ASSERT_TRUE(problem->getTrajectory()->size() > 1);
+            
+            // emplaced factors
+            ASSERT_FALSE(fac_list.empty());
+            
+            // factors' type
+            if (should_emplace_KF)
+                for (auto fac : fac_list)
+                {
+                    if (fac->getProcessor() == processor)
+                    {
+                        ASSERT_EQ(fac->getType(), std::string("FactorRelative") + 
+                                                  (orientation ? "Pose" : "Position") + 
+                                                  (dim == 2 ? "2d" : "3d") + 
+                                                  "WithExtrinsics");
+                    }
+                }
+            // landmarks created by processor
+            if (not add_landmarks)
+            {
+                auto landmarks_map = problem->getMap()->getLandmarkList();
+                // amount of landmarks due to quality filtering of detections
+                auto n_landmarks = (quality_th <= 0 ? 3 : (quality_th <= 0.5 ? 2 : (quality_th <= 1 ? 1 : 0)));
+                ASSERT_EQ(landmarks_map.size(), n_landmarks);
+
+                // check state correctly initialized
+                for (auto lmk_map : landmarks_map)
+                {
+                    ASSERT_TRUE(lmk_map->id() < landmarks.size());
+                    ASSERT_EQ(lmk_map->id(), landmarks.at(lmk_map->id())->id());
+                    assertVectorComposite(lmk_map->getState(), state_landmarks.at(lmk_map->id()));
+                }
+            }
+        }
+        else
+        {
+            // didn't vote for keyframe
+            ASSERT_FALSE(problem->getTrajectory()->size() > 1);
+            // no factors emplaced
+            ASSERT_TRUE(fac_list.empty());
+            // no landmarks created yet (if not added by the test)
+            ASSERT_EQ(problem->getMap()->getLandmarkList().empty(), not add_landmarks);
+        }
+
+        // step with random movement
+        t+=dt;
+        randomStep();
+
+        // solve
+        if (should_emplace_KF)
+        {
+            // perturb landmarks
+            for (auto lmk : problem->getMap()->getLandmarkList())
+                lmk->perturb();
+
+            // fix frame and extrinsics
+            sensor->fix();
+            problem->getLastFrame()->fix();
+
+            // solve
+            auto report = solver->solve(SolverManager::ReportVerbosity::FULL);
+            WOLF_INFO(report);
+
+            // check values                    
+            //assertVectorComposite(sensor->getState("PO"), state_sensor);
+            //assertVectorComposite(problem->getState("PO"), state_robot);
+            for (auto lmk_map : problem->getMap()->getLandmarkList())
+            {
+                assertVectorComposite(lmk_map->getState(), state_landmarks.at(lmk_map->id()));
+            }
+        }
+    }
+}
+
+void ProcessorLandmarkExternalTest::assertVectorComposite(const VectorComposite& v1, const VectorComposite& v2) const
+{
+    if (v1.includesStructure("PO") and v2.includesStructure("PO"))
+    {
+        if (dim == 2)
+        {
+            ASSERT_POSE2d_APPROX(v1.vector("PO"), v2.vector("PO"), Constants::EPS);
+        }
+        else
+        {
+            ASSERT_POSE3d_APPROX(v1.vector("PO"), v2.vector("PO"), Constants::EPS);
+        }
+    }
+    else if (v1.includesStructure("P") and v2.includesStructure("P"))
+    {
+        ASSERT_MATRIX_APPROX(v1.vector("P"), v2.vector("P"), Constants::EPS);
+    }
+    else
+        throw std::runtime_error("wrong vector composite");
+}
+
+// / TESTS //////////////////////////////////////////
+TEST_F(ProcessorLandmarkExternalTest, P_2d_existing_lmks)
+{
+    testConfiguration(2,      //int dim
+                      false,  //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      6,      //int track_length
+                      4.5*dt, //double time_span
+                      true);  //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, P_2d_no_lmks)
+{
+    testConfiguration(2,      //int dim
+                      false,  //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      2,      //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, P_2d_no_lmks_quality)
+{
+    testConfiguration(2,      //int dim
+                      false,  //bool orientation
+                      0.3,    //double quality_th
+                      1e6,    //double dist_th
+                      6,      //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, PO_2d_existing_lmks)
+{
+    testConfiguration(2,      //int dim
+                      true,   //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      3,      //int track_length
+                      4.5*dt, //double time_span
+                      true);  //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, PO_2d_no_lmks)
+{
+    testConfiguration(2,      //int dim
+                      true,   //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      1,      //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, PO_2d_no_lmks_quality)
+{
+    testConfiguration(2,      //int dim
+                      true,   //bool orientation
+                      0.3,    //double quality_th
+                      1e6,    //double dist_th
+                      0,      //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, P_3d_existing_lmks)
+{
+    testConfiguration(3,      //int dim
+                      false,  //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      7,      //int track_length
+                      4.5*dt, //double time_span
+                      true);  //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, P_3d_no_lmks)
+{
+    testConfiguration(3,      //int dim
+                      false,  //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      53,     //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, P_3d_no_lmks_quality)
+{
+    testConfiguration(3,      //int dim
+                      false,  //bool orientation
+                      0.3,    //double quality_th
+                      1e6,    //double dist_th
+                      2,      //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, PO_3d_existing_lmks)
+{
+    testConfiguration(3,      //int dim
+                      true,   //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      1,      //int track_length
+                      4.5*dt, //double time_span
+                      true);  //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, PO_3d_no_lmks)
+{
+    testConfiguration(3,      //int dim
+                      true,   //bool orientation
+                      0,      //double quality_th
+                      1e6,    //double dist_th
+                      4,      //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+TEST_F(ProcessorLandmarkExternalTest, PO_3d_no_lmks_quality)
+{
+    testConfiguration(3,      //int dim
+                      true,   //bool orientation
+                      0.2,    //double quality_th
+                      1e6,    //double dist_th
+                      5,      //int track_length
+                      4.5*dt, //double time_span
+                      false); //bool add_landmarks
+}
+
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_processor_loop_closure.cpp b/test/gtest_processor_loop_closure.cpp
index 9623ecbaa1dd1560650c8cb9e96c044c87d18e9b..57a0d4cbdb0dc23509fb4ef9f7f691d14c36cbea 100644
--- a/test/gtest_processor_loop_closure.cpp
+++ b/test/gtest_processor_loop_closure.cpp
@@ -24,6 +24,8 @@
 #include "core/problem/problem.h"
 #include "core/capture/capture_base.h"
 #include "core/factor/factor_relative_pose_2d.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 
 #include "dummy/processor_loop_closure_dummy.h"
 
@@ -47,8 +49,8 @@ class ProcessorLoopClosureTest : public testing::Test
             // Emplace sensor
             sensor = SensorBase::emplace<SensorBase>(problem->getHardware(),
                                                      "SensorBase",
-                                                     std::make_shared<StateBlock>(Vector2d::Zero()),
-                                                     std::make_shared<StateBlock>(Vector1d::Zero()),
+                                                     std::make_shared<StatePoint2d>(Vector2d::Zero()),
+                                                     std::make_shared<StateAngle>(0),
                                                      nullptr,
                                                      2);
 
diff --git a/test/gtest_processor_tracker_landmark_dummy.cpp b/test/gtest_processor_tracker_landmark_dummy.cpp
index 4ed3c0846017e15fe5882607ff1807fbbbbc93f5..69ed41d60e75af3346156192b145dff16c15bf31 100644
--- a/test/gtest_processor_tracker_landmark_dummy.cpp
+++ b/test/gtest_processor_tracker_landmark_dummy.cpp
@@ -25,6 +25,8 @@
 #include "core/sensor/factory_sensor.h"
 #include "dummy/processor_tracker_landmark_dummy.h"
 #include "core/capture/capture_void.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 
 using namespace wolf;
 
@@ -285,8 +287,8 @@ TEST_F(ProcessorTrackerLandmarkDummyTest, emplaceFactor)
                                                      Eigen::Vector1d::Ones(),
                                                      Eigen::MatrixXd::Ones(1, 1)));
     LandmarkBasePtr lmk(std::make_shared<LandmarkBase>("LandmarkBase",
-                                                       std::make_shared<StateBlock>(1),
-                                                       std::make_shared<StateBlock>(1)));
+                                                       std::make_shared<StateParams1>(Vector1d(0)),
+                                                       std::make_shared<StateAngle>(0)));
 
     FactorBasePtr fac = processor->callEmplaceFactor(ftr, lmk);
     ASSERT_EQ(fac->getFeature(),ftr);
diff --git a/test/gtest_rotation.cpp b/test/gtest_rotation.cpp
index bdc5e696b1e4a012abdee9fc93b16bd647b85172..7ec49fdee46c118d8d7907e17a43f5a2c598e661 100644
--- a/test/gtest_rotation.cpp
+++ b/test/gtest_rotation.cpp
@@ -813,6 +813,6 @@ int main(int argc, char **argv)
      */
 
     testing::InitGoogleTest(&argc, argv);
-    ::testing::GTEST_FLAG(filter) = "Conversions.R2q_norm_of_q";
+    // ::testing::GTEST_FLAG(filter) = "Conversions.R2q_norm_of_q";
     return RUN_ALL_TESTS();
 }
diff --git a/test/gtest_sensor_pose.cpp b/test/gtest_sensor_pose.cpp
index d37c700a7daf6a45862a4d8b830b8b8e67767282..ad32fad28a18655ac16993733e6fa371ef805d92 100644
--- a/test/gtest_sensor_pose.cpp
+++ b/test/gtest_sensor_pose.cpp
@@ -51,24 +51,32 @@ TEST(Pose, constructor)
 TEST(Pose, getParams)
 {
     auto intr = std::make_shared<ParamsSensorPose>();
-    intr->std_p = 2;
-    intr->std_o = 3;
+    intr->std_px = 2;
+    intr->std_py = 2;
+    intr->std_pz = 2;
+    intr->std_ox = 3;
+    intr->std_oy = 3;
+    intr->std_oz = 3;
 
     Vector7d extr; extr << 1,2,3,.5,.5,.5,.5;
 
     auto sen = std::make_shared<SensorPose>(extr, intr);
 
-    ASSERT_EQ(sen->getStdP(), intr->std_p); 
-    ASSERT_EQ(sen->getStdO(), intr->std_o); 
-    // ASSERT_EQ(sen->getNoiseCov()(0,0), (Vector1d() << 4).finished());
-    // ASSERT_EQ(sen->getNoiseCov()(3,3), (Vector1d() << 9).finished());
+    // ASSERT_EQ(sen->getStdP(), intr->std_p); 
+    // ASSERT_EQ(sen->getStdO(), intr->std_o); 
+    ASSERT_EQ(sen->getNoiseCov()(0,0), 4);
+    ASSERT_EQ(sen->getNoiseCov()(3,3), 9);
 }
 
 TEST(Pose, create)
 {
     auto intr = std::make_shared<ParamsSensorPose>();
-    intr->std_p = 2;
-    intr->std_o = 3;
+    intr->std_px = 2;
+    intr->std_py = 2;
+    intr->std_pz = 2;
+    intr->std_ox = 3;
+    intr->std_oy = 3;
+    intr->std_oz = 3;
 
     Vector7d extr; extr << 1,2,3,.5,.5,.5,.5;
 
@@ -76,10 +84,10 @@ TEST(Pose, create)
 
     auto sen = std::static_pointer_cast<SensorPose>(sen_base);
 
-    ASSERT_EQ(sen->getStdP(), intr->std_p); 
-    ASSERT_EQ(sen->getStdO(), intr->std_o);
-    // ASSERT_EQ(sen->getNoiseCov()(0,0), (Vector1d() << 4).finished());
-    // ASSERT_EQ(sen->getNoiseCov()(3,3), (Vector1d() << 9).finished());
+    // ASSERT_EQ(sen->getStdP(), intr->std_p); 
+    // ASSERT_EQ(sen->getStdO(), intr->std_o);
+    ASSERT_EQ(sen->getNoiseCov()(0,0), 4);
+    ASSERT_EQ(sen->getNoiseCov()(3,3), 9);
 }
 
 int main(int argc, char **argv)
diff --git a/test/gtest_solver_ceres.cpp b/test/gtest_solver_ceres.cpp
index 89bf93042391ae90c0f12560922795029ff6d704..3fb6f4abadd3528878978b95764fc1359aaf9148 100644
--- a/test/gtest_solver_ceres.cpp
+++ b/test/gtest_solver_ceres.cpp
@@ -30,7 +30,7 @@
 
 #include "core/problem/problem.h"
 #include "core/sensor/sensor_base.h"
-#include "core/state_block/state_block.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/capture/capture_void.h"
 #include "core/factor/factor_pose_2d.h"
 #include "core/factor/factor_quaternion_absolute.h"
@@ -57,7 +57,7 @@ using namespace Eigen;
 StateBlockPtr createStateBlock()
 {
     Vector4d state; state << 1, 0, 0, 0;
-    return std::make_shared<StateBlock>(state);
+    return std::make_shared<StateParams4>(state);
 }
 
 FactorBasePtr createFactor(StateBlockPtr sb_ptr)
diff --git a/test/gtest_solver_manager.cpp b/test/gtest_solver_manager.cpp
index e66342ba48d3ba7e2c6248e249e364540c1c9537..65056864243e2104071e4a5349961efb56d6b7aa 100644
--- a/test/gtest_solver_manager.cpp
+++ b/test/gtest_solver_manager.cpp
@@ -37,6 +37,8 @@
 #include "core/factor/factor_block_absolute.h"
 #include "core/state_block/local_parametrization_quaternion.h"
 #include "dummy/solver_manager_dummy.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
 
 #include <iostream>
 #include <thread>
@@ -52,7 +54,7 @@ using namespace Eigen;
 StateBlockPtr createStateBlock()
 {
     Vector4d state; state << 1, 0, 0, 0;
-    return std::make_shared<StateBlock>(state);
+    return std::make_shared<StateParams4>(state);
 }
 
 FactorBasePtr createFactor(StateBlockPtr sb_ptr)
diff --git a/test/gtest_state_block.cpp b/test/gtest_state_block.cpp
index 9bce46a4856e123124e3b62aed99c4e2a09a7b01..17ad54a8ce833aecb23ac1735b2d4c7c93439d59 100644
--- a/test/gtest_state_block.cpp
+++ b/test/gtest_state_block.cpp
@@ -28,34 +28,34 @@
 
 #include "core/utils/utils_gtest.h"
 
-
 #include "core/state_block/state_block.h"
 #include "core/state_block/state_quaternion.h"
 #include "core/state_block/state_angle.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_composite.h"
 
 #include <iostream>
 
-
 using namespace Eigen;
 using namespace std;
 using namespace wolf;
 
-TEST(StateBlock, block_perturb)
+TEST(StateBlock, point_perturb)
 {
-    Vector3d x(10,20,30);
-    StateBlock sb(x);
+    Vector3d     x(10, 20, 30);
+    StatePoint3d sb(x);
 
     sb.perturb(0.5);
 
     WOLF_INFO("original ", x.transpose(), ", perturbed = ", sb.getState().transpose());
 
     ASSERT_NE(x.norm(), sb.getState().norm());
-    ASSERT_MATRIX_APPROX(x , sb.getState() , 0.5 * 4); // 4-sigma test...
+    ASSERT_MATRIX_APPROX(x, sb.getState(), 0.5 * 4);  // 4-sigma test...
 }
 
 TEST(StateBlock, angle_perturb)
 {
-    Vector1d x(1.0);
+    Vector1d   x(1.0);
     StateAngle sb(x(0));
 
     sb.perturb(0.1);
@@ -63,28 +63,174 @@ TEST(StateBlock, angle_perturb)
     WOLF_INFO("original ", x.transpose(), ", perturbed = ", sb.getState().transpose());
 
     ASSERT_NE(x.norm(), sb.getState().norm());
-    ASSERT_MATRIX_APPROX(x , sb.getState() , 0.1 * 4); // 4-sigma test...
+    ASSERT_MATRIX_APPROX(x, sb.getState(), 0.1 * 4);  // 4-sigma test...
 }
 
 TEST(StateBlock, quaternion_perturb)
 {
-    Vector4d x(0.5,0.5,0.5,0.5);
+    Vector4d        x(0.5, 0.5, 0.5, 0.5);
     StateQuaternion sb(x);
 
     sb.perturb(0.01);
 
     WOLF_INFO("original ", x.transpose(), ", perturbed = ", sb.getState().transpose());
 
-    ASSERT_LT((sb.getState().transpose() * x).norm() , 1.0); // quaternions are not parallel ==> not equal
-    ASSERT_MATRIX_APPROX(x , sb.getState() , 0.01 * 4); // 4-sigma test...
+    ASSERT_LT((sb.getState().transpose() * x).norm(), 1.0);  // quaternions are not parallel ==> not equal
+    ASSERT_MATRIX_APPROX(x, sb.getState(), 0.01 * 4);        // 4-sigma test...
 }
 
-int main(int argc, char **argv)
+TEST(StatePoint3d, transformable)
+{
+    Vector3d x(1, 2, 3);
+
+    // default is transformable
+    StatePoint3d sb_tr(x);
+    ASSERT_TRUE(sb_tr.isTransformable());
+
+    // not fixed, non transformable
+    StatePoint3d sb_ntr(x, false, false);
+    ASSERT_FALSE(sb_ntr.isTransformable());
+
+    // not fixed, transformable
+    StatePoint3d sb_tr2(x, false, true);
+    ASSERT_TRUE(sb_tr2.isTransformable());
+}
+
+TEST(StatePoint3d, transform_identity)
+{
+    Vector3d     x0(1, 2, 3);
+    StatePoint3d sb(x0);
+
+    Vector7d xtrf;
+    xtrf << 0, 0, 0, 0, 0, 0, 1;
+    VectorComposite trf(xtrf, "PO", {3, 4});
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x0, 1e-15);
+}
+
+TEST(StatePoint3d, transform_translation_4x5y6z)
+{
+    Vector3d     x0(1, 2, 3);
+    StatePoint3d sb(x0);
+
+    Vector7d xtrf;
+    xtrf << 4, 5, 6, 0, 0, 0, 1;
+    VectorComposite trf(xtrf, "PO", {3, 4});
+
+    Vector3d x(5, 7, 9);  // x0 translated (4,5,6)
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
+}
+
+TEST(StatePoint3d, transform_rotation_90x)
 {
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
+    Vector3d     x0(1, 2, 3);
+    StatePoint3d sb(x0);
+
+    Vector7d xtrf;
+    xtrf << 0, 0, 0, sqrt(2) / 2, 0, 0, sqrt(2) / 2;
+    VectorComposite trf(xtrf, "PO", {3, 4});
+
+    Vector3d x(1, -3, 2);  // x0 rotated (90,0,0)
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
 }
 
+TEST(StatePoint3d, transform_translation_1y_rotation_90x)
+{
+    Vector3d     x0(1, 2, 3);
+    StatePoint3d sb(x0);
+
+    Vector7d xtrf;
+    xtrf << 0, 1, 0, sqrt(2) / 2, 0, 0, sqrt(2) / 2;
+    VectorComposite trf(xtrf, "PO", {3, 4});
+
+    Vector3d x(1, -2, 2);  // x0 translated (0,1,0) and rotated (90,0,0)
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
+}
+
+TEST(StatePoint3d, non_transformable_translation_rotation)
+{
+    Vector3d     x0(1, 2, 3);
+    StatePoint3d sb(x0, false, false);  // non transformable
 
+    Vector7d xtrf;
+    xtrf << 0, 1, 0, sqrt(2) / 2, 0, 0, sqrt(2) / 2;
+    VectorComposite trf(xtrf, "PO", {3, 4});
 
+    Vector3d x(1, 2, 3);  // x0 NOT translated (0,1,0) and NOT rotated (90,0,0)
 
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
+}
+
+TEST(StateVector3d, transform_translation_1y_rotation_90x)
+{
+    Vector3d      x0(1, 2, 3);
+    StateVector3d sb(x0);
+
+    Vector7d xtrf;
+    xtrf << 0, 1, 0, sqrt(2) / 2, 0, 0, sqrt(2) / 2;
+    VectorComposite trf(xtrf, "PO", {3, 4});
+
+    Vector3d x(1, -3, 2);  // x0 NOT translated (0,1,0) and rotated (90,0,0)
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
+}
+
+TEST(StatePoint2d, transform_translation_1y_rotation_90)
+{
+    Vector2d     x0(1, 2);
+    StatePoint2d sb(x0);
+
+    Vector3d xtrf;
+    xtrf << 0, 1, M_PI / 2;
+    VectorComposite trf(xtrf, "PO", {2, 1});
+
+    Vector2d x(-2, 2);  // x0 translated (0,1) and rotated 90 deg
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
+}
+
+TEST(StateVector2d, transform_translation_1y_rotation_90)
+{
+    Vector2d      x0(1, 2);
+    StateVector2d sb(x0);
+
+    Vector3d xtrf;
+    xtrf << 0, 1, M_PI / 2;
+    VectorComposite trf(xtrf, "PO", {2, 1});
+
+    Vector2d x(-2, 1);  // x0 NON translated (0,1) and rotated 90 deg
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
+}
+
+TEST(StateParams4, transform_translation_1y_rotation_90x)
+{
+    Vector4d       x0(1, 2, 3, 4);
+    StateParams<4> sb(x0);
+
+    Vector7d xtrf;
+    xtrf << 0, 1, 0, sqrt(2) / 2, 0, 0, sqrt(2) / 2;
+    VectorComposite trf(xtrf, "PO", {3, 4});
+
+    Vector4d x(1, 2, 3, 4);  // x0 NOT translated (0,1,0) and NOT rotated (90,0,0)
+
+    sb.transform(trf);
+    ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15);
+}
+
+int main(int argc, char **argv)
+{
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_state_composite.cpp b/test/gtest_state_composite.cpp
index 74dfe643ab18b1658a774fa782fe22e0a9284a66..5631d5fddeb26014baeb2698817481a602239a5e 100644
--- a/test/gtest_state_composite.cpp
+++ b/test/gtest_state_composite.cpp
@@ -27,6 +27,7 @@
  */
 
 #include "core/state_block/state_composite.h"
+#include "core/state_block/state_block_derived.h"
 #include "core/state_block/state_quaternion.h"
 
 #include "core/utils/utils_gtest.h"
@@ -46,11 +47,11 @@ class StateBlockCompositeInit : public testing::Test
 
         void SetUp() override
         {
-            sbp = states.emplace('P', Vector3d(1,2,3));
-            sbv = states.emplace('V', Vector3d(4,5,6));
+            sbp = states.emplace<StatePoint3d>('P', Vector3d(1,2,3));
+            sbv = states.emplace<StateVector3d>('V', Vector3d(4,5,6));
             sbq = states.emplace<StateQuaternion>('Q', Vector4d(.5,.5,.5,.5));
 
-            sbx = std::make_shared<StateBlock>(Vector3d(7,8,9));
+            sbx = std::make_shared<StateVector3d>(Vector3d(7,8,9));
         }
 };
 
diff --git a/test/gtest_yaml_conversions.cpp b/test/gtest_yaml_conversions.cpp
index 21a4f89781b017807033f8c33d59dff716ec68ac..f558c5764668e4833274f0b3daba34e40406a176 100644
--- a/test/gtest_yaml_conversions.cpp
+++ b/test/gtest_yaml_conversions.cpp
@@ -30,7 +30,7 @@
 #include "core/utils/utils_gtest.h"
 #include "core/yaml/yaml_conversion.h"
 #include <yaml-cpp/yaml.h>
-#include <eigen3/Eigen/Dense>
+#include <Eigen/Dense>
 #include <iostream>
 //#include <fstream>
 
diff --git a/test/yaml/maps/map_2d_manual.yaml b/test/yaml/maps/map_2d_manual.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4a2276ad153b4adfbae3679d42b1aba801f15bb3
--- /dev/null
+++ b/test/yaml/maps/map_2d_manual.yaml
@@ -0,0 +1,13 @@
+map_name: 2d map saved from Map::save()
+date_time: 21/09/2022 at 12:00:59
+useless_parameter: [1, 6, 3]
+landmarks:
+  - 
+    id: 1
+    type: LandmarkBase
+    position: [1, 2]
+    position fixed: false
+    transformable: true
+    another_useless_param: "wtf"
+  - {id: 2, type: LandmarkBase, position: [3, 4], position fixed: false, orientation: [2], orientation fixed: false, transformable: true}
+  - {id: 3, type: LandmarkBase, position: [5, 6], position fixed: true, orientation: [3], orientation fixed: true, transformable: true}
\ No newline at end of file
diff --git a/test/yaml/maps/map_3d_manual.yaml b/test/yaml/maps/map_3d_manual.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4b66ed90f51224d03b70cf484ac333da74e777f4
--- /dev/null
+++ b/test/yaml/maps/map_3d_manual.yaml
@@ -0,0 +1,25 @@
+#map_name: 3d map saved from Problem::saveMap()
+#date_time: 21/09/2022 at 12:00:59
+landmarks:
+  - 
+    id: 10
+    type: LandmarkBase
+    position: [1, 2, 3]
+    position fixed: false
+    transformable: true
+  - 
+    id: 11
+    type: LandmarkBase
+    position: [4, 5, 6]
+    position fixed: false
+    orientation: [0, 1, 0, 0]
+    orientation fixed: false
+    transformable: true
+  - 
+    id: 12
+    type: LandmarkBase
+    position: [7, 8, 9]
+    position fixed: true
+    orientation: [0, 0, 1, 0]
+    orientation fixed: true
+    transformable: true
\ No newline at end of file
diff --git a/test/yaml/params_problem_autosetup.yaml b/test/yaml/params_problem_autosetup.yaml
index c61eab58146a9a5917c1130a14e234afa08a01b6..0f7dd1954fc620a7670bc183337589ae52a25c46 100644
--- a/test/yaml/params_problem_autosetup.yaml
+++ b/test/yaml/params_problem_autosetup.yaml
@@ -43,7 +43,7 @@ config:
         dist_traveled:          999   # meters
         angle_turned:           999   # radians (1 rad approx 57 deg, approx 60 deg)
       
-      unmeasured_perturbation_std: 0.00111
+      unmeasured_perturbation_std: 0.001
       
       state_getter: true
       state_priority: 1