diff --git a/data/RobotAPI/VariantInfo-RobotAPI.xml b/data/RobotAPI/VariantInfo-RobotAPI.xml
index fac11b677edbf7fc26cd2538e453981601e94f13..9d9342ab21b51d11fd188a70c5e4cf6066afff24 100644
--- a/data/RobotAPI/VariantInfo-RobotAPI.xml
+++ b/data/RobotAPI/VariantInfo-RobotAPI.xml
@@ -43,10 +43,18 @@
         <Proxy include="RobotAPI/interface/units/PlatformUnitInterface.h"
             humanName="Platform Unit (obstacle avoiding)"
             typeName="PlatformUnitInterfacePrx"
-            memberName="obstacleAvoidingPlatformUnit"
-            getterName="getObstacleAvoidingPlatformUnit"
-            propertyName="ObstacleAvoidingPlatformUnitName"
+            memberName="platformUnit1"
+            getterName="getPlatformUnit1"
+            propertyName="PlatformUnitName1"
             propertyDefaultValue="ObstacleAvoidingPlatformUnit"
+	    propertyIsOptional="true" />
+         <Proxy include="RobotAPI/interface/units/PlatformUnitInterface.h"
+            humanName="Platform Unit (obstacle aware)"
+            typeName="PlatformUnitInterfacePrx"
+            memberName="platformUnit2"
+            getterName="getPlatformUnit2"
+            propertyName="PlatformUnitName2"
+            propertyDefaultValue="ObstacleAwarePlatformUnit"
             propertyIsOptional="true" />
         <Proxy include="RobotAPI/interface/observers/PlatformUnitObserverInterface.h"
             humanName="Platform Unit Observer"
diff --git a/source/RobotAPI/components/ArViz/Client/elements/Path.cpp b/source/RobotAPI/components/ArViz/Client/elements/Path.cpp
index 0a88d089382eebb915104c24ee41339a18dd2f29..cf5e9899e943dd4076c2dd21ca9fe982b3c1a9bc 100644
--- a/source/RobotAPI/components/ArViz/Client/elements/Path.cpp
+++ b/source/RobotAPI/components/ArViz/Client/elements/Path.cpp
@@ -1,27 +1,21 @@
 #include "Path.h"
 
+#include <iterator>
+
+#include <ArmarXCore/interface/core/BasicVectorTypes.h>
+#include <ArmarXCore/interface/core/BasicVectorTypesHelpers.h>
+
 namespace armarx::viz
 {
 
     Path& Path::clear()
     {
         data_->points.clear();
-        return *this;
-    }
-
-    Path& Path::lineColor(Color color)
-    {
-        data_->lineColor = color;
 
         return *this;
     }
 
-    Path& Path::lineColorGlasbeyLUT(std::size_t id, int alpha)
-    {
-        return lineColor(Color::fromRGBA(simox::color::GlasbeyLUT::at(id, alpha)));
-    }
-
-    Path& Path::lineWidth(float w)
+    Path& Path::width(float w)
     {
         data_->lineWidth = w;
 
@@ -33,17 +27,21 @@ namespace armarx::viz
         auto& points = data_->points;
         points.clear();
         points.reserve(ps.size());
-        for (auto& p : ps)
+
+        std::transform(ps.begin(),
+                       ps.end(),
+                       std::back_inserter(points),
+                       [](const auto & e)
         {
-            points.push_back(armarx::Vector3f{p.x(), p.y(), p.z()});
-        }
+            return ToBasicVectorType(e);
+        });
 
         return *this;
     }
 
     Path& Path::addPoint(Eigen::Vector3f p)
     {
-        data_->points.push_back(armarx::Vector3f{p.x(), p.y(), p.z()});
+        data_->points.emplace_back(ToBasicVectorType(p));
 
         return *this;
     }
diff --git a/source/RobotAPI/components/ArViz/Client/elements/Path.h b/source/RobotAPI/components/ArViz/Client/elements/Path.h
index 50eec6abe454bbf56bc45053c4d35f5e41ca5bc6..7cfbecbd973a194cef0e0e350a5c7c3c1aedbc13 100644
--- a/source/RobotAPI/components/ArViz/Client/elements/Path.h
+++ b/source/RobotAPI/components/ArViz/Client/elements/Path.h
@@ -35,17 +35,7 @@ namespace armarx::viz
 
         Path& clear();
 
-        Path& lineColor(Color color);
-
-        template<class...Ts>
-        Path& lineColor(Ts&& ...ts)
-        {
-            return lineColor({std::forward<Ts>(ts)...});
-        }
-
-        Path& lineColorGlasbeyLUT(std::size_t id, int alpha = 255);
-
-        Path& lineWidth(float w);
+        Path& width(float w);
 
         Path& points(std::vector<Eigen::Vector3f> const& ps);
 
diff --git a/source/RobotAPI/components/ArViz/Coin/VisualizationPath.cpp b/source/RobotAPI/components/ArViz/Coin/VisualizationPath.cpp
index 3018a3af51e25222b97c0c60ed06fea65a8c5822..a0190abd75a8438a2371c728a580837d68c3eecc 100644
--- a/source/RobotAPI/components/ArViz/Coin/VisualizationPath.cpp
+++ b/source/RobotAPI/components/ArViz/Coin/VisualizationPath.cpp
@@ -1,9 +1,12 @@
 #include "VisualizationPath.h"
 
+#include <algorithm>
+
 #include <Inventor/SbVec3f.h>
 #include <Inventor/nodes/SoCoordinate3.h>
 #include <Inventor/nodes/SoDrawStyle.h>
 #include <Inventor/nodes/SoLineSet.h>
+#include <Inventor/nodes/SoPointSet.h>
 
 namespace armarx::viz::coin
 {
@@ -27,19 +30,36 @@ namespace armarx::viz::coin
 
         node->addChild(coordinate3);
         node->addChild(lineSep);
+
+        // points (use the following, if the points should be drawn in a different color)
+
+        // pclMat = new SoMaterial;
+
+        // SoMaterialBinding* pclMatBind = new SoMaterialBinding;
+        // pclMatBind->value = SoMaterialBinding::PER_PART;
+
+        pclCoords = new SoCoordinate3;
+        pclStyle = new SoDrawStyle;
+
+        // node->addChild(pclMat);
+        // node->addChild(pclMatBind);
+        node->addChild(pclCoords);
+        node->addChild(pclStyle);
+        node->addChild(new SoPointSet);
     }
 
     bool VisualizationPath::update(ElementType const& element)
     {
         // set position
-        coordinate3->point.setValuesPointer(element.points.size(), reinterpret_cast<const float*>(element.points.data()));
+        coordinate3->point.setValuesPointer(element.points.size(),
+                                            reinterpret_cast<const float*>(element.points.data()));
 
         // set color
-        const auto lineColor = element.lineColor;
+        const auto lineColor = element.color;
 
         constexpr float toUnit = 1.0F / 255.0F;
 
-        const auto color = SbVec3f(lineColor.r, lineColor.g, lineColor.b) * toUnit;
+        const auto color         = SbVec3f(lineColor.r, lineColor.g, lineColor.b) * toUnit;
         const float transparency = 1.0F - static_cast<float>(lineColor.a) * toUnit;
 
         lineMaterial->diffuseColor.setValue(color);
@@ -58,6 +78,20 @@ namespace armarx::viz::coin
         const int pointSize = static_cast<int>(element.points.size());
         lineSet->numVertices.set1Value(0, pointSize);
 
+        // points at each node
+        // const std::vector<SbVec3f> colorData(element.points.size(), color);
+
+        // pclMat->diffuseColor.setValuesPointer(colorData.size(),
+        //                                       reinterpret_cast<const float*>(colorData.data()));
+        // pclMat->ambientColor.setValuesPointer(colorData.size(),
+        //                                       reinterpret_cast<const float*>(colorData.data()));
+        // pclMat->transparency = transparency;
+
+        pclCoords->point.setValuesPointer(element.points.size(),
+                                          reinterpret_cast<const float*>(element.points.data()));
+
+        pclStyle->pointSize = element.lineWidth + 5;
+
         return true;
     }
 } // namespace armarx::viz::coin
\ No newline at end of file
diff --git a/source/RobotAPI/components/ArViz/Coin/VisualizationPath.h b/source/RobotAPI/components/ArViz/Coin/VisualizationPath.h
index e1e446bbe66bc49a6d0c4bdf1999c7f0615f06ec..487ee9df58baeb6b91e91de6fdb3be873511a0af 100644
--- a/source/RobotAPI/components/ArViz/Coin/VisualizationPath.h
+++ b/source/RobotAPI/components/ArViz/Coin/VisualizationPath.h
@@ -39,9 +39,17 @@ namespace armarx::viz::coin
 
         bool update(ElementType const& element);
 
+        /// lines
         SoCoordinate3* coordinate3;
         SoDrawStyle* lineStyle;
         SoLineSet* lineSet;
         SoMaterial* lineMaterial;
+
+
+        /// points
+        // SoMaterial* pclMat;
+        SoCoordinate3* pclCoords;
+        SoDrawStyle* pclStyle;
+
     };
 }  // namespace armarx::viz::coin
diff --git a/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp b/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp
index e712e30ccb2978fdc7caa0210b919773b7dce46b..41fc3e85f7a6a7250c47fe58c0730973a16480be 100644
--- a/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp
+++ b/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp
@@ -22,6 +22,8 @@
 
 #include "DynamicObstacleManager.h"
 
+#include <VirtualRobot/MathTools.h>
+
 // STD/STL
 #include <string>
 #include <vector>
@@ -118,6 +120,7 @@ namespace armarx
 
     void DynamicObstacleManager::add_decayable_line_segment(const Eigen::Vector2f& line_start, const Eigen::Vector2f& line_end, const Ice::Current&)
     {
+        TIMING_START(add_decayable_line_segment);
         const Eigen::Vector2f difference_vec = line_end - line_start;
         const float length = difference_vec.norm();
         const Eigen::Vector2f center_vec = line_start + 0.5 * difference_vec;
@@ -136,10 +139,10 @@ namespace armarx
             return;
         }
 
-
         {
             std::shared_lock<std::shared_mutex> l(m_managed_obstacles_mutex);
 
+            TIMING_START(add_decayable_line_segment_mutex);
             // First check own obstacles
             for (ManagedObstaclePtr& managed_obstacle : m_managed_obstacles)
             {
@@ -155,13 +158,15 @@ namespace armarx
                     managed_obstacle->line_matches.push_back(line_start);
                     managed_obstacle->line_matches.push_back(line_end);
                     managed_obstacle->m_last_matched = IceUtil::Time::now();
+                    TIMING_END(add_decayable_line_segment_mutex);
                     return;
                 }
             }
+            TIMING_END(add_decayable_line_segment_mutex);
         }
 
         // Second check the obstacles from the obstacle avoidance
-        for (const auto& published_obstacle : m_obstacle_detection->getObstacles())
+        /*for (const auto& published_obstacle : m_obstacle_detection->getObstacles())
         {
             float coverage = ManagedObstacle::line_segment_ellipsis_coverage(
             {published_obstacle.posX, published_obstacle.posY}, published_obstacle.axisLengthX, published_obstacle.axisLengthY, published_obstacle.yaw,
@@ -172,7 +177,7 @@ namespace armarx
                 ARMARX_DEBUG << "Found the obstacle in the static obstacle list! Matching name: " << published_obstacle.name;
                 return;
             }
-        }
+        }*/
 
         ARMARX_DEBUG << " No matching obstacle found. Create new obstacle and add to list";
         ManagedObstaclePtr new_managed_obstacle(new ManagedObstacle());
@@ -198,6 +203,15 @@ namespace armarx
             std::lock_guard<std::shared_mutex> l(m_managed_obstacles_mutex);
             m_managed_obstacles.push_back(new_managed_obstacle);
         }
+        TIMING_END(add_decayable_line_segment);
+    }
+
+    void DynamicObstacleManager::add_decayable_line_segments(const dynamicobstaclemanager::LineSegments& lines, const Ice::Current& c)
+    {
+        for (const auto& line : lines)
+        {
+            add_decayable_line_segment(line.lineStart, line.lineEnd, c);
+        }
     }
 
 
@@ -218,7 +232,7 @@ namespace armarx
         std::lock_guard l(m_managed_obstacles_mutex);
 
         ARMARX_DEBUG << "Remove Obstacle " << name << " from obstacle map and from obstacledetection";
-        for (ManagedObstaclePtr& managed_obstacle : m_managed_obstacles)
+        for (ManagedObstaclePtr managed_obstacle : m_managed_obstacles)
         {
             if (managed_obstacle->m_obstacle.name == name)
             {
@@ -240,6 +254,36 @@ namespace armarx
     }
 
 
+    float
+    DynamicObstacleManager::distanceToObstacle(const Eigen::Vector2f& agentPosition, float safetyRadius, const Eigen::Vector2f& goal, const Ice::Current&)
+    {
+        std::shared_lock<std::shared_mutex> l{m_managed_obstacles_mutex};
+
+        const float sample_step = 5; // in [mm], sample step size towards goal.
+        const float distance_to_goal = (goal - agentPosition).norm() + safetyRadius;
+        float current_distance = safetyRadius;
+
+        while (current_distance < distance_to_goal)
+        {
+            for (const auto man_obstacle : m_managed_obstacles)
+            {
+                Eigen::Vector2f sample = agentPosition + ((goal - agentPosition).normalized() * current_distance);
+                obstacledetection::Obstacle obstacle = man_obstacle->m_obstacle;
+                Eigen::Vector2f obstacle_origin{obstacle.posX, obstacle.posY};
+
+                if (ManagedObstacle::point_ellipsis_coverage(obstacle_origin, obstacle.axisLengthX, obstacle.axisLengthY, obstacle.yaw, sample))
+                {
+                    return current_distance - safetyRadius;
+                }
+            }
+
+            current_distance += sample_step;
+        }
+
+        return std::numeric_limits<float>::infinity();
+    }
+
+
     void DynamicObstacleManager::directly_update_obstacle(const std::string& name, const Eigen::Vector2f& obs_pos, float e_rx, float e_ry, float e_yaw, const Ice::Current&)
     {
         obstacledetection::Obstacle new_unmanaged_obstacle;
@@ -283,7 +327,6 @@ namespace armarx
             }
         }
 
-
         // Update obstacle avoidance
         int checked_obstacles = 0;
         bool updated = false;
diff --git a/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.h b/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.h
index 1629b7efe3102dccf85521ac0fac6379e09225cc..5f2d8065a307706ea16789930c12f5165d6044eb 100644
--- a/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.h
+++ b/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.h
@@ -69,10 +69,12 @@ namespace armarx
 
         void add_decayable_obstacle(const Eigen::Vector2f&, float, float, float, const Ice::Current& = Ice::Current()) override;
         void add_decayable_line_segment(const Eigen::Vector2f&, const Eigen::Vector2f&, const Ice::Current& = Ice::Current()) override;
+        void add_decayable_line_segments(const dynamicobstaclemanager::LineSegments& lines, const Ice::Current& = Ice::Current()) override;
         void remove_all_decayable_obstacles(const Ice::Current& = Ice::Current()) override;
         void directly_update_obstacle(const std::string& name, const Eigen::Vector2f&, float, float, float, const Ice::Current& = Ice::Current()) override;
         void remove_obstacle(const std::string& name, const Ice::Current& = Ice::Current()) override;
         void wait_unitl_obstacles_are_ready(const Ice::Current& = Ice::Current()) override;
