diff --git a/source/RobotAPI/components/ArViz/Client/Elements.cpp b/source/RobotAPI/components/ArViz/Client/Elements.cpp
index f479f4ef5456fc7a88d0f3a0fa87d2e77e9aab66..d97fea3b03d55ddb82a379628197a5f04ffc3d74 100644
--- a/source/RobotAPI/components/ArViz/Client/Elements.cpp
+++ b/source/RobotAPI/components/ArViz/Client/Elements.cpp
@@ -107,9 +107,9 @@ namespace armarx::viz
         float angle = std::acos(naturalDir.dot(dir));
         if (cross.squaredNorm() < 1.0e-12f)
         {
-            // Directions are almost colinear ==> Do no rotation
+            // Directions are almost colinear ==> Angle is either 0 or 180 deg
             cross = Eigen::Vector3f::UnitX();
-            angle = 0.0f;
+            // Keep angle
         }
         Eigen::Vector3f axis = cross.normalized();
         Eigen::Quaternionf ori(Eigen::AngleAxisf(angle, axis));
diff --git a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp
index 2c93faf7fa4a464909bd6dea214c74022571474f..e52609fa085feac2dc4ef8fc93f68127aef8b040 100644
--- a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp
+++ b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp
@@ -275,13 +275,18 @@ namespace armarx
 
     void fillExampleLayer(viz::Layer& layer, double timeInSeconds)
     {
+        for (int i = 0; i < 6; ++i)
         {
-            Eigen::Vector3f pos = Eigen::Vector3f::Zero();
-            pos.z() = +300.0f;
+            Eigen::Vector3f pos = Eigen::Vector3f(-200.0, 200.0, 300);
+            pos.x() +=  -300 * i;
+
+            Eigen::Vector3f normal = Eigen::Vector3f::Zero();
+            normal(i / 2) = (i % 2 == 0 ? 1.0 : -1.0);
 
-            viz::ArrowCircle circle = viz::ArrowCircle("circle")
+            viz::ArrowCircle circle = viz::ArrowCircle("circle " + std::to_string(i))
                                       .position(pos)
                                       .radius(100.0f)
+                                      .normal(normal)
                                       .width(10.0f)
                                       .color(viz::Color::fromRGBA(255, 0, 255));
 
diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt b/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt
index d62a96eddf76d69b562c196d487354d81607c116..b40d5a2fcf9c480a71f0576d049ad0a311813815 100644
--- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt
+++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt
@@ -19,6 +19,10 @@ set(SOURCES
     aronTreeWidget/AronTreeWidgetItem.cpp
     aronTreeWidget/AronTreeWidgetController.cpp
     aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.cpp
+    aronTreeWidget/modal/dict/AronTreeWidgetDictInputModalController.cpp
+    aronTreeWidget/modal/float_double/AronTreeWidgetFloatInputModalController.cpp
+    aronTreeWidget/modal/int_long/AronTreeWidgetIntInputModalController.cpp
+    aronTreeWidget/modal/bool/AronTreeWidgetBoolInputModalController.cpp
     aronTreeWidget/modal/AronTreeWidgetModal.cpp
     ColorPalettes.cpp
     SkillManagerMonitorWidgetController.cpp
@@ -41,6 +45,10 @@ set(HEADERS
     aronTreeWidget/AronTreeWidgetController.h
     aronTreeWidget/modal/AronTreeWidgetModal.h
     aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.h
+    aronTreeWidget/modal/dict/AronTreeWidgetDictInputModalController.h
+    aronTreeWidget/modal/float_double/AronTreeWidgetFloatInputModalController.h
+    aronTreeWidget/modal/int_long/AronTreeWidgetIntInputModalController.h
+    aronTreeWidget/modal/bool/AronTreeWidgetBoolInputModalController.h
     ColorPalettes.h
     SkillManagerMonitorWidgetController.h
 )
diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/modal/dict/AronTreeWidgetDictInputModalController.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/modal/dict/AronTreeWidgetDictInputModalController.cpp
index 0e96cb6245384234e01f6d312866495f619e51f8..71a88e54fcf49d77ea5de6804ef06a11e90e6ae8 100644
--- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/modal/dict/AronTreeWidgetDictInputModalController.cpp
+++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/modal/dict/AronTreeWidgetDictInputModalController.cpp
@@ -30,7 +30,7 @@ namespace armarx
     {
         for (const auto& added : addedItems)
         {
-            item->addChild(added->copy());
+            //item->addChild(added->copy());
         }
 
         AronTreeWidgetModal::submit();
diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetModalCreator.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetModalCreator.cpp
index bb76232a8adb1dfd78792d24a9eb145f9b78e033..fc58634bb9bc02834d4e72663e3c16ffe36d6de6 100644
--- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetModalCreator.cpp
+++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetModalCreator.cpp
@@ -28,7 +28,7 @@
 
 // modals
 #include "../modal/text/AronTreeWidgetTextInputModalController.h"
-#include "../modal/dict/AronTreeWidgetDictInputModalController.h"
+//#include "../modal/dict/AronTreeWidgetDictInputModalController.h"
 
 // qt
 #include <QTreeWidget>
diff --git a/source/RobotAPI/interface/CMakeLists.txt b/source/RobotAPI/interface/CMakeLists.txt
index 5d9d3c04b278f8032125f98cf82d63aaa8253155..9e290598fee8047f853044e54ea3775b62ab49f7 100644
--- a/source/RobotAPI/interface/CMakeLists.txt
+++ b/source/RobotAPI/interface/CMakeLists.txt
@@ -108,6 +108,7 @@ set(SLICE_FILES
 
     aron.ice
     aron/Aron.ice
+    aron/test/AronConversionTestInterface.ice
 
 
     armem.ice
diff --git a/source/RobotAPI/interface/aron/test/AronConversionTestInterface.ice b/source/RobotAPI/interface/aron/test/AronConversionTestInterface.ice
new file mode 100644
index 0000000000000000000000000000000000000000..924f40c6d8664123c822eb9f7eb338d8d50b52d5
--- /dev/null
+++ b/source/RobotAPI/interface/aron/test/AronConversionTestInterface.ice
@@ -0,0 +1,61 @@
+/*
+ * 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    SpeechX::aron_cpp_to_python_conv_test
+ * author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * date       2023
+ * copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *            GNU General Public License
+ */
+
+
+#pragma once
+
+#include <RobotAPI/interface/aron/Aron.ice>
+
+
+module armarx { module aron {  module test
+{
+
+module dto
+{
+
+    struct TestAronConversionRequest
+    {
+        string aronClassName;
+        ::armarx::aron::data::dto::Dict probe;
+    };
+    struct TestAronConversionResponse
+    {
+        bool success;
+        string errorMessage;
+
+        ::armarx::aron::data::dto::Dict probe;
+    };
+
+};
+
+
+module dti
+{
+
+    interface AronConversionTestInterface
+    {
+        dto::TestAronConversionResponse testAronConversion(dto::TestAronConversionRequest req);
+    };
+
+};
+
+};};};
diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.cpp
index dbc68923c98f1974c7fbd224f09ad94c625ff6b7..d5318e1f1e6ec9933ddc7e6b50f3894044111917 100644
--- a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.cpp
+++ b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.cpp
@@ -34,8 +34,8 @@ namespace armarx::armem::server::ltm
         return _id.getLeafItem();
     }
 
-    void MemoryItem::setMemoryName(const std::string& m)
+    void MemoryItem::setMemoryName(const std::string& memoryName)
     {
-        _id.memoryName = m;
+        _id.memoryName = memoryName;
     }
 } // namespace armarx::armem::server::ltm
diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h
index 586d4d0e51dcb546eaae28d7cbc984893c548ca8..b8d17fe471c17e62aa188406648515e5a32f384a 100644
--- a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h
+++ b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h
@@ -21,7 +21,7 @@ namespace armarx::armem::server::ltm
         std::string name() const;
 
         virtual void setMemoryID(const MemoryID&);
-        void setMemoryName(const std::string&);
+        void setMemoryName(const std::string& memoryName);
 
     protected:
         std::shared_ptr<Processors> processors;
diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/filesystem_util.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/filesystem_util.cpp
index 6b6bfdc1be88e30507e2a5d865b94c44abfb090d..1a40e26a04923bccb123b4b07f00ab9be190efa9 100644
--- a/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/filesystem_util.cpp
+++ b/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/filesystem_util.cpp
@@ -1,5 +1,7 @@
 #include "filesystem_util.h"
 
+#include <algorithm>
+
 #include <RobotAPI/libraries/armem/core/error/ArMemError.h>
 
 
diff --git a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp
index 8253755604e04e1d65c4f7a67e6c04857eb52669..b9c789398b0d8ce5c6fb17e0c2cb44fb5bc5b531 100644
--- a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp
+++ b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp
@@ -267,7 +267,7 @@ namespace armarx::armem::gui
         {
             std::stringstream ss;
             ss << "Memory name '" << memoryName
-               << "' is unknown. Known are: " << simox::alg::to_string(simox::alg::get_keys(memoryData), ", ");
+               << "' is unknown. Known are: " << simox::alg::join(simox::alg::get_keys(memoryData), ", ");
             statusLabel->setText(QString::fromStdString(ss.str()));
             return nullptr;
         }
@@ -406,10 +406,7 @@ namespace armarx::armem::gui
         TIMING_START(MemoryExport)
 
         std::string status;
-        std::vector<wm::Memory> memoryDataVec;
-        std::transform(memoryData.begin(), memoryData.end(), std::back_inserter(memoryDataVec),
-                       [](auto& el) { return el.second; });
-
+        std::vector<wm::Memory> memoryDataVec = simox::alg::get_values(memoryData);
         diskControl->storeOnDisk(directory, memoryDataVec, &status);
 
         statusLabel->setText(QString::fromStdString(status));
@@ -436,11 +433,11 @@ namespace armarx::armem::gui
             }
             else
             {
-                ARMARX_INFO << "No memory with name " << name << " available for commit. Create new virtual memory.";
+                ARMARX_INFO << "No memory with name '" << name << "' available for commit. Create new virtual memory.";
 
                 // Please note: Here we assume that a memory server with the same name does not exist.
                 // I think this assumption is ok, since nobody should use filepaths as memory name.
-                // Nontheless, we did not restrict the user to do so...
+                // Nonetheless, we did not restrict the user to do so...
                 std::string virtualMemoryName = name + " (at " + path.string() + ")";
                 wm::Memory virtualMemory(virtualMemoryName);
                 virtualMemory.update(commit, true, false);
diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
index de7848b7fa00ea312ee99cea77608645e39bd1e7..c17ce69020b2b7f2c363219339754919fc805723 100644
--- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
+++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
@@ -1317,7 +1317,7 @@ namespace armarx::armem::server::obj::instance
 
             if (robot)
             {
-                reader->synchronizeRobot(*robot, Clock::Now());
+                ARMARX_CHECK(reader->synchronizeRobot(*robot, Clock::Now()));
                 // Store robot if valid.
                 loaded.emplace(robotName, robot);
             }
diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h
index 19a184e8c4cee71e2a7866206f2ae55cd51f0d27..ab30cbc1317d98f802dc24cbe5d844da02b038e9 100644
--- a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h
+++ b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h
@@ -27,7 +27,6 @@
 
 #include "RobotReader.h"
 
-
 namespace armarx::armem::robot_state
 {
     /**
@@ -47,7 +46,8 @@ namespace armarx::armem::robot_state
         void connect();
         void registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def);
 
-        bool synchronizeRobot(VirtualRobot::Robot& robot, const armem::Time& timestamp);
+        [[nodiscard]] bool synchronizeRobot(VirtualRobot::Robot& robot,
+                                            const armem::Time& timestamp);
 
         [[nodiscard]] VirtualRobot::RobotPtr
         getRobot(const std::string& name,
@@ -70,14 +70,12 @@ namespace armarx::armem::robot_state
 
 
     private:
-
         [[nodiscard]] VirtualRobot::RobotPtr
         _getSynchronizedRobot(const std::string& name,
                               const armem::Time& timestamp = armem::Time::Invalid(),
                               const VirtualRobot::RobotIO::RobotDescription& loadMode =
-                                 VirtualRobot::RobotIO::RobotDescription::eStructure,
+                                  VirtualRobot::RobotIO::RobotDescription::eStructure,
                               bool blocking = true);
-
     };
 
 } // namespace armarx::armem::robot_state
diff --git a/source/RobotAPI/libraries/aron/CMakeLists.txt b/source/RobotAPI/libraries/aron/CMakeLists.txt
index ce4f7bc99cb8d923decfdd7fdb79390837e79150..e1ccc8560ff75b018fbc958d0f1febf0512d9590 100644
--- a/source/RobotAPI/libraries/aron/CMakeLists.txt
+++ b/source/RobotAPI/libraries/aron/CMakeLists.txt
@@ -2,3 +2,4 @@ add_subdirectory(core)
 add_subdirectory(converter)
 add_subdirectory(codegeneration)
 add_subdirectory(common)
+add_subdirectory(test)
diff --git a/source/RobotAPI/libraries/aron/core/aron_conversions.h b/source/RobotAPI/libraries/aron/core/aron_conversions.h
index d0f88cab840be59b86bbeb22d7229afb9f9f0b48..d27d6415877d4c15f9ddf36310e656590f8efd7e 100644
--- a/source/RobotAPI/libraries/aron/core/aron_conversions.h
+++ b/source/RobotAPI/libraries/aron/core/aron_conversions.h
@@ -3,12 +3,29 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <type_traits>
 #include <vector>
 
 #include "Path.h"
 
 namespace armarx::aron
 {
+
+namespace detail
+{
+
+    // Helper concept to avoid ambiguities
+    template<typename DtoT, typename BoT>
+    concept DtoAndBoAreSame = std::is_same<DtoT, BoT>::value;
+
+    template <class ...>
+    struct is_optional : public std::false_type {};
+
+    template <class ...Ts>
+    struct is_optional<std::optional<Ts...>> : public std::true_type {};
+
+}
+
     /**
      * Framework for converting ARON DTOs (Data Transfer Objects) to C++ BOs
      * (Business Objects) and back.
@@ -51,12 +68,6 @@ namespace armarx::aron
      * }
      * @endcode
      */
-
-    // Helper concept to avoid ambiguities
-    template<typename DtoT, typename BoT>
-    concept DtoAndBoAreSame = std::is_same<DtoT, BoT>::value;
-
-
     // Same type
     template <class T>
     void toAron(T& dto, const T& bo)
@@ -139,6 +150,24 @@ namespace armarx::aron
         }
     }
 
+
+    // One-sided optional
+    template <class DtoT, class BoT>
+    requires (not detail::is_optional<BoT>::value)
+    void toAron(std::optional<DtoT>& dto, const BoT& bo)
+    {
+        dto = DtoT{};
+        toAron(*dto, bo);
+    }
+    template <class DtoT, class BoT>
+    requires (not detail::is_optional<DtoT>::value)
+    void fromAron(DtoT& dto, const std::optional<BoT>& bo)
+    {
+        bo = BoT{};
+        fromAron(dto, *bo);
+    }
+
+
     // Flag-controlled optional
     template <class DtoT, class BoT>
     void toAron(DtoT& dto, bool& dtoValid, const BoT& bo, bool boValid)
@@ -154,6 +183,7 @@ namespace armarx::aron
         }
     }
 
