diff --git a/include/core/landmark/landmark_base.h b/include/core/landmark/landmark_base.h
index b780457e2d028db1f93ea31999337caf2b51cc0f..e1fea8f6731696817cd3d4c7fad0a4227011c66b 100644
--- a/include/core/landmark/landmark_base.h
+++ b/include/core/landmark/landmark_base.h
@@ -58,6 +58,7 @@ class LandmarkBase : public NodeBase, public HasStateBlocks, public std::enable_
         // Navigate wolf tree
         void setProblem(ProblemPtr _problem) override final;
         unsigned int landmark_id_; ///< landmark unique id
+	    unsigned int track_id_; ///< associated track id
         TimeStamp stamp_;       ///< stamp of the creation of the landmark
         Eigen::VectorXd descriptor_;    //TODO: agree? JS: No: It is not general enough as descriptor to be in LmkBase.
 
@@ -80,6 +81,9 @@ class LandmarkBase : public NodeBase, public HasStateBlocks, public std::enable_
         unsigned int id() const;
         void setId(unsigned int _id);
 
+	    unsigned int trackId(); // get track ID
+	    void setTrackId(unsigned int _track_id); // set track ID
+
         // State blocks
         //std::vector<StateBlockConstPtr> getUsedStateBlockVec() const;
         //std::vector<StateBlockPtr> getUsedStateBlockVec();
@@ -176,6 +180,18 @@ inline void LandmarkBase::setId(unsigned int _id)
         landmark_id_count_ = _id;
 }
 
+inline unsigned int LandmarkBase::trackId()
+{
+    return track_id_;
+}
+
+inline void LandmarkBase::setTrackId(unsigned int _track_id) 
+{
+    track_id_ = _track_id;
+}
+
+
+
 inline unsigned int LandmarkBase::getHits() const
 {
     return constrained_by_list_.size();
diff --git a/include/core/processor/processor_tracker.h b/include/core/processor/processor_tracker.h
index fd1c12350c74419c100b4682e9cdbd9a56370867..8d604258f73f887702cab7d443f7f1072f272c78 100644
--- a/include/core/processor/processor_tracker.h
+++ b/include/core/processor/processor_tracker.h
@@ -142,6 +142,8 @@ class ProcessorTracker : public ProcessorBase
         virtual CaptureBasePtr getOrigin();
         virtual CaptureBaseConstPtr getLast() const;
         virtual CaptureBasePtr getLast();
+        virtual FrameBaseConstPtr getLastFrame() const;
+        virtual FrameBasePtr getLastFrame();
         virtual CaptureBaseConstPtr getIncoming() const;
         virtual CaptureBasePtr getIncoming();
 
@@ -343,6 +345,16 @@ inline CaptureBasePtr ProcessorTracker::getLast()
     return last_ptr_;
 }
 
