From 951f66c8ee91ae7a1981cb205c8d70306aa6b78e Mon Sep 17 00:00:00 2001
From: ArmarX User <armarx@kit.edu>
Date: Wed, 6 Mar 2019 13:33:22 +0100
Subject: [PATCH] final work Innsbruck 2019

---
 .../NJointTaskSpaceDMPController.ice          |   2 +
 .../DMPController/TaskSpaceDMPController.cpp  |   5 +
 .../DMPController/TaskSpaceDMPController.h    |  11 +
 .../JointPWMPositionController.cpp            |  40 +-
 .../JointPWMPositionController.h              |   1 +
 .../JointController/PWMVelocityController.cpp |   1 +
 .../JointController/PWMVelocityController.h   |   1 +
 .../KITGripperBasisBoardData.cpp              |  42 +-
 .../KITGripperBasisBoardData.h                |   6 +
 .../Misc/TorqueEstimation.h                   |  59 +-
 .../RobotAPINJointControllers/CMakeLists.txt  |   4 +-
 .../DMPController/NJointTSDMPController.cpp   |  12 +-
 .../DMPController/NJointTSDMPController.h     |   3 +-
 .../NJointTSMultiMPController.cpp             | 562 ++++++++++++++++++
 .../DMPController/NJointTSMultiMPController.h | 195 ++++++
 15 files changed, 924 insertions(+), 20 deletions(-)
 create mode 100644 source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.cpp
 create mode 100644 source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.h

diff --git a/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice b/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice
index 8388479b7..6c97923e2 100644
--- a/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice
+++ b/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice
@@ -92,6 +92,8 @@ module armarx
         double getCanVal();
         void resetDMP();
         void stopDMP();
+        void pauseDMP();
+        void resumeDMP();
 
         void setControllerTarget(float avoidJointLimitsKp, NJointTaskSpaceDMPControllerMode::CartesianSelection mode);
         void setTorqueKp(StringFloatDictionary torqueKp);
diff --git a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp
index eb748893a..de7de3cdc 100644
--- a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp
+++ b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp
@@ -29,6 +29,11 @@ using namespace armarx;
 
 void TaskSpaceDMPController::flow(double deltaT, const Eigen::Matrix4f& currentPose, const Eigen::VectorXf& twist)
 {
+    if (paused)
+    {
+        targetVel.setZero();
+        return;
+    }
     if (canVal < 1e-8)
     {
         if (config.DMPStyle == "Periodic")
diff --git a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h
index 777c75da7..eb8d5c541 100644
--- a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h
+++ b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h
@@ -106,6 +106,7 @@ namespace armarx
 
             this->isPhaseStopControl = isPhaseStopControl;
             dmpName = name;
+            this->paused = false;
         }
 
         std::string getName()
@@ -167,10 +168,20 @@ namespace armarx
             return dmpPtr;
         }
 
+        void pauseController()
+        {
+            this->paused = true;
+        }
+        void resumeController()
+        {
+            this->paused = false;
+        }
+
         double canVal;
         bool isPhaseStopControl;
         std::string dmpName;
         TaskSpaceDMPControllerConfig config;
+        bool paused;
 
     private:
 
diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp
index af1ca8a65..31b2274b1 100644
--- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp
+++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp
@@ -27,6 +27,22 @@ JointPWMPositionController::JointPWMPositionController(const std::string deviceN
     actorIndex = board->getActorIndex(deviceName);
     dataPtr = jointData;
 
+    // add limitless joint controller to solve the flipping sensor value problem
+    limitlessController.positionPeriodLo = dataPtr->getSoftLimitLo();
+    limitlessController.positionPeriodHi = dataPtr->getSoftLimitHi();
+    limitlessController.maxDt = velocityControllerConfigDataPtr->maxDt;
+    limitlessController.maxV = velocityControllerConfigDataPtr->maxVelocityRad;
+    limitlessController.p = velocityControllerConfigDataPtr->posControlP;
+    //    ARMARX_INFO << deviceName << " pos P: " << limitlessController.p;
+    //    limitlessController.p = 10.0f; // for bit joint
+    //    limitlessController.p = 3.0f; // for base joint
+    limitlessController.acceleration = velocityControllerConfigDataPtr->maxAccelerationRad;
+    limitlessController.deceleration = velocityControllerConfigDataPtr->maxDecelerationRad;
+    limitlessController.accuracy = 0.001f;
+    //    ARMARX_INFO << "period low: " << limitlessController.positionPeriodLo << ", period high: " << limitlessController.positionPeriodHi;
+    //    ARMARX_INFO << "maxDt: " << limitlessController.maxDt << ", maxV: " << limitlessController.maxV;
+    //    ARMARX_INFO << "acceleration: " << limitlessController.acceleration << ", deceleration: " << limitlessController.deceleration;
+
     posController.acceleration = velocityControllerConfigDataPtr->maxAccelerationRad;
     posController.deceleration = velocityControllerConfigDataPtr->maxDecelerationRad;
     posController.maxDt = velocityControllerConfigDataPtr->maxDt;
@@ -38,7 +54,7 @@ JointPWMPositionController::JointPWMPositionController(const std::string deviceN
     posController.positionLimitLo = jointData->getSoftLimitLo();
     posController.p = 3.0f;
     this->isLimitless = jointData->isLimitless();
-
+    //    ARMARX_INFO << "limitless: " << this->isLimitless;
 }
 
 JointPWMPositionController::~JointPWMPositionController() noexcept(true)
@@ -59,18 +75,26 @@ void JointPWMPositionController::rtRun(const IceUtil::Time& sensorValuesTimestam
     if (target.isValid())
     {
         auto currentPosition = dataPtr->getPosition();
+        double newVel = 0.0;
         if (isLimitless)
         {
-            ARMARX_RT_LOGF_WARNING("Position controller not implemented for limitless joints").deactivateSpam(10);
+            limitlessController.currentV = lastTargetVelocity;
+            limitlessController.dt = timeSinceLastIteration.toSecondsDouble();
+            limitlessController.currentPosition = currentPosition;
+            limitlessController.targetPosition = target.position;
+            newVel = limitlessController.run();
+            //            ARMARX_IMPORTANT << "using limitless position controller";
+            //            ARMARX_RT_LOGF_WARNING("Position controller not implemented for limitless joints").deactivateSpam(10);
         }
         else
         {
             posController.currentPosition =  currentPosition;
+            posController.currentV = lastTargetVelocity;
+            posController.dt = timeSinceLastIteration.toSecondsDouble();
+            posController.targetPosition = target.position;
+            newVel = posController.run();
         }
-        posController.currentV = lastTargetVelocity;
-        posController.dt = timeSinceLastIteration.toSecondsDouble();
-        posController.targetPosition = target.position;
-        double newVel = posController.run();
+
         //        double newVel = posController.p * (posController.targetPosition - posController.currentPosition);
         //        newVel = math::MathUtils::LimitTo(newVel, posController.maxV);
         //            ARMARX_INFO << deactivateSpam(1) << VAROUT(newVel);
@@ -92,8 +116,6 @@ void JointPWMPositionController::rtRun(const IceUtil::Time& sensorValuesTimestam
         //        ARMARX_RT_LOGF_INFO("%s: position: %.f, target position: %.f, targetvelocity: %.f, target PWM: %d", name,
         //                            currentPosition, target.position, newVel, targetPWM).deactivateSpam(1);
         //        ARMARX_INFO << deactivateSpam(1) << VAROUT(name) << VAROUT(currentPosition) << VAROUT(target.position) << VAROUT(newVel) << VAROUT(targetPWM);
-
-
     }
     else
     {
@@ -235,8 +257,6 @@ StringVariantBaseMap JointPWMPositionController::publish(const DebugDrawerInterf
         {"pidPropCV", new Variant(controller.pid->previousError * controller.pid->Kp)},
         {"pidDiffCV", new Variant(controller.pid->derivative * controller.pid->Kd)},
         {"pidUsed", new Variant(posController.getCurrentlyPIDActive())}
-
-
     };
 }
 
diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h
index b5ab09fff..62818578f 100644
--- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h
+++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h
@@ -38,6 +38,7 @@ namespace armarx
         PWMVelocityControllerConfigurationCPtr config;
         PWMVelocityController controller;
         PositionThroughVelocityControllerWithAccelerationAndPositionBounds posController;
+        PositionThroughVelocityControllerWithAccelerationBoundsAndPeriodicPosition limitlessController;
 
         ControlTarget1DoFActuatorPosition target;
 
diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp
index 318dde917..d5451bbd6 100644
--- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp
+++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp
@@ -86,6 +86,7 @@ namespace armarx
         configData.maxDecelerationRad = node.first_node("maxDecelerationRad").value_as_float();
         configData.maxDt = node.first_node("maxDt").value_as_float();
         configData.maxDt = node.first_node("directSetVLimit").value_as_float();
+        configData.posControlP = node.first_node("posControlP").value_as_float();
         configData.p = node.first_node("p").value_as_float();
         configData.i = node.first_node("i").value_as_float();
         configData.d = node.first_node("d").value_as_float();
diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h
index 47bc0c2e9..2dc6b7e80 100644
--- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h
+++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h
@@ -48,6 +48,7 @@ namespace armarx
         float maxDecelerationRad;
         float maxDt;
         float directSetVLimit;
+        float posControlP;
         float p;
         float i;
         float d;
diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp
index 7cc4f2762..a6530c674 100644
--- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp
+++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp
@@ -62,6 +62,7 @@ namespace armarx
 
                 actorData.at(connectorIndex)->parallelGripperDecouplingFactor = configNode.first_node("ParallelGripperDecouplingFactor").value_as_float();
                 actorData.at(connectorIndex)->parallelControlEnabled = configNode.first_node("ParallelControlSiblingIndex").value_as_int32();
+                actorData.at(connectorIndex)->relativePositionSensorOnly = configNode.first_node("RelativePositionSensorOnly").value_as_int32();
                 actorData.at(connectorIndex)->currentPWMBoundGradient = configNode.first_node("CurrentPWMBoundGradient").value_as_float();
                 actorData.at(connectorIndex)->currentPWMBoundOffset = configNode.first_node("CurrentPWMBoundOffset").value_as_int32();
 
@@ -83,6 +84,9 @@ namespace armarx
             }
 
         }
+        jumpingOffset = 0.0;
+        firstRun = true;
+        lastAbsoluteAngle = 0.0;
     }
 
     void KITGripperBasisBoardData::rtReadSensorValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration)
@@ -103,13 +107,29 @@ namespace armarx
             if (d.getSiblingControlActorIndex() >= 0)
             {
                 auto& d2 = actorData.at(d.getSiblingControlActorIndex());
-                //                ARMARX_IMPORTANT << "decoupling factor: " << d.parallelGripperDecouplingFactor;
+                if (firstRun)
+                {
+                    lastAbsoluteAngle = d2->position.value;
+                    firstRun = false;
+                }
+
+                // better to use relative position, but experiments shows that relative position measurements fails sometimes, reason unknown
+                // d.adjustedRelativePosition = d.relativePosition.value + d2->relativePosition.value * d.parallelGripperDecouplingFactor;
+
+                if (d2->position.value - lastAbsoluteAngle > M_PI)
+                {
+                    jumpingOffset -= 2 * M_PI;
+                }
+                else if (d2->position.value - lastAbsoluteAngle < -M_PI)
+                {
+                    jumpingOffset += 2 * M_PI;
+                }
                 d.adjustedRelativePosition = d.relativePosition.value +
-                                             d2->relativePosition.value * d.parallelGripperDecouplingFactor;
+                                             (d2->position.value + jumpingOffset) * d.parallelGripperDecouplingFactor;
+                lastAbsoluteAngle = d2->position.value;
             }
             else
             {
-                //ARMARX_IMPORTANT << "not even in the right place";
                 d.adjustedRelativePosition = d.relativePosition.value;
             }
 
@@ -132,7 +152,16 @@ namespace armarx
             }
 
 
-            if (i > 1)
+            //            if (i > 1)
+            //            {
+            //                d.position.value = d.adjustedRelativePosition;
+            //            }
+            //            else
+            //            {
+            //                d.position.read();
+            //            }
+
+            if (d.getSiblingControlActorIndex() >= 0 || d.getRelativePositionSensorOnly() >= 0)
             {
                 d.position.value = d.adjustedRelativePosition;
             }
@@ -273,6 +302,11 @@ namespace armarx
         return parallelControlEnabled;
     }
 
