From 8b2a2101f55f9f4f33336eb81043d24a2ca50678 Mon Sep 17 00:00:00 2001
From: phesch <ulila@student.kit.edu>
Date: Tue, 17 May 2022 21:09:57 +0200
Subject: [PATCH] Move predictObjectPose into ArmarXObjects

---
 .../libraries/ArmarXObjects/CMakeLists.txt    |  2 +
 .../libraries/ArmarXObjects/predictions.cpp   | 96 +++++++++++++++++++
 .../libraries/ArmarXObjects/predictions.h     | 46 +++++++++
 .../armem_objects/server/instance/Segment.cpp | 65 -------------
 .../armem_objects/server/instance/Segment.h   | 15 ---
 .../server/instance/SegmentAdapter.cpp        |  3 +-
 6 files changed, 146 insertions(+), 81 deletions(-)
 create mode 100644 source/RobotAPI/libraries/ArmarXObjects/predictions.cpp
 create mode 100644 source/RobotAPI/libraries/ArmarXObjects/predictions.h

diff --git a/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt b/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt
index 2b2fb5f9d..ec6658fa8 100644
--- a/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt
+++ b/source/RobotAPI/libraries/ArmarXObjects/CMakeLists.txt
@@ -31,6 +31,7 @@ set(LIB_FILES
     plugins/ObjectPoseClientPlugin.cpp
     plugins/RequestedObjects.cpp
 
+    predictions.cpp
     util.cpp
 )
 set(LIB_HEADERS
@@ -57,6 +58,7 @@ set(LIB_HEADERS
     plugins/ObjectPoseClientPlugin.h
     plugins/RequestedObjects.h
 
+    predictions.h
     util.h
 )
 
diff --git a/source/RobotAPI/libraries/ArmarXObjects/predictions.cpp b/source/RobotAPI/libraries/ArmarXObjects/predictions.cpp
new file mode 100644
index 000000000..f3550f874
--- /dev/null
+++ b/source/RobotAPI/libraries/ArmarXObjects/predictions.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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     phesch ( ulila at student dot kit dot edu )
+ * @date       2022
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#include "predictions.h"
+
+#include <SimoxUtility/math/pose/pose.h>
+#include <SimoxUtility/math/regression/linear.h>
+
+#include <ArmarXCore/core/ice_conversions/ice_conversions_templates.h>
+
+namespace armarx::objpose
+{
+
+    objpose::ObjectPosePredictionResult
+    predictObjectPose(const std::map<DateTime, ObjectPose>& poses,
+                      const DateTime& time,
+                      const ObjectPose& latestPose,
+                      const armem::client::PredictionSettings& settings)
+    {
+        objpose::ObjectPosePredictionResult result;
+        result.success = false;
+
+        if (!settings.predictionEngineID.empty() &&
+            settings.predictionEngineID != "Linear Position Regression")
+        {
+            result.errorMessage = "Prediction engine '" + settings.predictionEngineID +
+                                  "' not available for object pose prediction.";
+            return result;
+        }
+
+        const DateTime timeOrigin = DateTime::Now();
+
+        std::vector<double> timestampsSec;
+        std::vector<Eigen::Vector3d> positions;
+
+        // ToDo: How to handle attached objects?
+        for (const auto& [timestamp, pose] : poses)
+        {
+            timestampsSec.push_back((timestamp - timeOrigin).toSecondsDouble());
+            positions.emplace_back(simox::math::position(pose.objectPoseGlobal).cast<double>());
+        }
+
+        ARMARX_CHECK_EQUAL(timestampsSec.size(), positions.size());
+
+        Eigen::Vector3d prediction;
+        // Static objects don't move. Objects that haven't moved for a while probably won't either.
+        if (timestampsSec.size() <= 1 || latestPose.isStatic)
+        {
+            prediction = simox::math::position(latestPose.objectPoseGlobal).cast<double>();
+        }
+        else
+        {
+            using simox::math::LinearRegression3d;
+            const bool inputOffset = false;
+
+            const LinearRegression3d model =
+                LinearRegression3d::Fit(timestampsSec, positions, inputOffset);
+            const auto predictionTime = armarx::fromIce<DateTime>(time);
+            prediction = model.predict((predictionTime - timeOrigin).toSecondsDouble());
+        }
+
+        // Used as a template for the returned pose prediction.
+        ObjectPose latestCopy = latestPose;
+
+        // Reuse the rotation from the latest pose.
+        // This is a pretty generous assumption, but the linear model doesn't cover rotations,
+        // so it's our best guess.
+        Eigen::Matrix4f latest = latestPose.objectPoseGlobal;
+        simox::math::position(latest) = prediction.cast<float>();
+        latestCopy.setObjectPoseGlobal(latest);
+
+        result.success = true;
+        result.prediction = latestCopy.toIce();
+
+        return result;
+    }
+} // namespace armarx::objpose
diff --git a/source/RobotAPI/libraries/ArmarXObjects/predictions.h b/source/RobotAPI/libraries/ArmarXObjects/predictions.h
new file mode 100644
index 000000000..900758590
--- /dev/null
+++ b/source/RobotAPI/libraries/ArmarXObjects/predictions.h
@@ -0,0 +1,46 @@
+/*
+ * 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     phesch ( ulila at student dot kit dot edu )
+ * @date       2022
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+#pragma once
+
+#include <ArmarXCore/core/time/DateTime.h>
+
+#include <RobotAPI/interface/objectpose/ObjectPoseStorageInterface.h>
+#include <RobotAPI/libraries/ArmarXObjects/ObjectPose.h>
+#include <RobotAPI/libraries/armem/client/Prediction.h>
+
+namespace armarx::objpose
+{
+    /**
+    * @brief Predict the pose of an object given a history of poses.
+    *
+    * @param poses the history of poses to base the prediction on
+    * @param time the timestamp to make the prediction for
+    * @param latestPose used for metadata so the result is valid even if poses is empty
+    * @param settings the settings to use for the prediction
+    * @return the result of the prediction
+    */
+    objpose::ObjectPosePredictionResult
+    predictObjectPose(const std::map<DateTime, ObjectPose>& poses,
+                      const DateTime& time,
+                      const ObjectPose& latestPose,
+                      const armem::client::PredictionSettings& settings);
+} // namespace armarx::objpose
diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
index 9ae2fba6f..2c8ca4e7d 100644
--- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
+++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
@@ -602,71 +602,6 @@ namespace armarx::armem::server::obj::instance
     }
 
 
