diff --git a/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.cpp b/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.cpp
index dd24a1ecc8cc9cf390684902112e5ac44a49c624..2fd894a53894cd778ceabbedab556f185036aee4 100644
--- a/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.cpp
+++ b/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.cpp
@@ -8,10 +8,24 @@
 #include <RobotAPI/libraries/core/Pose.h>
 #include <RobotAPI/libraries/core/FramedPose.h>
 
+#include "ice_conversions.h"
+
 
 namespace armarx::objpose
 {
 
+    Eigen::Vector3f toEigen(const Vector3BasePtr pose)
+    {
+        auto cast = Vector3Ptr::dynamicCast(pose);
+        ARMARX_CHECK_NOT_NULL(cast);
+        return cast->toEigen();
+    }
+    Eigen::Matrix3f toEigen(const QuaternionBasePtr pose)
+    {
+        auto cast = QuaternionPtr::dynamicCast(pose);
+        ARMARX_CHECK_NOT_NULL(cast);
+        return cast->toEigen();
+    }
     Eigen::Matrix4f toEigen(const PoseBasePtr pose)
     {
         auto cast = PosePtr::dynamicCast(pose);
@@ -32,7 +46,7 @@ namespace armarx::objpose
     {
         providerName = ice.providerName;
         objectType = ice.objectType;
-        objectID = ice.objectID;
+        objectID = { ice.objectID.dataset, ice.objectID.name };
 
         objectPoseRobot = toEigen(ice.objectPoseRobot);
         objectPoseGlobal = toEigen(ice.objectPoseGlobal);
@@ -45,7 +59,7 @@ namespace armarx::objpose
         confidence = ice.confidence;
         timestamp = IceUtil::Time::microSeconds(ice.timestampMicroSeconds);
 
-        localOOBB = ice.localOOBB;
+        objpose::fromIce(ice.localOOBB, localOOBB);
     }
 
     data::ObjectPose ObjectPose::toIce() const
@@ -59,7 +73,7 @@ namespace armarx::objpose
     {
         ice.providerName = providerName;
         ice.objectType = objectType;
-        ice.objectID = objectID;
+        ice.objectID = { objectID.dataset(), objectID.name() };
 
         ice.objectPoseRobot = new Pose(objectPoseRobot);
         ice.objectPoseGlobal = new Pose(objectPoseGlobal);
@@ -72,7 +86,7 @@ namespace armarx::objpose
         ice.confidence = confidence;
         ice.timestampMicroSeconds = timestamp.toMicroSeconds();
 
-        ice.localOOBB = localOOBB;
+        objpose::toIce(ice.localOOBB, localOOBB);
     }
 
     void ObjectPose::fromProvidedPose(const data::ProvidedObjectPose& provided, VirtualRobot::RobotPtr robot)
@@ -80,7 +94,7 @@ namespace armarx::objpose
         providerName = provided.providerName;
         objectType = provided.objectType;
 
-        objectID = provided.objectID;
+        objectID = { provided.objectID.dataset, provided.objectID.name };
 
         objectPoseOriginal = toEigen(provided.objectPose);
         objectPoseOriginalFrame = provided.objectPoseFrame;
@@ -97,7 +111,7 @@ namespace armarx::objpose
         confidence = provided.confidence;
         timestamp = IceUtil::Time::microSeconds(provided.timestampMicroSeconds);
 
-        localOOBB = provided.localOOBB;
+        objpose::fromIce(provided.localOOBB, localOOBB);
     }
 
 }
@@ -158,3 +172,7 @@ namespace armarx
 
 }
 
+
+
+
+
diff --git a/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.h b/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.h
index 6a237e319daaae4c6e43ff0d6cd933990d2ae2f2..814ba29c50b3021663bf3d50163eea65f8aced9f 100644
--- a/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.h
+++ b/source/RobotAPI/components/ObjectPoseObserver/ObjectPose.h
@@ -2,11 +2,13 @@
 
 #include <Eigen/Core>
 
+#include <SimoxUtility/shapes/OrientedBox.h>
 #include <VirtualRobot/VirtualRobot.h>
 
 #include <IceUtil/Time.h>
 
 #include <RobotAPI/interface/objectpose/types.h>
+#include <RobotAPI/libraries/ArmarXObjects/ObjectID.h>
 
 
 namespace armarx::objpose
@@ -34,7 +36,7 @@ namespace armarx::objpose
         ObjectTypeEnum objectType = AnyObject;
 
         /// The object ID, i.e. dataset and name.
-        ObjectID objectID;
+        armarx::ObjectID objectID;
 
         Eigen::Matrix4f objectPoseRobot;
         Eigen::Matrix4f objectPoseGlobal;
@@ -50,7 +52,7 @@ namespace armarx::objpose
         IceUtil::Time timestamp = IceUtil::Time::microSeconds(-1);
 
         /// Object bounding box in object's local coordinate frame.
-        Box localOOBB;
+        simox::OrientedBoxf localOOBB;
     };
     using ObjectPoseSeq = std::vector<ObjectPose>;
 
diff --git a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp
index c9e12b8e92a20aed2b2c9a1d60b5b75d3dd7f2a7..f7d2eee97b3ec601420ce9cf010af23817814dd7 100644
--- a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp
+++ b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp
@@ -23,7 +23,9 @@
 #include "ObjectPoseObserver.h"
 
 #include <SimoxUtility/algorithm/get_map_keys_values.h>
+#include <SimoxUtility/math/pose/pose.h>
 #include <SimoxUtility/meta/EnumNames.hpp>
+#include <SimoxUtility/shapes/OrientedBox.h>
 
 #include <VirtualRobot/Robot.h>
 #include <VirtualRobot/RobotConfig.h>
@@ -57,7 +59,6 @@ namespace armarx
         return defs;
     }
 
-
     std::string ObjectPoseObserver::getDefaultName() const
     {
         return "ObjectPoseObserver";
@@ -71,6 +72,9 @@ namespace armarx
     void ObjectPoseObserver::onConnectObserver()
     {
         this->robot = RobotState::addRobot("robot", VirtualRobot::RobotIO::RobotDescription::eStructure);
+
+        createRemoteGuiTab();
+        RemoteGui_startRunningTask();
     }
 
     void ObjectPoseObserver::onDisconnectComponent()
@@ -82,42 +86,50 @@ namespace armarx
     }
 
 
