diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.cpp
index 08f5a512cd04670a0575adf645acbdeeff8ae21a..8069aeafdd1ed58ed6b8a45df986daff19b4ea83 100644
--- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.cpp
+++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.cpp
@@ -19,12 +19,14 @@ namespace armarx
 
             << "KpPos                       " << cfg.KpPos << '\n'
             << "KpOri                       " << cfg.KpOri << '\n'
-            << "maxPosVel                   " << cfg.maxPosVel << '\n'
-            << "maxOriVel                   " << cfg.maxOriVel << '\n'
+            << "maxTcpPosVel                " << cfg.maxTcpPosVel << '\n'
+            << "maxTcpOriVel                " << cfg.maxTcpOriVel << '\n'
+            << "maxElpPosVel                " << cfg.maxElbPosVel << '\n'
 
             << "jointLimitAvoidanceKp       " << cfg.jointLimitAvoidanceKp << '\n'
-            << "jointLimitAvoidanceScale    " << cfg.jointLimitAvoidanceScale << '\n'
             << "elbowKp                     " << cfg.elbowKp << '\n'
+            << "nullspaceTargetKp           " << cfg.nullspaceTargetKp << '\n'
+
             ;
         return out;
     }
@@ -165,6 +167,26 @@ namespace armarx
                 _rtPosController->setTarget(r.tcpTarget, r.elbowTarget);
             }
         }
+        if (_tripBufNullspaceTarget.updateReadBuffer())
+        {
+            ARMARX_RT_LOGF_IMPORTANT("updates in tripple buffer NullspaceTarget");
+            auto& r = _tripBufNullspaceTarget._getNonConstReadBuffer();
+            if (r.updated)
+            {
+                r.updated = false;
+                if (r.clearRequested)
+                {
+                    _rtPosController->clearNullspaceTarget();
+                }
+                else
+                {
+                    ARMARX_RT_LOGF_IMPORTANT("_rtPosController->setNullspaceTarget");
+                    _rtPosController->setNullspaceTarget(r.nullspaceTarget);
+                }
+                r.clearRequested = false;
+
+            }
+        }
 
         //run
         //bool reachedTarget = false;
@@ -293,6 +315,27 @@ namespace armarx
         _stopNowRequested = true;
     }
 
+    void NJointCartesianNaturalPositionController::setNullspaceTarget(const Ice::FloatSeq& nullspaceTarget, const Ice::Current&)
+    {
+        std::lock_guard g{_tripBufPosCtrlMut};
+        auto& w = _tripBufNullspaceTarget.getWriteBuffer();
+        w.nullspaceTarget = nullspaceTarget;
+        w.clearRequested = false;
+        w.updated = true;
+        _tripBufNullspaceTarget.commitWrite();
+        ARMARX_IMPORTANT << "set new nullspaceTarget\n" << nullspaceTarget;
+    }
+
+    void NJointCartesianNaturalPositionController::clearNullspaceTarget(const Ice::Current&)
+    {
+        std::lock_guard g{_tripBufPosCtrlMut};
+        auto& w = _tripBufNullspaceTarget.getWriteBuffer();
+        w.clearRequested = true;
+        w.updated = true;
+        _tripBufNullspaceTarget.commitWrite();
+        ARMARX_IMPORTANT << "reset nullspaceTarget";
+    }
+
     void NJointCartesianNaturalPositionController::onPublish(
         const SensorAndControl&,
         const DebugDrawerInterfacePrx& drawer,
@@ -374,3 +417,5 @@ namespace armarx
 }
 
 
+
+
diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.h
index 4bfd448af1a0f1b40337ec92b58fe03ed3cdfc50..3cb3403dd7e081bc4db25a074e5baa9cbbcd5cb9 100644
--- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.h
+++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianNaturalPositionController.h
@@ -44,6 +44,8 @@ namespace armarx
         void setTarget(const Eigen::Matrix4f& tcpTarget, const Eigen::Vector3f& elbowTarget, bool setOrientation, const Ice::Current& = Ice::emptyCurrent) override;
         void setFeedForwardVelocity(const Eigen::Vector6f& vel, const Ice::Current&) override;
         void stopMovement(const Ice::Current& = Ice::emptyCurrent) override;