+        float distanceToObstacle(const Eigen::Vector2f& agentPosition, float safetyRadius, const Eigen::Vector2f& goal, const Ice::Current& = Ice::Current()) override;
 
     protected:
         void onInitComponent() override;
diff --git a/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.cpp b/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.cpp
index fe21cc4e46558b3a70102592ee77ef56715421f8..f718a45434ec538fbd5d2541fb9413568f3e79f0 100644
--- a/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.cpp
+++ b/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.cpp
@@ -94,10 +94,25 @@ namespace armarx
     float ManagedObstacle::line_segment_ellipsis_coverage(const Eigen::Vector2f e_origin, const float e_rx, const float e_ry, const float e_yaw, const Eigen::Vector2f line_seg_start, const Eigen::Vector2f line_seg_end)
     {
         // Again we discretize the line into n points and we check the coverage of those points
-        const unsigned int SAMPLES = 40;
         const Eigen::Vector2f difference_vec = line_seg_end - line_seg_start;
         const Eigen::Vector2f difference_vec_normed = difference_vec.normalized();
         const float distance = difference_vec.norm();
+        const unsigned int SAMPLES = std::max(distance / 10.0, 40.0);
+
+        const Vector2f difference_start_origin = e_origin - line_seg_start;
+        const Vector2f difference_end_origin = e_origin - line_seg_end;
+
+        if (difference_start_origin.norm() > std::max(e_rx, e_ry) && distance < std::max(e_rx, e_ry))
+        {
+            return 0.0;
+        }
+
+        if (difference_end_origin.norm() > std::max(e_rx, e_ry) && distance < std::max(e_rx, e_ry))
+        {
+            return 0.0;
+        }
+
+
         const float step_size = distance / SAMPLES;
         const Eigen::Vector2f dir = difference_vec_normed * step_size;
 
diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp
index e43cbbc7c728405c745783a231484dd9cd154ebb..8c41ace39ea53283c22b3a191ae9cd9ebba2ce3c 100644
--- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp
+++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp
@@ -70,9 +70,15 @@ namespace armarx::armem::server::robot_state
 
     armarx::PropertyDefinitionsPtr RobotStateMemory::createPropertyDefinitions()
     {
+        armarx::PropertyDefinitionsPtr defs = new ComponentPropertyDefinitions(getConfigIdentifier());
+
+        const std::string prefix = "mem.";
+
+        workingMemory.name() = "RobotState";
+        defs->optional(workingMemory.name(), prefix + "MemoryName", "Name of this memory server.");
+
         const std::string robotUnitPrefix{sensorValuePrefix};
 
-        armarx::PropertyDefinitionsPtr defs = new ComponentPropertyDefinitions(getConfigIdentifier());
         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));
@@ -123,7 +129,7 @@ namespace armarx::armem::server::robot_state
 
         ArmarXDataPath::getAbsolutePath(robotUnitConfigPath, robotUnitConfigPath, includePaths);
 
-        workingMemory.name() = workingMemoryName;
+        // workingMemory.name() = workingMemoryName;
     }
 
 