-    void ObjectPoseObserver::reportProviderAvailable(const std::string& providerName, const Ice::Current&)
+    void ObjectPoseObserver::createRemoteGuiTab()
     {
-        objpose::ObjectPoseProviderPrx provider = getProviderProxy(providerName);
-        if (!provider)
+        using namespace armarx::RemoteGui::Client;
+
+        tab.visuEnabled.setValue(visu.enabled);
+        tab.visuInGlobalFrame.setValue(visu.inGlobalFrame);
+        tab.visuAlpha.setRange(0, 1.0);
+        tab.visuAlpha.setValue(visu.alpha);
+        tab.visuOOBBs.setValue(visu.oobbs);
+
+        GroupBox visuGroup;
         {
-            ARMARX_WARNING << "Received availability signal from provider '" << providerName << "', "
-                           << "but could not get provider proxy.";
-            return;
+            GridLayout grid;
+            int row = 0;
+            grid.add(Label("Enabled"), {row, 0}).add(tab.visuEnabled, {row, 1});
+            row++;
+            grid.add(Label("Global Frame"), {row, 0}).add(tab.visuInGlobalFrame, {row, 1});
+            row++;
+            grid.add(Label("Alpha"), {row, 0}).add(tab.visuAlpha, {row, 1});
+            row++;
+            grid.add(Label("OOBB"), {row, 0}).add(tab.visuOOBBs, {row, 1});
+            row++;
+
+            visuGroup.setLabel("Visualization");
+            visuGroup.addChild(grid);
         }
-        objpose::ProviderInfo info = provider->getProviderInfo();
 
-        {
-            std::scoped_lock lock(dataMutex);
-            if (providers.count(providerName) == 0)
-            {
-                std::stringstream ss;
-                for (const auto& id : info.supportedObjects)
-                {
-                    ss << "- " << id << "\n";
-                }
-                ARMARX_VERBOSE << "New provider '" << providerName << "' available.\n"
-                               << "Supported objects: \n" << ss.str();
-            }
-            providers[providerName] = info;
+        VBoxLayout root = {visuGroup, VSpacer()};
+        RemoteGui_createTab(getName(), root, &tab);
+    }
 
-            if (updateCounters.count(providerName) == 0)
-            {
-                updateCounters[providerName] = 0;
-            }
-        }
+    void ObjectPoseObserver::RemoteGui_update()
+    {
+        visu.enabled = tab.visuEnabled.getValue();
+        visu.inGlobalFrame = tab.visuInGlobalFrame.getValue();
+        visu.alpha = tab.visuAlpha.getValue();
+        visu.oobbs = tab.visuOOBBs.getValue();
+    }
 
-        if (!existsChannel(providerName))
-        {
-            offerChannel(providerName, "Channel of provider '" + providerName + "'.");
-        }
-        offerOrUpdateDataField(providerName, "objectType", objpose::ObjectTypeEnumNames.to_name(info.objectType), "");
+
+    void ObjectPoseObserver::reportProviderAvailable(const std::string& providerName, const Ice::Current&)
+    {
+        static const bool refresh = true;
+        fetchProviderInfo(providerName, refresh);
     }
 
     void ObjectPoseObserver::reportObjectPoses(
@@ -125,10 +137,13 @@ namespace armarx
     {
         ARMARX_VERBOSE << "Received object poses from '" << providerName << "'.";
 
+        static const bool refresh = false;
+        fetchProviderInfo(providerName, refresh);
+
         objpose::ObjectPoseSeq objectPoses;
         {
             std::scoped_lock lock(dataMutex);
-            synchronizeLocalClone(robot);
+            RobotState::synchronizeLocalClone("robot");
 
             if (robot->hasRobotNode(calibration.robotNode))
             {
@@ -141,6 +156,10 @@ namespace armarx
             {
                 objpose::ObjectPose& pose = objectPoses.emplace_back();
                 pose.fromProvidedPose(provided, robot);
+                if (!(provided.localOOBB.position && provided.localOOBB.orientation && provided.localOOBB.extents))
+                {
+                    pose.localOOBB = getObjectOOBB(pose.objectID);
+                }
             }
         }
 
@@ -152,9 +171,59 @@ namespace armarx
     }
 
 
+    void ObjectPoseObserver::fetchProviderInfo(const std::string& providerName, bool refresh)
+    {
+        {
+            std::scoped_lock lock(dataMutex);
+            if (!refresh && providers.count(providerName) > 0)
+            {
+                return;  // Do nothing.
+            }
+        }
+
+        objpose::ObjectPoseProviderPrx provider = getProviderProxy(providerName);
+        if (!provider)
+        {
+            ARMARX_WARNING << "Received availability signal from provider '" << providerName << "', "
+                           << "but could not get provider proxy.";
+            return;
+        }
+        objpose::ProviderInfo info = provider->getProviderInfo();
+
+        {
+            std::scoped_lock lock(dataMutex);
+            if (providers.count(providerName) == 0)
+            {
+                std::stringstream ss;
+                for (const auto& id : info.supportedObjects)
+                {
+                    ss << "- " << id << "\n";
+                }
+                ARMARX_VERBOSE << "New provider '" << providerName << "' available.\n"
+                               << "Supported objects: \n" << ss.str();
+            }
+            providers[providerName] = info;
+
+            if (updateCounters.count(providerName) == 0)
+            {
+                updateCounters[providerName] = 0;
+            }
+        }
+
+        if (!existsChannel(providerName))
+        {
+            offerChannel(providerName, "Channel of provider '" + providerName + "'.");
+        }
+        offerOrUpdateDataField(providerName, "objectType", objpose::ObjectTypeEnumNames.to_name(info.objectType), "");
+    }
+
+
     objpose::data::ObjectPoseSeq ObjectPoseObserver::getObjectPoses(const Ice::Current&)
     {
+        // IceUtil::Time start = IceUtil::Time::now();
         std::scoped_lock lock(dataMutex);
+        // ARMARX_INFO << "Locking took " << (IceUtil::Time::now() - start).toMilliSeconds() << " ms";
+        // start = IceUtil::Time::now();
         objpose::data::ObjectPoseSeq result;
         for (const auto& [name, poses] : objectPoses)
         {
@@ -163,6 +232,7 @@ namespace armarx
                 result.push_back(pose.toIce());
             }
         }
+        // ARMARX_INFO << "getObjectPoses() took " << (IceUtil::Time::now() - start).toMilliSeconds() << " ms";
         return result;
     }
 
@@ -254,20 +324,6 @@ namespace armarx
     }
 
 
