From 6e1ec4bd68528ccf10ec85a1021f721bfb5cfcd0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Mon, 6 Mar 2023 16:43:18 +0100
Subject: [PATCH 01/13] Setup new component for laser scanner feature
 extraction

---
 .../navigation/components/CMakeLists.txt      |   2 +
 .../CMakeLists.txt                            |  51 ++++
 .../Component.cpp                             | 220 ++++++++++++++++++
 .../Component.h                               | 137 +++++++++++
 .../ComponentInterface.ice                    |  35 +++
 5 files changed, 445 insertions(+)
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/ComponentInterface.ice

diff --git a/source/armarx/navigation/components/CMakeLists.txt b/source/armarx/navigation/components/CMakeLists.txt
index efa6ec80..651c7ef6 100644
--- a/source/armarx/navigation/components/CMakeLists.txt
+++ b/source/armarx/navigation/components/CMakeLists.txt
@@ -21,3 +21,5 @@ add_subdirectory(dynamic_scene_provider)
 add_subdirectory(human_simulator)
 
 add_subdirectory(navigation_skill_provider)
+
+add_subdirectory(laser_scanner_feature_extraction)
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
new file mode 100644
index 00000000..e2115cb0
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
@@ -0,0 +1,51 @@
+armarx_add_component(laser_scanner_feature_extraction
+    ICE_DEPENDENCIES
+        ArmarXCoreInterfaces
+        # RobotAPIInterfaces
+    SOURCES
+        LaserScannerFeatureExtraction.cpp
+        FeatureExtractor.cpp
+        ArVizDrawer.cpp
+        ScanClustering.cpp
+        ChainApproximation.cpp
+        #conversions
+        conversions/eigen.cpp
+        conversions/pcl.cpp
+        EnclosingEllipsoid.cpp
+        Path.cpp
+    HEADERS
+        Component.h
+        LaserScannerFeatureExtraction.h
+        FeatureExtractor.h
+        ArVizDrawer.h
+        ScanClustering.h
+        ChainApproximation.h
+        EnclosingEllipsoid.h
+        Path.h
+        #conversions
+        conversions/eigen.h
+        conversions/pcl_eigen.h
+        conversions/opencv_eigen.h
+        conversions/opencv_pcl.h
+        conversions/pcl.h
+    DEPENDENCIES
+        # ArmarXCore
+        ArmarXCore
+        ## ArmarXCoreComponentPlugins  # For DebugObserver plugin.
+        # ArmarXGui
+        ## ArmarXGuiComponentPlugins  # For RemoteGui plugin.
+        # RobotAPI
+        ## RobotAPICore
+        ## RobotAPIInterfaces
+        ## RobotAPIComponentPlugins  # For ArViz and other plugins.
+
+
+        # armarx_navigation
+        # armarx_navigation::my_library
+
+    # DEPENDENCIES_LEGACY
+        ## Add libraries that do not provide any targets but ${FOO_*} variables.
+        # FOO
+    # If you need a separate shared component library you can enable it with the following flag.
+    # SHARED_COMPONENT_LIBRARY
+)
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
new file mode 100644
index 00000000..d94c3ade
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
@@ -0,0 +1,220 @@
+/**
+ * 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    navigation::ArmarXObjects::laser_scanner_feature_extraction
+ * @author     Tobias Gröger ( tobias dot groeger at student dot kit dot edu )
+ * @date       2023
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+#include "Component.h"
+
+// Include headers you only need in function definitions in the .cpp.
+
+// #include <Eigen/Core>
+
+// #include <SimoxUtility/color/Color.h>
+
+#include <ArmarXCore/libraries/DecoupledSingleComponent/Decoupled.h>
+
+
+namespace armarx::navigation::components::laser_scanner_feature_extraction
+{
+
+    const std::string
+    Component::defaultName = "LaserScannerFeatureExtraction";
+
+
+    armarx::PropertyDefinitionsPtr
+    Component::createPropertyDefinitions()
+    {
+        armarx::PropertyDefinitionsPtr def = new armarx::ComponentPropertyDefinitions(getConfigIdentifier());
+
+        // Publish to a topic (passing the TopicListenerPrx).
+        // def->topic(myTopicListener);
+
+        // Subscribe to a topic (passing the topic name).
+        // def->topic<PlatformUnitListener>("MyTopic");
+
+        // Use (and depend on) another component (passing the ComponentInterfacePrx).
+        // def->component(myComponentProxy)
+
+
+        // Add a required property. (The component won't start without a value being set.)
+        // def->required(properties.boxLayerName, "p.box.LayerName", "Name of the box layer in ArViz.");
+
+        // Add an optional property.
+        // def->optional(properties.boxLayerName, "p.box.LayerName", "Name of the box layer in ArViz.");
+        // def->optional(properties.numBoxes, "p.box.Number", "Number of boxes to draw in ArViz.");
+
+        return def;
+    }
+
+
+    void
+    Component::onInitComponent()
+    {
+        // Topics and properties defined above are automagically registered.
+
+        // Keep debug observer data until calling `sendDebugObserverBatch()`.
+        // (Requires the armarx::DebugObserverComponentPluginUser.)
+        // setDebugObserverBatchModeEnabled(true);
+    }
+
+
+    void
+    Component::onConnectComponent()
+    {
+        // Do things after connecting to topics and components.
+
+        /* (Requires the armarx::DebugObserverComponentPluginUser.)
+        // Use the debug observer to log data over time.
+        // The data can be viewed in the ObserverView and the LivePlotter.
+        // (Before starting any threads, we don't need to lock mutexes.)
+        {
+            setDebugObserverDatafield("numBoxes", properties.numBoxes);
+            setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
+            sendDebugObserverBatch();
+        }
+        */
+
+        /* (Requires the armarx::ArVizComponentPluginUser.)
+        // Draw boxes in ArViz.
+        // (Before starting any threads, we don't need to lock mutexes.)
+        drawBoxes(properties, arviz);
+        */
+
+        /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
+        // Setup the remote GUI.
+        {
+            createRemoteGuiTab();
+            RemoteGui_startRunningTask();
+        }
+        */
+    }
+
+
+    void
+    Component::onDisconnectComponent()
+    {
+    }
+
+
+    void
+    Component::onExitComponent()
+    {
+    }
+
+
+    std::string
+    Component::getDefaultName() const
+    {
+        return Component::defaultName;
+    }
+
+
+    std::string
+    Component::GetDefaultName()
+    {
+        return Component::defaultName;
+    }
+
+
+    /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
+    void
+    Component::createRemoteGuiTab()
+    {
+        using namespace armarx::RemoteGui::Client;
+
+        // Setup the widgets.
+
+        tab.boxLayerName.setValue(properties.boxLayerName);
+
+        tab.numBoxes.setValue(properties.numBoxes);
+        tab.numBoxes.setRange(0, 100);
+
+        tab.drawBoxes.setLabel("Draw Boxes");
+
+        // Setup the layout.
+
+        GridLayout grid;
+        int row = 0;
+        {
+            grid.add(Label("Box Layer"), {row, 0}).add(tab.boxLayerName, {row, 1});
+            ++row;
+
+            grid.add(Label("Num Boxes"), {row, 0}).add(tab.numBoxes, {row, 1});
+            ++row;
+
+            grid.add(tab.drawBoxes, {row, 0}, {2, 1});
+            ++row;
+        }
+
+        VBoxLayout root = {grid, VSpacer()};
+        RemoteGui_createTab(getName(), root, &tab);
+    }
+
+
+    void
+    Component::RemoteGui_update()
+    {
+        if (tab.boxLayerName.hasValueChanged() || tab.numBoxes.hasValueChanged())
+        {
+            std::scoped_lock lock(propertiesMutex);
+            properties.boxLayerName = tab.boxLayerName.getValue();
+            properties.numBoxes = tab.numBoxes.getValue();
+
+            {
+                setDebugObserverDatafield("numBoxes", properties.numBoxes);
+                setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
+                sendDebugObserverBatch();
+            }
+        }
+        if (tab.drawBoxes.wasClicked())
+        {
+            // Lock shared variables in methods running in separate threads
+            // and pass them to functions. This way, the called functions do
+            // not need to think about locking.
+            std::scoped_lock lock(propertiesMutex);
+            drawBoxes(properties, arviz);
+        }
+    }
+    */
+
+
+    /* (Requires the armarx::ArVizComponentPluginUser.)
+    void
+    Component::drawBoxes(const Component::Properties& p, viz::Client& arviz)
+    {
+        // Draw something in ArViz (requires the armarx::ArVizComponentPluginUser.
+        // See the ArVizExample in RobotAPI for more examples.
+
+        viz::Layer layer = arviz.layer(p.boxLayerName);
+        for (int i = 0; i < p.numBoxes; ++i)
+        {
+            layer.add(viz::Box("box_" + std::to_string(i))
+                      .position(Eigen::Vector3f(i * 100, 0, 0))
+                      .size(20).color(simox::Color::blue()));
+        }
+        arviz.commit(layer);
+    }
+    */
+
+
+    ARMARX_REGISTER_COMPONENT_EXECUTABLE(Component, Component::GetDefaultName());
+
+}  // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
new file mode 100644
index 00000000..43520364
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
@@ -0,0 +1,137 @@
+/**
+ * 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    navigation::ArmarXObjects::laser_scanner_feature_extraction
+ * @author     Tobias Gröger ( tobias dot groeger at student dot kit dot edu )
+ * @date       2023
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+#pragma once
+
+
+// #include <mutex>
+
+#include <ArmarXCore/core/Component.h>
+
+// #include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
+
+// #include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h>
+
+// #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
+
+#include <armarx/navigation/components/laser_scanner_feature_extraction/ComponentInterface.h>
+
+
+namespace armarx::navigation::components::laser_scanner_feature_extraction
+{
+
+    class Component :
+        virtual public armarx::Component,
+        virtual public armarx::navigation::components::laser_scanner_feature_extraction::ComponentInterface
+        // , virtual public armarx::DebugObserverComponentPluginUser
+        // , virtual public armarx::LightweightRemoteGuiComponentPluginUser
+        // , virtual public armarx::ArVizComponentPluginUser
+    {
+    public:
+
+        /// @see armarx::ManagedIceObject::getDefaultName()
+        std::string getDefaultName() const override;
+
+        /// Get the component's default name.
+        static std::string GetDefaultName();
+
+
+    protected:
+
+        /// @see PropertyUser::createPropertyDefinitions()
+        armarx::PropertyDefinitionsPtr createPropertyDefinitions() override;
+
+        /// @see armarx::ManagedIceObject::onInitComponent()
+        void onInitComponent() override;
+
+        /// @see armarx::ManagedIceObject::onConnectComponent()
+        void onConnectComponent() override;
+
+        /// @see armarx::ManagedIceObject::onDisconnectComponent()
+        void onDisconnectComponent() override;
+
+        /// @see armarx::ManagedIceObject::onExitComponent()
+        void onExitComponent() override;
+
+
+        /* (Requires armarx::LightweightRemoteGuiComponentPluginUser.)
+        /// This function should be called once in onConnect() or when you
+        /// need to re-create the Remote GUI tab.
+        void createRemoteGuiTab();
+
+        /// After calling `RemoteGui_startRunningTask`, this function is
+        /// called periodically in a separate thread. If you update variables,
+        /// make sure to synchronize access to them.
+        void RemoteGui_update() override;
+        */
+
+
+    private:
+
+        // Private methods go here.
+
+        // Forward declare `Properties` if you used it before its defined.
+        // struct Properties;
+
+        /* (Requires the armarx::ArVizComponentPluginUser.)
+        /// Draw some boxes in ArViz.
+        void drawBoxes(const Properties& p, viz::Client& arviz);
+        */
+
+
+    private:
+
+        static const std::string defaultName;
+
+
+        // Private member variables go here.
+
+
+        /// Properties shown in the Scenario GUI.
+        struct Properties
+        {
+            std::string boxLayerName = "boxes";
+            int numBoxes = 10;
+        };
+        Properties properties;
+        /* Use a mutex if you access variables from different threads
+         * (e.g. ice functions and RemoteGui_update()).
+        std::mutex propertiesMutex;
+        */
+
+
+        /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
+        /// Tab shown in the Remote GUI.
+        struct RemoteGuiTab : armarx::RemoteGui::Client::Tab
+        {
+            armarx::RemoteGui::Client::LineEdit boxLayerName;
+            armarx::RemoteGui::Client::IntSpinBox numBoxes;
+
+            armarx::RemoteGui::Client::Button drawBoxes;
+        };
+        RemoteGuiTab tab;
+        */
+
+    };
+
+}  // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ComponentInterface.ice b/source/armarx/navigation/components/laser_scanner_feature_extraction/ComponentInterface.ice
new file mode 100644
index 00000000..2c380438
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ComponentInterface.ice
@@ -0,0 +1,35 @@
+/*
+ * 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    navigation::LaserScannerFeatureExtraction
+ * author     Tobias Gröger ( tobias dot groeger at student dot kit dot edu )
+ * date       2023
+ * copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *            GNU General Public License
+ */
+
+
+#pragma once
+
+
+module armarx {  module navigation {  module components {  module laser_scanner_feature_extraction
+{
+
+    interface ComponentInterface
+    {
+	// Define your interface here.
+    };
+
+};};};};
-- 
GitLab


From 388d49d03df0ae4032e15de114c9ad75bd6c95ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Mon, 6 Mar 2023 16:46:38 +0100
Subject: [PATCH 02/13] Copy files from old component

---
 .../ArVizDrawer.cpp                           | 226 +++++++
 .../ArVizDrawer.h                             | 109 ++++
 .../CMakeLists.txt                            |  19 +-
 .../ChainApproximation.cpp                    | 214 +++++++
 .../ChainApproximation.h                      |  97 +++
 .../Component.cpp                             | 566 ++++++++++++++----
 .../Component.h                               | 171 ++++--
 .../EnclosingEllipsoid.cpp                    | 124 ++++
 .../EnclosingEllipsoid.h                      |  70 +++
 .../FeatureExtractor.cpp                      | 239 ++++++++
 .../FeatureExtractor.h                        |  91 +++
 .../laser_scanner_feature_extraction/Path.cpp |  24 +
 .../laser_scanner_feature_extraction/Path.h   |  19 +
 .../ScanClustering.cpp                        |  71 +++
 .../ScanClustering.h                          |  71 +++
 .../conversions/eigen.cpp                     |  23 +
 .../conversions/eigen.h                       |  53 ++
 .../conversions/opencv.h                      |  47 ++
 .../conversions/opencv_eigen.h                |  52 ++
 .../conversions/opencv_pcl.h                  |  57 ++
 .../conversions/pcl.cpp                       |  27 +
 .../conversions/pcl.h                         |  39 ++
 .../conversions/pcl_eigen.h                   |  92 +++
 .../geometry.h                                |  57 ++
 .../test/CMakeLists.txt                       |   5 +
 .../LaserScannerFeatureExtractionTest.cpp     |  65 ++
 26 files changed, 2453 insertions(+), 175 deletions(-)
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_eigen.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_pcl.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.cpp
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl_eigen.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/geometry.h
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/test/CMakeLists.txt
 create mode 100644 source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp

diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
new file mode 100644
index 00000000..fe927c0e
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
@@ -0,0 +1,226 @@
+#include "ArVizDrawer.h"
+
+#include <iterator>
+#include <string>
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+#include <pcl/impl/point_types.hpp>
+#include <pcl/point_cloud.h>
+
+#include <SimoxUtility/color/Color.h>
+
+#include "RobotAPI/components/ArViz/Client/Client.h"
+#include "RobotAPI/components/ArViz/Client/Elements.h"
+#include "RobotAPI/components/ArViz/Client/elements/Color.h"
+#include "RobotAPI/components/ArViz/Client/elements/Line.h"
+#include "RobotAPI/components/ArViz/Client/elements/Path.h"
+#include "RobotAPI/components/ArViz/Client/elements/PointCloud.h"
+#include "RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/StyleSheets.h"
+
+#include "RobotComponents/libraries/cartographer/types.h"
+#include "RobotComponents/libraries/cartographer/util/laser_scanner_conversion.h"
+
+#include "FeatureExtractor.h"
+#include "conversions/eigen.h"
+#include "conversions/pcl.h"
+#include "conversions/pcl_eigen.h"
+
+namespace armarx::laser_scanner_feature_extraction
+{
+
+    void ArVizDrawer::draw(const std::vector<Features>& features,
+                           const std::string& frame,
+                           const Eigen::Isometry3f& globalSensorPose)
+    {
+        // drawCircles(features, frame, globalSensorPose);
+        drawConvexHulls(features, frame, globalSensorPose);
+        drawEllipsoids(features, frame, globalSensorPose);
+        drawChains(features, frame, globalSensorPose);
+    }
+
+    void ArVizDrawer::draw(const cartographer::LaserScannerMessage& msg,
+                           const Eigen::Isometry3f& globalSensorPose, const simox::Color& color)
+    {
+        auto layer = arviz.layer("points_" + msg.frame);
+
+        const auto pointCloud = conversions::eigen2pcl(toCartesian<Eigen::Vector3f>(msg.scan));
+
+        layer.add(viz::PointCloud("points_" + std::to_string(layer.size()))
+                  .pointCloud(pointCloud, viz::Color(color))
+                  .pointSizeInPixels(5)
+                  .pose(globalSensorPose));
+        arviz.commit(layer);
+    }
+
+    void
+    ArVizDrawer::draw(const std::string& layerName,
+                      const Circle& circle,
+                      const Eigen::Isometry3f& robotGlobalPose,
+                      const simox::Color& color)
+    {
+        auto layer = arviz.layer(layerName);
+
+        drawCircle(layer, circle, robotGlobalPose, color);
+        arviz.commit(layer);
+    }
+
+    void
+    ArVizDrawer::drawCircle(viz::Layer& layer,
+                            const Circle& circle,
+                            const Eigen::Isometry3f& globalSensorPose,
+                            const simox::Color& color)
+    {
+
+        const Eigen::Vector3f position =
+            globalSensorPose * Eigen::Vector3f(circle.center.x(), circle.center.y(), -1.F);
+
+        layer.add(viz::Ellipsoid("circle_" + std::to_string(layer.size()))
+                  .axisLengths(Eigen::Vector3f{circle.radius, circle.radius, 0.F})
+                  .position(position)
+                  .color(simox::Color::red(200, 100)));
+    }
+
+    void
+    ArVizDrawer::drawCircles(const std::vector<Features>& features,
+                             const std::string& frame,
+                             const Eigen::Isometry3f& globalSensorPose,
+                             const simox::Color& color)
+    {
+        auto layer = arviz.layer("circles_" + frame);
+
+        std::for_each(features.begin(),
+                      features.end(),
+                      [&](const Features& f)
+                      {
+                          if (not f.circle)
+                          {
+                              return;
+                          }
+                          drawCircle(layer, *f.circle, globalSensorPose, color);
+                      });
+
+        arviz.commit(layer);
+    }
+
+    void ArVizDrawer::drawConvexHulls(const std::vector<Features>& features,
+                                      const std::string& frame,
+                                      const Eigen::Isometry3f& globalSensorPose)
+    {
+        auto layer = arviz.layer("convex_hulls_" + frame);
+
+        std::for_each(features.begin(),
+                      features.end(),
+                      [&](const Features & f)
+        {
+            if (not f.convexHull)
+            {
+                return;
+            }
+            drawConvexHull(layer, *f.convexHull, globalSensorPose, simox::Color::red(100, 80));
+        });
+
+        arviz.commit(layer);
+    }
+
+    void ArVizDrawer::draw(const std::string& layerName, const VirtualRobot::MathTools::ConvexHull2D& robotHull,
+                           const Eigen::Isometry3f& robotGlobalPose,
+                           const simox::Color& color)
+    {
+        auto layer = arviz.layer(layerName);
+
+        drawConvexHull(layer, robotHull, robotGlobalPose, color);
+        arviz.commit(layer);
+    }
+
+    void ArVizDrawer::drawConvexHull(viz::Layer& layer,
+                                     const VirtualRobot::MathTools::ConvexHull2D& hull,
+                                     const Eigen::Isometry3f& globalSensorPose,
+                                     const simox::Color& color)
+    {
+        const auto points = conversions::to3D(hull.vertices);
+
+        layer.add(viz::Polygon("convex_hull_" + std::to_string(layer.size()))
+                  .points(points)
+                  .color(color)
+                  .pose(globalSensorPose));
+    }
+
+    void ArVizDrawer::drawEllipsoids(const std::vector<Features>& features,
+                                     const std::string& frame,
+                                     const Eigen::Isometry3f& globalSensorPose)
+    {
+        auto layer = arviz.layer("ellipsoids_" + frame);
+
+        std::for_each(features.begin(),
+                      features.end(),
+                      [&](const Features & f)
+        {
+            if (not f.ellipsoid)
+            {
+                return;
+            }
+            drawEllipsoid(layer, *f.ellipsoid, globalSensorPose);
+        });
+
+        arviz.commit(layer);
+    }
+
+    void ArVizDrawer::drawEllipsoid(viz::Layer& layer,
+                                    const Ellipsoid& ellipsoid,
+                                    const Eigen::Isometry3f& globalSensorPose)
+    {
+
+        const Eigen::Isometry3f pose = globalSensorPose * ellipsoid.pose;
+
+        layer.add(viz::Ellipsoid("ellipsoid_" + std::to_string(layer.size()))
+                  .axisLengths(conversions::to3D(ellipsoid.radii))
+                  .pose(pose)
+                  .color(simox::Color(255, 102, 0, 128))); // orange, but a bit more shiny
+    }
+
+    void ArVizDrawer::drawChains(const std::vector<Features>& features,
+                                 const std::string& frame,
+                                 const Eigen::Isometry3f& globalSensorPose)
+    {
+        auto layer = arviz.layer("chains_" + frame);
+
+        std::for_each(features.begin(),
+                      features.end(),
+                      [&](const Features & f)
+        {
+            if (not f.chain)
+            {
+                return;
+            }
+            drawChain(layer, *f.chain, globalSensorPose);
+
+            // const auto ellipsoids = f.linesAsEllipsoids(50);
+            // for (const auto& ellipsoid : ellipsoids)
+            // {
+            //     drawEllipsoid(layer, ellipsoid, globalSensorPose);
+            // }
+        });
+
+        arviz.commit(layer);
+    }
+
+    void ArVizDrawer::drawChain(viz::Layer& layer,
+                                const Points& chain,
+                                const Eigen::Isometry3f& globalSensorPose)
+    {
+        if (chain.size() < 2)
+        {
+            return;
+        }
+
+        const auto cloud = conversions::to3D(chain);
+
+        layer.add(viz::Path("chain_" + std::to_string(layer.size()))
+                  .points(cloud)
+                  .width(7.F)
+                  .color(viz::Color::blue())
+                  .pose(globalSensorPose));
+    }
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
new file mode 100644
index 00000000..59045681
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
@@ -0,0 +1,109 @@
+/*
+ * 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 <pcl/point_cloud.h>
+#include <pcl/point_types.h>
+
+#include <SimoxUtility/color/Color.h>
+#include <VirtualRobot/VirtualRobot.h>
+
+#include <RobotAPI/components/ArViz/Client/ScopedClient.h>
+
+#include "RobotComponents/components/LaserScannerFeatureExtraction/FeatureExtractor.h"
+
+namespace armarx::cartographer
+{
+    struct LaserScannerMessage;
+}
+
+namespace armarx::laser_scanner_feature_extraction
+{
+
+    struct LineSegment2Df;
+    // struct Circle;
+    // struct Ellipsoid;
+
+    class ArVizDrawer 
+    {
+    public:
+        using Points = std::vector<Eigen::Vector2f>;
+
+        ArVizDrawer(armarx::viz::Client& arviz) : arviz(arviz)
+        {
+        }
+
+
+        void draw(const std::vector<Features>& features,
+                  const std::string& frame,
+                  const Eigen::Isometry3f& globalSensorPose);
+
+        void draw(const cartographer::LaserScannerMessage& msg,
+                  const Eigen::Isometry3f& globalSensorPose,
+                  const simox::Color& color);
+
+        void draw(const std::string& layerName,
+                  const Circle& circle,
+                  const Eigen::Isometry3f& robotGlobalPose,
+                  const simox::Color& color);
+
+        void draw(const std::string& layerName,
+                  const VirtualRobot::MathTools::ConvexHull2D& robotHull,
+                  const Eigen::Isometry3f& robotGlobalPose,
+                  const simox::Color& color = simox::Color::red(100, 80));
+
+    private:
+        void drawCircles(const std::vector<Features>& features,
+                         const std::string& frame,
+                         const Eigen::Isometry3f& globalSensorPose,
+                         const simox::Color& color);
+        void drawCircle(viz::Layer& layer,
+                        const Circle& circle,
+                        const Eigen::Isometry3f& globalSensorPose,
+                        const simox::Color& color);
+
+        void drawConvexHulls(const std::vector<Features>& features,
+                             const std::string& frame,
+                             const Eigen::Isometry3f& globalSensorPose);
+        void drawConvexHull(viz::Layer& layer,
+                            const VirtualRobot::MathTools::ConvexHull2D& hull,
+                            const Eigen::Isometry3f& globalSensorPose,
+                            const simox::Color& color);
+
+        void drawEllipsoids(const std::vector<Features>& features,
+                            const std::string& frame,
+                            const Eigen::Isometry3f& globalSensorPose);
+
+        void drawEllipsoid(viz::Layer& layer,
+                           const Ellipsoid& ellipsoid,
+                           const Eigen::Isometry3f& globalSensorPose);
+
+        void drawChains(const std::vector<Features>& features,
+                        const std::string& frame,
+                        const Eigen::Isometry3f& globalSensorPose);
+        void drawChain(viz::Layer& layer,
+                       const Points& chain,
+                       const Eigen::Isometry3f& globalSensorPose);
+
+        armarx::viz::Client arviz;
+    };
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
index e2115cb0..8e5de1c1 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
@@ -3,7 +3,7 @@ armarx_add_component(laser_scanner_feature_extraction
         ArmarXCoreInterfaces
         # RobotAPIInterfaces
     SOURCES
-        LaserScannerFeatureExtraction.cpp
+        Component.cpp
         FeatureExtractor.cpp
         ArVizDrawer.cpp
         ScanClustering.cpp
@@ -15,7 +15,6 @@ armarx_add_component(laser_scanner_feature_extraction
         Path.cpp
     HEADERS
         Component.h
-        LaserScannerFeatureExtraction.h
         FeatureExtractor.h
         ArVizDrawer.h
         ScanClustering.h
@@ -31,21 +30,13 @@ armarx_add_component(laser_scanner_feature_extraction
     DEPENDENCIES
         # ArmarXCore
         ArmarXCore
-        ## ArmarXCoreComponentPlugins  # For DebugObserver plugin.
+        ArmarXCoreComponentPlugins  # For DebugObserver plugin.
         # ArmarXGui
         ## ArmarXGuiComponentPlugins  # For RemoteGui plugin.
         # RobotAPI
         ## RobotAPICore
         ## RobotAPIInterfaces
-        ## RobotAPIComponentPlugins  # For ArViz and other plugins.
-
-
-        # armarx_navigation
-        # armarx_navigation::my_library
-
-    # DEPENDENCIES_LEGACY
-        ## Add libraries that do not provide any targets but ${FOO_*} variables.
-        # FOO
-    # If you need a separate shared component library you can enable it with the following flag.
-    # SHARED_COMPONENT_LIBRARY
+        RobotAPIComponentPlugins  # For ArViz and other plugins.
+        armem_vision
+        RobotComponents::Cartographer
 )
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.cpp
new file mode 100644
index 00000000..905cf765
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.cpp
@@ -0,0 +1,214 @@
+#include "ChainApproximation.h"
+
+#include <iterator>
+#include <numeric>
+
+#include <Eigen/Geometry>
+
+#include "ArmarXCore/core/exceptions/LocalException.h"
+#include "ArmarXCore/core/exceptions/local/ExpressionException.h"
+
+namespace armarx
+{
+
+    ChainApproximation::ChainApproximation(const Points& points, const Params& params) :
+        points(points), params(params)
+    {
+        // fill indices
+        indices.resize(points.size());
+        std::iota(indices.begin(), indices.end(), 0);
+    }
+
+    ChainApproximation::ApproximationResult ChainApproximation::approximate()
+    {
+
+        int iterations = 0;
+
+        const auto maxIterConditionReached = [&]()
+        {
+            // inactive?
+            if (params.maxIterations <= 0)
+            {
+                return false;
+            }
+
+            return iterations >= params.maxIterations;
+        };
+
+        while (true)
+        {
+            if (maxIterConditionReached())
+            {
+                return ApproximationResult
+                {
+                    .condition  = ApproximationResult::TerminationCondition::IterationLimit,
+                    .iterations = iterations};
+            }
+
+            if (not approximateStep())
+            {
+                return ApproximationResult
+                {
+                    .condition       = ApproximationResult::TerminationCondition::Converged,
+                    .iterations      = iterations,
+                    .reductionFactor = 1.F - static_cast<float>(indices.size()) /
+                    static_cast<float>(points.size())};
+            }
+
+            iterations++;
+        }
+    }
+
+    ChainApproximation::Triplets ChainApproximation::getTriplets() const
+    {
+        const int nIndices = static_cast<int>(indices.size());
+
+        if (nIndices < 3)
+        {
+            return {};
+        }
+
+        Triplets triplets;
+        triplets.reserve(indices.size());
+
+        // Here, we iterate over all elements under consideration.
+        // The aim is to create a view - a sliding window - over the
+        // indices. i will always point to the centered element.
+
+        // the first element
+        triplets.emplace_back(indices.back(), indices.front(), indices.at(1));
+
+        // intermediate elements
+        for (int i = 1; i < (nIndices - 1); i++)
+        {
+            triplets.emplace_back(indices.at(i - 1), indices.at(i), indices.at(i + 1));
+        }
+
+        // the last element
+        triplets.emplace_back(indices.back(), indices.front(), indices.at(1));
+
+        return triplets;
+    }
+
+    std::vector<float>
+    ChainApproximation::computeDistances(const ChainApproximation::Triplets& triplets)
+    {
+        std::vector<float> distances;
+        distances.reserve(triplets.size());
+
+        std::transform(triplets.begin(),
+                       triplets.end(),
+                       std::back_inserter(distances),
+                       [&](const auto & triplet)
+        {
+            return computeDistance(triplet);
+        });
+
+        return distances;
+    }
+    float ChainApproximation::computeDistance(const ChainApproximation::Triplet& triplet) const
+    {
+        using Line = Eigen::ParametrizedLine<float, 2>;
+
+        const Eigen::Vector2f& ptBefore = points.at(triplet.a);
+        const Eigen::Vector2f& ptPivot  = points.at(triplet.b);
+        const Eigen::Vector2f& ptAfter  = points.at(triplet.c);
+
+        const auto line = Line::Through(ptBefore, ptAfter);
+        return line.distance(ptPivot);
+    }
+
+    bool ChainApproximation::approximateStep()
+    {
+        const size_t nIndices = indices.size();
+        if (nIndices <= 3)
+        {
+            return false;
+        }
+
+        const Triplets triplets            = getTriplets();
+        const std::vector<float> distances = computeDistances(triplets);
+
+        ARMARX_CHECK_EQUAL(triplets.size(), distances.size());
+        const int n = static_cast<int>(triplets.size());
+
+        std::vector<int> indicesToBeRemoved;
+
+        // TODO(fabian.reister): consider boundaries
+        for (int i = 1; i < n - 1; i++)
+        {
+            const auto& distance = distances.at(i);
+
+            // check distance criterion (necessary conditio)
+            if (distance >= params.distanceTh)
+            {
+                continue;
+            }
+
+            // better remove this element than those left and right (sufficient condition)
+            if (distance < std::min(distances.at(i - 1), distances.at(i + 1)))
+            {
+                indicesToBeRemoved.emplace_back(triplets.at(i).b);
+            }
+        }
+
+        // termination condition
+        if (indicesToBeRemoved.empty())
+        {
+            return false;
+        }
+
+        const auto isMatch = [&](const int& idx) -> bool
+        {
+            return std::find(indicesToBeRemoved.begin(), indicesToBeRemoved.end(), idx) !=
+            indicesToBeRemoved.end();
+        };
+
+        indices.erase(std::remove_if(indices.begin(), indices.end(), isMatch), indices.end());
+
+        return true;
+    }
+    ChainApproximation::Points ChainApproximation::approximatedChain() const
+    {
+        Points extractedPoints;
+        extractedPoints.reserve(indices.size());
+
+        std::transform(indices.begin(),
+                       indices.end(),
+                       std::back_inserter(extractedPoints),
+                       [&](const auto & idx)
+        {
+            return points.at(idx);
+        });
+
+        return extractedPoints;
+    }
+
+    std::ostream& detail::operator<<(std::ostream& str, const ApproximationResult& res)
+    {
+        using TerminationCondition = ApproximationResult::TerminationCondition;
+
+        const std::string condStr = [&res]() -> std::string
+        {
+            std::string repr;
+
+            switch (res.condition)
+            {
+                case TerminationCondition::Converged:
+                    repr = "Converged";
+                    break;
+                case TerminationCondition::IterationLimit:
+                    repr = "IterationLimit";
+                    break;
+            }
+            return repr;
+        }();
+
+        str << "ApproximationResult: ["
+            << "condition: " << condStr << " | "
+            << "iterations: " << res.iterations << " | "
+            << "reduction: " << res.reductionFactor * 100 << "%]";
+
+        return str;
+    }
+} // namespace armarx
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h
new file mode 100644
index 00000000..a7b5e6cd
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <Eigen/Core>
+
+namespace armarx
+{
+
+    namespace detail
+    {
+        struct ChainApproximationParams
+        {
+            float distanceTh; // [mm]
+
+            int maxIterations{-1};
+        };
+
+
+        struct ApproximationResult
+        {
+            enum class TerminationCondition
+            {
+                Converged,
+                IterationLimit
+            };
+            TerminationCondition condition;
+
+            int iterations;
+
+            float reductionFactor{0.F};
+        };
+
+        std::ostream& operator<<(std::ostream& str, const ApproximationResult& res);
+    } // namespace detail
+
+    class ChainApproximation
+    {
+    public:
+        using Point  = Eigen::Vector2f;
+        using Points = std::vector<Point>;
+
+        using Params = detail::ChainApproximationParams;
+        using ApproximationResult = detail::ApproximationResult;
+
+        ChainApproximation(const Points& points, const Params& params);
+
+        ApproximationResult approximate();
+
+        [[nodiscard]] Points approximatedChain() const;
+
+    private:
+        struct Triplet
+        {
+            Triplet(const int& a, const int& b, const int& c) : a(a), b(b), c(c) {}
+
+            int a;
+            int b;
+            int c;
+        };
+
+        using Triplets = std::vector<Triplet>;
+
+        Triplets getTriplets() const;
+
+        std::vector<float> computeDistances(const Triplets& triplets);
+        float computeDistance(const Triplet& triplet) const;
+
+        bool approximateStep();
+
+        Points points;
+
+        std::vector<int> indices;
+
+        const Params params;
+    };
+
+} // namespace armarx
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
index d94c3ade..399b1012 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
@@ -1,4 +1,4 @@
-/**
+/*
  * This file is part of ArmarX.
  *
  * ArmarX is free software; you can redistribute it and/or modify
@@ -13,208 +13,532 @@
  * 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    navigation::ArmarXObjects::laser_scanner_feature_extraction
- * @author     Tobias Gröger ( tobias dot groeger at student dot kit dot edu )
- * @date       2023
+ * @package    RobotComponents::ArmarXObjects::LaserScannerFeatureExtraction
+ * @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
  */
 
+#include "LaserScannerFeatureExtraction.h"
 
-#include "Component.h"
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+#include <utility>
 
-// Include headers you only need in function definitions in the .cpp.
+#include <Eigen/Core>
+#include <Eigen/Geometry>
 
-// #include <Eigen/Core>
+#include <IceUtil/Time.h>
 
-// #include <SimoxUtility/color/Color.h>
+#include <SimoxUtility/algorithm/apply.hpp>
+#include <SimoxUtility/color/Color.h>
+#include <VirtualRobot/BoundingBox.h>
+#include <VirtualRobot/MathTools.h>
 
-#include <ArmarXCore/libraries/DecoupledSingleComponent/Decoupled.h>
+#include "ArmarXCore/core/time/DateTime.h"
+#include "ArmarXCore/core/time/Duration.h"
 
+#include "RobotAPI/libraries/armem_vision/types.h"
+#include "RobotAPI/libraries/core/remoterobot/RemoteRobot.h"
 
-namespace armarx::navigation::components::laser_scanner_feature_extraction
-{
+#include "RobotComponents/components/LaserScannerFeatureExtraction/geometry.h"
 
-    const std::string
-    Component::defaultName = "LaserScannerFeatureExtraction";
+#include "ArVizDrawer.h"
+#include "FeatureExtractor.h"
+#include "conversions/eigen.h"
+#include "conversions/pcl_eigen.h"
 
+namespace armarx::laser_scanner_feature_extraction
+{
 
     armarx::PropertyDefinitionsPtr
-    Component::createPropertyDefinitions()
+    LaserScannerFeatureExtraction::createPropertyDefinitions()
     {
-        armarx::PropertyDefinitionsPtr def = new armarx::ComponentPropertyDefinitions(getConfigIdentifier());
+        armarx::PropertyDefinitionsPtr def =
+            new ComponentPropertyDefinitions(getConfigIdentifier());
 
         // Publish to a topic (passing the TopicListenerPrx).
-        // def->topic(myTopicListener);
+        def->topic(featuresTopic);
 
         // Subscribe to a topic (passing the topic name).
-        // def->topic<PlatformUnitListener>("MyTopic");
-
-        // Use (and depend on) another component (passing the ComponentInterfacePrx).
-        // def->component(myComponentProxy)
-
-
-        // Add a required property. (The component won't start without a value being set.)
-        // def->required(properties.boxLayerName, "p.box.LayerName", "Name of the box layer in ArViz.");
-
-        // Add an optional property.
-        // def->optional(properties.boxLayerName, "p.box.LayerName", "Name of the box layer in ArViz.");
-        // def->optional(properties.numBoxes, "p.box.Number", "Number of boxes to draw in ArViz.");
+        def->topic<LaserScannerUnitListener>("LaserScans");
+
+        // // Add an optionalproperty.
+        def->optional(properties.queueSize,
+                      "p.inputQueueSize",
+                      "Size of the message queue (max). Should not be too large to avoid delays.");
+
+        def->optional(properties.robotHull.shape, "p.robotHull.shape", "Shape of the robot area.")
+            .map({std::make_pair("Rectangle", Properties::RobotHull::RECTANGLE),
+                  std::make_pair("Circle", Properties::RobotHull::CIRCLE)});
+        def->optional(properties.robotHull.radius,
+                      "p.robotHull.radius",
+                      "The radius of the robot when using the circle shape.");
+        def->optional(
+            properties.robotHull.robotConvexHullMargin,
+            "p.robotHull.robotConvexHullMargin",
+            "Parameter to increase the robot's convex hull when using the rectangle shape.");
+
+        def->optional(properties.cableFix.enabled,
+                      "p.cableFix.enabled",
+                      "Try to supress clusters belonging to the power supply cable.");
+        def->optional(properties.cableFix.cableAreaWidth,
+                      "p.cableFix.cableAreaWidth",
+                      "Width of the area where to search for the cable.");
+
+        def->optional(
+            properties.cableFix.maxAreaTh,
+            "p.cableFix.maxAreaTh",
+            "The cable will only be removed if the cluster area is below this threshold.");
+
+        def->optional(
+            properties.chainApproximationParams.distanceTh, "p.chainApproximation.distanceTh", "");
+        def->optional(properties.chainApproximationParams.maxIterations,
+                      "p.chainApproximation.maxIterations",
+                      "");
+
+        def->optional(properties.scanClusteringParams.angleThreshold,
+                      "p.scanClustering.angleThreshold",
+                      "Angular distance between consecutive points in laser scan for clustering.");
+        def->optional(properties.scanClusteringParams.distanceThreshold,
+                      "p.scanClustering.distanceThreshold",
+                      "Radial distance between consecutive points in laser scan for clustering.");
+        def->optional(properties.scanClusteringParams.maxDistance,
+                      "p.scanClustering.maxDistance",
+                      "Maximum radius around sensors to detect clusters.");
+
+        def->optional(properties.arviz.drawRawPoints,
+                      "p.arviz.drawRawPoints",
+                      "If true, the laser scans will be drawn.");
+
+        laserScannerFeatureWriter.registerPropertyDefinitions(def);
 
         return def;
     }
 
+    LaserScannerFeatureExtraction::LaserScannerFeatureExtraction() :
+        laserScannerFeatureWriter(memoryNameSystem())
+    {
+    }
 
     void
-    Component::onInitComponent()
+    LaserScannerFeatureExtraction::onInitComponent()
     {
         // Topics and properties defined above are automagically registered.
 
         // Keep debug observer data until calling `sendDebugObserverBatch()`.
-        // (Requires the armarx::DebugObserverComponentPluginUser.)
+        // (Requies the armarx::DebugObserverComponentPluginUser.)
         // setDebugObserverBatchModeEnabled(true);
     }
 
-
     void
-    Component::onConnectComponent()
+    LaserScannerFeatureExtraction::onConnectComponent()
     {
-        // Do things after connecting to topics and components.
+        ARMARX_INFO << "Connecting";
+
+        frequencyReporterPublish = std::make_unique<FrequencyReporter>(
+            getDebugObserver(), "LaserScannerFeatureExtraction_publish");
+        frequencyReporterSubscribe = std::make_unique<FrequencyReporter>(
+            getDebugObserver(), "LaserScannerFeatureExtraction_subscribe");
+
+        arVizDrawer = std::make_unique<ArVizDrawer>(getArvizClient());
+
+        featureExtractor = std::make_unique<FeatureExtractor>(
+            properties.scanClusteringParams,
+            properties.chainApproximationParams,
+            [&](auto&&... args) { onFeatures(std::forward<decltype(args)>(args)...); });
 
-        /* (Requires the armarx::DebugObserverComponentPluginUser.)
-        // Use the debug observer to log data over time.
-        // The data can be viewed in the ObserverView and the LivePlotter.
-        // (Before starting any threads, we don't need to lock mutexes.)
+        laserMessageQueue.setQueueSize(properties.queueSize);
+        laserMessageQueue.connect([&](const auto& sharedArg)
+                                  { featureExtractor->onData(sharedArg); });
+
+        ARMARX_INFO << "Connected";
+
+        robot = RemoteRobot::createLocalClone(getRobotStateComponent());
+        RemoteRobot::synchronizeLocalClone(robot, getRobotStateComponent());
+        switch (properties.robotHull.shape)
         {
-            setDebugObserverDatafield("numBoxes", properties.numBoxes);
-            setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
-            sendDebugObserverBatch();
+            case Properties::RobotHull::RECTANGLE:
+            {
+                // initialize robot platform convex hull
+                const Eigen::Vector2f ptFrontLeft = conversions::to2D(
+                    robot->getRobotNode("PlatformCornerFrontLeft")->getPositionInRootFrame());
+                const Eigen::Vector2f ptFrontRight = conversions::to2D(
+                    robot->getRobotNode("PlatformCornerFrontRight")->getPositionInRootFrame());
+                const Eigen::Vector2f ptBackRight = conversions::to2D(
+                    robot->getRobotNode("PlatformCornerBackRight")->getPositionInRootFrame());
+                const Eigen::Vector2f ptBackLeft = conversions::to2D(
+                    robot->getRobotNode("PlatformCornerBackLeft")->getPositionInRootFrame());
+
+                const auto addMargin = [&](const Eigen::Vector2f& pt) -> Eigen::Vector2f
+                {
+                    Eigen::Vector2f ptWithMargin = pt;
+                    ptWithMargin.x() +=
+                        std::copysignf(properties.robotHull.robotConvexHullMargin, pt.x());
+                    ptWithMargin.y() +=
+                        std::copysignf(properties.robotHull.robotConvexHullMargin, pt.y());
+
+                    return ptWithMargin;
+                };
+
+                {
+                    std::vector<Eigen::Vector2f> hullPoints;
+                    hullPoints.push_back(addMargin(ptFrontLeft));
+                    hullPoints.push_back(addMargin(ptFrontRight));
+                    hullPoints.push_back(addMargin(ptBackRight));
+                    hullPoints.push_back(addMargin(ptBackLeft));
+
+                    robotHull = VirtualRobot::MathTools::createConvexHull2D(hullPoints);
+                }
+
+                // cable fix is only applicable when the robot shape is a rectangle
+                if (properties.cableFix.enabled)
+                {
+                    // the cable area and the robot hull overlap slightly. as all points must be within one region,
+                    // the cable area should start at approx. the position of the wheels
+
+                    std::vector<Eigen::Vector2f> cableAreaPoints;
+                    cableAreaPoints.emplace_back(addMargin(ptBackRight) +
+                                                 100 * Eigen::Vector2f::UnitY());
+                    cableAreaPoints.emplace_back(addMargin(ptBackLeft) +
+                                                 100 * Eigen::Vector2f::UnitY());
+                    cableAreaPoints.emplace_back(addMargin(ptBackRight) -
+                                                 properties.cableFix.cableAreaWidth *
+                                                     Eigen::Vector2f::UnitY());
+                    cableAreaPoints.emplace_back(addMargin(ptBackLeft) -
+                                                 properties.cableFix.cableAreaWidth *
+                                                     Eigen::Vector2f::UnitY());
+
+                    cableArea = VirtualRobot::MathTools::createConvexHull2D(cableAreaPoints);
+                }
+
+                break;
+            }
+            case Properties::RobotHull::CIRCLE:
+            {
+                // cable fix is not supported for circular robots
+                if (properties.cableFix.enabled)
+                {
+                    ARMARX_ERROR << "Cable fix is not supported for circular robots!";
+                }
+                const Eigen::Vector2f root =
+                    conversions::to2D(robot->getRootNode()->getPositionInRootFrame());
+                robotHull = root;
+                break;
+            }
         }
-        */
 
-        /* (Requires the armarx::ArVizComponentPluginUser.)
-        // Draw boxes in ArViz.
-        // (Before starting any threads, we don't need to lock mutexes.)
-        drawBoxes(properties, arviz);
-        */
 
-        /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
-        // Setup the remote GUI.
+        laserScannerFeatureWriter.connect();
+    }
+
+    armem::vision::LaserScannerFeature
+    toArmemFeature(const Features& features)
+    {
+        armem::vision::LaserScannerFeature armemFeature;
+
+        if (features.chain)
         {
-            createRemoteGuiTab();
-            RemoteGui_startRunningTask();
+            armemFeature.chain = features.chain.value();
         }
-        */
-    }
 
+        if (features.circle)
+        {
+            armemFeature.circle = features.circle.value();
+        }
 
-    void
-    Component::onDisconnectComponent()
-    {
-    }
+        if (features.convexHull)
+        {
+            armemFeature.convexHull = features.convexHull->vertices;
+        }
 
+        if (features.ellipsoid)
+        {
+            armemFeature.ellipsoid = features.ellipsoid.value();
+        }
 
-    void
-    Component::onExitComponent()
-    {
-    }
+        armemFeature.points = features.points;
 
+        return armemFeature;
+    }
 
-    std::string
-    Component::getDefaultName() const
+    armem::vision::LaserScannerFeatures
+    toArmemFeatures(const std::vector<Features>& features,
+                    const Eigen::Isometry3f& global_T_sensor,
+                    const std::string& sensorFrame)
     {
-        return Component::defaultName;
-    }
+        armem::vision::LaserScannerFeatures armemFeatures;
+        armemFeatures.frame = sensorFrame;
+        armemFeatures.frameGlobalPose = global_T_sensor;
 
 
-    std::string
-    Component::GetDefaultName()
-    {
-        return Component::defaultName;
+        armemFeatures.features = simox::alg::apply(features, toArmemFeature);
+
+        return armemFeatures;
     }
 
+    std::vector<Features>
+    removeInvalidFeatures(std::vector<Features> features)
+    {
+        const auto isInvalid = [](const Features& features) -> bool
+        {
+            if (features.points.size() < 2)
+            {
+                return true;
+            }
+
+            if (not features.convexHull.has_value())
+            {
+                return true;
+            }
+
+            // if(not features.circle.has_value())
+            // {
+            //     return true;
+            // }
+
+            // if(not features.ellipsoid.has_value())
+            // {
+            //     return true;
+            // }
+
+            return false;
+        };
+
+
+        features.erase(std::remove_if(features.begin(), features.end(), isInvalid), features.end());
+
+        return features;
+    }
 
-    /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
     void
-    Component::createRemoteGuiTab()
+    LaserScannerFeatureExtraction::onFeatures(const cartographer::LaserScannerMessage& data,
+                                              const std::vector<Features>& featuresFromExtractor)
     {
-        using namespace armarx::RemoteGui::Client;
+        ARMARX_DEBUG << "Publishing data";
+
+        // obtain sensor pose
+        RemoteRobot::synchronizeLocalCloneToTimestamp(
+            robot, getRobotStateComponent(), data.timestamp);
+        const Eigen::Isometry3f global_T_sensor(robot->getRobotNode(data.frame)->getGlobalPose());
+        const Eigen::Isometry3f global_T_robot(robot->getRootNode()->getGlobalPose());
+        const Eigen::Isometry3f robot_T_sensor(
+            robot->getRobotNode(data.frame)->getPoseInRootFrame());
+
+        //Eigen::AlignedBox2f box;
+        //box.extend(pt1).extend(pt2).extend(pt3).extend(pt4);
+
+        const auto transformPoints =
+            [](const std::vector<Eigen::Vector2f>& points, const Eigen::Isometry3f& tf)
+        {
+            std::vector<Eigen::Vector2f> out;
+            out.reserve(points.size());
 
-        // Setup the widgets.
+            std::transform(points.begin(),
+                           points.end(),
+                           std::back_inserter(out),
+                           [&tf](const auto& pt)
+                           { return conversions::to2D(tf * conversions::to3D(pt)); });
 
-        tab.boxLayerName.setValue(properties.boxLayerName);
+            return out;
+        };
 
-        tab.numBoxes.setValue(properties.numBoxes);
-        tab.numBoxes.setRange(0, 100);
+        const auto isPointInsideRobot = [&](const Eigen::Vector2f& pt) -> bool
+        {
+            if (std::holds_alternative<VirtualRobot::MathTools::ConvexHull2DPtr>(robotHull))
+            {
+                return VirtualRobot::MathTools::isInside(
+                    pt, std::get<VirtualRobot::MathTools::ConvexHull2DPtr>(robotHull));
+            }
+            else if (std::holds_alternative<Eigen::Vector2f>(robotHull))
+            {
+                return (std::get<Eigen::Vector2f>(robotHull) - pt).norm() <
+                       properties.robotHull.radius;
+            }
+            return false;
+        };
+
+        const auto isPointInsideCableArea = [&](const Eigen::Vector2f& pt) -> bool
+        { return VirtualRobot::MathTools::isInside(pt, cableArea); };
+
+        const auto isClusterInvalid = [&](const Features& features) -> bool
+        {
+            // either use convex hull (compact representation) or raw points as fallbacck
+            const auto points = [&]()
+            {
+                if (features.convexHull)
+                {
+                    return transformPoints(features.convexHull->vertices, robot_T_sensor);
+                }
+
+                return transformPoints(features.points, robot_T_sensor);
+            }();
+
+            const bool allPointsInsideRobot =
+                std::all_of(points.begin(), points.end(), isPointInsideRobot);
+            if (allPointsInsideRobot)
+            {
+                return true;
+            }
+
+            if (properties.cableFix.enabled)
+            {
 
-        tab.drawBoxes.setLabel("Draw Boxes");
+                const bool allPointsInCableArea =
+                    std::all_of(points.begin(), points.end(), isPointInsideCableArea);
+                if (allPointsInCableArea)
+                {
+                    if (geometry::ConvexHull(points).area() < properties.cableFix.maxAreaTh)
+                    {
+                        return true;
+                    }
+                }
+            }
 
-        // Setup the layout.
+            return false;
+        };
 
-        GridLayout grid;
-        int row = 0;
+        const auto removeFeaturesOnRobotOrCable = [&](std::vector<Features> features)
         {
-            grid.add(Label("Box Layer"), {row, 0}).add(tab.boxLayerName, {row, 1});
-            ++row;
+            features.erase(std::remove_if(features.begin(), features.end(), isClusterInvalid),
+                           features.end());
+            return features;
+        };
 
-            grid.add(Label("Num Boxes"), {row, 0}).add(tab.numBoxes, {row, 1});
-            ++row;
+        ARMARX_VERBOSE << featuresFromExtractor.size() << " features from extractor";
 
-            grid.add(tab.drawBoxes, {row, 0}, {2, 1});
-            ++row;
-        }
+        const auto features = removeFeaturesOnRobotOrCable(featuresFromExtractor);
+        ARMARX_VERBOSE << features.size() << " features without cable region";
 
-        VBoxLayout root = {grid, VSpacer()};
-        RemoteGui_createTab(getName(), root, &tab);
-    }
+        const auto validFeatures = removeInvalidFeatures(features);
+        ARMARX_VERBOSE << validFeatures.size() << " valid features without cable region";
 
+        const auto armemFeatures = toArmemFeatures(validFeatures, global_T_sensor, data.frame);
 
-    void
-    Component::RemoteGui_update()
-    {
-        if (tab.boxLayerName.hasValueChanged() || tab.numBoxes.hasValueChanged())
+        ARMARX_VERBOSE << "Reporting " << armemFeatures.features.size() << " features";
+
+        // report the features
+        publishFeatures(armemFeatures,
+                        armarx::core::time::DateTime(
+                            armarx::core::time::Duration::MicroSeconds(data.timestamp)));
+
+        // check if arviz should be triggered
+        const auto getOrCreateThrottler = [&]() -> Throttler&
         {
-            std::scoped_lock lock(propertiesMutex);
-            properties.boxLayerName = tab.boxLayerName.getValue();
-            properties.numBoxes = tab.numBoxes.getValue();
+            const auto it = throttlers.find(data.frame);
+            if (it == throttlers.end())
+            {
+                throttlers.emplace(data.frame, Throttler(10.F));
+            }
+
+            return throttlers.at(data.frame);
+        };
+
+        if (getOrCreateThrottler().check(data.timestamp))
+        {
+            arVizDrawer->draw(features, data.frame, global_T_sensor);
 
+            if (std::holds_alternative<VirtualRobot::MathTools::ConvexHull2DPtr>(robotHull))
             {
-                setDebugObserverDatafield("numBoxes", properties.numBoxes);
-                setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
-                sendDebugObserverBatch();
+                VirtualRobot::MathTools::ConvexHull2DPtr& convexHullPtr =
+                    std::get<VirtualRobot::MathTools::ConvexHull2DPtr>(robotHull);
+                if (convexHullPtr != nullptr)
+                {
+                    arVizDrawer->draw("robot_convex_hull",
+                                      *convexHullPtr,
+                                      global_T_robot,
+                                      simox::Color::azure(255, 80));
+                }
+            }
+            else if (std::holds_alternative<Eigen::Vector2f>(robotHull))
+            {
+                Eigen::Vector2f& root = std::get<Eigen::Vector2f>(robotHull);
+                arVizDrawer->draw("robot_circle_hull",
+                                  Circle{root, properties.robotHull.radius},
+                                  global_T_robot,
+                                  simox::Color::azure(255, 80));
+            }
+
+            if (cableArea != nullptr)
+            {
+                arVizDrawer->draw(
+                    "cable_area", *cableArea, global_T_robot, simox::Color::blue(255, 80));
+            }
+
+            if (properties.arviz.drawRawPoints)
+            {
+                arVizDrawer->draw(data, global_T_sensor, simox::Color::magenta(255, 100));
             }
         }
-        if (tab.drawBoxes.wasClicked())
+
+        // some debugobserver reporting
+        frequencyReporterPublish->add(IceUtil::Time::now().toMicroSeconds());
+        setDebugObserverDatafield("numClusters", features.size());
+    }
+
+    void
+    LaserScannerFeatureExtraction::publishFeatures(
+        const armem::vision::LaserScannerFeatures& features,
+        const armem::Time& timestamp)
+    {
+
+        // store in memory
+        laserScannerFeatureWriter.store(features, getName(), timestamp);
+
+        // legacy - topic
+        LineSegment2DChainSeq chains;
+        for (const auto& feature : features.features)
         {
-            // Lock shared variables in methods running in separate threads
-            // and pass them to functions. This way, the called functions do
-            // not need to think about locking.
-            std::scoped_lock lock(propertiesMutex);
-            drawBoxes(properties, arviz);
+            if (not feature.chain.empty())
+            {
+                LineSegment2DChain chain;
+                for (const auto& pt : feature.chain)
+                {
+                    chain.push_back(
+                        conversions::to2D(features.frameGlobalPose * conversions::to3D(pt)));
+                }
+                chains.push_back(chain);
+            }
         }
+
+        featuresTopic->reportExtractedLineSegments(chains);
     }
-    */
 
+    void
+    LaserScannerFeatureExtraction::onDisconnectComponent()
+    {
+    }
 
-    /* (Requires the armarx::ArVizComponentPluginUser.)
     void
-    Component::drawBoxes(const Component::Properties& p, viz::Client& arviz)
+    LaserScannerFeatureExtraction::onExitComponent()
     {
-        // Draw something in ArViz (requires the armarx::ArVizComponentPluginUser.
-        // See the ArVizExample in RobotAPI for more examples.
+    }
 
-        viz::Layer layer = arviz.layer(p.boxLayerName);
-        for (int i = 0; i < p.numBoxes; ++i)
-        {
-            layer.add(viz::Box("box_" + std::to_string(i))
-                      .position(Eigen::Vector3f(i * 100, 0, 0))
-                      .size(20).color(simox::Color::blue()));
-        }
-        arviz.commit(layer);
+    std::string
+    LaserScannerFeatureExtraction::getDefaultName() const
+    {
+        return "LaserScannerFeatureExtraction";
     }
-    */
 
+    void
+    LaserScannerFeatureExtraction::reportSensorValues(const std::string& device,
+                                                      const std::string& name,
+                                                      const LaserScan& scan,
+                                                      const TimestampBasePtr& timestamp,
+                                                      const Ice::Current& /*unused*/)
+    {
+        ARMARX_DEBUG << "Receiving data";
+
+        laserMessageQueue.push(cartographer::LaserScannerMessage{
+            .frame = name, .scan = scan, .timestamp = timestamp->timestamp});
 
-    ARMARX_REGISTER_COMPONENT_EXECUTABLE(Component, Component::GetDefaultName());
+        ARMARX_DEBUG << "Queue size" << laserMessageQueue.size();
+
+        frequencyReporterSubscribe->add(IceUtil::Time::now().toMicroSeconds());
+
+        setDebugObserverDatafield(name, scan.size());
+    }
 
-}  // namespace armarx::navigation::components::laser_scanner_feature_extraction
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
index 43520364..214ccf97 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
@@ -1,4 +1,4 @@
-/**
+/*
  * This file is part of ArmarX.
  *
  * ArmarX is free software; you can redistribute it and/or modify
@@ -13,51 +13,75 @@
  * 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    navigation::ArmarXObjects::laser_scanner_feature_extraction
- * @author     Tobias Gröger ( tobias dot groeger at student dot kit dot edu )
- * @date       2023
+ * @package    RobotComponents::ArmarXObjects::LaserScannerFeatureExtraction
+ * @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 <variant>
 
-// #include <mutex>
-
+#include "ArmarXCore/core/util/Throttler.h"
+#include "ArmarXCore/libraries/logging/FrequencyReporter.h"
 #include <ArmarXCore/core/Component.h>
-
-// #include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
-
-// #include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h>
-
-// #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
-
-#include <armarx/navigation/components/laser_scanner_feature_extraction/ComponentInterface.h>
-
-
-namespace armarx::navigation::components::laser_scanner_feature_extraction
+#include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
+
+#include "RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h"
+#include "RobotAPI/libraries/armem/client/forward_declarations.h"
+#include "RobotAPI/libraries/armem/client/plugins/PluginUser.h"
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h>
+#include <RobotAPI/libraries/armem_vision/client/laser_scanner_features/Writer.h>
+
+#include "RobotComponents/components/LaserScannerFeatureExtraction/ChainApproximation.h"
+#include "RobotComponents/components/LaserScannerFeatureExtraction/ScanClustering.h"
+#include "RobotComponents/components/LaserScannerFeatureExtraction/geometry.h"
+#include <RobotComponents/interface/components/LaserScannerFeatureExtraction/LaserScannerFeatureExtraction.h>
+#include <RobotComponents/libraries/cartographer/MessageQueue.h>
+#include <RobotComponents/libraries/cartographer/types.h>
+
+#include "ArVizDrawer.h"
+#include "FeatureExtractor.h"
+
+namespace armarx::laser_scanner_feature_extraction
 {
 
-    class Component :
+    // class FeatureExtractor;
+    // class ArVizDrawer;
+
+    /**
+     * @defgroup Component-LaserScannerFeatureExtraction LaserScannerFeatureExtraction
+     * @ingroup RobotComponents-Components
+     * A description of the component LaserScannerFeatureExtraction.
+     *
+     * @class LaserScannerFeatureExtraction
+     * @ingroup Component-LaserScannerFeatureExtraction
+     * @brief Brief description of class LaserScannerFeatureExtraction.
+     *
+     * Detailed description of class LaserScannerFeatureExtraction.
+     */
+    class LaserScannerFeatureExtraction :
         virtual public armarx::Component,