+    int32_t ActorData::getRelativePositionSensorOnly() const
+    {
+        return relativePositionSensorOnly;
+    }
+
     bool ActorData::getPositionControlEnabled() const
     {
         return positionControlEnabled;
diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h
index f0d9ae529..c54141772 100644
--- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h
+++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h
@@ -98,6 +98,8 @@ namespace armarx
 
         int32_t getSiblingControlActorIndex() const;
 
+        int32_t getRelativePositionSensorOnly() const;
+
         bool getPositionControlEnabled() const;
 
         bool getVelocityControlEnabled() const;
@@ -129,6 +131,7 @@ namespace armarx
         int32_t currentMinPWM = 0;
         size_t maxPWM;
         int32_t parallelControlEnabled = -1;
+        int32_t relativePositionSensorOnly = -1;
         VirtualRobot::RobotNodePtr robotNode;
         LinearConvertedValue<int32_t> targetPWMPtr;
         bool positionControlEnabled = true;
@@ -156,6 +159,9 @@ namespace armarx
         KITGripperBasisBoardIN_t* sensorIN;
 
         std::vector<ActorDataPtr> actorData;
+        bool firstRun;
+        float jumpingOffset;
+        float lastAbsoluteAngle; // this is the memory for last joint angle of the PG_base joint. to detect the jumping of the sensor value
 
     };
     using KITGripperBasisBoardDataPtr = std::shared_ptr<KITGripperBasisBoardData>;
diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/Misc/TorqueEstimation.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/Misc/TorqueEstimation.h
index 3c94921cc..51736ff07 100644
--- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/Misc/TorqueEstimation.h
+++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/Misc/TorqueEstimation.h
@@ -2,6 +2,7 @@
 /* Includes ------------------------------------------------------------------*/
 
 #include "TorqueEstimationWeights.h"
+#include <stdio.h>
 
 float imgIn[5];
 float imgFcl1[32];
@@ -175,9 +176,44 @@ uint8_t fcl6(void)
 }
 
 
+float linearModel_dcx16(int32_t nI, int32_t pwm)
+{
+    float n = (float) nI * 1000 * 60 / 4096; //ticks/ms -> turns/min
+    float T_f = 1000.0 / 406.0; //übersetzung getriebe + Nm->m
+    float motor_a = 265.0; //Drehzahlkonstante   [min-1 V-1]
+    float motor_b = 620.0;   //Kennliniensteigung [min-1 mNm-1]
+    float motor_eta = 0.55 * 0.78; //Wirkungsgrad Getriebe *Motor, max from datasheet:0.75*0.85
+    float Umax = 48.0; //Spannung bei pwm max
+    float pwmmax = 3000.0;
+    float pwm_zero = 400.0;
+    float U;
+    float T_motor;
+
+
+    U = (float(fabs(pwm) - pwm_zero) / (pwmmax - pwm_zero))  *   Umax;
+    if (U < 0)
+    {
+        U = 0;
+    }
+    if (pwm < 0)
+    {
+        U *= -1;
+    }
+    if (pwm == 0)
+    {
+        U = 0;
+    }
+
+    //U(M,n)=(n-b*M)/a
+    T_motor = (U * motor_a - n) / -motor_b;
+    auto T = T_motor * motor_eta / T_f;
+
+    return T;
+}
+
 float linearModel_dcx22(int32_t nI, int32_t pwm)
 {
-    float n = (float) nI * 1000 * 60 / 4096; //ticks/ms -> U/min
+    float n = (float) nI * 1000 * 60 / 4096; //ticks/ms -> turns/min
     float T_f = 1000. / 231.; //übersetzung getriebe + Nm->m
     float motor_a = 226. + 30.; //Drehzahlkonstante   [min-1 V-1]
     float motor_b = 123.;   //Kennliniensteigung [min-1 mNm-1]
@@ -211,8 +247,27 @@ float linearModel_dcx22(int32_t nI, int32_t pwm)
 }
 
 
-float estimateTorque(int32_t n, int32_t pwm)
+float estimateTorque(int32_t n, int32_t pwm)//, int motorModel, bool linearModelOnly = false)
 {
+    bool linearModelOnly = true;
+    int motorModel = 16;
+    if (linearModelOnly)
+    {
+        if (motorModel == 16)
+        {
+            return linearModel_dcx16(n, pwm);
+        }
+        else if (motorModel == 22)
+        {
+            return linearModel_dcx22(n, pwm);
+        }
+        else
+        {
+            std::cout << "motor model should be set to 16 or 22" << std::endl;
+            return 0.0;
+        }
+        //        return linearModel_dcx22(n, pwm);
+    }
     float n_input = (float)n / n_factor;
     float pwm_input = (float)pwm / pwm_factor;
     //    float inputData[6];
diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt b/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt
index c2da980f6..e12f16cab 100644
--- a/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt
+++ b/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt
@@ -49,7 +49,7 @@ if (DMP_FOUND )
            DMPController/NJointBimanualCCDMPController.h
            DMPController/NJointTaskSpaceImpedanceDMPController.h
            DMPController/NJointBimanualForceMPController.h
-
+#           DMPController/NJointTSMultiMPController.h
            )
     list(APPEND LIB_FILES
            DMPController/NJointJointSpaceDMPController.cpp
@@ -59,7 +59,7 @@ if (DMP_FOUND )
            DMPController/NJointBimanualCCDMPController.cpp
            DMPController/NJointTaskSpaceImpedanceDMPController.cpp
            DMPController/NJointBimanualForceMPController.cpp
-
+#           DMPController/NJointTSMultiMPController.cpp
            )
 
     list(APPEND LIBS ${DMP_LIBRARIES} DMPController)
diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp
index ec9bf0b23..b24cb76fb 100644
--- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp
+++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp
@@ -245,7 +245,7 @@ namespace armarx
             targetPose = currentPose;
         }
 
-        ARMARX_IMPORTANT << "targetPose: " << targetPose;
+        //        ARMARX_IMPORTANT << "targetPose: " << targetPose;
 
         Eigen::Matrix3f diffMat = targetPose.block<3, 3>(0, 0) * currentPose.block<3, 3>(0, 0).inverse();
         Eigen::Vector3f errorRPY = VirtualRobot::MathTools::eigen3f2rpy(diffMat);
@@ -418,6 +418,16 @@ namespace armarx
         writeControlStruct();
     }
 