+
     template <class DtoT, class BoT>
     void fromAron(const DtoT& dto, bool dtoValid, BoT& bo, bool& boValid)
     {
@@ -222,7 +252,7 @@ namespace armarx::aron
 
     // std::map
     template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
-    requires (!(DtoAndBoAreSame<DtoKeyT, BoKeyT> and DtoAndBoAreSame<DtoValueT, BoValueT>))
+    requires (!(detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and detail::DtoAndBoAreSame<DtoValueT, BoValueT>))
     void toAron(std::map<DtoKeyT, DtoValueT>& dtoMap,
                 const std::map<BoKeyT, BoValueT>& boMap)
     {
@@ -236,7 +266,7 @@ namespace armarx::aron
         }
     }
     template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
-    requires (!(DtoAndBoAreSame<DtoKeyT, BoKeyT> and DtoAndBoAreSame<DtoValueT, BoValueT>))
+    requires (!(detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and detail::DtoAndBoAreSame<DtoValueT, BoValueT>))
     void fromAron(const std::map<DtoKeyT, DtoValueT>& dtoMap,
                   std::map<BoKeyT, BoValueT>& boMap)
     {
@@ -252,7 +282,7 @@ namespace armarx::aron
 
 
     template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
-    requires (!(DtoAndBoAreSame<DtoKeyT, BoKeyT> and DtoAndBoAreSame<DtoValueT, BoValueT>))
+    requires (!(detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and detail::DtoAndBoAreSame<DtoValueT, BoValueT>))
     std::map<DtoKeyT, DtoValueT> toAron(const std::map<BoKeyT, BoValueT>& boMap)
     {
         std::map<DtoKeyT, DtoValueT> dtoMap;
@@ -343,20 +373,20 @@ namespace armarx
 
     // std::vector
     template <class DtoT, class BoT>
-    requires (!aron::DtoAndBoAreSame<DtoT, BoT>)
+    requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>)
     void toAron(std::vector<DtoT>& dtos, const std::vector<BoT>& bos)
     {
         armarx::aron::toAron(dtos, bos);
     }
     template <class DtoT, class BoT>
-    requires (!aron::DtoAndBoAreSame<DtoT, BoT>)
+    requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>)
     void fromAron(const std::vector<DtoT>& dtos, std::vector<BoT>& bos)
     {
         armarx::aron::fromAron(dtos, bos);
     }
 
     template <class DtoT, class BoT>