-        virtual public armarx::navigation::components::laser_scanner_feature_extraction::ComponentInterface
-        // , virtual public armarx::DebugObserverComponentPluginUser
+        virtual public armarx::DebugObserverComponentPluginUser
         // , virtual public armarx::LightweightRemoteGuiComponentPluginUser
-        // , virtual public armarx::ArVizComponentPluginUser
+        ,
+        virtual public armarx::ArVizComponentPluginUser,
+        virtual public LaserScannerUnitListener,
+        virtual public RobotStateComponentPluginUser,
+        virtual public armem::ClientPluginUser
+
     {
     public:
+        LaserScannerFeatureExtraction();
 
         /// @see armarx::ManagedIceObject::getDefaultName()
         std::string getDefaultName() const override;
 
-        /// Get the component's default name.
-        static std::string GetDefaultName();
-
-
     protected:
-
         /// @see PropertyUser::createPropertyDefinitions()
         armarx::PropertyDefinitionsPtr createPropertyDefinitions() override;
 
@@ -73,7 +97,6 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         /// @see armarx::ManagedIceObject::onExitComponent()
         void onExitComponent() override;
 
-
         /* (Requires armarx::LightweightRemoteGuiComponentPluginUser.)
         /// This function should be called once in onConnect() or when you
         /// need to re-create the Remote GUI tab.
@@ -85,8 +108,19 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         void RemoteGui_update() override;
         */
 
+        // LaserScannerUnitListener interface
+        void reportSensorValues(const std::string& device,
+                                const std::string& name,
+                                const LaserScan& scan,
+                                const TimestampBasePtr& timestamp,
+                                const Ice::Current& /*unused*/) override;
 
     private:
+        void onFeatures(const cartographer::LaserScannerMessage& data,
+                        const std::vector<Features>& features);
+
+        void publishFeatures(const armem::vision::LaserScannerFeatures& features,
+                             const armem::Time& timestamp);
 
         // Private methods go here.
 
@@ -98,28 +132,62 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         void drawBoxes(const Properties& p, viz::Client& arviz);
         */
 
-
-    private:
-
-        static const std::string defaultName;
-
-
         // Private member variables go here.
-
+        MessageQueue<cartographer::LaserScannerMessage> laserMessageQueue;
 
         /// Properties shown in the Scenario GUI.
         struct Properties
         {
-            std::string boxLayerName = "boxes";
-            int numBoxes = 10;
+            // input message queue size
+            size_t queueSize{2};
+
+            //
+            struct RobotHull
+            {
+                enum Shape
+                {
+                    RECTANGLE,
+                    CIRCLE
+                };
+
+                Shape shape = RECTANGLE;
+                // for shape circle
+                float radius = 500.F; // [mm]
+                // for shape rectangle
+                float robotConvexHullMargin{50.F};
+            } robotHull;
+
+            //
+            struct CableFix
+            {
+                bool enabled = {true};
+                float cableAreaWidth{400};
+                float maxAreaTh{50 * 50};
+            } cableFix;
+
+            ScanClustering::Params scanClusteringParams{
+                .distanceThreshold = 30.F, // [mm]
+                // we know the scan resolution which produces a bit more than 1000 points
+                .angleThreshold = 2 * M_PIf32 / 1000.F * 5.F, // [rad]
+                .maxDistance = 3'000.F // [mm]
+            };
+
+            ChainApproximation::Params chainApproximationParams{
+                .distanceTh = 40.F // [mm]
+            };
+
+            struct ArViz
+            {
+                bool drawRawPoints = {true};
+            } arviz;
         };
+
         Properties properties;
         /* Use a mutex if you access variables from different threads
          * (e.g. ice functions and RemoteGui_update()).
         std::mutex propertiesMutex;
         */
 
-
         /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
         /// Tab shown in the Remote GUI.
         struct RemoteGuiTab : armarx::RemoteGui::Client::Tab
@@ -132,6 +200,29 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         RemoteGuiTab tab;
         */
 
-    };
+        /* (Requires the armarx::ArVizComponentPluginUser.)
+         * When used from different threads, an ArViz client needs to be synchronized.
+        /// Protects the arviz client inherited from the ArViz plugin.
+        std::mutex arvizMutex;
+        */
 
