diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt b/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt
index 137453970c1a97f0f3bd9653c067e959db21b4a5..a25161ff2cdf35173921720f68ad5bcec16cba30 100644
--- a/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt
@@ -2,20 +2,31 @@ armarx_component_set_name("RobotStateMemory")
 
 
 set(COMPONENT_LIBS
+    # ArmarXCore
     ArmarXCore ArmarXCoreInterfaces  # for DebugObserverInterface
+    ArmarXCoreComponentPlugins
+    # ArmarXGUI
     ArmarXGuiComponentPlugins
-    RobotAPICore
-    RobotAPIInterfaces 
-    RobotAPIComponentPlugins 
+    # RobotAPI
+    RobotAPICore RobotAPIInterfaces
+    RobotAPIComponentPlugins
     armem
     armem_robot_state
 )
 
 set(SOURCES
+    aron_conversions.cpp
     RobotStateMemory.cpp
+    RobotStateWriter.cpp
+    RobotUnitData.cpp
+    RobotUnitReader.cpp
 )
 set(HEADERS
+    aron_conversions.h
     RobotStateMemory.h
+    RobotStateWriter.h
+    RobotUnitData.h
+    RobotUnitReader.h
 )
 
 armarx_add_component("${SOURCES}" "${HEADERS}")
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp
index 111cced66ba9f678ba852d1005a10ded834d0946..653aaaf6e598f5b3ddc291b1460076b698160078 100644
--- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp
@@ -23,36 +23,27 @@
 #include "RobotStateMemory.h"
 
 // STD
-#include <algorithm>
-#include <iostream>
-#include <fstream>
 #include <memory>
 
-// Eigen
-#include <Eigen/Core>
-#include <Eigen/Geometry>
-
 // Simox
 #include <SimoxUtility/algorithm/string.h>
-#include <SimoxUtility/math/convert/rpy_to_mat3f.h>
 
 // ArmarX
-#include <ArmarXCore/core/logging/Logging.h>
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/logging/Logging.h>
 #include <ArmarXCore/core/system/ArmarXDataPath.h>
+#include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
 
 #include <RobotAPI/libraries/armem/core/error.h>
-#include <RobotAPI/libraries/armem/server/MemoryRemoteGui.h>
-#include <RobotAPI/libraries/aron/core/navigator/data/AllNavigators.h>
-#include <RobotAPI/libraries/aron/core/navigator/type/AllNavigators.h>
 #include <RobotAPI/libraries/armem_robot_state/server/common/Visu.h>
-#include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h>
-#include <RobotAPI/libraries/core/FramedPose.h>
-#include <RobotAPI/libraries/armem/core/Time.h>
-#include <RobotAPI/libraries/core/Pose.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotUnitComponentPlugin.h>
+
+#include "aron_conversions.h"
+
 
 namespace armarx::armem::server::robot_state
 {
+
     RobotStateMemory::RobotStateMemory()
         : descriptionSegment(server::ComponentPluginUser::iceMemory,
                              server::ComponentPluginUser::workingMemoryMutex),
@@ -62,7 +53,13 @@ namespace armarx::armem::server::robot_state
                               server::ComponentPluginUser::workingMemoryMutex),
           commonVisu(descriptionSegment, proprioceptionSegment, localizationSegment)
     {
+        addPlugin(debugObserver);
+        ARMARX_CHECK_NOT_NULL(debugObserver);
+        addPlugin(robotUnit.plugin);
+        ARMARX_CHECK_NOT_NULL(robotUnit.plugin);
 
+        robotUnit.reader.setTag(getName());
+        robotUnit.writer.setTag(getName());
     }
 
     RobotStateMemory::~RobotStateMemory() = default;
@@ -79,18 +76,23 @@ namespace armarx::armem::server::robot_state
 
         const std::string robotUnitPrefix{sensorValuePrefix};
 
-        defs->optional(robotUnitSensorPrefix, robotUnitPrefix + "SensorValuePrefix", "Prefix of all sensor values");
-        defs->optional(robotUnitMemoryBatchSize, robotUnitPrefix + "MemoryBatchSize", "The size of the entity snapshot to send to the memory. Min is 1");
-        defs->optional(robotUnitPollFrequency, robotUnitPrefix + "UpdateFrequency", "The frequency in Hz to store values. All other values get discarded. Min is 1, max is " + std::to_string(ROBOT_UNIT_MAXIMUM_FREQUENCY));
-        defs->optional(robotUnitConfigPath, robotUnitPrefix + "ConfigPath", "Specify a configuration file to group the sensor values specifically.");
+        defs->optional(robotUnit.reader.properties.sensorPrefix, robotUnitPrefix + "SensorValuePrefix",
+                       "Prefix of all sensor values.");
+        defs->optional(robotUnit.writer.properties.memoryBatchSize, robotUnitPrefix + "MemoryBatchSize",
+                       "The size of the entity snapshot to send to the memory. Minimum is 1.")
+        .setMin(1);
+        defs->optional(robotUnit.pollFrequency, robotUnitPrefix + "UpdateFrequency",
+                       "The frequency to store values in Hz. All other values get discarded. "
+                       "Minimum is 1, max is " + std::to_string(ROBOT_UNIT_MAXIMUM_FREQUENCY) + ".")
+        .setMin(1).setMax(ROBOT_UNIT_MAXIMUM_FREQUENCY);
+        defs->optional(robotUnit.configPath, robotUnitPrefix + "ConfigPath",
+                       "Specify a configuration file to group the sensor values specifically.");
 
         descriptionSegment.defineProperties(defs);
         proprioceptionSegment.defineProperties(defs);
         localizationSegment.defineProperties(defs);
         commonVisu.defineProperties(defs);
 
-        setRobotUnitAsOptionalDependency();
-
         return defs;
     }
 
@@ -103,14 +105,13 @@ namespace armarx::armem::server::robot_state
 
     void RobotStateMemory::onInitComponent()
     {
-
         descriptionSegment.init();
         proprioceptionSegment.init();
         localizationSegment.init();
         commonVisu.init();
 
-        robotUnitMemoryBatchSize = std::max((unsigned int) 1, robotUnitMemoryBatchSize);
-        robotUnitPollFrequency = std::clamp(robotUnitPollFrequency, 1, ROBOT_UNIT_MAXIMUM_FREQUENCY);
+        robotUnit.pollFrequency = std::clamp(robotUnit.pollFrequency, 1, ROBOT_UNIT_MAXIMUM_FREQUENCY);
+        robotUnit.writer.properties.memoryBatchSize = std::max(static_cast<unsigned int>(1), robotUnit.writer.properties.memoryBatchSize);
 
         Ice::StringSeq includePaths;
         auto packages = armarx::Application::GetProjectDependencies();
@@ -129,39 +130,46 @@ namespace armarx::armem::server::robot_state
             includePaths.insert(includePaths.end(), projectIncludePaths.begin(), projectIncludePaths.end());
         }
 
