From 8fd1e8ac16c07fdf1f9eb936d2a4ff82220ef516 Mon Sep 17 00:00:00 2001
From: Mirko Waechter <mirko.waechter@kit.edu>
Date: Fri, 6 Dec 2013 00:15:21 +0100
Subject: [PATCH] Extended DifferentialIK with optional weights for joint
 angles and tcp DOFs (the letter not tested yet) Extended DifferentialIK with
 considering joint limits

---
 source/RobotAPI/units/TCPControlUnit.cpp | 244 +++++++++++++++++++++--
 source/RobotAPI/units/TCPControlUnit.h   |  12 +-
 2 files changed, 240 insertions(+), 16 deletions(-)

diff --git a/source/RobotAPI/units/TCPControlUnit.cpp b/source/RobotAPI/units/TCPControlUnit.cpp
index 5f0123486..55a7689e5 100644
--- a/source/RobotAPI/units/TCPControlUnit.cpp
+++ b/source/RobotAPI/units/TCPControlUnit.cpp
@@ -33,6 +33,7 @@
 #include <VirtualRobot/XML/RobotIO.h>
 
 using namespace VirtualRobot;
+using namespace Eigen;
 
 
 
@@ -95,8 +96,8 @@ namespace armarx
         //        request();
 
         listener = getTopic<TCPControlUnitListenerPrx>("TCPControlUnitState");
-        topicTask = new PeriodicTask<TCPControlUnit>(this, &TCPControlUnit::periodicReport,cycleTime, false, "TCPDataProvider");
-        topicTask->start();
+//        topicTask = new PeriodicTask<TCPControlUnit>(this, &TCPControlUnit::periodicReport,cycleTime, false, "TCPDataProvider");
+//        topicTask->start();
     }
 
     void TCPControlUnit::onDisconnectComponent()
@@ -123,6 +124,8 @@ namespace armarx
     void TCPControlUnit::setTCPVelocity(const std::string &nodeSetName, const std::string &tcpName, const FramedVector3BasePtr &translationVelocity, const FramedVector3BasePtr &orientationVelocityRPY, const Ice::Current &c)
     {
         ScopedLock lock(dataMutex);
+        ARMARX_CHECK_EXPRESSION_W_HINT(localRobot->hasRobotNodeSet(nodeSetName), "The robot does not have the node set: " + nodeSetName);
+
         if(translationVelocity)
             ARMARX_VERBOSE << "Setting new Velocity for " << nodeSetName << "\n" << FramedVector3Ptr::dynamicCast(translationVelocity)->toEigen();
         if(orientationVelocityRPY)
@@ -134,7 +137,10 @@ namespace armarx
         if(tcpName.empty())
             data.tcpName = localRobot->getRobotNodeSet(nodeSetName)->getTCP()->getName();
         else
+        {
+            ARMARX_CHECK_EXPRESSION_W_HINT(localRobot->hasRobotNode(tcpName), "The robot does not have the node: " + tcpName);
             data.tcpName = tcpName;
+        }
         tcpVelocitiesMap[data.tcpName] = data;
 //        if(translationVelocity)
 //            targetTranslationVelocities[nodeSetName] = translationVelocity;
@@ -212,6 +218,8 @@ namespace armarx
 
         localRobot->setJointValues(jointValues);
 
+        periodicReport();
+
 //        dIKMap.clear();
         TCPVelocityDataMap::iterator it = tcpVelocitiesMap.begin();
         for(; it != tcpVelocitiesMap.end(); it++)
@@ -222,7 +230,13 @@ namespace armarx
             DIKMap::iterator itDIK = dIKMap.find(data.nodeSetName);
             if(itDIK == dIKMap.end())
                 dIKMap[data.nodeSetName].reset(new EDifferentialIK(nodeSet, localRobot->getRootNode()/*, VirtualRobot::JacobiProvider::eTranspose*/));
-            boost::shared_dynamic_cast<EDifferentialIK>(dIKMap[data.nodeSetName])->clearGoals();
+            EDifferentialIKPtr dIk = boost::shared_dynamic_cast<EDifferentialIK>(dIKMap[data.nodeSetName]);
+            dIk->clearGoals();
+            Eigen::VectorXf dofWeights(nodeSet->getSize());
+            dofWeights.setOnes();
+            dofWeights(0) = 0.2;
+            dofWeights(1) = 0.0;
+            dIk->setDOFWeights(dofWeights);
         }
 
         using namespace Eigen;