-}  // namespace armarx::navigation::components::laser_scanner_feature_extraction
+        std::unique_ptr<FeatureExtractor> featureExtractor;
+        std::unique_ptr<ArVizDrawer> arVizDrawer;
+
+        std::unique_ptr<FrequencyReporter> frequencyReporterPublish;
+        std::unique_ptr<FrequencyReporter> frequencyReporterSubscribe;
+
+        std::unordered_map<std::string, Throttler> throttlers;
+
+        VirtualRobot::RobotPtr robot;
+
+        LaserScannerFeaturesListenerPrx featuresTopic;
+
+        std::variant<std::monostate, VirtualRobot::MathTools::ConvexHull2DPtr, Eigen::Vector2f>
+            robotHull;
+        VirtualRobot::MathTools::ConvexHull2DPtr cableArea;
+
+
+        armem::vision::laser_scanner_features::client::Writer laserScannerFeatureWriter;
+    };
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp
new file mode 100644
index 00000000..87bf8313
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp
@@ -0,0 +1,124 @@
+#include "EnclosingEllipsoid.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+#include <Eigen/src/Geometry/AngleAxis.h>
+
+#include <SimoxUtility/math/periodic/periodic_clamp.h>
+#include <VirtualRobot/math/Helpers.h>
+
+#include "ArmarXCore/core/exceptions/local/ExpressionException.h"
+#include "ArmarXCore/core/logging/Logging.h"
+
+
+namespace armarx::laser_scanner_feature_extraction
+{
+
+    // Eigen::Affine2f Ellipsoid::pose() const noexcept
+    // {
+    //     Eigen::Affine2f pose;
+    //     pose.translation() = center;
+    //     pose.linear()      = Eigen::Rotation2Df(angle).toRotationMatrix();
+
+    //     return pose;
+    // }
+
+    // TODO(fabian.reister): use Eigen built-in stuff.
+    size_t
+    argmin(const Eigen::VectorXf& v)
+    {
+        const std::vector<float> vec(v.data(), v.data() + v.rows() * v.cols());
+        return std::distance(std::begin(vec), std::max_element(vec.begin(), vec.end()));
+    }
+
+    // https://github.com/minillinim/ellipsoid/blob/master/ellipsoid.py
+
+    EnclosingEllipsoid::EnclosingEllipsoid(const Points& points)
+    {
+        ARMARX_CHECK(compute(points));
+    }
+
+    bool
+    EnclosingEllipsoid::compute(const Points& points)
+    {
+        const float tolerance = 0.01;
+
+        const int N = static_cast<int>(points.size());
+        const int d = 2;
+
+        Eigen::MatrixXf P(N, d);
+
+        for (int i = 0; i < N; i++)
+        {
+            P.row(i) = points.at(i);
+        }
+
+        Eigen::MatrixXf Q(d + 1, N);
+        Q.topRows(d) = P.transpose();
+        Q.bottomRows(1).setOnes(); // last row
+
+        Eigen::MatrixXf Qt = Q.transpose();
+
+        float err = 1.F + tolerance;
+        Eigen::VectorXf u = (1.F / N) * Eigen::VectorXf::Ones(N);
+
+        // Khachiyan Algorithm
+        while (err > tolerance)
+        {
+            const Eigen::MatrixXf V = Q * (u.asDiagonal() * Qt);
+            const Eigen::VectorXf M = (Qt * (V.inverse() * Q)).diagonal();
+
+            const int j = static_cast<int>(argmin(M));
+            const float maximum = M(j);
+
+            const float stepSize = (maximum - d - 1.0) / ((d + 1.0) * (maximum - 1.0));
+
+            Eigen::VectorXf uNew = (1.0 - stepSize) * u;
+            uNew(j) += stepSize;
+
+            err = (uNew - u).norm();
+            u = uNew;
+        }
+
+        const Eigen::VectorXf center = P.transpose() * u;
+
+        Eigen::MatrixXf K(d, d);
+        for (int i = 0; i < d; i++)
+        {
+            for (int j = 0; j < d; j++)
+            {
+                K(i, j) = center(i) * center(j);
+            }
+        }
+
+        const Eigen::MatrixXf H = (P.transpose() * (u.asDiagonal() * P)) - K;
+        const Eigen::MatrixXf A = H.inverse() / d;
+
+        const Eigen::JacobiSVD svd(A, Eigen::ComputeThinV);
+
+        // angle
+        const Eigen::VectorXf v0 = svd.matrixV().col(0);
+        const float angle = math::Helpers::Angle(v0);
+
+        // radii
+        const Eigen::Vector2f radii = svd.singularValues().cwiseSqrt().cwiseInverse();
+
+        // As the singular values are sorted descending, the radii are sorted ascending.
+        // To fix this, the vector is reversed.
+        this->radii = radii.reverse();
+
+        // Thus, the angle does no longer match the radii. It has to be altered by 90°.
+        const float angle1 = simox::math::periodic_clamp(angle + M_PI_2f32, -M_PIf32, M_PIf32);
+
+        this->pose.setIdentity();
+        this->pose.translation().head<2>() = center;
+        this->pose.linear() =
+            Eigen::AngleAxisf(angle1, Eigen::Vector3f::UnitZ()).toRotationMatrix();
+
+        return true;
+    }
+
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h
new file mode 100644
index 00000000..9628314b
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h
@@ -0,0 +1,70 @@
+/*
+ * 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 <Eigen/Core>
+#include <Eigen/Geometry>
+#include "RobotAPI/libraries/armem_vision/types.h"
+
+namespace armarx::laser_scanner_feature_extraction
+{
+
+    // struct Ellipsoid
+    // {
+    //     Eigen::Vector2f center;
+    //     float angle;
+
+    //     Eigen::Vector2f radii;
+
+    //     Eigen::Affine2f pose() const noexcept;
+    // };
+
+    using Ellipsoid = armem::vision::Ellipsoid;
+
+    /**
+     * @brief Minimum volume enclosing ellipsoid (MVEE) for a set of points.
+     *
+     * See https://de.mathworks.com/matlabcentral/fileexchange/9542-minimum-volume-enclosing-ellipsoid
+     *
+     */
+    class EnclosingEllipsoid : public Ellipsoid
+    {
+    public:
+        using Point  = Eigen::Vector2f;
+        using Points = std::vector<Point>;
+
+        EnclosingEllipsoid(const Points& points);
+
+    private:
+        /**
+         * @brief Computes the enclosing ellipsoid for the given points by using Khachiyan's Algorithm.
+         *
+         * The implementation is based on
+         * https://github.com/minillinim/ellipsoid/blob/master/ellipsoid.py
+         *
+         * @return true
+         * @return false
+         */
+        bool compute(const Points& points);
+    };
+
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
new file mode 100644
index 00000000..90949d11
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
@@ -0,0 +1,239 @@
+#include "FeatureExtractor.h"
+
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+#include <numeric>
+#include <optional>
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+#include <Eigen/src/Geometry/AngleAxis.h>
+#include <Eigen/src/Geometry/Transform.h>
+
+#include <pcl/PointIndices.h>
+#include <pcl/point_cloud.h>
+
+#include <opencv2/core/types.hpp>
+#include <opencv2/imgproc.hpp>
+
+#include <VirtualRobot/MathTools.h>
+#include <VirtualRobot/VirtualRobot.h>
+#include <VirtualRobot/math/Helpers.h>
+
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/logging/Logging.h>
+
+#include <RobotComponents/components/LaserScannerFeatureExtraction/ChainApproximation.h>
+#include <RobotComponents/components/LaserScannerFeatureExtraction/EnclosingEllipsoid.h>
+#include <RobotComponents/components/LaserScannerFeatureExtraction/ScanClustering.h>
+#include <RobotComponents/libraries/cartographer/util/laser_scanner_conversion.h>
+
+#include "Path.h"
+#include "conversions/eigen.h"
+#include "conversions/opencv.h"
+#include "conversions/opencv_eigen.h"
+#include "conversions/opencv_pcl.h"
+#include "conversions/pcl_eigen.h"
+
+
+namespace armarx::laser_scanner_feature_extraction
+{
+
+    // Features
+
+    std::vector<Ellipsoid>
+    Features::linesAsEllipsoids(const float axisLength) const
+    {
+        if (not chain)
+        {
+            return {};
+        }
+
+        const auto asEllipsoid = [&](const Path::Segment& segment) -> Ellipsoid
+        {
+            const Eigen::Vector2f center = (segment.first + segment.second) / 2;
+            const Eigen::Vector2f v = segment.second - center;
+            const float angle = math::Helpers::Angle(v);
+            const float r = v.norm();
+
+            Eigen::Isometry3f globalPose = Eigen::Isometry3f::Identity();
+            globalPose.translation().head<2>() = center;
+            globalPose.linear() =
+                Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitZ()).toRotationMatrix();
+
+            return Ellipsoid{.pose = globalPose, .radii = Eigen::Vector2f{r, axisLength}};
+        };
+
+        const auto segments = Path{.points = *chain}.segments();
+
+        std::vector<Ellipsoid> ellipsoids;
+        std::transform(
+            segments.begin(), segments.end(), std::back_inserter(ellipsoids), asEllipsoid);
+
+        return ellipsoids;
+    }
+
+    // FeatureExtractor
+
+    FeatureExtractor::FeatureExtractor(const ScanClustering::Params& scanClusteringParams,
+                                       const ChainApproximation::Params& chainApproximationParams,
+                                       const Callback& callback) :
+        scanClusteringParams(scanClusteringParams),
+        chainApproximationParams(chainApproximationParams),
+        callback(callback)
+    {
+    }
+
+    void
+    FeatureExtractor::onData(const cartographer::LaserScannerMessage& data)
+    {
+        ARMARX_DEBUG << "on data";
+        const auto clustersWithFeatures = features(data.scan);
+
+        ARMARX_DEBUG << "callback";
+        callback(data, clustersWithFeatures);
+    }
+
+    std::vector<Features>
+    FeatureExtractor::features(const LaserScan& scan) const
+    {
+        const auto clusters = detectClusters(scan);
+
+        std::vector<Features> fs;
+        std::transform(clusters.begin(),
+                       clusters.end(),
+                       std::back_inserter(fs),
+                       [&](const LaserScan& cluster)
+                       {
+                           const auto points = toCartesian<Eigen::Vector2f>(cluster);
+                           const auto hull = convexHull(points);
+
+                           return Features{.convexHull = hull,
+                                           .circle = std::nullopt, //circle(points),
+                                           .ellipsoid = std::nullopt, //ellipsoid(hull),
+                                           .chain = chainApproximation(points, hull),
+                                           .points = points};
+                       });
+
+        return fs;
+    }
+
+    std::vector<LaserScan>
+    FeatureExtractor::detectClusters(const LaserScan& scan) const
+    {
+        ScanClustering clustering(scanClusteringParams);
+        return clustering.detectClusters(scan);
+    }
+
+
+    std::optional<VirtualRobot::MathTools::ConvexHull2D>
+    FeatureExtractor::convexHull(const Points& points) const
+    {
+
+        if (points.size() < 3) // no meaningful area
+        {
+            return std::nullopt;
+        }
+
+        try
+        {
+            return *VirtualRobot::MathTools::createConvexHull2D(points);
+        }
+        catch (std::exception& e)
+        {
+            ARMARX_WARNING << "Couldn't create convex hull: " << e.what();
+        }
+        return std::nullopt;
+    }
+
+    // Legacy OpenCV implementation. Didn't work as expected.
+    // Also, tries to fit an ellipsoid which is NOT the enclosing ellipsoid!
+
+    // std::optional<Ellipsoid> FeatureExtractor::ellipsoid(const PointCloud &pointCloud) const
+    // {
+    //     // OpenCV::fitEllipse requirement
+    //     // "There should be at least 5 points to fit the ellipse"
+    //     // => Too few points will cause algorithmic issues
+    //     if (pointCloud.size() < 100)
+    //     {
+    //         return std::nullopt;
+    //     }
+
+    //     const auto points2f = conversions::pcl2cv(pointCloud);
+    //     const auto points2i = conversions::cast<cv::Point2i>(points2f);
+
+    //     cv::RotatedRect rect = cv::fitEllipse(points2i);
+
+    //     Eigen::Affine2f pose;
+    //     pose.translation() = conversions::cv2eigen(rect.center);
+    //     pose.linear() =
+    //         Eigen::Rotation2Df(VirtualRobot::MathTools::deg2rad(rect.angle)).toRotationMatrix();
+
+    //     return Ellipsoid{.axisLengths = conversions::cv2eigen(rect.size), .pose = pose};
+    // }
+
+    std::optional<Ellipsoid>
+    FeatureExtractor::ellipsoid(
+        const std::optional<VirtualRobot::MathTools::ConvexHull2D>& hull) const
+    {
+
+        if (not hull)
+        {
+            return std::nullopt;
+        }
+
+        // TODO(fabian.reister): it might result in multiple ellipsoids if hull->segments is considered
+
+        // If there are not enough points, don't even try to fit an ellipse ...
+        if (hull->vertices.size() < 5)
+        {
+            return std::nullopt;
+        }
+
+        EnclosingEllipsoid mvee(hull->vertices);
+        return mvee;
+    }
+
+    std::optional<Circle>
+    FeatureExtractor::circle(const Points& points) const
+    {
+        // Too few points will cause algorithmic issues
+        if (points.size() < 5)
+        {
+            return std::nullopt;
+        }
+
+        const auto points2f = conversions::eigen2cv(points);
+        const auto points2i = conversions::cast<cv::Point2i>(points2f);
+
+        cv::Point2f center;
+        float radius = NAN;
+        cv::minEnclosingCircle(points2i, center, radius);
+
+        return Circle{.center = conversions::cv2eigen(center), .radius = radius};
+    }
+
+    std::optional<FeatureExtractor::Points>
+    FeatureExtractor::chainApproximation(
+        const Points& points,
+        const std::optional<VirtualRobot::MathTools::ConvexHull2D>& convexHull) const
+    {
+        if (not convexHull)
+        {
+            return std::nullopt;
+        }
+
+        ChainApproximation chApprx(points, chainApproximationParams);
+        const auto result = chApprx.approximate();
+
+        if (result.condition !=
+            ChainApproximation::ApproximationResult::TerminationCondition::Converged)
+        {
+            return std::nullopt;
+        }
+
+        return chApprx.approximatedChain();
+    }
+
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
new file mode 100644
index 00000000..3dd5caac
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
@@ -0,0 +1,91 @@
+/*
+ * 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 <optional>
+
+#include <pcl/point_types.h>
+
+#include <VirtualRobot/MathTools.h>
+#include "RobotAPI/libraries/armem_vision/types.h"
+
+#include "RobotComponents/components/LaserScannerFeatureExtraction/ChainApproximation.h"
+#include "RobotComponents/libraries/cartographer/types.h"
+#include <RobotComponents/interface/components/GraspingManager/RobotPlacementInterface.h>
+
+#include "EnclosingEllipsoid.h"
+#include "ScanClustering.h"
+
+namespace armarx::laser_scanner_feature_extraction
+{
+    using Circle = armem::vision::Circle;
+   
+
+    struct Features
+    {
+        using Points   = std::vector<Eigen::Vector2f>;
+        using Chain = Points;
+
+        std::optional<VirtualRobot::MathTools::ConvexHull2D> convexHull;
+
+        std::optional<Circle> circle;
+        std::optional<Ellipsoid> ellipsoid;
+
+        std::optional<Chain> chain;
+
+        Points points;
+        std::vector<Ellipsoid> linesAsEllipsoids(float axisLength) const;
+    };
+
+    class FeatureExtractor
+    {
+    public:
+        using Points   = std::vector<Eigen::Vector2f>;
+        using Callback = std::function<void(const cartographer::LaserScannerMessage& data,
+                                            const std::vector<Features>& features)>;
+
+        FeatureExtractor(const ScanClustering::Params& scanClusteringParams,
+                         const ChainApproximation::Params& chainApproximationParams,
+                         const Callback& callback);
+
+        void onData(const cartographer::LaserScannerMessage& data);
+
+    private:
+        std::vector<Features> features(const LaserScan& scan) const;
+
+        std::vector<LaserScan> detectClusters(const LaserScan& scan) const;
+
+        std::optional<VirtualRobot::MathTools::ConvexHull2D> convexHull(const Points& points) const;
+        std::optional<Ellipsoid>
+        ellipsoid(const std::optional<VirtualRobot::MathTools::ConvexHull2D>& hull) const;
+        std::optional<Circle> circle(const Points& circle) const;
+
+        std::optional<Points> chainApproximation(
+            const Points& points,
+            const std::optional<VirtualRobot::MathTools::ConvexHull2D>& convexHull) const;
+
+        const ScanClustering::Params scanClusteringParams;
+        const ChainApproximation::Params chainApproximationParams;
+
+        const Callback callback;
+    };
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp
new file mode 100644
index 00000000..057ada4f
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp
@@ -0,0 +1,24 @@
+#include "Path.h"
+
+
+namespace armarx::laser_scanner_feature_extraction
+{
+
+    std::vector<Path::Segment> Path::segments() const noexcept
+    {
+        if (points.size() <= 1)
+        {
+            return {};
+        }
+
+        std::vector<Segment> segments;
+        segments.reserve(static_cast<int>(points.size()) - 1);
+
+        for (int i = 0; i < static_cast<int>(points.size()) - 1; i++)
+        {
+            segments.emplace_back(points.at(i), points.at(i + 1));
+        }
+
+        return segments;
+    }
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h
new file mode 100644
index 00000000..8f63e260
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <vector>
+
+#include <Eigen/Core>
+
+namespace armarx::laser_scanner_feature_extraction
+{
+
+    struct Path
+    {
+        std::vector<Eigen::Vector2f> points;
+
+        using Segment = std::pair<Eigen::Vector2f, Eigen::Vector2f>;
+
+        std::vector<Segment> segments() const noexcept;
+    };
+
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp
new file mode 100644
index 00000000..e0376c54
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp
@@ -0,0 +1,71 @@
+#include "ScanClustering.h"
+
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
+
+namespace armarx::laser_scanner_feature_extraction
+{
+    ScanClustering::ScanClustering(const Params& params) : params(params) {}
+
+    bool ScanClustering::add(const LaserScanStep& scanStep)
+    {
+        if (scan.empty())
+        {
+            scan.push_back(scanStep);
+            return true;
+        }
+
+        if (not supports(scanStep))
+        {
+            return false;
+        }
+
+        scan.push_back(scanStep);
+        return true;
+    }
+
+    std::vector<LaserScan> ScanClustering::detectClusters(const LaserScan& scan)
+    {
+        const auto isInvalid = [&](const LaserScanStep& step) -> bool
+        { return step.distance > params.maxDistance; };
+
+        std::vector<LaserScan> clusters;
+        for (const auto& laserScanStep : scan)
+        {
+            // cluster finished
+            if (isInvalid(laserScanStep) or (not add(laserScanStep)))
+            {
+                if (not cluster().empty())
+                {
+                    clusters.push_back(cluster());
+                    clear();
+
+                    // work on a new cluster
+                    add(laserScanStep);
+                }
+            }
+        }
+
+        return clusters;
+    }
+
+    const LaserScan& ScanClustering::cluster() const { return scan; }
+
+    void ScanClustering::clear() { scan.clear(); }
+
+    bool ScanClustering::supports(const LaserScanStep& scanStep)
+    {
+        // OK to create a new cluster if it's empty
+        if (scan.empty())
+        {
+            return true;
+        }
+
+        const float distanceDiff         = std::fabs(scanStep.distance - scan.back().distance);
+        const bool isWithinDistanceRange = distanceDiff < params.distanceThreshold;
+
+        const float angleDiff         = std::fabs(scanStep.angle - scan.back().angle);
+        const bool isWithinAngleRange = angleDiff < params.angleThreshold;
+
+        return (isWithinDistanceRange and isWithinAngleRange);
+    }
+} // namespace armarx::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h
new file mode 100644
index 00000000..6cee501d
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h
@@ -0,0 +1,71 @@
+/*
+ * 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>
+
+namespace armarx::laser_scanner_feature_extraction
+{
+    namespace detail
+    {
+        struct ScanClusteringParams
+        {
+            // Clustering options to decide whether a point belongs to the cluster
+            float distanceThreshold; // [mm]
+            float angleThreshold;    // [rad]
+
+            /// Range filter: only consider points that are closer than maxDistance
+            float maxDistance; // [mm]
+        };
+    } // namespace detail
+
+    class ScanClustering
+    {
+      public:
+        using Params   = detail::ScanClusteringParams;
+        using Clusters = std::vector<LaserScan>;
+
+        ScanClustering(const Params& params);
+
+        /**
+        * @brief Performs cluster detection on a full laser scan.
+        *
+        * @param scan A full scan
+        * @return The input scan split into clusters.
+        */
+        Clusters detectClusters(const LaserScan& scan);
+
+      protected:
+        const LaserScan& cluster() const;
+
+        bool add(const LaserScanStep& scanStep);
+        bool supports(const LaserScanStep& scanStep);
+
+        void clear();
+
+      private:
+        LaserScan scan;
+
+        const Params params;
+    };
+
+} // namespace armarx::laser_scanner_feature_extraction
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.cpp
new file mode 100644
index 00000000..41af90f4
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.cpp
@@ -0,0 +1,23 @@
+#include "eigen.h"
+
+#include <algorithm>
+
+namespace armarx::conversions
+{
+
+    std::vector<Eigen::Vector3f> to3D(const std::vector<Eigen::Vector2f>& v)
+
+    {
+        std::vector<Eigen::Vector3f> v3;
+        v3.reserve(v.size());
+
+        std::transform(
+            v.begin(),
+            v.end(),
+            std::back_inserter(v3),
+            static_cast<Eigen::Vector3f (*)(const Eigen::Vector2f&)>(&to3D));
+
+        return v3;
+    }
+
+} // namespace armarx::conversions
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.h
new file mode 100644
index 00000000..615d41c6
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/eigen.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author     Fabian Reister ( fabian dot reister at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+namespace armarx::conversions
+{
+
+    inline Eigen::Vector2f to2D(const Eigen::Vector3f& v2)
+    {
+        return Eigen::Vector2f{v2.x(), v2.y()};
+    }
+
+    inline Eigen::Vector3f to3D(const Eigen::Vector2f& v2)
+    {
+        return Eigen::Vector3f{v2.x(), v2.y(), 0.F};
+    }
+
+    inline Eigen::Isometry3f to3D(const Eigen::Affine2f& p2)
+    {
+        Eigen::Isometry3f pose            = Eigen::Isometry3f::Identity();
+        pose.linear().block<2, 2>(0, 0) = p2.linear();
+        pose.translation().head<2>()    = p2.translation();
+
+        return pose;
+    }
+
+    std::vector<Eigen::Vector3f> to3D(const std::vector<Eigen::Vector2f>& v);
+
+} // namespace armarx::conversions
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv.h
new file mode 100644
index 00000000..ae12d627
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author     Fabian Reister ( fabian dot reister at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <opencv2/core/types.hpp>
+
+namespace armarx::conversions
+{
+
+    // TODO(fabian.reister): this is a specialized method
+    template <typename CvT> CvT cast(const auto &pt) { return CvT(pt.x, pt.y); }
+
+    template <typename PointOutT, typename PointInT>
+    std::vector<PointOutT> cast(const std::vector<PointInT> &points)
+    {
+        std::vector<PointOutT> v;
+        v.reserve(points.size());
+
+        std::transform(points.begin(),
+                       points.end(),
+                       std::back_inserter(v),
+                       static_cast<PointOutT (*)(const PointInT &)>(cast));
+
+        return v;
+    }
+} // namespace armarx::conversions
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_eigen.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_eigen.h
new file mode 100644
index 00000000..0e6b8182
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_eigen.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <Eigen/Core>
+
+#include <opencv2/core/types.hpp>
+
+namespace armarx::conversions
+{
+    inline cv::Point2f eigen2cv(const Eigen::Vector2f& pt)
+    {
+        return {pt.x(), pt.y()};
+    }
+
+    template <typename EigenT, typename CvT = decltype(eigen2cv(EigenT()))>
+    auto eigen2cv(const std::vector<EigenT>& points)
+    {
+        std::vector<CvT> v;
+        v.reserve(points.size());
+
+        std::transform(
+            points.begin(), points.end(), std::back_inserter(v), static_cast<CvT(*)(const EigenT&)>(eigen2cv));
+
+        return v;
+    }
+
+    inline Eigen::Vector2f cv2eigen(const cv::Point2f& pt)
+    {
+        return Eigen::Vector2f{pt.x, pt.y};
+    }
+
+} // namespace armarx::conversions
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_pcl.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_pcl.h
new file mode 100644
index 00000000..375f166f
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/opencv_pcl.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <Eigen/Core>
+
+#include <pcl/point_cloud.h>
+#include <pcl/point_types.h>
+
+#include <opencv2/core/types.hpp>
+
+namespace armarx::conversions
+{
+
+    inline cv::Point2f pcl2cv(const pcl::PointXY& pt)
+    {
+        return cv::Point2f{pt.x, pt.y};
+    }
+    inline cv::Point3f pcl2cv(const pcl::PointXYZ& pt)
+    {
+        return cv::Point3f{pt.x, pt.y, pt.z};
+    }
+
+    template <typename PointT, typename CvT = decltype(pcl2cv(PointT()))>
+    auto pcl2cv(const pcl::PointCloud<PointT>& pointCloud)
+    {
+        std::vector<CvT> v;
+        v.reserve(pointCloud.points.size());
+
+        std::transform(pointCloud.points.begin(),
+                       pointCloud.points.end(),
+                       std::back_inserter(v),
+                       static_cast<CvT (*)(const PointT&)>(pcl2cv));
+
+        return v;
+    }
+
+} // namespace armarx::conversions
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.cpp
new file mode 100644
index 00000000..c8ed47f2
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.cpp
@@ -0,0 +1,27 @@
+#include "pcl.h"
+
+#include <algorithm>
+
+namespace armarx::conversions
+{
+    pcl::PointCloud<pcl::PointXYZ> to3D(const pcl::PointCloud<pcl::PointXY>& v)
+    {
+        pcl::PointCloud<pcl::PointXYZ> pc;
+        pc.points.reserve(v.size());
+
+        std::transform(
+            v.begin(),
+            v.end(),
+            std::back_inserter(pc),
+            static_cast<pcl::PointXYZ (*)(const pcl::PointXY&)>(&to3D));
+
+        pc.width    = v.height;
+        pc.height   = v.width;
+        pc.is_dense = v.is_dense;
+
+        pc.header = v.header;
+
+        return pc;
+    }
+
+} // namespace armarx::conversions
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.h
new file mode 100644
index 00000000..2caf92b9
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author     Fabian Reister ( fabian dot reister at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <pcl/point_cloud.h>
+#include <pcl/point_types.h>
+
+namespace armarx::conversions
+{
+
+    inline pcl::PointXYZ to3D(const pcl::PointXY& v2)
+    {
+        return pcl::PointXYZ{v2.x, v2.y, 0.F};
+    }
+
+    pcl::PointCloud<pcl::PointXYZ> to3D(const pcl::PointCloud<pcl::PointXY>& v);
+
+} // namespace armarx::conversions
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl_eigen.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl_eigen.h
new file mode 100644
index 00000000..b72277e0
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/conversions/pcl_eigen.h
@@ -0,0 +1,92 @@
+/*
+ * 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 <algorithm>
+
+#include <Eigen/Core>
+
+#include <pcl/point_cloud.h>
+#include <pcl/point_types.h>
+
+namespace armarx::conversions
+{
+    // pcl2eigen
+
+    inline Eigen::Vector2f pcl2eigen(const pcl::PointXY& pt)
+    {
+        return Eigen::Vector2f{pt.x, pt.y};
+    }
+    inline Eigen::Vector3f pcl2eigen(const pcl::PointXYZ& pt)
+    {
+        return Eigen::Vector3f{pt.x, pt.y, pt.z};
+    }
+
+    template <typename PointT,
+              typename EigenVector = decltype(pcl2eigen(PointT()))>
+    auto pcl2eigen(const pcl::PointCloud<PointT>& pointCloud)
+    -> std::vector<EigenVector>
+    {
+        std::vector<EigenVector> v;
+        v.reserve(pointCloud.points.size());
+
+        std::transform(pointCloud.points.begin(),
+                       pointCloud.points.end(),
+                       std::back_inserter(v),
+                       static_cast<EigenVector(*)(const PointT&)>(pcl2eigen));
+
+        return v;
+    }
+
+    // eigen2pcl
+
+    inline pcl::PointXY eigen2pcl(const Eigen::Vector2f& pt)
+    {
+        return pcl::PointXY{pt.x(), pt.y()};
+    }
+
+    inline pcl::PointXYZ eigen2pcl(const Eigen::Vector3f& pt)
+    {
+        return pcl::PointXYZ{pt.x(), pt.y(), 0.F};
+    }
+
+    template <typename EigenVector,
+              typename PointT = decltype(eigen2pcl(EigenVector()))>
+    auto eigen2pcl(const std::vector<EigenVector>& points)
+    -> pcl::PointCloud<PointT>
+    {
+        pcl::PointCloud<PointT> pointCloud;
+        pointCloud.points.reserve(points.size());
+
+        std::transform(points.begin(),
+                       points.end(),
+                       std::back_inserter(pointCloud.points),
+                       static_cast<PointT(*)(const EigenVector&)>(eigen2pcl));
+
+        pointCloud.width    = pointCloud.points.size();
+        pointCloud.height   = 1;
+        pointCloud.is_dense = true;
+
+        return pointCloud;
+    }
+
+} // namespace armarx::conversions
\ No newline at end of file
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/geometry.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/geometry.h
new file mode 100644
index 00000000..8b70a56d
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/geometry.h
@@ -0,0 +1,57 @@
+#pragma once
+
+// TODO To be moved to Simox after ROBDEKON demo
+
+#include <boost/geometry.hpp>
+#include <boost/geometry/algorithms/assign.hpp>
+#include <boost/geometry/algorithms/detail/within/interface.hpp>
+#include <boost/geometry/algorithms/distance.hpp>
+#include <boost/geometry/algorithms/make.hpp>
+#include <boost/geometry/geometries/point_xy.hpp>
+#include <boost/geometry/geometries/polygon.hpp>
+#include <boost/geometry/geometries/register/point.hpp>
+#include <boost/geometry/strategies/strategies.hpp>
+
+#include <VirtualRobot/MathTools.h>
+
+BOOST_GEOMETRY_REGISTER_POINT_2D(Eigen::Vector2f, float, cs::cartesian, x(), y())
+
+namespace armarx::geometry
+{
+    namespace bg = boost::geometry;
+
+    class ConvexHull
+    {
+    public:
+        using Point = bg::model::d2::point_xy<float>;
+
+        ConvexHull(const VirtualRobot::MathTools::ConvexHull2D& hull) : ConvexHull(hull.vertices)
+        {
+        }
+
+        ConvexHull(const std::vector<Eigen::Vector2f>& hull)
+        {
+            boost::geometry::assign_points(polygon, hull);
+        }
+
+        bool contains(const Eigen::Vector2f& point) const
+        {
+            return bg::within(Point(point.x(), point.y()), polygon);
+        }
+
+        float area() const
+        {
+            return static_cast<float>(bg::area(polygon));
+        }
+
+    private:
+        bg::model::polygon<Point> polygon;
+    };
+
+    // inline float area()
+    // {
+
+    //     return bg::area(polygon);
+    // }
+
+} // namespace armarx::geometry
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/test/CMakeLists.txt b/source/armarx/navigation/components/laser_scanner_feature_extraction/test/CMakeLists.txt
new file mode 100644
index 00000000..3435a5f5
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/test/CMakeLists.txt
@@ -0,0 +1,5 @@
+
+# Libs required for the tests
+SET(LIBS ${LIBS} ArmarXCore LaserScannerFeatureExtraction)
+ 
+armarx_add_test(LaserScannerFeatureExtractionTest LaserScannerFeatureExtractionTest.cpp "${LIBS}")
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp
new file mode 100644
index 00000000..fdad60e2
--- /dev/null
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp
@@ -0,0 +1,65 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    RobotComponents::ArmarXObjects::LaserScannerFeatureExtraction
+ * @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
+ */
+
+#include "ArmarXCore/core/logging/Logging.h"
+#include "RobotComponents/components/LaserScannerFeatureExtraction/FeatureExtractor.h"
+#include <boost/test/tools/old/interface.hpp>
+#define BOOST_TEST_MODULE RobotComponents::ArmarXObjects::LaserScannerFeatureExtraction
+
+#define ARMARX_BOOST_TEST
+
+#include <RobotComponents/Test.h>
+
+#include <pcl/point_types.h>
+
+#include "../EnclosingEllipsoid.h"
+
+#include <iostream>
+
+BOOST_AUTO_TEST_CASE(testExample)
+{
+    // Ground truth (GT) ellipsoid with params:
+    //  center = (0,0)
+    //  radii = (4,2)
+    //  angle = 0
+    armarx::laser_scanner_feature_extraction::FeatureExtractor::Points points;
+    points.push_back(Eigen::Vector2f{-4.F, 0});
+    points.push_back(Eigen::Vector2f{0, 2.F});
+    points.push_back(Eigen::Vector2f{4.F, 0});
+    points.push_back(Eigen::Vector2f{0, -2.F});
+
+    const Eigen::Vector2f radii(4, 2);
+
+    armarx::laser_scanner_feature_extraction::EnclosingEllipsoid ee(points);
+
+    // The computed ellipsoid must contain the GT ellipsoid
+    BOOST_CHECK_GE(ee.radii.x(), 4.F);
+    BOOST_CHECK_GE(ee.radii.y(), 2.F);
+
+    // ... but should not be too large
+    BOOST_CHECK_LE(ee.radii.x(), 4.1F);
+    BOOST_CHECK_LE(ee.radii.y(), 2.1F);
+
+    // The computed ellipsoid must be roughly at the position of the GT ellipsoid
+    BOOST_CHECK_LT(std::fabs(ee.pose.translation().x()), 0.1);
+    BOOST_CHECK_LT(std::fabs(ee.pose.translation().y()), 0.1);
+}
-- 
GitLab


From 06df98298a8ed09678e11993eecd094677394d66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Mon, 6 Mar 2023 18:13:19 +0100
Subject: [PATCH 03/13] Fix includes

---
 .../laser_scanner_feature_extraction/ArVizDrawer.h         | 2 +-
 .../laser_scanner_feature_extraction/Component.cpp         | 4 ++--
 .../laser_scanner_feature_extraction/Component.h           | 7 +++----
 .../laser_scanner_feature_extraction/FeatureExtractor.cpp  | 6 +++---
 .../laser_scanner_feature_extraction/FeatureExtractor.h    | 2 +-
 .../test/LaserScannerFeatureExtractionTest.cpp             | 2 +-
 6 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
index 59045681..06dc0f0d 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
@@ -29,7 +29,7 @@
 
 #include <RobotAPI/components/ArViz/Client/ScopedClient.h>
 
-#include "RobotComponents/components/LaserScannerFeatureExtraction/FeatureExtractor.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h"
 
 namespace armarx::cartographer
 {
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
index 399b1012..e1b38a2d 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
@@ -20,7 +20,7 @@
  *             GNU General Public License
  */
 
-#include "LaserScannerFeatureExtraction.h"
+#include "Component.h"
 
 #include <algorithm>
 #include <cmath>
@@ -43,7 +43,7 @@
 #include "RobotAPI/libraries/armem_vision/types.h"
 #include "RobotAPI/libraries/core/remoterobot/RemoteRobot.h"
 
-#include "RobotComponents/components/LaserScannerFeatureExtraction/geometry.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/geometry.h"
 
 #include "ArVizDrawer.h"
 #include "FeatureExtractor.h"
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
index 214ccf97..e02f8972 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
@@ -37,10 +37,9 @@
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h>
 #include <RobotAPI/libraries/armem_vision/client/laser_scanner_features/Writer.h>
 
-#include "RobotComponents/components/LaserScannerFeatureExtraction/ChainApproximation.h"
-#include "RobotComponents/components/LaserScannerFeatureExtraction/ScanClustering.h"
-#include "RobotComponents/components/LaserScannerFeatureExtraction/geometry.h"
-#include <RobotComponents/interface/components/LaserScannerFeatureExtraction/LaserScannerFeatureExtraction.h>
+#include "armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/geometry.h"
 #include <RobotComponents/libraries/cartographer/MessageQueue.h>
 #include <RobotComponents/libraries/cartographer/types.h>
 
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
index 90949d11..96cdfac5 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
@@ -24,9 +24,9 @@
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
 #include <ArmarXCore/core/logging/Logging.h>
 
-#include <RobotComponents/components/LaserScannerFeatureExtraction/ChainApproximation.h>
-#include <RobotComponents/components/LaserScannerFeatureExtraction/EnclosingEllipsoid.h>
-#include <RobotComponents/components/LaserScannerFeatureExtraction/ScanClustering.h>
+#include <armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h>
+#include <armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h>
+#include <armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h>
 #include <RobotComponents/libraries/cartographer/util/laser_scanner_conversion.h>
 
 #include "Path.h"
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
index 3dd5caac..f581c8e3 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
@@ -28,7 +28,7 @@
 #include <VirtualRobot/MathTools.h>
 #include "RobotAPI/libraries/armem_vision/types.h"
 
-#include "RobotComponents/components/LaserScannerFeatureExtraction/ChainApproximation.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h"
 #include "RobotComponents/libraries/cartographer/types.h"
 #include <RobotComponents/interface/components/GraspingManager/RobotPlacementInterface.h>
 
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp
index fdad60e2..db413af6 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/test/LaserScannerFeatureExtractionTest.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "ArmarXCore/core/logging/Logging.h"
-#include "RobotComponents/components/LaserScannerFeatureExtraction/FeatureExtractor.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h"
 #include <boost/test/tools/old/interface.hpp>
 #define BOOST_TEST_MODULE RobotComponents::ArmarXObjects::LaserScannerFeatureExtraction
 
-- 
GitLab


From dd49354d45e43eaea06dcf4ee24edde3c199c9cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Mon, 6 Mar 2023 18:50:16 +0100
Subject: [PATCH 04/13] Copy laser scanner features memory classes

---
 .../armarx/navigation/memory/CMakeLists.txt   |  10 +
 .../memory/aron/LaserScannerFeatures.xml      |  67 ++++++
 .../navigation/memory/aron_conversions.cpp    | 120 ++++++++++
 .../navigation/memory/aron_conversions.h      |  86 +++++++
 .../client/laser_scanner_features/Reader.cpp  | 218 ++++++++++++++++++
 .../client/laser_scanner_features/Reader.h    | 113 +++++++++
 .../client/laser_scanner_features/Writer.cpp  | 111 +++++++++
 .../client/laser_scanner_features/Writer.h    |  85 +++++++
 source/armarx/navigation/memory/constants.h   |   1 +
 source/armarx/navigation/memory/types.h       |  91 ++++++++
 10 files changed, 902 insertions(+)
 create mode 100644 source/armarx/navigation/memory/aron/LaserScannerFeatures.xml
 create mode 100644 source/armarx/navigation/memory/aron_conversions.cpp
 create mode 100644 source/armarx/navigation/memory/aron_conversions.h
 create mode 100644 source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp
 create mode 100644 source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
 create mode 100644 source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
 create mode 100644 source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
 create mode 100644 source/armarx/navigation/memory/types.h

diff --git a/source/armarx/navigation/memory/CMakeLists.txt b/source/armarx/navigation/memory/CMakeLists.txt
index c03f9a2b..71e7aee4 100644
--- a/source/armarx/navigation/memory/CMakeLists.txt
+++ b/source/armarx/navigation/memory/CMakeLists.txt
@@ -1,6 +1,12 @@
+armarx_add_aron_library(memory_aron
+    ARON_FILES
+        aron/LaserScannerFeatures.xml
+)
+
 armarx_add_library(memory
     SOURCES
         #./memory.cpp
+        aron_conversions.cpp
         client/stack_result/Writer.cpp
         client/stack_result/Reader.cpp
         client/parameterization/Reader.cpp
@@ -15,6 +21,7 @@ armarx_add_library(memory
         # ./client/events/Reader.cpp
     HEADERS
         memory.h
+        aron_conversions.h
         client/stack_result/Writer.h
         client/stack_result/Reader.h
         client/parameterization/Reader.h
@@ -25,8 +32,11 @@ armarx_add_library(memory
         client/costmap/Reader.h
         client/human/Reader.h
         client/human/Writer.h
+        client/laser_scanner_features/Reader.h
+        client/laser_scanner_features/Writer.h
         client/rooms/Reader.h
         # ./client/events/Reader.h
+        types.h
     DEPENDENCIES
         ArmarXCoreInterfaces
         ArmarXCore
diff --git a/source/armarx/navigation/memory/aron/LaserScannerFeatures.xml b/source/armarx/navigation/memory/aron/LaserScannerFeatures.xml
new file mode 100644
index 00000000..32600d7d
--- /dev/null
+++ b/source/armarx/navigation/memory/aron/LaserScannerFeatures.xml
@@ -0,0 +1,67 @@
+<!--Some fancy comment -->
+<?xml version="1.0" encoding="UTF-8" ?>
+<AronTypeDefinition>
+    <CodeIncludes>
+    </CodeIncludes>
+    <AronIncludes>
+    </AronIncludes>
+
+    <GenerateTypes>
+
+        <Object name='armarx::armem::vision::arondto::Ellipsoid'>
+            <ObjectChild key='globalPose'>
+                <Pose />
+            </ObjectChild>
+            <ObjectChild key='radii'>
+                <Matrix rows="2" cols="1" type="float32" />
+            </ObjectChild>
+        </Object>
+
+        <Object name='armarx::armem::vision::arondto::Circle'>
+            <ObjectChild key='center'>
+                <Matrix rows="2" cols="1" type="float32" />
+            </ObjectChild>
+            <ObjectChild key='radius'>
+                <float />
+            </ObjectChild>
+        </Object>
+
+        <Object name="armarx::armem::vision::arondto::LaserScannerFeature">
+            <ObjectChild key="convexHull">
+                <List>
+                    <Matrix rows="2" cols="1" type="float32" />
+                </List>
+            </ObjectChild>
+            <ObjectChild key="circle">
+                <armarx::armem::vision::arondto::Circle/>
+            </ObjectChild>
+            <ObjectChild key="ellipsoid">
+                <armarx::armem::vision::arondto::Ellipsoid/>
+            </ObjectChild>
+            <!-- <ObjectChild key="chain">
+                <armarx::armem::vision::arondto::Chain/>
+            </ObjectChild> -->
+            <ObjectChild key="points">
+                <List>
+                    <Matrix rows="2" cols="1" type="float32" />
+                </List>
+            </ObjectChild>
+        </Object>
+
+         <Object name="armarx::armem::vision::arondto::LaserScannerFeatures">
+            <ObjectChild key="frame">
+                <String/>
+            </ObjectChild>
+            <ObjectChild key="frameGlobalPose">
+                <Pose/>
+            </ObjectChild>
+            <ObjectChild key="features">
+                <List>
+                    <armarx::armem::vision::arondto::LaserScannerFeature />
+                </List>
+            </ObjectChild>
+        </Object>
+
+
+    </GenerateTypes>
+</AronTypeDefinition> 
diff --git a/source/armarx/navigation/memory/aron_conversions.cpp b/source/armarx/navigation/memory/aron_conversions.cpp
new file mode 100644
index 00000000..814ec546
--- /dev/null
+++ b/source/armarx/navigation/memory/aron_conversions.cpp
@@ -0,0 +1,120 @@
+#include "aron_conversions.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
+#include <RobotAPI/libraries/armem/core/aron_conversions.h>
+#include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron/LaserScannerFeatures.aron.generated.h>
+#include <RobotAPI/libraries/aron/common/aron_conversions.h>
+#include <RobotAPI/libraries/aron/converter/common/Converter.h>
+#include <RobotAPI/libraries/aron/core/data/variant/complex/NDArray.h>
+
+#include "types.h"
+
+
+namespace armarx::armem::vision
+{
+
+
+    /************ toAron ************/
+
+    // auto toAron(const LaserScan& laserScan, aron::LaserScan& aronLaserScan)
+    // {
+    //     aronLaserScan.scan = toAron(laserScan);
+    // }
+
+    int64_t
+    toAron(const armem::Time& timestamp)
+    {
+        return timestamp.toMicroSecondsSinceEpoch();
+    }
+    
+
+    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.
+    }
+
+    // LaserScannerFeatures
+
+
+    void
+    toAron(arondto::Circle& dto, const Circle& bo)
+    {
+        dto.center = bo.center;
+        dto.radius = bo.radius;
+    }
+
+    void
+    toAron(arondto::Ellipsoid& dto, const Ellipsoid& bo)
+    {
+        dto.globalPose = bo.pose.matrix();
+        dto.radii = bo.radii;
+    }
+
+    void
+    toAron(arondto::LaserScannerFeature& dto, const LaserScannerFeature& bo)
+    {
+        toAron(dto.circle, bo.circle);
+        dto.convexHull = bo.convexHull;
+        toAron(dto.ellipsoid, bo.ellipsoid);
+        dto.points = bo.points;
+    }
+
+    void
+    toAron(arondto::LaserScannerFeatures& dto, const LaserScannerFeatures& bo)
+    {
+        aron::toAron(dto.frame, bo.frame);
+        aron::toAron(dto.frameGlobalPose, bo.frameGlobalPose);
+        aron::toAron(dto.features, bo.features);
+    }
+
+    void
+    fromAron(const arondto::Circle& dto, Circle& bo)
+    {
+        bo.center = dto.center;
+        bo.radius = dto.radius;
+    }
+    void
+    fromAron(const arondto::Ellipsoid& dto, Ellipsoid& bo)
+    {
+        bo.pose = dto.globalPose;
+        bo.radii = dto.radii;
+    }
+
+    void
+    fromAron(const arondto::LaserScannerFeature& dto, LaserScannerFeature& bo)
+    {
+        bo.convexHull = dto.convexHull;
+        fromAron(dto.circle, bo.circle);
+        fromAron(dto.ellipsoid, bo.ellipsoid);
+
+        // bo.chain = dto.chain;
+        bo.points = dto.points;
+    }
+
+    void
+    fromAron(const arondto::LaserScannerFeatures& dto, LaserScannerFeatures& bo)
+    {
+        aron::fromAron(dto.frame, bo.frame);
+        aron::fromAron(dto.frameGlobalPose, bo.frameGlobalPose);
+        aron::fromAron(dto.features, bo.features);
+    }
+
+} // namespace armarx::armem::vision
diff --git a/source/armarx/navigation/memory/aron_conversions.h b/source/armarx/navigation/memory/aron_conversions.h
new file mode 100644
index 00000000..bb1e5253
--- /dev/null
+++ b/source/armarx/navigation/memory/aron_conversions.h
@@ -0,0 +1,86 @@
+/*
+ * 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/armem/core/Time.h>
+#include <RobotAPI/libraries/armem_vision/aron/LaserScannerFeatures.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron/OccupancyGrid.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/types.h>
+#include <RobotAPI/libraries/aron/converter/common/VectorConverter.h>
+#include <RobotAPI/libraries/aron/converter/eigen/EigenConverter.h>
+#include <RobotAPI/libraries/aron/core/data/variant/complex/NDArray.h>
+
+namespace armarx::armem::vision
+{
+
+    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::data::NDArrayPtr& 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::data::NDArrayPtr
+    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::data::NDArrayPtr
+    toAron(const OccupancyGrid::Grid& grid)
+    {
+        return aron::converter::AronEigenConverter::ConvertFromArray(grid);
+    }
+
+    // LaserScannerFeatures
+    void toAron(arondto::LaserScannerFeatures& dto, const LaserScannerFeatures& bo);
+    void fromAron(const arondto::LaserScannerFeatures& dto, LaserScannerFeatures& bo);
+
+} // namespace armarx::armem::vision
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp
new file mode 100644
index 00000000..216088b4
--- /dev/null
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp
@@ -0,0 +1,218 @@
+#include "Reader.h"
+
+// STD / STL
+#include <algorithm>
+#include <cstring>
+#include <map>
+#include <optional>
+#include <ostream>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+// ICE
+#include <IceUtil/Handle.h>
+#include <IceUtil/Time.h>
+
+// Simox
+#include <SimoxUtility/algorithm/get_map_keys_values.h>
+
+// ArmarXCore
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/logging/LogSender.h>
+#include <ArmarXCore/core/logging/Logging.h>
+
+// RobotAPI Interfaces
+#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/LaserScannerFeatures.aron.generated.h>
+#include <RobotAPI/libraries/aron/core/Exception.h>
+#include <RobotAPI/libraries/aron/core/data/variant/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/error.h>
+#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h>
+#include <RobotAPI/libraries/armem/util/util.h>
+#include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron_conversions.h>
+#include <RobotAPI/libraries/armem_vision/types.h>
+
+
+namespace armarx::armem::vision::laser_scanner_features::client
+{
+
+    Reader::Reader(armem::client::MemoryNameSystem& memoryNameSystem) :
+        memoryNameSystem(memoryNameSystem)
+    {
+    }
+    Reader::~Reader() = default;
+
+
+    void
+    Reader::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def)
+    {
+        // ARMARX_DEBUG << "TransformReader: registerPropertyDefinitions";
+        // registerPropertyDefinitions(def);
+
+        const std::string prefix = propertyPrefix;
+
+        def->optional(properties.coreSegmentName,
+                      prefix + "CoreSegment",
+                      "Name of the mapping memory core segment to use.");
+
+        def->optional(properties.memoryName, prefix + "MemoryName");
+    }
+
+    void
+    Reader::connect()
+    {
+        // Wait for the memory to become available and add it as dependency.
+        ARMARX_IMPORTANT << "MappingDataReader: Waiting for memory '" << properties.memoryName
+                         << "' ...";
+        try
+        {
+            memoryReader =
+                memoryNameSystem.useReader(MemoryID().withMemoryName(properties.memoryName));
+            ARMARX_IMPORTANT << "MappingDataReader: Connected to memory '" << properties.memoryName
+                             << "'";
+        }
+        catch (const armem::error::CouldNotResolveMemoryServer& e)
+        {
+            ARMARX_ERROR << e.what();
+            return;
+        }
+    }
+
+    armarx::armem::client::query::Builder
+    Reader::buildQuery(const Query& query) const
+    {
+        armarx::armem::client::query::Builder qb;
+
+        qb.coreSegments()
+                       .withName(properties.coreSegmentName)
+                       .providerSegments()
+                       .withName(query.providerName)
+                       .entities()
+                       .all()
+                       .snapshots()
+                       .beforeOrAtTime(query.timestamp);
+
+        // auto entitySel = [&]()
+        // {
+        //     if (query.name.empty())
+        //     {
+        //         ARMARX_INFO << "querying all entities";
+        //         return sel.entities().all();
+        //     }
+        //     return sel.entities().withName(query.name);
+        // }();
+
+        // entitySel.snapshots().beforeOrAtTime(query.timestamp);
+
+        return qb;
+    }
+
+    std::vector<LaserScannerFeatures>
+    asFeaturesList(const wm::ProviderSegment& providerSegment)
+    {
+        if (providerSegment.empty())
+        {
+            ARMARX_WARNING << "No entities!";
+            return {};
+        }
+
+        // const auto convert = [](const auto& aronLaserScanStamped,
+        //                         const wm::EntityInstance& ei) -> LaserScanStamped
+        // {
+        //     LaserScanStamped laserScanStamped;
+        //     fromAron(aronLaserScanStamped, laserScanStamped);
+
+        //     const auto ndArrayNavigator =
+        //         aron::data::NDArray::DynamicCast(ei.data()->getElement("scan"));
+
+        //     ARMARX_CHECK_NOT_NULL(ndArrayNavigator);
+
+        //     laserScanStamped.data = fromAron<LaserScanStep>(ndArrayNavigator);
+        //     ARMARX_IMPORTANT << "4";
+
+        //     return laserScanStamped;
+        // };
+
+        std::vector<LaserScannerFeatures> laserScannerFeatures;
+
+        // loop over all entities and their snapshots
+        providerSegment.forEachEntity(
+            [&](const wm::Entity& entity)
+            {
+                // If we don't need this warning, we could directly iterate over the snapshots.
+                if (entity.empty())
+                {
+                    ARMARX_WARNING << "Empty history for " << entity.id();
+                }
+                ARMARX_DEBUG << "History size: " << entity.size();
+
+                entity.forEachInstance(
+                    [&](const wm::EntityInstance& entityInstance)
+                    {
+                        if (const auto o = tryCast<arondto::LaserScannerFeatures>(entityInstance))
+                        {
+                            LaserScannerFeatures& f = laserScannerFeatures.emplace_back();
+                            fromAron(o.value(), f);
+                        }
+                        return true;
+                    });
+                return true;
+            });
+
+        return laserScannerFeatures;
+    }
+
+    Reader::Result
+    Reader::queryData(const Query& query) const
+    {
+        const auto qb = buildQuery(query);
+
+        ARMARX_DEBUG << "[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 {.features = {},
+                    .status = Result::Status::Error,
+                    .errorMessage = qResult.errorMessage};
+        }
+
+        // now create result from memory
+        const wm::ProviderSegment& providerSegment =
+            qResult.memory.getCoreSegment(properties.coreSegmentName)
+                .getProviderSegment(query.providerName);
+
+        const auto features = asFeaturesList(providerSegment);
+
+        // const auto laserScans = asLaserScans(providerSegment);
+        // std::vector<std::string> sensors;
+        // providerSegment.forEachEntity(
+        //     [&sensors](const wm::Entity& entity)
+        //     {
+        //         sensors.push_back(entity.name());
+        //         return true;
+        //     });
+
+        return {.features = features,
+                // .sensors = sensors,
+                .status = Result::Status::Success,
+                .errorMessage = ""};
+    }
+
+} // namespace armarx::armem::vision::laser_scanner_features::client
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
new file mode 100644
index 00000000..4752ca85
--- /dev/null
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
@@ -0,0 +1,113 @@
+/*
+ * 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 <string>
+#include <vector>
+
+#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h>
+#include <ArmarXCore/core/time/DateTime.h>
+
+#include <RobotAPI/libraries/armem/client.h>
+#include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
+#include <RobotAPI/libraries/armem/client/Reader.h>
+#include <RobotAPI/libraries/armem/client/query/Builder.h>
+#include <RobotAPI/libraries/armem_vision/types.h>
+
+
+namespace armarx
+{
+    class ManagedIceObject;
+}
+
+namespace armarx::armem::vision::laser_scanner_features::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 Reader
+    {
+    public:
+        Reader(armem::client::MemoryNameSystem& memoryNameSystem);
+        virtual ~Reader();
+
+        void connect();
+
+        struct Query
+        {
+            std::string providerName;
+
+            std::string name; // sensor name
+
+            armarx::core::time::DateTime timestamp;
+
+            // std::vector<std::string> sensorList;
+        };
+
+        struct Result
+        {
+            std::vector<LaserScannerFeatures> features;
+
+            // std::vector<std::string> sensors;
+
+            enum Status
+            {
+                Error,
+                Success
+            } status;
+
+            std::string errorMessage;
+        };
+
+        Result queryData(const Query& query) const;
+
+        void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def);
+
+
+    protected:
+        armarx::armem::client::query::Builder buildQuery(const Query& query) const;
+
+    private:
+        armem::client::MemoryNameSystem& memoryNameSystem;
+        armem::client::Reader memoryReader;
+
+        // Properties
+        struct Properties
+        {
+            std::string memoryName = "Vision";
+            std::string coreSegmentName = "LaserScannerFeatures";
+        } properties;
+
+        const std::string propertyPrefix = "mem.vision.laser_scanner_features.";
+    };
+
+} // namespace armarx::armem::vision::laser_scanner_features::client
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
new file mode 100644
index 00000000..0b24561d
--- /dev/null
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
@@ -0,0 +1,111 @@
+#include "Writer.h"
+
+#include "RobotAPI/libraries/armem_vision/constants.h"
+#include <RobotAPI/libraries/armem/core/error.h>
+#include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron/LaserScannerFeatures.aron.generated.h>
+#include <RobotAPI/libraries/armem_vision/aron_conversions.h>
+
+
+namespace armarx::armem::vision::laser_scanner_features::client
+{
+
+    Writer::Writer(armem::client::MemoryNameSystem& memoryNameSystem) :
+        memoryNameSystem(memoryNameSystem)
+    {
+    }
+    Writer::~Writer() = default;
+
+
+    void
+    Writer::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def)
+    {
+        ARMARX_DEBUG << "LaserScansWriter: registerPropertyDefinitions";
+
+        const std::string prefix = propertyPrefix;
+
+        // def->optional(properties.coreSegmentName,
+        //               prefix + "CoreSegment",
+        //               "Name of the mapping memory core segment to use.");
+
+        // def->optional(properties.memoryName, prefix + "MemoryName");
+    }
+
+    void
+    Writer::connect()
+    {
+        // Wait for the memory to become available and add it as dependency.
+        ARMARX_IMPORTANT << "LaserScansWriter: Waiting for memory '" << constants::memoryName
+                         << "' ...";
+        try
+        {
+            memoryWriter =
+                memoryNameSystem.useWriter(MemoryID().withMemoryName(constants::memoryName));
+            ARMARX_IMPORTANT << "MappingDataWriter: Connected to memory '" << constants::memoryName
+                             << "'";
+        }
+        catch (const armem::error::CouldNotResolveMemoryServer& e)
+        {
+            ARMARX_ERROR << e.what();
+            return;
+        }
+
+        ARMARX_IMPORTANT << "LaserScansWriter: Connected to memory '" << constants::memoryName;
+    }
+
+    bool
+    Writer::store(const LaserScannerFeatures& features,
+                  const std::string& providerName,
+                  const Time& timestamp)
+    {
+        std::lock_guard g{memoryWriterMutex};
+
+        // const auto result = memoryWriter.addSegment(constants::memoryName,
+        //                                             constants::laserScannerFeaturesCoreSegment);
+
+        // if (not result.success)
+        // {
+        //     ARMARX_ERROR << result.errorMessage;
+
+        //     // TODO(fabian.reister): message
+        //     return false;
+        // }
+
+        const auto entityID = armem::MemoryID()
+                                    .withMemoryName(constants::memoryName)
+                                    .withCoreSegmentName(constants::laserScannerFeaturesCoreSegment)
+                                    .withProviderSegmentName(providerName)
+                                    .withEntityName(features.frame)
+                                    .withTimestamp(timestamp);
+
+        ARMARX_VERBOSE << "Memory id is " << entityID.str();
+
+        armem::EntityUpdate update;
+        update.entityID = entityID;
+
+        ARMARX_TRACE;
+
+        arondto::LaserScannerFeatures dto;
+        toAron(dto, features);
+
+        ARMARX_TRACE;
+
+        update.instancesData = {dto.toAron()};
+        update.timeCreated = timestamp;
+
+        ARMARX_DEBUG << "Committing " << update << " at time " << timestamp;
+
+        ARMARX_TRACE;
+        armem::EntityUpdateResult updateResult = memoryWriter.commit(update);
+
+        ARMARX_DEBUG << updateResult;
+
+        if (not updateResult.success)
+        {
+            ARMARX_ERROR << updateResult.errorMessage;
+        }
+
+        return updateResult.success;
+    }
+
+} // namespace armarx::armem::vision::laser_scanner_features::client
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
new file mode 100644
index 00000000..ca580057
--- /dev/null
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
@@ -0,0 +1,85 @@
+/*
+ * 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 <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h>
+
+#include "RobotAPI/libraries/armem_vision/types.h"
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
+#include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
+#include <RobotAPI/libraries/armem/client/Writer.h>
+
+
+namespace armarx::armem::vision::laser_scanner_features::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
+    {
+    public:
+        Writer(armem::client::MemoryNameSystem& memoryNameSystem);
+        virtual ~Writer();
+
+
+        void connect();
+
+        // MappingDataWriterInterface
+        /// to be called in Component::onConnectComponent
+        // void connect() override;
+
+        /// to be called in Component::addPropertyDefinitions
+        void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def) /*override*/;
+
+        bool store(const LaserScannerFeatures& features,
+                   const std::string& providerName,
+                   const Time& timestamp);
+
+    private:
+        armem::client::MemoryNameSystem& memoryNameSystem;
+        armem::client::Writer memoryWriter;
+
+        // Properties
+        struct Properties
+        {
+            // std::string memoryName = "Vision";
+            // std::string coreSegmentName = "LaserScannerFeatures";
+        } properties;
+
+        std::mutex memoryWriterMutex;
+
+        const std::string propertyPrefix = "mem.vision.laser_scanner_features.";
+    };
+
+} // namespace armarx::armem::vision::laser_scanner_features::client
diff --git a/source/armarx/navigation/memory/constants.h b/source/armarx/navigation/memory/constants.h
index 177f9730..56538fb7 100644
--- a/source/armarx/navigation/memory/constants.h
+++ b/source/armarx/navigation/memory/constants.h
@@ -33,6 +33,7 @@ namespace armarx::navigation::memory::constants
     inline const std::string LocationCoreSegmentName = "Location";
     inline const std::string CostmapCoreSegmentName = "Costmap";
     inline const std::string HumanCoreSegmentName = "Human";
+    inline const std::string laserScannerFeaturesCoreSegment = "LaserScannerFeatures";
 
     inline const std::string GlobalPlannerResultCoreSegment = "Results_GlobalPlanner";
     inline const std::string LocalPlannerResultCoreSegment = "Results_LocalPlanner";
diff --git a/source/armarx/navigation/memory/types.h b/source/armarx/navigation/memory/types.h
new file mode 100644
index 00000000..fe584e3d
--- /dev/null
+++ b/source/armarx/navigation/memory/types.h
@@ -0,0 +1,91 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author     Fabian Reister ( fabian dot reister at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <VirtualRobot/MathTools.h>
+
+#include <RobotAPI/interface/units/LaserScannerUnit.h>
+#include <RobotAPI/libraries/armem/core/Time.h>
+
+namespace armarx::armem::vision
+{
+
+
+    // template<typename _ValueT = float>
+    struct OccupancyGrid
+    {
+        float resolution; // [mm]
+
+        std::string frame;
+        Eigen::Affine3f pose;
+
+        // using ValueType = _ValueT;
+        using CellType = float;
+        using Grid = Eigen::Array<CellType, Eigen::Dynamic, Eigen::Dynamic>;
+
+        Grid grid;
+    };
+
+    struct Ellipsoid
+    {
+        Eigen::Isometry3f pose = Eigen::Isometry3f::Identity();
+
+        Eigen::Vector2f radii = Eigen::Vector2f::Zero();
+    };
+
+    struct Circle
+    {
+        Eigen::Vector2f center = Eigen::Vector2f::Zero();
+        float radius = 0.F;
+    };
+
+    struct LaserScannerFeature
+    {
+        using Points = std::vector<Eigen::Vector2f>;
+        using Chain = Points;
+
+        Points convexHull;
+
+        Circle circle;
+        Ellipsoid ellipsoid;
+
+        Chain chain;
+
+        Points points;
+    };
+
+    struct LaserScannerFeatures
+    {
+        // TODO(fabian.reister): framed pose
+        std::string frame;
+        Eigen::Isometry3f frameGlobalPose;
+
+        std::vector<LaserScannerFeature> features;
+
+
+        // std::vector<Ellipsoid> linesAsEllipsoids(float axisLength) const;
+    };
+
+
+} // namespace armarx::armem::vision
-- 
GitLab


From 14dcafba1e8e4eaeff8e0dc5298af4db6109f50c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Wed, 8 Mar 2023 05:56:40 +0100
Subject: [PATCH 05/13] Adjust memory

Change to proper namespace and fix resulting conflicts.
---
 .../armarx/navigation/memory/CMakeLists.txt   |  3 ++
 .../memory/aron/LaserScannerFeatures.xml      | 16 +++---
 .../navigation/memory/aron_conversions.cpp    | 36 ++-----------
 .../navigation/memory/aron_conversions.h      | 53 ++-----------------
 .../navigation/memory/client/human/Writer.h   |  5 +-
 .../client/laser_scanner_features/Reader.cpp  | 39 +++++++-------
 .../client/laser_scanner_features/Reader.h    |  6 +--
 .../client/laser_scanner_features/Writer.cpp  | 40 +++++++-------
 .../client/laser_scanner_features/Writer.h    |  9 ++--
 source/armarx/navigation/memory/types.h       | 20 +------
 10 files changed, 71 insertions(+), 156 deletions(-)

diff --git a/source/armarx/navigation/memory/CMakeLists.txt b/source/armarx/navigation/memory/CMakeLists.txt
index 71e7aee4..c3910e06 100644
--- a/source/armarx/navigation/memory/CMakeLists.txt
+++ b/source/armarx/navigation/memory/CMakeLists.txt
@@ -17,6 +17,8 @@ armarx_add_library(memory
         client/costmap/Reader.cpp
         client/human/Reader.cpp
         client/human/Writer.cpp
+        client/laser_scanner_features/Reader.cpp
+        client/laser_scanner_features/Writer.cpp
         client/rooms/Reader.cpp
         # ./client/events/Reader.cpp
     HEADERS
@@ -41,6 +43,7 @@ armarx_add_library(memory
         ArmarXCoreInterfaces
         ArmarXCore
         armem
+        armarx_navigation::memory_aron
         armarx_navigation::core
         armarx_navigation::algorithms
         armarx_navigation::graph
diff --git a/source/armarx/navigation/memory/aron/LaserScannerFeatures.xml b/source/armarx/navigation/memory/aron/LaserScannerFeatures.xml
index 32600d7d..f78dea48 100644
--- a/source/armarx/navigation/memory/aron/LaserScannerFeatures.xml
+++ b/source/armarx/navigation/memory/aron/LaserScannerFeatures.xml
@@ -8,7 +8,7 @@
 
     <GenerateTypes>
 
-        <Object name='armarx::armem::vision::arondto::Ellipsoid'>
+        <Object name='armarx::navigation::memory::arondto::Ellipsoid'>
             <ObjectChild key='globalPose'>
                 <Pose />
             </ObjectChild>
@@ -17,7 +17,7 @@
             </ObjectChild>
         </Object>
 
-        <Object name='armarx::armem::vision::arondto::Circle'>
+        <Object name='armarx::navigation::memory::arondto::Circle'>
             <ObjectChild key='center'>
                 <Matrix rows="2" cols="1" type="float32" />
             </ObjectChild>
@@ -26,20 +26,20 @@
             </ObjectChild>
         </Object>
 
-        <Object name="armarx::armem::vision::arondto::LaserScannerFeature">
+        <Object name="armarx::navigation::memory::arondto::LaserScannerFeature">
             <ObjectChild key="convexHull">
                 <List>
                     <Matrix rows="2" cols="1" type="float32" />
                 </List>
             </ObjectChild>
             <ObjectChild key="circle">
-                <armarx::armem::vision::arondto::Circle/>
+                <armarx::navigation::memory::arondto::Circle/>
             </ObjectChild>
             <ObjectChild key="ellipsoid">
-                <armarx::armem::vision::arondto::Ellipsoid/>
+                <armarx::navigation::memory::arondto::Ellipsoid/>
             </ObjectChild>
             <!-- <ObjectChild key="chain">
-                <armarx::armem::vision::arondto::Chain/>
+                <armarx::navigation::memory::arondto::Chain/>
             </ObjectChild> -->
             <ObjectChild key="points">
                 <List>
@@ -48,7 +48,7 @@
             </ObjectChild>
         </Object>
 
-         <Object name="armarx::armem::vision::arondto::LaserScannerFeatures">
+         <Object name="armarx::navigation::memory::arondto::LaserScannerFeatures">
             <ObjectChild key="frame">
                 <String/>
             </ObjectChild>
@@ -57,7 +57,7 @@
             </ObjectChild>
             <ObjectChild key="features">
                 <List>
-                    <armarx::armem::vision::arondto::LaserScannerFeature />
+                    <armarx::navigation::memory::arondto::LaserScannerFeature />
                 </List>
             </ObjectChild>
         </Object>
diff --git a/source/armarx/navigation/memory/aron_conversions.cpp b/source/armarx/navigation/memory/aron_conversions.cpp
index 814ec546..bfc55c27 100644
--- a/source/armarx/navigation/memory/aron_conversions.cpp
+++ b/source/armarx/navigation/memory/aron_conversions.cpp
@@ -4,18 +4,14 @@
 #include <cstdint>
 #include <iterator>
 
-#include <RobotAPI/interface/units/LaserScannerUnit.h>
-#include <RobotAPI/libraries/armem/core/aron_conversions.h>
-#include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h>
-#include <RobotAPI/libraries/armem_vision/aron/LaserScannerFeatures.aron.generated.h>
 #include <RobotAPI/libraries/aron/common/aron_conversions.h>
 #include <RobotAPI/libraries/aron/converter/common/Converter.h>
 #include <RobotAPI/libraries/aron/core/data/variant/complex/NDArray.h>
 
 #include "types.h"
+#include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
 
-
-namespace armarx::armem::vision
+namespace armarx::navigation::memory
 {
 
 
@@ -26,34 +22,9 @@ namespace armarx::armem::vision
     //     aronLaserScan.scan = toAron(laserScan);
     // }
 
-    int64_t
-    toAron(const armem::Time& timestamp)
-    {
-        return timestamp.toMicroSecondsSinceEpoch();
-    }
-    
-
-    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.
-    }
 
     // LaserScannerFeatures
 
-
     void
     toAron(arondto::Circle& dto, const Circle& bo)
     {
@@ -91,6 +62,7 @@ namespace armarx::armem::vision
         bo.center = dto.center;
         bo.radius = dto.radius;
     }
+
     void
     fromAron(const arondto::Ellipsoid& dto, Ellipsoid& bo)
     {
@@ -117,4 +89,4 @@ namespace armarx::armem::vision
         aron::fromAron(dto.features, bo.features);
     }
 
-} // namespace armarx::armem::vision
+} // namespace armarx::navigation::memory
diff --git a/source/armarx/navigation/memory/aron_conversions.h b/source/armarx/navigation/memory/aron_conversions.h
index bb1e5253..0ad52e46 100644
--- a/source/armarx/navigation/memory/aron_conversions.h
+++ b/source/armarx/navigation/memory/aron_conversions.h
@@ -21,66 +21,23 @@
 
 #pragma once
 
-#include <RobotAPI/interface/units/LaserScannerUnit.h>
 #include <RobotAPI/libraries/armem/core/Time.h>
-#include <RobotAPI/libraries/armem_vision/aron/LaserScannerFeatures.aron.generated.h>
-#include <RobotAPI/libraries/armem_vision/aron/OccupancyGrid.aron.generated.h>
-#include <RobotAPI/libraries/armem_vision/types.h>
 #include <RobotAPI/libraries/aron/converter/common/VectorConverter.h>
 #include <RobotAPI/libraries/aron/converter/eigen/EigenConverter.h>
 #include <RobotAPI/libraries/aron/core/data/variant/complex/NDArray.h>
 
-namespace armarx::armem::vision
+namespace armarx::navigation::memory
 {
 
-    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::data::NDArrayPtr& navigator)
-    {
-        return aron::converter::AronVectorConverter::ConvertToVector<T>(navigator);
-    }
+    struct LaserScannerFeatures;
 
-    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::data::NDArrayPtr
-    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::data::NDArrayPtr
-    toAron(const OccupancyGrid::Grid& grid)
+    namespace arondto
     {
-        return aron::converter::AronEigenConverter::ConvertFromArray(grid);
+        struct LaserScannerFeatures;
     }
 
     // LaserScannerFeatures
     void toAron(arondto::LaserScannerFeatures& dto, const LaserScannerFeatures& bo);
     void fromAron(const arondto::LaserScannerFeatures& dto, LaserScannerFeatures& bo);
 
-} // namespace armarx::armem::vision
+} // namespace armarx::navigation::memory
diff --git a/source/armarx/navigation/memory/client/human/Writer.h b/source/armarx/navigation/memory/client/human/Writer.h
index b224149c..3a55ffc4 100644
--- a/source/armarx/navigation/memory/client/human/Writer.h
+++ b/source/armarx/navigation/memory/client/human/Writer.h
@@ -25,7 +25,6 @@
 #include <mutex>
 
 #include <RobotAPI/libraries/armem/client/util/SimpleWriterBase.h>
-#include <RobotAPI/libraries/armem_vision/types.h>
 
 #include <armarx/navigation/algorithms/Costmap.h>
 #include <armarx/navigation/human/types.h>
@@ -51,12 +50,12 @@ namespace armarx::navigation::memory::client::human
         ~Writer() override;
 
         bool store(const armarx::navigation::human::Humans& humans,
-                //    const std::string& name,
+                   //    const std::string& name,
                    const std::string& providerName,
                    const armem::Time& timestamp);
 
         bool store(const armarx::navigation::human::HumanGroups& groups,
-                //    const std::string& name,
+                   //    const std::string& name,
                    const std::string& providerName,
                    const armem::Time& timestamp);
 
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp
index 216088b4..c7640bdc 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.cpp
@@ -28,10 +28,11 @@
 #include <RobotAPI/interface/units/LaserScannerUnit.h>
 
 // RobotAPI Aron
-#include <RobotAPI/libraries/armem_vision/aron/LaserScannerFeatures.aron.generated.h>
 #include <RobotAPI/libraries/aron/core/Exception.h>
 #include <RobotAPI/libraries/aron/core/data/variant/complex/NDArray.h>
 
+#include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
+
 // RobotAPI Armem
 #include <RobotAPI/libraries/armem/client/Query.h>
 #include <RobotAPI/libraries/armem/client/Reader.h>
@@ -41,19 +42,18 @@
 #include <RobotAPI/libraries/armem/core/wm/memory_definitions.h>
 #include <RobotAPI/libraries/armem/util/util.h>
 #include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h>
-#include <RobotAPI/libraries/armem_vision/aron_conversions.h>
-#include <RobotAPI/libraries/armem_vision/types.h>
 
+#include <armarx/navigation/memory/aron_conversions.h>
 
-namespace armarx::armem::vision::laser_scanner_features::client
+namespace armarx::navigation::memory::client::laser_scanner_features
 {
 
     Reader::Reader(armem::client::MemoryNameSystem& memoryNameSystem) :
         memoryNameSystem(memoryNameSystem)
     {
     }
-    Reader::~Reader() = default;
 
+    Reader::~Reader() = default;
 
     void
     Reader::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def)
@@ -79,7 +79,7 @@ namespace armarx::armem::vision::laser_scanner_features::client
         try
         {
             memoryReader =
-                memoryNameSystem.useReader(MemoryID().withMemoryName(properties.memoryName));
+                memoryNameSystem.useReader(armem::MemoryID().withMemoryName(properties.memoryName));
             ARMARX_IMPORTANT << "MappingDataReader: Connected to memory '" << properties.memoryName
                              << "'";
         }
@@ -96,13 +96,13 @@ namespace armarx::armem::vision::laser_scanner_features::client
         armarx::armem::client::query::Builder qb;
 
         qb.coreSegments()
-                       .withName(properties.coreSegmentName)
-                       .providerSegments()
-                       .withName(query.providerName)
-                       .entities()
-                       .all()
-                       .snapshots()
-                       .beforeOrAtTime(query.timestamp);
+            .withName(properties.coreSegmentName)
+            .providerSegments()
+            .withName(query.providerName)
+            .entities()
+            .all()
+            .snapshots()
+            .beforeOrAtTime(query.timestamp);
 
         // auto entitySel = [&]()
         // {
@@ -120,7 +120,7 @@ namespace armarx::armem::vision::laser_scanner_features::client
     }
 
     std::vector<LaserScannerFeatures>
-    asFeaturesList(const wm::ProviderSegment& providerSegment)
+    asFeaturesList(const armem::wm::ProviderSegment& providerSegment)
     {
         if (providerSegment.empty())
         {
@@ -149,7 +149,7 @@ namespace armarx::armem::vision::laser_scanner_features::client
 
         // loop over all entities and their snapshots
         providerSegment.forEachEntity(
-            [&](const wm::Entity& entity)
+            [&](const armem::wm::Entity& entity)
             {
                 // If we don't need this warning, we could directly iterate over the snapshots.
                 if (entity.empty())
@@ -159,9 +159,10 @@ namespace armarx::armem::vision::laser_scanner_features::client
                 ARMARX_DEBUG << "History size: " << entity.size();
 
                 entity.forEachInstance(
-                    [&](const wm::EntityInstance& entityInstance)
+                    [&](const armem::wm::EntityInstance& entityInstance)
                     {
-                        if (const auto o = tryCast<arondto::LaserScannerFeatures>(entityInstance))
+                        if (const auto o =
+                                armem::tryCast<arondto::LaserScannerFeatures>(entityInstance))
                         {
                             LaserScannerFeatures& f = laserScannerFeatures.emplace_back();
                             fromAron(o.value(), f);
@@ -194,7 +195,7 @@ namespace armarx::armem::vision::laser_scanner_features::client
         }
 
         // now create result from memory
-        const wm::ProviderSegment& providerSegment =
+        const armem::wm::ProviderSegment& providerSegment =
             qResult.memory.getCoreSegment(properties.coreSegmentName)
                 .getProviderSegment(query.providerName);
 
@@ -215,4 +216,4 @@ namespace armarx::armem::vision::laser_scanner_features::client
                 .errorMessage = ""};
     }
 
-} // namespace armarx::armem::vision::laser_scanner_features::client
+} // namespace armarx::navigation::memory::client::laser_scanner_features
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
index 4752ca85..14cca12a 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
@@ -31,15 +31,15 @@
 #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
 #include <RobotAPI/libraries/armem/client/Reader.h>
 #include <RobotAPI/libraries/armem/client/query/Builder.h>
-#include <RobotAPI/libraries/armem_vision/types.h>
 
+#include <armarx/navigation/memory/types.h>
 
 namespace armarx
 {
     class ManagedIceObject;
 }
 
-namespace armarx::armem::vision::laser_scanner_features::client
+namespace armarx::navigation::memory::client::laser_scanner_features
 {
 
 
@@ -110,4 +110,4 @@ namespace armarx::armem::vision::laser_scanner_features::client
         const std::string propertyPrefix = "mem.vision.laser_scanner_features.";
     };
 
-} // namespace armarx::armem::vision::laser_scanner_features::client
+} // namespace armarx::navigation::memory::client::laser_scanner_features
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
index 0b24561d..5e453ea7 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
@@ -1,21 +1,20 @@
 #include "Writer.h"
 
-#include "RobotAPI/libraries/armem_vision/constants.h"
 #include <RobotAPI/libraries/armem/core/error.h>
-#include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h>
-#include <RobotAPI/libraries/armem_vision/aron/LaserScannerFeatures.aron.generated.h>
-#include <RobotAPI/libraries/armem_vision/aron_conversions.h>
 
+#include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
+#include <armarx/navigation/memory/aron_conversions.h>
+#include <armarx/navigation/memory/constants.h>
 
-namespace armarx::armem::vision::laser_scanner_features::client
+namespace armarx::navigation::memory::client::laser_scanner_features
 {
 
     Writer::Writer(armem::client::MemoryNameSystem& memoryNameSystem) :
         memoryNameSystem(memoryNameSystem)
     {
     }
-    Writer::~Writer() = default;
 
+    Writer::~Writer() = default;
 
     void
     Writer::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def)
@@ -35,14 +34,14 @@ namespace armarx::armem::vision::laser_scanner_features::client
     Writer::connect()
     {
         // Wait for the memory to become available and add it as dependency.
-        ARMARX_IMPORTANT << "LaserScansWriter: Waiting for memory '" << constants::memoryName
-                         << "' ...";
+        ARMARX_IMPORTANT << "LaserScansWriter: Waiting for memory '"
+                         << constants::NavigationMemoryName << "' ...";
         try
         {
-            memoryWriter =
-                memoryNameSystem.useWriter(MemoryID().withMemoryName(constants::memoryName));
-            ARMARX_IMPORTANT << "MappingDataWriter: Connected to memory '" << constants::memoryName
-                             << "'";
+            memoryWriter = memoryNameSystem.useWriter(
+                armem::MemoryID().withMemoryName(constants::NavigationMemoryName));
+            ARMARX_IMPORTANT << "MappingDataWriter: Connected to memory '"
+                             << constants::NavigationMemoryName << "'";
         }
         catch (const armem::error::CouldNotResolveMemoryServer& e)
         {
@@ -50,13 +49,14 @@ namespace armarx::armem::vision::laser_scanner_features::client
             return;
         }
 
-        ARMARX_IMPORTANT << "LaserScansWriter: Connected to memory '" << constants::memoryName;
+        ARMARX_IMPORTANT << "LaserScansWriter: Connected to memory '"
+                         << constants::NavigationMemoryName;
     }
 
     bool
     Writer::store(const LaserScannerFeatures& features,
                   const std::string& providerName,
-                  const Time& timestamp)
+                  const armem::Time& timestamp)
     {
         std::lock_guard g{memoryWriterMutex};
 
@@ -72,11 +72,11 @@ namespace armarx::armem::vision::laser_scanner_features::client
         // }
 
         const auto entityID = armem::MemoryID()
-                                    .withMemoryName(constants::memoryName)
-                                    .withCoreSegmentName(constants::laserScannerFeaturesCoreSegment)
-                                    .withProviderSegmentName(providerName)
-                                    .withEntityName(features.frame)
-                                    .withTimestamp(timestamp);
+                                  .withMemoryName(constants::NavigationMemoryName)
+                                  .withCoreSegmentName(constants::laserScannerFeaturesCoreSegment)
+                                  .withProviderSegmentName(providerName)
+                                  .withEntityName(features.frame)
+                                  .withTimestamp(timestamp);
 
         ARMARX_VERBOSE << "Memory id is " << entityID.str();
 
@@ -108,4 +108,4 @@ namespace armarx::armem::vision::laser_scanner_features::client
         return updateResult.success;
     }
 
-} // namespace armarx::armem::vision::laser_scanner_features::client
+} // namespace armarx::navigation::memory::client::laser_scanner_features
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
index ca580057..30f42933 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
@@ -26,13 +26,12 @@
 
 #include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h>
 