-    void ObjectPoseObserver::createRemoteGuiTab()
-    {
-        using namespace armarx::RemoteGui::Client;
-
-        VBoxLayout root = {};
-        RemoteGui_createTab(getName(), root, &tab);
-    }
-
-    void ObjectPoseObserver::RemoteGui_update()
-    {
-
-    }
-
-
     void ObjectPoseObserver::handleProviderUpdate(const std::string& providerName)
     {
         // Initialized to 0 on first access.
@@ -297,27 +353,37 @@ namespace armarx
 
         for (const objpose::ObjectPose& objectPose : objectPoses.at(providerName))
         {
-            const objpose::ObjectID id = objectPose.objectID;
-            std::string key = id.dataset + "/" + id.name;
+            const armarx::ObjectID id = objectPose.objectID;
+            std::string key = id.str();
 
-            std::optional<ObjectInfo> objectInfo = objectFinder.findObject(id.dataset, id.name);
-            if (!objectInfo)
-            {
-                ARMARX_WARNING << "Cannot visualize object '" << key << "'.";
-                continue;
-            }
+            std::optional<ObjectInfo> objectInfo = objectFinder.findObject(id);
 
             Eigen::Matrix4f pose = visu.inGlobalFrame ? objectPose.objectPoseGlobal : objectPose.objectPoseRobot;
 
-            viz::Object object = viz::Object(id.dataset + "/" + id.name)
-                                 .file(objectInfo->package(), objectInfo->simoxXML().relativePath)
-                                 .pose(pose);
-            if (visu.alpha < 1)
+            if (objectInfo)
+            {
+                viz::Object object = viz::Object(key)
+                                     .file(objectInfo->package(), objectInfo->simoxXML().relativePath)
+                                     .pose(pose);
+                if (visu.alpha < 1)
+                {
+                    object.overrideColor(simox::Color::white().with_alpha(visu.alpha));
+                }
+
+                layer.add(object);
+            }
+            else
             {
-                object.overrideColor(simox::Color::white().with_alpha(visu.alpha));
+                ARMARX_WARNING << "Cannot visualize object '" << key << "'.";
             }
 
-            layer.add(object);
+            if (visu.oobbs)
+            {
+                simox::OrientedBox<float> oobb = objectPose.localOOBB;
+                layer.add(viz::Box(key + " OOBB")
+                          .pose(pose * simox::math::pose(oobb.center(), oobb.rotation()))
+                          .size(oobb.dimensions()).color(simox::Color::lime(255, 64)));
+            }
         }
 
         arviz.commit(layer);
@@ -328,5 +394,32 @@ namespace armarx
         return getProxy<objpose::ObjectPoseProviderPrx>(providerName, false, "", false);
     }
 
+    simox::OrientedBoxf ObjectPoseObserver::getObjectOOBB(const ObjectID& id)
+    {
+        if (auto it = oobbCache.find(id); it != oobbCache.end())
+        {
+            return it->second;
+        }
+        else
+        {
+            // Try to get OOBB from repository.
+            simox::OrientedBoxf oobb;
+            if (std::optional<ObjectInfo> objectInfo = objectFinder.findObject(id))
+            {
+                try
+                {
+                    oobb = objectInfo->oobb();
+                }
+                catch (const std::ios_base::failure&)
+                {
+                    // Give up - no OOBB information.
+                    ARMARX_WARNING << "Could not get OOBB of object " << id << ".";
+                }
+            }
+            oobbCache[id] = oobb;  // Store result in any case.
+            return oobb;
+        }
+    }
+
 }
 
diff --git a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h
index 2d6452899537ad83df272130162802062f544700..0c631b1e65bffbfd1199abccc016e98c5f1be724 100644
--- a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h
+++ b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h
@@ -120,11 +120,14 @@ namespace armarx
 
     private:
 
+        void fetchProviderInfo(const std::string& providerName, bool refresh = false);
         void handleProviderUpdate(const std::string& providerName);
         void visProviderUpdate(const std::string& providerName);
 
         objpose::ObjectPoseProviderPrx getProviderProxy(const std::string& providerName);
 
+        simox::OrientedBoxf getObjectOOBB(const ObjectID& id);
+
 
     private:
 
@@ -140,13 +143,14 @@ namespace armarx
 
 
         ObjectFinder objectFinder;
+        std::map<ObjectID, simox::OrientedBox<float>> oobbCache;
 
         struct Visu
         {
             bool enabled = false;
             bool inGlobalFrame = true;
-
             float alpha = 1.0;
+            bool oobbs = false;
         };
         Visu visu;
 
@@ -160,7 +164,10 @@ namespace armarx
 
         struct RemoteGuiTab : RemoteGui::Client::Tab
         {
-
+            RemoteGui::Client::CheckBox visuEnabled;
+            RemoteGui::Client::CheckBox visuInGlobalFrame;
+            RemoteGui::Client::FloatSlider visuAlpha;
+            RemoteGui::Client::CheckBox visuOOBBs;
         };
         RemoteGuiTab tab;
     };