-    requires (!aron::DtoAndBoAreSame<DtoT, BoT>)
+    requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>)
     std::vector<DtoT> toAron(const std::vector<BoT>& bos)
     {
         return armarx::aron::toAron<DtoT, BoT>(bos);
@@ -365,13 +395,13 @@ namespace armarx
 
     // std::map
     template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
-    requires (!(aron::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::DtoAndBoAreSame<DtoValueT, BoValueT>))
+    requires (!(aron::detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::detail::DtoAndBoAreSame<DtoValueT, BoValueT>))
     void toAron(std::map<DtoKeyT, DtoValueT>& dtoMap, const std::map<BoKeyT, BoValueT>& boMap)
     {
         armarx::aron::toAron(dtoMap, boMap);
     }
     template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
-    requires (!(aron::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::DtoAndBoAreSame<DtoValueT, BoValueT>))
+    requires (!(aron::detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::detail::DtoAndBoAreSame<DtoValueT, BoValueT>))
     void fromAron(const std::map<DtoKeyT, DtoValueT>& dtoMap, std::map<BoKeyT, BoValueT>& boMap)
     {
         armarx::aron::fromAron(dtoMap, boMap);
@@ -379,7 +409,7 @@ namespace armarx
 
 
     template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
-    requires (!(aron::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::DtoAndBoAreSame<DtoValueT, BoValueT>))
+    requires (!(aron::detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::detail::DtoAndBoAreSame<DtoValueT, BoValueT>))
     std::map<DtoKeyT, DtoValueT> toAron(const std::map<BoKeyT, BoValueT>& boMap)
     {
         armarx::aron::toAron<DtoKeyT, DtoValueT, BoKeyT, BoValueT>(boMap);
diff --git a/source/RobotAPI/libraries/aron/core/rw.h b/source/RobotAPI/libraries/aron/core/rw.h
index 6257f2422687591b0991afed8a0577ee127207ef..95b6aac606f47d3c46de34ac5ec077ded28c41a4 100644
--- a/source/RobotAPI/libraries/aron/core/rw.h
+++ b/source/RobotAPI/libraries/aron/core/rw.h
@@ -26,7 +26,7 @@ namespace armarx::aron
     }
 
     template<class ReaderT, class DtoT, class BoT>
-    requires (data::isReader<ReaderT> && !DtoAndBoAreSame<DtoT, BoT>)
+    requires (data::isReader<ReaderT> && !detail::DtoAndBoAreSame<DtoT, BoT>)
     inline void read(ReaderT& aron_r, typename ReaderT::InputType& input, BoT& ret)
     {
         DtoT aron;
@@ -36,7 +36,7 @@ namespace armarx::aron
     }
 
     template<class WriterT, class DtoT, class BoT>
-    requires (data::isWriter<WriterT> && !DtoAndBoAreSame<DtoT, BoT>)
+    requires (data::isWriter<WriterT> && !detail::DtoAndBoAreSame<DtoT, BoT>)
     inline void write(WriterT& aron_w, const BoT& input, typename WriterT::ReturnType& ret, const armarx::aron::Path& aron_p = armarx::aron::Path())
     {
         DtoT aron;
diff --git a/source/RobotAPI/libraries/aron/test/AronConversionTester.cpp b/source/RobotAPI/libraries/aron/test/AronConversionTester.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aac543a04b6ba1696dedc7a21c7d4ea125896e4e
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/test/AronConversionTester.cpp
@@ -0,0 +1,38 @@
+/**
+ * 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::aron_cpp_to_python_conv_test
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2023
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+#include "AronConversionTester.h"
+
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+
+
+namespace armarx::aron::test
+{
+
+    AronConversionTester::AronConversionTester(dti::AronConversionTestInterfacePrx python) : interface(python)
+    {
+        ARMARX_CHECK(python);
+    }
+
+
+} // namespace armarx::aron::test
diff --git a/source/RobotAPI/libraries/aron/test/AronConversionTester.h b/source/RobotAPI/libraries/aron/test/AronConversionTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..b7ad4b8dfdcf55a37f8efc3116c27440d925d428
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/test/AronConversionTester.h
@@ -0,0 +1,111 @@
+/**
+ * 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::aron_cpp_to_python_conv_test
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2023
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+
+#pragma once
+
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/logging/Logging.h>
+
+#include <RobotAPI/interface/aron/test/AronConversionTestInterface.h>
+
+
+namespace armarx::aron::test
+{
+
+    /**
+     * @brief Helper class for implementing distributed ARON conversion tests
+     * based on the `dti::AronConversionTestInterfacePrx`.
+     *
+     * Example usage:
+     *
+     * @code
+     *
+     * armarx::aron::test::dti::AronConversionTestInterfacePrx pythonComponent = ...;
+     *
+     * auto myAronClassProbeFn = []()
+     * {
+     *     my::arondto::MyAronClass probe;
+     *     probe.data = "42";
+     *     return probe,
+     * };
+     *
+     * armarx::aron::test::AronConversionTester tester(pythonComponent);
+     *
+     * tester.test<my::arondto::MyAronClass>(myAronClassProbeFn, "MyAronClass");
+     *
+     * @endcode
+     *
+     * Note that the `pythonComponent` must point to a corresponding
+     * implementation of the `AronConversionTestInterfacePrx`.
+     */
+    class AronConversionTester
+    {
+    public:
+        template <class AronClassT>
+        using ProbeFn = std::function<AronClassT()>;
+
+
+    public:
+        AronConversionTester(dti::AronConversionTestInterfacePrx interface);
+
+        /**
+         * @brief Test the conversion of a specific ARON class.
+         *
+         * @param probeFn A factory function creating a test instance of the
+         *      ARON class.
+         * @param aronClassName The name of the ARON class. Can be used by the
+         *      other component to decide which class to convert to.
+         */
+        template <class AronClassT>
+        void
+        test(ProbeFn<AronClassT> probeFn, const std::string& aronClassName)
+        {
+            std::stringstream ss;
+            ss << "Test for ARON class '" << aronClassName << "': ";
+
+            const AronClassT probe = probeFn();
+
+            dto::TestAronConversionRequest req;
+            req.aronClassName = aronClassName;
+            req.probe = probe.toAronDTO();
+
+            dto::TestAronConversionResponse res = interface->testAronConversion(req);
+
+            if (res.success)
+            {
+                const AronClassT probeOut = AronClassT::FromAron(res.probe);
+                ARMARX_CHECK(probeOut == probe);
+
+                ARMARX_IMPORTANT << ss.str() << "Success";
+            }
+            else
+            {
+                ARMARX_WARNING << ss.str() << "Conversion in Python component failed: \n" << res.errorMessage;
+            }
+        }
+
+    public:
+        dti::AronConversionTestInterfacePrx interface;
+    };
+
+} // namespace armarx::aron::test
diff --git a/source/RobotAPI/libraries/aron/test/CMakeLists.txt b/source/RobotAPI/libraries/aron/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3a199acf0f19c13f6253e66947e540bfe3950efd
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/test/CMakeLists.txt
@@ -0,0 +1,25 @@
+set(LIB_NAME arontest)
+
+armarx_component_set_name("${LIB_NAME}")
+armarx_set_target("Library: ${LIB_NAME}")
+
+
+armarx_add_library(
+    LIBS
+        # ArmarXCore
+        ArmarXCore
+        # RobotAPI
+        RobotAPICore
+        RobotAPIInterfaces
+        aron
+
+    HEADERS
+        AronConversionTester.h
+
+    SOURCES
+        AronConversionTester.cpp
+)
+
+
+add_library(aron::test ALIAS arontest)
+add_library(${PROJECT_NAME}::Aron::test ALIAS arontest)