-#include "RobotAPI/libraries/armem_vision/types.h"
-#include <RobotAPI/interface/units/LaserScannerUnit.h>
 #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
 #include <RobotAPI/libraries/armem/client/Writer.h>
 
+#include <armarx/navigation/memory/types.h>
 
-namespace armarx::armem::vision::laser_scanner_features::client
+namespace armarx::navigation::memory::client::laser_scanner_features
 {
 
     /**
@@ -64,7 +63,7 @@ namespace armarx::armem::vision::laser_scanner_features::client
 
         bool store(const LaserScannerFeatures& features,
                    const std::string& providerName,
-                   const Time& timestamp);
+                   const armem::Time& timestamp);
 
     private:
         armem::client::MemoryNameSystem& memoryNameSystem;
@@ -82,4 +81,4 @@ namespace armarx::armem::vision::laser_scanner_features::client
         const std::string propertyPrefix = "mem.vision.laser_scanner_features.";
     };
 
-} // namespace armarx::armem::vision::laser_scanner_features::client
+} // namespace armarx::navigation::memory::client::laser_scanner_features
diff --git a/source/armarx/navigation/memory/types.h b/source/armarx/navigation/memory/types.h
index fe584e3d..73935f91 100644
--- a/source/armarx/navigation/memory/types.h
+++ b/source/armarx/navigation/memory/types.h
@@ -28,25 +28,9 @@
 #include <RobotAPI/interface/units/LaserScannerUnit.h>
 #include <RobotAPI/libraries/armem/core/Time.h>
 
-namespace armarx::armem::vision
+namespace armarx::navigation::memory
 {
 
-
-    // template<typename _ValueT = float>
-    struct OccupancyGrid
-    {
-        float resolution; // [mm]
-
-        std::string frame;
-        Eigen::Affine3f pose;
-
-        // using ValueType = _ValueT;
-        using CellType = float;
-        using Grid = Eigen::Array<CellType, Eigen::Dynamic, Eigen::Dynamic>;
-
-        Grid grid;
-    };
-
     struct Ellipsoid
     {
         Eigen::Isometry3f pose = Eigen::Isometry3f::Identity();
@@ -88,4 +72,4 @@ namespace armarx::armem::vision
     };
 
 
-} // namespace armarx::armem::vision
+} // namespace armarx::navigation::memory
-- 
GitLab


From 1b3720e0e2e0515db3b02947e1d15c985881aa96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Wed, 8 Mar 2023 06:08:25 +0100
Subject: [PATCH 06/13] Add core segment to navigation memory

---
 .../navigation/components/navigation_memory/Component.cpp   | 6 ++++++
 .../navigation/components/navigation_memory/Component.h     | 2 ++
 source/armarx/navigation/memory/constants.h                 | 2 +-
 3 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/source/armarx/navigation/components/navigation_memory/Component.cpp b/source/armarx/navigation/components/navigation_memory/Component.cpp
index 22f674ca..e7f220c1 100644
--- a/source/armarx/navigation/components/navigation_memory/Component.cpp
+++ b/source/armarx/navigation/components/navigation_memory/Component.cpp
@@ -55,6 +55,7 @@
 #include <armarx/navigation/human/aron/Human.aron.generated.h>
 #include <armarx/navigation/graph/constants.h>
 #include <armarx/navigation/location/constants.h>
+#include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
 #include <armarx/navigation/memory/constants.h>
 #include <armarx/navigation/rooms/aron/Room.aron.generated.h>
 
@@ -153,6 +154,11 @@ namespace armarx::navigation::components::navigation_memory
         workingMemory().addCoreSegment(memory::constants::HumanCoreSegmentName,
                                        navigation::human::arondto::Human::ToAronType());
 
+        workingMemory()
+            .addCoreSegment(memory::constants::LaserScannerFeaturesCoreSegment,
+                            navigation::memory::arondto::LaserScannerFeatures::ToAronType())
+            .setMaxHistorySize(properties.laserScannerFeaturesMaxHistorySize);
+
         // workingMemory().addCoreSegment(memory::constants::HumanGroupCoreSegmentName,
         //                                navigation::human::arondto::Human::ToAronType());
         workingMemory().addCoreSegment(navigation::rooms::coreSegmentID.coreSegmentName,
diff --git a/source/armarx/navigation/components/navigation_memory/Component.h b/source/armarx/navigation/components/navigation_memory/Component.h
index 77220afe..bc359652 100644
--- a/source/armarx/navigation/components/navigation_memory/Component.h
+++ b/source/armarx/navigation/components/navigation_memory/Component.h
@@ -99,6 +99,8 @@ namespace armarx::navigation::components::navigation_memory
         {
             std::string snapshotToLoad = "";
 
+            long laserScannerFeaturesMaxHistorySize = 20;
+
             struct LocationGraph
             {
                 bool visuLocations = true;
diff --git a/source/armarx/navigation/memory/constants.h b/source/armarx/navigation/memory/constants.h
index 56538fb7..0df19cc8 100644
--- a/source/armarx/navigation/memory/constants.h
+++ b/source/armarx/navigation/memory/constants.h
@@ -33,7 +33,7 @@ namespace armarx::navigation::memory::constants
     inline const std::string LocationCoreSegmentName = "Location";
     inline const std::string CostmapCoreSegmentName = "Costmap";
     inline const std::string HumanCoreSegmentName = "Human";
-    inline const std::string laserScannerFeaturesCoreSegment = "LaserScannerFeatures";
+    inline const std::string LaserScannerFeaturesCoreSegment = "LaserScannerFeatures";
 
     inline const std::string GlobalPlannerResultCoreSegment = "Results_GlobalPlanner";
     inline const std::string LocalPlannerResultCoreSegment = "Results_LocalPlanner";
-- 
GitLab


From 45fcec89fbc13fd8f6ed2e8b9d8a9ef1fa51cbae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Wed, 8 Mar 2023 07:09:47 +0100
Subject: [PATCH 07/13] Fix HumanTracker

---
 source/armarx/navigation/human/HumanTracker.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/armarx/navigation/human/HumanTracker.h b/source/armarx/navigation/human/HumanTracker.h
index 1f27aed7..32c25cc4 100644
--- a/source/armarx/navigation/human/HumanTracker.h
+++ b/source/armarx/navigation/human/HumanTracker.h
@@ -24,7 +24,6 @@
 
 #include <ArmarXCore/core/time.h>
 
-#include <RobotAPI/libraries/armem_vision/types.h>
 #include <RobotAPI/libraries/ukfm/UnscentedKalmanFilter.h>
 
 #include <VisionX/libraries/armem_human/types.h>
@@ -33,11 +32,11 @@
 #include <armarx/navigation/human/HumanFilter.h>
 #include <armarx/navigation/human/HumanSystemModel.h>
 #include <armarx/navigation/human/types.h>
+#include <armarx/navigation/memory/types.h>
 
 namespace armarx::navigation::human
 {
-    using Cluster = armem::vision::LaserScannerFeature;
-
+    using Cluster = memory::LaserScannerFeature;
 
     /**
      * @brief The HumanTracker class can be used to track and filter multiple humans. It hides
@@ -115,6 +114,7 @@ namespace armarx::navigation::human
             // the old) velocity should be weighted when calculating the new velocity
             float velocityAlpha = 0.7;
         };
+
         /**
          * @brief HumanTracker::update Updates the tracked humans with the human measurements from a camera. When a
          * measurement is close enough to an existing tracked human, they are associated, otherwise a
-- 
GitLab


From 80f124d1332164d37de9d5166515f942cb9b3a77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Wed, 8 Mar 2023 07:18:01 +0100
Subject: [PATCH 08/13] Fix includes of
 dynamic_distance_to_obstacle_costmap_provider

---
 .../Component.cpp                             | 19 +++----------------
 .../Component.h                               | 15 +++++++--------
 2 files changed, 10 insertions(+), 24 deletions(-)

diff --git a/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.cpp b/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.cpp
index ab0d5810..5bab4b0d 100644
--- a/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.cpp
+++ b/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.cpp
@@ -35,13 +35,12 @@
 #include <RobotAPI/interface/ArViz/Elements.h>
 #include <RobotAPI/libraries/armem_robot/types.h>
 #include <RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h>
-#include <RobotAPI/libraries/armem_vision/client/laser_scanner_features/Reader.h>
 
-#include <armarx/navigation/util/geometry.h>
 #include <armarx/navigation/algorithms/Costmap.h>
 #include <armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h>
 #include <armarx/navigation/conversions/eigen.h>
 #include <armarx/navigation/memory/client/costmap/Reader.h>
+#include <armarx/navigation/util/geometry.h>
 
 // Include headers you only need in function definitions in the .cpp.
 
@@ -55,7 +54,6 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
 
     const std::string Component::defaultName = "dynamic_distance_to_obstacle_costmap_provider";
 
-
     armarx::PropertyDefinitionsPtr
     Component::createPropertyDefinitions()
     {
@@ -97,7 +95,6 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
         return def;
     }
 
-
     Component::Component() :
         robotReader(memoryNameSystem()),
         costmapReader(memoryNameSystem()),
@@ -116,7 +113,6 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
         // setDebugObserverBatchModeEnabled(true);
     }
 
-
     void
     Component::onConnectComponent()
     {
@@ -191,11 +187,8 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
         // return false;
     }
 
-
-
-
     void
-    fillCostmap(const std::vector<armem::vision::LaserScannerFeatures>& features,
+    fillCostmap(const std::vector<memory::LaserScannerFeatures>& features,
                 algorithms::Costmap& costmap)
     {
 
@@ -257,7 +250,6 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
         }
     }
 
-
     void
     Component::drawCostmap(const armarx::navigation::algorithms::Costmap& costmap,
                            const std::string& name,
@@ -325,7 +317,7 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
 
         const auto timestamp = armarx::core::time::Clock::Now();
 
-        const armem::vision::laser_scanner_features::client::Reader::Query query{
+        const memory::client::laser_scanner_features::Reader::Query query{
             .providerName = properties.laserScannerFeatures.providerName,
             .name = properties.laserScannerFeatures.name,
             .timestamp = timestamp};
@@ -405,33 +397,28 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
         return navigationPlanningCostmap;
     }
 
-
     void
     Component::onDisconnectComponent()
     {
     }
 
-
     void
     Component::onExitComponent()
     {
     }
 
-
     std::string
     Component::getDefaultName() const
     {
         return Component::defaultName;
     }
 
-
     std::string
     Component::GetDefaultName()
     {
         return Component::defaultName;
     }
 
-
     ARMARX_REGISTER_COMPONENT_EXECUTABLE(Component, Component::GetDefaultName());
 
 } // namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_provider
diff --git a/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.h b/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.h
index 1837d8ac..92179ba8 100644
--- a/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.h
+++ b/source/armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/Component.h
@@ -23,19 +23,18 @@
 
 #pragma once
 
-#include <ArmarXCore/core/services/tasks/PeriodicTask.h>
 #include <ArmarXCore/core/Component.h>
+#include <ArmarXCore/core/services/tasks/PeriodicTask.h>
 
-#include <RobotAPI/libraries/armem/client/forward_declarations.h>
-#include <RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h>
-#include <RobotAPI/libraries/armem_vision/client/laser_scanner_features/Reader.h>
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
 #include <RobotAPI/libraries/armem/client.h>
+#include <RobotAPI/libraries/armem/client/forward_declarations.h>
+#include <RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h>
 
+#include <armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/ComponentInterface.h>
 #include <armarx/navigation/memory/client/costmap/Reader.h>
 #include <armarx/navigation/memory/client/costmap/Writer.h>
-#include <armarx/navigation/components/dynamic_distance_to_obstacle_costmap_provider/ComponentInterface.h>
-
+#include <armarx/navigation/memory/client/laser_scanner_features/Reader.h>
 
 namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_provider
 {
@@ -114,7 +113,6 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
 
         static const std::string defaultName;
 
-
         // Private member variables go here.
 
 
@@ -141,6 +139,7 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
 
             int updatePeriodMs = 100;
         };
+
         Properties properties;
         /* Use a mutex if you access variables from different threads
          * (e.g. ice functions and RemoteGui_update()).
@@ -172,7 +171,7 @@ namespace armarx::navigation::components::dynamic_distance_to_obstacle_costmap_p
         memory::client::costmap::Reader costmapReader;
         memory::client::costmap::Writer costmapWriter;
 
-        armem::vision::laser_scanner_features::client::Reader laserScannerFeaturesReader;
+        memory::client::laser_scanner_features::Reader laserScannerFeaturesReader;
 
         std::optional<algorithms::Costmap> staticCostmap;
 
-- 
GitLab


From 3d337cf7ca7ab6ff761e983a5ead6e87ff2153f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Wed, 8 Mar 2023 08:00:00 +0100
Subject: [PATCH 09/13] Adjust laser_scanner_feature_extraction for new layout

---
 CMakeLists.txt                                |   4 +
 .../navigation/components/CMakeLists.txt      |   2 +-
 .../dynamic_scene_provider/Component.h        |  11 +-
 .../ArVizDrawer.cpp                           | 162 ++++++++++--------
 .../ArVizDrawer.h                             |   8 +-
 .../CMakeLists.txt                            |   9 +-
 .../Component.cpp                             |  58 ++-----
 .../Component.h                               |  27 +--
 .../EnclosingEllipsoid.cpp                    |   5 +-
 .../EnclosingEllipsoid.h                      |  11 +-
 .../FeatureExtractor.cpp                      |  12 +-
 .../FeatureExtractor.h                        |  15 +-
 .../laser_scanner_feature_extraction/Path.cpp |   8 +-
 .../laser_scanner_feature_extraction/Path.h   |   4 +-
 .../ScanClustering.cpp                        |  33 ++--
 .../ScanClustering.h                          |  14 +-
 .../client/laser_scanner_features/Writer.cpp  |   4 +-
 17 files changed, 190 insertions(+), 197 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 494d9723..a79cf08d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -46,6 +46,10 @@ armarx_find_package(PUBLIC RVO QUIET)
 armarx_find_package(PUBLIC teb_local_planner QUIET)
 armarx_find_package(PUBLIC teb_extension QUIET)
 
+# laser scanner feature extraction
+armarx_find_package(PUBLIC RobotComponents QUIET)
+armarx_find_package(PUBLIC PCL QUIET COMPONENTS io common)
+
 add_subdirectory(etc)
 
 # FetchContent_Declare(
diff --git a/source/armarx/navigation/components/CMakeLists.txt b/source/armarx/navigation/components/CMakeLists.txt
index 651c7ef6..e65951f6 100644
--- a/source/armarx/navigation/components/CMakeLists.txt
+++ b/source/armarx/navigation/components/CMakeLists.txt
@@ -22,4 +22,4 @@ add_subdirectory(human_simulator)
 
 add_subdirectory(navigation_skill_provider)
 
-add_subdirectory(laser_scanner_feature_extraction)
\ No newline at end of file
+add_subdirectory(laser_scanner_feature_extraction)
diff --git a/source/armarx/navigation/components/dynamic_scene_provider/Component.h b/source/armarx/navigation/components/dynamic_scene_provider/Component.h
index 11e0d654..c70b0f66 100644
--- a/source/armarx/navigation/components/dynamic_scene_provider/Component.h
+++ b/source/armarx/navigation/components/dynamic_scene_provider/Component.h
@@ -34,10 +34,11 @@
 #include "RobotAPI/libraries/armem/client/plugins/PluginUser.h"
 #include "RobotAPI/libraries/armem/client/plugins/ReaderWriterPlugin.h"
 #include "RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h"
-#include "RobotAPI/libraries/armem_vision/client/laser_scanner_features/Reader.h"
 
 #include "VisionX/libraries/armem_human/client/HumanPoseReader.h"
 
+#include <armarx/navigation/memory/client/laser_scanner_features/Reader.h>
+
 // #include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
 
 // #include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h>
@@ -47,11 +48,10 @@
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
 
 #include <armarx/navigation/components/dynamic_scene_provider/ArVizDrawer.h>
+#include <armarx/navigation/components/dynamic_scene_provider/ComponentInterface.h>
 #include <armarx/navigation/human/HumanTracker.h>
 #include <armarx/navigation/memory/client/costmap/Reader.h>
 #include <armarx/navigation/memory/client/human/Writer.h>
-#include <armarx/navigation/components/dynamic_scene_provider/ComponentInterface.h>
-
 
 namespace armarx::navigation::components::dynamic_scene_provider
 {
@@ -123,7 +123,6 @@ namespace armarx::navigation::components::dynamic_scene_provider
     private:
         static const std::string defaultName;
 
-
         // Private member variables go here.
 
 
@@ -138,7 +137,6 @@ namespace armarx::navigation::components::dynamic_scene_provider
                 std::string name = ""; // all
             } laserScannerFeatures;
 
-
             struct
             {
                 std::string name = "Armar6";
@@ -155,6 +153,7 @@ namespace armarx::navigation::components::dynamic_scene_provider
 
             std::string humanPoseProvider = "AzureKinectPointCloudProvider";
         };
+
         Properties properties;
         /* Use a mutex if you access variables from different threads
          * (e.g. ice functions and RemoteGui_update()).
@@ -191,7 +190,7 @@ namespace armarx::navigation::components::dynamic_scene_provider
 
         ReaderWriterPlugin<armem::human::client::Reader>* humanPoseReaderPlugin = nullptr;
 
-        ReaderWriterPlugin<armem::vision::laser_scanner_features::client::Reader>*
+        ReaderWriterPlugin<memory::client::laser_scanner_features::Reader>*
             laserScannerFeaturesReaderPlugin = nullptr;
 
         ReaderWriterPlugin<armem::robot_state::VirtualRobotReader>* virtualRobotReaderPlugin =
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
index fe927c0e..3c30fd0f 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
@@ -27,12 +27,13 @@
 #include "conversions/pcl.h"
 #include "conversions/pcl_eigen.h"
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
-    void ArVizDrawer::draw(const std::vector<Features>& features,
-                           const std::string& frame,
-                           const Eigen::Isometry3f& globalSensorPose)
+    void
+    ArVizDrawer::draw(const std::vector<Features>& features,
+                      const std::string& frame,
+                      const Eigen::Isometry3f& globalSensorPose)
     {
         // drawCircles(features, frame, globalSensorPose);
         drawConvexHulls(features, frame, globalSensorPose);
@@ -40,17 +41,19 @@ namespace armarx::laser_scanner_feature_extraction
         drawChains(features, frame, globalSensorPose);
     }
 
-    void ArVizDrawer::draw(const cartographer::LaserScannerMessage& msg,
-                           const Eigen::Isometry3f& globalSensorPose, const simox::Color& color)
+    void
+    ArVizDrawer::draw(const cartographer::LaserScannerMessage& msg,
+                      const Eigen::Isometry3f& globalSensorPose,
+                      const simox::Color& color)
     {
         auto layer = arviz.layer("points_" + msg.frame);
 
         const auto pointCloud = conversions::eigen2pcl(toCartesian<Eigen::Vector3f>(msg.scan));
 
         layer.add(viz::PointCloud("points_" + std::to_string(layer.size()))
-                  .pointCloud(pointCloud, viz::Color(color))
-                  .pointSizeInPixels(5)
-                  .pose(globalSensorPose));
+                      .pointCloud(pointCloud, viz::Color(color))
+                      .pointSizeInPixels(5)
+                      .pose(globalSensorPose));
         arviz.commit(layer);
     }
 
@@ -77,9 +80,9 @@ namespace armarx::laser_scanner_feature_extraction
             globalSensorPose * Eigen::Vector3f(circle.center.x(), circle.center.y(), -1.F);
 
         layer.add(viz::Ellipsoid("circle_" + std::to_string(layer.size()))
-                  .axisLengths(Eigen::Vector3f{circle.radius, circle.radius, 0.F})
-                  .position(position)
-                  .color(simox::Color::red(200, 100)));
+                      .axisLengths(Eigen::Vector3f{circle.radius, circle.radius, 0.F})
+                      .position(position)
+                      .color(simox::Color::red(200, 100)));
     }
 
     void
@@ -104,29 +107,33 @@ namespace armarx::laser_scanner_feature_extraction
         arviz.commit(layer);
     }
 
-    void ArVizDrawer::drawConvexHulls(const std::vector<Features>& features,
-                                      const std::string& frame,
-                                      const Eigen::Isometry3f& globalSensorPose)
+    void
+    ArVizDrawer::drawConvexHulls(const std::vector<Features>& features,
+                                 const std::string& frame,
+                                 const Eigen::Isometry3f& globalSensorPose)
     {
         auto layer = arviz.layer("convex_hulls_" + frame);
 
         std::for_each(features.begin(),
                       features.end(),
-                      [&](const Features & f)
-        {
-            if (not f.convexHull)
-            {
-                return;
-            }
-            drawConvexHull(layer, *f.convexHull, globalSensorPose, simox::Color::red(100, 80));
-        });
+                      [&](const Features& f)
+                      {
+                          if (not f.convexHull)
+                          {
+                              return;
+                          }
+                          drawConvexHull(
+                              layer, *f.convexHull, globalSensorPose, simox::Color::red(100, 80));
+                      });
 
         arviz.commit(layer);
     }
 
-    void ArVizDrawer::draw(const std::string& layerName, const VirtualRobot::MathTools::ConvexHull2D& robotHull,
-                           const Eigen::Isometry3f& robotGlobalPose,
-                           const simox::Color& color)
+    void
+    ArVizDrawer::draw(const std::string& layerName,
+                      const VirtualRobot::MathTools::ConvexHull2D& robotHull,
+                      const Eigen::Isometry3f& robotGlobalPose,
+                      const simox::Color& color)
     {
         auto layer = arviz.layer(layerName);
 
@@ -134,81 +141,86 @@ namespace armarx::laser_scanner_feature_extraction
         arviz.commit(layer);
     }
 
-    void ArVizDrawer::drawConvexHull(viz::Layer& layer,
-                                     const VirtualRobot::MathTools::ConvexHull2D& hull,
-                                     const Eigen::Isometry3f& globalSensorPose,
-                                     const simox::Color& color)
+    void
+    ArVizDrawer::drawConvexHull(viz::Layer& layer,
+                                const VirtualRobot::MathTools::ConvexHull2D& hull,
+                                const Eigen::Isometry3f& globalSensorPose,
+                                const simox::Color& color)
     {
         const auto points = conversions::to3D(hull.vertices);
 
         layer.add(viz::Polygon("convex_hull_" + std::to_string(layer.size()))
-                  .points(points)
-                  .color(color)
-                  .pose(globalSensorPose));
+                      .points(points)
+                      .color(color)
+                      .pose(globalSensorPose));
     }
 
-    void ArVizDrawer::drawEllipsoids(const std::vector<Features>& features,
-                                     const std::string& frame,
-                                     const Eigen::Isometry3f& globalSensorPose)
+    void
+    ArVizDrawer::drawEllipsoids(const std::vector<Features>& features,
+                                const std::string& frame,
+                                const Eigen::Isometry3f& globalSensorPose)
     {
         auto layer = arviz.layer("ellipsoids_" + frame);
 
         std::for_each(features.begin(),
                       features.end(),
-                      [&](const Features & f)
-        {
-            if (not f.ellipsoid)
-            {
-                return;
-            }
-            drawEllipsoid(layer, *f.ellipsoid, globalSensorPose);
-        });
+                      [&](const Features& f)
+                      {
+                          if (not f.ellipsoid)
+                          {
+                              return;
+                          }
+                          drawEllipsoid(layer, *f.ellipsoid, globalSensorPose);
+                      });
 
         arviz.commit(layer);
     }
 
-    void ArVizDrawer::drawEllipsoid(viz::Layer& layer,
-                                    const Ellipsoid& ellipsoid,
-                                    const Eigen::Isometry3f& globalSensorPose)
+    void
+    ArVizDrawer::drawEllipsoid(viz::Layer& layer,
+                               const Ellipsoid& ellipsoid,
+                               const Eigen::Isometry3f& globalSensorPose)
     {
 
         const Eigen::Isometry3f pose = globalSensorPose * ellipsoid.pose;
 
         layer.add(viz::Ellipsoid("ellipsoid_" + std::to_string(layer.size()))
-                  .axisLengths(conversions::to3D(ellipsoid.radii))
-                  .pose(pose)
-                  .color(simox::Color(255, 102, 0, 128))); // orange, but a bit more shiny
+                      .axisLengths(conversions::to3D(ellipsoid.radii))
+                      .pose(pose)
+                      .color(simox::Color(255, 102, 0, 128))); // orange, but a bit more shiny
     }
 
-    void ArVizDrawer::drawChains(const std::vector<Features>& features,
-                                 const std::string& frame,
-                                 const Eigen::Isometry3f& globalSensorPose)
+    void
+    ArVizDrawer::drawChains(const std::vector<Features>& features,
+                            const std::string& frame,
+                            const Eigen::Isometry3f& globalSensorPose)
     {
         auto layer = arviz.layer("chains_" + frame);
 
         std::for_each(features.begin(),
                       features.end(),
-                      [&](const Features & f)
-        {
-            if (not f.chain)
-            {
-                return;
-            }
-            drawChain(layer, *f.chain, globalSensorPose);
-
-            // const auto ellipsoids = f.linesAsEllipsoids(50);
-            // for (const auto& ellipsoid : ellipsoids)
-            // {
-            //     drawEllipsoid(layer, ellipsoid, globalSensorPose);
-            // }
-        });
+                      [&](const Features& f)
+                      {
+                          if (not f.chain)
+                          {
+                              return;
+                          }
+                          drawChain(layer, *f.chain, globalSensorPose);
+
+                          // const auto ellipsoids = f.linesAsEllipsoids(50);
+                          // for (const auto& ellipsoid : ellipsoids)
+                          // {
+                          //     drawEllipsoid(layer, ellipsoid, globalSensorPose);
+                          // }
+                      });
 
         arviz.commit(layer);
     }
 
-    void ArVizDrawer::drawChain(viz::Layer& layer,
-                                const Points& chain,
-                                const Eigen::Isometry3f& globalSensorPose)
+    void
+    ArVizDrawer::drawChain(viz::Layer& layer,
+                           const Points& chain,
+                           const Eigen::Isometry3f& globalSensorPose)
     {
         if (chain.size() < 2)
         {
@@ -218,9 +230,9 @@ namespace armarx::laser_scanner_feature_extraction
         const auto cloud = conversions::to3D(chain);
 
         layer.add(viz::Path("chain_" + std::to_string(layer.size()))
-                  .points(cloud)
-                  .width(7.F)
-                  .color(viz::Color::blue())
-                  .pose(globalSensorPose));
+                      .points(cloud)
+                      .width(7.F)
+                      .color(viz::Color::blue())
+                      .pose(globalSensorPose));
     }
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
index 06dc0f0d..ce0f1abb 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
@@ -36,14 +36,15 @@ namespace armarx::cartographer
     struct LaserScannerMessage;
 }
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
     struct LineSegment2Df;
+
     // struct Circle;
     // struct Ellipsoid;
 
-    class ArVizDrawer 
+    class ArVizDrawer
     {
     public:
         using Points = std::vector<Eigen::Vector2f>;
@@ -52,7 +53,6 @@ namespace armarx::laser_scanner_feature_extraction
         {
         }
 
-
         void draw(const std::vector<Features>& features,
                   const std::string& frame,
                   const Eigen::Isometry3f& globalSensorPose);
@@ -106,4 +106,4 @@ namespace armarx::laser_scanner_feature_extraction
 
         armarx::viz::Client arviz;
     };
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
index 8e5de1c1..aae16e62 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
@@ -31,12 +31,17 @@ armarx_add_component(laser_scanner_feature_extraction
         # ArmarXCore
         ArmarXCore
         ArmarXCoreComponentPlugins  # For DebugObserver plugin.
+        ArmarXCoreInterfaces
+        ArmarXCoreLogging
         # ArmarXGui
         ## ArmarXGuiComponentPlugins  # For RemoteGui plugin.
         # RobotAPI
         ## RobotAPICore
         ## RobotAPIInterfaces
         RobotAPIComponentPlugins  # For ArViz and other plugins.
-        armem_vision
-        RobotComponents::Cartographer
+        #armem_vision
+        #RobotComponents::Cartographer
+        armarx_navigation::memory
+    DEPENDENCIES_LEGACY
+        PCL
 )
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
index e1b38a2d..5ac47033 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
@@ -40,17 +40,15 @@
 #include "ArmarXCore/core/time/DateTime.h"
 #include "ArmarXCore/core/time/Duration.h"
 
-#include "RobotAPI/libraries/armem_vision/types.h"
 #include "RobotAPI/libraries/core/remoterobot/RemoteRobot.h"
 
-#include "armarx/navigation/components/laser_scanner_feature_extraction/geometry.h"
-
 #include "ArVizDrawer.h"
 #include "FeatureExtractor.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/geometry.h"
 #include "conversions/eigen.h"
 #include "conversions/pcl_eigen.h"
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
     armarx::PropertyDefinitionsPtr
@@ -59,12 +57,6 @@ namespace armarx::laser_scanner_feature_extraction
         armarx::PropertyDefinitionsPtr def =
             new ComponentPropertyDefinitions(getConfigIdentifier());
 
-        // Publish to a topic (passing the TopicListenerPrx).
-        def->topic(featuresTopic);
-
-        // Subscribe to a topic (passing the topic name).
-        def->topic<LaserScannerUnitListener>("LaserScans");
-
         // // Add an optionalproperty.
         def->optional(properties.queueSize,
                       "p.inputQueueSize",
@@ -234,10 +226,10 @@ namespace armarx::laser_scanner_feature_extraction
         laserScannerFeatureWriter.connect();
     }
 
-    armem::vision::LaserScannerFeature
+    memory::LaserScannerFeature
     toArmemFeature(const Features& features)
     {
-        armem::vision::LaserScannerFeature armemFeature;
+        memory::LaserScannerFeature armemFeature;
 
         if (features.chain)
         {
@@ -264,12 +256,12 @@ namespace armarx::laser_scanner_feature_extraction
         return armemFeature;
     }
 
-    armem::vision::LaserScannerFeatures
+    memory::LaserScannerFeatures
     toArmemFeatures(const std::vector<Features>& features,
                     const Eigen::Isometry3f& global_T_sensor,
                     const std::string& sensorFrame)
     {
-        armem::vision::LaserScannerFeatures armemFeatures;
+        memory::LaserScannerFeatures armemFeatures;
         armemFeatures.frame = sensorFrame;
         armemFeatures.frameGlobalPose = global_T_sensor;
 
@@ -479,31 +471,12 @@ namespace armarx::laser_scanner_feature_extraction
     }
 
     void
-    LaserScannerFeatureExtraction::publishFeatures(
-        const armem::vision::LaserScannerFeatures& features,
-        const armem::Time& timestamp)
+    LaserScannerFeatureExtraction::publishFeatures(const memory::LaserScannerFeatures& features,
+                                                   const armem::Time& timestamp)
     {
 
         // store in memory
         laserScannerFeatureWriter.store(features, getName(), timestamp);
-
-        // legacy - topic
-        LineSegment2DChainSeq chains;
-        for (const auto& feature : features.features)
-        {
-            if (not feature.chain.empty())
-            {
-                LineSegment2DChain chain;
-                for (const auto& pt : feature.chain)
-                {
-                    chain.push_back(
-                        conversions::to2D(features.frameGlobalPose * conversions::to3D(pt)));
-                }
-                chains.push_back(chain);
-            }
-        }
-
-        featuresTopic->reportExtractedLineSegments(chains);
     }
 
     void
@@ -523,14 +496,14 @@ namespace armarx::laser_scanner_feature_extraction
     }
 
     void
-    LaserScannerFeatureExtraction::reportSensorValues(const std::string& device,
-                                                      const std::string& name,
-                                                      const LaserScan& scan,
-                                                      const TimestampBasePtr& timestamp,
-                                                      const Ice::Current& /*unused*/)
+    reportSensorValues(const std::string& device,
+                       const std::string& name,
+                       const LaserScan& scan,
+                       const TimestampBasePtr& timestamp,
+                       const Ice::Current& /*unused*/)
     {
         ARMARX_DEBUG << "Receiving data";
-
+        /*
         laserMessageQueue.push(cartographer::LaserScannerMessage{
             .frame = name, .scan = scan, .timestamp = timestamp->timestamp});
 
@@ -539,6 +512,7 @@ namespace armarx::laser_scanner_feature_extraction
         frequencyReporterSubscribe->add(IceUtil::Time::now().toMicroSeconds());
 
         setDebugObserverDatafield(name, scan.size());
+*/
     }
 
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
index e02f8972..664a8dbb 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
@@ -32,21 +32,20 @@
 #include "RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h"
 #include "RobotAPI/libraries/armem/client/forward_declarations.h"
 #include "RobotAPI/libraries/armem/client/plugins/PluginUser.h"