diff --git a/source/RobotAPI/components/ObjectPoseObserver/ProvidedObjectPose.cpp b/source/RobotAPI/components/ObjectPoseObserver/ProvidedObjectPose.cpp
deleted file mode 100644
index ecf05de2b31c102245e4fccfbf9663d43ab19f9b..0000000000000000000000000000000000000000
--- a/source/RobotAPI/components/ObjectPoseObserver/ProvidedObjectPose.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "ProvidedObjectPose.h"
-
-
-namespace armarx::objpose
-{
-    ProvidedObjectPose::ProvidedObjectPose(const data::ProvidedObjectPose& ice)
-    {
-
-    }
-}
-
-
diff --git a/source/RobotAPI/components/ObjectPoseObserver/ProvidedObjectPose.h b/source/RobotAPI/components/ObjectPoseObserver/ProvidedObjectPose.h
deleted file mode 100644
index afc47fe515c61511c1ab225df37b2c96a48eaa2f..0000000000000000000000000000000000000000
--- a/source/RobotAPI/components/ObjectPoseObserver/ProvidedObjectPose.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <Eigen/Core>
-
-#include <RobotAPI/interface/objectpose/types.h>
-
-
-namespace armarx::objpose
-{
-
-    struct ProvidedObjectPose
-    {
-        ProvidedObjectPose();
-        ProvidedObjectPose(const data::ProvidedObjectPose& ice);
-
-
-        /// Name of the providing component.
-        std::string providerName;
-        /// Known or unknown object.
-        ObjectTypeEnum objectType = AnyObject;
-
-        /// The object ID, i.e. dataset and name.
-        ObjectID objectID;
-
-        /// Pose in `objectPoseFrame`.
-        Eigen::Matrix4f objectPose;
-        std::string objectPoseFrame;
-
-        /// Confidence in [0, 1] (1 = full, 0 = none).
-        float confidence = 0;
-        /// Source timestamp.
-        long timestampMicroSeconds = -1;
-
-        /// Object bounding box in object's local coordinate frame.
-        Box localOOBB;
-    };
-
-
-
-
-}
diff --git a/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.cpp b/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.cpp
index 0085e76c8cca45298aad7c3cf11617c08c3e4ed8..506fc289280788d783f46455ead20a2ded115454 100644
--- a/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.cpp
+++ b/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.cpp
@@ -15,6 +15,31 @@
 namespace armarx
 {
 
+    Eigen::Vector3f toEigen(const Vector3BasePtr pose)
+    {
+        auto cast = Vector3Ptr::dynamicCast(pose);
+        ARMARX_CHECK_NOT_NULL(cast);
+        return cast->toEigen();
+    }
+    Eigen::Matrix3f toEigen(const QuaternionBasePtr pose)
+    {
+        auto cast = QuaternionPtr::dynamicCast(pose);
+        ARMARX_CHECK_NOT_NULL(cast);
+        return cast->toEigen();
+    }
+    Eigen::Matrix4f toEigen(const PoseBasePtr pose)
+    {
+        auto cast = PosePtr::dynamicCast(pose);
+        ARMARX_CHECK_NOT_NULL(cast);
+        return cast->toEigen();
+    }
+
+
+    std::string objpose::getID(const ObjectID& id)
+    {
+        return id.dataset + "/" + id.name;
+    }
+
     std::ostream& objpose::operator<<(std::ostream& os, const ObjectID& id)
     {
         return os << "'" << id.dataset << "/" << id.name << "'";
@@ -36,16 +61,53 @@ namespace armarx
         return ice;
     }
 
-    objpose::Box objpose::toIce(const simox::OrientedBox<float>& oobb)
+
+    void objpose::fromIce(const Box& box, simox::OrientedBoxf& oobb)
     {
-        objpose::Box ice;
-        ice.position = new Vector3(oobb.center());
-        ice.orientation = new Quaternion(oobb.rotation().eval());
-        ice.extents = new Vector3(oobb.dimensions());
-        return ice;
+        try
+        {
+            Eigen::Vector3f pos = toEigen(box.position);
+            Eigen::Matrix3f ori = toEigen(box.orientation);
+            Eigen::Vector3f extents = toEigen(box.extents);
+            Eigen::Vector3f corner = pos - ori * extents / 2;
+
+            oobb = simox::OrientedBox<float> (corner,
+                                              ori.col(0) * extents(0),
+                                              ori.col(1) * extents(1),
+                                              ori.col(2) * extents(2));
+        }
+        catch (const armarx::LocalException&)
+        {
+            // No OOBB information.
+            oobb = {};
+        }
+    }
+
+    simox::OrientedBoxf objpose::fromIce(const Box& box)
+    {
+        simox::OrientedBoxf oobb;
+        fromIce(box, oobb);
+        return oobb;
+    }
+
+    void objpose::toIce(Box& box, const simox::OrientedBoxf& oobb)
+    {
+        box.position = new Vector3(oobb.center());
+        box.orientation = new Quaternion(oobb.rotation().eval());
+        box.extents = new Vector3(oobb.dimensions());
     }
 
+    objpose::Box objpose::toIce(const simox::OrientedBoxf& oobb)
+    {
+        objpose::Box box;
+        toIce(box, oobb);
+        return box;
+    }
+
+
 }
 
 
 
+
+
diff --git a/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.h b/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.h
index d71b34cae0b1e09f19ea32e37e5c5564aa70e2ea..b459a01cd9a7411cdbbb1c32dc2a15394c0dee25 100644
--- a/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.h
+++ b/source/RobotAPI/components/ObjectPoseObserver/ice_conversions.h
@@ -19,12 +19,19 @@ namespace simox
 namespace armarx::objpose
 {
 
+    std::string getID(const ObjectID& id);
     std::ostream& operator<<(std::ostream& os, const ObjectID& id);
 
     extern const simox::meta::EnumNames<objpose::ObjectTypeEnum> ObjectTypeEnumNames;
 
 
     objpose::AABB toIce(const simox::AxisAlignedBoundingBox& aabb);
-    objpose::Box toIce(const simox::OrientedBox<float>& oobb);
+
+
+    void fromIce(const Box& box, simox::OrientedBox<float>& oobb);
+    simox::OrientedBox<float> fromIce(const Box& box);
+
+    void toIce(Box& box, const simox::OrientedBox<float>& oobb);
+    Box toIce(const simox::OrientedBox<float>& oobb);
 
 }
diff --git a/source/RobotAPI/components/ObjectPoseObserver/test/ObjectPoseObserverTest.cpp b/source/RobotAPI/components/ObjectPoseObserver/test/ObjectPoseObserverTest.cpp
index c75c9d76a5398d870a06ad06597f405ade209cbb..88d11dc86bbd10ed4f324ee2b281c1b969583319 100644
--- a/source/RobotAPI/components/ObjectPoseObserver/test/ObjectPoseObserverTest.cpp
+++ b/source/RobotAPI/components/ObjectPoseObserver/test/ObjectPoseObserverTest.cpp
@@ -27,11 +27,42 @@
 #include <RobotAPI/Test.h>
 #include <RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h>
 
+#include <RobotAPI/components/ObjectPoseObserver/ice_conversions.h>
+#include <RobotAPI/libraries/core/Pose.h>
+
 #include <iostream>
 
-BOOST_AUTO_TEST_CASE(testExample)
+using namespace armarx;
+
+
+BOOST_AUTO_TEST_CASE(test_from_to_OOBB)
 {
-    armarx::ObjectPoseObserver instance;
+    Eigen::Vector3f pos(-100, -200, -300);
+    Eigen::Matrix3f ori = Eigen::AngleAxisf(1.0, Eigen::Vector3f(1, 2, 3).normalized()).toRotationMatrix();
+    Eigen::Vector3f extents(40, 50, 60);
+
+    armarx::objpose::Box box;
+    box.position = new Vector3(pos);
+    box.orientation = new Quaternion(ori);
+    box.extents = new Vector3(extents);
+
+
+    const float prec = 1e-3;
+
+    simox::OrientedBoxf oobb;
+    armarx::objpose::fromIce(box, oobb);
+    ARMARX_CHECK_LESS_EQUAL((oobb.center() - pos).norm(), prec);
+    ARMARX_CHECK(oobb.rotation().isApprox(ori, prec));
+    ARMARX_CHECK_LESS_EQUAL((oobb.dimensions() - extents).norm(), prec);
+
+    armarx::objpose::Box boxOut;
+    armarx::objpose::toIce(boxOut, oobb);
+
+    Eigen::Vector3f posOut = Vector3Ptr::dynamicCast(boxOut.position)->toEigen();
+    Eigen::Matrix3f oriOut = QuaternionPtr::dynamicCast(boxOut.orientation)->toEigen();
+    Eigen::Vector3f extentsOut = Vector3Ptr::dynamicCast(boxOut.extents)->toEigen();
 
-    BOOST_CHECK_EQUAL(true, true);
+    ARMARX_CHECK_LESS_EQUAL((posOut - pos).norm(), prec);
+    ARMARX_CHECK(oriOut.isApprox(ori, prec));
+    ARMARX_CHECK_LESS_EQUAL((extentsOut - extents).norm(), prec);
 }