diff --git a/source/RobotAPI/components/units/CMakeLists.txt b/source/RobotAPI/components/units/CMakeLists.txt
index 51d1930b83753c195ff7558154092ea025466e4c..3fa53590170e0ff9d288f7f130adae9814ee7446 100644
--- a/source/RobotAPI/components/units/CMakeLists.txt
+++ b/source/RobotAPI/components/units/CMakeLists.txt
@@ -75,6 +75,7 @@ set(LIB_FILES
 armarx_add_library("${LIB_NAME}"  "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}")
 
 add_subdirectory(ObstacleAvoidingPlatformUnit)
+add_subdirectory(ObstacleAwarePlatformUnit)
 add_subdirectory(relays)
 add_subdirectory(RobotUnit)
 
diff --git a/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/CMakeLists.txt b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..524b650bf5a3767a92a457ab97b26ffcdd903a40
--- /dev/null
+++ b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/CMakeLists.txt
@@ -0,0 +1,10 @@
+armarx_add_component(
+    COMPONENT_NAME  ObstacleAwarePlatformUnit
+    HEADERS         ObstacleAwarePlatformUnit.h
+    SOURCES         ObstacleAwarePlatformUnit.cpp
+    COMPONENT_LIBS  ArmarXCoreComponentPlugins
+                    RobotAPICore
+                    RobotAPIComponentPlugins
+                    RobotUnit
+)
+armarx_add_component_executable("main.cpp")
diff --git a/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.cpp b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2acb4a23b423df17ca7f80ffc76251634bcb933f
--- /dev/null
+++ b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.cpp
@@ -0,0 +1,728 @@
+/*
+ * 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::ObstacleAwarePlatformUnit
+ * @author     Christian R. G. Dreher <c.dreher@kit.edu>
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+#include <RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.h>
+
+
+// STD/STL
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <numeric>
+
+// Eigen
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+// Simox
+#include <SimoxUtility/math.h>
+
+// ArmarX
+#include <ArmarXCore/observers/variant/Variant.h>
+
+
+namespace
+{
+
+    void
+    invalidate(float& v)
+    {
+        v = std::numeric_limits<float>::infinity();
+    }
+
+
+    void
+    invalidate(Eigen::Vector2f& v)
+    {
+        v.x() = std::numeric_limits<float>::infinity();
+        v.y() = std::numeric_limits<float>::infinity();
+    }
+
+
+    void
+    invalidate(Eigen::Vector3f& v)
+    {
+        v.x() = std::numeric_limits<float>::infinity();
+        v.y() = std::numeric_limits<float>::infinity();
+        v.z() = std::numeric_limits<float>::infinity();
+    }
+
+    template<typename T>
+    void invalidate(std::deque<T>& d)
+    {
+        d.clear();
+    }
+
+
+    std::string
+    to_string(armarx::ObstacleAwarePlatformUnit::control_mode mode)
+    {
+        switch (mode)
+        {
+            case armarx::ObstacleAwarePlatformUnit::control_mode::position:
+                return "position";
+            case armarx::ObstacleAwarePlatformUnit::control_mode::velocity:
+                return "velocity";
+            case armarx::ObstacleAwarePlatformUnit::control_mode::none:
+            default:
+                return "unset";
+        }
+    }
+
+}
+
+
+const std::string
+armarx::ObstacleAwarePlatformUnit::default_name = "ObstacleAwarePlatformUnit";
+
+
+armarx::ObstacleAwarePlatformUnit::ObstacleAwarePlatformUnit() = default;
+
+
+armarx::ObstacleAwarePlatformUnit::~ObstacleAwarePlatformUnit() = default;
+
+
+void
+armarx::ObstacleAwarePlatformUnit::onInitPlatformUnit()
+{
+    ARMARX_DEBUG << "Initializing " << getName() << ".";
+
+    ARMARX_DEBUG << "Initialized " << getName() << ".";
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::onStartPlatformUnit()
+{
+    ARMARX_DEBUG << "Starting " << getName() << ".";
+
+    if (!hasRobot("robot"))
+    {
+        m_robot = addRobot("robot", VirtualRobot::RobotIO::eStructure);
+    }
+
+    invalidate(m_control_data.target_vel);
+    invalidate(m_control_data.target_rot_vel);
+    invalidate(m_control_data.target_pos);
+    invalidate(m_control_data.target_ori);
+    invalidate(m_viz.start);
+
+    ARMARX_DEBUG << "Started " << getName() << ".";
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::onExitPlatformUnit()
+{
+    ARMARX_DEBUG << "Exiting " << getName() << ".";
+
+    schedule_high_level_control_loop(control_mode::none);
+
+    ARMARX_DEBUG << "Exited " << getName() << ".";
+}
+
+
+std::string
+armarx::ObstacleAwarePlatformUnit::getDefaultName()
+const
+{
+    return default_name;
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::moveTo(
+    const float target_pos_x,
+    const float target_pos_y,
+    const float target_ori,
+    const float pos_reached_threshold,
+    const float ori_reached_threshold,
+    const Ice::Current&)
+{
+    using namespace simox::math;
+
+    std::scoped_lock l{m_control_data.mutex};
+
+    m_control_data.target_pos = Eigen::Vector2f{target_pos_x, target_pos_y};
+    m_control_data.target_ori = periodic_clamp<float>(target_ori, -M_PI, M_PI);
+    m_control_data.pos_reached_threshold = pos_reached_threshold;
+    m_control_data.ori_reached_threshold = ori_reached_threshold;
+
+    invalidate(m_control_data.target_vel);
+    invalidate(m_control_data.target_rot_vel);
+
+    schedule_high_level_control_loop(control_mode::position);
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::move(
+    const float target_vel_x,
+    const float target_vel_y,
+    const float target_rot_vel,
+    const Ice::Current&)
+{
+    using namespace simox::math;
+
+    std::scoped_lock l{m_control_data.mutex};
+
+    m_control_data.target_vel = Eigen::Vector2f{target_vel_x, target_vel_y};
+    m_control_data.target_rot_vel = periodic_clamp<float>(target_rot_vel, -M_PI, M_PI);
+
+    invalidate(m_control_data.target_pos);
+    invalidate(m_control_data.target_ori);
+
+    ARMARX_CHECK(m_control_data.target_vel.allFinite());
+    ARMARX_CHECK(std::isfinite(m_control_data.target_rot_vel));
+
+    schedule_high_level_control_loop(control_mode::velocity);
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::moveRelative(
+    const float target_pos_delta_x,
+    const float target_pos_delta_y,
+    const float target_delta_ori,
+    const float pos_reached_threshold,
+    const float ori_reached_threshold,
+    const Ice::Current& current)
+{
+    using namespace simox::math;
+
+    // Transform relative to global.
+    std::unique_lock lock{m_control_data.mutex};
+    synchronizeLocalClone(m_robot);
+    const Eigen::Vector2f agent_pos = m_robot->getGlobalPosition().head<2>();
+    const float agent_ori = mat4f_to_rpy(m_robot->getGlobalPose()).z();
+    lock.unlock();
+
+    // Use moveTo.
+    moveTo(
+        agent_pos.x() + target_pos_delta_x,
+        agent_pos.y() + target_pos_delta_y,
+        agent_ori + target_delta_ori,
+        pos_reached_threshold,
+        ori_reached_threshold,
+        current);
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::setMaxVelocities(
+    const float max_pos_vel,
+    const float max_rot_vel,
+    const Ice::Current&)
+{
+    std::scoped_lock l{m_control_data.mutex};
+    m_control_data.max_vel = max_pos_vel;
+    m_control_data.max_rot_vel = max_rot_vel;
+    m_platform->setMaxVelocities(max_pos_vel, max_rot_vel);
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::stopPlatform(const Ice::Current&)
+{
+    schedule_high_level_control_loop(control_mode::none);
+    m_platform->stopPlatform();
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::schedule_high_level_control_loop(control_mode mode)
+{
+    std::scoped_lock l{m_control_loop.mutex};
+
+    // If the control mode didn't change, there's nothing to do.
+    if (m_control_loop.mode == mode)
+    {
+        return;
+    }
+
+    // If a control loop is runnung, stop it and wait for termination.
+    if (m_control_loop.mode != mode and m_control_loop.task)
+    {
+        ARMARX_VERBOSE << "Stopping " << ::to_string(m_control_loop.mode) << " control.";
+        const bool join = true;
+        m_control_loop.task->stop(join);
+    }
+
+    // If the new control mode is none, no new control loop needs to be created.
+    if (mode == control_mode::none)
+    {
+        ARMARX_VERBOSE << "Going into stand-by.";
+        return;
+    }
+
+    // Start next control loop.
+    ARMARX_VERBOSE << "Starting " << ::to_string(mode) << " control.";
+    m_control_loop.mode = mode;
+    m_control_loop.task = new RunningTask<ObstacleAwarePlatformUnit>(
+        this,
+        &ObstacleAwarePlatformUnit::high_level_control_loop);
+    m_control_loop.task->start();
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::high_level_control_loop()
+{
+    const control_mode mode = m_control_loop.mode;
+    ARMARX_VERBOSE << "Started " << ::to_string(mode) << " control.";
+
+    try
+    {
+        CycleUtil cu{m_control_loop.cycle_time};
+        while (not m_control_loop.task->isStopped())
+        {
+            const velocities vels = get_velocities();
+
+            // Debug.
+            StringVariantBaseMap m;
+            m["err_dist"] = new Variant{vels.err_dist};
+            m["err_angular_dist"] = new Variant{vels.err_angular_dist};
+
+            m["target_global_x"] = new Variant{vels.target_global.x()};
+            m["target_global_y"] = new Variant{vels.target_global.y()};
+            m["target_global_abs"] = new Variant{vels.target_global.norm()};
+
+            m["target_local_x"] = new Variant{vels.target_local.x()};
+            m["target_local_y"] = new Variant{vels.target_local.y()};
+            m["target_local_abs"] = new Variant(vels.target_local.norm());
+            m["target_rot"] = new Variant{vels.target_rot};
+
+            m["modulated_global_x"] = new Variant{vels.modulated_global.x()};
+            m["modulated_global_y"] = new Variant{vels.modulated_global.y()};
+            m["modulated_global_abs"] = new Variant{vels.modulated_global.norm()};
+
+            m["modulated_local_x"] = new Variant{vels.modulated_local.x()};
+            m["modulated_local_y"] = new Variant{vels.modulated_local.y()};
+            m["modulated_local_abs"] = new Variant{vels.modulated_local.norm()};
+
+            setDebugObserverChannel("ObstacleAvoidingPlatformCtrl", m);
+
+            m_platform->move(vels.modulated_local.x(), vels.modulated_local.y(), vels.target_rot);
+            visualize(vels);
+            cu.waitForCycleDuration();
+        }
+    }
+    catch (const std::exception& e)
+    {
+        ARMARX_ERROR << "Error occured while running control loop.\n"
+                     << e.what();
+    }
+    catch (...)
+    {
+        ARMARX_ERROR << "Unknown error occured while running control loop.";
+    }
+
+    m_platform->move(0, 0, 0);
+    m_platform->stopPlatform();
+    m_control_loop.mode = control_mode::none;
+
+    visualize();
+
+    ARMARX_VERBOSE << "Stopped " << ::to_string(mode) << " control.";
+}
+
+
+armarx::ObstacleAwarePlatformUnit::velocities
+armarx::ObstacleAwarePlatformUnit::get_velocities()
+{
+    using namespace simox::math;
+
+    // Acquire control_data mutex to ensure data remains consistent.
+    std::scoped_lock l{m_control_data.mutex};
+    // Update agent and get target velocities.
+    update_agent_dependent_values();
+    const Eigen::Vector2f target_vel = get_target_velocity();
+    const float target_rot_vel = get_target_rotational_velocity();
+
+    const Eigen::Vector2f extents = Eigen::Affine3f(m_robot->getRobotNode("PlatformCornerFrontLeft")->getPoseInRootFrame()).translation().head<2>();
+
+    // Apply obstacle avoidance and apply post processing to get the modulated velocity.
+    const Eigen::Vector2f modulated_vel = [this, &target_vel, &extents]
+    {
+        const VirtualRobot::Circle circle = VirtualRobot::projectedBoundingCircle(*m_robot);
+        ARMARX_DEBUG << "Circle approximation: " << circle.radius;
+        m_viz.boundingCircle = circle;
+        const float distance = m_obsman->distanceToObstacle(m_control_data.agent_pos /*circle.center*/, circle.radius, m_control_data.target_pos);
+
+        ARMARX_DEBUG << "Distance to obstacle: " << distance;
+
+        // https://www.wolframalpha.com/input/?i=line+through+%281000%2C+0%29+and+%282000%2C+1%29
+        float modifier = std::clamp((distance / 1000) - 1., 0.0, 1.0);
+
+        const Eigen::Vector2f vel = modifier * target_vel;
+        return vel.norm() > 20 ? vel : Eigen::Vector2f{0, 0};
+    }();
+
+    ARMARX_CHECK(modulated_vel.allFinite()) << "Velocity contains non-finite values.";
+    ARMARX_CHECK(std::isfinite(target_rot_vel)) << "Rotation velocity is non-finite.";
+
+    ARMARX_DEBUG << "Target velocity:  " << target_vel.transpose()
+                 << "; norm: " << target_vel.norm() << "; " << target_rot_vel;
+    ARMARX_DEBUG << "Modulated velocity:  " << modulated_vel.transpose() << modulated_vel.norm();
+
+    const auto r = Eigen::Rotation2D(m_control_data.agent_ori).toRotationMatrix().inverse();
+
+    velocities vels;
+    vels.target_local = r * target_vel;
+    vels.target_global = target_vel;
+    vels.modulated_local = r * modulated_vel;
+    vels.modulated_global = modulated_vel;
+    vels.target_rot = target_rot_vel;
+    vels.err_dist = m_control_data.target_dist;
+    vels.err_angular_dist = m_control_data.target_angular_dist;
+
+    return vels;
+}
+
+
+Eigen::Vector2f
+armarx::ObstacleAwarePlatformUnit::get_target_velocity()
+const
+{
+    using namespace simox::math;
+
+    Eigen::Vector2f uncapped_target_vel = Eigen::Vector2f::Zero();
+
+    if (m_control_loop.mode == control_mode::position /*and not target_position_reached()*/)
+    {
+        uncapped_target_vel =
+            (m_control_data.target_pos - m_control_data.agent_pos) * m_control_data.kp;
+    }
+    else if (m_control_loop.mode == control_mode::velocity)
+    {
+        uncapped_target_vel = m_control_data.target_vel;
+    }
+
+    ARMARX_CHECK(uncapped_target_vel.allFinite());
+
+    return norm_max(uncapped_target_vel, m_control_data.max_vel);
+}
+
+
+float
+armarx::ObstacleAwarePlatformUnit::get_target_rotational_velocity()
+const
+{
+    using namespace simox::math;
+
+    float uncapped_target_rot_vel = 0;
+
+    if (m_control_loop.mode == control_mode::position /*and not target_orientation_reached()*/)
+    {
+        m_rot_pid_controller.update(m_control_data.target_angular_dist, 0);
+        uncapped_target_rot_vel = -m_rot_pid_controller.getControlValue();
+    }
+    else if (m_control_loop.mode == control_mode::velocity)
+    {
+        uncapped_target_rot_vel = m_control_data.target_rot_vel;
+    }
+
+    ARMARX_CHECK(std::isfinite(uncapped_target_rot_vel));
+
+    return std::copysign(std::min(std::fabs(uncapped_target_rot_vel), m_control_data.max_rot_vel),
+                         uncapped_target_rot_vel);
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::update_agent_dependent_values()
+{
+    using namespace simox::math;
+
+    synchronizeLocalClone(m_robot);
+    m_control_data.agent_pos = m_robot->getGlobalPosition().head<2>();
+    m_control_data.agent_ori =
+        periodic_clamp<float>(mat4f_to_rpy(m_robot->getGlobalPose()).z(), -M_PI, M_PI);
+
+    ARMARX_CHECK_GREATER(m_control_data.agent_ori, -M_PI);
+    ARMARX_CHECK_LESS(m_control_data.agent_ori, M_PI);
+
+    // Update distances in position mode.
+    if (m_control_loop.mode == control_mode::position)
+    {
+        ARMARX_CHECK_GREATER(m_control_data.target_ori, -M_PI);
+        ARMARX_CHECK_LESS(m_control_data.target_ori, M_PI);
+        ARMARX_CHECK(m_control_data.target_pos.allFinite());
+        ARMARX_CHECK(std::isfinite(m_control_data.target_ori));
+
+        m_control_data.target_dist =
+            (m_control_data.target_pos - m_control_data.agent_pos).norm();
+        m_control_data.target_angular_dist =
+            periodic_clamp<float>(m_control_data.target_ori - m_control_data.agent_ori,
+                                  -M_PI, M_PI);
+
+        ARMARX_CHECK_GREATER(m_control_data.target_angular_dist, -M_PI);
+        ARMARX_CHECK_LESS(m_control_data.target_angular_dist, M_PI);
+
+        ARMARX_DEBUG << "Distance to target:  " << m_control_data.target_dist << " mm and "
+                     << m_control_data.target_angular_dist << " rad.";
+    }
+    // Otherwise invalidate them.
+    else
+    {
+        invalidate(m_control_data.target_dist);
+        invalidate(m_control_data.target_angular_dist);
+    }
+}
+
+
+bool
+armarx::ObstacleAwarePlatformUnit::target_position_reached()
+const
+{
+    if (m_control_loop.mode == control_mode::position)
+    {
+        return m_control_data.target_dist < m_control_data.pos_reached_threshold;
+    }
+
+    // Cannot reach any target in non-position mode.
+    return false;
+}
+
+
+bool
+armarx::ObstacleAwarePlatformUnit::target_orientation_reached()
+const
+{
+    if (m_control_loop.mode == control_mode::position)
+    {
+        return std::fabs(m_control_data.target_angular_dist) < m_control_data.ori_reached_threshold;
+    }
+
+    // Cannot reach any target in non-position mode.
+    return false;
+}
+
+
+bool
+armarx::ObstacleAwarePlatformUnit::target_reached()
+const
+{
+    if (m_control_loop.mode == control_mode::position)
+    {
+        return target_position_reached() and target_orientation_reached();
+    }
+
+    return false;
+}
+
+
+bool
+armarx::ObstacleAwarePlatformUnit::is_near_target(const Eigen::Vector2f& control_velocity)
+const noexcept
+{
+    if (m_control_data.target_dist < m_control_data.pos_near_threshold)
+    {
+        const Eigen::Vector2f target_direction = m_control_data.target_pos - m_control_data.agent_pos;
+        const Eigen::Vector2f control_direction = control_velocity / control_velocity.norm();
+
+        const float sim = simox::math::cosine_similarity(target_direction, control_direction);
+
+        // if almost pointing into same direction
+        if (sim > cos(M_PI_4f32))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::visualize()
+{
+    const Eigen::Vector2f zero = Eigen::Vector2f::Zero();
+    velocities vels;
+    vels.target_local = zero;
+    vels.target_global = zero;
+    vels.modulated_local = zero;
+    vels.modulated_global = zero;
+    vels.target_rot = 0;
+
+    visualize(vels);
+}
+
+
+void
+armarx::ObstacleAwarePlatformUnit::visualize(const velocities& vels)
+{
+    using namespace armarx::viz;
+
+    if (not m_viz.enabled)
+    {
+        return;
+    }
+
+    Eigen::Vector3f agent_pos{m_control_data.agent_pos.x(), m_control_data.agent_pos.y(), 0};
+
+    // Progress.  Only draw in position control mode.
+    Layer l_prog = arviz.layer("progress");
+    if (m_control_loop.mode == control_mode::position)
+    {
+        const float min_keypoint_dist = 50;
+
+        {
+            l_prog.add(Cylinder{"boundingCylinder"}
+                       .position(agent_pos)
+                       .color(Color::cyan(255, 64))
+                       .pose(simox::math::pos_rpy_to_mat4f(agent_pos, -M_PI_2, 0, 0))
+                       .radius(m_viz.boundingCircle.radius)
+                       .height(2000));
+        }
+
+        // If no start is set, use current agent pos.
+        if (not m_viz.start.allFinite())
+        {
+            m_viz.start = agent_pos;
+        }
+
+        const Eigen::Vector3f& last_keypoint_pos =
+            m_viz.path.size() >= 1 ? m_viz.path.back() : m_viz.start;
+
+        // Keep a certain distance between path keypoints.
+        if ((last_keypoint_pos - agent_pos).norm() > min_keypoint_dist)
+        {
+            m_viz.path.push_back(agent_pos);
+        }
+
+        // Draw path.
+        if (not target_reached())
+        {
+            // Start.
+            l_prog.add(Sphere{"start"}
+                       .position(m_viz.start)
+                       .color(Color::cyan(255, 64))
+                       .radius(40));
+
+            // Path.
+            for (unsigned i = 0; i < m_viz.path.size(); ++i)
+            {
+                l_prog.add(Sphere{"path_" + std::to_string(i + 1)}
+                           .position(m_viz.path[i])
+                           .color(Color::magenta(255, 128))
+                           .radius(20));
+            }
+
+            // Goal.
+            const Eigen::Vector3f target{m_control_data.target_pos.x(),
+                                         m_control_data.target_pos.y(),
+                                         0};
+            l_prog.add(Arrow{"goal"}
+                       .fromTo(target + Eigen::Vector3f{0, 0, 220},
+                               target + Eigen::Vector3f{0, 0, 40})
+                       .color(Color::red(255, 128))
+                       .width(20));
+        }
+        else
+        {
+            m_viz.path.clear();
+            invalidate(m_viz.start);
+        }
+    }
+
+    // Velocities.
+    Layer l_vels = arviz.layer("velocities");
+    if (m_control_loop.mode != control_mode::none)
+    {
+        const float min_velocity = 15;
+        const Eigen::Vector3f from1{agent_pos + Eigen::Vector3f{0, 0, 2000}};
+        const Eigen::Vector3f from2 = from1 + Eigen::Vector3f{0, 0, 200};
+        const Eigen::Vector3f original{vels.target_global.x(), vels.target_global.y(), 0};
+        const Eigen::Vector3f modulated{vels.modulated_global.x(), vels.modulated_global.y(), 0};
+
+        if (original.norm() > min_velocity)
+        {
+            l_vels.add(Arrow{"original"}
+                       .fromTo(from1, from1 + original * 5)
+                       .color(Color::green(255, 128))
+                       .width(25));
+        }
+
+        if (modulated.norm() > min_velocity)
+        {
+            l_vels.add(Arrow{"modulated"}
+                       .fromTo(from2, from2 + modulated * 5)
+                       .color(Color::cyan(255, 128))
+                       .width(25));
+        }
+    }
+
+    // Agent.
+    Layer l_agnt = arviz.layer("agent");
+    if (m_control_loop.mode != control_mode::none)
+    {
+        l_agnt.add(Sphere{"agent"}
+                   .position(agent_pos)
+                   .color(Color::red(255, 128))
+                   .radius(50));
+
+        // Agent safety margin.
+        if (m_control_data.agent_safety_margin > 0)
+        {
+            l_agnt.add(Cylinder{"agent_safety_margin"}
+                       .pose(simox::math::pos_rpy_to_mat4f(agent_pos, -M_PI_2, 0, 0))
+                       .color(Color::red(255, 64))
+                       .radius(m_control_data.agent_safety_margin)
+                       .height(2));
+        }
+    }
+
+    arviz.commit({l_prog, l_vels, l_agnt});
+}
+
+
+armarx::PropertyDefinitionsPtr
+armarx::ObstacleAwarePlatformUnit::createPropertyDefinitions()
+{
+    PropertyDefinitionsPtr def = PlatformUnit::createPropertyDefinitions();
+
+    def->component(m_platform, "Platform");
+    def->component(m_obsman, "ObstacleAvoidingPlatformUnit");
+
+    // Settings.
+    def->optional(m_control_data.min_vel_near_target, "min_vel_near_target", "Velocity in [mm/s] "
+                  "the robot should at least set when near the target");
+    def->optional(m_control_data.min_vel_general, "min_vel_general", "Velocity in [mm/s] the robot "
+                  "should at least set on general");
+    def->optional(m_control_data.pos_near_threshold, "pos_near_threshold", "Distance in [mm] after "
+                  "which the robot is considered to be near the target for min velocity, "
+                  "smoothing, etc.");
+
+    // Control parameters.
+    def->optional(m_control_data.kp, "control.pos.kp");
+    def->optional(m_rot_pid_controller.Kp, "control.rot.kp");
+    def->optional(m_rot_pid_controller.Ki, "control.rot.ki");
+    def->optional(m_rot_pid_controller.Kd, "control.rot.kd");
+    def->optional(m_control_loop.cycle_time, "control.pose.cycle_time", "Control loop cycle time.");
+
+    // Obstacle avoidance parameter.
+    def->optional(m_control_data.agent_safety_margin, "doa.agent_safety_margin");
+
+    return def;
+}
diff --git a/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.h b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.h
new file mode 100644
index 0000000000000000000000000000000000000000..274b67ea153eec20473b67fa4f45b8425c7b174d
--- /dev/null
+++ b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.h
@@ -0,0 +1,259 @@
+/*
+ * 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::ObstacleAwarePlatformUnit
+ * @author     Christian R. G. Dreher <c.dreher@kit.edu>
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+#pragma once
+
+
+// STD/STL
+#include <deque>
+#include <string>
+#include <tuple>
+#include <mutex>
+#include <vector>
+
+// Eigen
+#include <Eigen/Core>
+
+// Ice
+#include <IceUtil/Time.h>
+
+// Simox
+#include <VirtualRobot/Nodes/RobotNode.h>
+#include <VirtualRobot/Safety.h>
+
+// ArmarX
+#include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
+#include <ArmarXCore/util/tasks.h>
+#include <ArmarXCore/util/time.h>
+
+// RobotAPI
+#include <RobotAPI/components/units/PlatformUnit.h>
+#include <RobotAPI/interface/components/ObstacleAvoidance/DynamicObstacleManagerInterface.h>
+#include <RobotAPI/libraries/core/PIDController.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h>
+
+
+namespace armarx
+{
+
+    class ObstacleAwarePlatformUnit :
+        virtual public PlatformUnit,
+        virtual public RobotStateComponentPluginUser,
+        virtual public ArVizComponentPluginUser,
+        virtual public DebugObserverComponentPluginUser
+    {
+
+    public:
+
+        enum class control_mode
+        {
+            position,
+            velocity,
+            none
+        };
+
+    private:
+
+        struct control_loop
+        {
+            std::mutex mutex;
+            control_mode mode = control_mode::none;
+            RunningTask<ObstacleAwarePlatformUnit>::pointer_type task;
+            IceUtil::Time cycle_time = IceUtil::Time::milliSeconds(10);
+        };
+
+        struct control_data
+        {
+            std::mutex mutex;
+            Eigen::Vector2f target_pos;
+            float target_ori;
+            Eigen::Vector2f target_vel;
+            float target_rot_vel;
+            float target_dist;
+            float target_angular_dist;
+            Eigen::Vector2f agent_pos;
+            float agent_ori;
+            double agent_safety_margin = 0;
+            float min_vel_near_target = 50;
+            float min_vel_general = 100;
+            float max_vel = 200;
+            float max_rot_vel = 0.3;
+            float pos_near_threshold = 250;
+            float pos_reached_threshold = 8;
+            float ori_reached_threshold = 0.1;
+            float kp = 3.5;
+        };
+
+        struct visualization
+        {
+            Eigen::Vector3f start;
+            std::vector<Eigen::Vector3f> path;
+            bool enabled = true;
+            VirtualRobot::Circle boundingCircle;
+        };
+
+        struct velocities
+        {
+            Eigen::Vector2f target_local;
+            Eigen::Vector2f modulated_local;
+            Eigen::Vector2f target_global;
+            Eigen::Vector2f modulated_global;
+            float target_rot;
+            float err_dist;
+            float err_angular_dist;
+        };
+
+    public:
+
+        ObstacleAwarePlatformUnit();
+
+        ~ObstacleAwarePlatformUnit()
+        override;
+
+        std::string
+        getDefaultName()
+        const override;
+
+        void
+        moveTo(
+            float target_pos_x,
+            float target_pos_y,
+            float target_ori,
+            float pos_reached_threshold,
+            float ori_reached_threshold,
+            const Ice::Current& = Ice::Current{})
+        override;
+
+        void
+        move(
+            float target_vel_x,
+            float target_vel_y,
+            float target_rot_vel,
+            const Ice::Current& = Ice::Current{})
+        override;
+
+        void
+        moveRelative(
+            float target_pos_delta_x,
+            float target_pos_delta_y,
+            float target_delta_ori,
+            float pos_reached_threshold,
+            float ori_reached_threshold,
+            const Ice::Current& = Ice::Current{})
+        override;
+
+        void
+        setMaxVelocities(
+            float max_pos_vel,
+            float max_rot_vel,
+            const Ice::Current& = Ice::Current{})
+        override;
+
+        void
+        stopPlatform(const Ice::Current& = Ice::Current{})
+        override;
+
+    protected:
+
+        void
+        onInitPlatformUnit()
+        override;
+
+        void
+        onStartPlatformUnit()
+        override;
+
+        void
+        onExitPlatformUnit()
+        override;
+
+        PropertyDefinitionsPtr
+        createPropertyDefinitions()
+        override;
+
+    private:
+
+        void
+        schedule_high_level_control_loop(control_mode mode);
+
+        void
+        high_level_control_loop();
+
+        velocities
+        get_velocities();
+
+        void
+        update_agent_dependent_values();
+
+        Eigen::Vector2f
+        get_target_velocity()
+        const;
+
+        float
+        get_target_rotational_velocity()
+        const;
+
+        bool
+        target_position_reached()
+        const;
+
+        bool
+        target_orientation_reached()
+        const;
+
+        bool
+        target_reached()
+        const;
+
+        void
+        visualize();
+
+        void
+        visualize(const velocities& vels);
+
+        bool
+        is_near_target(const Eigen::Vector2f& control_velocity)
+        const
+        noexcept;
+
+    public:
+
+        static const std::string default_name;
+
+    private:
+
+        PlatformUnitInterfacePrx m_platform;
+        VirtualRobot::RobotPtr m_robot;
+        DynamicObstacleManagerInterfacePrx m_obsman;
+
+        ObstacleAwarePlatformUnit::control_loop m_control_loop;
+        ObstacleAwarePlatformUnit::control_data m_control_data;
+
+        mutable PIDController m_rot_pid_controller{1.0, 0.00009, 0.0};
+
+        visualization m_viz;
+
+    };
+
+}
diff --git a/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/main.cpp b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f168208f6734ea228aa848dcf0c5d0976370bae7
--- /dev/null
+++ b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/main.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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::ObstacleAwarePlatformUnit
+ * @author     Christian R. G. Dreher <c.dreher@kit.edu>
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+// STD/STL
+#include <string>
+
+// ArmarX
+#include <ArmarXCore/core/application/Application.h>
+#include <ArmarXCore/core/Component.h>
+
+// RobotAPI
+#include <RobotAPI/components/units/ObstacleAwarePlatformUnit/ObstacleAwarePlatformUnit.h>
+
+
+int main(int argc, char* argv[])
+{
+    using namespace armarx;
+    const std::string name = ObstacleAwarePlatformUnit::default_name;
+    return runSimpleComponentApp<ObstacleAwarePlatformUnit>(argc, argv, name);
+}
diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp
index bc246422b98fbd89feac994a2ace52645532e22a..77f9c8d8b49f9f192eb63ab64fe9d5b308b9e22e 100755
--- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp
+++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp
@@ -23,6 +23,10 @@
  */
 #include "NJointHolonomicPlatformGlobalPositionController.h"
 
+#include <cmath>
+
+#include <SimoxUtility/math/periodic/periodic_clamp.h>
+
 namespace armarx
 {
     NJointControllerRegistration<NJointHolonomicPlatformGlobalPositionController>
@@ -34,7 +38,7 @@ namespace armarx
         const NJointHolonomicPlatformGlobalPositionControllerConfigPtr& cfg,
         const VirtualRobot::RobotPtr&) :
         pid(cfg->p, cfg->i, cfg->d, cfg->maxVelocity, cfg->maxAcceleration),
-        opid(cfg->p_rot, cfg->i_rot, cfg->d_rot, cfg->maxRotationVelocity, cfg->maxRotationAcceleration)
+        opid(cfg->p_rot, cfg->i_rot, cfg->d_rot, cfg->maxRotationVelocity, cfg->maxRotationAcceleration, true)
 
     {
         const SensorValueBase* sv = useSensorValue(cfg->platformName);
@@ -78,24 +82,14 @@ namespace armarx
             return;
         }
 
-        float relativeOrientation = currentOrientation - rtGetControlStruct().startOrientation;
-        Eigen::Vector2f relativeCurrentPosition = currentPosition - rtGetControlStruct().startPosition;
-
-        Eigen::Vector2f updatedPosition = rtGetControlStruct().globalPosition;// + relativeCurrentPosition;
-        float updatedOrientation = rtGetControlStruct().globalOrientation;//+ relativeOrientation;
-
-        float relativeGlobalOrientation = rtGetControlStruct().globalOrientation - getWriterControlStruct().startOrientation;
-        relativeGlobalOrientation = std::atan2(std::sin(relativeGlobalOrientation), std::cos(relativeGlobalOrientation));
-
-        float relativeTargetOrientation = rtGetControlStruct().targetOrientation - getWriterControlStruct().startOrientation;
-        relativeTargetOrientation = std::atan2(std::sin(relativeTargetOrientation), std::cos(relativeTargetOrientation));
+        const float measuredOrientation = rtGetControlStruct().globalOrientation;
 
+        pid.update(timeSinceLastIteration.toSecondsDouble(), rtGetControlStruct().globalPosition, rtGetControlStruct().target);
+        opid.update(timeSinceLastIteration.toSecondsDouble(), static_cast<double>(measuredOrientation), rtGetControlStruct().targetOrientation);
 
-        pid.update(timeSinceLastIteration.toSecondsDouble(), updatedPosition, rtGetControlStruct().target);
-        //opid.update(timeSinceLastIteration.toSecondsDouble(), static_cast<double>(updatedOrientation), rtGetControlStruct().targetOrientation);
-        opid.update(timeSinceLastIteration.toSecondsDouble(), static_cast<double>(relativeGlobalOrientation), relativeTargetOrientation);
+        const Eigen::Rotation2Df global_R_local(-measuredOrientation);
 
-        Eigen::Vector2f velocities = Eigen::Rotation2Df(-updatedOrientation) * Eigen::Vector2f(pid.getControlValue()[0], pid.getControlValue()[1]);
+        Eigen::Vector2f velocities = global_R_local * pid.getControlValue();
         target->velocityX = velocities.x();
         target->velocityY = velocities.y();
         target->velocityRotation = static_cast<float>(opid.getControlValue());
@@ -112,7 +106,7 @@ namespace armarx
         std::lock_guard<std::recursive_mutex> lock(controlDataMutex);
 
         getWriterControlStruct().target << x, y;
-        getWriterControlStruct().targetOrientation = std::atan2(std::sin(yaw), std::cos(yaw));
+        getWriterControlStruct().targetOrientation = simox::math::periodic_clamp(yaw, -M_PIf32, M_PIf32);
         getWriterControlStruct().translationAccuracy = translationAccuracy;
         getWriterControlStruct().rotationAccuracy = rotationAccuracy;
         getWriterControlStruct().newTargetSet = true;
@@ -125,7 +119,7 @@ namespace armarx
         // ..todo: check if norm is too large
 
         getWriterControlStruct().globalPosition << currentPose.x, currentPose.y;
-        getWriterControlStruct().globalOrientation = currentPose.rotationAroundZ;
+        getWriterControlStruct().globalOrientation = simox::math::periodic_clamp(currentPose.rotationAroundZ, -M_PIf32, M_PIf32);
 
         getWriterControlStruct().startPosition = currentPosition;
         getWriterControlStruct().startOrientation = currentOrientation;
diff --git a/source/RobotAPI/interface/ArViz/Elements.ice b/source/RobotAPI/interface/ArViz/Elements.ice
index 1c00dd4e630208a79852ea3d18d96da8a501f7d0..bebbb3f414be2184f51fdb8518cdcd368da33ebc 100644
--- a/source/RobotAPI/interface/ArViz/Elements.ice
+++ b/source/RobotAPI/interface/ArViz/Elements.ice
@@ -104,7 +104,6 @@ module data
     {
         Vector3fSeq points;
 
-        Color lineColor;
         float lineWidth = 10.0f;
     };
 
diff --git a/source/RobotAPI/interface/components/ObstacleAvoidance/DynamicObstacleManagerInterface.ice b/source/RobotAPI/interface/components/ObstacleAvoidance/DynamicObstacleManagerInterface.ice
index d21a3c55b7574a459f0be3e2577870cf7640f5e3..5ff2649fae12cb906f5821f73ba1fb1fd21647fe 100644
--- a/source/RobotAPI/interface/components/ObstacleAvoidance/DynamicObstacleManagerInterface.ice
+++ b/source/RobotAPI/interface/components/ObstacleAvoidance/DynamicObstacleManagerInterface.ice
@@ -30,6 +30,18 @@
 
 module armarx
 {
+    module dynamicobstaclemanager
+    {
+        struct LineSegment
+        {
+            Eigen::Vector2f lineStart;
+            Eigen::Vector2f lineEnd;
+        };
+
+        sequence<LineSegment> LineSegments;
+    }
+
+
     interface DynamicObstacleManagerInterface
     {
         void
@@ -38,6 +50,9 @@ module armarx
         void
         add_decayable_line_segment(Eigen::Vector2f line_start, Eigen::Vector2f line_end);
 
+        void
+        add_decayable_line_segments(dynamicobstaclemanager::LineSegments lines);
+
         void
         remove_all_decayable_obstacles();
 
@@ -48,6 +63,8 @@ module armarx
         remove_obstacle(string name);
 
         void wait_unitl_obstacles_are_ready();
+
+        float distanceToObstacle(Eigen::Vector2f agentPosition, float safetyRadius, Eigen::Vector2f goal);
     };
 };
 
diff --git a/source/RobotAPI/libraries/CMakeLists.txt b/source/RobotAPI/libraries/CMakeLists.txt
index 5df6b028f0272c51a0538392375c27421d19dc06..02e5c638bfdfc145c271a995e016ce0c5f15ba01 100644
--- a/source/RobotAPI/libraries/CMakeLists.txt
+++ b/source/RobotAPI/libraries/CMakeLists.txt
@@ -22,7 +22,7 @@ add_subdirectory(armem_gui)
 add_subdirectory(armem_objects)
 add_subdirectory(armem_robot)
 add_subdirectory(armem_robot_state)
-add_subdirectory(armem_robot_mapping)
+add_subdirectory(armem_vision)
 add_subdirectory(armem_skills)
 add_subdirectory(aron)
 
diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt
index 003f611949956c306ff73aac3c0602217c02a69c..ee5a16f2563ac08d997f1273035a16085ab1919c 100644
--- a/source/RobotAPI/libraries/armem/CMakeLists.txt
+++ b/source/RobotAPI/libraries/armem/CMakeLists.txt
@@ -85,6 +85,9 @@ set(LIB_FILES
     client/Writer.cpp
     client/WriterComponentPlugin.cpp
 
+    client/util/SimpleReaderBase.cpp
+    client/util/SimpleWriterBase.cpp
+
     client/Query.cpp
     client/query/Builder.cpp
     client/query/selectors.cpp
@@ -196,6 +199,9 @@ set(LIB_HEADERS
     client/query/detail/NameSelectorOps.h
     client/query/detail/SelectorOps.h
 
+    client/util/SimpleReaderBase.h
+    client/util/SimpleWriterBase.h
+
     server.h
     server/ComponentPlugin.h
     server/MemoryToIceAdapter.h
diff --git a/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.cpp b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c3afb9a2adcc3f08495bc8c979d33ebc11742f2
--- /dev/null
+++ b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.cpp
@@ -0,0 +1,55 @@
+#include "SimpleReaderBase.h"
+
+#include "RobotAPI/libraries/armem/client/ComponentPlugin.h"
+
+namespace armarx::armem::client::util
+{
+    SimpleReaderBase::SimpleReaderBase(ComponentPluginUser& memoryClient) :
+        component(memoryClient)
+    {
+    }
+
+    void SimpleReaderBase::registerPropertyDefinitions(
+        armarx::PropertyDefinitionsPtr& def)
+    {
+        ARMARX_DEBUG << "Writer: registerPropertyDefinitions";
+
+        const std::string prefix = propertyPrefix();
+
+        def->optional(props.memoryName, prefix + "Memory");
+        def->optional(props.coreSegmentName, prefix + "CoreSegment");
+    }
+
+    void SimpleReaderBase::connect()
+    {
+        // Wait for the memory to become available and add it as dependency.
+        ARMARX_IMPORTANT << "Writer: Waiting for memory '" << props.memoryName
+                         << "' ...";
+        auto result = component.useMemory(props.memoryName);
+        if (not result.success)
+        {
+            ARMARX_ERROR << result.errorMessage;
+            return;
+        }
+
+        ARMARX_IMPORTANT << "SimpleReaderBase: Connected to memory '"
+                         << props.memoryName;
+
+        memoryReaderClient.setReadingMemory(result.proxy);
+    }
+
+    std::mutex& SimpleReaderBase::memoryReaderMutex()
+    {
+        return memoryMutex;
+    }
+
+    const armem::client::Reader& SimpleReaderBase::memoryReader() const
+    {
+        return memoryReaderClient;
+    }
+
+    const SimpleReaderBase::Properties& SimpleReaderBase::properties() const
+    {
+        return props;
+    }
+} // namespace armarx::armem::client::util
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.h b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..4e2c8edf24d8b99362e81225b439f1ea7ed56522
--- /dev/null
+++ b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <mutex>
+
+#include "ArmarXCore/core/application/properties/PropertyDefinitionContainer.h"
+
+#include "RobotAPI/libraries/armem/client/Reader.h"
+
+
+namespace armarx::armem::client
+{
+    class ComponentPluginUser;
+}
+
+namespace armarx::armem::client::util
+{
+
+    class SimpleReaderBase
+    {
+    public:
+        SimpleReaderBase(ComponentPluginUser& memoryClient);
+        virtual ~SimpleReaderBase() = default;
+
+        void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def);
+        void connect();
+
+    protected:
+        // Properties
+        struct Properties
+        {
+            std::string memoryName      = "";
+            std::string coreSegmentName = "";
+        };
+
+        const Properties& properties() const;
+
+        virtual std::string propertyPrefix() const   = 0;
+        virtual Properties defaultProperties() const = 0;
+
+        std::mutex& memoryReaderMutex();
+        const armem::client::Reader& memoryReader() const;
+
+    private:
+        Properties props;
+
+        armem::client::Reader memoryReaderClient;
+        std::mutex memoryMutex;
+
+        ComponentPluginUser& component;
+    };
+
+} // namespace armarx::armem::client::util
diff --git a/source/RobotAPI/libraries/armem/client/util/SimpleWriterBase.cpp b/source/RobotAPI/libraries/armem/client/util/SimpleWriterBase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7ac78e00496cf890b97f11523f200c5fc9c3ab3
--- /dev/null
+++ b/source/RobotAPI/libraries/armem/client/util/SimpleWriterBase.cpp
@@ -0,0 +1,61 @@
+#include "SimpleWriterBase.h"
+
+#include "RobotAPI/libraries/armem/client/ComponentPlugin.h"
+
+namespace armarx::armem::client::util
+{
+    SimpleWriterBase::SimpleWriterBase(ComponentPluginUser& component) :
+        component(component)
+    {
+    }
+
+    void
+    SimpleWriterBase::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def)
+    {
+        ARMARX_DEBUG << "Writer: registerPropertyDefinitions";
+
+        const std::string prefix = propertyPrefix();
+
+        props = defaultProperties();
+
+        def->optional(props.memoryName, prefix + "Memory");
+        def->optional(props.coreSegmentName, prefix + "CoreSegment");
+
+        def->required(props.providerName,
+                      prefix + "Provider",
+                      "Name of this provider");
+    }
+
+    void SimpleWriterBase::connect()
+    {
+        // Wait for the memory to become available and add it as dependency.
+        ARMARX_IMPORTANT << "SimpleWriterBase: Waiting for memory '"
+                         << props.memoryName << "' ...";
+        auto result = component.useMemory(props.memoryName);
+        if (not result.success)
+        {
+            ARMARX_ERROR << result.errorMessage;
+            return;
+        }
+
+        ARMARX_IMPORTANT << "SimpleWriterBase: Connected to memory '"
+                         << props.memoryName;
+
+        memoryWriterClient.setWritingMemory(result.proxy);
+        // memoryReader.setReadingMemory(result.proxy);
+    }
+
+    std::mutex& SimpleWriterBase::memoryWriterMutex()
+    {
+        return memoryMutex;
+    }
+
+    armem::client::Writer& SimpleWriterBase::memoryWriter()
+    {
+        return memoryWriterClient;
+    }
+    const SimpleWriterBase::Properties& SimpleWriterBase::properties() const
+    {
+        return props;
+    }
+} // namespace armarx::armem::client::util
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem/client/util/SimpleWriterBase.h b/source/RobotAPI/libraries/armem/client/util/SimpleWriterBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..55829b2c7bbc3dc5e9758156ad7c4f7e14956db1
--- /dev/null
+++ b/source/RobotAPI/libraries/armem/client/util/SimpleWriterBase.h
@@ -0,0 +1,77 @@
+/*
+ * 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 <mutex>
+
+#include "ArmarXCore/core/application/properties/PropertyDefinitionContainer.h"
+
+// #include "RobotAPI/libraries/armem/client/Reader.h"
+#include "RobotAPI/libraries/armem/client/Writer.h"
+
+
+namespace armarx::armem::client
+{
+    class ComponentPluginUser;
+}
+
+namespace armarx::armem::client::util
+{
+
+    class SimpleWriterBase
+    {
+    public:
+        SimpleWriterBase(ComponentPluginUser& component);
+        virtual ~SimpleWriterBase() = default;
+
+        void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def);
+        void connect();
+
+    protected:
+        std::mutex& memoryWriterMutex();
+        armem::client::Writer& memoryWriter();
+
+        struct Properties
+        {
+            std::string memoryName      = "";
+            std::string coreSegmentName = "";
+            std::string providerName    = ""; // required property
+        };
+
+        const Properties& properties() const;
+
+        virtual std::string propertyPrefix() const   = 0;
+        virtual Properties defaultProperties() const = 0;
+
+    private:
+        Properties props;
+
+        armem::client::Writer memoryWriterClient;
+        std::mutex memoryMutex;
+
+        // armem::client::Reader memoryReader;
+        // std::mutex memoryReaderMutex;
+
+        ComponentPluginUser& component;
+    };
+
+} // namespace armarx::armem::client::util
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_mapping/CMakeLists.txt
deleted file mode 100644
index 145ced819b68db659a92e232747c645c24037ce5..0000000000000000000000000000000000000000
--- a/source/RobotAPI/libraries/armem_robot_mapping/CMakeLists.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-set(LIB_NAME armem_robot_mapping)
-
-armarx_component_set_name("${LIB_NAME}")
-armarx_set_target("Library: ${LIB_NAME}")
-
-armarx_add_library(
-    LIBS 
-        # ArmarX
-        ArmarXCore 
-        # This package
-        RobotAPICore 
-        RobotAPI::armem
-        # System / External
-        Eigen3::Eigen
-    HEADERS
-        ./aron_conversions.h
-        ./MappingDataWriter.h
-        ./MappingDataReader.h
-    SOURCES
-        ./aron_conversions.cpp
-        ./MappingDataWriter.cpp
-        ./MappingDataReader.cpp
-)
-
-
-armarx_enable_aron_file_generation_for_target(
-    TARGET_NAME
-        "${LIB_NAME}"
-    ARON_FILES
-        aron/LaserScan.xml
-)
-
-
-add_library(RobotAPI::armem_robot_mapping ALIAS armem_robot_mapping)
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.h b/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.h
deleted file mode 100644
index 7ecffc237d480fd33c30226ef7f1cf02c09308de..0000000000000000000000000000000000000000
--- a/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 <RobotAPI/interface/units/LaserScannerUnit.h>
-#include <RobotAPI/libraries/aron/converter/common/VectorConverter.h>
-#include <RobotAPI/libraries/aron/core/navigator/data/complex/NDArray.h>
-#include <RobotAPI/libraries/armem/core/Time.h>
-
-namespace armarx
-{
-
-    namespace arondto
-    {
-        struct LaserScanStamped;
-    } // namespace aron
-
-    // struct LaserScan;
-    struct LaserScanStamped;
-
-    void fromAron(
-        const arondto::LaserScanStamped& aronLaserScan,
-        LaserScan& laserScan,
-        std::int64_t& timestamp,
-        std::string& frame,
-        std::string& agentName);
-
-
-    template<typename T>
-    auto fromAron(const aron::datanavigator::NDArrayNavigatorPtr& navigator)
-    {
-        return aron::converter::AronVectorConverter::ConvertToVector<T>(navigator);
-    }
-
-    void fromAron(const arondto::LaserScanStamped& aronLaserScan, LaserScanStamped& laserScan);
-
-    void toAron(
-        const LaserScan& laserScan,
-        const armem::Time& timestamp,
-        const std::string& frame,
-        const std::string& agentName,
-        arondto::LaserScanStamped& aronLaserScan);
-
-    inline aron::datanavigator::NDArrayNavigatorPtr toAron(const LaserScan& laserScan)
-    {
-        return aron::converter::AronVectorConverter::ConvertFromVector(laserScan);
-    }
-
-
-} // namespace armarx
\ No newline at end of file
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 516769871608464d59aeb5318119dc5d5a1ab4f5..433694ec819859bdcda209dd57aad21d22512116 100644
--- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h
+++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h
@@ -66,7 +66,7 @@ namespace armarx::armem::client::robot_state::localization
         // Properties
         struct Properties
         {
-            std::string memoryName             = "RobotStateMemory";
+            std::string memoryName             = "RobotState";
             std::string localizationSegment    = "Localization";
         } properties;
 
diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h
index f69ab33f282c82599af7c66b43f7919fcba8114f..299c79b69222accb8290a7064229a9dfe59e8572 100644
--- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h
+++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h
@@ -70,7 +70,7 @@ namespace armarx::armem::client::robot_state::localization
         // Properties
         struct Properties
         {
-            std::string memoryName             = "RobotStateMemory";
+            std::string memoryName             = "RobotState";
             std::string localizationSegment    = "Localization";
         } properties;
 
diff --git a/source/RobotAPI/libraries/armem_vision/CMakeLists.txt b/source/RobotAPI/libraries/armem_vision/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1767c9cebd878b1d15cad6f8043a2bd7cf932814
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/CMakeLists.txt
@@ -0,0 +1,43 @@
+set(LIB_NAME armem_vision)
+
+armarx_component_set_name("${LIB_NAME}")
+armarx_set_target("Library: ${LIB_NAME}")
+
+armarx_add_library(
+    LIBS
+        # ArmarX
+        ArmarXCore
+        # This package
+        RobotAPI::Core
+        RobotAPI::armem
+        aroncommon
+        # System / External
+        Eigen3::Eigen
+    HEADERS
+        ./aron_conversions.h
+        ./client/laser_scans/Reader.h
+        ./client/laser_scans/Writer.h
+        ./client/occupancy_grid/Reader.h
+        ./client/occupancy_grid/Writer.h
+    SOURCES
+        ./aron_conversions.cpp
+        ./client/laser_scans/Reader.cpp
+        ./client/laser_scans/Writer.cpp
+        ./client/occupancy_grid/Reader.cpp
+        ./client/occupancy_grid/Writer.cpp
+        ./OccupancyGridHelper.cpp
+)
+
+armarx_enable_aron_file_generation_for_target(
+    TARGET_NAME
+        "${LIB_NAME}"
+    ARON_FILES
+        aron/LaserScan.xml
+        aron/OccupancyGrid.xml
+)
+
+add_library(
+    RobotAPI::armem_vision
+    ALIAS
+    armem_vision
+)
diff --git a/source/RobotAPI/libraries/armem_vision/OccupancyGridHelper.cpp b/source/RobotAPI/libraries/armem_vision/OccupancyGridHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f88cc75454fd6f83545c3207c1e58642bedcc225
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/OccupancyGridHelper.cpp
@@ -0,0 +1,39 @@
+#include "OccupancyGridHelper.h"
+
+#include "types.h"
+
+namespace armarx
+{
+    OccupancyGridHelper::OccupancyGridHelper(const OccupancyGrid& occupancyGrid,
+            const Params& params) :
+        occupancyGrid(occupancyGrid), params(params)
+    {
+    }
+
+    OccupancyGridHelper::BinaryArray OccupancyGridHelper::knownCells() const
+    {
+        return (occupancyGrid.grid > 0.F).cast<bool>();
+    }
+
+    OccupancyGridHelper::BinaryArray OccupancyGridHelper::freespace() const
+    {
+        // matrix1 = matrix1 .unaryExpr(std::ptr_fun(ReplaceNanWithValue<1>));
+        // return (occupancyGrid.grid ).cast<bool>();
+
+        const auto isFree = [&](OccupancyGrid::CellType p) -> float
+        { return static_cast<float>(p < params.freespaceThreshold and p > 0.F); };
+
+        // TODO(fabian.reister): which one to choose?
+        // return occupancyGrid.grid.unaryExpr(isFree).cast<bool>();
+        return occupancyGrid.grid.unaryViewExpr(isFree).cast<bool>();
+    }
+
+    OccupancyGridHelper::BinaryArray OccupancyGridHelper::obstacles() const
+    {
+        const auto isOccupied = [&](OccupancyGrid::CellType p) -> float
+        { return static_cast<float>(p > params.occupiedThreshold); };
+
+        return occupancyGrid.grid.unaryViewExpr(isOccupied).cast<bool>();
+    }
+
+} // namespace armarx
diff --git a/source/RobotAPI/libraries/armem_vision/OccupancyGridHelper.h b/source/RobotAPI/libraries/armem_vision/OccupancyGridHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..17628ef34fc4ef30fc2b46dd8f031f2fc46dbda3
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/OccupancyGridHelper.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <Eigen/Core>
+
+namespace armarx::armem
+{
+    struct OccupancyGrid;
+}
+
+namespace armarx
+{
+    using armarx::armem::OccupancyGrid;
+
+    namespace detail
+    {
+        struct OccupancyGridHelperParams
+        {
+            float freespaceThreshold = 0.45F;
+            float occupiedThreshold = 0.55F;
+        };
+    }
+
+    class OccupancyGridHelper
+    {
+    public:
+        using Params = detail::OccupancyGridHelperParams;
+
+        OccupancyGridHelper(const OccupancyGrid& occupancyGrid, const Params& params);
+
+        using BinaryArray = Eigen::Array<bool, Eigen::Dynamic, Eigen::Dynamic>;
+
+        BinaryArray knownCells() const;
+        BinaryArray freespace() const;
+        BinaryArray obstacles() const;
+
+    private:
+        const OccupancyGrid& occupancyGrid;
+        const Params params;
+    };
+} // namespace armarx
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.xml b/source/RobotAPI/libraries/armem_vision/aron/LaserScan.xml
similarity index 83%
rename from source/RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.xml
rename to source/RobotAPI/libraries/armem_vision/aron/LaserScan.xml
index 659df4a1536b0f81b81ba64f77e4049ba42fdd5c..f2d11c2e4111dfd0c24896ba807aef6435b7014b 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.xml
+++ b/source/RobotAPI/libraries/armem_vision/aron/LaserScan.xml
@@ -8,7 +8,7 @@
 
     <GenerateTypes>
 
-        <Object name='armarx::arondto::LaserScannerInfo'>
+        <Object name='armarx::armem::arondto::LaserScannerInfo'>
             <ObjectChild key='device'>
                 <string />
             </ObjectChild>
@@ -26,7 +26,7 @@
             </ObjectChild>
         </Object>
 
-        <Object name="armarx::arondto::SensorHeader">
+        <Object name="armarx::armem::arondto::SensorHeader">
             <ObjectChild key="agent">
                 <string/>
             </ObjectChild>
@@ -39,9 +39,9 @@
         </Object>
 
 
-        <Object name='armarx::arondto::LaserScanStamped'>
+        <Object name='armarx::armem::arondto::LaserScanStamped'>
             <ObjectChild key="header">
-                <armarx::arondto::SensorHeader />
+                <armarx::armem::arondto::SensorHeader />
             </ObjectChild>
 
             <!-- 
diff --git a/source/RobotAPI/libraries/armem_vision/aron/OccupancyGrid.xml b/source/RobotAPI/libraries/armem_vision/aron/OccupancyGrid.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0c508a4e2138b4b04c126287bf46d8826fb3da6f
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/aron/OccupancyGrid.xml
@@ -0,0 +1,30 @@
+<!--Some fancy comment -->
+<?xml version="1.0" encoding="UTF-8" ?>
+<AronTypeDefinition>
+    <CodeIncludes>
+    </CodeIncludes>
+    <AronIncludes>
+    </AronIncludes>
+
+    <GenerateTypes>
+
+        <Object name='armarx::armem::arondto::OccupancyGrid'>
+            <ObjectChild key='resolution'>
+                <float />
+            </ObjectChild>
+            <ObjectChild key='frame'>
+                <string />
+            </ObjectChild>
+            <ObjectChild key='pose'>
+                <Pose />
+            </ObjectChild>
+            
+            <!-- 
+            <ObjectChild key='grid'>
+                <NdArray />
+            </ObjectChild> -->
+        </Object>
+
+
+    </GenerateTypes>
+</AronTypeDefinition> 
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.cpp b/source/RobotAPI/libraries/armem_vision/aron_conversions.cpp
similarity index 62%
rename from source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.cpp
rename to source/RobotAPI/libraries/armem_vision/aron_conversions.cpp
index 9b45357198639190d12b9a896705885f8046ea85..34d87dcf8473475b3a6b5e151f842b5d6004a62c 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.cpp
+++ b/source/RobotAPI/libraries/armem_vision/aron_conversions.cpp
@@ -5,23 +5,22 @@
 #include <iterator>
 
 #include <RobotAPI/interface/units/LaserScannerUnit.h>
-#include <RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron/LaserScan.aron.generated.h>
+#include <RobotAPI/libraries/aron/common/aron_conversions.h>
 #include <RobotAPI/libraries/aron/converter/common/Converter.h>
 #include <RobotAPI/libraries/aron/core/navigator/data/complex/NDArray.h>
 
 #include "types.h"
 
-
-namespace armarx
+namespace armarx::armem
 {
 
     /************ fromAron ************/
     SensorHeader fromAron(const arondto::SensorHeader& aronSensorHeader)
     {
-
-        return {.agent = aronSensorHeader.agent,
-                .frame = aronSensorHeader.frame,
-                .timestamp = aronSensorHeader.timestamp};
+        return {.agent     = aronSensorHeader.agent,
+                .frame     = aronSensorHeader.frame,
+                .timestamp = timeFromAron(aronSensorHeader.timestamp)};
     }
 
     void fromAron(const arondto::LaserScanStamped& aronLaserScan,
@@ -31,8 +30,10 @@ namespace armarx
         // laserScan.data = fromAron(aronLaserScan.data);
     }
 
-    void fromAron(const arondto::LaserScanStamped& aronLaserScan, LaserScan& laserScan,
-                  std::int64_t& timestamp, std::string& frame,
+    void fromAron(const arondto::LaserScanStamped& aronLaserScan,
+                  LaserScan& laserScan,
+                  std::int64_t& timestamp,
+                  std::string& frame,
                   std::string& agentName)
     {
         const auto header = fromAron(aronLaserScan.header);
@@ -40,14 +41,12 @@ namespace armarx
         // laserScan = fromAron(aronLaserScan.data);
 
         timestamp = header.timestamp.toMicroSeconds();
-        frame = header.frame;
+        frame     = header.frame;
         agentName = header.agent;
     }
 
-
     /************ toAron ************/
 
-
     // auto toAron(const LaserScan& laserScan, aron::LaserScan& aronLaserScan)
     // {
     //     aronLaserScan.scan = toAron(laserScan);
@@ -62,9 +61,9 @@ namespace armarx
     {
         arondto::SensorHeader aronSensorHeader;
 
-        aronSensorHeader.agent = sensorHeader.agent;
-        aronSensorHeader.frame = sensorHeader.frame;
-        aronSensorHeader.timestamp = sensorHeader.timestamp;
+        aronSensorHeader.agent     = sensorHeader.agent;
+        aronSensorHeader.frame     = sensorHeader.frame;
+        aronSensorHeader.timestamp = toAron(sensorHeader.timestamp);
 
         return aronSensorHeader;
     }
@@ -86,9 +85,26 @@ namespace armarx
         {
             .agent = agentName, .frame = frame, .timestamp = timestamp};
 
-        const LaserScanStamped laserScanStamped{.header = header, .data = laserScan};
+        const LaserScanStamped laserScanStamped{.header = header,
+                                                .data   = laserScan};
 
         toAron(laserScanStamped, aronLaserScanStamped);
     }
 
-} // namespace armarx
+    void toAron(arondto::OccupancyGrid& dto, const OccupancyGrid& bo)
+    {
+        aron::toAron(dto.frame, bo.frame);
+        aron::toAron(dto.pose, bo.pose);
+        aron::toAron(dto.resolution, bo.resolution);
+        // bo.grid is NdArray -> need special handling.
+    }
+
+    void fromAron(const arondto::OccupancyGrid& dto, OccupancyGrid& bo)
+    {
+        aron::fromAron(dto.frame, bo.frame);
+        aron::fromAron(dto.pose, bo.pose);
+        aron::fromAron(dto.resolution, bo.resolution);
+        // bo.grid is NdArray -> need special handling.
+    }
+
+} // namespace armarx::armem
diff --git a/source/RobotAPI/libraries/armem_vision/aron_conversions.h b/source/RobotAPI/libraries/armem_vision/aron_conversions.h
new file mode 100644
index 0000000000000000000000000000000000000000..db97ad5076457805d953100201821f912a046291
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/aron_conversions.h
@@ -0,0 +1,82 @@
+/*
+ * 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 "RobotAPI/libraries/armem_vision/types.h"
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
+#include <RobotAPI/libraries/armem/core/Time.h>
+#include <RobotAPI/libraries/armem_vision/aron/OccupancyGrid.aron.generated.h>
+#include <RobotAPI/libraries/aron/converter/common/VectorConverter.h>
+#include <RobotAPI/libraries/aron/converter/eigen/EigenConverter.h>
+#include <RobotAPI/libraries/aron/core/navigator/data/complex/NDArray.h>
+
+namespace armarx::armem
+{
+
+    namespace arondto
+    {
+        struct LaserScanStamped;
+    } // namespace arondto
+
+    // struct LaserScan;
+    struct LaserScanStamped;
+
+    void fromAron(const arondto::LaserScanStamped& aronLaserScan,
+                  LaserScan& laserScan,
+                  std::int64_t& timestamp,
+                  std::string& frame,
+                  std::string& agentName);
+
+    template <typename T>
+    auto fromAron(const aron::datanavigator::NDArrayNavigatorPtr& navigator)
+    {
+        return aron::converter::AronVectorConverter::ConvertToVector<T>(
+                   navigator);
+    }
+
+    void fromAron(const arondto::LaserScanStamped& aronLaserScan,
+                  LaserScanStamped& laserScan);
+
+    void toAron(const LaserScan& laserScan,
+                const armem::Time& timestamp,
+                const std::string& frame,
+                const std::string& agentName,
+                arondto::LaserScanStamped& aronLaserScan);
+
+    inline aron::datanavigator::NDArrayNavigatorPtr
+    toAron(const LaserScan& laserScan)
+    {
+        using aron::converter::AronVectorConverter;
+        return AronVectorConverter::ConvertFromVector(laserScan);
+    }
+
+    // OccupancyGrid
+    void toAron(arondto::OccupancyGrid& dto, const OccupancyGrid& bo);
+    void fromAron(const arondto::OccupancyGrid& dto, OccupancyGrid& bo);
+
+    inline aron::datanavigator::NDArrayNavigatorPtr
+    toAron(const OccupancyGrid::Grid& grid)
+    {
+        return aron::converter::AronEigenConverter::ConvertFromArray(grid);
+    }
+
+} // namespace armarx::armem
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.cpp
similarity index 70%
rename from source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp
rename to source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.cpp
index 3c3e0e8c3303728b233eb76bf62a0a505b30f0e1..9c46b0c4729dfe6ca038c1f31ef81255f56d4968 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp
+++ b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.cpp
@@ -1,31 +1,32 @@
-#include "MappingDataReader.h"
+#include "Reader.h"
 
 // STD / STL
-#include <cstring>
-#include <vector>
 #include <algorithm>
+#include <cstring>
 #include <map>
 #include <optional>
 #include <ostream>
-#include <type_traits>
 #include <utility>
+#include <vector>
+
+#include <type_traits>
 
 // ICE
-#include <IceUtil/Time.h>
 #include <IceUtil/Handle.h>
+#include <IceUtil/Time.h>
 
 // Simox
 #include <SimoxUtility/algorithm/get_map_keys_values.h>
 
 // ArmarXCore
-#include <ArmarXCore/core/logging/Logging.h>
-#include <ArmarXCore/core/logging/LogSender.h>
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/logging/LogSender.h>
+#include <ArmarXCore/core/logging/Logging.h>
 
 // RobotAPI Interfaces
-#include <RobotAPI/interface/units/LaserScannerUnit.h>
 #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h>
 #include <RobotAPI/interface/armem/server/ReadingMemoryInterface.h>
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
 
 // RobotAPI Aron
 #include <RobotAPI/libraries/aron/core/Exception.h>
@@ -40,41 +41,40 @@
 #include <RobotAPI/libraries/armem/core/error.h>
 #include <RobotAPI/libraries/armem/core/workingmemory/Memory.h>
 #include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h>
-#include <RobotAPI/libraries/armem/core/workingmemory/Entity.h>
 #include <RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h>
 #include <RobotAPI/libraries/armem/core/workingmemory/Entity.h>
 #include <RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h>
-
 #include <RobotAPI/libraries/armem/util/util.h>
+#include <RobotAPI/libraries/armem_vision/aron/LaserScan.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron_conversions.h>
+#include <RobotAPI/libraries/armem_vision/types.h>
 
-#include <RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.aron.generated.h>
-#include <RobotAPI/libraries/armem_robot_mapping/aron_conversions.h>
-#include <RobotAPI/libraries/armem_robot_mapping/types.h>
-
-namespace armarx::armem
+namespace armarx::armem::vision::laser_scans::client
 {
 
-    MappingDataReader::MappingDataReader(armem::client::MemoryNameSystem& memoryNameSystem) :
+    Reader::Reader(armem::client::MemoryNameSystem& memoryNameSystem) :
         memoryNameSystem(memoryNameSystem)
     {
     }
-    MappingDataReader::~MappingDataReader() = default;
+    Reader::~Reader() = default;
+
 
-    void MappingDataReader::registerPropertyDefinitions(
-        armarx::PropertyDefinitionsPtr& def)
+    void
+    Reader::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def)
     {
         ARMARX_DEBUG << "TransformReader: registerPropertyDefinitions";
         registerPropertyDefinitions(def);
 
         const std::string prefix = propertyPrefix;
 
-        def->optional(properties.mappingMemoryName, prefix + "MappingMemoryName",
+        def->optional(properties.coreSegmentName,
+                      prefix + "CoreSegment",
                       "Name of the mapping memory core segment to use.");
 
         def->optional(properties.memoryName, prefix + "MemoryName");
     }
 
-    void MappingDataReader::connect()
+    void Reader::connect()
     {
         // Wait for the memory to become available and add it as dependency.
         ARMARX_IMPORTANT << "MappingDataReader: Waiting for memory '"
@@ -91,19 +91,19 @@ namespace armarx::armem
         }
     }
 
-    armem::client::query::Builder
-    MappingDataReader::buildQuery(const Query& query) const
+    armarx::armem::client::query::Builder
+    Reader::buildQuery(const Query& query) const
     {
-        armem::client::query::Builder qb;
+        armarx::armem::client::query::Builder qb;
 
         ARMARX_INFO << "Query for agent: " << query.agent
-                    << " memory name: " << properties.mappingMemoryName;
+                    << " memory name: " << properties.memoryName;
 
         if (query.sensorList.empty()) // all sensors
         {
             // clang-format off
             qb
-            .coreSegments().withName(properties.mappingMemoryName)
+            .coreSegments().withName(properties.memoryName)
             .providerSegments().withName(query.agent)
             .entities().all()
             .snapshots().timeRange(query.timeRange.min, query.timeRange.max);
@@ -113,7 +113,7 @@ namespace armarx::armem
         {
             // clang-format off
             qb
-            .coreSegments().withName(properties.mappingMemoryName)
+            .coreSegments().withName(properties.memoryName)
             .providerSegments().withName(query.agent)
             .entities().withNames(query.sensorList)
             .snapshots().timeRange(query.timeRange.min, query.timeRange.max);
@@ -121,10 +121,10 @@ namespace armarx::armem
         }
 
         return qb;
-
     }
 
-    std::vector<LaserScanStamped> asLaserScans(const std::map<std::string, wm::Entity>& entities)
+    std::vector<LaserScanStamped>
+    asLaserScans(const std::map<std::string, wm::Entity>& entities)
     {
         std::vector<LaserScanStamped> outV;
 
@@ -133,25 +133,27 @@ namespace armarx::armem
             ARMARX_WARNING << "No entities!";
         }
 
-        const auto convert = [](const arondto::LaserScanStamped & aronLaserScanStamped, const wm::EntityInstance & ei) -> LaserScanStamped
+        const auto convert =
+            [](const arondto::LaserScanStamped & aronLaserScanStamped,
+               const wm::EntityInstance & ei) -> LaserScanStamped
         {
             LaserScanStamped laserScanStamped;
             fromAron(aronLaserScanStamped, laserScanStamped);
 
-            const auto ndArrayNavigator = aron::datanavigator::NDArrayNavigator::DynamicCast(ei.data()->getElement("scan"));
+            const auto ndArrayNavigator =
+            aron::datanavigator::NDArrayNavigator::DynamicCast(
+                ei.data()->getElement("scan"));
 
             ARMARX_CHECK_NOT_NULL(ndArrayNavigator);
 
             laserScanStamped.data = fromAron<LaserScanStep>(ndArrayNavigator);
             ARMARX_IMPORTANT << "4";
 
-
             return laserScanStamped;
-
         };
 
         // loop over all entities and their snapshots
-        for (const auto &[s, entity] : entities)
+        for (const auto& [s, entity] : entities)
         {
             if (entity.empty())
             {
@@ -160,11 +162,12 @@ namespace armarx::armem
 
             ARMARX_DEBUG << "History size: " << entity.size();
 
-            for (const auto &[ss, entitySnapshot] : entity)
+            for (const auto& [ss, entitySnapshot] : entity)
             {
                 for (const auto& entityInstance : entitySnapshot.instances())
                 {
-                    const auto o = tryCast<arondto::LaserScanStamped>(entityInstance);
+                    const auto o =
+                        tryCast<arondto::LaserScanStamped>(entityInstance);
 
                     if (o)
                     {
@@ -177,8 +180,7 @@ namespace armarx::armem
         return outV;
     }
 
-    MappingDataReader::Result
-    MappingDataReader::queryData(const Query& query) const
+    Reader::Result Reader::queryData(const Query& query) const
     {
         const auto qb = buildQuery(query);
 
@@ -193,25 +195,25 @@ namespace armarx::armem
         {
             ARMARX_WARNING << "Failed to query data from memory: "
                            << qResult.errorMessage;
-            return {.laserScans = {},
-                    .sensors = {},
-                    .status = Result::Status::Error,
+            return {.laserScans   = {},
+                    .sensors      = {},
+                    .status       = Result::Status::Error,
                     .errorMessage = qResult.errorMessage};
         }
 
         // now create result from memory
         const auto& entities =
-            qResult.memory.getCoreSegment(properties.mappingMemoryName)
+            qResult.memory.getCoreSegment(properties.memoryName)
             .getProviderSegment(query.agent)
             .entities();
 
         const auto laserScans = asLaserScans(entities);
-        const auto sensors = simox::alg::get_keys(entities);
+        const auto sensors    = simox::alg::get_keys(entities);
 
-        return {.laserScans = laserScans,
-                .sensors = sensors,
-                .status = Result::Status::Success,
+        return {.laserScans   = laserScans,
+                .sensors      = sensors,
+                .status       = Result::Status::Success,
                 .errorMessage = ""};
     }
 
-} // namespace armarx::armem
+} // namespace armarx::armem::vision::laser_scans::client
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.h
similarity index 81%
rename from source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h
rename to source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.h
index 2479b9413aebe634257eadb2cf00f0765f4ba20c..138717c42257e02275a15785940783330126ee4d 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h
+++ b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.h
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include <stdint.h>
+#include <cstdint>
 #include <string>
 #include <vector>
 
@@ -31,8 +31,7 @@
 #include <RobotAPI/libraries/armem/client/Reader.h>
 #include <RobotAPI/libraries/armem/client/query/Builder.h>
 #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
-#include <RobotAPI/libraries/armem_robot_mapping/types.h>
-
+#include <RobotAPI/libraries/armem_vision/types.h>
 
 
 namespace armarx
@@ -40,7 +39,7 @@ namespace armarx
     class ManagedIceObject;
 }
 
-namespace armarx::armem
+namespace armarx::armem::vision::laser_scans::client
 {
 
     struct TimeRange
@@ -60,16 +59,15 @@ namespace armarx::armem
     *
     * Detailed description of class ExampleClient.
     */
-    class MappingDataReader
+    class Reader
     {
     public:
 
-        MappingDataReader(armem::client::MemoryNameSystem& memoryNameSystem);
-        virtual ~MappingDataReader();
+        Reader(armem::client::MemoryNameSystem& memoryNameSystem);
+        virtual ~Reader();
 
         void connect();
 
-
         struct Query
         {
             std::string agent;
@@ -78,7 +76,6 @@ namespace armarx::armem
 
             // if empty, all sensors will be queried
             std::vector<std::string> sensorList;
-
         };
 
         struct Result
@@ -95,16 +92,14 @@ namespace armarx::armem
             } status;
 
             std::string errorMessage;
-
         };
 
         Result queryData(const Query& query) const;
 
         void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def);
 