@@ -234,7 +248,6 @@ namespace armarx
             TCPVelocityData& data = it->second;
             RobotNodeSetPtr nodeSet = localRobot->getRobotNodeSet(data.nodeSetName);
             std::string refFrame = localRobot->getRootNode()->getName();
-
             IKSolver::CartesianSelection mode;
             if(data.translationVelocity && data.orientationVelocity)
             {
@@ -288,7 +301,7 @@ namespace armarx
             }
 //            ARMARX_VERBOSE << deactivateSpam(1) << "Old Pos: \n" << tcpNode->getGlobalPose();
 //            ARMARX_VERBOSE << deactivateSpam(1) << "New Goal: \n" << m;
-            dIK->setGoal(m, tcpNode,mode);
+            dIK->setGoal(m, tcpNode,mode, 0.001, 0.001/180.0f*3.14159);
 
 //            ARMARX_VERBOSE << deactivateSpam(1) << "Delta to Goal: " << dIK->getDeltaToGoal(tcpNode);
         }
@@ -301,12 +314,15 @@ namespace armarx
         {
             RobotNodeSetPtr robotNodeSet = localRobot->getRobotNodeSet(itDIK->first);
             EDifferentialIKPtr dIK = boost::shared_dynamic_cast<EDifferentialIK>(itDIK->second);
-            Eigen::VectorXf jointDelta = dIK->computeStep(1.0);
+            ARMARX_VERBOSE << deactivateSpam(1) << "Old Pos: \n" << robotNodeSet->getTCP()->getGlobalPose();
+            dIK->setVerbose(true);
+            Eigen::VectorXf jointDelta = dIK->computeSteps(0.5, 0.00001, 20);
+            ARMARX_VERBOSE << deactivateSpam(1) << "New Goal: \n" << robotNodeSet->getTCP()->getGlobalPose();
 
-            MatrixXf fullJac = dIK->calcFullJacobian();
-            MatrixXf fullJacInv = dIK->computePseudoInverseJacobianMatrix(fullJac);
-            Eigen::VectorXf jointLimitCompensation = CalcJointLimitAvoidanceDeltas(robotNodeSet,fullJac, fullJacInv);
-            jointDelta += jointLimitCompensation;
+//            MatrixXf fullJac = dIK->calcFullJacobian();
+//            MatrixXf fullJacInv = dIK->computePseudoInverseJacobianMatrix(fullJac);
+//            Eigen::VectorXf jointLimitCompensation = CalcJointLimitAvoidanceDeltas(robotNodeSet,fullJac, fullJacInv);
+//            jointDelta += jointLimitCompensation;
 
             jointDelta = applyMaxJointVelocity(jointDelta, maxJointVelocity);
 
@@ -344,13 +360,13 @@ namespace armarx
 
     void TCPControlUnit::periodicReport()
     {
-        ScopedLock lock(dataMutex);
+//        ScopedLock lock(dataMutex);
 
-        NameValueMap robotConfigMap = remoteRobotPrx->getConfig();
-//        NameValueMap::iterator it = robotConfigMap.begin();
-        std::map<std::string, float> jointValues(robotConfigMap.begin(), robotConfigMap.end());
+//        NameValueMap robotConfigMap = remoteRobotPrx->getConfig();
+////        NameValueMap::iterator it = robotConfigMap.begin();
+//        std::map<std::string, float> jointValues(robotConfigMap.begin(), robotConfigMap.end());
 
-        localRobot->setJointValues(jointValues);
+//        localRobot->setJointValues(jointValues);
         std::vector<RobotNodeSetPtr > nodeSets = localRobot->getRobotNodeSets();
         FramedPoseBaseMap tcpPoses;
         std::string rootFrame =  localRobot->getRootNode()->getName();
@@ -473,6 +489,7 @@ namespace armarx
         tolerancePosition.clear();
         toleranceRotation.clear();
         parents.clear();
+        tcpWeights = Eigen::VectorXf();
     }
 
     void EDifferentialIK::setRefFrame(RobotNodePtr coordSystem)
@@ -480,6 +497,203 @@ namespace armarx
         this->coordSystem = coordSystem;
     }
 