-#include <RobotAPI/interface/units/LaserScannerUnit.h>
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h>
-#include <RobotAPI/libraries/armem_vision/client/laser_scanner_features/Writer.h>
 
-#include "armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h"
-#include "armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h"
-#include "armarx/navigation/components/laser_scanner_feature_extraction/geometry.h"
 #include <RobotComponents/libraries/cartographer/MessageQueue.h>
 #include <RobotComponents/libraries/cartographer/types.h>
 
 #include "ArVizDrawer.h"
 #include "FeatureExtractor.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/geometry.h"
+#include <armarx/navigation/memory/client/laser_scanner_features/Writer.h>
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
     // class FeatureExtractor;
@@ -69,7 +68,6 @@ namespace armarx::laser_scanner_feature_extraction
         // , virtual public armarx::LightweightRemoteGuiComponentPluginUser
         ,
         virtual public armarx::ArVizComponentPluginUser,
-        virtual public LaserScannerUnitListener,
         virtual public RobotStateComponentPluginUser,
         virtual public armem::ClientPluginUser
 
@@ -107,18 +105,11 @@ namespace armarx::laser_scanner_feature_extraction
         void RemoteGui_update() override;
         */
 
-        // LaserScannerUnitListener interface
-        void reportSensorValues(const std::string& device,
-                                const std::string& name,
-                                const LaserScan& scan,
-                                const TimestampBasePtr& timestamp,
-                                const Ice::Current& /*unused*/) override;
-
     private:
         void onFeatures(const cartographer::LaserScannerMessage& data,
                         const std::vector<Features>& features);
 
