diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 05026060218f95900ef4517df78e099099b80c89..cbaac4732ca625c3b9430327a66f6b3c82ca39fa 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
@@ -15,7 +25,6 @@ stages:
   - echo $GNSSUTILS_BRANCH
   - echo $LASERSCANUTILS_BRANCH
   - echo $CI_COMMIT_BRANCH
-  - echo $WOLF_ROS_NODE_BRANCH
   - echo $WOLF_ROS_IMU_BRANCH
   - echo $WOLF_ROS_GNSS_BRANCH
   - echo $WOLF_ROS_LASER_BRANCH
@@ -90,8 +99,7 @@ stages:
   - if [ -d wolf ]; then
   -   echo "directory wolf exists"
   -   cd wolf
-  -   git checkout devel
-  -   git pull
+  -   git fetch --all
   -   git checkout $WOLF_CORE_BRANCH
   -   git pull
   - else
@@ -198,7 +206,7 @@ deploy_vision:
     WOLF_VISION_BRANCH: $WOLF_VISION_BRANCH
     WOLF_ROS_NODE_BRANCH: $CI_COMMIT_BRANCH
   trigger: 
-    project: mobile_robotics/wolf_projects/wolf_ros/wolf_ros_apriltag
+    project: mobile_robotics/wolf_projects/wolf_ros/wolf_ros_vision
     branch: $WOLF_ROS_VISION_BRANCH
 
 deploy_apriltag:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dfb477604a3919687abde4cd76fd6618c185bacd..5331b32813d9ad1be6b1431bbc16ed83e83369c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ project(wolf_ros_node)
 ## Compile as C++14
 add_compile_options(-std=c++14) 
 # -fPIC and -rdynamic ensure unique singleton instance across shared libraries (for factories) see: https://stackoverflow.com/a/8626922
-SET(CMAKE_CXX_FLAGS "-fPIC -rdynamic")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -rdynamic")
 
 find_package(catkin REQUIRED COMPONENTS
   roslib
@@ -15,11 +15,25 @@ find_package(catkin REQUIRED COMPONENTS
   tf
   tf_conversions
   tf2_ros
+  message_generation
 )
 
 ## System dependencies are found with CMake's conventions
 find_package(wolfcore REQUIRED)
 
+## Generate messages in the 'msg' folder
+add_message_files(
+  FILES
+  LandmarkDetection.msg
+  LandmarkDetectionArray.msg
+)
+
+## Generate added messages and services with any dependencies listed here
+generate_messages(
+  DEPENDENCIES
+  geometry_msgs
+  std_msgs
+)
 
 ###################################
 ## catkin specific configuration ##
@@ -27,6 +41,7 @@ find_package(wolfcore REQUIRED)
 # produces the XXXConfig.cmake file among other things
 catkin_package(
   INCLUDE_DIRS include
+  CATKIN_DEPENDS message_runtime std_msgs geometry_msgs
 )
 
 ###########
@@ -43,16 +58,21 @@ include_directories(
 add_executable(${PROJECT_NAME} src/node.cpp)
 
 add_library(subscriber_${PROJECT_NAME}
-  			    src/subscriber_diffdrive.cpp
-  			    src/subscriber_odom2d.cpp)
+            src/subscriber_diffdrive.cpp
+  			    src/subscriber_landmarks.cpp
+  			    src/subscriber_odom2d.cpp
+  			    src/subscriber_pose.cpp)
+
+# messages generated are a dependency of subscriber_landmarks
+add_dependencies(subscriber_${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS})
 
 add_library(publisher_${PROJECT_NAME}
   			    src/publisher_graph.cpp
   			    src/publisher_pose.cpp
-  			    src/publisher_trajectory.cpp
   			    src/publisher_state_block.cpp
-  			    src/publisher_tf.cpp)
-
+  			    src/publisher_tf.cpp
+  			    src/publisher_trajectory.cpp)
+            
 ## Specify libraries to link a library or executable target against
 target_link_libraries(subscriber_${PROJECT_NAME}
                       ${catkin_LIBRARIES}
diff --git a/include/factory_publisher.h b/include/factory_publisher.h
index 1911c43b42d8a01516c615223b8f42a237178a0e..b64e40f72c2f1260b1724f8ce2d7ac20ef90196f 100644
--- a/include/factory_publisher.h
+++ b/include/factory_publisher.h
@@ -25,6 +25,8 @@
 // wolf
 #include <core/common/factory.h>
 #include <core/utils/params_server.h>
+
+// ros
 #include <ros/ros.h>
 
 namespace wolf
diff --git a/include/factory_subscriber.h b/include/factory_subscriber.h
index dc23500bc1842bcb79acec461f72603dbba1ded8..6d2eaf9f3cbbaf38514b880dd6efceb41835b96a 100644
--- a/include/factory_subscriber.h
+++ b/include/factory_subscriber.h
@@ -25,10 +25,9 @@
 // wolf
 #include <core/common/factory.h>
 #include <core/utils/params_server.h>
-#include <ros/ros.h>
 
-// #include "wolf_ros_subscriber.h"
-// std
+// ros
+#include <ros/ros.h>
 
 
 namespace wolf
diff --git a/include/node.h b/include/node.h
index 8324843dbbc865276bb527563164660e32e5b19a..d4c2dde1ee2d044bc501f74bf1c09d4f21d81c7f 100644
--- a/include/node.h
+++ b/include/node.h
@@ -19,29 +19,33 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
+#ifndef NODE_H
+#define NODE_H
+
+/**************************
+ *   WOLF ROS includes    *
+ **************************/
+#include "subscriber.h"
+#include "publisher.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
-#include <core/common/node_base.h>
 #include <core/common/wolf.h>
-#include <core/capture/capture_odom_2d.h>
-#include <core/ceres_wrapper/solver_ceres.h>
-#include <core/sensor/sensor_odom_2d.h>
-#include <core/processor/processor_odom_2d.h>
+#include <core/solver/solver_manager.h>
 #include <core/problem/problem.h>
 #include <core/utils/loader.h>
-#include <core/yaml/parser_yaml.h>
-#include <core/solver/factory_solver.h>
 
 
 /**************************
- *     CERES includes     *
+ *     ROS includes     *
  **************************/
 #include <ros/ros.h>
 #include <ros/package.h>
 #include <nav_msgs/Odometry.h>
-#include "tf/LinearMath/Transform.h"
-#include "tf/transform_datatypes.h"
+#include <tf/LinearMath/Transform.h>
+#include <tf/transform_datatypes.h>
 #include <tf/transform_broadcaster.h>
 #include <tf/transform_listener.h>
 #include <visualization_msgs/Marker.h>
@@ -57,8 +61,6 @@
 #include <fstream>
 #include <string>
 
-#include "subscriber.h"
-#include "publisher.h"
 
 using namespace wolf;
 
@@ -78,7 +80,7 @@ class WolfRosNode
 
 
     protected:
-        //std::vector<std::shared_ptr<Loader>> loaders_;
+        std::vector<std::shared_ptr<Loader>> loaders_;
 
         // solver
         SolverManagerPtr solver_;
@@ -111,3 +113,5 @@ class WolfRosNode
 
         void createProfilingFile();
 };
