Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pinv velocity solver sigma output #359

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions orocos_kdl/src/chainiksolvervel_pinv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ namespace KDL
eps(_eps),
maxiter(_maxiter),
nrZeroSigmas(0),
svdResult(0)
svdResult(0),
sigmaMin(0),
Smaxtomin(nj)
{
}

Expand All @@ -52,13 +54,35 @@ namespace KDL
for(unsigned int i = 0 ; i < V.size(); i++)
V[i].resize(nj);
tmp.resize(nj);
Smaxtomin.resize(nj);
}

ChainIkSolverVel_pinv::~ChainIkSolverVel_pinv()
{
}


void ChainIkSolverVel_pinv::setEps(const double eps_in)
{
if (0 < eps_in) eps = eps_in;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
// else silently ignore
}

void ChainIkSolverVel_pinv::setMaxIter(const unsigned int maxiter_in)
{
if (1 <= maxiter_in) maxiter = maxiter_in;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
// else silently ignore
}

int ChainIkSolverVel_pinv::getSigma(JntArray& Sout)
{
if (Sout.rows() != Smaxtomin.rows())
return (error = E_SIZE_MISMATCH);
for (unsigned int i=0;i<Sout.rows();i++)
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
Sout(i)=Smaxtomin(i);
return (error = E_NOERROR);
}

int ChainIkSolverVel_pinv::CartToJnt(const JntArray& q_in, const Twist& v_in, JntArray& qdot_out)
{
if (nj != chain.getNrOfJoints())
Expand All @@ -75,8 +99,9 @@ namespace KDL
double sum;
unsigned int i,j;

// Initialize near zero singular value counter
// Initialize (internal) return values
nrZeroSigmas = 0 ;
sigmaMin = 0.;

//Do a singular value decomposition of "jac" with maximum
//iterations "maxiter", put the results in "U", "S" and "V"
Expand All @@ -88,6 +113,18 @@ namespace KDL
return (error = E_SVD_FAILED);
}

// Sort S in descending order (S is not sorted in SVD_HH)
// copied from svd_eigen_HH.cpp
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
Smaxtomin = S;
SortJntArrayMaxToMin(Smaxtomin);
// Minimum of six largest singular values of J is S(5) if number of joints >=6 and 0 for <6
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this behaviour comming from? As there should be the same number of singular values as the number of joints, correct?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the number of singular values is the same as the number of joints. However, any singular values beyond the sixth which is S(5) will be zero (assuming SVs are ordered from high to low). We are interested in whether the sixth singular value is zero because that is when the determinant of J*J^T is zero and a Cartesian degree of freedom is lost. Then the condition corresponding to fabs(S(i))<eps (line 140) must be applied in order to compute the pseudoinverse.

if ( jac.columns() >= 6 ) {
sigmaMin = Smaxtomin(5);
}
else {
sigmaMin = 0.;
}
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

// We have to calculate qdot_out = jac_pinv*v_in
// Using the svd decomposition this becomes(jac_pinv=V*S_pinv*Ut):
// qdot_out = V*S_pinv*Ut*v_in
Expand Down Expand Up @@ -129,6 +166,29 @@ namespace KDL
}
}

void ChainIkSolverVel_pinv::SortJntArrayMaxToMin(JntArray& Smaxtomin)
{
int n=Smaxtomin.rows();
for (int i=0; i<n; i++){
double S_max = Smaxtomin(i);
int i_max = i;
for (int j=i+1; j<n; j++){
double Sj = Smaxtomin(j);
if (Sj > S_max){
S_max = Sj;
i_max = j;
}
}
if (i_max != i){
/* swap eigenvalues */
double tmp = Smaxtomin(i);
Smaxtomin(i)=Smaxtomin(i_max);
Smaxtomin(i_max)=tmp;
}
}
return;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
}