+    void EDifferentialIK::setDOFWeights(Eigen::VectorXf dofWeights)
+    {
+        this->dofWeights = dofWeights;
+    }
+
+    void EDifferentialIK::setResultDimensionWeights(Eigen::VectorXf tcpWeights)
+    {
+        this->tcpWeights = tcpWeights;
+    }
+
+    Eigen::VectorXf EDifferentialIK::computeSteps(float stepSize, float minumChange, int maxNStep)
+    {
+        VR_ASSERT(rns);
+        VR_ASSERT(nodes.size() == rns->getSize());
+        //std::vector<RobotNodePtr> rn = this->nodes;
+        RobotPtr robot = rns->getRobot();
+        VR_ASSERT(robot);
+        std::vector<float> jv(nodes.size(),0.0f);
+        std::vector<float> jvBest = rns->getJointValues();
+        int step = 0;
+        checkTolerances();
+        float lastDist = FLT_MAX;
+
+        VectorXf dThetaSum(nodes.size());
+        dThetaSum.setZero();
+        while (step<maxNStep)
+        {
+            VectorXf dTheta = this->computeStep(stepSize);
+            if(adjustDOFWeightsToJointLimits(dTheta))
+                dTheta = computeStep(stepSize);
+            dThetaSum += dTheta;
+            for (unsigned int i=0; i<nodes.size();i++)
+            {
+                jv[i] = (nodes[i]->getJointValue() + dTheta[i]);
+                if (boost::math::isnan(jv[i]) || boost::math::isinf(jv[i]))
+                {
+                    VR_WARNING << "Aborting, invalid joint value (nan)" << endl;
+                    return dThetaSum;
+                }
+                //nodes[i]->setJointValue(nodes[i]->getJointValue() + dTheta[i]);
+            }
+
+            robot->setJointValues(rns,jv);
+
+            // check tolerances
+            if (checkTolerances())
+            {
+                if (verbose)
+                    VR_INFO << "Tolerances ok, loop:" << step << endl;
+                return dThetaSum;
+            }
+            float d = dTheta.norm();
+            if (dTheta.norm()<minumChange)
+            {
+                if (verbose)
+                    VR_INFO << "Could not improve result any more (dTheta.norm()=" << d << "), loop:" << step << endl;
+                return dThetaSum;
+            }
+            float posDist = getMeanErrorPosition();
+            if (checkImprovement && posDist>lastDist)
+            {
+                if (verbose)
+                    VR_INFO << "Could not improve result any more (current position error=" << posDist << ", last loop's error:" << lastDist << "), loop:" << step << endl;
+                robot->setJointValues(rns,jvBest);
+                return dThetaSum;
+            }
+            jvBest = jv;
+            lastDist = posDist;
+            step++;
+        }
+        if (verbose)
+        {
+            VR_INFO << "IK failed, loop:" << step << endl;
+            VR_INFO << "pos error:" << getErrorPosition() << endl;
+            VR_INFO << "rot error:" << getErrorRotation() << endl;
+        }
+        return dThetaSum;
+    }
+
+    void EDifferentialIK::applyWeightsToJacobian(MatrixXf &Jacobian)
+    {
+        if(dofWeights.rows() == Jacobian.cols())
+        {
+            Eigen::MatrixXf weightMat(dofWeights.rows(), dofWeights.rows());
+            weightMat.setIdentity();
+            weightMat.diagonal() = dofWeights;
+//            ARMARX_INFO << deactivateSpam(1) << "Jac before:\n" << Jacobian;
+//            ARMARX_INFO << deactivateSpam(1) << "Weights:\n" << weightMat;
+            Jacobian = Jacobian * weightMat;
+//            ARMARX_INFO << deactivateSpam(1) << "Jac after:\n" << Jacobian;
+
+        }
+
+        if(tcpWeights.rows() == Jacobian.rows())
+        {
+            Eigen::MatrixXf weightMat(tcpWeights.rows(), tcpWeights.rows());
+            weightMat.setIdentity();
+            weightMat.diagonal() = dofWeights;
+
+            Jacobian = weightMat * Jacobian;
+
+        }
+    }
+
+    VectorXf EDifferentialIK::computeStep(float stepSize )
+    {
+
+        if (nRows==0) this->setNRows();
+        size_t nDoF = nodes.size();
+
+        MatrixXf Jacobian(nRows,nDoF);
+        VectorXf error(nRows);
+
+        size_t index=0;
+        for (size_t i=0; i<tcp_set.size();i++)
+        {
+            RobotNodePtr tcp = tcp_set[i];
+            if (this->targets.find(tcp)!=this->targets.end())
+            {
+                Eigen::VectorXf delta = getDeltaToGoal(tcp);
+                IKSolver::CartesianSelection mode = this->modes[tcp];
+                MatrixXf partJacobian = this->getJacobianMatrix(tcp,mode);
+
+                Jacobian.block(index,0,partJacobian.rows(),nDoF) = partJacobian;
+                Vector3f position = delta.head(3);
+                position *=stepSize;
+                if (mode & IKSolver::X)
+                {
+                    error(index) = position(0);
+                    index++;
+                }
+                if (mode & IKSolver::Y)
+                {
+                    error(index) = position(1);
+                    index++;
+                }
+                if (mode & IKSolver::Z)
+                {
+                    error(index) = position(2);
+                    index++;
+                }
+                if (mode & IKSolver::Orientation)
+                {
+                    error.segment(index,3) = delta.tail(3)*stepSize;
+                    index+=3;
+                }
+
+            }
+            else
+                VR_ERROR << "Internal error?!" << endl; // Error
+
+
+        }
+
+        applyWeightsToJacobian(Jacobian);
+
+        //cout << "ERROR:" << endl;
+        //cout << error << endl;
+        VectorXf dTheta(nDoF);
+
+        //MatrixXf pseudo = (Jacobian.transpose() * Jacobian).inverse() * Jacobian.transpose();
+        //MatrixXf pseudo = Jacobian.transpose() * (Jacobian*Jacobian.transpose()).inverse();
+        MatrixXf pseudo = computePseudoInverseJacobianMatrix(Jacobian);
+        //cout << "PSEUDO:" << endl;
+        //cout << pseudo << endl;
+
+        dTheta = pseudo * error;
+        //cout << "THETA:" << endl;
+        //cout << dTheta << endl;
+        return dTheta;
+
+    }
+
+    bool EDifferentialIK::adjustDOFWeightsToJointLimits(const VectorXf &plannedJointDeltas)
+    {
+        if(dofWeights.rows() != plannedJointDeltas.rows())
+        {
+            dofWeights.resize(plannedJointDeltas.rows());
+            dofWeights.setOnes();
+
+        }
+
+        bool result = false;
+        for (unsigned int i=0; i<nodes.size();i++)
+        {
+            float angle = nodes[i]->getJointValue() + plannedJointDeltas[i]*0.1;
+            if(angle > nodes[i]->getJointLimitHi() || angle < nodes[i]->getJointLimitLo())
+            {
+                ARMARX_VERBOSE << deactivateSpam(3) << nodes[i]->getName() << " joint deactivated because of joint limit";
+                dofWeights(i) = 0;
+                result = true;
+            }
+        }
+        return result;
+
+    }
+
 
 }
 