diff --git a/source/RobotAPI/gui-plugins/CMakeLists.txt b/source/RobotAPI/gui-plugins/CMakeLists.txt
index 95c3e16c4995fd97aaaf7fb43b1aea114fc3d3f8..efa75d05dcf3051fcdb7ae16d29b151bcf4ebee4 100644
--- a/source/RobotAPI/gui-plugins/CMakeLists.txt
+++ b/source/RobotAPI/gui-plugins/CMakeLists.txt
@@ -15,3 +15,4 @@ add_subdirectory(CartesianWaypointControlGui)
 add_subdirectory(DebugDrawerGuiPlugin)
 add_subdirectory(ArViz)
 add_subdirectory(CartesianNaturalPositionController)
+add_subdirectory(ObjectPoseGui)
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/CMakeLists.txt b/source/RobotAPI/gui-plugins/ObjectPoseGui/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d4d29bd3e4181aa055f7dee976a778f75d42a877
--- /dev/null
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/CMakeLists.txt
@@ -0,0 +1,41 @@
+armarx_set_target("ObjectPoseGuiPlugin")
+
+# most qt components will be linked against in the call armarx_gui_library
+#armarx_find_qt(QtCore QtGui QtDesigner)
+
+# ArmarXGui gets included through depends_on_armarx_package(ArmarXGui "OPTIONAL")
+# in the toplevel CMakeLists.txt
+armarx_build_if(ArmarXGui_FOUND "ArmarXGui not available")
+
+set(SOURCES
+    ObjectPoseGuiPlugin.cpp ObjectPoseGuiWidgetController.cpp
+)
+
+# do not rename this variable, it is used in armarx_gui_library()...
+set(HEADERS
+    ObjectPoseGuiPlugin.h ObjectPoseGuiWidgetController.h
+)
+
+set(GUI_MOC_HDRS ${HEADERS})
+
+set(GUI_UIS
+    ObjectPoseGuiWidget.ui
+)
+
+# Add more libraries you depend on here, e.g. ${QT_LIBRARIES}.
+set(COMPONENT_LIBS
+    SimpleConfigDialog
+    ObjectPoseObserver
+)
+
+if(ArmarXGui_FOUND)
+    armarx_gui_library(ObjectPoseGuiPlugin "${SOURCES}" "${GUI_MOC_HDRS}" "${GUI_UIS}" "" "${COMPONENT_LIBS}")
+
+    #find_package(MyLib QUIET)
+    #armarx_build_if(MyLib_FOUND "MyLib not available")
+    # all target_include_directories must be guarded by if(Xyz_FOUND)
+    # for multiple libraries write: if(X_FOUND AND Y_FOUND)....
+    #if(MyLib_FOUND)
+    #    target_include_directories(ObjectPoseGuiPlugi PUBLIC ${MyLib_INCLUDE_DIRS})
+    #endif()
+endif()
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiPlugin.cpp b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiPlugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..01cff7dcbe628fc900c62a5ad74c75b3aa6fab4d
--- /dev/null
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiPlugin.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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::gui-plugins::ObjectPoseGuiPlugin
+ * \author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * \date       2020
+ * \copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#include "ObjectPoseGuiPlugin.h"
+
+#include "ObjectPoseGuiWidgetController.h"
+
+namespace armarx
+{
+    ObjectPoseGuiPlugin::ObjectPoseGuiPlugin()
+    {
+        addWidget < ObjectPoseGuiWidgetController > ();
+    }
+}
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiPlugin.h b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiPlugin.h
new file mode 100644
index 0000000000000000000000000000000000000000..69625e6dc30760ebca74ba87db6b9338316185ac
--- /dev/null
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiPlugin.h
@@ -0,0 +1,50 @@
+/*
+ * 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::gui-plugins::ObjectPoseGui
+ * \author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * \date       2020
+ * \copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+#pragma once
+
+#include <ArmarXCore/core/system/ImportExportComponent.h>
+#include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXGuiPlugin.h>
+#include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h>
+
+namespace armarx
+{
+    /**
+     * \class ObjectPoseGuiPlugin
+     * \ingroup ArmarXGuiPlugins
+     * \brief ObjectPoseGuiPlugin brief description
+     *
+     * Detailed description
+     */
+    class ARMARXCOMPONENT_IMPORT_EXPORT ObjectPoseGuiPlugin:
+        public armarx::ArmarXGuiPlugin
+    {
+        Q_OBJECT
+        Q_INTERFACES(ArmarXGuiInterface)
+        Q_PLUGIN_METADATA(IID "ArmarXGuiInterface/1.00")
+    public:
+        /**
+         * All widgets exposed by this plugin are added in the constructor
+         * via calls to addWidget()
+         */
+        ObjectPoseGuiPlugin();
+    };
+}
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..83f77d3ae4886eaced3509fa4c1af1c62a21d80f
--- /dev/null
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ObjectPoseGuiWidget</class>
+ <widget class="QWidget" name="ObjectPoseGuiWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>ObjectPoseGuiWidget</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="objectsGroupBox">
+     <property name="title">
+      <string>Objects</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QPushButton" name="updateObjectsButton">
+          <property name="text">
+           <string>Update</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="autoUpdateCheckBox">
+          <property name="text">
+           <string>Auto Update</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QTableWidget" name="objectsTable"/>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..816e797f42714e8da58658d7bde649241fac6cf8
--- /dev/null
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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::gui-plugins::ObjectPoseGuiWidgetController
+ * \author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * \date       2020
+ * \copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#include "ObjectPoseGuiWidgetController.h"
+
+#include <string>
+
+#include <QTimer>
+
+#include <RobotAPI/components/ObjectPoseObserver/ice_conversions.h>
+#include <RobotAPI/components/ObjectPoseObserver/ObjectPose.h>
+
+
+namespace armarx
+{
+
+    ObjectPoseGuiWidgetController::ObjectPoseGuiWidgetController()
+    {
+        widget.setupUi(getWidget());
+
+        QStringList header = {"Dataset", "Name", "Provider", "Type"};
+        widget.objectsTable->setColumnCount(header.size());
+        widget.objectsTable->setHorizontalHeaderLabels(header);
+
+        using This = ObjectPoseGuiWidgetController;
+
+        connect(widget.updateObjectsButton, &QPushButton::clicked, this, &This::updateObjects);
+        // QTimer* timer = new QTimer(this,);
+    }
+
+
+    ObjectPoseGuiWidgetController::~ObjectPoseGuiWidgetController()
+    {
+
+    }
+
+
+    void ObjectPoseGuiWidgetController::loadSettings(QSettings* settings)
+    {
+        (void) settings;
+    }
+
+    void ObjectPoseGuiWidgetController::saveSettings(QSettings* settings)
+    {
+        (void) settings;
+    }
+
+    QString ObjectPoseGuiWidgetController::GetWidgetName()
+    {
+        return "ObjectPoseGui";
+    }
+
+    static const std::string CONFIG_KEY_OBJECT_POSE_OBSERVER = "ObjectPoseObserver";
+
+    QPointer<QDialog> ObjectPoseGuiWidgetController::getConfigDialog(QWidget* parent)
+    {
+        if (!configDialog)
+        {
+            configDialog = new SimpleConfigDialog(parent);
+            configDialog->addProxyFinder<armarx::objpose::ObjectPoseObserverInterfacePrx>({CONFIG_KEY_OBJECT_POSE_OBSERVER, "Object pose observer.", "ObjectPoseObserver"});
+        }
+        return qobject_cast<QDialog*>(configDialog);
+    }
+
+    void ObjectPoseGuiWidgetController::configured()
+    {
+        if (configDialog)
+        {
+            objectPoseObserverName = configDialog->getProxyName(CONFIG_KEY_OBJECT_POSE_OBSERVER);
+        }
+    }
+
+    void ObjectPoseGuiWidgetController::onInitComponent()
+    {
+        if (!objectPoseObserverName.empty())
+        {
+            usingProxy(objectPoseObserverName);
+        }
+    }
+
+    void ObjectPoseGuiWidgetController::onConnectComponent()
+    {
+        if (!objectPoseObserverName.empty())
+        {
+            getProxy(objectPoseObserver, objectPoseObserverName);
+        }
+    }
+
+    void ObjectPoseGuiWidgetController::updateObjects()
+    {
+        if (!objectPoseObserver)
+        {
+            return;
+        }
+
+        ARMARX_INFO << "Get object poses...";
+        // const objpose::ObjectPoseSeq objectPoses = objpose::fromIce(objectPoseObserver->getObjectPoses());
+
+        const objpose::data::ObjectPoseSeq objectPoses = objectPoseObserver->getObjectPoses();
+        ARMARX_INFO << "Got " << objectPoses.size() << " object poses.";
+
+        widget.objectsTable->setRowCount(int(objectPoses.size()));
+
+        for (int i = 0; i < int(objectPoses.size()); ++i)
+        {
+            const objpose::data::ObjectPose& pose = objectPoses.at(size_t(i));
+            int col = 0;
+
+            widget.objectsTable->setItem(
+                i, col++, new QTableWidgetItem(pose.objectID.dataset.c_str()));
+            widget.objectsTable->setItem(
+                i, col++, new QTableWidgetItem(pose.objectID.name.c_str()));
+            widget.objectsTable->setItem(
+                i, col++, new QTableWidgetItem(pose.providerName.c_str()));
+            widget.objectsTable->setItem(
+                i, col++, new QTableWidgetItem(objpose::ObjectTypeEnumNames.to_name(pose.objectType).c_str()));
+
+        }
+    }
+
+}
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.h b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.h
new file mode 100644
index 0000000000000000000000000000000000000000..d9b716d7cc4186cbbff58858aaf25088726fa283
--- /dev/null
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.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/>.
+ *
+ * @package    RobotAPI::gui-plugins::ObjectPoseGuiWidgetController
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2020
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+#pragma once
+
+#include <RobotAPI/gui-plugins/ObjectPoseGui/ui_ObjectPoseGuiWidget.h>
+
+#include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXGuiPlugin.h>
+#include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h>
+#include <ArmarXGui/libraries/SimpleConfigDialog/SimpleConfigDialog.h>
+
+#include <ArmarXCore/core/system/ImportExportComponent.h>
+
+#include <RobotAPI/interface/objectpose/ObjectPoseObserver.h>
+
+
+namespace armarx
+{
+    /**
+    @page RobotAPI-GuiPlugins-ObjectPoseGui ObjectPoseGui
+    @brief The ObjectPoseGui allows visualizing ...
+
+    @image html ObjectPoseGui.png
+    The user can
+
+    API Documentation @ref ObjectPoseGuiWidgetController
+
+    @see ObjectPoseGuiGuiPlugin
+    */
+
+    /**
+     * @class ObjectPoseGuiWidgetController
+     * @brief ObjectPoseGuiWidgetController brief one line description
+     *
+     * Detailed description
+     */
+    class ARMARXCOMPONENT_IMPORT_EXPORT
+        ObjectPoseGuiWidgetController:
+        public armarx::ArmarXComponentWidgetControllerTemplate < ObjectPoseGuiWidgetController >
+    {
+        Q_OBJECT
+
+    public:
+        /// Controller Constructor
+        explicit ObjectPoseGuiWidgetController();
+
+        /// Controller destructor
+        virtual ~ObjectPoseGuiWidgetController() override;
+
+        /// Returns the Widget name displayed in the ArmarXGui to create an instance of this class.
+        static QString GetWidgetName();
+
+
+        /// @see ArmarXWidgetController::loadSettings()
+        void loadSettings(QSettings* settings) override;
+        /// @see ArmarXWidgetController::saveSettings()
+        void saveSettings(QSettings* settings) override;
+
+
+        QPointer<QDialog> getConfigDialog(QWidget* parent) override;
+        void configured() override;
+
+
+        /// @see armarx::Component::onInitComponent()
+        void onInitComponent() override;
+
+        /// @see armarx::Component::onConnectComponent()
+        void onConnectComponent() override;
+
+
+    public slots:
+        /* QT slot declarations */
+
+        void updateObjects();
+
+
+    signals:
+        /* QT signal declarations */
+
+    private:
+
+        /// Widget Form
+        Ui::ObjectPoseGuiWidget widget;
+
+        QPointer<SimpleConfigDialog> configDialog;
+
+
+        std::string objectPoseObserverName;
+        armarx::objpose::ObjectPoseObserverInterfacePrx objectPoseObserver;
+
+
+    };
+}
+
+
diff --git a/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt b/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt
index c819dd3e7e82293c031e5c6d19267cb9a2b1c533..07ae9ab2f69565288eedace181c8a790506b0800 100644
--- a/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt
+++ b/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt
@@ -10,14 +10,16 @@ set(LIBS
 set(LIB_FILES
     ArmarXObjects.cpp
 
-    ObjectFinder.cpp
+    ObjectID.cpp
     ObjectInfo.cpp
+    ObjectFinder.cpp
 )
 set(LIB_HEADERS
     ArmarXObjects.h
 
-    ObjectFinder.h
+    ObjectID.h
     ObjectInfo.h
+    ObjectFinder.h
 )
 
 
diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp
index b7a74d925c6fc005cd506fff1f5a28558106c752..28634f811f1147c77dbe04271165ae4d86f45aec 100644
--- a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp
+++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp
@@ -4,7 +4,6 @@
 
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
 #include <ArmarXCore/core/system/cmake/CMakePackageFinder.h>
-#include <ArmarXCore/core/util/StringHelpers.h>
 
 
 namespace armarx
@@ -67,18 +66,12 @@ namespace armarx
 
     std::optional<ObjectInfo> ObjectFinder::findObject(const std::string& nameOrID) const
     {
-        init();
-        if (nameOrID.find("/") != nameOrID.npos)
-        {
-            const std::vector<std::string> split = armarx::split(nameOrID, "/", true);
-            ARMARX_CHECK_EQUAL(split.size(), 2) << "Expected ID of format 'Dataset/Name', but got: '" << nameOrID
-                                                << "' (too many '/').";
-            return findObject(split[0], split[1]);
-        }
-        else
-        {
-            return findObject("", nameOrID);
-        }
+        return findObject(ObjectID(nameOrID));
+    }
+
+    std::optional<ObjectInfo> ObjectFinder::findObject(const ObjectID& id) const
+    {
+        return findObject(id.dataset(), id.name());
     }
 
     std::vector<std::string> ObjectFinder::getDatasets() const
diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h
index e7f3170c1954e810a077c88c0ea2f3aa1cc9f898..80907d7b4a4fd5cdaf12d7c6372576987ee2df6e 100644
--- a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h
+++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h
@@ -25,6 +25,7 @@ namespace armarx
 
         std::optional<ObjectInfo> findObject(const std::string& dataset, const std::string& name) const;
         std::optional<ObjectInfo> findObject(const std::string& nameOrID) const;
+        std::optional<ObjectInfo> findObject(const ObjectID& id) const;
 
 
         std::vector<std::string> getDatasets() const;
diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectID.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectID.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f24af1a926487d44b5efca3135813ddf70416ceb
--- /dev/null
+++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectID.cpp
@@ -0,0 +1,52 @@
+#include "ObjectID.h"
+
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+#include <ArmarXCore/core/util/StringHelpers.h>
+
+
+namespace armarx
+{
+    ObjectID::ObjectID()
+    {
+    }
+
+    ObjectID::ObjectID(const std::string& dataset, const std::string& name) :
+        _dataset(dataset), _name(name)
+    {
+    }
+
+    ObjectID::ObjectID(const std::string& nameOrID)
+    {
+        if (nameOrID.find("/") != nameOrID.npos)
+        {
+            const std::vector<std::string> split = armarx::split(nameOrID, "/", true);
+            ARMARX_CHECK_EQUAL(split.size(), 2) << "Expected ID of format 'Dataset/Name', but got: '" << nameOrID
+                                                << "' (too many '/').";
+            _dataset = split[0];
+            _name = split[1];
+        }
+        else
+        {
+            // dataset is left empty.
+            _name = nameOrID;
+        }
+    }
+
+    bool ObjectID::operator==(const ObjectID& rhs) const
+    {
+        return _name == rhs._name && _dataset == rhs._dataset;
+    }
+
+    bool ObjectID::operator<(const ObjectID& rhs) const
+    {
+        return _dataset < rhs._dataset
+               || (_dataset == rhs._dataset && _name < rhs._name);
+    }
+
+}
+
+
+std::ostream& armarx::operator<<(std::ostream& os, const ObjectID& id)
+{
+    return os << "'" << id.str() << "'";
+}
diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectID.h b/source/RobotAPI/libraries/ArmarXObjects/ObjectID.h
new file mode 100644
index 0000000000000000000000000000000000000000..437bbaa88e3de76657c42cd52e68799896c53e17
--- /dev/null
+++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectID.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <string>
+
+
+namespace armarx
+{
+    /**
+     * @brief A known object ID of the form "Dataset/Name".
+     */
+    class ObjectID
+    {
+    public:
+
+        ObjectID();
+        ObjectID(const std::string& dataset, const std::string& name);
+        /// Construct from either a name ("myobject") or ID ("mydataset/myobject").
+        ObjectID(const std::string& nameOrID);
+
+
+        std::string dataset() const
+        {
+            return _dataset;
+        }
+        std::string name() const
+        {
+            return _name;
+        }
+        /// Return "dataset/name".
+        std::string str() const
+        {
+            return _dataset + "/" + _name;
+        }
+
+        bool operator==(const ObjectID& rhs) const;
+        inline bool operator!=(const ObjectID& rhs) const
+        {
+            return !operator==(rhs);
+        }
+        bool operator< (const ObjectID& rhs) const;
+        inline bool operator> (const ObjectID& rhs) const
+        {
+            return rhs < (*this);
+        }
+        inline bool operator<=(const ObjectID& rhs) const
+        {
+            return !operator> (rhs);
+        }
+        inline bool operator>=(const ObjectID& rhs) const
+        {
+            return !operator< (rhs);
+        }
+
+
+    private:
+
+        std::string _dataset;
+        std::string _name;
+
+    };
+
+    std::ostream& operator<<(std::ostream& os, const ObjectID& rhs);
+
+
+
+}
+
+
diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp
index feaf2f203aca7445081f2f91e3d511a7b5bbdbd5..83f815b7a7cab582b8605119f69aad5a8758e14f 100644
--- a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp
+++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp
@@ -12,9 +12,15 @@ namespace armarx
     namespace fs = std::filesystem;
 
 
+    ObjectInfo::ObjectInfo(const std::string& packageName, const ObjectInfo::path& packageDataDir,
+                           const ObjectID& id) :
+        _packageName(packageName), _packageDataDir(packageDataDir), _id(id)
+    {
+    }
+
     ObjectInfo::ObjectInfo(const std::string& packageName, const ObjectInfo::path& packageDataDir,
                            const std::string& dataset, const std::string& name) :
-        _packageName(packageName), _packageDataDir(packageDataDir), _dataset(dataset), _name(name)
+        _packageName(packageName), _packageDataDir(packageDataDir), _id(dataset, name)
     {
     }
 
@@ -25,22 +31,27 @@ namespace armarx
 
     std::string ObjectInfo::dataset() const
     {
-        return _dataset;
+        return _id.dataset();
     }
 
     std::string ObjectInfo::name() const
     {
-        return _name;
+        return _id.name();
+    }
+
+    ObjectID ObjectInfo::id() const
+    {
+        return _id;
     }
 
-    std::string ObjectInfo::id() const
+    std::string ObjectInfo::idStr() const
     {
-        return _dataset + "/" + _name;
+        return _id.str();
     }
 
     ObjectInfo::path ObjectInfo::objectDirectory() const
     {
-        return path(_packageName) / _dataset / _name;
+        return path(_packageName) / _id.dataset() / _id.name();
     }
 
     PackageFileLocation ObjectInfo::file(const std::string& _extension, const std::string& suffix) const
@@ -50,7 +61,7 @@ namespace armarx
         {
             extension = "." + extension;
         }
-        std::string filename = _name + suffix + extension;
+        std::string filename = _id.name() + suffix + extension;
 
         PackageFileLocation loc;
         loc.package = _packageName;
@@ -86,10 +97,11 @@ namespace armarx
 
         simox::AxisAlignedBoundingBox aabb(min, max);
 
-        ARMARX_CHECK(aabb.center().isApprox(center)) << aabb.center().transpose() << "\n" << center.transpose();
-        ARMARX_CHECK(aabb.extents().isApprox(extents)) << aabb.extents().transpose() << "\n" << extents.transpose();
-        ARMARX_CHECK(aabb.min().isApprox(min)) << aabb.min().transpose() << "\n" << min.transpose();
-        ARMARX_CHECK(aabb.max().isApprox(max)) << aabb.max().transpose() << "\n" << max.transpose();
+        static const float prec = 1e-4f;
+        ARMARX_CHECK_LESS_EQUAL((aabb.center() - center).norm(), prec) << aabb.center().transpose() << "\n" << center.transpose();
+        ARMARX_CHECK_LESS_EQUAL((aabb.extents() - extents).norm(), prec) << aabb.extents().transpose() << "\n" << extents.transpose();
+        ARMARX_CHECK_LESS_EQUAL((aabb.min() - min).norm(), prec) << aabb.min().transpose() << "\n" << min.transpose();
+        ARMARX_CHECK_LESS_EQUAL((aabb.max() - max).norm(), prec) << aabb.max().transpose() << "\n" << max.transpose();
 
         return aabb;
     }