+        void setNullspaceTarget(const Ice::FloatSeq& nullspaceTarget, const Ice::Current&) override;
+        void clearNullspaceTarget(const Ice::Current&) override;
 
         void setVisualizationRobotGlobalPose(const Eigen::Matrix4f& p, const Ice::Current& = Ice::emptyCurrent) override;
         void resetVisualizationRobotGlobalPose(const Ice::Current& = Ice::emptyCurrent) override;
@@ -71,6 +73,12 @@ namespace armarx
             CartesianNaturalPositionControllerConfig cfg;
             bool updated = false;
         };
+        struct TB_NT
+        {
+            std::vector<float> nullspaceTarget;
+            bool clearRequested = false;
+            bool updated = false;
+        };
 
         struct RtToNonRtData
         {
@@ -122,6 +130,7 @@ namespace armarx
         mutable std::recursive_mutex    _tripBufPosCtrlMut;
         //TripleBuffer<CtrlData>          _tripBufPosCtrl;
         TripleBuffer<TB_Target>         _tripBufTarget;
+        TripleBuffer<TB_NT>             _tripBufNullspaceTarget;
         TripleBuffer<TB_FFvel>          _tripBufFFvel;
         TripleBuffer<TB_Cfg>            _tripBufCfg;
 
@@ -140,5 +149,7 @@ namespace armarx
         //std::atomic<float>              _publishForceCurrent{0};
         //std::atomic_bool                _publishForceThresholdInRobotRootZ{0};
 
+
+
     };
 }
diff --git a/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidget.ui b/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidget.ui
index a9c862e336c5d771825234f05307db653f1c8c8c..dd7b4452001a5913c35ca23c85c549e56682657c 100644
--- a/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidget.ui
+++ b/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidget.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>697</width>
-    <height>553</height>
+    <width>767</width>
+    <height>493</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -336,6 +336,64 @@
     </item>
    </layout>
   </widget>
+  <widget class="QGroupBox" name="groupBoxNullspaceTargets">
+   <property name="geometry">
+    <rect>
+     <x>390</x>
+     <y>160</y>
+     <width>371</width>
+     <height>291</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Nullspace Targets</string>
+   </property>
+   <layout class="QGridLayout" name="gridLayout_6">
+    <item row="1" column="1">
+     <widget class="QWidget" name="widgetNullspaceTargets" native="true">
+      <layout class="QGridLayout" name="gridLayoutNullspaceTargets">
+       <item row="0" column="1">
+        <widget class="QSlider" name="horizontalSliderExampleJoint1">
+         <property name="maximum">
+          <number>360</number>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <widget class="QCheckBox" name="checkBoxExampleJoint1">
+         <property name="text">
+          <string>Joint1</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QLabel" name="labelExampleJoint1">
+         <property name="text">
+          <string>0</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item row="2" column="1">
+     <spacer name="verticalSpacer_2">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>40</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+   </layout>
+  </widget>
  </widget>
  <resources/>
  <connections/>
diff --git a/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.cpp b/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.cpp
index 8a0907fad01eb9684bbb6f117f9d233e56e23c17..1536cac11e7c8627cfe9ca73b1d79c6f07846f56 100644
--- a/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.cpp
+++ b/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.cpp
@@ -35,6 +35,8 @@ namespace armarx
         std::lock_guard g{_allMutex};
         _ui.setupUi(getWidget());
 
+        connect(this, SIGNAL(invokeConnectComponentQt()), this, SLOT(onConnectComponentQt()), Qt::QueuedConnection);
+        connect(this, SIGNAL(invokeDisconnectComponentQt()), this, SLOT(onDisconnectComponentQt()), Qt::QueuedConnection);
 
 
         //connect(_ui.pushButtonStop,             SIGNAL(clicked()), this, SLOT(on_pushButtonStop_clicked()));
@@ -116,10 +118,14 @@ namespace armarx
             _robot = RemoteRobot::createLocalCloneFromFile(_robotStateComponent, VirtualRobot::RobotIO::eStructure);
         }
 