-
-        client::query::Builder buildQuery(const Query& query) const ;
-
+        armarx::armem::client::query::Builder
+        buildQuery(const Query& query) const;
 
     private:
 
@@ -114,12 +109,12 @@ namespace armarx::armem
         // Properties
         struct Properties
         {
-            std::string memoryName             = "RobotState";
-            std::string mappingMemoryName = "Mapping";
+            std::string memoryName      = "Vision";
+            std::string coreSegmentName = "LaserScans";
         } properties;
 
+        const std::string propertyPrefix = "mem.vision.";
 
-        const std::string propertyPrefix = "mem.mapping.";
     };
 
-}  // namespace armarx::armem
+} // namespace armarx::armem::vision::laser_scans::client
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.cpp b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.cpp
similarity index 61%
rename from source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.cpp
rename to source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.cpp
index fcf303e7bdfeb2bc35ce415da34e315671bb5ec9..7358e504ffecf3a2a710bd77b1631437e3818a42 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.cpp
+++ b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.cpp
@@ -1,36 +1,37 @@
-#include "MappingDataWriter.h"
+#include "Writer.h"
 
-#include <RobotAPI/libraries/armem_robot_mapping/aron_conversions.h>
-#include <RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.aron.generated.h>
 #include <RobotAPI/libraries/armem/core/error.h>