+    void NJointTSDMPController::pauseDMP(const Ice::Current&)
+    {
+        taskSpaceDMPController->pauseController();
+    }
+
+    void NJointTSDMPController::resumeDMP(const Ice::Current&)
+    {
+        taskSpaceDMPController->resumeController();
+    }
+
     void NJointTSDMPController::resetDMP(const Ice::Current&)
     {
         if (started)
diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h
index e50b9890b..9eea8c9bb 100644
--- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h
+++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h
@@ -86,7 +86,8 @@ namespace armarx
         void setControllerTarget(Ice::Float avoidJointLimitsKp, NJointTaskSpaceDMPControllerMode::CartesianSelection mode, const Ice::Current&) override;
         void setTorqueKp(const StringFloatDictionary& torqueKp, const Ice::Current&) override;
         void setNullspaceJointVelocities(const StringFloatDictionary& nullspaceJointVelocities, const Ice::Current&) override;
-
+        void pauseDMP(const Ice::Current&) override;
+        void resumeDMP(const Ice::Current&) override;
 
         void resetDMP(const Ice::Current&) override;
         void stopDMP(const Ice::Current&) override;
diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.cpp
new file mode 100644
index 000000000..4a65e60de
--- /dev/null
+++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.cpp
@@ -0,0 +1,562 @@
+/* Jeff
+ *
+ */
+#include "NJointTSMultiMPController.h"
+
+namespace armarx
+{
+    NJointControllerRegistration<NJointTSMultiMPController> registrationControllerNJointTSMultiMPController("NJointTSMultiMPController");
+
+    NJointTSMultiMPController::NJointTSMultiMPController(const RobotUnitPtr&, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&)
+    {
+        useSynchronizedRtRobot();
+        cfg = NJointTaskSpaceDMPControllerConfigPtr::dynamicCast(config);
+        ARMARX_CHECK_EXPRESSION(!cfg->nodeSetName.empty());
+        VirtualRobot::RobotNodeSetPtr rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName);
+
+        ARMARX_CHECK_EXPRESSION_W_HINT(rns, cfg->nodeSetName);
+        for (size_t i = 0; i < rns->getSize(); ++i)
+        {
+            std::string jointName = rns->getNode(i)->getName();
+            ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Velocity1DoF);
+            const SensorValueBase* sv = useSensorValue(jointName);
+            targets.push_back(ct->asA<ControlTarget1DoFActuatorVelocity>());
+
+            //            const SensorValue1DoFActuatorTorque* torqueSensor = sv->asA<SensorValue1DoFActuatorTorque>();
+            //            const SensorValue1DoFGravityTorque* gravityTorqueSensor = sv->asA<SensorValue1DoFGravityTorque>();
+
+            const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>();
+            const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>();
+
+            //            torqueSensors.push_back(torqueSensor);
+            //            gravityTorqueSensors.push_back(gravityTorqueSensor);
+            velocitySensors.push_back(velocitySensor);
+            positionSensors.push_back(positionSensor);
+        };
+
+        tcp = (cfg->tcpName.empty()) ? rns->getTCP() : rtGetRobot()->getRobotNode(cfg->tcpName);
+        ARMARX_CHECK_EXPRESSION_W_HINT(tcp, cfg->tcpName);
+
+        // set tcp controller
+        tcpController.reset(new CartesianVelocityController(rns, tcp));
+        nodeSetName = cfg->nodeSetName;
+        torquePIDs.resize(tcpController->rns->getSize(), pidController());
+
+        ik.reset(new VirtualRobot::DifferentialIK(rns, rns->getRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped));
+
+
+        finished = false;
+        TaskSpaceDMPControllerConfig taskSpaceDMPConfig;
+        taskSpaceDMPConfig.motionTimeDuration = cfg->timeDuration;
+        taskSpaceDMPConfig.DMPKernelSize = cfg->kernelSize;
+        taskSpaceDMPConfig.DMPMode = cfg->dmpMode;
+        taskSpaceDMPConfig.DMPStyle = cfg->dmpStyle;
+        taskSpaceDMPConfig.DMPAmplitude = cfg->dmpAmplitude;
+        taskSpaceDMPConfig.phaseStopParas.goDist = cfg->phaseDist0;
+        taskSpaceDMPConfig.phaseStopParas.backDist = cfg->phaseDist1;
+        taskSpaceDMPConfig.phaseStopParas.Kpos = cfg->phaseKpPos;
+        taskSpaceDMPConfig.phaseStopParas.Dpos = 0;
+        taskSpaceDMPConfig.phaseStopParas.Kori = cfg->phaseKpOri;
+        taskSpaceDMPConfig.phaseStopParas.Dori = 0;
+        taskSpaceDMPConfig.phaseStopParas.mm2radi = cfg->posToOriRatio;
+        taskSpaceDMPConfig.phaseStopParas.maxValue = cfg->phaseL;
+        taskSpaceDMPConfig.phaseStopParas.slop = cfg->phaseK;
+
+        taskSpaceDMPController.reset(new TaskSpaceDMPController("default", taskSpaceDMPConfig, false));
+
+        // initialize tcp position and orientation
+
+
+        NJointTSMultiMPControllerSensorData initSensorData;
+        initSensorData.deltaT = 0;
+        initSensorData.currentTime = 0;
+        initSensorData.currentPose.setZero();
+        initSensorData.currentTwist.setZero();
+        controllerSensorData.reinitAllBuffers(initSensorData);
+
+
+
+        debugName = cfg->debugName;
+
+        KpF = cfg->Kp_LinearVel;
+        KoF = cfg->Kp_AngularVel;
+        DpF = cfg->Kd_LinearVel;
+        DoF = cfg->Kd_AngularVel;
+
+        filtered_qvel.setZero(targets.size());
+        vel_filter_factor = cfg->vel_filter;
+
+        filtered_position.setZero();
+        pos_filter_factor = cfg->pos_filter;
+
+        //        jlhigh = rns->getNode("..")->getJointLimitHi();
+        //        jllow = rns->getNode("")->getJointLimitLo();
+        firstRun = true;
+
+        jointLowLimits.setZero(targets.size());
+        jointHighLimits.setZero(targets.size());
+        for (size_t i = 0; i < rns->getSize(); i++)
+        {
+            VirtualRobot::RobotNodePtr rn = rns->getAllRobotNodes().at(i);
+
+            jointLowLimits(i) = rn->getJointLimitLo();
+            jointHighLimits(i) = rn->getJointLimitHi();
+        }
+
+        started = false;
+
+        NJointTSMultiMPControllerInterfaceData initInterfaceData;
+        initInterfaceData.currentTcpPose = tcp->getPoseInRootFrame();
+        interfaceData.reinitAllBuffers(initInterfaceData);
+    }
+
+    std::string NJointTSMultiMPController::getClassName(const Ice::Current&) const
+    {
+        return "NJointTSMultiMPController";
+    }
+
+    void NJointTSMultiMPController::controllerRun()
+    {
+        if (!started)
+        {
+            return;
+        }
+
+        if (!controllerSensorData.updateReadBuffer() || !taskSpaceDMPController)
+        {
+            return;
+        }
+        double deltaT = controllerSensorData.getReadBuffer().deltaT;
+        Eigen::Matrix4f currentPose = controllerSensorData.getReadBuffer().currentPose;
+        Eigen::VectorXf currentTwist = controllerSensorData.getReadBuffer().currentTwist;
+
+
+        LockGuardType guard {controllerMutex};
+        taskSpaceDMPController->flow(deltaT, currentPose, currentTwist);
+
+
+        if (taskSpaceDMPController->canVal < 1e-8)
+        {
+            finished = true;
+        }
+        targetVels = taskSpaceDMPController->getTargetVelocity();
+        targetPose = taskSpaceDMPController->getTargetPoseMat();
+        std::vector<double> targetState = taskSpaceDMPController->getTargetPose();
+
+        //        ARMARX_IMPORTANT << "CanVal: " << taskSpaceDMPController->canVal;
+        //        ARMARX_IMPORTANT << "currentPose: " << currentPose;
+        //        ARMARX_IMPORTANT << "targetVels: " << targetVels;
+        //        ARMARX_IMPORTANT << "targetPose: " << targetPose;
+
+        debugOutputData.getWriteBuffer().latestTargetVelocities["x_vel"] = targetVels(0);
+        debugOutputData.getWriteBuffer().latestTargetVelocities["y_vel"] = targetVels(1);
+        debugOutputData.getWriteBuffer().latestTargetVelocities["z_vel"] = targetVels(2);
+        debugOutputData.getWriteBuffer().latestTargetVelocities["roll_vel"] = targetVels(3);
+        debugOutputData.getWriteBuffer().latestTargetVelocities["pitch_vel"] = targetVels(4);
+        debugOutputData.getWriteBuffer().latestTargetVelocities["yaw_vel"] = targetVels(5);
+        debugOutputData.getWriteBuffer().dmpTargets["dmp_x"] = targetState[0];
+        debugOutputData.getWriteBuffer().dmpTargets["dmp_y"] = targetState[1];
+        debugOutputData.getWriteBuffer().dmpTargets["dmp_z"] = targetState[2];
+        debugOutputData.getWriteBuffer().dmpTargets["dmp_qw"] = targetState[3];
+        debugOutputData.getWriteBuffer().dmpTargets["dmp_qx"] = targetState[4];
+        debugOutputData.getWriteBuffer().dmpTargets["dmp_qy"] = targetState[5];
+        debugOutputData.getWriteBuffer().dmpTargets["dmp_qz"] = targetState[6];
+        debugOutputData.getWriteBuffer().currentPose["currentPose_x"] = currentPose(0, 3);
+        debugOutputData.getWriteBuffer().currentPose["currentPose_y"] = currentPose(1, 3);
+        debugOutputData.getWriteBuffer().currentPose["currentPose_z"] = currentPose(2, 3);
+
+        VirtualRobot::MathTools::Quaternion currentQ = VirtualRobot::MathTools::eigen4f2quat(currentPose);
+        debugOutputData.getWriteBuffer().currentPose["currentPose_qw"] = currentQ.w;
+        debugOutputData.getWriteBuffer().currentPose["currentPose_qx"] = currentQ.x;
+        debugOutputData.getWriteBuffer().currentPose["currentPose_qy"] = currentQ.y;
+        debugOutputData.getWriteBuffer().currentPose["currentPose_qz"] = currentQ.z;
+        debugOutputData.getWriteBuffer().currentCanVal = taskSpaceDMPController->debugData.canVal;
+        debugOutputData.getWriteBuffer().mpcFactor =  taskSpaceDMPController->debugData.mpcFactor;
+        debugOutputData.getWriteBuffer().error = taskSpaceDMPController->debugData.poseError;
+        debugOutputData.getWriteBuffer().posError = taskSpaceDMPController->debugData.posiError;
+        debugOutputData.getWriteBuffer().oriError = taskSpaceDMPController->debugData.oriError;
+        debugOutputData.getWriteBuffer().deltaT = deltaT;
+
+        debugOutputData.commitWrite();
+
+        getWriterControlStruct().targetTSVel = targetVels;
+        getWriterControlStruct().targetPose = targetPose;
+
+        writeControlStruct();
+
+    }
+
+
+    void NJointTSMultiMPController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration)
+    {
+        Eigen::Matrix4f currentPose = tcp->getPoseInRootFrame();
+        interfaceData.getWriteBuffer().currentTcpPose = currentPose;
+        interfaceData.commitWrite();
+
+        if (firstRun)
+        {
+            filtered_position = currentPose.block<3, 1>(0, 3);
+
+            firstRun = false;
+            for (size_t i = 0; i < targets.size(); ++i)
+            {
+                targets.at(i)->velocity = 0;
+            }
+            return;
+        }
+        else
+        {
+            filtered_position = (1 - pos_filter_factor) * filtered_position + pos_filter_factor * currentPose.block<3, 1>(0, 3);
+        }
+
+        double deltaT = timeSinceLastIteration.toSecondsDouble();
+
+        Eigen::MatrixXf jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All);
+
+        Eigen::VectorXf qvel;
+        qvel.resize(velocitySensors.size());
+        Eigen::VectorXf qpos;
+        qpos.resize(positionSensors.size());
+        for (size_t i = 0; i < velocitySensors.size(); ++i)
+        {
+            qvel(i) = velocitySensors[i]->velocity;
+            qpos(i) = positionSensors[i]->position;
+        }
+
+        filtered_qvel = (1 - vel_filter_factor) * filtered_qvel + vel_filter_factor * qvel;
+        Eigen::VectorXf tcptwist = jacobi * filtered_qvel;
+
+        controllerSensorData.getWriteBuffer().currentPose = currentPose;
+        controllerSensorData.getWriteBuffer().currentTwist = tcptwist;
+        controllerSensorData.getWriteBuffer().deltaT = deltaT;
+        controllerSensorData.getWriteBuffer().currentTime += deltaT;
+        controllerSensorData.commitWrite();
+
+
+
+        Eigen::VectorXf targetVel = rtGetControlStruct().targetTSVel;
+        Eigen::Matrix4f targetPose = rtGetControlStruct().targetPose;
+
+        if (started)
+        {
+            targetVel = rtGetControlStruct().targetTSVel;
+            targetPose = rtGetControlStruct().targetPose;
+        }
+        else
+        {
+            targetVel.setZero(6);
+            targetPose = currentPose;
+        }
+
+        //        ARMARX_IMPORTANT << "targetPose: " << targetPose;
+
+        Eigen::Matrix3f diffMat = targetPose.block<3, 3>(0, 0) * currentPose.block<3, 3>(0, 0).inverse();
+        Eigen::Vector3f errorRPY = VirtualRobot::MathTools::eigen3f2rpy(diffMat);
+
+        Eigen::VectorXf rtTargetVel;
+        rtTargetVel.resize(6);
+        rtTargetVel.block<3, 1>(0, 0) = KpF * (targetPose.block<3, 1>(0, 3) - filtered_position) + DpF * (targetVel.block<3, 1>(0, 0) - tcptwist.block<3, 1>(0, 0));
+        rtTargetVel.block<3, 1>(3, 0) = KoF * errorRPY + DoF * (targetVel.block<3, 1>(3, 0) - tcptwist.block<3, 1>(3, 0));
+        //        rtTargetVel = targetVel;
+
+
+
+        float normLinearVelocity = rtTargetVel.block<3, 1>(0, 0).norm();
+        if (normLinearVelocity > cfg->maxLinearVel)
+        {
+            rtTargetVel.block<3, 1>(0, 0) = cfg->maxLinearVel * rtTargetVel.block<3, 1>(0, 0) / normLinearVelocity;
+        }
+
+        float normAngularVelocity = rtTargetVel.block<3, 1>(3, 0).norm();
+        if (normAngularVelocity > cfg->maxAngularVel)
+        {
+            rtTargetVel.block<3, 1>(3, 0) = cfg->maxAngularVel * rtTargetVel.block<3, 1>(3, 0) / normAngularVelocity;
+        }
+
+
+        // cartesian vel controller
+        Eigen::VectorXf x;
+        auto mode = rtGetControlStruct().mode;
+
+        if (mode == VirtualRobot::IKSolver::All)
+        {
+            x.resize(6);
+            for (size_t i = 0; i < 6; i++)
+            {
+                x(i) = rtTargetVel(i);
+            }
+        }
+        else if (mode == VirtualRobot::IKSolver::Position)
+        {
+            x.resize(3);
+            for (size_t i = 0; i < 3; i++)
+            {
+                x(i) = rtTargetVel(i);
+            }
+
+        }
+        else if (mode == VirtualRobot::IKSolver::Orientation)
+        {
+            x.resize(3);
+            for (size_t i = 0; i < 3; i++)
+            {
+                x(i) = rtTargetVel(i + 3);
+            }
+        }
+        else
+        {
+
+            // No mode has been set
+            return;
+        }
+
+        Eigen::VectorXf jnv = Eigen::VectorXf::Zero(tcpController->rns->getSize());
+        float jointLimitAvoidanceKp = rtGetControlStruct().avoidJointLimitsKp;
+        if (jointLimitAvoidanceKp > 0)
+        {
+            jnv += jointLimitAvoidanceKp * tcpController->calculateJointLimitAvoidance();
+        }
+        for (size_t i = 0; i < tcpController->rns->getSize(); i++)
+        {
+            jnv(i) += rtGetControlStruct().nullspaceJointVelocities.at(i);
+        }
+
+        Eigen::VectorXf jointTargetVelocities = tcpController->calculate(x, jnv, mode);
+        ARMARX_CHECK_EXPRESSION(!targets.empty());
+        ARMARX_CHECK_LESS(targets.size(), 1000);
+        for (size_t i = 0; i < targets.size(); ++i)
+        {
+            targets.at(i)->velocity = jointTargetVelocities(i);
+
+            if (!targets.at(i)->isValid() || fabs(targets.at(i)->velocity) > cfg->maxJointVelocity)
+            {
+                ARMARX_IMPORTANT << deactivateSpam(1, std::to_string(i))
+                                 << "Velocity controller target is invalid - setting to zero! set value: " << targets.at(i)->velocity;
+                targets.at(i)->velocity = 0.0f;
+            }
+
+        }
+
+
+    }
+
+
+    void NJointTSMultiMPController::learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&)
+    {
+        taskSpaceDMPController->learnDMPFromFiles(fileNames);
+
+        ARMARX_INFO << "Learned DMP ... ";
+    }
+
+    void NJointTSMultiMPController::setSpeed(Ice::Double times, const Ice::Current&)
+    {
+        LockGuardType guard {controllerMutex};
+        taskSpaceDMPController->setSpeed(times);
+    }
+
+    void NJointTSMultiMPController::setViaPoints(Ice::Double u, const Ice::DoubleSeq& viapoint, const Ice::Current&)
+    {
+        LockGuardType guard {controllerMutex};
+        ARMARX_INFO << "setting via points ";
+        taskSpaceDMPController->setViaPose(u, viapoint);
+    }
+
+    void NJointTSMultiMPController::removeAllViaPoints(const Ice::Current&)
+    {
+        LockGuardType guard {controllerMutex};
+        ARMARX_INFO << "setting via points ";
+        taskSpaceDMPController->removeAllViaPoints();
+    }
+
+
+    void NJointTSMultiMPController::setGoals(const Ice::DoubleSeq& goals, const Ice::Current& ice)
+    {
+        taskSpaceDMPController->setGoalPoseVec(goals);
+    }
+
+    void NJointTSMultiMPController::setControllerTarget(Ice::Float avoidJointLimitsKp, NJointTaskSpaceDMPControllerMode::CartesianSelection mode, const Ice::Current&)
+    {
+        LockGuardType guard {controlDataMutex};
+        getWriterControlStruct().avoidJointLimitsKp = avoidJointLimitsKp;
+        getWriterControlStruct().mode = ModeFromIce(mode);
+        writeControlStruct();
+    }
+
+    VirtualRobot::IKSolver::CartesianSelection NJointTSMultiMPController::ModeFromIce(const NJointTaskSpaceDMPControllerMode::CartesianSelection mode)
+    {
+        if (mode == NJointTaskSpaceDMPControllerMode::CartesianSelection::ePosition)
+        {
+            return VirtualRobot::IKSolver::CartesianSelection::Position;
+        }
+        if (mode == NJointTaskSpaceDMPControllerMode::CartesianSelection::eOrientation)
+        {
+            return VirtualRobot::IKSolver::CartesianSelection::Orientation;
+        }
+        if (mode == NJointTaskSpaceDMPControllerMode::CartesianSelection::eAll)
+        {
+            return VirtualRobot::IKSolver::CartesianSelection::All;
+        }
+        ARMARX_ERROR_S << "invalid mode " << mode;
+        return (VirtualRobot::IKSolver::CartesianSelection)0;
+    }
+
+
+    void NJointTSMultiMPController::setTorqueKp(const StringFloatDictionary& torqueKp, const Ice::Current&)
+    {
+        LockGuardType guard {controlDataMutex};
+        for (size_t i = 0; i < tcpController->rns->getSize(); i++)
+        {
+            getWriterControlStruct().torqueKp.at(i) = torqueKp.at(tcpController->rns->getNode(i)->getName());
+        }
+        writeControlStruct();
+    }
+
+    void NJointTSMultiMPController::setNullspaceJointVelocities(const StringFloatDictionary& nullspaceJointVelocities, const Ice::Current&)
+    {
+        LockGuardType guard {controlDataMutex};
+        for (size_t i = 0; i < tcpController->rns->getSize(); i++)
+        {
+            getWriterControlStruct().nullspaceJointVelocities.at(i) = nullspaceJointVelocities.at(tcpController->rns->getNode(i)->getName());
+        }
+        writeControlStruct();
+    }
+
+    void NJointTSMultiMPController::resetDMP(const Ice::Current&)
+    {
+        if (started)
+        {
+            ARMARX_INFO << "Cannot reset running DMP";
+        }
+        firstRun = true;
+    }
+
+    void NJointTSMultiMPController::stopDMP(const Ice::Current&)
+    {
+        mpMagazine.at(mpName).firstRun = true;
+        mpMagazine.at(mpName).status = mpStatus::stopped;
+        //        started = false;
+        //        firstRun = true;
+    }
+
+    void NJointTSMultiMPController::runDMP(const std::string& mpName, const Ice::DoubleSeq&  goals, double tau, const Ice::Current&)
+    {
+        mpMagazine.at(mpName).firstRun = true;
+        while (mpMagazine.at(mpName).firstRun)
+        {
+            usleep(100);
+        }
+        while (!interfaceData.updateReadBuffer())
+        {
+            usleep(100);
+        }
+        //        firstRun = true;
+        //        while (firstRun)
+        //        {
+        //            usleep(100);
+        //        }
+        //        while (!interfaceData.updateReadBuffer())
+        //        {
+        //            usleep(100);
+        //        }
+
+        Eigen::Matrix4f pose = interfaceData.getReadBuffer().currentTcpPose;
+        ARMARX_INFO << "current pose: " << pose;
+        LockGuardType guard {controllerMutex};
+        mpMagazine.at(mpName).mpController->prepareExecution(mpMagazine.at(mpName).mpController->eigen4f2vec(pose), goals, tau);
+        mpMagazine.at(mpName).status = mpStatus::running;
+        //        taskSpaceDMPController->prepareExecution(taskSpaceDMPController->eigen4f2vec(pose), goals, tau);
+        //        finished = false;
+
+        //        ARMARX_INFO << "run DMP";
+        //        started = true;
+    }
+
+
+
+
+    void NJointTSMultiMPController::rtPreActivateController()
+    {
+    }
+
+    void NJointTSMultiMPController::rtPostDeactivateController()
+    {
+
+    }
+
+    void NJointTSMultiMPController::onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx& debugObs)
+    {
+        std::string datafieldName = debugName;
+        StringVariantBaseMap datafields;
+        auto values = debugOutputData.getUpToDateReadBuffer().latestTargetVelocities;
+        for (auto& pair : values)
+        {
+            datafieldName = pair.first  + "_" + debugName;
+            datafields[datafieldName] = new Variant(pair.second);
+        }
+
+        auto dmpTargets = debugOutputData.getUpToDateReadBuffer().dmpTargets;
+        for (auto& pair : dmpTargets)
+        {
+            datafieldName = pair.first  + "_" + debugName;
+            datafields[datafieldName] = new Variant(pair.second);
+        }
+
+        auto currentPose = debugOutputData.getUpToDateReadBuffer().currentPose;
+        for (auto& pair : currentPose)
+        {
+            datafieldName = pair.first + "_" + debugName;
+            datafields[datafieldName] = new Variant(pair.second);
+        }
+
+        datafieldName = "canVal_" + debugName;
+        datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal);
+        datafieldName = "mpcFactor_" + debugName;
+        datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().mpcFactor);
+        datafieldName = "error_" + debugName;
+        datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().error);
+        datafieldName = "posError_" + debugName;
+        datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().posError);
+        datafieldName = "oriError_" + debugName;
+        datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().oriError);
+        datafieldName = "deltaT_" + debugName;
+        datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT);
+        datafieldName = "DMPController_" + debugName;
+
+        debugObs->setDebugChannel(datafieldName, datafields);
+    }
+
+    void NJointTSMultiMPController::onInitComponent()
+    {
+        ARMARX_INFO << "init ...";
+        targetVels.resize(6);
+        NJointTSMultiMPControllerControlData initData;
+        initData.targetTSVel.resize(6);
+        for (size_t i = 0; i < 6; ++i)
+        {
+            initData.targetTSVel(i) = 0;
+            targetVels(i) = 0;
+            initData.targetPose = tcp->getPoseInRootFrame();
+        }
+        initData.nullspaceJointVelocities.resize(tcpController->rns->getSize(), 0);
+        initData.torqueKp.resize(tcpController->rns->getSize(), 0);
+        initData.torqueKd.resize(tcpController->rns->getSize(), 0);
+        initData.mode = ModeFromIce(cfg->mode);
+        reinitTripleBuffer(initData);
+
+
+
+        controllerTask = new PeriodicTask<NJointTSMultiMPController>(this, &NJointTSMultiMPController::controllerRun, 0.3);
+        controllerTask->start();
+
+
+    }
+
+    void NJointTSMultiMPController::onDisconnectComponent()
+    {
+        ARMARX_INFO << "stopped ...";
+        controllerTask->stop();
+    }
+
+
+
+}
diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.h
new file mode 100644
index 000000000..55d7cfb33
--- /dev/null
+++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSMultiMPController.h
@@ -0,0 +1,195 @@
+/* Jeff
+ *
+ */
+#pragma once
+
+#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h>
+#include <VirtualRobot/Robot.h>
+#include <RobotAPI/components/units/RobotUnit/RobotUnit.h>
+#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h>
+#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h>
+#include <VirtualRobot/IK/DifferentialIK.h>
+#include <dmp/representation/dmp/umitsmp.h>
+#include <RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.h>
+#include <RobotAPI/libraries/core/CartesianVelocityController.h>
+
+#include <RobotAPI/libraries/DMPController/TaskSpaceDMPController.h>
+
+using namespace DMP;
+namespace armarx
+{
+    TYPEDEF_PTRS_HANDLE(NJointTSMultiMPController);
+    TYPEDEF_PTRS_HANDLE(NJointTSMultiMPControllerControlData);
+
+    using ViaPoint = std::pair<double, DVec >;
+    using ViaPointsSet = std::vector<ViaPoint >;
+    class NJointTSMultiMPControllerControlData
+    {
+    public:
+        Eigen::VectorXf targetTSVel;
+        Eigen::Matrix4f targetPose;
+        // cartesian velocity control data
+        std::vector<float> nullspaceJointVelocities;
+        float avoidJointLimitsKp = 0;
+        std::vector<float> torqueKp;
+        std::vector<float> torqueKd;
+        VirtualRobot::IKSolver::CartesianSelection mode = VirtualRobot::IKSolver::All;
+    };
+
+    class pidController
+    {
+    public:
+        float Kp = 0, Kd = 0;
+        float lastError = 0;
+        float update(float dt, float error)
+        {
+            float derivative = (error - lastError) / dt;
+            float retVal = Kp * error + Kd * derivative;
+            lastError = error;
+            return retVal;
+        }
+    };
+
+    /**
+     * @brief The NJointTSMultiMPController class
+     * @ingroup Library-RobotUnit-NJointControllers
+     */
+    class NJointTSMultiMPController :
+        public NJointControllerWithTripleBuffer<NJointTSMultiMPControllerControlData>,
+        public NJointTaskSpaceDMPControllerInterface
+    {
+    public:
+        using ConfigPtrT = NJointTaskSpaceDMPControllerConfigPtr;
+        NJointTSMultiMPController(const RobotUnitPtr&, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&);
+
+        std::string getClassName(const Ice::Current&) const override;
+        void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override;
+
+        // NJointTSMultiMPControllerInterface interface
+        void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) override;
+        bool isFinished(const Ice::Current&) override
+        {
+            return finished;
+        }
+
+        void runDMP(const std::string& mpName, const Ice::DoubleSeq& goals, Ice::Double tau, const Ice::Current&) override;
+        void setSpeed(Ice::Double times, const Ice::Current&) override;
+        void setViaPoints(Ice::Double u, const Ice::DoubleSeq& viapoint, const Ice::Current&) override;
+        void removeAllViaPoints(const Ice::Current&) override;
+
+        void setGoals(const Ice::DoubleSeq& goals, const Ice::Current&) override;
+
+        void setControllerTarget(Ice::Float avoidJointLimitsKp, NJointTaskSpaceDMPControllerMode::CartesianSelection mode, const Ice::Current&) override;
+        void setTorqueKp(const StringFloatDictionary& torqueKp, const Ice::Current&) override;
+        void setNullspaceJointVelocities(const StringFloatDictionary& nullspaceJointVelocities, const Ice::Current&) override;
+
+
+        void resetDMP(const Ice::Current&) override;
+        void stopDMP(const std::string& mpName, const Ice::Current&) override;
+        double getCanVal(const Ice::Current&) override
+        {
+            return taskSpaceDMPController->canVal / cfg->timeDuration;
+        }
+
+    protected:
+        void rtPreActivateController() override;
+        void rtPostDeactivateController() override;
+        VirtualRobot::IKSolver::CartesianSelection ModeFromIce(const NJointTaskSpaceDMPControllerMode::CartesianSelection mode);
+        virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override;
+
+        void onInitComponent() override;
+        void onDisconnectComponent() override;
+        void controllerRun();
+
+    private:
+        struct DebugBufferData
+        {
+            StringFloatDictionary latestTargetVelocities;
+            StringFloatDictionary dmpTargets;
+            StringFloatDictionary currentPose;
+            double currentCanVal;
+            double mpcFactor;
+            double error;
+            double phaseStop;
+            double posError;
+            double oriError;
+            double deltaT;
+        };
+
+        TripleBuffer<DebugBufferData> debugOutputData;
+
+        struct NJointTSMultiMPControllerSensorData
+        {
+            double currentTime;
+            double deltaT;
+            Eigen::Matrix4f currentPose;
+            Eigen::VectorXf currentTwist;
+        };
+        TripleBuffer<NJointTSMultiMPControllerSensorData> controllerSensorData;
+
+        struct NJointTSMultiMPControllerInterfaceData
+        {
+            Eigen::Matrix4f currentTcpPose;
+        };
+
+        TripleBuffer<NJointTSMultiMPControllerInterfaceData> interfaceData;
+
+        std::vector<const SensorValue1DoFActuatorTorque*> torqueSensors;
+        std::vector<const SensorValue1DoFGravityTorque*> gravityTorqueSensors;
+        std::vector<ControlTarget1DoFActuatorVelocity*> targets;
+        std::vector<const SensorValue1DoFActuatorVelocity*> velocitySensors;
+        std::vector<const SensorValue1DoFActuatorPosition*> positionSensors;
+
+
+
+        // velocity ik controller parameters
+        std::vector<pidController> torquePIDs;
+        CartesianVelocityControllerPtr tcpController;
+        std::string nodeSetName;
+
+        /* set up movement magazine and parameters
+         *
+         */
+        // DMP magazine
+        enum mpStatus {running, paused, finished};
+        struct mp
+        {
+            TaskSpaceDMPControllerPtr mpController;
+            mpStatus status;
+            bool firstRun;
+        };
+        std::map<std::string, mp> mpMagazine;
+        //        TaskSpaceDMPControllerPtr taskSpaceDMPController;
+        // dmp parameters
+        //        bool finished;
+        //        bool started;
+
+        VirtualRobot::DifferentialIKPtr ik;
+        VirtualRobot::RobotNodePtr tcp;
+        Eigen::VectorXf targetVels;
+        Eigen::Matrix4f targetPose;
+
+        NJointTaskSpaceDMPControllerConfigPtr cfg;
+        mutable MutexType controllerMutex;
+        PeriodicTask<NJointTSMultiMPController>::pointer_type controllerTask;
+
+
+        std::string debugName;
+
+        Eigen::VectorXf filtered_qvel;
+        Eigen::Vector3f filtered_position;
+        float vel_filter_factor;
+        float pos_filter_factor;
+        bool firstRun;
+
+        float KpF;
+        float DpF;
+        float KoF;
+        float DoF;
+
+        Eigen::VectorXf jointHighLimits;
+        Eigen::VectorXf jointLowLimits;
+    };
+
+} // namespace armarx
+
-- 
GitLab