+inline FrameBaseConstPtr ProcessorTracker::getLastFrame() const
+{
+    return last_frame_ptr_;
+}
+
+inline FrameBasePtr ProcessorTracker::getLastFrame()
+{
+    return last_frame_ptr_;
+}
+
 inline CaptureBaseConstPtr ProcessorTracker::getIncoming() const
 {
     return incoming_ptr_;
diff --git a/src/processor/processor_tracker.cpp b/src/processor/processor_tracker.cpp
index 25a62410b1871dbd3d8d69c6481b48a0c0ae0b02..3fe8d24aa87ca7f63cd74fcf3ef0c037c5aeb44e 100644
--- a/src/processor/processor_tracker.cpp
+++ b/src/processor/processor_tracker.cpp
@@ -82,10 +82,25 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
 
             WOLF_DEBUG( "PT ", getName(), " FIRST_TIME_WITH_KEYFRAME: KF" , keyframe_from_callback->id() , " callback unpacked with ts= " , keyframe_from_callback->getTimeStamp() );
 
-            // Append incoming to KF
-            incoming_ptr_->link(keyframe_from_callback);
+            // check if the callback keyframe has a capture of this sensor
+            auto capture_from_callback = keyframe_from_callback->getCaptureOf(this->getSensor());
+
+            if (incoming_ptr_ == capture_from_callback)
+            {
+                // If captures match, then frames must match too
+                assert(incoming_ptr_->getFrame() != nullptr 
+                        and incoming_ptr_->getFrame() == keyframe_from_callback 
+                        and "The keyframe has a Capture from this sensor, but this capture is not incoming!");
+                WOLF_DEBUG("PT ", getName(), " Incoming capture has been processed previously by another processor!")
+            }
+            else
+            {
+                WOLF_DEBUG("PT ", getName(), " Incoming capture had not been processed by any other processor!")
+                
+                // Join KF
+                incoming_ptr_->link(keyframe_from_callback);
+            }
 
-            // 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();
@@ -95,6 +110,7 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             // Update pointers
             origin_ptr_     = incoming_ptr_;
             last_ptr_       = incoming_ptr_;
+            last_frame_ptr_ = keyframe_from_callback;
             incoming_ptr_   = nullptr;
 
             break;
@@ -103,13 +119,18 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
         {
             WOLF_DEBUG( "PT ", getName(), " FIRST_TIME_WITHOUT_KEYFRAME" );
 
+            // Check if incoming has already a Frame
+            auto frame_incoming = incoming_ptr_->getFrame();
+            assert(frame_incoming == nullptr and " Incoming capture has been processed and linked by another processor, but no keyframe callback was received!");
+
+            WOLF_DEBUG("PT ", getName(), " Incoming capture has not been processed by another processor!")
+
             // make a new KF at incoming
             FrameBasePtr keyframe = FrameBase::emplace<FrameBase>(getProblem()->getTrajectory(),
-                                                                  incoming_ptr_->getTimeStamp(),
-                                                                  getProblem()->getFrameStructure(),
-                                                                  getProblem()->getState());
-
-            // link incoming to the new KF
+                                                                incoming_ptr_->getTimeStamp(),
+                                                                getProblem()->getFrameStructure(),
+                                                                getProblem()->getState(incoming_ptr_->getTimeStamp()));
+            // Append incoming to KF
             incoming_ptr_->link(keyframe);
 
             // Process info
@@ -126,6 +147,7 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             // Update pointers
             origin_ptr_ = incoming_ptr_;
             last_ptr_   = incoming_ptr_;
+            last_frame_ptr_ = keyframe;
             incoming_ptr_ = nullptr;
 
             break;
@@ -154,13 +176,10 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
             // Make a NON KEY Frame to hold incoming capture
             FrameBasePtr frame = std::make_shared<FrameBase>(incoming_ptr_->getTimeStamp(),
                                                                 getProblem()->getFrameStructure(),
-                                                                getProblem()->getState());
-            incoming_ptr_->link(frame);
+                                                                getProblem()->getState(incoming_ptr_->getTimeStamp()));
 
-            // Process info
-            // If we have known landmarks or features. Process them.
-            if (not getProblem()->getMap()->getLandmarkList().empty() or not known_features_last_.empty())
-                processKnown();
+            // Process known information
+            processKnown();
             
             // Both Trackers:  We have a last_ Capture with not enough features, so populate it.
             processNew(params_tracker_->max_new_features);
@@ -188,16 +207,29 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
 
             processKnown();
 
-            // Capture last_ is added to the new keyframe
-            FrameBasePtr last_old_frame = last_ptr_->getFrame();
-            last_ptr_->move(keyframe_from_callback);
-            last_old_frame->remove();
+            // chack if the received KF has a capture of this sensor, and if it matches with last_ptr
+            if (last_ptr_ == keyframe_from_callback->getCaptureOf(this->getSensor()))
+            {
+                WOLF_DEBUG("PT ", getName(), " Last capture has been processed previously by another processor!")
+
+                // If captures match, then frames must match too
+                assert( last_ptr_->getFrame() == keyframe_from_callback 
+                        and "The keyframe has a Capture from this sensor, but this capture is not last!");
+
+                // No need to join KF since it is the same capture, and it was already joined by the other processor
+            }
+            else
+            {
+                WOLF_DEBUG("PT ", getName(), " Last capture had not been processed previously!")
+                
+                // join KF
+                last_ptr_->link(keyframe_from_callback);
+            }
 
             // Create new NON KEY frame for incoming
             FrameBasePtr frame = std::make_shared<FrameBase>(incoming_ptr_->getTimeStamp(),
                                                            getProblem()->getFrameStructure(),
                                                            getProblem()->getState());
-            incoming_ptr_->link(frame);
 
             // Detect new Features, initialize Landmarks, ...
             processNew(params_tracker_->max_new_features);
@@ -228,21 +260,21 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
 
                 // We create a KF
                 // set KF on last