+#include "RobotAPI/libraries/armem_vision/aron_conversions.h"
+#include <RobotAPI/libraries/armem_vision/aron/LaserScan.aron.generated.h>
 
 
-namespace armarx::armem
+namespace armarx::armem::vision::laser_scans::client
 {
 
-    MappingDataWriter::MappingDataWriter(armem::client::MemoryNameSystem& memoryNameSystem)
+    Writer::Writer(armem::client::MemoryNameSystem& memoryNameSystem)
         : memoryNameSystem(memoryNameSystem) {}
+    Writer::~Writer() = default;
 
-    MappingDataWriter::~MappingDataWriter() = default;
 
-    void MappingDataWriter::registerPropertyDefinitions(
-        armarx::PropertyDefinitionsPtr& def)
+    void
+    Writer::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def)
     {
-        ARMARX_DEBUG << "TransformWriter: registerPropertyDefinitions";
+        ARMARX_DEBUG << "LaserScansWriter: registerPropertyDefinitions";
 
         const std::string prefix = propertyPrefix;
 
-        def->optional(properties.mappingMemoryName, prefix + "MappingMemoryName",
+        def->optional(properties.coreSegmentName,
+                      prefix + "CoreSegment",
                       "Name of the mapping memory core segment to use.");
 
         def->optional(properties.memoryName, prefix + "MemoryName");
     }
 
-    void MappingDataWriter::connect()
+    void Writer::connect()
     {
         // Wait for the memory to become available and add it as dependency.
-        ARMARX_IMPORTANT << "MappingDataWriter: Waiting for memory '" << properties.memoryName
-                         << "' ...";
+        ARMARX_IMPORTANT << "LaserScansWriter: Waiting for memory '"
+                         << properties.memoryName << "' ...";
         try
         {
             memoryWriter = memoryNameSystem.useWriter(MemoryID().withMemoryName(properties.memoryName));
@@ -41,18 +42,20 @@ namespace armarx::armem
             ARMARX_ERROR << e.what();
             return;
         }
-    }
 
+        ARMARX_IMPORTANT << "LaserScansWriter: Connected to memory '"
+                         << properties.memoryName;
+    }
 