+        _controller.reset();
+        invokeConnectComponentQt();
+    }
+    void CartesianNaturalPositionControllerWidgetController::onConnectComponentQt()
+    {
         _ui.comboBoxSide->addItem("Right");
         _ui.comboBoxSide->addItem("Left");
         _ui.comboBoxSide->setCurrentIndex(0);
-        _controller.reset();
 
     }
     void CartesianNaturalPositionControllerWidgetController::onDisconnectComponent()
@@ -137,6 +143,31 @@ namespace armarx
 
         VirtualRobot::RobotNodeSetPtr rns = _robot->getRobotNodeSet(side + "Arm");
 
+        _ui.gridLayoutNullspaceTargets->removeWidget(_ui.checkBoxExampleJoint1);
+        _ui.gridLayoutNullspaceTargets->removeWidget(_ui.labelExampleJoint1);
+        _ui.gridLayoutNullspaceTargets->removeWidget(_ui.horizontalSliderExampleJoint1);
+
+        for (size_t i = 0; i < rns->getSize(); i++)
+        {
+            QCheckBox* checkBox = new QCheckBox(QString::fromStdString(rns->getNode(i)->getName()));
+            QSlider* slider = new QSlider(Qt::Orientation::Horizontal);
+            slider->setMinimum(rns->getNode(i)->getJointLimitLo() / M_PI * 180);
+            slider->setMaximum(rns->getNode(i)->getJointLimitHi() / M_PI * 180);
+            slider->setValue(rns->getNode(i)->getJointValue() / M_PI * 180);
+            QLabel* label = new QLabel(QString::number(slider->value()));
+            _ui.gridLayoutNullspaceTargets->addWidget(checkBox, i, 0);
+            _ui.gridLayoutNullspaceTargets->addWidget(slider, i, 1);
+            _ui.gridLayoutNullspaceTargets->addWidget(label, i, 2);
+            connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(on_anyNullspaceCheckbox_stateChanged(int)));
+            connect(slider, SIGNAL(valueChanged(int)), this, SLOT(on_anyNullspaceSlider_valueChanged(int)));
+            NullspaceTarget nt;
+            nt.checkBox = checkBox;
+            nt.slider = slider;
+            nt.label = label;
+            nt.i = i;
+            _nullspaceTargets.emplace_back(nt);
+        }
+
 
         VirtualRobot::RobotNodePtr sho1 = rns->getNode(1);
         Eigen::Vector3f shoulder = sho1->getPositionInRootFrame();
@@ -234,6 +265,26 @@ namespace armarx
         updateTarget();
     }
 
+    void CartesianNaturalPositionControllerWidgetController::updateNullspaceTargets()
+    {
+        std::vector<float> nsTargets;
+        for (const NullspaceTarget& nt : _nullspaceTargets)
+        {
+            nsTargets.push_back(nt.checkBox->isChecked() ? nt.slider->value() * M_PI / 180 : std::nanf(""));
+        }
+        _controller->getInternalController()->setNullspaceTarget(nsTargets);
+    }
+
+    void CartesianNaturalPositionControllerWidgetController::on_anyNullspaceCheckbox_stateChanged(int)
+    {
+        updateNullspaceTargets();
+    }
+
+    void CartesianNaturalPositionControllerWidgetController::on_anyNullspaceSlider_valueChanged(int)
+    {
+        updateNullspaceTargets();
+    }
+
     //void CartesianNaturalPositionControllerWidgetController::on_pushButtonSendSettings_clicked()
     //{
     //    std::lock_guard g{_allMutex};
diff --git a/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.h b/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.h
index f8600c5f83f46920c3af787bb54fdc516a2a36ea..08b0f52714eae1eb6d33331b2433d9829c4bba4a 100644
--- a/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.h
+++ b/source/RobotAPI/gui-plugins/CartesianNaturalPositionController/CartesianNaturalPositionControllerWidgetController.h
@@ -64,6 +64,14 @@ namespace armarx
         Q_OBJECT
 
     public:
+        struct NullspaceTarget
+        {
+            QCheckBox* checkBox;
+            QSlider* slider;
+            QLabel* label;
+            size_t i;
+        };
+
         /**
          * Controller Constructor
          */
@@ -102,14 +110,19 @@ namespace armarx
 
     public slots:
         /* QT slot declarations */