-    objpose::ObjectPosePredictionResult
-    Segment::predictObjectPose(const std::map<DateTime, ObjectPose>& poses,
-                               const DateTime& time,
-                               const ObjectPose& latestPose,
-                               const armem::client::PredictionSettings& settings)
-    {
-        objpose::ObjectPosePredictionResult result;
-        result.success = false;
-
-        if (!settings.predictionEngineID.empty() &&
-            settings.predictionEngineID != "Linear Position Regression")
-        {
-            result.errorMessage = "Prediction engine '" + settings.predictionEngineID +
-                                  "' not available for object pose prediction.";
-            return result;
-        }
-
-        const Time timeOrigin = Time::Now();
-
-        std::vector<double> timestampsSec;
-        std::vector<Eigen::Vector3d> positions;
-
-        // ToDo: How to handle attached objects?
-        for (const auto& [timestamp, pose] : poses)
-        {
-            timestampsSec.push_back((timestamp - timeOrigin).toSecondsDouble());
-            positions.push_back(simox::math::position(pose.objectPoseGlobal).cast<double>());
-        }
-
-        ARMARX_CHECK_EQUAL(timestampsSec.size(), positions.size());
-
-        Eigen::Vector3d prediction;
-        // Static objects don't move. Objects that haven't moved for a while probably won't either.
-        if (timestampsSec.size() <= 1 || latestPose.isStatic)
-        {
-            prediction = simox::math::position(latestPose.objectPoseGlobal).cast<double>();
-        }
-        else
-        {
-            using simox::math::LinearRegression3d;
-            const bool inputOffset = false;
-
-            const LinearRegression3d model =
-                LinearRegression3d::Fit(timestampsSec, positions, inputOffset);
-            const Time predictionTime = armarx::fromIce<Time>(time);
-            prediction = model.predict((predictionTime - timeOrigin).toSecondsDouble());
-        }
-
-        // Used as a template for the returned pose prediction.
-        ObjectPose latestCopy = latestPose;
-
-        // Reuse the rotation from the latest pose.
-        // This is a pretty generous assumption, but the linear model doesn't cover rotations,
-        // so it's our best guess.
-        Eigen::Matrix4f latest = latestPose.objectPoseGlobal;
-        simox::math::position(latest) = prediction.cast<float>();
-        latestCopy.setObjectPoseGlobal(latest);
-
-        result.success = true;
-        result.prediction = latestCopy.toIce();
-
-        return result;
-    }
-
-
     objpose::AttachObjectToRobotNodeOutput
     Segment::attachObjectToRobotNode(const objpose::AttachObjectToRobotNodeInput& input)
     {
diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h
index e0f97dfce..0aa9b9304 100644
--- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h
+++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h
@@ -108,21 +108,6 @@ namespace armarx::armem::server::obj::instance
         static std::map<DateTime, ObjectPose>
         getObjectPosesInRange(const wm::Entity& entity, const DateTime& start, const DateTime& end);
 
-        /**
-         * @brief Predict the pose of an object given a history of poses.
-         *
-         * @param poses the history of poses to base the prediction on
-         * @param time the timestamp to make the prediction for
-         * @param latestPose used for metadata so the result is valid even if poses is empty
-         * @param settings the settings to use for the prediction
-         * @return the result of the prediction
-         */
-        objpose::ObjectPosePredictionResult
-        predictObjectPose(const std::map<DateTime, ObjectPose>& poses,
-                          const DateTime& time,
-                          const ObjectPose& latestPose,
-                          const armem::client::PredictionSettings& settings);
-
     private:
 
         ObjectPoseMap getLatestObjectPoses() const;
diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp
index c32be9853..010263ceb 100644
--- a/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp
+++ b/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp
@@ -26,6 +26,7 @@
 #include <RobotAPI/libraries/ArmarXObjects/aron_conversions.h>
 #include <RobotAPI/libraries/ArmarXObjects/ice_conversions.h>
 #include <RobotAPI/libraries/ArmarXObjects/ObjectID.h>
+#include <RobotAPI/libraries/ArmarXObjects/predictions.h>
 #include <RobotAPI/libraries/armem_objects/aron/ObjectInstance.aron.generated.h>
 #include <RobotAPI/libraries/aron/common/aron_conversions/core.h>
 
@@ -491,7 +492,7 @@ namespace armarx::armem::server::obj::instance
         {
             if (results.at(i).success)
             {
-                results.at(i) = segment.predictObjectPose(
+                results.at(i) = objpose::predictObjectPose(
                     poses.at(i),
                     armarx::fromIce<DateTime>(requests.at(i).timestamp),
                     latestPoses.at(i),
-- 
GitLab