-    bool MappingDataWriter::storeSensorData(const LaserScan& laserScan,
-                                            const std::string& frame,
-                                            const std::string& agentName,
-                                            const std::int64_t& timestamp)
+    bool Writer::storeSensorData(const LaserScan& laserScan,
+                                 const std::string& frame,
+                                 const std::string& agentName,
+                                 const std::int64_t& timestamp)
     {
         std::lock_guard g{memoryWriterMutex};
 
         const auto result =
-            memoryWriter.addSegment(properties.mappingMemoryName, agentName);
+            memoryWriter.addSegment(properties.memoryName, agentName);
 
         if (not result.success)
         {
@@ -79,7 +82,7 @@ namespace armarx::armem
         dict->addElement("scan", toAron(laserScan));
 
         update.instancesData = {dict};
-        update.timeCreated = iceTimestamp;
+        update.timeCreated   = iceTimestamp;
 
         ARMARX_DEBUG << "Committing " << update << " at time " << iceTimestamp;
         armem::EntityUpdateResult updateResult = memoryWriter.commit(update);
@@ -94,4 +97,4 @@ namespace armarx::armem
         return updateResult.success;
     }
 
-} // namespace armarx::armem
+} // namespace armarx::armem::vision::laser_scans::client
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.h
similarity index 72%
rename from source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h
rename to source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.h
index ab4187ada2cf1476f1841c869112e5cce1b3d78c..afc383634b87a31fc84a841a5658f83ec845ee27 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h
+++ b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.h
@@ -27,12 +27,11 @@
 #include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h>
 
 #include <RobotAPI/interface/units/LaserScannerUnit.h>