+        void onConnectComponentQt();
         void on_pushButtonCreateController_clicked();
         void on_anyDeltaPushButton_clicked();
         void on_sliders_valueChanged(int);
         void on_checkBoxAutoKp_stateChanged(int);
         void on_checkBoxSetOri_stateChanged(int);
+        void on_anyNullspaceCheckbox_stateChanged(int);
+        void on_anyNullspaceSlider_valueChanged(int);
 
     signals:
         /* QT signal declarations */
+        void invokeConnectComponentQt();
+        void invokeDisconnectComponentQt();
 
     private:
         void deleteOldController();
@@ -118,6 +131,7 @@ namespace armarx
         void updateTarget();
         void updateKpSliders();
         void updateKpSliderLabels();
+        void updateNullspaceTargets();
 
 
 
@@ -140,6 +154,8 @@ namespace armarx
         std::map<QObject*, Eigen::Vector3f>             _deltaMapPos;
         std::map<QObject*, Eigen::Vector3f>             _deltaMapOri;
         CartesianNaturalPositionControllerConfig        _runCfg;
+
+        std::vector<NullspaceTarget>                    _nullspaceTargets;
     };
 }
 
diff --git a/source/RobotAPI/interface/core/CartesianNaturalPositionControllerConfig.ice b/source/RobotAPI/interface/core/CartesianNaturalPositionControllerConfig.ice
index 86e63f5024c6e6091ec7a9834a9dac72fad3064a..9ea75137bd5490f7c2b68f41e4fee490aeeb9e9b 100644
--- a/source/RobotAPI/interface/core/CartesianNaturalPositionControllerConfig.ice
+++ b/source/RobotAPI/interface/core/CartesianNaturalPositionControllerConfig.ice
@@ -22,6 +22,9 @@
 
 #pragma once
 
+//#include <ArmarXCore/interface/serialization/Eigen.ice>
+#include <ArmarXCore/interface/core/BasicVectorTypes.ice>
+
 module armarx
 {
     struct CartesianNaturalPositionControllerConfig
@@ -29,11 +32,13 @@ module armarx
         float maxPositionAcceleration       = 500;
         float maxOrientationAcceleration    = 1;
         float maxNullspaceAcceleration      = 2;
-        float maxJointVelocity              = 999;
+
+        Ice::FloatSeq maxJointVelocity;
+        Ice::FloatSeq maxNullspaceVelocity;
 
         float jointLimitAvoidanceKp         = 0.01;
-        float jointLimitAvoidanceScale      = 0;
         float elbowKp                       = 1;
+        float nullspaceTargetKp             = 1;
 
         float thresholdOrientationNear      = 0.1;
         float thresholdOrientationReached   = 0.05;
@@ -41,8 +46,9 @@ module armarx
         float thresholdPositionReached      = 5;
 
 
-        float maxOriVel = 1;
-        float maxPosVel = 80;
+        float maxTcpPosVel = 80;
+        float maxTcpOriVel = 1;
+        float maxElbPosVel = 80;
         float KpOri     = 1;
         float KpPos     = 1;
     };
diff --git a/source/RobotAPI/interface/units/RobotUnit/NJointCartesianNaturalPositionController.ice b/source/RobotAPI/interface/units/RobotUnit/NJointCartesianNaturalPositionController.ice
index f3e2415eb87434be1ea6b5c4a647ade0b7903dc6..1dc159fe82c83f3c81d4cbb7375a2bcf304caa0b 100644
--- a/source/RobotAPI/interface/units/RobotUnit/NJointCartesianNaturalPositionController.ice
+++ b/source/RobotAPI/interface/units/RobotUnit/NJointCartesianNaturalPositionController.ice
@@ -26,6 +26,7 @@
 
 #include <RobotAPI/interface/units/RobotUnit/NJointController.ice>
 #include <RobotAPI/interface/core/CartesianNaturalPositionControllerConfig.ice>