const char* ChainIkSolverVel_pinv::strError(const int error) const
{
if (E_CONVERGE_PINV_SINGULAR == error) return "Converged put pseudo inverse of jacobian is singular.";
Expand Down
43 changes: 41 additions & 2 deletions orocos_kdl/src/chainiksolvervel_pinv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,57 @@ namespace KDL
*/
virtual int CartToJnt(const JntArray& /*q_init*/, const FrameVel& /*v_in*/, JntArrayVel& /*q_out*/){return -1;};

/**
* Set eps
* \pre 0 < eps, otherwise eps is ignored
*/
void setEps(const double eps_in);

/**
* Set maxIter
* \pre 1 <= maxiter, otherwise maxiter is ignored
*/
void setMaxIter(const unsigned int maxiter_in);

/**
* Retrieve the number of singular values of the jacobian that are < eps;
* if the number of near zero singular values is > jac.col()-jac.row(),
* then the jacobian pseudoinverse is singular
*/
unsigned int getNrZeroSigmas()const {return nrZeroSigmas;};

/**
* Request the minimum of the first six singular values
*/
double getSigmaMin()const {return sigmaMin;};
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

/**
* Request the singular values of the Jacobian
*/
int getSigma(JntArray& Sout);

/**
* Request the value of eps
*/
double getEps()const {return eps;};
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

/**
* Get maximum number of iterations
* \pre 1 <= maxiter, otherwise maxiter is ignored
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
*/
unsigned int getMaxIter()const { return maxiter; }
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

/**
* Retrieve the latest return code from the SVD algorithm
* @return 0 if CartToJnt() not yet called, otherwise latest SVD result code.
*/
int getSVDResult()const {return svdResult;};

/**
* Sort a JntArray from maximum to minimum value
*/
void SortJntArrayMaxToMin(JntArray& Smaxtomin);

/// @copydoc KDL::SolverI::strError()
virtual const char* strError(const int error) const;

Expand All @@ -108,10 +146,11 @@ namespace KDL
std::vector<JntArray> V;
JntArray tmp;
double eps;
int maxiter;
unsigned int maxiter;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ABI breaking change. I can't accept this for now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maxiter is an int type in the constructor.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so why not also store it as an int, which is the current situation.

But I can't accept ABI breaking changes. So this one needs to be reverted.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still open issue

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how to revert this change in this system. Note that changing maxiter to an int might break the comparison in setMaxIter() which expects an unsigned int input;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can change that too....

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved this issue with second to last commit on the latest branch. "maxiter" was updated to int in several other places in the same commit.

unsigned int nrZeroSigmas;
int svdResult;

double sigmaMin;
JntArray Smaxtomin;
};
}
#endif
Expand Down
79 changes: 79 additions & 0 deletions orocos_kdl/tests/solvertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,85 @@ void SolverTest::IkVelSolverWDLSTest()
}