-        void publishFeatures(const armem::vision::LaserScannerFeatures& features,
+        void publishFeatures(const memory::LaserScannerFeatures& features,
                              const armem::Time& timestamp);
 
         // Private methods go here.
@@ -215,13 +206,11 @@ namespace armarx::laser_scanner_feature_extraction
 
         VirtualRobot::RobotPtr robot;
 
-        LaserScannerFeaturesListenerPrx featuresTopic;
-
         std::variant<std::monostate, VirtualRobot::MathTools::ConvexHull2DPtr, Eigen::Vector2f>
             robotHull;
         VirtualRobot::MathTools::ConvexHull2DPtr cableArea;
 
 
-        armem::vision::laser_scanner_features::client::Writer laserScannerFeatureWriter;
+        memory::client::laser_scanner_features::Writer laserScannerFeatureWriter;
     };
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp
index 87bf8313..eea10eeb 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.cpp
@@ -13,8 +13,7 @@
 #include "ArmarXCore/core/exceptions/local/ExpressionException.h"
 #include "ArmarXCore/core/logging/Logging.h"
 
-
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
     // Eigen::Affine2f Ellipsoid::pose() const noexcept
@@ -121,4 +120,4 @@ namespace armarx::laser_scanner_feature_extraction
         return true;
     }
 
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h
index 9628314b..277531e7 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h
@@ -23,9 +23,10 @@
 
 #include <Eigen/Core>
 #include <Eigen/Geometry>
-#include "RobotAPI/libraries/armem_vision/types.h"
 
-namespace armarx::laser_scanner_feature_extraction
+#include <armarx/navigation/memory/types.h>
+
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
     // struct Ellipsoid
@@ -38,7 +39,7 @@ namespace armarx::laser_scanner_feature_extraction
     //     Eigen::Affine2f pose() const noexcept;
     // };
 
-    using Ellipsoid = armem::vision::Ellipsoid;
+    using Ellipsoid = memory::Ellipsoid;
 
     /**
      * @brief Minimum volume enclosing ellipsoid (MVEE) for a set of points.
@@ -49,7 +50,7 @@ namespace armarx::laser_scanner_feature_extraction
     class EnclosingEllipsoid : public Ellipsoid
     {
     public:
-        using Point  = Eigen::Vector2f;
+        using Point = Eigen::Vector2f;
         using Points = std::vector<Point>;
 
         EnclosingEllipsoid(const Points& points);
@@ -67,4 +68,4 @@ namespace armarx::laser_scanner_feature_extraction
         bool compute(const Points& points);
     };
 
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
index 96cdfac5..588cd302 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
@@ -24,9 +24,6 @@
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
 #include <ArmarXCore/core/logging/Logging.h>
 
-#include <armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h>
-#include <armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h>
-#include <armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h>
 #include <RobotComponents/libraries/cartographer/util/laser_scanner_conversion.h>
 
 #include "Path.h"
@@ -35,9 +32,11 @@
 #include "conversions/opencv_eigen.h"
 #include "conversions/opencv_pcl.h"
 #include "conversions/pcl_eigen.h"
+#include <armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h>
+#include <armarx/navigation/components/laser_scanner_feature_extraction/EnclosingEllipsoid.h>
+#include <armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h>
 
-
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
     // Features
@@ -126,7 +125,6 @@ namespace armarx::laser_scanner_feature_extraction
         return clustering.detectClusters(scan);
     }
 
-
     std::optional<VirtualRobot::MathTools::ConvexHull2D>
     FeatureExtractor::convexHull(const Points& points) const
     {
@@ -236,4 +234,4 @@ namespace armarx::laser_scanner_feature_extraction
         return chApprx.approximatedChain();
     }
 
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
index f581c8e3..d2fa134f 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
@@ -26,23 +26,22 @@
 #include <pcl/point_types.h>
 
 #include <VirtualRobot/MathTools.h>
-#include "RobotAPI/libraries/armem_vision/types.h"
 
-#include "armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h"
 #include "RobotComponents/libraries/cartographer/types.h"
 #include <RobotComponents/interface/components/GraspingManager/RobotPlacementInterface.h>
 
 #include "EnclosingEllipsoid.h"
 #include "ScanClustering.h"
+#include "armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h"
+#include <armarx/navigation/memory/types.h>
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
-    using Circle = armem::vision::Circle;
-   
+    using Circle = memory::Circle;
 
     struct Features
     {
-        using Points   = std::vector<Eigen::Vector2f>;
+        using Points = std::vector<Eigen::Vector2f>;
         using Chain = Points;
 
         std::optional<VirtualRobot::MathTools::ConvexHull2D> convexHull;
@@ -59,7 +58,7 @@ namespace armarx::laser_scanner_feature_extraction
     class FeatureExtractor
     {
     public:
-        using Points   = std::vector<Eigen::Vector2f>;
+        using Points = std::vector<Eigen::Vector2f>;
         using Callback = std::function<void(const cartographer::LaserScannerMessage& data,
                                             const std::vector<Features>& features)>;
 
@@ -88,4 +87,4 @@ namespace armarx::laser_scanner_feature_extraction
 
         const Callback callback;
     };
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp
index 057ada4f..d57cae5c 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.cpp
@@ -1,10 +1,10 @@
 #include "Path.h"
 
-
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
-    std::vector<Path::Segment> Path::segments() const noexcept
+    std::vector<Path::Segment>
+    Path::segments() const noexcept
     {
         if (points.size() <= 1)
         {
@@ -21,4 +21,4 @@ namespace armarx::laser_scanner_feature_extraction
 
         return segments;
     }
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h
index 8f63e260..2cf3a413 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Path.h
@@ -4,7 +4,7 @@
 
 #include <Eigen/Core>
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
     struct Path
@@ -16,4 +16,4 @@ namespace armarx::laser_scanner_feature_extraction
         std::vector<Segment> segments() const noexcept;
     };
 
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp
index e0376c54..32503968 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.cpp
@@ -2,11 +2,14 @@
 
 #include <RobotAPI/interface/units/LaserScannerUnit.h>
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
-    ScanClustering::ScanClustering(const Params& params) : params(params) {}
+    ScanClustering::ScanClustering(const Params& params) : params(params)
+    {
+    }
 
-    bool ScanClustering::add(const LaserScanStep& scanStep)
+    bool
+    ScanClustering::add(const LaserScanStep& scanStep)
     {
         if (scan.empty())
         {
@@ -23,7 +26,8 @@ namespace armarx::laser_scanner_feature_extraction
         return true;
     }
 
-    std::vector<LaserScan> ScanClustering::detectClusters(const LaserScan& scan)
+    std::vector<LaserScan>
+    ScanClustering::detectClusters(const LaserScan& scan)
     {
         const auto isInvalid = [&](const LaserScanStep& step) -> bool
         { return step.distance > params.maxDistance; };
@@ -48,11 +52,20 @@ namespace armarx::laser_scanner_feature_extraction
         return clusters;
     }
 
-    const LaserScan& ScanClustering::cluster() const { return scan; }
+    const LaserScan&
+    ScanClustering::cluster() const
+    {
+        return scan;
+    }
 
-    void ScanClustering::clear() { scan.clear(); }
+    void
+    ScanClustering::clear()
+    {
+        scan.clear();
+    }
 
-    bool ScanClustering::supports(const LaserScanStep& scanStep)
+    bool
+    ScanClustering::supports(const LaserScanStep& scanStep)
     {
         // OK to create a new cluster if it's empty
         if (scan.empty())
@@ -60,12 +73,12 @@ namespace armarx::laser_scanner_feature_extraction
             return true;
         }
 
-        const float distanceDiff         = std::fabs(scanStep.distance - scan.back().distance);
+        const float distanceDiff = std::fabs(scanStep.distance - scan.back().distance);
         const bool isWithinDistanceRange = distanceDiff < params.distanceThreshold;
 
-        const float angleDiff         = std::fabs(scanStep.angle - scan.back().angle);
+        const float angleDiff = std::fabs(scanStep.angle - scan.back().angle);
         const bool isWithinAngleRange = angleDiff < params.angleThreshold;
 
         return (isWithinDistanceRange and isWithinAngleRange);
     }
-} // namespace armarx::laser_scanner_feature_extraction
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h
index 6cee501d..1159c2c6 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ScanClustering.h
@@ -23,7 +23,7 @@
 
 #include <RobotAPI/interface/units/LaserScannerUnit.h>
 
-namespace armarx::laser_scanner_feature_extraction
+namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
     namespace detail
     {
@@ -31,7 +31,7 @@ namespace armarx::laser_scanner_feature_extraction
         {
             // Clustering options to decide whether a point belongs to the cluster
             float distanceThreshold; // [mm]
-            float angleThreshold;    // [rad]
+            float angleThreshold; // [rad]
 
             /// Range filter: only consider points that are closer than maxDistance
             float maxDistance; // [mm]
@@ -40,8 +40,8 @@ namespace armarx::laser_scanner_feature_extraction
 
     class ScanClustering
     {
-      public:
-        using Params   = detail::ScanClusteringParams;
+    public:
+        using Params = detail::ScanClusteringParams;
         using Clusters = std::vector<LaserScan>;
 
         ScanClustering(const Params& params);
@@ -54,7 +54,7 @@ namespace armarx::laser_scanner_feature_extraction
         */
         Clusters detectClusters(const LaserScan& scan);
 
-      protected:
+    protected:
         const LaserScan& cluster() const;
 
         bool add(const LaserScanStep& scanStep);
@@ -62,10 +62,10 @@ namespace armarx::laser_scanner_feature_extraction
 
         void clear();
 
-      private:
+    private:
         LaserScan scan;
 
         const Params params;
     };
 
-} // namespace armarx::laser_scanner_feature_extraction
\ No newline at end of file
+} // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
index 5e453ea7..e2cb6739 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
@@ -61,7 +61,7 @@ namespace armarx::navigation::memory::client::laser_scanner_features
         std::lock_guard g{memoryWriterMutex};
 
         // const auto result = memoryWriter.addSegment(constants::memoryName,
-        //                                             constants::laserScannerFeaturesCoreSegment);
+        //                                             constants::LaserScannerFeaturesCoreSegment);
 
         // if (not result.success)
         // {
@@ -73,7 +73,7 @@ namespace armarx::navigation::memory::client::laser_scanner_features
 
         const auto entityID = armem::MemoryID()
                                   .withMemoryName(constants::NavigationMemoryName)
-                                  .withCoreSegmentName(constants::laserScannerFeaturesCoreSegment)
+                                  .withCoreSegmentName(constants::LaserScannerFeaturesCoreSegment)
                                   .withProviderSegmentName(providerName)
                                   .withEntityName(features.frame)
                                   .withTimestamp(timestamp);
-- 
GitLab


From 6fb5d67b834ba373e6d42411712bc762c1a67cdf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Wed, 8 Mar 2023 09:03:43 +0100
Subject: [PATCH 10/13] Rework feature extractor to work with memory instead of
 topic

Read the laser scan data from the memory and run the extraction in a
periodic task instead of receiving from the legacy topic.
---
 .../ArVizDrawer.cpp                           |  7 +-
 .../ArVizDrawer.h                             | 12 +--
 .../CMakeLists.txt                            |  4 +-
 .../Component.cpp                             | 73 +++++++++++--------
 .../Component.h                               | 20 +++--
 .../FeatureExtractor.cpp                      |  4 +-
 .../FeatureExtractor.h                        |  7 +-
 7 files changed, 66 insertions(+), 61 deletions(-)

diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
index 3c30fd0f..ad8b55c8 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.cpp
@@ -19,7 +19,6 @@
 #include "RobotAPI/components/ArViz/Client/elements/PointCloud.h"
 #include "RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/StyleSheets.h"
 
-#include "RobotComponents/libraries/cartographer/types.h"
 #include "RobotComponents/libraries/cartographer/util/laser_scanner_conversion.h"
 
 #include "FeatureExtractor.h"
@@ -42,13 +41,13 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
     }
 
     void