+#include <ArmarXCore/interface/core/BasicVectorTypes.ice>
 
 module armarx
 {
@@ -52,6 +53,8 @@ module armarx
         void setConfig(CartesianNaturalPositionControllerConfig cfg);
         void setTarget(Eigen::Matrix4f tcpTarget, Eigen::Vector3f elbowTarget, bool setOrientation);
         void setFeedForwardVelocity(Eigen::Vector6f vel);
+        void setNullspaceTarget(Ice::FloatSeq nullspaceTarget);
+        void clearNullspaceTarget();
 
         //void setConfigAndWaypoints(NJointCartesianNaturalPositionControllerRuntimeConfig cfg, Eigen::Matrix4fSeq wps);
         //void setConfigAndWaypoint(NJointCartesianNaturalPositionControllerRuntimeConfig cfg, Eigen::Matrix4f wp);
diff --git a/source/RobotAPI/libraries/core/CartesianNaturalPositionController.cpp b/source/RobotAPI/libraries/core/CartesianNaturalPositionController.cpp
index 4d2e857bdb9d52db8ad197f472b9a0e9bfd05555..298626cd868ae9bfea02c7a104bb8889860ae551 100644
--- a/source/RobotAPI/libraries/core/CartesianNaturalPositionController.cpp
+++ b/source/RobotAPI/libraries/core/CartesianNaturalPositionController.cpp
@@ -52,46 +52,90 @@ namespace armarx
         rns(rns)
     {
         ARMARX_CHECK_NOT_NULL(rns);
+        clearNullspaceTarget();
     }
 
-    Eigen::VectorXf CartesianNaturalPositionController::LimitInfNormTo(Eigen::VectorXf vec, float maxValue)
+    Eigen::VectorXf CartesianNaturalPositionController::LimitInfNormTo(const Eigen::VectorXf& vec, const std::vector<float>& maxValue)
     {
-        float infNorm = vec.lpNorm<Eigen::Infinity>();
-        if (infNorm > maxValue)
+        if (maxValue.size() == 0)
         {
-            vec = vec / infNorm * maxValue;
+            return vec;
         }
-        return vec;
+        if (maxValue.size() != 1 && (int)maxValue.size() != vec.rows())
+        {
+            throw std::invalid_argument("maxValue.size != 1 and != maxValue.size");
+        }
+        float scale = 1;
+        for (int i = 0; i < vec.rows(); i++)
+        {
+            int j = maxValue.size() == 1 ? 0 : i;
+            if (std::abs(vec(i)) > maxValue.at(j) && maxValue.at(j) >= 0)
+            {
+                scale = std::min(scale, maxValue.at(j) / std::abs(vec(i)));
+            }
+        }
+        return vec / scale;
     }
 
-    const Eigen::VectorXf CartesianNaturalPositionController::calculate(float dt, VirtualRobot::IKSolver::CartesianSelection mode)
+    const Eigen::VectorXf CartesianNaturalPositionController::calculateNullspaceTargetDifference()
     {
-
-        int posLen = mode & VirtualRobot::IKSolver::Position ? 3 : 0;
-        int oriLen = mode & VirtualRobot::IKSolver::Orientation ? 3 : 0;
-        Eigen::Vector3f pdTcp = posLen ? pcTcp.getPositionDiff(tcpTarget) : Eigen::Vector3f::Zero();
-        Eigen::Vector3f odTcp = oriLen ? pcTcp.getOrientationDiff(tcpTarget) : Eigen::Vector3f::Zero();
-        Eigen::VectorXf cartesianVel(posLen + oriLen);
-        if (posLen)
-        {
-            cartesianVel.block<3, 1>(0, 0) = pdTcp;
-        }
-        if (oriLen)
+        Eigen::VectorXf jvNT = Eigen::VectorXf(rns->getSize());
+        for (size_t i = 0; i < rns->getSize(); i++)
         {
-            cartesianVel.block<3, 1>(posLen, 0) = odTcp;
+            if (std::isnan(nullspaceTarget(i)))
+            {
+                jvNT(i) = 0;
+            }
+            else
+            {
+                float diff = nullspaceTarget(i) - rns->getNode(i)->getJointValue();
+                if (rns->getNode(i)->isLimitless())
+                {
+                    diff = math::Helpers::AngleModPI(diff);
+                }
+                jvNT(i) = diff;
+            }
         }
+        return jvNT;
+    }
 
-        Eigen::Vector3f pdElb = pcElb.getPositionDiffVec3(elbowTarget);
-        //ARMARX_IMPORTANT << deactivateSpam(0.1) << VAROUT(pdElb) << VAROUT(elbowTarget) << VAROUT(pcElb.getTcp()->getPositionInRootFrame());
-        Eigen::VectorXf cartesianVelElb(3);
-        cartesianVelElb.block<3, 1>(0, 0) = pdElb;
-        Eigen::VectorXf jnv = Eigen::VectorXf::Zero(rns->getSize());
+    const Eigen::VectorXf CartesianNaturalPositionController::calculate(float dt, VirtualRobot::IKSolver::CartesianSelection mode)
+    {
+
+        //int posLen = mode & VirtualRobot::IKSolver::Position ? 3 : 0;
+        //int oriLen = mode & VirtualRobot::IKSolver::Orientation ? 3 : 0;
+        //Eigen::Vector3f pdTcp = posLen ? pcTcp.getPositionDiff(tcpTarget) : Eigen::Vector3f::Zero();
+        //Eigen::Vector3f odTcp = oriLen ? pcTcp.getOrientationDiff(tcpTarget) : Eigen::Vector3f::Zero();
+        //Eigen::VectorXf cartesianVel(posLen + oriLen);
+        //if (posLen)
+        //{
+        //    cartesianVel.block<3, 1>(0, 0) = pdTcp;
+        //}
+        //if (oriLen)
+        //{
+        //    cartesianVel.block<3, 1>(posLen, 0) = odTcp;
+        //}
+        Eigen::VectorXf cartesianVelTcp = pcTcp.calculate(tcpTarget, mode);
+
+        //Eigen::Vector3f pdElb = pcElb.getPositionDiffVec3(elbowTarget);
+        ////ARMARX_IMPORTANT << deactivateSpam(0.1) << VAROUT(pdElb) << VAROUT(elbowTarget) << VAROUT(pcElb.getTcp()->getPositionInRootFrame());
+        //Eigen::VectorXf cartesianVelElb(3);
+        //cartesianVelElb.block<3, 1>(0, 0) = pdElb;
+
+        Eigen::VectorXf cartesianVelElb = pcElb.calculatePos(elbowTarget);
+        Eigen::VectorXf jnvClamped = Eigen::VectorXf::Zero(rns->getSize());
         if (nullSpaceControlEnabled)
         {
-            Eigen::VectorXf jvElb = elbowKp * vcElb.calculate(cartesianVelElb, VirtualRobot::IKSolver::Position);
+            Eigen::VectorXf jnv = Eigen::VectorXf::Zero(rns->getSize());
+            Eigen::VectorXf jvElb = vcElb.calculate(cartesianVelElb, VirtualRobot::IKSolver::Position);
+
             Eigen::VectorXf jvLA = jointLimitAvoidanceKp * vcTcp.controller.calculateJointLimitAvoidance();
+            Eigen::VectorXf jvNT = nullspaceTargetKp * calculateNullspaceTargetDifference();
+            ARMARX_IMPORTANT << deactivateSpam(1) << VAROUT(jvNT);
+
             //ARMARX_IMPORTANT << VAROUT(jvElb);
-            jnv = jvElb + jvLA;
+            jnv = jvElb + jvLA + jvNT;
+            jnvClamped = LimitInfNormTo(jnv, maxNullspaceVelocity);
         }
 
         //ARMARX_IMPORTANT << VAROUT(jnv);
@@ -99,7 +143,7 @@ namespace armarx
         {
             vcTcp.switchMode(lastJointVelocity, mode);
         }
-        Eigen::VectorXf jv = vcTcp.calculate(cartesianVel, jnv, dt);
+        Eigen::VectorXf jv = vcTcp.calculate(cartesianVelTcp, jnvClamped, dt);
         //ARMARX_IMPORTANT << VAROUT(jv);
 
         Eigen::VectorXf jvClamped = LimitInfNormTo(jv, maxJointVelocity);
@@ -132,6 +176,25 @@ namespace armarx
         feedForwardVelocity.block<3, 1>(3, 0) = feedForwardVelocityOri;
     }
 
+    void CartesianNaturalPositionController::setNullspaceTarget(const Eigen::VectorXf& nullspaceTarget)
+    {
+        this->nullspaceTarget = nullspaceTarget;
+    }
+
+    void CartesianNaturalPositionController::setNullspaceTarget(const std::vector<float>& nullspaceTarget)
+    {
+        ARMARX_CHECK_EXPRESSION(rns->getSize() == nullspaceTarget.size());
+        for (size_t i = 0; i < rns->getSize(); i++)
+        {
+            this->nullspaceTarget(i) = nullspaceTarget.at(i);
+        }
+    }
+
+    void CartesianNaturalPositionController::clearNullspaceTarget()
+    {
+        this->nullspaceTarget = Eigen::VectorXf::Constant(rns->getSize(), std::nanf(""));
+    }
+
     void CartesianNaturalPositionController::clearFeedForwardVelocity()
     {
         feedForwardVelocity = Eigen::Vector6f::Zero();
@@ -172,6 +235,23 @@ namespace armarx
         return elbowTarget;
     }
 
+    const Eigen::VectorXf& CartesianNaturalPositionController::getCurrentNullspaceTarget() const
+    {
+        return nullspaceTarget;
+    }
+
+    bool CartesianNaturalPositionController::hasNullspaceTarget() const
+    {
+        for (int i = 0; i < nullspaceTarget.rows(); i++)
+        {
+            if (!std::isnan(nullspaceTarget(i)))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void CartesianNaturalPositionController::setNullSpaceControl(bool enabled)
     {
         nullSpaceControlEnabled = enabled;
@@ -188,8 +268,6 @@ namespace armarx
     void CartesianNaturalPositionController::setConfig(const CartesianNaturalPositionControllerConfig& cfg)
     {
         jointLimitAvoidanceKp           = cfg.jointLimitAvoidanceKp;
-        jointLimitAvoidanceScale        = cfg.jointLimitAvoidanceScale;
-        elbowKp                         = cfg.elbowKp;
 
         thresholdOrientationNear        = cfg.thresholdOrientationNear;
         thresholdOrientationReached     = cfg.thresholdOrientationReached;
@@ -197,11 +275,16 @@ namespace armarx
         thresholdPositionReached        = cfg.thresholdPositionReached;
 
         maxJointVelocity                = cfg.maxJointVelocity;
+        maxNullspaceVelocity            = cfg.maxNullspaceVelocity;
+
+        nullspaceTargetKp               = cfg.nullspaceTargetKp;
 
-        pcTcp.maxOriVel  = cfg.maxOriVel;
-        pcTcp.maxPosVel  = cfg.maxPosVel;
+        pcTcp.maxPosVel  = cfg.maxTcpPosVel;
+        pcTcp.maxOriVel  = cfg.maxTcpOriVel;
         pcTcp.KpOri      = cfg.KpOri;
         pcTcp.KpPos      = cfg.KpPos;
+        pcElb.maxPosVel  = cfg.maxElbPosVel;
+        pcElb.KpPos      = cfg.elbowKp;
 
         vcTcp.setMaxAccelerations(
             cfg.maxPositionAcceleration,
diff --git a/source/RobotAPI/libraries/core/CartesianNaturalPositionController.h b/source/RobotAPI/libraries/core/CartesianNaturalPositionController.h
index 59d8dc1eac6005a5cdc4bc5fecf3a0c0d376c28e..68ea5f35b597900f64a706c443ea4d19b4b8342c 100644
--- a/source/RobotAPI/libraries/core/CartesianNaturalPositionController.h
+++ b/source/RobotAPI/libraries/core/CartesianNaturalPositionController.h
@@ -58,12 +58,16 @@ namespace armarx
                                           );
 
 
-        static Eigen::VectorXf LimitInfNormTo(Eigen::VectorXf vec, float maxValue);
+        static Eigen::VectorXf LimitInfNormTo(const Eigen::VectorXf& vec, const std::vector<float>& maxValue);
+        const Eigen::VectorXf calculateNullspaceTargetDifference();
         const Eigen::VectorXf calculate(float dt, VirtualRobot::IKSolver::CartesianSelection mode = VirtualRobot::IKSolver::All);
 
         void setTarget(const Eigen::Matrix4f& tcpTarget, const Eigen::Vector3f& elbowTarget);
         void setFeedForwardVelocity(const Eigen::Vector6f& feedForwardVelocity);
         void setFeedForwardVelocity(const Eigen::Vector3f& feedForwardVelocityPos, const Eigen::Vector3f& feedForwardVelocityOri);
+        void setNullspaceTarget(const Eigen::VectorXf& nullspaceTarget);
+        void setNullspaceTarget(const std::vector<float>& nullspaceTarget);
+        void clearNullspaceTarget();
         void clearFeedForwardVelocity();
 
         float getPositionError() const;
@@ -77,6 +81,8 @@ namespace armarx
         const Eigen::Matrix4f& getCurrentTarget() const;
         const Eigen::Vector3f getCurrentTargetPosition() const;
         const Eigen::Vector3f& getCurrentElbowTarget() const;
+        const Eigen::VectorXf& getCurrentNullspaceTarget() const;
+        bool hasNullspaceTarget() const;
 
         void setNullSpaceControl(bool enabled);
 
@@ -92,13 +98,15 @@ namespace armarx
 
         Eigen::Matrix4f tcpTarget;
         Eigen::Vector3f elbowTarget;
+        Eigen::VectorXf nullspaceTarget = Eigen::VectorXf(0);
 
         float thresholdPositionReached      = 0.f;
         float thresholdOrientationReached   = 0.f;
         float thresholdPositionNear         = 0.f;
         float thresholdOrientationNear      = 0.f;
 
-        float maxJointVelocity = -1;
+        std::vector<float> maxJointVelocity;
+        std::vector<float> maxNullspaceVelocity;
 
 
         Eigen::Vector6f feedForwardVelocity = Eigen::Vector6f::Zero();
@@ -109,7 +117,7 @@ namespace armarx
         bool nullSpaceControlEnabled        = true;
         float jointLimitAvoidanceScale      = 0.f;
         float jointLimitAvoidanceKp         = 0.f;
-        float elbowKp                       = 0;
+        float nullspaceTargetKp             = 0;
 
     private:
         VirtualRobot::RobotNodeSetPtr rns;
diff --git a/source/RobotAPI/libraries/core/CartesianPositionController.cpp b/source/RobotAPI/libraries/core/CartesianPositionController.cpp
index efe266741f146f560da821f0c8f17ecc1cdb45fd..25a88e737bb0685abeb07b421158487039b023ac 100644
--- a/source/RobotAPI/libraries/core/CartesianPositionController.cpp
+++ b/source/RobotAPI/libraries/core/CartesianPositionController.cpp
@@ -69,6 +69,20 @@ namespace armarx
         return cartesianVel;
     }
 
+    Eigen::VectorXf CartesianPositionController::calculatePos(const Eigen::Vector3f& targetPos) const
+    {
+        Eigen::VectorXf cartesianVel(3);
+        Eigen::Vector3f currentPos = tcp->getPositionInRootFrame();
+        Eigen::Vector3f errPos = targetPos - currentPos;
+        Eigen::Vector3f posVel =  errPos * KpPos;
+        if (maxPosVel > 0)
+        {
+            posVel = math::MathUtils::LimitTo(posVel, maxPosVel);
+        }
+        cartesianVel.block<3, 1>(0, 0) = posVel;
+        return cartesianVel;
+    }
+
     float CartesianPositionController::getPositionError(const Eigen::Matrix4f& targetPose) const
     {
         Eigen::Vector3f targetPos = targetPose.block<3, 1>(0, 3);
diff --git a/source/RobotAPI/libraries/core/CartesianPositionController.h b/source/RobotAPI/libraries/core/CartesianPositionController.h
index cde4cd4734cf8daf6f21551e741e56c04629819a..a25b14e28aec91d34d7a0caa7b64e2bd4a0944b7 100644
--- a/source/RobotAPI/libraries/core/CartesianPositionController.h
+++ b/source/RobotAPI/libraries/core/CartesianPositionController.h
@@ -47,6 +47,7 @@ namespace armarx
         CartesianPositionController& operator=(CartesianPositionController&&) = default;
 
         Eigen::VectorXf calculate(const Eigen::Matrix4f& targetPose, VirtualRobot::IKSolver::CartesianSelection mode) const;
+        Eigen::VectorXf calculatePos(const Eigen::Vector3f& targetPos) const;
 
         float getPositionError(const Eigen::Matrix4f& targetPose) const;
         float getOrientationError(const Eigen::Matrix4f& targetPose) const;