-
 #include <RobotAPI/libraries/armem/client/Writer.h>
 #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
 
 
-namespace armarx::armem
+namespace armarx::armem::vision::laser_scans::client
 {
 
     /**
@@ -46,12 +45,13 @@ namespace armarx::armem
     *
     * Detailed description of class ExampleClient.
     */
-    class MappingDataWriter
+    class Writer
     {
     public:
 
-        MappingDataWriter(armem::client::MemoryNameSystem& memoryNameSystem);
-        virtual ~MappingDataWriter();
+        Writer(armem::client::MemoryNameSystem& memoryNameSystem);
+        virtual ~Writer();
+
 
         void connect();
 
@@ -60,9 +60,13 @@ namespace armarx::armem
         // void connect() override;
 
         /// to be called in Component::addPropertyDefinitions
-        void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def) /*override*/;
+        void registerPropertyDefinitions(
+            armarx::PropertyDefinitionsPtr& def) /*override*/;
 
-        bool storeSensorData(const LaserScan& laserScan, const std::string& frame, const std::string& agentName, const std::int64_t& timestamp);
+        bool storeSensorData(const LaserScan& laserScan,
+                             const std::string& frame,
+                             const std::string& agentName,
+                             const std::int64_t& timestamp);
 
     private:
         armem::client::MemoryNameSystem& memoryNameSystem;
@@ -71,15 +75,14 @@ namespace armarx::armem
         // Properties
         struct Properties
         {
-            std::string memoryName         = "RobotState";
-            std::string mappingMemoryName  = "Mapping";
+            std::string memoryName      = "Vision";
+            std::string coreSegmentName = "LaserScans";
         } properties;
 
         std::mutex memoryWriterMutex;
 
+        const std::string propertyPrefix = "mem.vision.";
 
-        const std::string propertyPrefix = "mem.mapping.";
     };
 
-
-} // namespace armarx::armem
+} // namespace armarx::armem::vision::laser_scans::client
diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.cpp b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba7edf547d1d45873609d723b92934b18d7f5c88
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.cpp
@@ -0,0 +1,146 @@
+#include "Reader.h"
+
+// STD / STL
+#include <algorithm>
+#include <cstring>
+#include <map>
+#include <optional>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include <type_traits>
+
+// ICE
+#include <IceUtil/Handle.h>
+#include <IceUtil/Time.h>
+
+// Simox
+#include <SimoxUtility/algorithm/get_map_keys_values.h>
+
+// ArmarXCore
+#include "ArmarXCore/core/exceptions/LocalException.h"
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/logging/LogSender.h>
+#include <ArmarXCore/core/logging/Logging.h>
+
+// RobotAPI Interfaces
+#include "RobotAPI/libraries/aron/converter/eigen/EigenConverter.h"
+#include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h>
+#include <RobotAPI/interface/armem/server/ReadingMemoryInterface.h>
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
+
+// RobotAPI Aron
+#include <RobotAPI/libraries/armem_vision/aron/OccupancyGrid.aron.generated.h>
+#include <RobotAPI/libraries/aron/core/Exception.h>
+#include <RobotAPI/libraries/aron/core/navigator/data/complex/NDArray.h>
+
+// RobotAPI Armem
+#include <RobotAPI/libraries/armem/client/Query.h>
+#include <RobotAPI/libraries/armem/client/Reader.h>
+#include <RobotAPI/libraries/armem/client/query/Builder.h>
+#include <RobotAPI/libraries/armem/client/query/selectors.h>
+#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h>
+#include <RobotAPI/libraries/armem/core/workingmemory/Entity.h>
+#include <RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h>
+#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h>
+#include <RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h>
+#include <RobotAPI/libraries/armem/util/util.h>
+#include <RobotAPI/libraries/armem_vision/aron/LaserScan.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron_conversions.h>
+#include <RobotAPI/libraries/armem_vision/types.h>
+
+namespace armarx::armem::vision::occupancy_grid::client
+{
+
+    armarx::armem::client::query::Builder Reader::buildQuery(const Query& query) const
+    {
+        armarx::armem::client::query::Builder qb;
+
+        // clang-format off
+        qb
+        .coreSegments().withName(properties().memoryName)
+        .providerSegments().withName(query.providerName)
+        .entities().all()
+        .snapshots().beforeOrAtTime(query.timestamp);
+        // clang-format on
+
+        return qb;
+    }
+
+    OccupancyGrid asOccupancyGrid(const std::map<std::string, wm::Entity>& entities)
+    {
+        ARMARX_CHECK(not entities.empty()) << "No entities";
+        ARMARX_CHECK(entities.size() == 1) << "There should be only one entity!";
+
+        const wm::Entity& entity = entities.begin()->second;
+        ARMARX_CHECK(not entity.empty()) << "No snapshots";
+
+        const auto& entitySnapshot = entity.getLatestSnapshot();
+        ARMARX_CHECK(not entitySnapshot.empty()) << "No entity snapshot instances";
+
+        const auto& entityInstance = entitySnapshot.instances().front();
+
+        const auto aronDto = tryCast<arondto::OccupancyGrid>(entityInstance);
+        ARMARX_CHECK(aronDto) << "Failed casting to OccupancyGrid";
+
+        OccupancyGrid occupancyGrid;
+        fromAron(*aronDto, occupancyGrid);
+
+        // direct access to grid data
+        const auto ndArrayNavigator = aron::datanavigator::NDArrayNavigator::DynamicCast(
+                                          entityInstance.data()->getElement("grid"));
+        ARMARX_CHECK_NOT_NULL(ndArrayNavigator);
+
+        occupancyGrid.grid = aron::converter::AronEigenConverter::ConvertToArray<float>(*ndArrayNavigator);
+
+        return occupancyGrid;
+    }
+
+    Reader::Result Reader::query(const Query& query) const
+    {
+        const auto qb = buildQuery(query);
+
+        ARMARX_IMPORTANT << "[MappingDataReader] query ... ";
+
+        const armem::client::QueryResult qResult =
+            memoryReader().query(qb.buildQueryInput());
+
+        ARMARX_DEBUG << "[MappingDataReader] result: " << qResult;
+
+        if (not qResult.success)
+        {
+            ARMARX_WARNING << "Failed to query data from memory: "
+                           << qResult.errorMessage;
+            return {.occupancyGrid = std::nullopt,
+                    .status        = Result::Status::Error,
+                    .errorMessage  = qResult.errorMessage};
+        }
+
+        // now create result from memory
+        const auto& entities = qResult.memory.getCoreSegment(properties().memoryName)
+                               .getProviderSegment(query.providerName)
+                               .entities();
+
+        if (entities.empty())
+        {
+            ARMARX_WARNING << "No entities.";
+            return {.occupancyGrid = std::nullopt,
+                    .status        = Result::Status::NoData,
+                    .errorMessage  = "No entities"};
+        }
+
+        try
+        {
+            const auto occupancyGrid = asOccupancyGrid(entities);
+            return Result{.occupancyGrid = occupancyGrid,
+                          .status        = Result::Status::Success};
+        }
+        catch (...)
+        {
+            return Result{.status       = Result::Status::Error,
+                          .errorMessage = GetHandledExceptionString()};
+        }
+    }
+
+} // namespace armarx::armem::vision::occupancy_grid::client
diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h
new file mode 100644
index 0000000000000000000000000000000000000000..a43c2c7c1151223358d5b1ab608f824fd3cdf66f
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h
@@ -0,0 +1,74 @@
+/*
+ * 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 <mutex>
+
+#include "RobotAPI/libraries/armem/client/util/SimpleReaderBase.h"
+#include "RobotAPI/libraries/armem/core/Time.h"
+#include "RobotAPI/libraries/armem_vision/types.h"
+#include <RobotAPI/libraries/armem/client/query/Builder.h>
+
+namespace armarx::armem::vision::occupancy_grid::client
+{
+
+    class Reader : virtual public armarx::armem::client::util::SimpleReaderBase
+    {
+    public:
+        using armarx::armem::client::util::SimpleReaderBase::SimpleReaderBase;
+        ~Reader() override;
+
+        struct Query
+        {
+            std::string providerName;
+            armem::Time timestamp;
+        };
+
+        struct Result
+        {
+            std::optional<OccupancyGrid> occupancyGrid = std::nullopt;
+
+            enum Status
+            {
+                Success,
+                NoData,
+                Error
+            } status;
+
+            std::string errorMessage = "";
+
+            operator bool() const noexcept
+            {
+                return status == Status::Success;
+            }
+        };
+
+        Result query(const Query& query) const;
+
+        ::armarx::armem::client::query::Builder buildQuery(const Query& query) const;
+
+    protected:
+        std::string propertyPrefix() const override;
+        Properties defaultProperties() const override;
+    };
+
+} // namespace armarx::armem::vision::occupancy_grid::client
diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.cpp b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2930f14f479d9ae68ee222f2643edf327392a43
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.cpp
@@ -0,0 +1,70 @@
+#include "Writer.h"
+
+#include "RobotAPI/libraries/armem_vision/aron_conversions.h"
+#include <RobotAPI/libraries/armem_vision/aron/OccupancyGrid.aron.generated.h>
+
+namespace armarx::armem::vision::occupancy_grid::client
+{
+    Writer::~Writer() = default;
+
+    bool Writer::store(const OccupancyGrid& grid,
+                       const std::string& frame,
+                       const std::string& agentName,
+                       const std::int64_t& timestamp)
+    {
+        std::lock_guard g{memoryWriterMutex()};
+
+        const auto result = memoryWriter().addSegment(properties().coreSegmentName, agentName);
+
+        if (not result.success)
+        {
+            ARMARX_ERROR << result.errorMessage;
+
+            // TODO(fabian.reister): message
+            return false;
+        }
+
+        const auto iceTimestamp = Time::microSeconds(timestamp);
+
+        const auto providerId = armem::MemoryID(result.segmentID);
+        const auto entityID   = providerId.withEntityName(frame).withTimestamp(iceTimestamp);
+
+        armem::EntityUpdate update;
+        update.entityID = entityID;
+
+        arondto::OccupancyGrid aronGrid;
+        // currently only sets the header
+        toAron(aronGrid, grid);
+
+        auto dict = aronGrid.toAron();
+        dict->addElement("grid", toAron(grid.grid));
+
+        update.instancesData = {dict};
+        update.timeCreated   = iceTimestamp;
+
+        ARMARX_DEBUG << "Committing " << update << " at time " << iceTimestamp;
+        armem::EntityUpdateResult updateResult = memoryWriter().commit(update);
+
+        ARMARX_DEBUG << updateResult;
+
+        if (not updateResult.success)
+        {
+            ARMARX_ERROR << updateResult.errorMessage;
+        }
+
+        return updateResult.success;
+    }
+
+    std::string Writer::propertyPrefix() const
+    {
+        return "mem.vision.occupancy_grid.";
+    }
+
+    armarx::armem::client::util::SimpleWriterBase::SimpleWriterBase::Properties
+    Writer::defaultProperties() const
+    {
+
+        return SimpleWriterBase::Properties{.memoryName      = "Vision",
+                                            .coreSegmentName = "OccupancyGrid"};
+    }
+} // namespace armarx::armem::vision::occupancy_grid::client
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.h b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.h
new file mode 100644
index 0000000000000000000000000000000000000000..bf1268444c333d10bb3c2f9efb68da11fe697cc8
--- /dev/null
+++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.h
@@ -0,0 +1,62 @@
+/*
+ * 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 <mutex>
+
+#include "RobotAPI/libraries/armem/client/util/SimpleWriterBase.h"
+#include "RobotAPI/libraries/armem_vision/types.h"
+
+namespace armarx::armem::vision::occupancy_grid::client
+{
+
+    /**
+    * @defgroup Component-ExampleClient ExampleClient
+    * @ingroup RobotAPI-Components
+    * A description of the component ExampleClient.
+    *
+    * @class ExampleClient
+    * @ingroup Component-ExampleClient
+    * @brief Brief description of class ExampleClient.
+    *
+    * Detailed description of class ExampleClient.
+    */
+    class Writer : virtual public armarx::armem::client::util::SimpleWriterBase
+    {
+    public:
+        using armarx::armem::client::util::SimpleWriterBase::SimpleWriterBase;
+        ~Writer() override;
+
+        bool store(const OccupancyGrid& grid,
+                   const std::string& frame,
+                   const std::string& agentName,
+                   const std::int64_t& timestamp);
+
+    protected:
+        std::string propertyPrefix() const override;
+        Properties defaultProperties() const override;
+    };
+
+
+
+} // namespace armarx::armem::vision::occupancy_grid::client
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/types.h b/source/RobotAPI/libraries/armem_vision/types.h
similarity index 73%
rename from source/RobotAPI/libraries/armem_robot_mapping/types.h
rename to source/RobotAPI/libraries/armem_vision/types.h
index d822597e103177b0833014d25f0530e25f2b2075..dd975e9b1e6e76484c3077bebf9dc99c9f3a8d85 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/types.h
+++ b/source/RobotAPI/libraries/armem_vision/types.h
@@ -21,10 +21,12 @@
 
 #pragma once
 