@@ -109,9 +121,10 @@ namespace armarx
                                        ori.col(1) * extents(1),
                                        ori.col(2) * extents(2));
 
-        ARMARX_CHECK(oobb.center().isApprox(pos)) << oobb.center().transpose() << "\n" << pos.transpose();
-        ARMARX_CHECK(oobb.rotation().isApprox(ori)) << oobb.rotation() << "\n" << ori;
-        ARMARX_CHECK(oobb.dimensions().isApprox(extents)) << oobb.dimensions().transpose() << "\n" << extents.transpose();
+        static const float prec = 1e-3f;
+        ARMARX_CHECK_LESS_EQUAL((oobb.center() - pos).norm(), prec) << oobb.center().transpose() << "\n" << pos.transpose();
+        ARMARX_CHECK(oobb.rotation().isApprox(ori, prec)) << oobb.rotation() << "\n" << ori;
+        ARMARX_CHECK_LESS_EQUAL((oobb.dimensions() - extents).norm(), prec) << oobb.dimensions().transpose() << "\n" << extents.transpose();
         return oobb;
     }
 
@@ -136,11 +149,10 @@ namespace armarx
 
 }
 
-namespace armarx
+
+std::ostream& armarx::operator<<(std::ostream& os, const ObjectInfo& rhs)
 {
-    std::ostream& operator<<(std::ostream& os, const ObjectInfo& rhs)
-    {
-        return os << "'" << rhs.id() << "'";
-    }
+    return os << rhs.id();
 }
 