-        ArmarXDataPath::getAbsolutePath(robotUnitConfigPath, robotUnitConfigPath, includePaths);
-
-        // workingMemory.name() = workingMemoryName;
+        ArmarXDataPath::getAbsolutePath(robotUnit.configPath, robotUnit.configPath, includePaths);
     }
 
 
     void RobotStateMemory::onConnectComponent()
     {
+        ARMARX_CHECK_NOT_NULL(debugObserver->getDebugObserver());
 
-        if (getRobotUnit())
+        if (robotUnit.plugin->getRobotUnit())
         {
-            waitUntilRobotUnitIsRunning();
+            robotUnit.plugin->waitUntilRobotUnitIsRunning();
         }
+        RobotUnitInterfacePrx robotUnitPrx = robotUnit.plugin->getRobotUnit();
+        ARMARX_CHECK_NOT_NULL(robotUnitPrx->getKinematicUnit());
 
-        ARMARX_CHECK_NOT_NULL(getRobotUnit()->getKinematicUnit());
+        descriptionSegment.connect(getArvizClient(), robotUnitPrx);
 
-        descriptionSegment.connect(getArvizClient(), getRobotUnit());
-
-        proprioceptionSegment.connect(getArvizClient(), getRobotUnit());
-        toIce(robotUnitProviderID, proprioceptionSegment.getRobotUnitProviderID());
+        proprioceptionSegment.connect(getArvizClient(), robotUnitPrx);
 
         localizationSegment.connect(getArvizClient());
 
-        commonVisu.connect(
-            getArvizClient()
-        );
+        commonVisu.connect(getArvizClient());
 
-        cfg.loggingNames.emplace_back(robotUnitSensorPrefix);
-        handler = make_shared<RobotUnitDataStreamingReceiver>(this, getRobotUnit(), cfg);
-        keys = handler->getDataDescription();
+        robotUnit.reader.connect(*robotUnit.plugin, *debugObserver);
+        robotUnit.writer.connect(*debugObserver);
+        robotUnit.writer.properties.robotUnitProviderID = proprioceptionSegment.getRobotUnitProviderID();
 
-        robotUnitConversionTask = new PeriodicTask<RobotStateMemory>(this, &RobotStateMemory::convertRobotUnitStreamingDataToAron, (1000 / robotUnitPollFrequency));
-        robotUnitStoringTask = new PeriodicTask<RobotStateMemory>(this, &RobotStateMemory::storeConvertedRobotUnitDataInMemory, (1000 / robotUnitPollFrequency));
+        robotUnit.reader.task = new SimpleRunningTask<>([this]()
+        {
+            robotUnit.reader.run(
+                robotUnit.pollFrequency, robotUnit.dataQueue, robotUnit.dataMutex
+            );
+        }, "Robot Unit Reader");
+        robotUnit.writer.task = new SimpleRunningTask<>([this]()
+        {
+            robotUnit.writer.run(
+                robotUnit.pollFrequency, robotUnit.dataQueue, robotUnit.dataMutex,
+                iceMemory, localizationSegment, workingMemoryMutex
+            );
+        }, "Robot State Writer");
 
         startRobotUnitStream();
     }
@@ -178,340 +186,57 @@ namespace armarx::armem::server::robot_state
         stopRobotUnitStream();
     }
 
+
     /*************************************************************/
     // RobotUnit Streaming functions
     /*************************************************************/