-#include <RobotAPI/libraries/armem/core/Time.h>
+#include <vector>
+
 #include <RobotAPI/interface/units/LaserScannerUnit.h>
+#include <RobotAPI/libraries/armem/core/Time.h>
 
-namespace armarx
+namespace armarx::armem
 {
 
     struct SensorHeader
@@ -40,4 +42,20 @@ namespace armarx
         LaserScan data;
     };
 
-} // namespace armarx
+    // template<typename _ValueT = float>
+    struct OccupancyGrid
+    {
+        float resolution;
+
+        std::string frame;
+        Eigen::Affine3f pose;
+
+        // using ValueType = _ValueT;
+        using CellType = float;
+        using Grid     = Eigen::Array<CellType, Eigen::Dynamic, Eigen::Dynamic>;
+
+        Grid grid;
+    };
+
+
+} // namespace armarx::armem
diff --git a/source/RobotAPI/libraries/aron/converter/eigen/EigenConverter.h b/source/RobotAPI/libraries/aron/converter/eigen/EigenConverter.h
index 787ae30a23f097821989fcd9658a3fa31d6b4761..4a5a02e56edc8fae399b2a216b4ee77a6f4536d8 100644
--- a/source/RobotAPI/libraries/aron/converter/eigen/EigenConverter.h
+++ b/source/RobotAPI/libraries/aron/converter/eigen/EigenConverter.h
@@ -28,6 +28,7 @@
 // Eigen
 #include <Eigen/Geometry>
 #include <Eigen/Core>
+#include <Eigen/src/Core/util/Constants.h>
 
 // ArmarX
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
@@ -103,9 +104,26 @@ namespace armarx::aron::converter
             return ConvertToMatrix<T, Rows, Cols>(*nav);
         }
 
-        template<typename T, int Rows, int Cols>
+        template<typename T>
+        static Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> ConvertToDynamicMatrix(const datanavigator::NDArrayNavigator& nav)
+        {
+            const auto dims = nav.getDimensions();
+
+            using MatrixT = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>;
+
+            MatrixT ret;
+            memcpy(reinterpret_cast<unsigned char*>(ret.data()), nav.getData(), std::accumulate(std::begin(dims), std::end(dims), 1, std::multiplies<>()));
+            return ret;
+        }
+
+        template<typename T, int Rows = Eigen::Dynamic, int Cols = Eigen::Dynamic>
         static Eigen::Matrix<T, Rows, Cols> ConvertToMatrix(const datanavigator::NDArrayNavigator& nav)
         {
+            if constexpr(Rows == Eigen::Dynamic and Cols == Eigen::Dynamic)
+            {
+                return ConvertToDynamicMatrix<T>(nav);
+            }
+
             checkDimensions(nav, {Rows, Cols, sizeof(T)}, "ConvertToMatrix");
             auto dims = nav.getDimensions();
 
@@ -114,6 +132,61 @@ namespace armarx::aron::converter
             return ret;
         }
 
+        template<typename T>
+        static datanavigator::NDArrayNavigatorPtr ConvertFromMatrix(const Eigen::Matrix < T, Eigen::Dynamic, Eigen::Dynamic >& mat)
+        {
+            datanavigator::NDArrayNavigatorPtr ndArr(new datanavigator::NDArrayNavigator);
+
+            ndArr->setDimensions({static_cast<int>(mat.rows()), static_cast<int>(mat.cols())});
+            ndArr->setData(sizeof(T) * mat.size(), reinterpret_cast <const unsigned char* >(mat.data()));
+
+            return ndArr;
+        }
+
+
+        // Eigen::Array
+
+        template<typename T>
+        static Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic> ConvertToDynamicArray(const datanavigator::NDArrayNavigator& nav)
+        {
+            const auto dims = nav.getDimensions();
+
+            using ArrayT = Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic>;
+
+            ArrayT ret;
+            memcpy(reinterpret_cast<unsigned char*>(ret.data()), nav.getData(), std::accumulate(std::begin(dims), std::end(dims), 1, std::multiplies<>()));
+            return ret;
+        }
+
+        template<typename T, int Rows = Eigen::Dynamic, int Cols = Eigen::Dynamic>
+        static Eigen::Matrix<T, Rows, Cols> ConvertToArray(const datanavigator::NDArrayNavigator& nav)
+        {
+            if constexpr(Rows == Eigen::Dynamic and Cols == Eigen::Dynamic)
+            {
+                return ConvertToDynamicArray<T>(nav);
+            }
+
+            checkDimensions(nav, {Rows, Cols, sizeof(T)}, "ConvertToMatrix");
+            auto dims = nav.getDimensions();
+
+            Eigen::Array<T, Rows, Cols> ret;
+            memcpy(reinterpret_cast<unsigned char*>(ret.data()), nav.getData(), std::accumulate(std::begin(dims), std::end(dims), 1, std::multiplies<int>()));
+            return ret;
+        }
+
+        template<typename T>
+        static datanavigator::NDArrayNavigatorPtr ConvertFromArray(const Eigen::Array < T, Eigen::Dynamic, Eigen::Dynamic >& mat)
+        {
+            datanavigator::NDArrayNavigatorPtr ndArr(new datanavigator::NDArrayNavigator);
+
+            ndArr->setDimensions({static_cast<int>(mat.rows()), static_cast<int>(mat.cols())});
+            ndArr->setData(sizeof(T) * mat.size(), reinterpret_cast <const unsigned char* >(mat.data()));
+
+            return ndArr;
+        }
+
+
+
     private:
 
         /**
diff --git a/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.cpp b/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.cpp
index 91f15876447e836e6a7e5f883747ae613765a0e5..65db64e872112548b72048542cfdecb7835cd81f 100644
--- a/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.cpp
+++ b/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.cpp
@@ -90,7 +90,7 @@ namespace armarx
         return _root;
     }
 
-    bool RemoteRobot::hasRobotNode(const std::string& robotNodeName)
+    bool RemoteRobot::hasRobotNode(const std::string& robotNodeName) const
     {
         if (_cachedNodes.find(name) == _cachedNodes.end())
         {
@@ -103,7 +103,7 @@ namespace armarx
     }
 
 
-    bool RemoteRobot::hasRobotNode(RobotNodePtr robotNode)
+    bool RemoteRobot::hasRobotNode(RobotNodePtr robotNode) const
     {
         return this->hasRobotNode(robotNode->getName());
 
@@ -159,7 +159,7 @@ namespace armarx
         }
     }
 
-    bool RemoteRobot::hasRobotNodeSet(const std::string& name)
+    bool RemoteRobot::hasRobotNodeSet(const std::string& name) const
     {
         return _robot->hasRobotNodeSet(name);
     }
@@ -539,17 +539,17 @@ namespace armarx
 
     // Private (unused methods)
 
-    bool RemoteRobot::hasEndEffector(const std::string& endEffectorName)
+    bool RemoteRobot::hasEndEffector(const std::string& endEffectorName) const
     {
         return false;
     }
 
-    EndEffectorPtr RemoteRobot::getEndEffector(const std::string& endEffectorName)
+    EndEffectorPtr RemoteRobot::getEndEffector(const std::string& endEffectorName) const
     {
         return EndEffectorPtr();
     }
 
-    void RemoteRobot::getEndEffectors(std::vector<EndEffectorPtr>& storeEEF) {}
+    void RemoteRobot::getEndEffectors(std::vector<EndEffectorPtr>& storeEEF) const {}
     void RemoteRobot::setRootNode(RobotNodePtr node) {}
     void RemoteRobot::registerRobotNode(RobotNodePtr node) {}
     void RemoteRobot::deregisterRobotNode(RobotNodePtr node) {}
diff --git a/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.h b/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.h
index b687f0e0fc8cc1eac7b967b10408ec8b63440f56..734db7407354682df6fa45c866acdb0e9c8c0a6f 100644
--- a/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.h
+++ b/source/RobotAPI/libraries/core/remoterobot/RemoteRobot.h
@@ -145,13 +145,13 @@ namespace armarx
 
         VirtualRobot::RobotNodePtr getRootNode() const override;
 
-        bool hasRobotNode(const std::string& robotNodeName) override;
-        bool hasRobotNode(VirtualRobot::RobotNodePtr) override;
+        bool hasRobotNode(const std::string& robotNodeName) const override;
+        bool hasRobotNode(VirtualRobot::RobotNodePtr) const override;
 
         VirtualRobot::RobotNodePtr getRobotNode(const std::string& robotNodeName) const override;
         void getRobotNodes(std::vector< VirtualRobot::RobotNodePtr >& storeNodes, bool clearVector = true) const override;
 
-        bool hasRobotNodeSet(const std::string& name) override;
+        bool hasRobotNodeSet(const std::string& name) const override;
         VirtualRobot::RobotNodeSetPtr getRobotNodeSet(const std::string& nodeSetName) const override;
         void getRobotNodeSets(std::vector<VirtualRobot::RobotNodeSetPtr>& storeNodeSet) const override;
 
@@ -223,11 +223,11 @@ namespace armarx
     protected:
 
         /// Not implemented yet
-        bool hasEndEffector(const std::string& endEffectorName) override;
+        bool hasEndEffector(const std::string& endEffectorName) const override;
         /// Not implemented yet
-        VirtualRobot::EndEffectorPtr getEndEffector(const std::string& endEffectorName) override;
+        VirtualRobot::EndEffectorPtr getEndEffector(const std::string& endEffectorName) const override;
         /// Not implemented yet
-        void getEndEffectors(std::vector<VirtualRobot::EndEffectorPtr>& storeEEF) override;
+        void getEndEffectors(std::vector<VirtualRobot::EndEffectorPtr>& storeEEF) const override;
 
         /// Not implemented yet
         void setRootNode(VirtualRobot::RobotNodePtr node) override;