diff --git a/source/RobotAPI/units/TCPControlUnit.h b/source/RobotAPI/units/TCPControlUnit.h
index d66e04be6..368bdd30b 100644
--- a/source/RobotAPI/units/TCPControlUnit.h
+++ b/source/RobotAPI/units/TCPControlUnit.h
@@ -125,7 +125,7 @@ namespace armarx {
 
     };
 
-    class EDifferentialIK : public VirtualRobot::DifferentialIK
+    class EDifferentialIK : public VirtualRobot::DifferentialIK, virtual public Logging
     {
     public:
         EDifferentialIK(VirtualRobot::RobotNodeSetPtr rns,VirtualRobot:: RobotNodePtr coordSystem = VirtualRobot::RobotNodePtr(), VirtualRobot::JacobiProvider::InverseJacobiMethod invJacMethod = eSVD);
@@ -137,6 +137,16 @@ namespace armarx {
 
         void clearGoals();
         void setRefFrame(VirtualRobot:: RobotNodePtr coordSystem);
+
+        void setDOFWeights(Eigen::VectorXf dofWeights);
+        void setResultDimensionWeights(Eigen::VectorXf tcpWeights);
+        Eigen::VectorXf computeSteps(float stepSize, float minumChange, int maxNStep);
+        Eigen::VectorXf computeStep(float stepSize);
+    protected:
+        bool adjustDOFWeightsToJointLimits(const Eigen::VectorXf &plannedJointDeltas);
+
+        Eigen::VectorXf dofWeights;
+        Eigen::VectorXf tcpWeights;
     };
     typedef boost::shared_ptr<EDifferentialIK> EDifferentialIKPtr;
 
-- 
GitLab