+
+#endif // NODE_H
\ No newline at end of file
diff --git a/include/publisher.h b/include/publisher.h
index e86ad782577d46932d9ff9f61d21ab6c78f7be4a..e595811d746744319dbe7e42576c47a77c7efa29 100644
--- a/include/publisher.h
+++ b/include/publisher.h
@@ -21,6 +21,9 @@
 //--------LICENSE_END--------
 #ifndef WOLF_PUBLISHER_H
 #define WOLF_PUBLISHER_H
+
+#include "factory_publisher.h"
+
 /**************************
  *      ROS includes      *
  **************************/
@@ -29,9 +32,8 @@
 /**************************
  *      WOLF includes     *
  **************************/
-#include "core/common/wolf.h"
-#include "core/problem/problem.h"
-#include "factory_publisher.h"
+#include <core/common/wolf.h>
+#include <core/problem/problem.h>
 
 namespace wolf
 {
diff --git a/include/publisher_graph.h b/include/publisher_graph.h
index 22bdd40aa09890400c3dcf71640b1f4ae573ce42..4cdf019cef9e1708f8b0216b58bdd1cb09c0443e 100644
--- a/include/publisher_graph.h
+++ b/include/publisher_graph.h
@@ -22,11 +22,12 @@
 #ifndef PUBLISHER_GRAPH_H
 #define PUBLISHER_GRAPH_H
 
+#include "publisher.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
-#include "core/problem/problem.h"
-#include "publisher.h"
+#include <core/problem/problem.h>
 
 /**************************
  *      ROS includes      *
diff --git a/include/publisher_pose.h b/include/publisher_pose.h
index 1e7428f959bc7fe78654e9266345e631dc658299..59a0ef334366dd0cb1fa6883550f0a9f2f50f904 100644
--- a/include/publisher_pose.h
+++ b/include/publisher_pose.h
@@ -22,12 +22,14 @@
 #ifndef PUBLISHER_POSE_H
 #define PUBLISHER_POSE_H
 
+
+#include "publisher.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
-#include "core/problem/problem.h"
+#include <core/problem/problem.h>
 
-#include "publisher.h"
 
 /**************************
  *      ROS includes      *
diff --git a/include/publisher_state_block.h b/include/publisher_state_block.h
index 78bd104683cecc9a68bbbdc08bb8871d97d83a15..c0d229cf8539f1bf52e452848501e4f0dafe1999 100644
--- a/include/publisher_state_block.h
+++ b/include/publisher_state_block.h
@@ -22,12 +22,12 @@
 #ifndef PUBLISHER_STATE_BLOCK_H
 #define PUBLISHER_STATE_BLOCK_H
 
+#include "publisher.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
-#include "core/problem/problem.h"
-
-#include "publisher.h"
+#include <core/problem/problem.h>
 
 /**************************
  *      ROS includes      *
diff --git a/include/publisher_tf.h b/include/publisher_tf.h
index 333676b43bf7c1a0da7317722431685d4e49fed8..fe545e53b558df0293150e1ed1671d37373f2115 100644
--- a/include/publisher_tf.h
+++ b/include/publisher_tf.h
@@ -22,12 +22,12 @@
 #ifndef PUBLISHER_TF_H
 #define PUBLISHER_TF_H
 
+#include "publisher.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
-#include "core/problem/problem.h"
-
-#include "publisher.h"
+#include <core/problem/problem.h>
 
 /**************************
  *      ROS includes      *
diff --git a/include/publisher_trajectory.h b/include/publisher_trajectory.h
index bd26b6cc366d3dfd139049f6ddf4cd9ac612f246..7ce9eaafa0ac0128f7aee3a441a5cc1362efce5f 100644
--- a/include/publisher_trajectory.h
+++ b/include/publisher_trajectory.h
@@ -28,12 +28,13 @@
 #ifndef PUBLISHER_TRAJECTORY_H
 #define PUBLISHER_TRAJECTORY_H
 
+#include "publisher.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
-#include "core/problem/problem.h"
+#include <core/problem/problem.h>
 
-#include "publisher.h"
 
 /**************************
  *      ROS includes      *
diff --git a/include/subscriber.h b/include/subscriber.h
index 0db5b5cad5158ef5aaa4e8371e03e2be0cb21d5a..5de7b2bb8ab20a022b4cbe921673ca3723a8b462 100644
--- a/include/subscriber.h
+++ b/include/subscriber.h
@@ -22,13 +22,14 @@
 #ifndef WOLF_SUBSCRIBER_H_
 #define WOLF_SUBSCRIBER_H_
 
+#include "factory_subscriber.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
-#include "core/common/wolf.h"
+#include <core/common/wolf.h>
 #include <core/utils/params_server.h>
 #include <core/sensor/sensor_base.h>
-#include "factory_subscriber.h"
 
 /**************************
  *      ROS includes      *
diff --git a/include/subscriber_diffdrive.h b/include/subscriber_diffdrive.h
index 3638e5b372884b07d3398d78fb0d4eb3bf8f172a..48f64e2c91bf6ca2b4cef8fca79b2950f26bcdde 100644
--- a/include/subscriber_diffdrive.h
+++ b/include/subscriber_diffdrive.h
@@ -19,27 +19,30 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
+#ifndef WOLF_SUBSCRIBER_DIFFDRIVE_H_
+#define WOLF_SUBSCRIBER_DIFFDRIVE_H_
+
+
+#include "subscriber.h"
 /**************************
  *      WOLF includes     *
  **************************/
 #include <core/common/wolf.h>
-#include "subscriber.h"
 
 /**************************
  *      ROS includes      *
  **************************/
 #include <ros/ros.h>
 #include <nav_msgs/Odometry.h>
-#include "sensor_msgs/JointState.h"
+#include <sensor_msgs/JointState.h>
 
 namespace wolf
 {
 class SubscriberDiffdrive : public Subscriber
 {
    protected:
-        ros::Time last_odom_stamp_;
         Eigen::Vector2d last_angles_;
-        int last_odom_seq_;
         int last_kf = -1;
         double ticks_cov_factor_;
 
@@ -56,3 +59,5 @@ class SubscriberDiffdrive : public Subscriber
 };
 
 }  // namespace wolf
+
+#endif // WOLF_SUBSCRIBER_DIFFDRIVE_H_
\ No newline at end of file
diff --git a/include/subscriber_landmarks.h b/include/subscriber_landmarks.h
new file mode 100644
index 0000000000000000000000000000000000000000..f389542a57bfcc607692c7c772e56bf69bf89d93
--- /dev/null
+++ b/include/subscriber_landmarks.h
@@ -0,0 +1,60 @@
+//--------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 "subscriber.h"
+
+/**************************
+ *      WOLF includes     *
+ **************************/
+#include <core/common/wolf.h>
+#include <core/utils/params_server.h>
+
+/**************************
+ *      ROS includes      *
+ **************************/
+#include <ros/ros.h>
+#include "wolf_ros_node/LandmarkDetectionArray.h"
+
+
+namespace wolf
+{
+class SubscriberLandmarks : public Subscriber
+{
+   protected:
+      SizeEigen dim; 
+      bool inverse_detections_;
+      Eigen::Vector3d sensor_p_;
+      Eigen::Quaterniond sensor_q_;
+
+   public:
+
+    SubscriberLandmarks(const std::string& _unique_name,
+                       const ParamsServer& _server,
+                       const SensorBasePtr _sensor_ptr);
+    WOLF_SUBSCRIBER_CREATE(SubscriberLandmarks);
+
+    virtual void initialize(ros::NodeHandle& nh, const std::string& topic);
+
+    void callback(const wolf_ros_node::LandmarkDetectionArray::ConstPtr& msg);
+};
+}  // namespace wolf
\ No newline at end of file
diff --git a/include/subscriber_odom2d.h b/include/subscriber_odom2d.h
index db786e723b76f644efa934c8eeda30fcd7fcdb45..16ea0f0bc779e1d63bc384096b806fdbacc280f4 100644
--- a/include/subscriber_odom2d.h
+++ b/include/subscriber_odom2d.h
@@ -19,11 +19,19 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
+#ifndef WOLF_SUBSCRIBER_ODOM2D_H_
+#define WOLF_SUBSCRIBER_ODOM2D_H_
+
+
+#include "subscriber.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
 #include <core/common/wolf.h>
 #include <core/utils/params_server.h>
+#include <core/sensor/sensor_odom_2d.h>
 
 /**************************
  *      ROS includes      *
@@ -31,7 +39,6 @@
 #include <ros/ros.h>
 #include <nav_msgs/Odometry.h>
 
-#include "subscriber.h"
 
 namespace wolf
 {
@@ -54,3 +61,5 @@ class SubscriberOdom2d : public Subscriber
 };
 
 }  // namespace wolf
+
+#endif
\ No newline at end of file
diff --git a/include/subscriber_pose.h b/include/subscriber_pose.h
new file mode 100644
index 0000000000000000000000000000000000000000..27be7e4e349e2ce59fedd1a074c1a01dd34341b3
--- /dev/null
+++ b/include/subscriber_pose.h
@@ -0,0 +1,65 @@
+//--------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 WOLF_SUBSCRIBER_POSE_H_
+#define WOLF_SUBSCRIBER_POSE_H_
+
+#include "subscriber.h"
+
+/**************************
+ *      WOLF includes     *
+ **************************/
+#include <core/common/wolf.h>
+#include <core/utils/params_server.h>
+#include <core/capture/capture_pose.h>
+#include <core/sensor/sensor_pose.h>
+#include <core/processor/processor_pose.h>
+
+/**************************
+ *      ROS includes      *
+ **************************/
+#include <ros/ros.h>
+#include <geometry_msgs/PoseStamped.h>
+
+
+namespace wolf
+{
+class SubscriberPose : public Subscriber
+{
+   protected:
+      ros::Time last_pose_stamp_;
+      SensorPosePtr sensor_pose_;
+
+   public:
+
+    SubscriberPose(const std::string& _unique_name,
+                     const ParamsServer& _server,
+                     const SensorBasePtr _sensor_ptr);
+    WOLF_SUBSCRIBER_CREATE(SubscriberPose);
+
+    virtual void initialize(ros::NodeHandle& nh, const std::string& topic);
+
+    void callback(const geometry_msgs::PoseStamped::ConstPtr& msg);
+};
+
+}  // namespace wolf
+
+#endif
\ No newline at end of file
diff --git a/msg/LandmarkDetection.msg b/msg/LandmarkDetection.msg
new file mode 100644
index 0000000000000000000000000000000000000000..640da05d6f75f583471ff5178b954845c7879d55
--- /dev/null
+++ b/msg/LandmarkDetection.msg
@@ -0,0 +1,3 @@
+geometry_msgs/PoseWithCovariance pose
+int32 id
+float32 quality
\ No newline at end of file
diff --git a/msg/LandmarkDetectionArray.msg b/msg/LandmarkDetectionArray.msg
new file mode 100644
index 0000000000000000000000000000000000000000..d59391da0cd9b0d219bb20cfd688a4d1faa9dd95
--- /dev/null
+++ b/msg/LandmarkDetectionArray.msg
@@ -0,0 +1,2 @@
+Header header
+LandmarkDetection[] detections
\ No newline at end of file
diff --git a/package.xml b/package.xml
index de43c538101cdb9822e207f24ef42eb7766058f3..93cfc209a2c38f60124d5aecb5b39520324262bd 100644
--- a/package.xml
+++ b/package.xml
@@ -49,29 +49,19 @@
   <!-- Use doc_depend for packages you need only for building documentation: -->
   <!--   <doc_depend>doxygen</doc_depend> -->
   <buildtool_depend>catkin</buildtool_depend>
-  <build_depend>roscpp</build_depend>
-  <build_depend>sensor_msgs</build_depend>
-  <build_depend>nav_msgs</build_depend>
-  <build_depend>std_msgs</build_depend>
-  <build_depend>tf</build_depend>
-  <build_depend>tf2_ros</build_depend>
-  <!-- <build_depend>roslib</build_depend> -->
-  <build_export_depend>roscpp</build_export_depend>
-  <build_export_depend>sensor_msgs</build_export_depend>
-  <build_export_depend>nav_msgs</build_export_depend>
-  <build_export_depend>std_msgs</build_export_depend>
-  <build_export_depend>tf</build_export_depend>
-  <build_export_depend>tf2_ros</build_export_depend>
-  <exec_depend>roscpp</exec_depend>
-  <exec_depend>sensor_msgs</exec_depend>
-  <exec_depend>std_msgs</exec_depend>
-  <exec_depend>tf</exec_depend>
-  <exec_depend>tf2_ros</exec_depend>
-  <!-- <exec_depend>roslib</exec_depend> -->
+  <depend>roscpp</depend>
+  <depend>std_msgs</depend>
+  <depend>geometry_msgs</depend>
+  <depend>sensor_msgs</depend>
+  <depend>nav_msgs</depend>
+  <depend>tf</depend>
+  <depend>tf2_ros</depend>
+  <build_depend>message_generation</build_depend>
+  <build_export_depend>message_runtime</build_export_depend>
+  <exec_depend>message_runtime</exec_depend>
 
   <!-- The export tag contains other, unspecified, tags -->
   <export>
     <!-- Other tools can request additional information be placed here -->
-
   </export>
 </package>
diff --git a/src/node.cpp b/src/node.cpp
index 910cfdacd7ce47c71426bc8553d4dbf32cb9feea..7e102f812439d204fa69dc6e65142ca57ebcd263 100644
--- a/src/node.cpp
+++ b/src/node.cpp
@@ -19,14 +19,23 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
-#include <chrono>
-#include "node.h"
-#include "ros/time.h"
-#include "core/solver/factory_solver.h"
-#include "tf/transform_datatypes.h"
+
+// wolf ros
 #include "factory_subscriber.h"
 #include "factory_publisher.h"
 
+// wolf
+#include <core/solver/factory_solver.h>
+#include <core/yaml/parser_yaml.h>
+
+// ros
+#include <node.h>
+#include <ros/time.h>
+#include <tf/transform_datatypes.h>
+
+// std
+#include <chrono>
+
 WolfRosNode::WolfRosNode()
     : nh_(ros::this_node::getName())
     , last_print_(ros::Time(0))
@@ -58,6 +67,16 @@ WolfRosNode::WolfRosNode()
     ROS_INFO("Creating problem...");
     problem_ptr_ = Problem::autoSetup(server);
 
+    // // HACK -> SENSOR PRIORS
+    // auto imu = problem_ptr_->findSensor("IMU");
+    // double std_acc = server.getParam<double>("sensor/IMU/ab_initial_stdev");
+    // double std_gyro = server.getParam<double>("sensor/IMU/wb_initial_stdev");
+    // Array<double,6,1> std_bias;
+    // std_bias << std_acc, std_acc, std_acc, std_gyro, std_gyro, std_gyro;
+    // imu->addPriorParameter('I',                   // bias
+    //                      Vector6d::Zero(),            // mean
+    //                      std_bias.square().matrix().asDiagonal());  // cov
+
     // SOLVER
     ROS_INFO("Creating solver...");
     solver_ = FactorySolver::create("SolverCeres", problem_ptr_, server);
@@ -85,7 +104,7 @@ WolfRosNode::WolfRosNode()
         WOLF_TRACE("Loading publisher " + type + " via " + lib_publisher);
         auto l = std::make_shared<LoaderRaw>(lib_publisher);
         l->load();
-        //loaders_.push_back(l);
+        loaders_.push_back(l);
 
         WOLF_INFO("Pub: ", type, " name: ", name);
         publishers_.push_back(FactoryPublisher::create(type,
@@ -109,13 +128,17 @@ WolfRosNode::WolfRosNode()
         WOLF_TRACE("Loading subscriber " + type + " via " + lib_subscriber);
         auto l = std::make_shared<LoaderRaw>(lib_subscriber);
         l->load();
-        //loaders_.push_back(l);
+        loaders_.push_back(l);
+
+        auto sensor_ptr = problem_ptr_->findSensor(sensor);
+        if (not sensor_ptr)
+            throw std::runtime_error("Sensor " + sensor + " was not found!");
 
         WOLF_TRACE("From sensor {" + sensor + "} subscribing {" + type + "} to {" + topic + "} topic");
         subscribers_.push_back(FactorySubscriber::create(type,
                                                          name,
                                                          server,
-                                                         problem_ptr_->findSensor(sensor),
+                                                         sensor_ptr,
                                                          nh_));
     }
 
diff --git a/src/publisher_graph.cpp b/src/publisher_graph.cpp
index 562634885f86c85246bfd60f978a5154ff43c326..33e6aa24749ad9b7e6c6c63151681cd7abdad764 100644
--- a/src/publisher_graph.cpp
+++ b/src/publisher_graph.cpp
@@ -20,8 +20,13 @@
 //
 //--------LICENSE_END--------
 #include "publisher_graph.h"
+
+// wolf
+#include <core/processor/processor_motion.h>
+
+// ros
 #include <tf/transform_datatypes.h>
-#include "core/processor/processor_motion.h"
+
 
 namespace wolf
 {
@@ -316,6 +321,14 @@ void PublisherGraph::publishFactors()
     // Iterate over the list of factors
     for (auto fac : fac_list)
     {
+        // reset factor_marker contents to minimum content
+        factor_marker = factor_marker_;
+
+        // check factor is valid (it is not being removed) 
+        // (this is redundant with getFactorList() checks but just in case)
+        if (fac->isRemoving())
+            continue;
+
         // Try to fill marker
         if (not fillFactorMarker(fac, factor_marker, factor_text_marker))
             continue;
@@ -410,31 +423,57 @@ bool PublisherGraph::fillLandmarkMarkers(LandmarkBaseConstPtr lmk,
         return false;
 
     // SHAPE ------------------------------------------------------
+    // Pose
+    //    2d: ARROW
+    //    3d: FRAME
     // Position
     //    2d: CYLINDER
     //    3d: SPHERE
-    // Pose -> ARROW
-    if (lmk->getO() != nullptr)
+    if (lmk->getO() and lmk->getO())
     {
-        landmark_marker_.type = visualization_msgs::Marker::ARROW;
-        lmk_marker.scale.x = viz_scale_*landmark_length_;
-        lmk_marker.scale.y = viz_scale_*landmark_width_;
-        lmk_marker.scale.z = viz_scale_*landmark_width_;
+        if (lmk->getP()->getSize() == 2)
+        {
+            landmark_marker_.type = visualization_msgs::Marker::ARROW;
+            lmk_marker.scale.x = viz_scale_*landmark_length_;
+            lmk_marker.scale.y = viz_scale_*landmark_width_;
+            lmk_marker.scale.z = viz_scale_*landmark_width_;
+        }
+        else if (lmk->getP()->getSize() == 3)
+        {
+            landmark_marker_.type = visualization_msgs::Marker::LINE_LIST;
+            lmk_marker.scale.x = viz_scale_*landmark_width_;
+
+            lmk_marker.points = frame_marker_.points;
+            lmk_marker.points[1].x = viz_scale_*landmark_length_;
+            lmk_marker.points[3].y = viz_scale_*landmark_length_;
+            lmk_marker.points[5].z = viz_scale_*landmark_length_;
+
+            lmk_marker.colors = frame_marker_.colors;
+        }
+        else
+            return false;
     }
-    else if (lmk->getP()->getSize() == 2)
+    else if (lmk->getP())
     {
-        landmark_marker_.type = visualization_msgs::Marker::CYLINDER;
-        lmk_marker.scale.x = viz_scale_*landmark_width_;
-        lmk_marker.scale.y = viz_scale_*landmark_width_;
-        lmk_marker.scale.z = viz_scale_*landmark_length_;
+        if (lmk->getP()->getSize() == 2)
+        {
+            landmark_marker_.type = visualization_msgs::Marker::CYLINDER;
+            lmk_marker.scale.x = viz_scale_*landmark_width_;
+            lmk_marker.scale.y = viz_scale_*landmark_width_;
+            lmk_marker.scale.z = viz_scale_*landmark_length_;
+        }
+        else if (lmk->getP()->getSize() == 3)
+        {
+            landmark_marker_.type = visualization_msgs::Marker::SPHERE;
+            lmk_marker.scale.x = viz_scale_*landmark_width_;
+            lmk_marker.scale.y = viz_scale_*landmark_width_;
+            lmk_marker.scale.z = viz_scale_*landmark_width_;
+        }
+        else
+            return false;
     }
     else
-    {
-        landmark_marker_.type = visualization_msgs::Marker::SPHERE;
-        lmk_marker.scale.x = viz_scale_*landmark_width_;
-        lmk_marker.scale.y = viz_scale_*landmark_width_;
-        lmk_marker.scale.z = viz_scale_*landmark_width_;
-    }
+        return false;
 
     // COLOR ------------------------------------------------------
     if (lmk->getHits() > landmark_max_hits_)
@@ -506,28 +545,41 @@ bool PublisherGraph::fillFactorMarker(FactorBaseConstPtr fac,
     geometry_msgs::Point point1, point2;
 
     // point1 -> frame ------------------------------------------------------
-    if (not fac->getCapture() or
-        not fac->getCapture()->getFrame() or
-        not fac->getCapture()->getFrame()->getP())
-        return false;
-
-    point1.x = fac->getCapture()->getFrame()->getP()->getState()(0);
-    point1.y = fac->getCapture()->getFrame()->getP()->getState()(1);
+    auto capture = fac->getCapture();
+    if (not capture or capture->isRemoving()) return false;
+    auto frame = capture->getFrame();
+    if (not frame or frame->isRemoving()) return false;
+    auto position = frame->getP();
+    if (not position) return false;
+
+    point1.x = position->getState()(0);
+    point1.y = position->getState()(1);
     if (fac->getProblem()->getDim() == 3)
-        point1.z = fac->getCapture()->getFrame()->getP()->getState()(2);
+        point1.z = position->getState()(2);
     else
         point1.z = 0;
 
+
+
     // point2 -> other ------------------------------------------------------
+    auto frame_other    = fac->getFrameOther();
+    auto capture_other  = fac->getCaptureOther();
+    auto feature_other  = fac->getFeatureOther();
+    auto landmark_other = fac->getLandmarkOther();
+
     // FRAME
-    if (fac->getFrameOther() != nullptr)
+    if (frame_other != nullptr)
     {
+        if (frame_other->isRemoving()) return false;
+        auto position_other = frame_other->getP();
+        if (not position_other) return false;
+
         // special case: Motion from ProcessorMotion
         auto proc_motion = std::dynamic_pointer_cast<const ProcessorMotion>(fac->getProcessor());
-        if (proc_motion and fac->getCaptureOther())
+        if (proc_motion and capture_other and not capture_other->isRemoving())
         {
             // Get state of other
-            auto x_other = fac->getFrameOther()->getState(proc_motion->getStateStructure());
+            auto x_other = frame_other->getState(proc_motion->getStateStructure());
 
             // Get most recent motion
             auto cap_own = std::static_pointer_cast<const CaptureMotion>(fac->getFeature()->getCapture());
@@ -543,7 +595,7 @@ bool PublisherGraph::fillFactorMarker(FactorBaseConstPtr fac,
             if ( proc_motion->hasCalibration())
             {
                 // Get current calibration -- from other capture
-                auto calib = proc_motion->getCalibration(fac->getCaptureOther());
+                auto calib = proc_motion->getCalibration(capture_other);
 
                 // get Jacobian of delta wrt calibration
                 const auto& J_delta_calib = motion.jacobian_calib_;
@@ -555,20 +607,20 @@ bool PublisherGraph::fillFactorMarker(FactorBaseConstPtr fac,
                 auto delta_corrected = proc_motion->correctDelta(delta_preint, delta_step);
 
                 // compute current state // this is [+]
-                proc_motion->statePlusDelta(x_other, delta_corrected, cap_own->getTimeStamp() - fac->getCaptureOther()->getTimeStamp(), state_integrated);
+                proc_motion->statePlusDelta(x_other, delta_corrected, cap_own->getTimeStamp() - capture_other->getTimeStamp(), state_integrated);
 
             }
             else
             {
-                proc_motion->statePlusDelta(x_other, delta_preint, cap_own->getTimeStamp() - fac->getCaptureOther()->getTimeStamp(), state_integrated);
+                proc_motion->statePlusDelta(x_other, delta_preint, cap_own->getTimeStamp() - capture_other->getTimeStamp(), state_integrated);
             }
 
             // FILL POINTS
             // 1=origin (other)
-            point1.x = fac->getFrameOther()->getP()->getState()(0);
-            point1.y = fac->getFrameOther()->getP()->getState()(1);
+            point1.x = position_other->getState()(0);
+            point1.y = position_other->getState()(1);
             if (fac->getProblem()->getDim() == 3)
-                point1.z = fac->getFrameOther()->getP()->getState()(2);
+                point1.z = position_other->getState()(2);
             else
                 point1.z = 0;
             // 2=own
@@ -582,59 +634,53 @@ bool PublisherGraph::fillFactorMarker(FactorBaseConstPtr fac,
         // Normal frame-frame factor
         else
         {
-            if (fac->getFrameOther()->isRemoving() or
-                not fac->getFrameOther() or
-                not fac->getFrameOther()->getP())
-                return false;
-
-            point2.x = fac->getFrameOther()->getP()->getState()(0);
-            point2.y = fac->getFrameOther()->getP()->getState()(1);
+            point2.x = position_other->getState()(0);
+            point2.y = position_other->getState()(1);
             if (fac->getProblem()->getDim() == 3)
-                point2.z = fac->getFrameOther()->getP()->getState()(2);
+                point2.z = position_other->getState()(2);
             else
                 point2.z = 0;
         }
     }
     // CAPTURE
-    else if (fac->getCaptureOther() != nullptr)
+    else if (capture_other != nullptr and not capture_other->isRemoving())
     {
-        if (fac->getCaptureOther()->isRemoving() or
-            not fac->getCaptureOther()->getFrame() or
-            not fac->getCaptureOther()->getFrame()->getP())
-            return false;
+        auto frame_capture_other = capture_other->getFrame();
+        if (not frame_capture_other or frame_capture_other->isRemoving()) return false;
+        auto position = frame_capture_other->getP();
+        if (not position) return false;
 
-        point2.x = fac->getCaptureOther()->getFrame()->getP()->getState()(0);
-        point2.y = fac->getCaptureOther()->getFrame()->getP()->getState()(1);
+        point2.x = position->getState()(0);
+        point2.y = position->getState()(1);
         if (fac->getProblem()->getDim() == 3)
-            point2.z = fac->getCaptureOther()->getFrame()->getP()->getState()(2);
+            point2.z = position->getState()(2);
         else
             point2.z = 0;
     }
     // FEATURE
-    else if (fac->getFeatureOther() != nullptr)
+    else if (feature_other != nullptr and not feature_other->isRemoving())
     {
-        if (fac->getFeatureOther()->isRemoving() or
-            not fac->getFeatureOther()->getCapture() or
-            not fac->getFeatureOther()->getCapture()->getFrame() or
-            not fac->getFeatureOther()->getCapture()->getFrame()->getP())
-            return false;
-
-        point2.x = fac->getFeatureOther()->getCapture()->getFrame()->getP()->getState()(0);
-        point2.y = fac->getFeatureOther()->getCapture()->getFrame()->getP()->getState()(1);
+        auto capture_feature_other = feature_other->getCapture();
+        if (not capture_feature_other or capture_feature_other->isRemoving()) return false;
+        auto frame_feature_other = capture_feature_other->getFrame();
+        if (not frame_feature_other or frame_feature_other->isRemoving()) return false;
+        auto position_other = frame_feature_other->getP();
+        if (not position_other) return false;
+
+        point2.x = position_other->getState()(0);
+        point2.y = position_other->getState()(1);
         if (fac->getProblem()->getDim() == 3)
-            point2.z = fac->getFeatureOther()->getCapture()->getFrame()->getP()->getState()(2);
+            point2.z = position_other->getState()(2);
         else
             point2.z = 0;
     }
     // LANDMARK
-    else if (fac->getLandmarkOther() != nullptr)
+    else if (landmark_other != nullptr and not landmark_other->isRemoving())
     {
-        if (fac->getLandmarkOther()->isRemoving() or
-            not fac->getLandmarkOther()->getP())
-            return false;
+        auto position_other = landmark_other->getP();
+        if (not position_other) return false;
 
-        Eigen::VectorXd lmk_pos;
-        lmk_pos  = fac->getLandmarkOther()->getP()->getState();
+        Eigen::VectorXd lmk_pos  = position_other->getState();
         point2.x = lmk_pos(0);
         point2.y = lmk_pos(1);
         if (fac->getProblem()->getDim() == 3)
@@ -659,6 +705,7 @@ bool PublisherGraph::fillFactorMarker(FactorBaseConstPtr fac,
     }
 
     // store points ------------------------------------------------------
+    fac_marker.points.clear();
     fac_marker.points.push_back(point1);
     fac_marker.points.push_back(point2);
 
@@ -687,16 +734,17 @@ bool PublisherGraph::fillFactorMarker(FactorBaseConstPtr fac,
     if (fac->getStatus() == FAC_INACTIVE)
         color.a *= 0.5;
 
+    fac_marker.colors.clear();
     fac_marker.colors.push_back(color);
     fac_marker.colors.push_back(color);// 2 times because of 2 points
-    fac_marker.ns = std::string("factors_"+(fac->getProcessor() ? fac->getProcessor()->getName() : "unnamed_processor"));
+    fac_marker.ns = std::string("factors_"+ fac->getType());
 
     // TEXT MARKER --------------------------------------------------------
     fac_text_marker.text = std::to_string(fac->id());
     fac_text_marker.pose.position.x = (point1.x + point2.x)/(double) 2;
     fac_text_marker.pose.position.y = (point1.y + point2.y)/(double) 2;
     fac_text_marker.pose.position.z = (point1.z + point2.z)/(double) 2;
-    fac_text_marker.ns = std::string("factors_text_"+(fac->getProcessor() ? fac->getProcessor()->getName() : "unnamed_processor"));
+    fac_text_marker.ns = std::string("factors_text_"+ fac->getType());
 
     return true;
 }
@@ -795,7 +843,7 @@ std::string PublisherGraph::factorString(FactorBaseConstPtr fac) const
     // ABSOLUTE (nothing
 
     // Topology
-    factor_string += "_T" + fac->getTopology();
+    factor_string += "_T-" + fac->getTopologyString();
 
     // Processor
     factor_string += "_P" + (fac->getProcessor() ? std::to_string(fac->getProcessor()->id()) : "NO");
diff --git a/src/publisher_pose.cpp b/src/publisher_pose.cpp
index abcecb335300a170dfc443f26511f4b30f6dfbd1..65222db813e14cb72d63cd723209d094ea996497 100644
--- a/src/publisher_pose.cpp
+++ b/src/publisher_pose.cpp
@@ -19,14 +19,16 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
 #include "publisher_pose.h"
 
 /**************************
  *      ROS includes      *
  **************************/
 #include <ros/ros.h>
-#include "tf/transform_datatypes.h"
-#include "tf_conversions/tf_eigen.h"
+#include <tf/transform_datatypes.h>
+#include <tf_conversions/tf_eigen.h>
+
 
 namespace wolf
 {
diff --git a/src/publisher_state_block.cpp b/src/publisher_state_block.cpp
index 8300340954bef448333c990eb8ab2462ad4016bc..f2a698d122f63aebe3722904343487791e33490b 100644
--- a/src/publisher_state_block.cpp
+++ b/src/publisher_state_block.cpp
@@ -19,6 +19,7 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
 #include "publisher_state_block.h"
 
 namespace wolf
diff --git a/src/publisher_tf.cpp b/src/publisher_tf.cpp
index 2591644b7278b9a0c1dbf2e174c2834afd03a061..448d9bb6fc7a159e1c5c895f2825cc4370fc8a6a 100644
--- a/src/publisher_tf.cpp
+++ b/src/publisher_tf.cpp
@@ -19,14 +19,15 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
 #include "publisher_tf.h"
 
 /**************************
  *      ROS includes      *
  **************************/
 #include <ros/ros.h>
-#include "tf/transform_datatypes.h"
-#include "tf_conversions/tf_eigen.h"
+#include <tf/transform_datatypes.h>
+#include <tf_conversions/tf_eigen.h>
 
 namespace wolf
 {
diff --git a/src/subscriber_diffdrive.cpp b/src/subscriber_diffdrive.cpp
index da7a29504cadb1209c504a26892f1d443681d462..815e5f84942324e0760dbf85d1f2c90c7e0ad6bb 100644
--- a/src/subscriber_diffdrive.cpp
+++ b/src/subscriber_diffdrive.cpp
@@ -19,14 +19,16 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
+#include "subscriber_diffdrive.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
 #include <core/capture/capture_diff_drive.h>
 #include <core/sensor/sensor_diff_drive.h>
+#include <core/math/rotations.h>
 
-#include "core/math/rotations.h"
-#include "subscriber_diffdrive.h"
 
 namespace wolf
 {
@@ -34,8 +36,6 @@ SubscriberDiffdrive::SubscriberDiffdrive(const std::string& _unique_name,
                                          const ParamsServer& _server,
                                          const SensorBasePtr _sensor_ptr)
 : Subscriber(_unique_name, _server, _sensor_ptr)
-, last_odom_stamp_(ros::Time(0))
-, last_odom_seq_(-1)
 {
     last_angles_ = Eigen::Vector2d();
     ticks_cov_factor_ = std::static_pointer_cast<SensorDiffDrive>(_sensor_ptr)->getParams()->ticks_cov_factor;
@@ -60,7 +60,7 @@ void SubscriberDiffdrive::callback(const sensor_msgs::JointState::ConstPtr& msg)
 
     Eigen::MatrixXd cov        = ticks_cov_factor_ * (angles_inc.cwiseAbs() + Eigen::Vector2d::Ones()).asDiagonal();  // TODO check this
 
-    if (last_odom_seq_ != -1)
+    if (last_seq_ != -1)
     {
         CaptureDiffDrivePtr cptr = std::make_shared<CaptureDiffDrive>(
                 TimeStamp(msg->header.stamp.sec, msg->header.stamp.nsec), sensor_ptr_, angles_inc, cov, nullptr);
@@ -80,12 +80,9 @@ void SubscriberDiffdrive::callback(const sensor_msgs::JointState::ConstPtr& msg)
 
             last_kf = current_kf;
         }
-
     }
 
     last_angles_     = msg_angles;
-    last_odom_stamp_ = msg->header.stamp;
-    last_odom_seq_   = msg->header.seq;
 }
 
 WOLF_REGISTER_SUBSCRIBER(SubscriberDiffdrive)
diff --git a/src/subscriber_landmarks.cpp b/src/subscriber_landmarks.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..503b9f1cd735c29a8394f4400642f38407418f80
--- /dev/null
+++ b/src/subscriber_landmarks.cpp
@@ -0,0 +1,129 @@
+//--------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 "subscriber_landmarks.h"
+
+/**************************
+ *      WOLF includes     *
+ **************************/
+#include <core/capture/capture_landmarks_external.h>
+#include <core/math/covariance.h>
+#include <core/math/rotations.h>
+#include <core/math/SE3.h>
+
+/**************************
+ *      ROS includes      *
+ **************************/
+// #include <tf/transform_datatypes.h>
+
+using namespace Eigen;
+
+namespace wolf
+{
+SubscriberLandmarks::SubscriberLandmarks(const std::string& _unique_name,
+                                         const ParamsServer& _server,
+                                         const SensorBasePtr _sensor_ptr)
+  : Subscriber(_unique_name, _server, _sensor_ptr)
+{
+    assert(_sensor_ptr);
+    dim = _sensor_ptr->getProblem()->getDim();
+    inverse_detections_ = _server.getParam<bool>(prefix_ + "/inverse_detections");
+    auto sensor_extrinsics = _server.getParam<VectorXd>(prefix_ + "/sensor_extrinsics");
+    sensor_p_ = sensor_extrinsics.head<3>();
+    sensor_q_ = Quaterniond(Vector4d(sensor_extrinsics.tail<4>()));
+}
+
+void SubscriberLandmarks::initialize(ros::NodeHandle& nh, const std::string& topic)
+{
+    sub_ = nh.subscribe(topic, 100, &SubscriberLandmarks::callback, this);
+}
+
+void SubscriberLandmarks::callback(const wolf_ros_node::LandmarkDetectionArray::ConstPtr& msg)
+{
+    ROS_INFO("SubscriberLandmarks::callback: %lu detections", msg->detections.size());
+
+    updateLastHeader(msg->header);
+
+    auto cap = std::make_shared<CaptureLandmarksExternal>(TimeStamp(msg->header.stamp.sec, msg->header.stamp.nsec), sensor_ptr_);
+    // Extract detections from msg
+    for (auto i = 0; i < msg->detections.size(); i++)
+    {
+        // 3D measurement from msg
+        VectorXd meas(7);
+        meas << msg->detections.at(i).pose.pose.position.x,
+                msg->detections.at(i).pose.pose.position.y,
+                msg->detections.at(i).pose.pose.position.z,
+                msg->detections.at(i).pose.pose.orientation.x,
+                msg->detections.at(i).pose.pose.orientation.y,
+                msg->detections.at(i).pose.pose.orientation.z,
+                msg->detections.at(i).pose.pose.orientation.w;
+        
+        meas.head<3>() = sensor_p_ + sensor_q_ * meas.head<3>();
+        meas.tail<4>() = (sensor_q_ * Quaterniond(Vector4d(meas.tail<4>()))).coeffs();
+
+        // PoseWithCovariance documentation:
+        //   Row-major representation of the 6x6 covariance matrix.
+        //   The orientation parameters use a fixed-axis representation.
+        //   In order, the parameters are: (x, y, z, rotation about X axis, rotation about Y axis, rotation about Z axis)
+        MatrixXd cov = Eigen::Map<const Eigen::Matrix6d>(msg->detections.at(i).pose.covariance.data());
+        auto R = q2R(meas.tail<4>());
+        cov.topLeftCorner<3,3>() = R * cov.topLeftCorner<3,3>() * R.transpose();
+        //TODO: rotate covariance for orientation
+
+        // inverse measurement
+        if (inverse_detections_)
+        {
+            meas = SE3::inverse(meas);
+            cov.topLeftCorner<3,3>() = R.transpose() * cov.topLeftCorner<3,3>() * R;
+            //TODO: rotate covariance for orientation
+        }
+
+        // 2D
+        if (dim == 2)
+        {
+            VectorXd meas2d = meas.head<3>();
+            meas2d(2) = getYaw(q2R(meas.tail<4>()));
+
+            MatrixXd cov2d(3,3);
+            cov2d << cov(0,0), cov(0,1), cov(0,5),
+                     cov(1,0), cov(1,1), cov(1,5),
+                     cov(5,0), cov(5,1), cov(5,5);
+
+            meas = meas2d;
+            cov = cov2d;
+        }
+        
+        std::cout << "\tid " << msg->detections.at(i).id << ": quality: " << msg->detections.at(i).quality << ", meas: " << meas.transpose() << std::endl;
+
+        // fill capture
+        makePosDef(cov);
+        cap->addDetection(msg->detections.at(i).id, meas, cov, msg->detections.at(i).quality);
+    }
+
+    // process
+    sensor_ptr_->process(cap);
+
+    ROS_DEBUG("SubscriberLandmarks::callback: end");
+}
+
+WOLF_REGISTER_SUBSCRIBER(SubscriberLandmarks)
+}  // namespace wolf
diff --git a/src/subscriber_odom2d.cpp b/src/subscriber_odom2d.cpp
index f46d0a5e2eb8623bd16845d86f3dca1fb90f051f..699f10d361a3b90923fe8cbcc52f9293978ec74a 100644
--- a/src/subscriber_odom2d.cpp
+++ b/src/subscriber_odom2d.cpp
@@ -19,6 +19,10 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //--------LICENSE_END--------
+
+#include "subscriber.h"
+#include "subscriber_odom2d.h"
+
 /**************************
  *      WOLF includes     *
  **************************/
@@ -39,9 +43,6 @@
 #include <iomanip>
 #include <queue>
 
-#include "subscriber.h"
-#include "subscriber_odom2d.h"
-
 namespace wolf
 {
 SubscriberOdom2d::SubscriberOdom2d(const std::string& _unique_name,
diff --git a/src/subscriber_pose.cpp b/src/subscriber_pose.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70fddeac734105fecf453db7884dad49717e55e8
--- /dev/null
+++ b/src/subscriber_pose.cpp
@@ -0,0 +1,79 @@
+//--------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 "subscriber_pose.h"
+
+/**************************
+ *      WOLF includes     *
+ **************************/
+
+/**************************
+ *      ROS includes      *
+ **************************/
+
+/**************************
+ *      STD includes      *
+ **************************/
+#include <iostream>
+#include <iomanip>
+#include <queue>
+
+
+namespace wolf
+{
+SubscriberPose::SubscriberPose(const std::string& _unique_name,
+                                   const ParamsServer& _server,
+                                   const SensorBasePtr _sensor_ptr)
+  : Subscriber(_unique_name, _server, _sensor_ptr)
+  , last_pose_stamp_(ros::Time(0))
+  , sensor_pose_(std::static_pointer_cast<SensorPose>(_sensor_ptr))
+{
+    assert(std::dynamic_pointer_cast<SensorPose>(_sensor_ptr) != nullptr && "SubscriberPose: sensor provided is not of type SensorPose!");
+}
+
+void SubscriberPose::initialize(ros::NodeHandle& nh, const std::string& topic)
+{
+    sub_ = nh.subscribe(topic, 100, &SubscriberPose::callback, this);
+}
+
+void SubscriberPose::callback(const geometry_msgs::PoseStamped::ConstPtr& msg)
+{
+    updateLastHeader(msg->header);
+
+    if (last_pose_stamp_ != ros::Time(0))
+    {
+        double           dt          = (msg->header.stamp - last_pose_stamp_).toSec();
+        Eigen::Vector7d data;
+        data << msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, msg->pose.orientation.x, msg->pose.orientation.y, msg->pose.orientation.z, msg->pose.orientation.w;
+        CapturePosePtr new_capture = std::make_shared<CapturePose>(
+            TimeStamp(msg->header.stamp.sec, msg->header.stamp.nsec),
+            sensor_ptr_,
+            data,
+            sensor_pose_->getNoiseCov());
+        sensor_ptr_->process(new_capture);
+    }
+    last_pose_stamp_ = msg->header.stamp;
+
+}
+
+WOLF_REGISTER_SUBSCRIBER(SubscriberPose)
+}  // namespace wolf