nlopt_ik.cpp
Go to the documentation of this file.
00001 /********************************************************************************
00002 Copyright (c) 2015, TRACLabs, Inc.
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without modification,
00006  are permitted provided that the following conditions are met:
00007 
00008     1. Redistributions of source code must retain the above copyright notice, 
00009        this list of conditions and the following disclaimer.
00010 
00011     2. Redistributions in binary form must reproduce the above copyright notice,
00012        this list of conditions and the following disclaimer in the documentation 
00013        and/or other materials provided with the distribution.
00014 
00015     3. Neither the name of the copyright holder nor the names of its contributors
00016        may be used to endorse or promote products derived from this software 
00017        without specific prior written permission.
00018 
00019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
00021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
00024 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
00025 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00026 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
00027 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
00028 OF THE POSSIBILITY OF SUCH DAMAGE.
00029 ********************************************************************************/
00030 
00031 #include <trac_ik/nlopt_ik.hpp>
00032 #include <boost/bind.hpp>
00033 #include <boost/function.hpp>
00034 #include <ros/ros.h>
00035 #include <limits>
00036 #include <boost/date_time.hpp>
00037 #include <trac_ik/dual_quaternion.h>
00038 #include <cmath>
00039 
00040 
00041 
00042 namespace NLOPT_IK {
00043 
00044   dual_quaternion targetDQ;
00045 
00046   double minfunc(const std::vector<double>& x, std::vector<double>& grad, void* data) {
00047     // Auxilory function to minimize (Sum of Squared joint angle error
00048     // from the requested configuration).  Because we wanted a Class
00049     // without static members, but NLOpt library does not support
00050     // passing methods of Classes, we use these auxilary functions.
00051 
00052     NLOPT_IK *c = (NLOPT_IK *) data;
00053 
00054     return c->minJoints(x,grad);
00055   }
00056 
00057   double minfuncDQ(const std::vector<double>& x, std::vector<double>& grad, void* data) {
00058     // Auxilory function to minimize (Sum of Squared joint angle error
00059     // from the requested configuration).  Because we wanted a Class
00060     // without static members, but NLOpt library does not support
00061     // passing methods of Classes, we use these auxilary functions.
00062     NLOPT_IK *c = (NLOPT_IK *) data;
00063 
00064     std::vector<double> vals(x);
00065 
00066     double jump=boost::math::tools::epsilon<float>();
00067     double result[1]; 
00068     c->cartDQError(vals, result);
00069 
00070     if (!grad.empty()) {
00071       double v1[1];
00072       for (uint i=0; i<x.size(); i++) {
00073         double original=vals[i];
00074 
00075         vals[i]=original+jump;
00076         c->cartDQError(vals, v1);
00077 
00078         vals[i]=original;
00079         grad[i]=(v1[0]-result[0])/(2*jump);
00080       }
00081     }
00082 
00083     return result[0]; 
00084   }
00085 
00086 
00087   double minfuncSumSquared(const std::vector<double>& x, std::vector<double>& grad, void* data) {
00088     // Auxilory function to minimize (Sum of Squared joint angle error
00089     // from the requested configuration).  Because we wanted a Class
00090     // without static members, but NLOpt library does not support
00091     // passing methods of Classes, we use these auxilary functions.
00092 
00093     NLOPT_IK *c = (NLOPT_IK *) data;
00094 
00095     std::vector<double> vals(x);
00096 
00097     double jump=boost::math::tools::epsilon<float>();
00098     double result[1]; 
00099     c->cartSumSquaredError(vals, result);
00100 
00101     if (!grad.empty()) {
00102       double v1[1];
00103       for (uint i=0; i<x.size(); i++) {
00104         double original=vals[i];
00105 
00106         vals[i]=original+jump;
00107         c->cartSumSquaredError(vals, v1);
00108 
00109         vals[i]=original;
00110         grad[i]=(v1[0]-result[0])/(2.0*jump);
00111       }
00112     }
00113 
00114     return result[0];
00115   }
00116 
00117 
00118   double minfuncL2(const std::vector<double>& x, std::vector<double>& grad, void* data) {
00119     // Auxilory function to minimize (Sum of Squared joint angle error
00120     // from the requested configuration).  Because we wanted a Class
00121     // without static members, but NLOpt library does not support
00122     // passing methods of Classes, we use these auxilary functions.
00123 
00124     NLOPT_IK *c = (NLOPT_IK *) data;
00125 
00126     std::vector<double> vals(x);
00127 
00128     double jump=boost::math::tools::epsilon<float>();
00129     double result[1]; 
00130     c->cartL2NormError(vals, result);
00131 
00132     if (!grad.empty()) {
00133       double v1[1];
00134       for (uint i=0; i<x.size(); i++) {
00135         double original=vals[i];
00136 
00137         vals[i]=original+jump;
00138         c->cartL2NormError(vals, v1);
00139 
00140         vals[i]=original;
00141         grad[i]=(v1[0]-result[0])/(2.0*jump);
00142       }
00143     }
00144 
00145     return result[0];
00146   }
00147 
00148 
00149 
00150   void constrainfuncm(uint m, double* result, uint n, const double* x, double* grad, void* data) {
00151     //Equality constraint auxilary function for Euclidean distance .
00152     //This also uses a small walk to approximate the gradient of the
00153     //constraint function at the current joint angles.
00154 
00155     NLOPT_IK *c = (NLOPT_IK *) data;
00156 
00157     std::vector<double> vals(n);
00158 
00159     for (uint i=0; i<n; i++) {
00160       vals[i]=x[i];
00161     }
00162 
00163     double jump=boost::math::tools::epsilon<float>();
00164 
00165     c->cartSumSquaredError(vals, result);
00166 
00167     if (grad!=NULL) {
00168       std::vector<double> v1(m);
00169       for (uint i=0; i<n; i++) {
00170         double o=vals[i];
00171         vals[i]=o+jump;
00172         c->cartSumSquaredError(vals, v1.data());
00173         vals[i]=o;
00174         for (uint j=0; j<m; j++)  {
00175           grad[j*n+i]=(v1[j]-result[j])/(2*jump);
00176         }
00177       }
00178     }
00179   }
00180 
00181 
00182   NLOPT_IK::NLOPT_IK(const KDL::Chain& _chain, const KDL::JntArray& _q_min, const KDL::JntArray& _q_max, double _maxtime, double _eps, OptType _type):
00183     chain(_chain), fksolver(_chain), maxtime(_maxtime), eps(std::abs(_eps)), TYPE(_type)
00184   {
00185     assert(chain.getNrOfJoints()==_q_min.data.size());
00186     assert(chain.getNrOfJoints()==_q_max.data.size());
00187 
00188     //Constructor for an IK Class.  Takes in a Chain to operate on,
00189     //the min and max joint limits, an (optional) maximum number of
00190     //iterations, and an (optional) desired error.
00191     reset();
00192 
00193     if (chain.getNrOfJoints() < 2) {
00194       ROS_WARN_THROTTLE(1.0,"NLOpt_IK can only be run for chains of length 2 or more");
00195       return;
00196     }
00197     opt = nlopt::opt(nlopt::LD_SLSQP, _chain.getNrOfJoints());
00198 
00199     for (uint i=0; i<chain.getNrOfJoints(); i++) {
00200       lb.push_back(_q_min(i)); 
00201       ub.push_back(_q_max(i));
00202     }
00203      
00204     for (uint i=0; i<chain.segments.size(); i++) {
00205       std::string type = chain.segments[i].getJoint().getTypeName();
00206       if (type.find("Rot")!=std::string::npos) {
00207         if (_q_max(types.size())>=std::numeric_limits<float>::max() && 
00208             _q_min(types.size())<=std::numeric_limits<float>::lowest())
00209           types.push_back(KDL::BasicJointType::Continuous);
00210         else
00211           types.push_back(KDL::BasicJointType::RotJoint);
00212       }
00213       else if (type.find("Trans")!=std::string::npos)
00214         types.push_back(KDL::BasicJointType::TransJoint);
00215     }
00216     
00217     assert(types.size()==lb.size());
00218    
00219     std::vector<double> tolerance(1,boost::math::tools::epsilon<float>());
00220     opt.set_xtol_abs(tolerance[0]);
00221 
00222    
00223     switch (TYPE) {
00224     case Joint: 
00225       opt.set_min_objective(minfunc, this);
00226       opt.add_equality_mconstraint(constrainfuncm, this, tolerance);
00227       break;
00228     case DualQuat: 
00229       opt.set_min_objective(minfuncDQ, this);
00230       break; 
00231     case SumSq: 
00232       opt.set_min_objective(minfuncSumSquared, this);
00233       break; 
00234     case L2: 
00235       opt.set_min_objective(minfuncL2, this);
00236       break; 
00237     }
00238   }
00239 
00240 
00241   double NLOPT_IK::minJoints(const std::vector<double>& x, std::vector<double>& grad)
00242   {
00243     // Actual function to compute the error between the current joint
00244     // configuration and the desired.  The SSE is easy to provide a
00245     // closed form gradient for.
00246 
00247     bool gradient = !grad.empty();
00248 
00249     double err = 0;
00250     for (uint i=0; i<x.size(); i++) {
00251       err += pow(x[i] - des[i],2);
00252       if (gradient) 
00253         grad[i]=2.0*(x[i]-des[i]);
00254     }
00255 
00256     return err;
00257 
00258   }
00259 
00260 
00261   void NLOPT_IK::cartSumSquaredError(const std::vector<double>& x, double error[])
00262   {
00263     // Actual function to compute Euclidean distance error.  This uses
00264     // the KDL Forward Kinematics solver to compute the Cartesian pose
00265     // of the current joint configuration and compares that to the
00266     // desired Cartesian pose for the IK solve.
00267 
00268     if (aborted || progress != -3) {
00269       opt.force_stop();
00270       return;
00271     }
00272 
00273    
00274     KDL::JntArray q(x.size());
00275 
00276     for (uint i=0; i<x.size(); i++)
00277       q(i)=x[i];
00278 
00279     int rc = fksolver.JntToCart(q,currentPose);
00280 
00281     if (rc < 0)
00282       ROS_FATAL_STREAM("KDL FKSolver is failing: "<<q.data);
00283 
00284     if (std::isnan(currentPose.p.x())) {
00285       ROS_ERROR("NaNs from NLOpt!!");
00286       error[0] = std::numeric_limits<float>::max();
00287       progress = -1;
00288       return;
00289     }
00290 
00291     KDL::Twist delta_twist = KDL::diffRelative(targetPose,currentPose);
00292 
00293     for (int i=0; i<6; i++) {
00294       if (std::abs(delta_twist[i]) <= std::abs(bounds[i]))
00295         delta_twist[i] = 0.0;
00296     }
00297     
00298     error[0] = KDL::dot(delta_twist.vel,delta_twist.vel) + KDL::dot(delta_twist.rot,delta_twist.rot);
00299 
00300     if (KDL::Equal(delta_twist,KDL::Twist::Zero(),eps)) {
00301       progress=1;
00302       best_x=x;
00303       return;
00304     }
00305   }
00306   
00307 
00308 
00309   void NLOPT_IK::cartL2NormError(const std::vector<double>& x, double error[])
00310   {
00311     // Actual function to compute Euclidean distance error.  This uses
00312     // the KDL Forward Kinematics solver to compute the Cartesian pose
00313     // of the current joint configuration and compares that to the
00314     // desired Cartesian pose for the IK solve.
00315 
00316     if (aborted || progress != -3) {
00317       opt.force_stop();
00318       return;     
00319     }
00320 
00321     KDL::JntArray q(x.size());
00322 
00323     for (uint i=0; i<x.size(); i++)
00324       q(i)=x[i];
00325 
00326     int rc = fksolver.JntToCart(q,currentPose);
00327 
00328     if (rc < 0)
00329       ROS_FATAL_STREAM("KDL FKSolver is failing: "<<q.data);
00330 
00331 
00332     if (std::isnan(currentPose.p.x())) {
00333       ROS_ERROR("NaNs from NLOpt!!");
00334       error[0] = std::numeric_limits<float>::max();
00335       progress = -1;
00336       return;
00337     }
00338 
00339     KDL::Twist delta_twist = KDL::diffRelative(targetPose,currentPose);
00340 
00341     for (int i=0; i<6; i++) {
00342       if (std::abs(delta_twist[i]) <= std::abs(bounds[i]))
00343         delta_twist[i] = 0.0;
00344     }
00345 
00346     error[0] = std::sqrt(KDL::dot(delta_twist.vel,delta_twist.vel) + KDL::dot(delta_twist.rot,delta_twist.rot));
00347 
00348     if (KDL::Equal(delta_twist,KDL::Twist::Zero(),eps)) {
00349       progress=1;
00350       best_x=x;
00351       return;
00352     }
00353   }
00354   
00355 
00356 
00357 
00358   void NLOPT_IK::cartDQError(const std::vector<double>& x, double error[])
00359   {
00360     // Actual function to compute Euclidean distance error.  This uses
00361     // the KDL Forward Kinematics solver to compute the Cartesian pose
00362     // of the current joint configuration and compares that to the
00363     // desired Cartesian pose for the IK solve.
00364 
00365     if (aborted || progress != -3) {
00366       opt.force_stop();
00367       return;     
00368     }
00369     
00370     KDL::JntArray q(x.size());
00371     
00372     for (uint i=0; i<x.size(); i++)
00373       q(i)=x[i];
00374     
00375     int rc = fksolver.JntToCart(q,currentPose);
00376 
00377     if (rc < 0)
00378       ROS_FATAL_STREAM("KDL FKSolver is failing: "<<q.data);
00379 
00380 
00381     if (std::isnan(currentPose.p.x())) {
00382       ROS_ERROR("NaNs from NLOpt!!");
00383       error[0] = std::numeric_limits<float>::max();
00384       progress = -1;
00385       return;
00386     }
00387 
00388     KDL::Twist delta_twist = KDL::diffRelative(targetPose,currentPose);
00389 
00390     for (int i=0; i<6; i++) {
00391       if (std::abs(delta_twist[i]) <= std::abs(bounds[i]))
00392         delta_twist[i] = 0.0;
00393     }
00394 
00395     math3d::matrix3x3<double> currentRotationMatrix(currentPose.M.data); 
00396     math3d::quaternion<double> currentQuaternion = math3d::rot_matrix_to_quaternion<double>(currentRotationMatrix);
00397     math3d::point3d currentTranslation (currentPose.p.data);
00398     dual_quaternion currentDQ = dual_quaternion::rigid_transformation(currentQuaternion, currentTranslation); 
00399 
00400     dual_quaternion errorDQ = (currentDQ*!targetDQ).normalize(); 
00401     errorDQ.log(); 
00402     error[0] = 4.0f*dot(errorDQ,errorDQ);
00403 
00404 
00405     if (KDL::Equal(delta_twist,KDL::Twist::Zero(),eps)) {
00406       progress=1;
00407       best_x=x;
00408       return;
00409     }
00410   }
00411 
00412 
00413   int NLOPT_IK::CartToJnt(const KDL::JntArray &q_init, const KDL::Frame &p_in, KDL::JntArray &q_out, const KDL::Twist _bounds, const KDL::JntArray& q_desired) {
00414     // User command to start an IK solve.  Takes in a seed
00415     // configuration, a Cartesian pose, and (optional) a desired
00416     // configuration.  If the desired is not provided, the seed is
00417     // used.  Outputs the joint configuration found that solves the
00418     // IK.
00419 
00420     // Returns -3 if a configuration could not be found within the eps
00421     // set up in the constructor.
00422 
00423     boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time();
00424     boost::posix_time::time_duration diff;
00425 
00426     bounds = _bounds;
00427     q_out=q_init;
00428 
00429     if (chain.getNrOfJoints() < 2) {
00430       ROS_ERROR_THROTTLE(1.0,"NLOpt_IK can only be run for chains of length 2 or more");
00431       return -3;
00432     }
00433 
00434     if (q_init.data.size() != types.size()) {
00435       ROS_ERROR_THROTTLE(1.0,"IK seeded with wrong number of joints.  Expected %d but got %d",(int)types.size(), (int)q_init.data.size());
00436       return -3;
00437     }
00438 
00439     opt.set_maxtime(maxtime);
00440 
00441 
00442     double minf; /* the minimum objective value, upon return */
00443 
00444     targetPose = p_in;
00445 
00446     if (TYPE == 1) { // DQ
00447       math3d::matrix3x3<double> targetRotationMatrix(targetPose.M.data); 
00448       math3d::quaternion<double> targetQuaternion = math3d::rot_matrix_to_quaternion<double>(targetRotationMatrix);
00449       math3d::point3d targetTranslation (targetPose.p.data);
00450       targetDQ = dual_quaternion::rigid_transformation(targetQuaternion, targetTranslation); 
00451     }
00452     // else if (TYPE == 1)
00453     // {
00454     //   z_target = targetPose*z_up;
00455     //   x_target = targetPose*x_out;
00456     //   y_target = targetPose*y_out;     
00457     // }
00458 
00459     
00460     //    fksolver.JntToCart(q_init,currentPose);
00461 
00462     std::vector<double> x(chain.getNrOfJoints());
00463 
00464     for (uint i=0; i < x.size(); i++) {
00465       x[i] = q_init(i);
00466       
00467       if (types[i]==KDL::BasicJointType::Continuous)
00468         continue;
00469 
00470       if (types[i]==KDL::BasicJointType::TransJoint) {
00471         x[i] = std::min(x[i],ub[i]);
00472         x[i] = std::max(x[i],lb[i]);
00473       }
00474       else {
00475         
00476         // Below is to handle bad seeds outside of limits
00477         
00478         if (x[i] > ub[i]) {
00479           //Find actual angle offset
00480           double diffangle = fmod(x[i]-ub[i],2*M_PI);
00481           // Add that to upper bound and go back a full rotation
00482           x[i] = ub[i] + diffangle - 2*M_PI;
00483         }
00484         
00485         if (x[i] < lb[i]) {
00486           //Find actual angle offset
00487           double diffangle = fmod(lb[i]-x[i],2*M_PI);
00488           // Subtract that from lower bound and go forward a full rotation
00489           x[i] = lb[i] - diffangle + 2*M_PI;
00490         }        
00491         
00492         if (x[i] > ub[i]) 
00493           x[i] = (ub[i]+lb[i])/2.0;
00494       }
00495     }
00496     
00497     best_x=x;
00498     progress = -3;
00499 
00500     std::vector<double> artificial_lower_limits(lb.size());
00501 
00502     for (uint i=0; i< lb.size(); i++)
00503       if (types[i]==KDL::BasicJointType::Continuous) 
00504         artificial_lower_limits[i] = best_x[i]-2*M_PI;
00505       else if (types[i]==KDL::BasicJointType::TransJoint) 
00506         artificial_lower_limits[i] = lb[i];
00507       else
00508         artificial_lower_limits[i] = std::max(lb[i],best_x[i]-2*M_PI);
00509     
00510     opt.set_lower_bounds(artificial_lower_limits);
00511 
00512     std::vector<double> artificial_upper_limits(lb.size());
00513 
00514     for (uint i=0; i< ub.size(); i++)
00515       if (types[i]==KDL::BasicJointType::Continuous) 
00516         artificial_upper_limits[i] = best_x[i]+2*M_PI;
00517       else if (types[i]==KDL::BasicJointType::TransJoint) 
00518         artificial_upper_limits[i] = ub[i];
00519       else
00520         artificial_upper_limits[i] = std::min(ub[i],best_x[i]+2*M_PI);
00521     
00522     opt.set_upper_bounds(artificial_upper_limits);
00523     
00524     if (q_desired.data.size()==0) {
00525       des=x;
00526     }
00527     else 
00528       {
00529         des.resize(x.size());
00530         for (uint i=0; i< des.size(); i++)
00531           des[i]=q_desired(i);
00532       }
00533     
00534     try {
00535       opt.optimize(x, minf);
00536     } catch (...) {
00537     }
00538     
00539     if (progress == -1) // Got NaNs
00540       progress = -3;
00541         
00542 
00543     if (!aborted && progress < 0) {
00544 
00545       double time_left;
00546       diff=boost::posix_time::microsec_clock::local_time()-start_time;
00547       time_left = maxtime - diff.total_nanoseconds()/1000000000.0;
00548 
00549       while (time_left > 0 && !aborted && progress < 0) {
00550 
00551         for (uint i=0; i< x.size(); i++)
00552           x[i]=fRand(artificial_lower_limits[i], artificial_upper_limits[i]);
00553 
00554         opt.set_maxtime(time_left);
00555         
00556         try 
00557           {
00558             opt.optimize(x, minf);
00559           } 
00560         catch (...) {}
00561 
00562         if (progress == -1) // Got NaNs
00563           progress = -3;
00564         
00565         diff=boost::posix_time::microsec_clock::local_time()-start_time;
00566         time_left = maxtime - diff.total_nanoseconds()/1000000000.0;
00567       }
00568     }
00569            
00570 
00571     for (uint i=0; i < x.size(); i++) {
00572       q_out(i) = best_x[i];
00573     }
00574         
00575     return progress;
00576     
00577   }
00578   
00579 
00580 }


trac_ik_lib
Author(s): Patrick Beeson, Barrett Ames
autogenerated on Thu Sep 21 2017 02:53:02