-                last_ptr_->getFrame()->setState(getProblem()->getState(last_ptr_->getTimeStamp()));
-                last_ptr_->getFrame()->link(getProblem());
+                last_frame_ptr_->setState(getProblem()->getState(last_ptr_->getTimeStamp()));
+                last_ptr_->link(last_frame_ptr_);
+                last_frame_ptr_->link(getProblem());
 
                 // Establish factors
                 establishFactors();
 
-                // Call the new keyframe callback in order to let the other processors to establish their factors
-                getProblem()->keyFrameCallback(last_ptr_->getFrame(), shared_from_this());
+                // Call the new keyframe callback in order to let the other processors to join
+                getProblem()->keyFrameCallback(last_frame_ptr_, shared_from_this());
 
 
                 // make NON KEY frame; append incoming to new frame
                 FrameBasePtr frame = std::make_shared<FrameBase>(incoming_ptr_->getTimeStamp(),
                                                                  getProblem()->getFrameStructure(),
                                                                  getProblem()->getState(incoming_ptr_->getTimeStamp()));
-                incoming_ptr_   ->link(frame);
 
                 // Reset this
                 resetDerived();
@@ -261,8 +293,6 @@ void ProcessorTracker::processCapture(CaptureBasePtr _incoming_ptr)
                 FrameBasePtr frame = std::make_shared<FrameBase>(incoming_ptr_->getTimeStamp(),
                                                                  getProblem()->getFrameStructure(),
                                                                  getProblem()->getState(incoming_ptr_->getTimeStamp()));
-                incoming_ptr_->link(frame);
-                last_ptr_->unlink(); // unlink last (destroying the frame) instead of frame destruction that would implicitly destroy last
 
                 // Advance this
                 advanceDerived();
