diff --git a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt
index 7f25517703685f36cfa7cf992a27252f1bd54135..e6a42e3a28b9db59d4dc24bf4dd34e5ee2074450 100644
--- a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt
+++ b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt
@@ -22,6 +22,9 @@ armarx_add_library(
         aroncommon
     HEADERS
         
+        ./common/localization/types.h
+        ./common/localization/TransformHelper.h
+
         ./client/localization/interfaces.h
         ./client/localization/TransformReader.h
         ./client/localization/TransformWriter.h
@@ -39,6 +42,9 @@ armarx_add_library(
 
         ./aron_conversions.h
     SOURCES
+        ./common/localization/TransformHelper.cpp
+
+
         ./client/localization/TransformReader.cpp
         ./client/localization/TransformWriter.cpp
 
diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp
index 727c954dbce9679de1ae81343ce9bba4a5c6bb8b..1c9289135d6a654f8fbf24b1bd401b242d855e2d 100644
--- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp
+++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp
@@ -56,6 +56,8 @@
 #include <RobotAPI/libraries/armem_robot_state/aron_conversions.h>
 #include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h>
 
+#include <RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h>
+
 namespace armarx::armem::client::robot_state::localization
 {
 
@@ -128,254 +130,9 @@ namespace armarx::armem::client::robot_state::localization
 
     // }
 
-    std::vector<std::string> TransformReader::buildTransformChain(const armem::wm::Memory& memory,
-            const TransformQuery& query) const
-    {
-        ARMARX_DEBUG << "Building transform chain";
-
-        auto join = [](const std::string & parentFrame, const std::string & frame)
-        {
-            return parentFrame + "," + frame;
-        };
-
-        std::vector<std::string> chain;
-
-        const auto& agentProviderSegment = memory.getCoreSegment(properties.localizationSegment)
-                                           .getProviderSegment(query.header.agent);
-
-        auto addToChain = [&](const std::string & parentFrame, const std::string & frame)
-        {
-            const auto entityName = join(parentFrame, frame);
-
-            if (agentProviderSegment.hasEntity(entityName))
-            {
-                chain.push_back(entityName);
-            }
-            else
-            {
-                ARMARX_WARNING << "Cannot perform tf lookup '" << parentFrame << " -> " << frame
-                               << "'";
-            }
-        };
-
-        std::array<std::string, 3> knownChain
-        {
-            GlobalFrame, MapFrame, OdometryFrame}; // Robot comes next
-
-        auto* frameBeginIt =
-            std::find(knownChain.begin(), knownChain.end(), query.header.parentFrame);
-        auto* const frameEndIt =
-            std::find(knownChain.begin(), knownChain.end(), query.header.frame);
-
-        if (frameBeginIt == knownChain.end())
-        {
-            ARMARX_WARNING << "Parent frame '" << query.header.parentFrame << "' unknown";
-            return {};
-        }
-
-        if (frameEndIt == knownChain.end())
-        {
-            ARMARX_DEBUG << "Frame '" << query.header.frame << "' must be robot frame";
-        }
-
-        const size_t nFrames = std::distance(frameBeginIt, frameEndIt);
-        ARMARX_DEBUG << "Lookup '" << query.header.parentFrame << " -> " << query.header.frame
-                     << "' across " << nFrames << " frames";
-
-        for (; frameBeginIt != knownChain.end() - 1; frameBeginIt++)
-        {
-            addToChain(*frameBeginIt, *(frameBeginIt + 1));
-        }
-
-        if (frameEndIt == knownChain.end())
-        {
-            addToChain(knownChain.back(), query.header.frame);
-        }
-
-        if (chain.empty())
-        {
-            ARMARX_WARNING << "Cannot create tf lookup chain '" << query.header.parentFrame
-                           << " -> " << query.header.frame << "'";
-            return {};
-        }
-
-        return chain;
-    }
-
-    inline ::armarx::armem::robot_state::Transform convertEntityToTransform(const armem::wm::EntityInstance& item)
-    {
-        arondto::Transform aronTransform;
-        aronTransform.fromAron(item.data());
-
-        ::armarx::armem::robot_state::Transform transform;
-        fromAron(aronTransform, transform);
-
-        return transform;
-    }
-
-    auto findFirstElementAfter(const std::vector<::armarx::armem::robot_state::Transform>& transforms,
-                               const int64_t timestamp)
-    {
-        auto timestampBeyond = [timestamp](const ::armarx::armem::robot_state::Transform & transform)
-        {
-            return transform.header.timestamp > timestamp;
-        };
-
-        const auto poseNextIt = std::find_if(transforms.begin(), transforms.end(), timestampBeyond);
-        return poseNextIt;
-    }
-
-    Eigen::Affine3f interpolateTransform(const std::vector<::armarx::armem::robot_state::Transform>& queue, int64_t timestamp)
-    {
-        ARMARX_TRACE;
-
-        ARMARX_DEBUG << "Entering";
-
-
-        ARMARX_CHECK(not queue.empty())
-                << "The queue has to contain at least two items to perform a lookup";
-
-
-        ARMARX_DEBUG << "Entering ... "
-                     << "Q front " << queue.front().header.timestamp << "  "
-                     << "Q back " << queue.back().header.timestamp << "  "
-                     << "query timestamp " << timestamp;
-
-        // TODO(fabian.reister): sort queue.
-
-
-        ARMARX_CHECK(queue.back().header.timestamp > timestamp)
-                << "Cannot perform lookup into the future!";
-
-        // ARMARX_DEBUG << "Entering 1.5 " << queue.front().timestamp << "  " << timestamp;
-        ARMARX_CHECK(queue.front().header.timestamp < timestamp)
-                << "Cannot perform lookup. Timestamp too old";
-        // => now we know that there is an element right after and before the timestamp within our queue
-
-        ARMARX_DEBUG << "Entering 2";
-
-        const auto poseNextIt = findFirstElementAfter(queue, timestamp);
-
-        ARMARX_DEBUG << "it ari";
-
-        const auto posePreIt = poseNextIt - 1;
-
-        ARMARX_DEBUG << "deref";
-
-        // the time fraction [0..1] of the lookup wrt to posePre and poseNext
-        const float t = static_cast<float>(timestamp - posePreIt->header.timestamp) /
-                        (poseNextIt->header.timestamp - posePreIt->header.timestamp);
-
-        ARMARX_DEBUG << "interpolate";
-
-        return simox::math::interpolatePose(posePreIt->transform, poseNextIt->transform, t);
-    }
-
-    Eigen::Affine3f TransformReader::obtainTransform(const std::string& entityName, const armem::wm::ProviderSegment& agentProviderSegment, const int64_t timestamp) const
-    {
-
-        ARMARX_DEBUG << "getEntity:" + entityName;
-        const auto& entity = agentProviderSegment.getEntity(entityName);
-
-        ARMARX_DEBUG << "History (size: " << entity.history().size() << ")"
-                     << simox::alg::get_keys(entity.history());
-
-        // if (entity.history.empty())
-        // {
-        //     // TODO(fabian.reister): fixme boom
-        //     ARMARX_ERROR << "No snapshots received.";
-        //     return Eigen::Affine3f::Identity();
-        // }
-
-
-        std::vector<::armarx::armem::robot_state::Transform> transforms;
-        transforms.reserve(entity.history().size());
-
-        const auto entitySnapshots = simox::alg::get_values(entity.history());
-        std::transform(entitySnapshots.begin(),
-                       entitySnapshots.end(),
-                       std::back_inserter(transforms),
-                       [](const auto & entity)
-        {
-            return convertEntityToTransform(entity.getInstance(0));
-        });
-
-        ARMARX_DEBUG << "obtaining transform";
-
-        if (transforms.size() > 1)
-        {
-            // TODO(fabian.reister): remove
-            return transforms.front().transform;
-
-            ARMARX_DEBUG << "More than one snapshots received: " << transforms.size();
-            const auto p = interpolateTransform(transforms, timestamp);
-            ARMARX_DEBUG << "Done interpolating transform";
-            return p;
-        }
-
-
-        // accept this to fail (will raise armem::error::MissingEntry)
-        if (transforms.empty())
-        {
-            ARMARX_DEBUG << "empty transform";
-
-            throw armem::error::MissingEntry("foo", "bar", "foo2", "bar2");
-        }
-
-        ARMARX_DEBUG << "single transform";
-
-
-        return transforms.front().transform;
-    }
-
-    std::vector<Eigen::Affine3f>
-    TransformReader::obtainTransforms(const armem::wm::Memory& memory,
-                                      const std::vector<std::string>& tfChain,
-                                      const std::string& agent,
-                                      const std::int64_t& timestamp) const
-    {
-
-        ARMARX_DEBUG << "Core segments" << simox::alg::get_keys(memory.coreSegments());
-
-        const auto& agentProviderSegment =
-            memory.getCoreSegment(properties.localizationSegment).getProviderSegment(agent);
-
-        ARMARX_DEBUG << "Provider segments"
-                     << simox::alg::get_keys(
-                         memory.getCoreSegment(properties.localizationSegment)
-                         .providerSegments());
-
-        ARMARX_DEBUG << "Entities: " << simox::alg::get_keys(agentProviderSegment.entities());
-
-        try
-        {
-            std::vector<Eigen::Affine3f> transforms;
-            transforms.reserve(tfChain.size());
-            std::transform(tfChain.begin(),
-                           tfChain.end(),
-                           std::back_inserter(transforms),
-                           [&](const std::string & entityName)
-            {
-                return obtainTransform(entityName, agentProviderSegment, timestamp);
-            });
-            return transforms;
-        }
-        catch (const armem::error::MissingEntry& missingEntryError)
-        {
-            ARMARX_WARNING << missingEntryError.what();
-        }
-        catch (const ::armarx::exceptions::local::ExpressionException& ex)
-        {
-            ARMARX_WARNING << "local exception: " <<  ex.what();
-        }
-
-        return {};
-
-    }
 
     TransformResult TransformReader::lookupTransform(const TransformQuery& query) const
     {
-
         const auto timestamp = IceUtil::Time::microSeconds(query.header.timestamp);
 
         const auto durationEpsilon = IceUtil::Time::milliSeconds(100);
@@ -390,7 +147,7 @@ namespace armarx::armem::client::robot_state::localization
         .coreSegments().withName(properties.localizationSegment)
         .providerSegments().withName(query.header.agent) // agent
         .entities().all() // parentFrame,frame
-        .snapshots().timeRange(timestamp - durationEpsilon, timestamp + durationEpsilon);
+        .snapshots().latest(); //timeRange(timestamp - durationEpsilon, timestamp + durationEpsilon);
         // clang-format on
 
         // TODO(fabian.reister): remove latest() and add atTime
@@ -411,39 +168,10 @@ namespace armarx::armem::client::robot_state::localization
                 query.header.frame + "' : " + qResult.errorMessage};
         }
 
-        const std::vector<std::string> tfChain = buildTransformChain(qResult.memory, query);
-
-        if (tfChain.empty())
-        {
-            return {.transform    = {.header = query.header},
-                    .status       = TransformResult::Status::ErrorFrameNotAvailable,
-                    .errorMessage = "Cannot create tf lookup chain '" +
-                                    query.header.parentFrame + " -> " + query.header.frame +
-                                    "'"};
-        }
-
-        const std::vector<Eigen::Affine3f> transforms = obtainTransforms(
-                    qResult.memory, tfChain, query.header.agent, timestamp.toMicroSeconds());
-
-        if (transforms.empty())
-        {
-            ARMARX_WARNING << "No transform available.";
-            return {.transform    = {.header = query.header},
-                    .status       = TransformResult::Status::ErrorFrameNotAvailable,
-                    .errorMessage = "Error in TF loookup:  '" +
-                                    query.header.parentFrame + " -> " + query.header.frame +
-                                    "'. No memory data in time range."};
-        }
-
-        const Eigen::Affine3f transform = std::accumulate(transforms.begin(),
-                                          transforms.end(),
-                                          Eigen::Affine3f::Identity(),
-                                          std::multiplies<>());
-
-        ARMARX_DEBUG << "Found valid transform";
+        const auto& localizationCoreSegment = qResult.memory.getCoreSegment(properties.localizationSegment);
 
-        return {.transform = {.header = query.header, .transform = transform},
-                .status    = TransformResult::Status::Success};
+        using armarx::armem::common::robot_state::localization::TransformHelper;
+        return TransformHelper::lookupTransform(localizationCoreSegment, query);
     }
 
 }  // namespace armarx::armem::client::robot_state::localization
diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h
index cd577b6f07e205f3e97d609aea0066fe4ab3d2b1..53d3ad522db60c0cb6150e7bc90b6bb3174f0cbe 100644
--- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h
+++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h
@@ -43,7 +43,6 @@ namespace armarx::armem::client::robot_state::localization
     */
     class TransformReader :
         virtual public TransformReaderInterface
-    // virtual public ::armarx::armem::MemoryConnector
     {
     public:
         TransformReader(armem::ClientReaderComponentPluginUser& memoryClient);
@@ -60,25 +59,7 @@ namespace armarx::armem::client::robot_state::localization
 
         void registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def) override;
 
-        // const std::string& getPropertyPrefix() const override
-        // {
-        //     return propertyPrefix;
-        // }
-
     private:
-        std::vector<std::string> buildTransformChain(const armem::wm::Memory& memory,
-                const TransformQuery& query) const;
-
-        std::vector<Eigen::Affine3f> obtainTransforms(const armem::wm::Memory& memory,
-                const std::vector<std::string>& tfChain,
-                const std::string& agent, const std::int64_t& timestamp) const;
-
-        Eigen::Affine3f obtainTransform(
-            const std::string& entityName,
-            const armem::wm::ProviderSegment& agentProviderSegment,
-            int64_t timestamp)
-        const;
-
 
         armem::client::Reader memoryReader;
 
diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/interfaces.h b/source/RobotAPI/libraries/armem_robot_state/client/localization/interfaces.h
index a1989d6f6c3289db0f731cebcfede34556153821..e8afaf3e4d853da674ade877200a4de1b672bf84 100644
--- a/source/RobotAPI/libraries/armem_robot_state/client/localization/interfaces.h
+++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/interfaces.h
@@ -26,61 +26,43 @@
 
 #include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h>
 
+#include <RobotAPI/libraries/armem_robot_state/common/localization/types.h>
 #include <RobotAPI/libraries/armem_robot_state/types.h>
 
 namespace armarx::armem::client::robot_state::localization
 {
-    struct TransformResult
-    {
-        ::armarx::armem::robot_state::Transform transform;
-
-        enum class Status
-        {
-            Success,
-            ErrorLookupIntoFuture,
-            ErrorFrameNotAvailable
-        } status;
-
-        explicit operator bool() const { return status == Status::Success; }
-
-        std::string errorMessage = "";
-    };
-
-    struct TransformQuery
-    {
-        ::armarx::armem::robot_state::TransformHeader header;
 
-        // bool exact;
-    };
+    using armarx::armem::common::robot_state::localization::TransformQuery;
+    using armarx::armem::common::robot_state::localization::TransformResult;
 
     class TransformInterface
     {
-      public:
+    public:
         virtual ~TransformInterface() = default;
 
-        virtual void registerPropertyDefinitions(PropertyDefinitionsPtr &def) = 0;
+        virtual void registerPropertyDefinitions(PropertyDefinitionsPtr& def) = 0;
         virtual void connect()                                                = 0;
     };
 
     class TransformReaderInterface : virtual public TransformInterface
     {
-      public:
+    public:
         virtual ~TransformReaderInterface() = default;
 
-        virtual TransformResult getGlobalPose(const std::string &agentName,
-                                              const std::string &robotRootFrame,
-                                              const std::int64_t &timestamp) const = 0;
+        virtual TransformResult getGlobalPose(const std::string& agentName,
+                                              const std::string& robotRootFrame,
+                                              const std::int64_t& timestamp) const = 0;
 
-        virtual TransformResult lookupTransform(const TransformQuery &query) const = 0;
+        virtual TransformResult lookupTransform(const TransformQuery& query) const = 0;
         // waitForTransform()
     };
 
     class TransformWriterInterface : virtual public TransformInterface
     {
-      public:
+    public:
         ~TransformWriterInterface() override = default;
 
-        virtual bool commitTransform(const ::armarx::armem::robot_state::Transform &transform) = 0;
+        virtual bool commitTransform(const ::armarx::armem::robot_state::Transform& transform) = 0;
     };
 
 } // namespace armarx::armem::client::robot_state::localization
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.cpp b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bc4f7e897e05ec7d945158de62c3bcba4f11d603
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.cpp
@@ -0,0 +1,297 @@
+#include "TransformHelper.h"
+
+#include <SimoxUtility/algorithm/get_map_keys_values.h>
+#include <SimoxUtility/algorithm/string/string_tools.h>
+#include <SimoxUtility/color/cmaps.h>
+#include <SimoxUtility/math/pose/interpolate.h>
+
+#include "RobotAPI/libraries/core/FramedPose.h"
+
+#include "RobotAPI/libraries/aron/common/aron_conversions.h"
+
+#include "RobotAPI/libraries/armem/core/error/ArMemError.h"
+#include "RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h"
+#include "RobotAPI/libraries/armem/core/workingmemory/Memory.h"
+
+#include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h>
+#include <RobotAPI/libraries/armem_robot_state/aron_conversions.h>
+
+namespace armarx::armem::common::robot_state::localization
+{
+
+    TransformResult TransformHelper::lookupTransform(const armem::wm::CoreSegment &localizationCoreSegment,
+                                    const TransformQuery &query)
+    {
+        const std::vector<std::string> tfChain =
+                buildTransformChain(localizationCoreSegment, query);
+
+        if (tfChain.empty())
+            {
+                return {.transform    = {.header = query.header},
+                        .status       = TransformResult::Status::ErrorFrameNotAvailable,
+                        .errorMessage = "Cannot create tf lookup chain '" +
+                                        query.header.parentFrame + " -> " + query.header.frame +
+                                        "'"};
+            }
+
+        const std::vector<Eigen::Affine3f> transforms = obtainTransforms(
+                localizationCoreSegment, tfChain, query.header.agent, query.header.timestamp);
+
+        if (transforms.empty())
+            {
+                ARMARX_WARNING << deactivateSpam(1) << "No transform available.";
+                return {.transform    = {.header = query.header},
+                        .status       = TransformResult::Status::ErrorFrameNotAvailable,
+                        .errorMessage = "Error in TF loookup:  '" + query.header.parentFrame +
+                                        " -> " + query.header.frame +
+                                        "'. No memory data in time range."};
+            }
+
+        const Eigen::Affine3f transform = std::accumulate(transforms.begin(),
+                                                          transforms.end(),
+                                                          Eigen::Affine3f::Identity(),
+                                                          std::multiplies<>());
+
+        ARMARX_DEBUG << "Found valid transform";
+
+        return {.transform = {.header = query.header, .transform = transform},
+                .status    = TransformResult::Status::Success};
+    }
+
+    std::vector<std::string>
+    TransformHelper::buildTransformChain(const armem::wm::CoreSegment &localizationCoreSegment,
+                                         const TransformQuery &query)
+    {
+        ARMARX_DEBUG << "Building transform chain";
+
+        auto join = [](const std::string &parentFrame, const std::string &frame) {
+            return parentFrame + "," + frame;
+        };
+
+        std::vector<std::string> chain;
+
+        const auto &agentProviderSegment =
+                localizationCoreSegment.getProviderSegment(query.header.agent);
+
+        auto addToChain = [&](const std::string &parentFrame, const std::string &frame) {
+            const auto entityName = join(parentFrame, frame);
+
+            if (agentProviderSegment.hasEntity(entityName))
+                {
+                    chain.push_back(entityName);
+                }
+            else
+                {
+                    ARMARX_WARNING << deactivateSpam(1) << "Cannot perform tf lookup '" << parentFrame << " -> " << frame
+                                   << "'";
+                }
+        };
+
+        std::array<std::string, 3> knownChain{
+                GlobalFrame, MapFrame, OdometryFrame}; // Robot comes next
+
+        auto *frameBeginIt =
+                std::find(knownChain.begin(), knownChain.end(), query.header.parentFrame);
+        auto *const frameEndIt =
+                std::find(knownChain.begin(), knownChain.end(), query.header.frame);
+
+        if (frameBeginIt == knownChain.end())
+            {
+                ARMARX_WARNING << "Parent frame '" << query.header.parentFrame << "' unknown";
+                return {};
+            }
+
+        if (frameEndIt == knownChain.end())
+            {
+                ARMARX_DEBUG << "Frame '" << query.header.frame << "' must be robot frame";
+            }
+
+        const size_t nFrames = std::distance(frameBeginIt, frameEndIt);
+        ARMARX_DEBUG << "Lookup '" << query.header.parentFrame << " -> " << query.header.frame
+                     << "' across " << nFrames << " frames";
+
+        for (; frameBeginIt != knownChain.end() - 1; frameBeginIt++)
+            {
+                addToChain(*frameBeginIt, *(frameBeginIt + 1));
+            }
+
+        if (frameEndIt == knownChain.end())
+            {
+                addToChain(knownChain.back(), query.header.frame);
+            }
+
+        if (chain.empty())
+            {
+                ARMARX_WARNING << deactivateSpam(1) << "Cannot create tf lookup chain '" << query.header.parentFrame
+                               << " -> " << query.header.frame << "'";
+                return {};
+            }
+
+        return chain;
+    }
+
+    inline ::armarx::armem::robot_state::Transform
+    convertEntityToTransform(const armem::wm::EntityInstance &item)
+    {
+        arondto::Transform aronTransform;
+        aronTransform.fromAron(item.data());
+
+        ::armarx::armem::robot_state::Transform transform;
+        fromAron(aronTransform, transform);
+
+        return transform;
+    }
+
+    auto
+    findFirstElementAfter(const std::vector<::armarx::armem::robot_state::Transform> &transforms,
+                          const int64_t timestamp)
+    {
+        auto timestampBeyond =
+                [timestamp](const ::armarx::armem::robot_state::Transform &transform) {
+                    return transform.header.timestamp > timestamp;
+                };
+
+        const auto poseNextIt = std::find_if(transforms.begin(), transforms.end(), timestampBeyond);
+        return poseNextIt;
+    }
+
+    Eigen::Affine3f
+    interpolateTransform(const std::vector<::armarx::armem::robot_state::Transform> &queue,
+                         int64_t timestamp)
+    {
+        ARMARX_TRACE;
+
+        ARMARX_DEBUG << "Entering";
+
+        ARMARX_CHECK(not queue.empty())
+                << "The queue has to contain at least two items to perform a lookup";
+
+        ARMARX_DEBUG << "Entering ... "
+                     << "Q front " << queue.front().header.timestamp << "  "
+                     << "Q back " << queue.back().header.timestamp << "  "
+                     << "query timestamp " << timestamp;
+
+        // TODO(fabian.reister): sort queue.
+
+        ARMARX_CHECK(queue.back().header.timestamp > timestamp)
+                << "Cannot perform lookup into the future!";
+
+        // ARMARX_DEBUG << "Entering 1.5 " << queue.front().timestamp << "  " << timestamp;
+        ARMARX_CHECK(queue.front().header.timestamp < timestamp)
+                << "Cannot perform lookup. Timestamp too old";
+        // => now we know that there is an element right after and before the timestamp within our queue
+
+        ARMARX_DEBUG << "Entering 2";
+
+        const auto poseNextIt = findFirstElementAfter(queue, timestamp);
+
+        ARMARX_DEBUG << "it ari";
+
+        const auto posePreIt = poseNextIt - 1;
+
+        ARMARX_DEBUG << "deref";
+
+        // the time fraction [0..1] of the lookup wrt to posePre and poseNext
+        const float t = static_cast<float>(timestamp - posePreIt->header.timestamp) /
+                        (poseNextIt->header.timestamp - posePreIt->header.timestamp);
+
+        ARMARX_DEBUG << "interpolate";
+
+        return simox::math::interpolatePose(posePreIt->transform, poseNextIt->transform, t);
+    }
+
+    std::vector<Eigen::Affine3f>
+    TransformHelper::obtainTransforms(const armem::wm::CoreSegment &localizationCoreSegment,
+                                      const std::vector<std::string> &tfChain,
+                                      const std::string &agent,
+                                      const std::int64_t &timestamp)
+    {
+        const auto &agentProviderSegment = localizationCoreSegment.getProviderSegment(agent);
+
+        ARMARX_DEBUG << "Provider segments"
+                     << simox::alg::get_keys(localizationCoreSegment.providerSegments());
+
+        ARMARX_DEBUG << "Entities: " << simox::alg::get_keys(agentProviderSegment.entities());
+
+        try
+            {
+                std::vector<Eigen::Affine3f> transforms;
+                transforms.reserve(tfChain.size());
+                std::transform(tfChain.begin(),
+                               tfChain.end(),
+                               std::back_inserter(transforms),
+                               [&](const std::string &entityName) {
+                                   return obtainTransform(
+                                           entityName, agentProviderSegment, timestamp);
+                               });
+                return transforms;
+            }
+        catch (const armem::error::MissingEntry &missingEntryError)
+            {
+                ARMARX_WARNING << missingEntryError.what();
+            }
+        catch (const ::armarx::exceptions::local::ExpressionException &ex)
+            {
+                ARMARX_WARNING << "local exception: " << ex.what();
+            }
+
+        ARMARX_WARNING << "Unkown error.";
+
+        return {};
+    }
+
+    Eigen::Affine3f
+    TransformHelper::obtainTransform(const std::string &entityName,
+                                     const armem::wm::ProviderSegment &agentProviderSegment,
+                                     const int64_t timestamp)
+    {
+
+        ARMARX_DEBUG << "getEntity:" + entityName;
+        const auto &entity = agentProviderSegment.getEntity(entityName);
+
+        ARMARX_DEBUG << "History (size: " << entity.history().size() << ")"
+                    << simox::alg::get_keys(entity.history());
+
+        // if (entity.history.empty())
+        // {
+        //     // TODO(fabian.reister): fixme boom
+        //     ARMARX_ERROR << "No snapshots received.";
+        //     return Eigen::Affine3f::Identity();
+        // }
+
+        std::vector<::armarx::armem::robot_state::Transform> transforms;
+        transforms.reserve(entity.history().size());
+
+        const auto entitySnapshots = simox::alg::get_values(entity.history());
+        std::transform(
+                entitySnapshots.begin(),
+                entitySnapshots.end(),
+                std::back_inserter(transforms),
+                [](const auto &entity) { return convertEntityToTransform(entity.getInstance(0)); });
+
+        ARMARX_DEBUG << "obtaining transform";
+
+        if (transforms.size() > 1)
+            {
+                // TODO(fabian.reister): remove
+                return transforms.front().transform;
+
+                ARMARX_DEBUG << "More than one snapshots received: " << transforms.size();
+                const auto p = interpolateTransform(transforms, timestamp);
+                ARMARX_DEBUG << "Done interpolating transform";
+                return p;
+            }
+
+        // accept this to fail (will raise armem::error::MissingEntry)
+        if (transforms.empty())
+            {
+                ARMARX_DEBUG << "empty transform";
+
+                throw armem::error::MissingEntry("foo", "bar", "foo2", "bar2");
+            }
+
+        ARMARX_DEBUG << "single transform";
+
+        return transforms.front().transform;
+    }
+} // namespace armarx::armem::common::robot_state::localization
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..7505dd3859be919000e5bb80b1c9b6aaddb7ddd4
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h
@@ -0,0 +1,65 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author     Fabian Reister ( fabian dot reister at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+#include <RobotAPI/libraries/armem_robot_state/common/localization/types.h>
+
+namespace armarx::armem::wm
+{
+    class CoreSegment;
+    class ProviderSegment;
+} // namespace armarx::armem::wm
+
+namespace armarx::armem::common::robot_state::localization
+{
+    using armarx::armem::common::robot_state::localization::TransformQuery;
+    using armarx::armem::common::robot_state::localization::TransformResult;
+
+    class TransformHelper
+    {
+      public:
+        static TransformResult
+        lookupTransform(const armem::wm::CoreSegment &localizationCoreSegment,
+                        const TransformQuery &query);
+
+      private:
+        static std::vector<std::string>
+        buildTransformChain(const armem::wm::CoreSegment &localizationCoreSegment,
+                            const TransformQuery &query);
+
+        static std::vector<Eigen::Affine3f>
+        obtainTransforms(const armem::wm::CoreSegment &localizationCoreSegment,
+                         const std::vector<std::string> &tfChain,
+                         const std::string &agent,
+                         const std::int64_t &timestamp);
+
+        static Eigen::Affine3f
+        obtainTransform(const std::string &entityName,
+                        const armem::wm::ProviderSegment &agentProviderSegment,
+                        int64_t timestamp);
+    };
+} // namespace armarx::armem::common::robot_state::localization
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_state/common/localization/types.h b/source/RobotAPI/libraries/armem_robot_state/common/localization/types.h
new file mode 100644
index 0000000000000000000000000000000000000000..207660c68325fee922a7497ba771c014d597ef71
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_robot_state/common/localization/types.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    RobotAPI::ArmarXObjects::
+ * @author     Fabian Reister ( fabian dot reister at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <Eigen/Geometry>
+
+#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h>
+
+#include <RobotAPI/libraries/armem_robot_state/types.h>
+
+namespace armarx::armem::common::robot_state::localization
+{
+    struct TransformResult
+    {
+        ::armarx::armem::robot_state::Transform transform;
+
+        enum class Status
+        {
+            Success,
+            ErrorLookupIntoFuture,
+            ErrorFrameNotAvailable
+        } status;
+
+        explicit operator bool() const { return status == Status::Success; }
+
+        std::string errorMessage = "";
+    };
+
+    struct TransformQuery
+    {
+        ::armarx::armem::robot_state::TransformHeader header;
+
+        // bool exact;
+    };
+
+}  // namespace armarx::armem::common::robot_state::localization
\ No newline at end of file