-    void RobotStateMemory::convertRobotUnitStreamingDataToAron()
-    {
-        auto& data = handler->getDataBuffer();
-        if (data.size() == 0)
-        {
-            return;
-        }
-        auto& currentTimestep = data.back();
-
-        ARMARX_DEBUG << "RobotUnitData: Generating new aron map for current timestep";
-        auto start = std::chrono::high_resolution_clock::now();
 
-        RobotUnitData convertedAndGroupedData;
-        for (const auto& [nameEntry, dataEntry] : keys.entries)
-        {
-            std::string name = nameEntry;
-            std::string jointOrWhateverName;
-
-            std::string groupName = "";
-            if (configSensorMapping.find(name) != configSensorMapping.end())
-            {
-                groupName = configSensorMapping.at(name);
-            }
-            else
-            {
-                const size_t first_dot_pos = name.find(".");
-                const size_t second_dot_pos = name.find(".", first_dot_pos + 1); // find second occurence of "."
-                if (second_dot_pos == std::string::npos)
-                {
-                    ARMARX_WARNING << "Could not find a groupname for the sensor with name " << name << ". All sensors must be called sens.X.Y where X is the name of the group and Y is the actual sensor. Ignoring this sensor.";
-                    continue;
-                }
-                groupName = name.substr(0, second_dot_pos);
-
-                jointOrWhateverName = name.substr(first_dot_pos + 1, second_dot_pos - first_dot_pos - 1);
-
-                name = name.substr(second_dot_pos + 1); // remove the groupName, TODO check if +1 is valid
-            }
-
-            RobotUnitData::RobotUnitDataGroup e;
-            if (auto it = convertedAndGroupedData.groups.find(groupName); it == convertedAndGroupedData.groups.end())
-            {
-                // generate new dict for the group
-                auto dict = std::make_shared<aron::datanavigator::DictNavigator>();
-                auto encGroupName = std::make_shared<aron::datanavigator::StringNavigator>(groupName);
-                dict->addElement("EncoderGroupName", encGroupName);
-
-                auto iterId = std::make_shared<aron::datanavigator::LongNavigator>(currentTimestep.iterationId);
-                dict->addElement("IterationId", iterId);
-
-                const auto nameId = std::make_shared<aron::datanavigator::StringNavigator>(jointOrWhateverName);
-                dict->addElement("name", nameId);
-
-                e.timestamp = currentTimestep.timestampUSec;
-                e.name = groupName;
-                e.data = dict;
-            }
-            else
-            {
-                // reuse existing entry
-                e = it->second;
-            }
-
-            switch (dataEntry.type)
-            {
-                case RobotUnitDataStreaming::NodeTypeFloat:
-                {
-                    float value = RobotUnitDataStreamingReceiver::GetAs<Ice::Float>(currentTimestep, dataEntry);
-                    auto aron = std::make_shared<aron::datanavigator::FloatNavigator>(value);
-                    e.data->addElement(name, aron);
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeBool:
-                {
-                    bool value = RobotUnitDataStreamingReceiver::GetAs<bool>(currentTimestep, dataEntry);
-                    auto aron = std::make_shared<aron::datanavigator::BoolNavigator>(value);
-                    e.data->addElement(name, aron);
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeByte:
-                {
-                    int value = RobotUnitDataStreamingReceiver::GetAs<Ice::Byte>(currentTimestep, dataEntry);
-                    auto aron = std::make_shared<aron::datanavigator::IntNavigator>(value);
-                    e.data->addElement(name, aron);
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeShort:
-                {
-                    int value = RobotUnitDataStreamingReceiver::GetAs<Ice::Short>(currentTimestep, dataEntry);
-                    auto aron = std::make_shared<aron::datanavigator::IntNavigator>(value);
-                    e.data->addElement(name, aron);
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeInt:
-                {
-                    int value = RobotUnitDataStreamingReceiver::GetAs<Ice::Int>(currentTimestep, dataEntry);
-                    auto aron = std::make_shared<aron::datanavigator::IntNavigator>(value);
-                    e.data->addElement(name, aron);
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeLong:
-                {
-                    long value = RobotUnitDataStreamingReceiver::GetAs<Ice::Long>(currentTimestep, dataEntry);
-                    auto aron = std::make_shared<aron::datanavigator::LongNavigator>(value);
-                    e.data->addElement(name, aron);
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeDouble:
-                {
-                    double value = RobotUnitDataStreamingReceiver::GetAs<Ice::Double>(currentTimestep, dataEntry);
-                    auto aron = std::make_shared<aron::datanavigator::DoubleNavigator>(value);
-                    e.data->addElement(name, aron);
-                    break;
-                }
-                default:
-                    throw LocalException("The enum type should not exist! Perhaps someone changed the RobotUnitDataStreaming::NodeType enum definition?");
-            }
-
-            convertedAndGroupedData.groups.insert({groupName, e});
-        }
-        {
-            std::lock_guard g{robotUnitDataMutex};
-            robotUnitDataQueue.push(convertedAndGroupedData);
-        }
-
-        auto stop = std::chrono::high_resolution_clock::now();
-        ARMARX_DEBUG << "RobotUnitData: The total time needed to convert the data to aron is: " << std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
-    }
-
-    void RobotStateMemory::storeConvertedRobotUnitDataInMemory()
-    {
-        unsigned int size = 0;
-        {
-            std::lock_guard g{robotUnitDataMutex};
-            size = robotUnitDataQueue.size(); // the size can only grow
-        }
-        if (size >= robotUnitMemoryBatchSize)
-        {
-            ARMARX_DEBUG << "RobotUnitData: Sending batch of " << robotUnitMemoryBatchSize << " timesteps to memory... The size of the queue is: " << size;
-            auto start = std::chrono::high_resolution_clock::now();
-
-            // send batch to memory
-            armem::data::Commit proprioceptionCommit;
-            armem::data::Commit odometryCommit;
-
-            for (unsigned int i = 0; i < robotUnitMemoryBatchSize; ++i)
-            {
-                std::lock_guard g{robotUnitDataMutex};
-                const auto& convertedAndGroupedData = robotUnitDataQueue.front();
-                for (const auto& [encName, encTimestep] : convertedAndGroupedData.groups)
-                {
-                    ARMARX_CHECK_EQUAL(encName, encTimestep.name);
-
-                    auto entityID = robotUnitProviderID;
-                    entityID.entityName = encName;
-
-                    const auto& timeUSec = encTimestep.timestamp;
-                    const auto& aron = encTimestep.data;
-
-                    armem::data::EntityUpdate& update = proprioceptionCommit.updates.emplace_back();
-                    update.entityID = entityID;
-                    update.timeCreatedMicroSeconds = timeUSec;
-                    update.instancesData = {aron->toAronDictPtr()};
-                }
-
-                // odometry pose -> localization segment
-                auto it = convertedAndGroupedData.groups.find("sens.Platform");
-                if (it != convertedAndGroupedData.groups.end())
-                {
-                    ARMARX_DEBUG << "Found odometry data.";
-
-                    const RobotUnitData::RobotUnitDataGroup& timestep = it->second;
-                    const float relPosX = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionX"))->getValue();
-                    const float relPosY = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionY"))->getValue();
-                    const float relOrientation = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionRotation"))->getValue();
-
-                    Eigen::Affine3f odometryPose = Eigen::Affine3f::Identity();
-                    odometryPose.translation() << relPosX, relPosY, 0; // TODO set height
-                    odometryPose.linear() = simox::math::rpy_to_mat3f(0.F, 0.F, relOrientation);
-
-                    // const auto timestamp = armem::Time::microSeconds(it->second.timestamp);
-                    const auto timestamp = armem::Time::now();
-
-                    const ::armarx::armem::robot_state::Transform transform
-                    {
-                        .header =
-                        {
-                            .parentFrame = OdometryFrame,
-                            .frame = "root", // TODO: robot root node
-                            .agent = robotUnitProviderID.providerSegmentName,
-                            .timestamp = timestamp
-                        },
-                        .transform = odometryPose
-                    };
-
-                    localizationSegment.storeTransform(transform);
-
-                }
-                else
-                {
-                    ARMARX_INFO << deactivateSpam(1000) << "No odometry data! If you are using a mobile platform this should not have happened";
-                }
-
-
-                robotUnitDataQueue.pop();
-            }
-
-            auto results = commit(proprioceptionCommit);
-            auto stop = std::chrono::high_resolution_clock::now();
-            ARMARX_DEBUG << "RobotUnitData: The total runtime of sending a batch to the memory is: " << std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
-
-            for (const auto& result : results.results)
-            {
-                if (!result.success)
-                {
-                    ARMARX_WARNING << "Could not add data to memory. Error message: " << result.errorMessage;
-                }
-            }
-        }
-    }
-
-    void RobotStateMemory::stopRobotUnitStream()
-    {
-        std::lock_guard g{startStopMutex};
-        robotUnitConversionTask->stop();
-        robotUnitStoringTask->stop();
-    }
 
     void RobotStateMemory::startRobotUnitStream()
     {
-        std::lock_guard g{startStopMutex};
-        if (robotUnitConversionTask->isRunning() || robotUnitStoringTask->isRunning())
+        std::lock_guard lock{startStopMutex};
+        if (robotUnit.reader.task->isRunning() || robotUnit.writer.task->isRunning())
         {
-            if (robotUnitConversionTask->isRunning() && robotUnitStoringTask->isRunning())
+            if (robotUnit.reader.task->isRunning() && robotUnit.writer.task->isRunning())
             {
                 return;
             }
             ARMARX_WARNING << "Found inconsistency in running tasks. Restarting all!";
             stopRobotUnitStream();
         }
-
-        std::stringstream ss;
-        ss << "Getting sensor values for:" << std::endl;
-        for (const auto& [name, dataEntry] : keys.entries)
         {
-            std::string type = "";
-            switch (dataEntry.type)
+            std::stringstream ss;
+            ss << "Getting sensor values for:" << std::endl;
+            for (const auto& [name, dataEntry] : robotUnit.reader.description.entries)
             {
-                case RobotUnitDataStreaming::NodeTypeBool:
-                {
-                    type = "Bool";
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeByte:
-                {
-                    type = "Byte";
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeShort:
-                {
-                    type = "Short";
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeInt:
-                {
-                    type = "Int";
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeLong:
-                {
-                    type = "Long";
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeFloat:
-                {
-                    type = "Float";
-                    break;
-                }
-                case RobotUnitDataStreaming::NodeTypeDouble:
-                {
-                    type = "Double";
-                    break;
-                }
+                const std::string type = RobotUnitDataStreaming::DataEntryNames.to_name(dataEntry.type);
+                ss << "\t" << name << " (type: '" << type << "')" << std::endl;
             }
-            ss << "\t" << name << " (type: '" << type << "')" << std::endl;
+            ARMARX_INFO << ss.str();
         }
-        ARMARX_INFO << ss.str();
 
         // parse config
-        std::filesystem::path configPath(robotUnitConfigPath);
-        configSensorMapping.clear();
-        if (std::filesystem::is_regular_file(configPath))
+        if (std::filesystem::is_regular_file(robotUnit.configPath))
         {
-            ARMARX_INFO << "Found a configuration file at: " << robotUnitConfigPath;
-            // a simple self-made parser for the config file. Extend it if you need to
-            std::ifstream in(configPath);
-            std::string line;
-            while (std::getline(in, line))
-            {
-                if (line.size() > 0)
-                {
-                    if (simox::alg::starts_with(line, "//"))
-                    {
-                        continue;
-                    }
-
-                    std::vector<std::string> mappings = simox::alg::split(line, ",");
-                    for (const auto& mapping : mappings)
-                    {
-                        std::vector<std::string> split = simox::alg::split(mapping, "=>");
-                        if (split.size() < 2)
-                        {
-                            ARMARX_WARNING << "A mapping in the RobotUnitConfig file is not valid. Ignoring it: " << mapping;
-                            continue;
-                        }
-                        for (unsigned int i = 0; i < split.size() - 1; ++i)
-                        {
-                            ARMARX_DEBUG << "Setting sensor value '" << split[i] << "' to group '" << split[split.size() - 1] << "'.";
-                            configSensorMapping.insert({split[i], split[split.size() - 1]});
-                        }
-                    }
-                }
-            }
+            ARMARX_INFO << "Found a configuration file at: " << robotUnit.configPath;
+            // A simple self-made parser for the config file. Extend it if you need to.
+            robotUnit.reader.configSensorMapping = RobotUnitReader::readConfig(robotUnit.configPath);
         }
         else
         {
-            ARMARX_INFO << "The config path '" << robotUnitConfigPath << "' is not valid.";
+            ARMARX_INFO << "The config path '" << robotUnit.configPath << "' is not valid.";
         }
 
-        robotUnitConversionTask->start();
-        robotUnitStoringTask->start();
+        robotUnit.reader.task->start();
+        robotUnit.writer.task->start();
+    }
+
+
+    void RobotStateMemory::stopRobotUnitStream()
+    {
+        std::lock_guard lock{startStopMutex};
+        robotUnit.reader.task->stop();
+        robotUnit.writer.task->stop();
     }
 
 }
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h
index e8174416e9b547f70af5e5c6b578242a0234743d..d249678b6a9232ebc32743091f2038e0ed1ea3e5 100644
--- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h
@@ -24,18 +24,18 @@
 
 // STD
 #include <atomic>
-#include <thread>
+#include <optional>
 #include <queue>
+#include <thread>
 
 // Simox
 #include <SimoxUtility/meta/enum/adapt_enum.h>
 
 // ArmarX
 #include <ArmarXCore/core/Component.h>
-#include <ArmarXCore/core/services/tasks/PeriodicTask.h>
+#include <ArmarXCore/core/services/tasks/RunningTask.h>
 
 // BaseClass
-#include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotUnitComponentPlugin.h>
 #include "RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h"
 
 #include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h>
@@ -46,6 +46,16 @@
 #include <RobotAPI/libraries/armem_robot_state/server/localization/Segment.h>
 #include <RobotAPI/libraries/armem_robot_state/server/common/Visu.h>
 
+#include "RobotUnitData.h"
+#include "RobotUnitReader.h"
+#include "RobotStateWriter.h"
+
+
+namespace armarx::plugins
+{
+    class DebugObserverComponentPlugin;
+    class RobotUnitComponentPlugin;
+}
 namespace armarx::armem::server::robot_state
 {
 
@@ -63,18 +73,18 @@ namespace armarx::armem::server::robot_state
     class RobotStateMemory :
         virtual public armarx::Component,
         virtual public armem::server::ComponentPluginUser,
-        virtual public RobotUnitComponentPluginUser,
         virtual public armarx::ArVizComponentPluginUser
     {
     public:
-        RobotStateMemory();
 
-        virtual ~RobotStateMemory();
+        RobotStateMemory();
+        virtual ~RobotStateMemory() override;
 
         /// @see armarx::ManagedIceObject::getDefaultName()
         std::string getDefaultName() const override;
 
     protected:
+
         /// @see armarx::ManagedIceObject::onInitComponent()
         void onInitComponent() override;
 
@@ -90,17 +100,21 @@ namespace armarx::armem::server::robot_state
         /// @see PropertyUser::createPropertyDefinitions()
         armarx::PropertyDefinitionsPtr createPropertyDefinitions() override;
 
+
     private:
-        // RobotUnit Streaming
-        void convertRobotUnitStreamingDataToAron();
-        void storeConvertedRobotUnitDataInMemory();
+
+        /// Reads from `robotUnitDataQueue` and commits into memory.
+        void convertRobotUnitStreamingDataToAronTask();
+        /// Reads from `robotUnitDataQueue` and commits into memory.
+        void storeConvertedRobotUnitDataInMemoryTask();
 
         void startRobotUnitStream();
         void stopRobotUnitStream();
 
+
     private:
-        std::string workingMemoryName = "RobotStateMemory";
-        bool addCoreSegmentOnUsage = false;
+
+        armarx::plugins::DebugObserverComponentPlugin* debugObserver = nullptr;
 
         mutable std::recursive_mutex startStopMutex;
 
@@ -118,41 +132,28 @@ namespace armarx::armem::server::robot_state
         // Joint visu for all segments => robot pose and configuration
         Visu commonVisu;
 
-        // RobotUnit stuff
-        RobotUnitDataStreaming::DataStreamingDescription keys;
-        std::vector<RobotUnitDataStreaming::DataEntry> keysList;
-        RobotUnitDataStreaming::Config cfg;
-        RobotUnitDataStreamingReceiverPtr handler;
-        std::map<std::string, std::string> configSensorMapping;
 
-        struct RobotUnitData
+        struct RobotUnit
         {
-            struct RobotUnitDataGroup
-            {
-                long timestamp;
-                std::string name;
-                aron::datanavigator::DictNavigatorPtr data;
-            };
-
-            std::map<std::string, RobotUnitDataGroup> groups;
+            int pollFrequency = 50;
+            std::string configPath = "NO CONFIG SET";
+
+            armarx::plugins::RobotUnitComponentPlugin* plugin = nullptr;
+            RobotUnitReader reader;
+            RobotStateWriter writer;
+
+            // queue
+            std::queue<RobotUnitData> dataQueue;
+            mutable std::mutex dataMutex;
         };
+        RobotUnit robotUnit;
+
 
         // params
         static constexpr int ROBOT_UNIT_MAXIMUM_FREQUENCY = 100;
         static constexpr const char* sensorValuePrefix = "RobotUnit.";
 
-        int robotUnitPollFrequency = 50;
-        std::string robotUnitSensorPrefix = "sens.*";
-        unsigned int robotUnitMemoryBatchSize = 50;
-        std::string robotUnitConfigPath = "NO CONFIG SET";
-
-        // queue
-        std::queue<RobotUnitData> robotUnitDataQueue;
-        mutable std::mutex robotUnitDataMutex;
 
-        // running tasks
-        armarx::PeriodicTask<RobotStateMemory>::pointer_type robotUnitConversionTask;
-        armarx::PeriodicTask<RobotStateMemory>::pointer_type robotUnitStoringTask;
     };
 
 }  // namespace armarx::armem::server::robot_state
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8b49307bd3b52f4cefdbd8adc350d721e3f0f10d
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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::RobotSensorMemory
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2020
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#include "RobotStateWriter.h"
+
+// STL
+#include <chrono>
+
+// Simox
+#include <SimoxUtility/math/convert/rpy_to_mat3f.h>
+
+// ArmarX
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/time/CycleUtil.h>
+#include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
+
+#include <RobotAPI/libraries/core/FramedPose.h>
+#include <RobotAPI/libraries/aron/core/navigator/data/AllNavigators.h>
+#include <RobotAPI/libraries/armem/core/Time.h>
+#include <RobotAPI/libraries/armem/core/ice_conversions.h>
+#include <RobotAPI/libraries/armem/server/MemoryToIceAdapter.h>
+#include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h>
+#include <RobotAPI/libraries/armem_robot_state/server/localization/Segment.h>
+
+
+namespace armarx::armem::server::robot_state
+{
+
+    void RobotStateWriter::connect(armarx::plugins::DebugObserverComponentPlugin& debugObserver)
+    {
+        {
+            // Thread-local copy of debug observer helper.
+            this->debugObserver = DebugObserverHelper(
+                                      Logging::tag.tagName, debugObserver.getDebugObserver(), true
+                                  );
+        }
+    }
+
+
+    static float toDurationMs(
+        std::chrono::high_resolution_clock::time_point start,
+        std::chrono::high_resolution_clock::time_point end)
+    {
+        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+        return duration.count() / 1000.f;
+    }
+
+
+    void RobotStateWriter::run(
+        float pollFrequency,
+        std::queue<RobotUnitData>& dataQueue, std::mutex& dataMutex,
+        MemoryToIceAdapter& memory, localization::Segment& localizationSegment,
+        std::mutex& workingMemoryMutex)
+    {
+        CycleUtil cycle(static_cast<int>(1000.f / pollFrequency));
+        while (task and not task->isStopped())
+        {
+            size_t queueSize = 0;
+            std::queue<RobotUnitData> batch;
+            {
+                std::lock_guard lock{dataMutex};
+                queueSize = dataQueue.size();
+                if (dataQueue.size() >= properties.memoryBatchSize)
+                {
+                    std::swap(batch, dataQueue);
+                }
+            }
+            if (debugObserver)
+            {
+                debugObserver->setDebugObserverDatafield("RobotStateWriter | Queue Size", queueSize);
+            }
+            if (batch.size() > 0)
+            {
+                Update update = buildUpdate(batch);
+
+                auto start = std::chrono::high_resolution_clock::now();
+                auto endLock = start, endProprioception = start, endLocalization = start;
+
+                armem::CommitResult result;
+                {
+                    std::scoped_lock lock(workingMemoryMutex);
+                    endLock = std::chrono::high_resolution_clock::now();
+
+                    result = memory.commit(update.proprioception);
+                    endProprioception = std::chrono::high_resolution_clock::now();
+
+                    localizationSegment.storeTransform(update.localization);
+                    endLocalization = std::chrono::high_resolution_clock::now();
+                }
+                if (not result.allSuccess())
+                {
+                    ARMARX_WARNING << "Could not add data to memory. Error message: " << result.allErrorMessages();
+                }
+                if (debugObserver)
+                {
+                    auto end = std::chrono::high_resolution_clock::now();
+
+                    debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit [ms]", toDurationMs(start, end));
+                    debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit Lock [ms]", toDurationMs(start, endLock));
+                    debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit Proprioception [ms]", toDurationMs(endLock, endProprioception));
+                    debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit Localization [ms]", toDurationMs(endProprioception, endLocalization));
+                }
+            }
+
+            if (debugObserver)
+            {
+                debugObserver->sendDebugObserverBatch();
+            }
+            cycle.waitForCycleDuration();
+        }
+    }
+
+
+    RobotStateWriter::Update RobotStateWriter::buildUpdate(std::queue<RobotUnitData> dataQueue)
+    {
+        ARMARX_CHECK_GREATER(dataQueue.size(), 0);
+
+        ARMARX_DEBUG << "RobotStateWriter: Commit batch of " << dataQueue.size() << " timesteps to memory...";
+        auto start = std::chrono::high_resolution_clock::now();
+
+        // Send batch to memory
+        Update update;
+
+        while (dataQueue.size() > 0)
+        {
+            const RobotUnitData& convertedAndGroupedData = dataQueue.front();
+
+            for (const auto& [encName, encTimestep] : convertedAndGroupedData.groups)
+            {
+                ARMARX_CHECK_EQUAL(encName, encTimestep.name);
+
+                armem::EntityUpdate& up = update.proprioception.add();
+                up.entityID = properties.robotUnitProviderID.withEntityName(encName);
+                up.timeCreated = encTimestep.timestamp;
+                up.instancesData = { encTimestep.data };
+            }
+
+            // odometry pose -> localization segment
+            auto it = convertedAndGroupedData.groups.find(properties.platformGroupName);
+            if (it != convertedAndGroupedData.groups.end())
+            {
+                ARMARX_DEBUG << "Found odometry data.";
+
+                const RobotUnitData::RobotUnitDataGroup& timestep = it->second;
+                const float relPosX = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionX"))->getValue();
+                const float relPosY = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionY"))->getValue();
+                const float relOrientation = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionRotation"))->getValue();
+
+                Eigen::Affine3f odometryPose = Eigen::Affine3f::Identity();
+                odometryPose.translation() << relPosX, relPosY, 0; // TODO set height
+                odometryPose.linear() = simox::math::rpy_to_mat3f(0.F, 0.F, relOrientation);
+
+                // const auto timestamp = armem::Time::microSeconds(it->second.timestamp);
+
+                armem::robot_state::Transform& transform = update.localization;
+                transform.header.parentFrame = armarx::OdometryFrame;
+                transform.header.frame = "root"; // TODO: robot root node
+                transform.header.agent = properties.robotUnitProviderID.providerSegmentName;
+                transform.header.timestamp = it->second.timestamp;
+                transform.transform = odometryPose;
+            }
+            else if (!noOdometryDataLogged)
+            {
+                ARMARX_INFO << "No odometry data! (No group with name '" << properties.platformGroupName << "'.). \n"
+                            << "If you are using a mobile platform this should not have happened.";
+                noOdometryDataLogged = true;
+            }
+
+            dataQueue.pop();
+        }
+
+        auto stop = std::chrono::high_resolution_clock::now();
+        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
+        ARMARX_DEBUG << "RobotUnitData: The total runtime of sending a batch to the memory is: " << duration;
+        if (debugObserver)
+        {
+            debugObserver->setDebugObserverDatafield("RobotStateWriter | t Build Update [ms]", duration.count() / 1000.f);
+        }
+
+        return update;
+    }
+
+}
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.h b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.h
new file mode 100644
index 0000000000000000000000000000000000000000..8111357d24728f4670719a1d9a991ac12083da1c
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.h
@@ -0,0 +1,99 @@
+/*
+ * 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::RobotSensorMemory
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2020
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+#pragma once
+
+#include <optional>
+
+#include <ArmarXCore/core/logging/Logging.h>
+#include <ArmarXCore/core/services/tasks/TaskUtil.h>
+#include <ArmarXCore/libraries/DebugObserverHelper/DebugObserverHelper.h>
+
+#include <RobotAPI/libraries/armem/core/Commit.h>
+#include <RobotAPI/libraries/armem/core/MemoryID.h>
+#include <RobotAPI/libraries/armem_robot_state/types.h>
+
+#include "RobotUnitData.h"
+
+
+namespace armarx::plugins
+{
+    class DebugObserverComponentPlugin;
+}
+namespace armarx::armem::server
+{
+    class MemoryToIceAdapter;
+}
+namespace armarx::armem::server::robot_state::localization
+{
+    class Segment;
+}
+namespace armarx::armem::server::robot_state
+{
+
+    class RobotStateWriter : public armarx::Logging
+    {
+    public:
+
+        void connect(armarx::plugins::DebugObserverComponentPlugin& debugObserver);
+
+        /// Reads data from `dataQueue` and commits to the memory.
+        void run(float pollFrequency,
+                 std::queue<RobotUnitData>& dataQueue, std::mutex& dataMutex,
+                 MemoryToIceAdapter& memory,
+                 localization::Segment& localizationSegment,
+                 std::mutex& workingMemoryMutex
+                );
+
+
+        struct Update
+        {
+            armem::Commit proprioception;
+            armem::robot_state::Transform localization;
+        };
+        Update buildUpdate(std::queue<RobotUnitData> dataQueue);
+
+
+    public:
+
+        struct Properties
+        {
+            unsigned int memoryBatchSize = 50;
+            armem::MemoryID robotUnitProviderID;
+            std::string platformGroupName = "sens.Platform";
+        };
+        Properties properties;
+
+        std::optional<DebugObserverHelper> debugObserver;
+
+
+        armarx::SimpleRunningTask<>::pointer_type task = nullptr;
+
+
+    private:
+
+        bool noOdometryDataLogged = false;
+
+    };
+
+}
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a9b72e2a74203cb9f44dd54796b397770257ecd
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.cpp
@@ -0,0 +1,9 @@
+#include "RobotUnitData.h"
+
+#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h>
+
+
+namespace armarx::armem::server::robot_state
+{
+
+}
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.h b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc43dcddca3eb693fe5200dcc0a256a15be23cac
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <RobotAPI/libraries/armem/core/Time.h>
+
+
+namespace armarx::aron::datanavigator
+{
+    using DictNavigatorPtr = std::shared_ptr<class DictNavigator>;
+}
+
+namespace armarx::armem::server::robot_state
+{
+
+    struct RobotUnitData
+    {
+        struct RobotUnitDataGroup
+        {
+            armem::Time timestamp;
+            std::string name;
+            aron::datanavigator::DictNavigatorPtr data;
+        };
+
+        std::map<std::string, RobotUnitDataGroup> groups;
+    };
+
+}
+
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..58b688557eb86e8efb28afd0329d8f3b50e8508c
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.cpp
@@ -0,0 +1,201 @@
+#include "RobotUnitReader.h"
+
+#include "aron_conversions.h"
+
+#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h>
+#include <RobotAPI/libraries/aron/core/navigator/data/primitive/Long.h>
+#include <RobotAPI/libraries/aron/core/navigator/data/primitive/String.h>
+
+#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h>
+
+#include <ArmarXCore/core/time/CycleUtil.h>
+#include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
+
+#include <SimoxUtility/algorithm/string/string_tools.h>
+
+#include <istream>
+#include <filesystem>
+#include <fstream>
+
+
+namespace armarx::armem::server::robot_state
+{
+
+    std::map<std::string, std::string> RobotUnitReader::readConfig(const std::string& configPath)
+    {
+        ARMARX_CHECK(std::filesystem::is_regular_file(configPath));
+
+        std::map<std::string, std::string> config;
+
+        std::ifstream in(configPath);
+        std::string line;
+        while (std::getline(in, line))
+        {
+            if (line.size() > 0)
+            {
+                if (simox::alg::starts_with(line, "//"))
+                {
+                    continue;
+                }
+
+                std::vector<std::string> mappings = simox::alg::split(line, ",");
+                for (const auto& mapping : mappings)
+                {
+                    std::vector<std::string> split = simox::alg::split(mapping, "=>");
+                    if (split.size() < 2)
+                    {
+                        ARMARX_WARNING_S << "A mapping in the RobotUnitConfig file is not valid. Ignoring it: " << mapping;
+                        continue;
+                    }
+                    for (unsigned int i = 0; i < split.size() - 1; ++i)
+                    {
+                        ARMARX_DEBUG_S << "Setting sensor value '" << split[i] << "' to group '" << split[split.size() - 1] << "'.";
+                        config.emplace(split[i], split[split.size() - 1]);
+                    }
+                }
+            }
+        }
+
+        return config;
+    }
+
+
+    void RobotUnitReader::connect(
+        armarx::plugins::RobotUnitComponentPlugin& robotUnitPlugin,
+        armarx::plugins::DebugObserverComponentPlugin& debugObserverPlugin)
+    {
+        {
+            config.loggingNames.push_back(properties.sensorPrefix);
+            receiver = robotUnitPlugin.startDataSatreming(config);
+            description = receiver->getDataDescription();
+        }
+        {
+            // Thread-local copy of debug observer helper.
+            debugObserver = DebugObserverHelper(
+                                Logging::tag.tagName, debugObserverPlugin.getDebugObserver(), true
+                            );
+        }
+    }
+
+
+    void RobotUnitReader::run(
+        float pollFrequency,
+        std::queue<RobotUnitData>& dataQueue,
+        std::mutex& dataMutex)
+    {
+        CycleUtil cycle(static_cast<int>(1000.f / pollFrequency));
+        while (task and not task->isStopped())
+        {
+            if (std::optional<RobotUnitData> data = fetchAndGroupLatestRobotUnitData())
+            {
+                std::lock_guard g{dataMutex};
+                dataQueue.push(data.value());
+            }
+
+            if (debugObserver)
+            {
+                debugObserver->sendDebugObserverBatch();
+            }
+            cycle.waitForCycleDuration();
+        }
+    }
+
+
+    std::optional<RobotUnitData> RobotUnitReader::fetchAndGroupLatestRobotUnitData()
+    {
+        const std::optional<RobotUnitDataStreaming::TimeStep> data = fetchLatestData();
+        if (not data.has_value())
+        {
+            return std::nullopt;
+        }
+
+        ARMARX_DEBUG << "RobotUnitData: Generating new aron map for current timestep";
+        auto start = std::chrono::high_resolution_clock::now();
+
+        RobotUnitData convertedAndGroupedData;
+        for (const auto& [nameEntry, dataEntry] : description.entries)
+        {
+            std::string name = nameEntry;
+            std::string jointOrWhateverName;
+
+            std::string groupName = "";
+            if (auto it = configSensorMapping.find(name); it != configSensorMapping.end())
+            {
+                groupName = it->second;
+            }
+            else
+            {
+                const size_t first_dot_pos = name.find(".");
+                const size_t second_dot_pos = name.find(".", first_dot_pos + 1); // find second occurence of "."
+                if (second_dot_pos == std::string::npos)
+                {
+                    ARMARX_WARNING << "Could not find a groupname for the sensor with name " << name << ". "
+                                   << "All sensors must be called sens.X.Y where X is the name of the group "
+                                   << "and Y is the actual sensor. Ignoring this sensor.";
+                    continue;
+                }
+                groupName = name.substr(0, second_dot_pos);
+
+                jointOrWhateverName = name.substr(first_dot_pos + 1, second_dot_pos - first_dot_pos - 1);
+
+                name = name.substr(second_dot_pos + 1); // remove the groupName, TODO check if +1 is valid
+            }
+
+            auto it = convertedAndGroupedData.groups.find(groupName);
+            if (it == convertedAndGroupedData.groups.end())
+            {
+                namespace adn = aron::datanavigator;
+
+                // Generate new dict for the group
+                RobotUnitData::RobotUnitDataGroup group;
+                group.timestamp = armem::Time::microSeconds(data->timestampUSec);
+                group.name = groupName;
+                group.data = std::make_shared<adn::DictNavigator>();
+
+                group.data->addElement("EncoderGroupName", std::make_shared<adn::StringNavigator>(groupName));
+                group.data->addElement("IterationId", std::make_shared<adn::LongNavigator>(data->iterationId));
+                group.data->addElement("name", std::make_shared<adn::StringNavigator>(jointOrWhateverName));
+
+                auto r = convertedAndGroupedData.groups.emplace(groupName, group);
+                it = r.first;
+            }
+
+            RobotUnitData::RobotUnitDataGroup& group = it->second;
+            group.data->addElement(name, RobotUnitDataStreaming::toAron(data.value(), dataEntry));
+        }
+
+        auto stop = std::chrono::high_resolution_clock::now();
+        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
+        ARMARX_DEBUG << "RobotUnitData: The total time needed to convert the data to aron is: " << duration;
+
+        if (debugObserver)
+        {
+            debugObserver->setDebugObserverDatafield("RobotUnitReader | t Read+Group [ms]", duration.count() / 1000.f);
+        }
+
+        return convertedAndGroupedData;
+    }
+
+
+    std::optional<RobotUnitDataStreaming::TimeStep> RobotUnitReader::fetchLatestData()
+    {
+        std::deque<RobotUnitDataStreaming::TimeStep>& data = receiver->getDataBuffer();
+        if (debugObserver)
+        {
+            debugObserver->setDebugObserverDatafield("RobotUnitReader | Buffer Size", data.size());
+        }
+        if (data.empty())
+        {
+            return std::nullopt;
+        }
+        else
+        {
+            RobotUnitDataStreaming::TimeStep currentTimestep = data.back();
+            data.clear();
+            return currentTimestep;
+        }
+    }
+
+
+}
+
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.h b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.h
new file mode 100644
index 0000000000000000000000000000000000000000..a44c9c0f19a586f48b3f899ae314ca70cc223abe
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <queue>
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include <ArmarXCore/core/logging/Logging.h>
+#include <ArmarXCore/core/services/tasks/TaskUtil.h>
+#include <ArmarXCore/libraries/DebugObserverHelper/DebugObserverHelper.h>
+
+
+#include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotUnitComponentPlugin.h>
+
+#include "RobotUnitData.h"
+
+
+namespace armarx::plugins
+{
+    class RobotUnitComponentPlugin;
+    class DebugObserverComponentPlugin;
+}
+namespace armarx
+{
+    using RobotUnitDataStreamingReceiverPtr = std::shared_ptr<class RobotUnitDataStreamingReceiver>;
+}
+
+namespace armarx::armem::server::robot_state
+{
+
+    class RobotUnitReader : public armarx::Logging
+    {
+    public:
+
+        static std::map<std::string, std::string> readConfig(const std::string& configPath);
+
+
+    public:
+
+        void connect(armarx::plugins::RobotUnitComponentPlugin& robotUnitPlugin,
+                     armarx::plugins::DebugObserverComponentPlugin& debugObserverPlugin);
+
+
+
+        /// Reads data from `handler` and fills `robotUnitDataQueue`.
+        void run(float pollFrequency,
+                 std::queue<RobotUnitData>& dataQueue,
+                 std::mutex& dataMutex);
+
+        std::optional<RobotUnitData> fetchAndGroupLatestRobotUnitData();
+
+
+    private:
+
+        /// Fetch the latest timestep and clear the robot unit buffer.
+        std::optional<RobotUnitDataStreaming::TimeStep> fetchLatestData();
+
+
+    public:
+
+        struct Properties
+        {
+            std::string sensorPrefix = "sens.*";
+        };
+        Properties properties;
+
+        std::optional<DebugObserverHelper> debugObserver;
+
+
+        RobotUnitDataStreaming::Config config;
+        RobotUnitDataStreamingReceiverPtr receiver;
+        RobotUnitDataStreaming::DataStreamingDescription description;
+
+        /// RobotUnit name -> group name.
+        std::map<std::string, std::string> configSensorMapping;
+
+        armarx::SimpleRunningTask<>::pointer_type task = nullptr;
+
+    };
+
+}
+
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8caf73057ac54df453f6e3c2d67295dc7e69ab89
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.cpp
@@ -0,0 +1,69 @@
+#include "aron_conversions.h"
+
+#include <RobotAPI/libraries/aron/core/navigator/data/AllNavigators.h>
+#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h>
+
+#include <ArmarXCore/core/exceptions/local/UnexpectedEnumValueException.h>
+
+
+namespace armarx
+{
+    aron::datanavigator::NavigatorPtr RobotUnitDataStreaming::toAron(
+        const TimeStep& timestep,
+        const DataEntry& dataEntry)
+    {
+        switch (dataEntry.type)
+        {
+            case RobotUnitDataStreaming::NodeTypeFloat:
+            {
+                float value = RobotUnitDataStreamingReceiver::GetAs<Ice::Float>(timestep, dataEntry);
+                return std::make_shared<aron::datanavigator::FloatNavigator>(value);
+            }
+            case RobotUnitDataStreaming::NodeTypeBool:
+            {
+                bool value = RobotUnitDataStreamingReceiver::GetAs<bool>(timestep, dataEntry);
+                return std::make_shared<aron::datanavigator::BoolNavigator>(value);
+            }
+            case RobotUnitDataStreaming::NodeTypeByte:
+            {
+                int value = RobotUnitDataStreamingReceiver::GetAs<Ice::Byte>(timestep, dataEntry);
+                return std::make_shared<aron::datanavigator::IntNavigator>(value);
+            }
+            case RobotUnitDataStreaming::NodeTypeShort:
+            {
+                int value = RobotUnitDataStreamingReceiver::GetAs<Ice::Short>(timestep, dataEntry);
+                return std::make_shared<aron::datanavigator::IntNavigator>(value);
+            }
+            case RobotUnitDataStreaming::NodeTypeInt:
+            {
+                int value = RobotUnitDataStreamingReceiver::GetAs<Ice::Int>(timestep, dataEntry);
+                return std::make_shared<aron::datanavigator::IntNavigator>(value);
+            }
+            case RobotUnitDataStreaming::NodeTypeLong:
+            {
+                long value = RobotUnitDataStreamingReceiver::GetAs<Ice::Long>(timestep, dataEntry);
+                return std::make_shared<aron::datanavigator::LongNavigator>(value);
+            }
+            case RobotUnitDataStreaming::NodeTypeDouble:
+            {
+                double value = RobotUnitDataStreamingReceiver::GetAs<Ice::Double>(timestep, dataEntry);
+                return std::make_shared<aron::datanavigator::DoubleNavigator>(value);
+            }
+            default:
+                ARMARX_UNEXPECTED_ENUM_VALUE(RobotUnitDataStreaming::NodeType, dataEntry.type);
+        }
+    }
+
+
+    const simox::meta::EnumNames<RobotUnitDataStreaming::DataEntryType> RobotUnitDataStreaming::DataEntryNames =
+    {
+        { NodeTypeBool, "Bool" },
+        { NodeTypeByte, "Byte" },
+        { NodeTypeShort, "Short" },
+        { NodeTypeInt, "Int" },
+        { NodeTypeLong, "Long" },
+        { NodeTypeFloat, "Float" },
+        { NodeTypeDouble, "Double" },
+    };
+
+}
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.h b/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd2177c9e2f167af3946119f381dcd7ec43d170c
--- /dev/null
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <memory>
+
+#include <SimoxUtility/meta/enum/EnumNames.hpp>
+
+#include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h>
+
+
+namespace armarx::aron::datanavigator
+{
+    using NavigatorPtr = std::shared_ptr<class Navigator>;
+}
+
+namespace armarx::RobotUnitDataStreaming
+{
+    aron::datanavigator::NavigatorPtr toAron(
+        const TimeStep& timestep,
+        const DataEntry& dataEntry);
+
+    extern const simox::meta::EnumNames<DataEntryType> DataEntryNames;
+
+}
diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h
index 13b5572d382059bcff17ce2bb5bfb04cc819b04b..b3f71e59ac2b33c19ff97fd0c238f5540503b6ee 100644
--- a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h
+++ b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h
@@ -82,7 +82,7 @@ namespace armarx::armem::robot_state
 
         struct Properties
         {
-            std::string memoryName                = "RobotStateMemory";
+            std::string memoryName                = "RobotState";
             std::string descriptionCoreSegment    = "Description";
             std::string localizationCoreSegment   = "Localization";
             std::string proprioceptionCoreSegment = "Proprioception";