+
diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h
index 0fd1ef03216ba8fdbb620462306b9779baeabde9..c141bd9d467857c8869b60c2b5c5eebdd48697d4 100644
--- a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h
+++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h
@@ -3,6 +3,8 @@
 #include <filesystem>
 #include <string>
 
+#include "ObjectID.h"
+
 
 namespace simox
 {
@@ -37,9 +39,12 @@ namespace armarx
 
     public:
 
+        ObjectInfo(const std::string& packageName, const path& packageDataDir,
+                   const ObjectID& id);
         ObjectInfo(const std::string& packageName, const path& packageDataDir,
                    const std::string& dataset, const std::string& name);
 
+
         virtual ~ObjectInfo() = default;
 
 
@@ -48,7 +53,8 @@ namespace armarx
         std::string dataset() const;
         std::string name() const;
         /// Return "dataset/name".
-        std::string id() const;
+        ObjectID id() const;
+        std::string idStr() const;
 
         PackageFileLocation file(const std::string& extension, const std::string& suffix = "") const;
 
@@ -79,11 +85,36 @@ namespace armarx
         std::string _packageName;
         path _packageDataDir;
 
-        std::string _dataset;
-        std::string _name;
+        ObjectID _id;
 
     };
 
     std::ostream& operator<<(std::ostream& os, const ObjectInfo& rhs);
 
+
+    inline bool operator==(const ObjectInfo& lhs, const ObjectInfo& rhs)
+    {
+        return lhs.id() == rhs.id();
+    }
+    inline bool operator!=(const ObjectInfo& lhs, const ObjectInfo& rhs)
+    {
+        return lhs.id() != rhs.id();
+    }
+    inline bool operator< (const ObjectInfo& lhs, const ObjectInfo& rhs)
+    {
+        return lhs.id() < rhs.id();
+    }
+    inline bool operator> (const ObjectInfo& lhs, const ObjectInfo& rhs)
+    {
+        return lhs.id() > rhs.id();
+    }
+    inline bool operator<=(const ObjectInfo& lhs, const ObjectInfo& rhs)
+    {
+        return lhs.id() <= rhs.id();
+    }
+    inline bool operator>=(const ObjectInfo& lhs, const ObjectInfo& rhs)
+    {
+        return lhs.id() >= rhs.id();
+    }
+
 }