craigirobot marked this conversation as resolved.
Show resolved Hide resolved
void SolverTest::IkVelSolverPinvTest()
{
unsigned int maxiter = 30;
double eps = 1e-6;
unsigned int maxiternew = 10;
double epsnew = 0.1;

std::cout<<"KDL-IK Pinv Vel Solver Tests for Near Zero SVs"<<std::endl;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

KDL::ChainIkSolverVel_pinv ikvelsolver(motomansia10,eps,maxiter);
CPPUNIT_ASSERT_EQUAL(eps,ikvelsolver.getEps());
CPPUNIT_ASSERT_EQUAL(maxiter,ikvelsolver.getMaxIter());
ikvelsolver.setEps(epsnew);
CPPUNIT_ASSERT_EQUAL(epsnew,ikvelsolver.getEps());
ikvelsolver.setMaxIter(maxiternew);
CPPUNIT_ASSERT_EQUAL(maxiternew,ikvelsolver.getMaxIter());
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

unsigned int nj = motomansia10.getNrOfJoints();
JntArray q(nj), dq(nj);

KDL::Vector v05(0.05,0.05,0.05);
KDL::Twist dx(v05,v05);
craigirobot marked this conversation as resolved.
Show resolved Hide resolved


std::cout<<"smallest singular value is above threshold (no Pinv)"<<std::endl;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

q(0) = 0. ;
q(1) = 0.5 ;
q(2) = 0.4 ;
q(3) = -PI_2 ;
q(4) = 0. ;
q(5) = 0. ;
q(6) = 0. ;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

CPPUNIT_ASSERT_EQUAL((int)SolverI::E_NOERROR,
ikvelsolver.CartToJnt(q, dx, dq)) ; // pinv mode
CPPUNIT_ASSERT(1==ikvelsolver.getNrZeroSigmas()) ; // 1 singular value
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

std::cout<<"Test singular value function"<<std::endl;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
JntArray S(nj), Sexp(nj);
Sexp(0) = 1.86694;
Sexp(1) = 1.61924;
Sexp(2) = 1.3175;
Sexp(3) = 0.330559;
Sexp(4) = 0.206596;
Sexp(5) = 0.1163;
Sexp(6) = 0.0;
CPPUNIT_ASSERT_EQUAL(0,(int)ikvelsolver.getSigma(S)) ;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
for(unsigned int i=0;i<nj;i++) {
CPPUNIT_ASSERT_DOUBLES_EQUAL(Sexp(i),S(i),0.0001);
}
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
double Smin = ikvelsolver.getSigmaMin();
CPPUNIT_ASSERT_EQUAL(S(5),Smin);
craigirobot marked this conversation as resolved.
Show resolved Hide resolved


std::cout<<"smallest singular value is below threshold"<<std::endl;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

q(1) = 0.2 ;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

CPPUNIT_ASSERT_EQUAL((int)ChainIkSolverVel_pinv::E_CONVERGE_PINV_SINGULAR,
ikvelsolver.CartToJnt(q, dx, dq)) ; // pinv mode
CPPUNIT_ASSERT_EQUAL((unsigned int)2,ikvelsolver.getNrZeroSigmas()) ; // 2 singular values
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

q(1) = 0.0 ;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved

CPPUNIT_ASSERT_EQUAL((int)ChainIkSolverVel_pinv::E_CONVERGE_PINV_SINGULAR,
ikvelsolver.CartToJnt(q, dx, dq)) ; // pinv mode
CPPUNIT_ASSERT_EQUAL((unsigned int)2,ikvelsolver.getNrZeroSigmas()) ; // 2 singular values
MatthijsBurgh marked this conversation as resolved.
Show resolved Hide resolved

// fully singular
MatthijsBurgh marked this conversation as resolved.
Show resolved Hide resolved
q(2) = 0.0 ;
q(3) = 0.0 ;
MatthijsBurgh marked this conversation as resolved.
Show resolved Hide resolved

CPPUNIT_ASSERT_EQUAL((int)ChainIkSolverVel_pinv::E_CONVERGE_PINV_SINGULAR,
ikvelsolver.CartToJnt(q, dx, dq)) ; // pinv mode
CPPUNIT_ASSERT_EQUAL(4,(int)ikvelsolver.getNrZeroSigmas()) ;
craigirobot marked this conversation as resolved.
Show resolved Hide resolved
}


craigirobot marked this conversation as resolved.
Show resolved Hide resolved
void SolverTest::FkPosAndJacLocal(Chain& chain,ChainFkSolverPos& fksolverpos,ChainJntToJacSolver& jacsolver)
{
double deltaq = 1E-4;
Expand Down
2 changes: 2 additions & 0 deletions orocos_kdl/tests/solvertest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class SolverTest : public CppUnit::TestFixture
CPPUNIT_TEST(ExternalWrenchEstimatorTest );
CPPUNIT_TEST(IkSingularValueTest );
CPPUNIT_TEST(IkVelSolverWDLSTest );
CPPUNIT_TEST(IkVelSolverPinvTest );
CPPUNIT_TEST(FkPosVectTest );
CPPUNIT_TEST(FkVelVectTest );
CPPUNIT_TEST(FdSolverDevelopmentTest );
Expand All @@ -58,6 +59,7 @@ class SolverTest : public CppUnit::TestFixture
void ExternalWrenchEstimatorTest();
void IkSingularValueTest() ;
void IkVelSolverWDLSTest();
void IkVelSolverPinvTest();
void FkPosVectTest();
void FkVelVectTest();
void FdSolverDevelopmentTest();
Expand Down