-    ArVizDrawer::draw(const cartographer::LaserScannerMessage& msg,
+    ArVizDrawer::draw(const armem::laser_scans::LaserScanStamped& msg,
                       const Eigen::Isometry3f& globalSensorPose,
                       const simox::Color& color)
     {
-        auto layer = arviz.layer("points_" + msg.frame);
+        auto layer = arviz.layer("points_" + msg.header.frame);
 
-        const auto pointCloud = conversions::eigen2pcl(toCartesian<Eigen::Vector3f>(msg.scan));
+        const auto pointCloud = conversions::eigen2pcl(toCartesian<Eigen::Vector3f>(msg.data));
 
         layer.add(viz::PointCloud("points_" + std::to_string(layer.size()))
                       .pointCloud(pointCloud, viz::Color(color))
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
index ce0f1abb..f1c51a4c 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/ArVizDrawer.h
@@ -31,19 +31,9 @@
 
 #include "armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h"
 
-namespace armarx::cartographer
-{
-    struct LaserScannerMessage;
-}
-
 namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
-    struct LineSegment2Df;
-
-    // struct Circle;
-    // struct Ellipsoid;
-
     class ArVizDrawer
     {
     public:
@@ -57,7 +47,7 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
                   const std::string& frame,
                   const Eigen::Isometry3f& globalSensorPose);
 
-        void draw(const cartographer::LaserScannerMessage& msg,
+        void draw(const armem::laser_scans::LaserScanStamped& msg,
                   const Eigen::Isometry3f& globalSensorPose,
                   const simox::Color& color);
 
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
index aae16e62..bc399a2c 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
@@ -21,6 +21,7 @@ armarx_add_component(laser_scanner_feature_extraction
         ChainApproximation.h
         EnclosingEllipsoid.h
         Path.h
+        geometry.h
         #conversions
         conversions/eigen.h
         conversions/pcl_eigen.h
@@ -39,9 +40,8 @@ armarx_add_component(laser_scanner_feature_extraction
         ## RobotAPICore
         ## RobotAPIInterfaces
         RobotAPIComponentPlugins  # For ArViz and other plugins.
-        #armem_vision
-        #RobotComponents::Cartographer
         armarx_navigation::memory
+        armem_laser_scans
     DEPENDENCIES_LEGACY
         PCL
 )
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
index 5ac47033..d05e42d5 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
@@ -58,9 +58,8 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
             new ComponentPropertyDefinitions(getConfigIdentifier());
 
         // // Add an optionalproperty.
-        def->optional(properties.queueSize,
-                      "p.inputQueueSize",
-                      "Size of the message queue (max). Should not be too large to avoid delays.");
+        def->optional(
+            properties.taskPeriodMs, "p.taskPeriodMs", "Update rate of the running task.");
 
         def->optional(properties.robotHull.shape, "p.robotHull.shape", "Shape of the robot area.")
             .map({std::make_pair("Rectangle", Properties::RobotHull::RECTANGLE),
@@ -113,6 +112,7 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
     LaserScannerFeatureExtraction::LaserScannerFeatureExtraction() :
         laserScannerFeatureWriter(memoryNameSystem())
     {
+        addPlugin(laserScannerReaderPlugin);
     }
 
     void
@@ -142,10 +142,6 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
             properties.chainApproximationParams,
             [&](auto&&... args) { onFeatures(std::forward<decltype(args)>(args)...); });
 
-        laserMessageQueue.setQueueSize(properties.queueSize);
-        laserMessageQueue.connect([&](const auto& sharedArg)
-                                  { featureExtractor->onData(sharedArg); });
-
         ARMARX_INFO << "Connected";
 
         robot = RemoteRobot::createLocalClone(getRobotStateComponent());
@@ -224,6 +220,14 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
 
 
         laserScannerFeatureWriter.connect();
+
+        task = new PeriodicTask<LaserScannerFeatureExtraction>(
+            this,
+            &LaserScannerFeatureExtraction::runPeriodically,
+            properties.taskPeriodMs,
+            false,
+            "runningTask");
+        task->start();
     }
 
     memory::LaserScannerFeature
@@ -306,18 +310,19 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
     }
 
     void
-    LaserScannerFeatureExtraction::onFeatures(const cartographer::LaserScannerMessage& data,
+    LaserScannerFeatureExtraction::onFeatures(const armem::laser_scans::LaserScanStamped& data,
                                               const std::vector<Features>& featuresFromExtractor)
     {
         ARMARX_DEBUG << "Publishing data";
 
         // obtain sensor pose
         RemoteRobot::synchronizeLocalCloneToTimestamp(
-            robot, getRobotStateComponent(), data.timestamp);
-        const Eigen::Isometry3f global_T_sensor(robot->getRobotNode(data.frame)->getGlobalPose());
+            robot, getRobotStateComponent(), data.header.timestamp.toMicroSecondsSinceEpoch());
+        const Eigen::Isometry3f global_T_sensor(
+            robot->getRobotNode(data.header.frame)->getGlobalPose());
         const Eigen::Isometry3f global_T_robot(robot->getRootNode()->getGlobalPose());
         const Eigen::Isometry3f robot_T_sensor(
-            robot->getRobotNode(data.frame)->getPoseInRootFrame());
+            robot->getRobotNode(data.header.frame)->getPoseInRootFrame());
 
         //Eigen::AlignedBox2f box;
         //box.extend(pt1).extend(pt2).extend(pt3).extend(pt4);
@@ -407,30 +412,29 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         const auto validFeatures = removeInvalidFeatures(features);
         ARMARX_VERBOSE << validFeatures.size() << " valid features without cable region";
 
-        const auto armemFeatures = toArmemFeatures(validFeatures, global_T_sensor, data.frame);
+        const auto armemFeatures =
+            toArmemFeatures(validFeatures, global_T_sensor, data.header.frame);
 
         ARMARX_VERBOSE << "Reporting " << armemFeatures.features.size() << " features";
 
         // report the features
-        publishFeatures(armemFeatures,
-                        armarx::core::time::DateTime(
-                            armarx::core::time::Duration::MicroSeconds(data.timestamp)));
+        publishFeatures(armemFeatures, data.header.timestamp);
 
         // check if arviz should be triggered
         const auto getOrCreateThrottler = [&]() -> Throttler&
         {
-            const auto it = throttlers.find(data.frame);
+            const auto it = throttlers.find(data.header.frame);
             if (it == throttlers.end())
             {
-                throttlers.emplace(data.frame, Throttler(10.F));
+                throttlers.emplace(data.header.frame, Throttler(10.F));
             }
 
-            return throttlers.at(data.frame);
+            return throttlers.at(data.header.frame);
         };
 
-        if (getOrCreateThrottler().check(data.timestamp))
+        if (getOrCreateThrottler().check(data.header.timestamp))
         {
-            arVizDrawer->draw(features, data.frame, global_T_sensor);
+            arVizDrawer->draw(features, data.header.frame, global_T_sensor);
 
             if (std::holds_alternative<VirtualRobot::MathTools::ConvexHull2DPtr>(robotHull))
             {
@@ -482,6 +486,7 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
     void
     LaserScannerFeatureExtraction::onDisconnectComponent()
     {
+        task->stop();
     }
 
     void
@@ -496,23 +501,27 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
     }
 
     void
-    reportSensorValues(const std::string& device,
-                       const std::string& name,
-                       const LaserScan& scan,
-                       const TimestampBasePtr& timestamp,
-                       const Ice::Current& /*unused*/)
+    LaserScannerFeatureExtraction::runPeriodically()
     {
-        ARMARX_DEBUG << "Receiving data";
-        /*
-        laserMessageQueue.push(cartographer::LaserScannerMessage{
-            .frame = name, .scan = scan, .timestamp = timestamp->timestamp});
+        const armem::laser_scans::client::Reader::Query laserScanQuery{
+            .agent = robot->getName(), .timeRange = std::nullopt, .sensorList = {}};
 
-        ARMARX_DEBUG << "Queue size" << laserMessageQueue.size();
+        const armem::laser_scans::client::Reader::Result laserScanResult =
+            laserScannerReaderPlugin->get().queryData(laserScanQuery);
+        ARMARX_CHECK_EQUAL(laserScanResult.status,
+                           armem::laser_scans::client::Reader::Result::Error)
+            << laserScanResult.errorMessage;
+
+        ARMARX_DEBUG << "Received laser scan data from " << laserScanResult.laserScans.size()
+                     << " sensors";
 
         frequencyReporterSubscribe->add(IceUtil::Time::now().toMicroSeconds());
 
-        setDebugObserverDatafield(name, scan.size());
-*/
+        for (const auto& scan : laserScanResult.laserScans)
+        {
+            setDebugObserverDatafield(getDefaultName() + scan.header.frame, scan.data.size());
+            featureExtractor->onData(scan);
+        }
     }
 
 } // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
index 664a8dbb..4b979483 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
@@ -32,11 +32,10 @@
 #include "RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h"
 #include "RobotAPI/libraries/armem/client/forward_declarations.h"
 #include "RobotAPI/libraries/armem/client/plugins/PluginUser.h"
+#include "RobotAPI/libraries/armem/client/plugins/ReaderWriterPlugin.h"
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h>
-
-#include <RobotComponents/libraries/cartographer/MessageQueue.h>
-#include <RobotComponents/libraries/cartographer/types.h>
+#include <RobotAPI/libraries/armem_laser_scans/client/common/Reader.h>
 
 #include "ArVizDrawer.h"
 #include "FeatureExtractor.h"
@@ -106,9 +105,12 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         */
 
     private:
-        void onFeatures(const cartographer::LaserScannerMessage& data,
+        void runPeriodically();
+
+        void onFeatures(const armem::laser_scans::LaserScanStamped& data,
                         const std::vector<Features>& features);
 
+
         void publishFeatures(const memory::LaserScannerFeatures& features,
                              const armem::Time& timestamp);
 
@@ -123,13 +125,11 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         */
 
         // Private member variables go here.
-        MessageQueue<cartographer::LaserScannerMessage> laserMessageQueue;
 
         /// Properties shown in the Scenario GUI.
         struct Properties
         {
-            // input message queue size
-            size_t queueSize{2};
+            int taskPeriodMs = 100;
 
             //
             struct RobotHull
@@ -196,6 +196,8 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         std::mutex arvizMutex;
         */
 
+        PeriodicTask<LaserScannerFeatureExtraction>::pointer_type task;
+
         std::unique_ptr<FeatureExtractor> featureExtractor;
         std::unique_ptr<ArVizDrawer> arVizDrawer;
 
@@ -211,6 +213,10 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         VirtualRobot::MathTools::ConvexHull2DPtr cableArea;
 
 
+        armem::client::plugins::ReaderWriterPlugin<armem::laser_scans::client::Reader>*
+            laserScannerReaderPlugin = nullptr;
+
+
         memory::client::laser_scanner_features::Writer laserScannerFeatureWriter;
     };
 } // namespace armarx::navigation::components::laser_scanner_feature_extraction
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
index 588cd302..d5c875ec 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.cpp
@@ -85,10 +85,10 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
     }
 
     void
-    FeatureExtractor::onData(const cartographer::LaserScannerMessage& data)
+    FeatureExtractor::onData(const armem::laser_scans::LaserScanStamped& data)
     {
         ARMARX_DEBUG << "on data";
-        const auto clustersWithFeatures = features(data.scan);
+        const auto clustersWithFeatures = features(data.data);
 
         ARMARX_DEBUG << "callback";
         callback(data, clustersWithFeatures);
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
index d2fa134f..373fc748 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/FeatureExtractor.h
@@ -27,7 +27,8 @@
 
 #include <VirtualRobot/MathTools.h>
 
-#include "RobotComponents/libraries/cartographer/types.h"
+#include <RobotAPI/libraries/armem_laser_scans/types.h>
+
 #include <RobotComponents/interface/components/GraspingManager/RobotPlacementInterface.h>
 
 #include "EnclosingEllipsoid.h"
@@ -59,14 +60,14 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
     {
     public:
         using Points = std::vector<Eigen::Vector2f>;
-        using Callback = std::function<void(const cartographer::LaserScannerMessage& data,
+        using Callback = std::function<void(const armem::laser_scans::LaserScanStamped& data,
                                             const std::vector<Features>& features)>;
 
         FeatureExtractor(const ScanClustering::Params& scanClusteringParams,
                          const ChainApproximation::Params& chainApproximationParams,
                          const Callback& callback);
 
-        void onData(const cartographer::LaserScannerMessage& data);
+        void onData(const armem::laser_scans::LaserScanStamped& data);
 
     private:
         std::vector<Features> features(const LaserScan& scan) const;
-- 
GitLab


From 303084653eddb910747807f19a529998a7973ec1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Wed, 8 Mar 2023 19:50:15 +0100
Subject: [PATCH 11/13] Add back legacy topic

---
 .../CMakeLists.txt                            |  2 +-
 .../Component.cpp                             | 21 +++++++++++++++++++
 .../Component.h                               |  9 ++++++++
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
index bc399a2c..ede04e8d 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/CMakeLists.txt
@@ -32,7 +32,6 @@ armarx_add_component(laser_scanner_feature_extraction
         # ArmarXCore
         ArmarXCore
         ArmarXCoreComponentPlugins  # For DebugObserver plugin.
-        ArmarXCoreInterfaces
         ArmarXCoreLogging
         # ArmarXGui
         ## ArmarXGuiComponentPlugins  # For RemoteGui plugin.
@@ -40,6 +39,7 @@ armarx_add_component(laser_scanner_feature_extraction
         ## RobotAPICore
         ## RobotAPIInterfaces
         RobotAPIComponentPlugins  # For ArViz and other plugins.
+        RobotComponentsInterfaces # For legacy LaserScannerFeatureExtraction topic
         armarx_navigation::memory
         armem_laser_scans
     DEPENDENCIES_LEGACY
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
index d05e42d5..6c2f235b 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.cpp
@@ -57,6 +57,9 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
         armarx::PropertyDefinitionsPtr def =
             new ComponentPropertyDefinitions(getConfigIdentifier());
 
+        // Publish to a topic (passing the TopicListenerPrx).
+        def->topic(featuresTopic);
+
         // // Add an optionalproperty.
         def->optional(
             properties.taskPeriodMs, "p.taskPeriodMs", "Update rate of the running task.");
@@ -481,6 +484,24 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
 
         // store in memory
         laserScannerFeatureWriter.store(features, getName(), timestamp);
+
+        // legacy - topic
+        LineSegment2DChainSeq chains;
+        for (const auto& feature : features.features)
+        {
+            if (not feature.chain.empty())
+            {
+                LineSegment2DChain chain;
+                for (const auto& pt : feature.chain)
+                {
+                    chain.push_back(
+                        conversions::to2D(features.frameGlobalPose * conversions::to3D(pt)));
+                }
+                chains.push_back(chain);
+            }
+        }
+
+        featuresTopic->reportExtractedLineSegments(chains);
     }
 
     void
diff --git a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
index 4b979483..fac5e425 100644
--- a/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
+++ b/source/armarx/navigation/components/laser_scanner_feature_extraction/Component.h
@@ -37,6 +37,8 @@
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h>
 #include <RobotAPI/libraries/armem_laser_scans/client/common/Reader.h>
 
+#include <RobotComponents/interface/components/LaserScannerFeatureExtraction/LaserScannerFeatureExtraction.h>
+
 #include "ArVizDrawer.h"
 #include "FeatureExtractor.h"
 #include "armarx/navigation/components/laser_scanner_feature_extraction/ChainApproximation.h"
@@ -47,6 +49,11 @@
 namespace armarx::navigation::components::laser_scanner_feature_extraction
 {
 
+    using LaserScannerFeaturesListenerPrx =
+        armarx::laser_scanner_feature_extraction::LaserScannerFeaturesListenerPrx;
+    using LineSegment2DChain = armarx::laser_scanner_feature_extraction::LineSegment2DChain;
+    using LineSegment2DChainSeq = armarx::laser_scanner_feature_extraction::LineSegment2DChainSeq;
+
     // class FeatureExtractor;
     // class ArVizDrawer;
 
@@ -208,6 +215,8 @@ namespace armarx::navigation::components::laser_scanner_feature_extraction
 
         VirtualRobot::RobotPtr robot;
 
+        LaserScannerFeaturesListenerPrx featuresTopic;
+
         std::variant<std::monostate, VirtualRobot::MathTools::ConvexHull2DPtr, Eigen::Vector2f>
             robotHull;
         VirtualRobot::MathTools::ConvexHull2DPtr cableArea;
-- 
GitLab


From a801d28cf42e2b62116d74c1c131da4f9f37ecf0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Mon, 20 Mar 2023 11:08:05 +0100
Subject: [PATCH 12/13] Fix laser scanner features reader/writer

Fix old memory name constants.
---
 .../client/laser_scanner_features/Reader.h    |  7 +++--
 .../client/laser_scanner_features/Writer.cpp  | 28 +++++++++----------
 .../client/laser_scanner_features/Writer.h    |  7 +++--
 3 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
index 14cca12a..4e08755e 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Reader.h
@@ -32,6 +32,7 @@
 #include <RobotAPI/libraries/armem/client/Reader.h>
 #include <RobotAPI/libraries/armem/client/query/Builder.h>
 
+#include <armarx/navigation/memory/constants.h>
 #include <armarx/navigation/memory/types.h>
 
 namespace armarx
@@ -103,11 +104,11 @@ namespace armarx::navigation::memory::client::laser_scanner_features
         // Properties
         struct Properties
         {
-            std::string memoryName = "Vision";
-            std::string coreSegmentName = "LaserScannerFeatures";
+            std::string memoryName = memory::constants::NavigationMemoryName;
+            std::string coreSegmentName = memory::constants::LaserScannerFeaturesCoreSegment;
         } properties;
 
-        const std::string propertyPrefix = "mem.vision.laser_scanner_features.";
+        const std::string propertyPrefix = "mem.nav.laser_scanner_features.";
     };
 
 } // namespace armarx::navigation::memory::client::laser_scanner_features
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
index e2cb6739..425345e3 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.cpp
@@ -4,7 +4,6 @@
 
 #include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
 #include <armarx/navigation/memory/aron_conversions.h>
-#include <armarx/navigation/memory/constants.h>
 
 namespace armarx::navigation::memory::client::laser_scanner_features
 {
@@ -23,25 +22,25 @@ namespace armarx::navigation::memory::client::laser_scanner_features
 
         const std::string prefix = propertyPrefix;
 
-        // def->optional(properties.coreSegmentName,
-        //               prefix + "CoreSegment",
-        //               "Name of the mapping memory core segment to use.");
+        def->optional(properties.coreSegmentName,
+                      prefix + "CoreSegment",
+                      "Name of the mapping memory core segment to use.");
 
-        // def->optional(properties.memoryName, prefix + "MemoryName");
+        def->optional(properties.memoryName, prefix + "MemoryName");
     }
 
     void
     Writer::connect()
     {
         // Wait for the memory to become available and add it as dependency.
-        ARMARX_IMPORTANT << "LaserScansWriter: Waiting for memory '"
-                         << constants::NavigationMemoryName << "' ...";
+        ARMARX_IMPORTANT << "LaserScansWriter: Waiting for memory '" << properties.memoryName
+                         << "' ...";
         try
         {
-            memoryWriter = memoryNameSystem.useWriter(
-                armem::MemoryID().withMemoryName(constants::NavigationMemoryName));
-            ARMARX_IMPORTANT << "MappingDataWriter: Connected to memory '"
-                             << constants::NavigationMemoryName << "'";
+            memoryWriter =
+                memoryNameSystem.useWriter(armem::MemoryID().withMemoryName(properties.memoryName));
+            ARMARX_IMPORTANT << "MappingDataWriter: Connected to memory '" << properties.memoryName
+                             << "'";
         }
         catch (const armem::error::CouldNotResolveMemoryServer& e)
         {
@@ -49,8 +48,7 @@ namespace armarx::navigation::memory::client::laser_scanner_features
             return;
         }
 
-        ARMARX_IMPORTANT << "LaserScansWriter: Connected to memory '"
-                         << constants::NavigationMemoryName;
+        ARMARX_IMPORTANT << "LaserScansWriter: Connected to memory '" << properties.memoryName;
     }
 
     bool
@@ -72,8 +70,8 @@ namespace armarx::navigation::memory::client::laser_scanner_features
         // }
 
         const auto entityID = armem::MemoryID()
-                                  .withMemoryName(constants::NavigationMemoryName)
-                                  .withCoreSegmentName(constants::LaserScannerFeaturesCoreSegment)
+                                  .withMemoryName(properties.memoryName)
+                                  .withCoreSegmentName(properties.coreSegmentName)
                                   .withProviderSegmentName(providerName)
                                   .withEntityName(features.frame)
                                   .withTimestamp(timestamp);
diff --git a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
index 30f42933..bd4cad88 100644
--- a/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
+++ b/source/armarx/navigation/memory/client/laser_scanner_features/Writer.h
@@ -29,6 +29,7 @@
 #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
 #include <RobotAPI/libraries/armem/client/Writer.h>
 
+#include <armarx/navigation/memory/constants.h>
 #include <armarx/navigation/memory/types.h>
 
 namespace armarx::navigation::memory::client::laser_scanner_features
@@ -72,13 +73,13 @@ namespace armarx::navigation::memory::client::laser_scanner_features
         // Properties
         struct Properties
         {
-            // std::string memoryName = "Vision";
-            // std::string coreSegmentName = "LaserScannerFeatures";
+            std::string memoryName = memory::constants::NavigationMemoryName;
+            std::string coreSegmentName = memory::constants::LaserScannerFeaturesCoreSegment;
         } properties;
 
         std::mutex memoryWriterMutex;
 
-        const std::string propertyPrefix = "mem.vision.laser_scanner_features.";
+        const std::string propertyPrefix = "mem.nav.laser_scanner_features.";
     };
 
 } // namespace armarx::navigation::memory::client::laser_scanner_features
-- 
GitLab


From 6e4b545bf3e4e2659fed17b9a106361f11ece870 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20Gr=C3=B6ger?= <tobias.groeger@student.kit.edu>
Date: Mon, 20 Mar 2023 11:09:22 +0100
Subject: [PATCH 13/13] Add features to dynamic scene

---
 .../components/navigator/Component.cpp        | 23 +++++++++++--------
 .../components/navigator/Component.h          | 17 +++++++-------
 source/armarx/navigation/core/DynamicScene.h  |  2 ++
 .../server/scene_provider/SceneProvider.cpp   | 20 +++++++++++-----
 .../server/scene_provider/SceneProvider.h     | 13 +++++++++--
 5 files changed, 50 insertions(+), 25 deletions(-)

diff --git a/source/armarx/navigation/components/navigator/Component.cpp b/source/armarx/navigation/components/navigator/Component.cpp
index 3943986f..424e2851 100644
--- a/source/armarx/navigation/components/navigator/Component.cpp
+++ b/source/armarx/navigation/components/navigator/Component.cpp
@@ -168,6 +168,7 @@ namespace armarx::navigation::components::navigator
                 .costmapReader = &costmapReaderPlugin->get(),
                 .virtualRobotReader = &virtualRobotReaderPlugin->get(),
                 .humanReader = &humanReaderPlugin->get(),
+                .laserScannerFeaturesReader = &laserScannerFeaturesReaderPlugin->get(),
                 .objectPoseClient = ObjectPoseClientPluginUser::getClient()};
 
             sceneProvider.emplace(srv, params.sceneCfg);
@@ -235,7 +236,6 @@ namespace armarx::navigation::components::navigator
         return "navigator";
     }
 
-
     void
     Component::createConfig(const aron::data::dto::DictPtr& stackConfig,
                             const std::string& callerId,
@@ -293,7 +293,8 @@ namespace armarx::navigation::components::navigator
     {
         ARMARX_TRACE;
 
-        ARMARX_INFO << "moveTo() requested by caller '" << callerId << "' with navigation mode `" << navigationMode << "`.";
+        ARMARX_INFO << "moveTo() requested by caller '" << callerId << "' with navigation mode `"
+                    << navigationMode << "`.";
         ARMARX_CHECK(navigators.count(callerId) > 0)
             << "Navigator config for caller `" << callerId << "` not registered!";
 
@@ -345,7 +346,6 @@ namespace armarx::navigation::components::navigator
                                        core::NavigationFrameNames.from_name(navigationMode));
     }
 
-
     void
     Component::moveTo2(const client::detail::Waypoints& waypoints,
                        const std::string& navigationMode,
@@ -363,13 +363,15 @@ namespace armarx::navigation::components::navigator
 
         navigators.at(callerId).moveTo(wps, core::NavigationFrameNames.from_name(navigationMode));
     }
-    
-    void Component::moveToLocation(const std::string& location,
-                         const std::string& callerId,
-                         const Ice::Current& c)
+
+    void
+    Component::moveToLocation(const std::string& location,
+                              const std::string& callerId,
+                              const Ice::Current& c)
     {
         ARMARX_TRACE;
-        ARMARX_INFO << "MoveToLocation `" << location << "` requested by caller '" << callerId << "'";
+        ARMARX_INFO << "MoveToLocation `" << location << "` requested by caller '" << callerId
+                    << "'";
 
         ARMARX_CHECK(navigators.count(callerId) > 0)
             << "Navigator config for caller `" << callerId << "` not registered!";
@@ -501,9 +503,12 @@ namespace armarx::navigation::components::navigator
                       "but won't execute it.");
 
         def->required(params.sceneCfg.robotName, "p.scene.robotName");
-        def->optional(params.sceneCfg.staticCostmapProviderName, "p.scene.staticCostmapProviderName");
+        def->optional(params.sceneCfg.staticCostmapProviderName,
+                      "p.scene.staticCostmapProviderName");
         def->optional(params.sceneCfg.staticCostmapName, "p.scene.staticCostmapName");
         def->optional(params.sceneCfg.humanProviderName, "p.scene.humanProviderName");
+        def->optional(params.sceneCfg.laserScannerFeaturesProviderName,
+                      "p.scene.laserScannerFeaturesProviderName");
 
         return def;
     }
diff --git a/source/armarx/navigation/components/navigator/Component.h b/source/armarx/navigation/components/navigator/Component.h
index aac593f1..b8526d92 100644
--- a/source/armarx/navigation/components/navigator/Component.h
+++ b/source/armarx/navigation/components/navigator/Component.h
@@ -51,11 +51,11 @@
 #include <RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h>
 
 #include <armarx/control/client/ComponentPlugin.h>
-
-#include <armarx/navigation/memory/client/human/Reader.h>
 #include <armarx/navigation/components/navigator/RemoteGui.h>
 #include <armarx/navigation/core/types.h>
 #include <armarx/navigation/memory/client/graph/Reader.h>
+#include <armarx/navigation/memory/client/human/Reader.h>
+#include <armarx/navigation/memory/client/laser_scanner_features/Reader.h>
 #include <armarx/navigation/memory/client/parameterization/Reader.h>
 #include <armarx/navigation/memory/client/stack_result/Writer.h>
 #include <armarx/navigation/server/Navigator.h>
@@ -66,7 +66,6 @@
 #include <armarx/navigation/server/parameterization/MemoryParameterizationService.h>
 #include <armarx/navigation/server/scene_provider/SceneProvider.h>
 
-
 namespace armarx::navigation::components::navigator
 {
 
@@ -115,13 +114,13 @@ namespace armarx::navigation::components::navigator
                      const Ice::Current& c = Ice::emptyCurrent) override;
 
         void moveToLocation(const std::string& location,
-                     const std::string& callerId,
-                     const Ice::Current& c = Ice::emptyCurrent) override;
+                            const std::string& callerId,
+                            const Ice::Current& c = Ice::emptyCurrent) override;
 
         void updateMoveTo(const std::vector<Eigen::Matrix4f>& waypoints,
-                    const std::string& navigationMode,
-                    const std::string& callerId,
-                    const Ice::Current& c = Ice::emptyCurrent) override;
+                          const std::string& navigationMode,
+                          const std::string& callerId,
+                          const Ice::Current& c = Ice::emptyCurrent) override;
 
         void moveTowards(const Eigen::Vector3f& direction,
                          const std::string& navigationMode,
@@ -208,6 +207,8 @@ namespace armarx::navigation::components::navigator
             costmapReaderPlugin = nullptr;
         armem::client::plugins::ReaderWriterPlugin<memory::client::human::Reader>*
             humanReaderPlugin = nullptr;
+        armem::client::plugins::ReaderWriterPlugin<memory::client::laser_scanner_features::Reader>*
+            laserScannerFeaturesReaderPlugin = nullptr;
 
         // armem::vision::occupancy_grid::client::Reader occupancyGridReader;
 
diff --git a/source/armarx/navigation/core/DynamicScene.h b/source/armarx/navigation/core/DynamicScene.h
index 172957d0..66f31707 100644
--- a/source/armarx/navigation/core/DynamicScene.h
+++ b/source/armarx/navigation/core/DynamicScene.h
@@ -24,6 +24,7 @@
 
 
 #include <armarx/navigation/human/types.h>
+#include <armarx/navigation/memory/types.h>
 
 namespace armarx::navigation::core
 {
@@ -31,6 +32,7 @@ namespace armarx::navigation::core
     struct DynamicScene
     {
         human::Humans humans;
+        std::vector<memory::LaserScannerFeatures> laserScannerFeatures;
     };
 
 } // namespace armarx::navigation::core
diff --git a/source/armarx/navigation/server/scene_provider/SceneProvider.cpp b/source/armarx/navigation/server/scene_provider/SceneProvider.cpp
index 85e0d623..3fa2eb01 100644
--- a/source/armarx/navigation/server/scene_provider/SceneProvider.cpp
+++ b/source/armarx/navigation/server/scene_provider/SceneProvider.cpp
@@ -14,9 +14,9 @@
 #include <armarx/navigation/memory/client/costmap/Reader.h>
 #include <armarx/navigation/memory/client/graph/Reader.h>
 #include <armarx/navigation/memory/client/human/Reader.h>
+#include <armarx/navigation/memory/client/laser_scanner_features/Reader.h>
 #include <armarx/navigation/util/util.h>
 
-
 namespace armarx::navigation::server::scene_provider
 {
 
@@ -124,11 +124,19 @@ namespace armarx::navigation::server::scene_provider
     core::DynamicScene
     SceneProvider::getDynamicScene(const DateTime& timestamp) const
     {
-        const memory::client::human::Reader::Query query{.providerName = config.humanProviderName,
-                                                         .timestamp = timestamp,
-                                                         .maxAge = Duration::MilliSeconds(500)};
-
-        return {.humans = srv.humanReader->queryHumans(query).humans};
+        const memory::client::human::Reader::Query queryHumans{
+            .providerName = config.humanProviderName,
+            .timestamp = timestamp,
+            .maxAge = Duration::MilliSeconds(500)};
+
+        const memory::client::laser_scanner_features::Reader::Query queryFeatures{
+            .providerName = config.laserScannerFeaturesProviderName,
+            .name = {},
+            .timestamp = timestamp};
+
+        return {.humans = srv.humanReader->queryHumans(queryHumans).humans,
+                .laserScannerFeatures =
+                    srv.laserScannerFeaturesReader->queryData(queryFeatures).features};
     }
 
     core::SceneGraph
diff --git a/source/armarx/navigation/server/scene_provider/SceneProvider.h b/source/armarx/navigation/server/scene_provider/SceneProvider.h
index 1f21e85e..df8a9e77 100644
--- a/source/armarx/navigation/server/scene_provider/SceneProvider.h
+++ b/source/armarx/navigation/server/scene_provider/SceneProvider.h
@@ -35,23 +35,29 @@
 #include <armarx/navigation/memory/client/human/Reader.h>
 #include <armarx/navigation/server/scene_provider/SceneProviderInterface.h>
 
-
 namespace armarx::navigation::memory::client
 {
     namespace graph
     {
         class Reader;
     }
+
     namespace costmap
     {
         class Reader;
     }
+
     namespace human
     {
         class Reader;
     }
-} // namespace armarx::navigation::memory::client
 
+    namespace laser_scanner_features
+    {
+        class Reader;
+    }
+
+} // namespace armarx::navigation::memory::client
 
 namespace armarx::navigation::server::scene_provider
 {
@@ -72,6 +78,8 @@ namespace armarx::navigation::server::scene_provider
 
             memory::client::human::Reader* humanReader;
 
+            memory::client::laser_scanner_features::Reader* laserScannerFeaturesReader;
+
             objpose::ObjectPoseClient objectPoseClient;
         };
 
@@ -83,6 +91,7 @@ namespace armarx::navigation::server::scene_provider
             std::string staticCostmapName = "distance_to_obstacles";
 
             std::string humanProviderName = "dynamic_scene_provider";
+            std::string laserScannerFeaturesProviderName = "LaserScannerFeatureExtraction";
         };
 
         SceneProvider(const InjectedServices& srv, const Config& config);
-- 
GitLab