@@ -315,7 +345,7 @@ void ProcessorTracker::computeProcessingStep()
 
             if (buffer_frame_.select(last_ptr_->getTimeStamp(), params_tracker_->time_tolerance))
             {
-                if (last_ptr_->getFrame()->getProblem())
+                if (last_frame_ptr_->getProblem())
                 {
                     WOLF_WARN("||*||");
                     WOLF_INFO(" ... It seems you missed something!");
@@ -341,7 +371,7 @@ void ProcessorTracker::printHeader(int _depth, bool _constr_by, bool _metric, bo
                 << getOrigin()->getFrame()->id() << std::endl;
     if (getLast())
         _stream << _tabs << "  " << "l: Cap" << getLast()->id() << " - " << " Frm"
-                << getLast()->getFrame()->id() << std::endl;
+                << getLastFrame()->id() << std::endl;
     if (getIncoming())
         _stream << _tabs << "  " << "i: Cap" << getIncoming()->id() << std::endl;
 }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 299594805cb46a551145613baf30481f4e7aed22..1709df19170f9110e8e2d180c7d27597a6becc64 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -225,6 +225,10 @@ wolf_add_gtest(gtest_processor_and_factor_pose_3d_with_extrinsics gtest_processo
 wolf_add_gtest(gtest_processor_tracker_feature_dummy gtest_processor_tracker_feature_dummy.cpp)
 target_link_libraries(gtest_processor_tracker_feature_dummy PUBLIC dummy)
 
+# ProcessorTrackerFeatureDummy 2 processors in one sensor test
+wolf_add_gtest(gtest_processor_tracker_two_processors_one_sensor gtest_processor_tracker_two_processors_one_sensor.cpp)
+target_link_libraries(gtest_processor_tracker_two_processors_one_sensor PUBLIC dummy)
+
 # ProcessorTrackerLandmarkDummy class test
 wolf_add_gtest(gtest_processor_tracker_landmark_dummy gtest_processor_tracker_landmark_dummy.cpp)
 target_link_libraries(gtest_processor_tracker_landmark_dummy PUBLIC dummy)
diff --git a/test/gtest_processor_landmark_external.cpp b/test/gtest_processor_landmark_external.cpp
index c7ac5c831f2c03d3588f224946f1ec662a72ab18..f97df272146e529317e64ee2458a973d18c67b4d 100644
--- a/test/gtest_processor_landmark_external.cpp
+++ b/test/gtest_processor_landmark_external.cpp
@@ -296,17 +296,17 @@ void ProcessorLandmarkExternalTest::testConfiguration(int dim,
             ASSERT_FALSE(fac_list.empty());
             
             // factors' type
-            if (should_emplace_KF)
-                for (auto fac : fac_list)
+            for (auto fac : fac_list)
+            {
+                if (fac->getProcessor() == processor)
                 {
-                    if (fac->getProcessor() == processor)
-                    {
-                        ASSERT_EQ(fac->getType(), std::string("FactorRelative") + 
-                                                  (orientation ? "Pose" : "Position") + 
-                                                  (dim == 2 ? "2d" : "3d") + 
-                                                  "WithExtrinsics");
-                    }
+                    ASSERT_EQ(fac->getType(), std::string("FactorRelative") + 
+                                                (orientation ? "Pose" : "Position") + 
+                                                (dim == 2 ? "2d" : "3d") + 
+                                                "WithExtrinsics");
                 }
+            }
+
             // landmarks created by processor
             if (not add_landmarks)
             {
diff --git a/test/gtest_processor_tracker_two_processors_one_sensor.cpp b/test/gtest_processor_tracker_two_processors_one_sensor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2c7b69081cf3a500a2282308ff8e762d237e753
--- /dev/null
+++ b/test/gtest_processor_tracker_two_processors_one_sensor.cpp
@@ -0,0 +1,237 @@
+//--------LICENSE_START--------
+//
+// Copyright (C) 2020,2021,2022,2023 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--------
+// wolf includes
+#include "core/utils/utils_gtest.h"
+#include "core/sensor/factory_sensor.h"
+#include "dummy/processor_tracker_feature_dummy.h"
+#include "core/capture/capture_void.h"
+
+using namespace wolf;
+
+std::string wolf_root = _WOLF_ROOT_DIR;
+
+WOLF_PTR_TYPEDEFS(ProcessorTrackerFeatureDummyDummy);
+
+class ProcessorTrackerFeatureDummyDummy : public ProcessorTrackerFeatureDummy
+{
+  public:
+    ProcessorTrackerFeatureDummyDummy(ParamsProcessorTrackerFeatureDummyPtr& _params)
+        : ProcessorTrackerFeatureDummy(_params)
+    {
+    }
+
+    void setLast(CaptureBasePtr _last_ptr)
+    {
+        last_ptr_ = _last_ptr;
+    }
+    void setInc(CaptureBasePtr _incoming_ptr)
+    {
+        incoming_ptr_ = _incoming_ptr;
+    }
+
+    unsigned int callProcessKnown()
+    {
+        return this->processKnown();
+    }
+
+    unsigned int callProcessNew(const int& _max_new_features)
+    {
+        return this->processNew(_max_new_features);
+    }
+
+    unsigned int callDetectNewFeatures(const int&            _max_features,
+                                       const CaptureBasePtr& _capture,
+                                       FeatureBasePtrList&   _features_out)
+    {
+        return this->detectNewFeatures(_max_features, _capture, _features_out);
+    }
+
+    unsigned int callTrackFeatures(const FeatureBasePtrList& _features_in,
+                                   const CaptureBasePtr&     _capture,
+                                   FeatureBasePtrList&       _features_out,
+                                   FeatureMatchMap&          _feature_correspondences)
+    {
+        return this->trackFeatures(_features_in, _capture, _features_out, _feature_correspondences);
+    }
+
+    FactorBasePtr callEmplaceFactor(FeatureBasePtr _feature_ptr, FeatureBasePtr _feature_other_ptr)
+    {
+        return this->emplaceFactor(_feature_ptr, _feature_other_ptr);
+    }
+
+    void callEstablishFactors()
+    {
+        this->establishFactors();
+    }
+
+    TrackMatrix getTrackMatrix()
+    {
+        return track_matrix_;
+    }
+
+    FeatureMatchMap getMatchesLastFromIncoming()
+    {
+        return matches_last_from_incoming_;
+    }
+
+    void callReset()
+    {
+        this->resetDerived();
+        origin_ptr_   = last_ptr_;
+        last_ptr_     = incoming_ptr_;
+        incoming_ptr_ = nullptr;
+    };
+};
+
+// Use the following in case you want to initialize tests with predefines variables or methods.
+class ProcessorTrackerFeatureDummyDoubledTest : public testing::Test
+{
+  public:
+    ProblemPtr                            problem;
+    SensorBasePtr                         sensor;
+    ParamsProcessorTrackerFeatureDummyPtr params;
+    ProcessorTrackerFeatureDummyDummyPtr  processor_1, processor_2;
+
+    // ~ProcessorTrackerFeatureDummyDoubledTest() override{}
+
+    void SetUp() override
+    {
+        // Wolf problem
+        problem = Problem::create("PO", 2);
+
+        // Install camera
+        sensor = problem->installSensor("SensorOdom2d",
+                                        "dummy sensor",
+                                        (Eigen::Vector3d() << 0, 0, 0).finished(),
+                                        std::make_shared<ParamsSensorBase>());
+
+        // Install processor
+        params                            = std::make_shared<ParamsProcessorTrackerFeatureDummy>();
+        params->max_new_features          = 10;
+        params->min_features_for_keyframe = 7;
+        params->time_tolerance            = 0.5;
+        params->voting_active             = true;
+        params->n_tracks_lost             = 2;  // 1 (the first) track is lost each time trackFeatures is called
+        processor_1                       = std::make_shared<ProcessorTrackerFeatureDummyDummy>(params);
+        processor_1->setName("Processor 1");
+        processor_1->link(sensor);
+        processor_2 = std::make_shared<ProcessorTrackerFeatureDummyDummy>(params);
+        processor_2->setName("Processor 2");
+        processor_2->link(sensor);
+
+        problem->print(4, 1, 1, 1);
+    }
+};
+
+// TEST_F(ProcessorTrackerFeatureDummyDoubledTest, installProcessor)
+// {
+//     ASSERT_EQ(processor_1->getProblem(), problem);
+//     ASSERT_EQ(processor_2->getProblem(), problem);
+//     ASSERT_TRUE(problem->check(0));
+// }
+
+// TEST_F(ProcessorTrackerFeatureDummyDoubledTest, process_one_prc)
+// {
+//     // Create a capture
+//     CaptureBasePtr capture = std::make_shared<CaptureVoid>(0, sensor);
+
+//     // Process by one processor
+//     processor_1->captureCallback(capture);
+
+//     problem->print(4,1,1,1);
+// }
+
+TEST_F(ProcessorTrackerFeatureDummyDoubledTest, process_once_one_prc_then_other)
+{
+    // Create a capture
+    CaptureBasePtr capture = std::make_shared<CaptureVoid>(0, sensor);
+
+    // Process by one processor
+    processor_1->captureCallback(capture);
+    auto frame_1 = capture->getFrame();
+
+    // Process by the other processor
+    processor_2->captureCallback(capture);
+    auto frame_2 = capture->getFrame();
+
+    problem->print(4, 1, 1, 1);
+
+    ASSERT_EQ(problem->getTrajectory()->getFrameMap().size(), 1);
+
+    ASSERT_EQ(frame_1, frame_2);
+}
+
+TEST_F(ProcessorTrackerFeatureDummyDoubledTest, process_once)
+{
+    // Process by both processors automatically in sequence
+    CaptureBasePtr capture = std::make_shared<CaptureVoid>(0, sensor);
+    capture->process();
+    problem->print(4, 1, 1, 1);
+
+    ASSERT_EQ(problem->getTrajectory()->getFrameMap().size(), 1);
+}
+
+TEST_F(ProcessorTrackerFeatureDummyDoubledTest, process_twice)
+{
+    // Process by both processors automatically in sequence
+
+    CaptureBasePtr capture = std::make_shared<CaptureVoid>(0, sensor);
+    capture->process();
+
+    capture = std::make_shared<CaptureVoid>(1, sensor);
+    capture->process();
+
+    problem->print(4, 1, 1, 1);
+}
+
+TEST_F(ProcessorTrackerFeatureDummyDoubledTest, process_thrice)
+{
+    // Create a capture
+    CaptureBasePtr capture = std::make_shared<CaptureVoid>(0, sensor);
+
+    // Process by both processors automatically in sequence
+    capture->process();
+
+    capture = std::make_shared<CaptureVoid>(1, sensor);
+    capture->process();
+
+    capture = std::make_shared<CaptureVoid>(2, sensor);
+    capture->process();
+
+    problem->print(2, 1, 1, 1);
+}
+
+TEST_F(ProcessorTrackerFeatureDummyDoubledTest, process_loop)
+{
+    for (TimeStamp t(0); t <= 6; t = t + 1)
+    {
+        auto capture = std::make_shared<CaptureVoid>(t, sensor);
+        capture->process();
+    }
+    problem->print(2, 1, 1, 1);
+}
+
+int main(int argc, char** argv)
+{
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}