opspace/src/task_library.cpp

Go to the documentation of this file.
00001 /*
00002  * Shared copyright notice and LGPLv3 license statement.
00003  *
00004  * Copyright (C) 2011 The Board of Trustees of The Leland Stanford Junior University. All rights reserved.
00005  * Copyright (C) 2011 University of Texas at Austin. All rights reserved.
00006  *
00007  * Authors: Roland Philippsen (Stanford) and Luis Sentis (UT Austin)
00008  *          http://cs.stanford.edu/group/manips/
00009  *          http://www.me.utexas.edu/~hcrl/
00010  *
00011  * This program is free software: you can redistribute it and/or
00012  * modify it under the terms of the GNU Lesser General Public License
00013  * as published by the Free Software Foundation, either version 3 of
00014  * the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with this program.  If not, see
00023  * <http://www.gnu.org/licenses/>
00024  */
00025 
00026 #include <opspace/task_library.hpp>
00027 #include <opspace/TypeIOTGCursor.hpp>
00028 
00029 using jspace::pretty_print;
00030 
00031 namespace opspace {
00032 
00033 
00034   PDTask::
00035   PDTask(std::string const & name,
00036    saturation_policy_t saturation_policy)
00037     : Task(name),
00038       saturation_policy_(saturation_policy),
00039       initialized_(false)
00040   {
00041     declareParameter("goalpos", &goalpos_);
00042     declareParameter("goalvel", &goalvel_);
00043     declareParameter("errpos", &errpos_);
00044     declareParameter("errvel", &errvel_);
00045     declareParameter("kp", &kp_, PARAMETER_FLAG_NOLOG);
00046     declareParameter("kd", &kd_, PARAMETER_FLAG_NOLOG);
00047     declareParameter("maxvel", &maxvel_, PARAMETER_FLAG_NOLOG);
00048   }
00049   
00050   
00051   Status PDTask::
00052   check(Vector const * param, Vector const & value) const
00053   {
00054     if ((param == &errpos_) || (param == &errvel_)) {
00055       return Status(false, "error signals are read-only");
00056     }
00057     if ((param == &kp_) || (param == &kd_) || (param == &maxvel_)) {
00058       if (SATURATION_NORM == saturation_policy_) {
00059   if (1 != value.rows()) {
00060     return Status(false, "SATURATION_NORM requires one-dimensional gains and limits");
00061   }
00062       }
00063       for (size_t ii(0); ii < value.rows(); ++ii) {
00064   if (0 > value[ii]) {
00065     return Status(false, "gains and limits must be >= 0");
00066   }
00067       }
00068     }
00069     if (initialized_) {
00070       if (SATURATION_NORM != saturation_policy_) {
00071   if ((param == &kp_) || (param == &kd_) || (param == &maxvel_)) {
00072     if (goalpos_.rows() != value.rows()) {
00073       return Status(false, "invalid dimension");
00074     }
00075   }
00076       }
00077       if ((param == &goalpos_) || (param == &goalvel_)) {
00078   if (goalpos_.rows() != value.rows()) {
00079     return Status(false, "invalid dimension");
00080   }
00081       }
00082     }
00083     return Status();
00084   }
00085   
00086   
00087   Status PDTask::
00088   initPDTask(Vector const & initpos)
00089   {
00090     int const ndim(initpos.rows());
00091     
00092     if (SATURATION_NORM == saturation_policy_) {
00093       if (1 != kp_.rows()) {
00094   return Status(false, "kp must be one-dimensional for SATURATION_NORM policy");
00095       }
00096       if (1 != kd_.rows()) {
00097   return Status(false, "kd must be one-dimensional for SATURATION_NORM policy");
00098       }
00099       if (1 != maxvel_.rows()) {
00100   return Status(false, "maxvel must be one-dimensional for SATURATION_NORM policy");
00101       }
00102     }
00103     else {
00104       if (ndim != kp_.rows()) {
00105   if ((ndim != 1) && (1 == kp_.rows())) {
00106     kp_ = kp_[0] * Vector::Ones(ndim);
00107   }
00108   else {
00109     return Status(false, "invalid kp dimension");
00110   }
00111       }
00112       if (ndim != kd_.rows()) {
00113   if ((ndim != 1) && (1 == kd_.rows())) {
00114     kd_ = kd_[0] * Vector::Ones(ndim);
00115   }
00116   else {
00117     return Status(false, "invalid kd dimension");
00118   }
00119       }
00120       if (ndim != maxvel_.rows()) {
00121   if ((ndim != 1) && (1 == maxvel_.rows())) {
00122     maxvel_ = maxvel_[0] * Vector::Ones(ndim);
00123   }
00124   else {
00125     return Status(false, "invalid maxvel dimension");
00126   }
00127       }
00128     }
00129     
00130     goalpos_ = initpos;
00131     goalvel_ = Vector::Zero(ndim);
00132     errpos_ = Vector::Zero(ndim);
00133     errvel_ = Vector::Zero(ndim);
00134     initialized_ = true;
00135     
00136     Status ok;
00137     return ok;
00138   }
00139   
00140   
00141   Status PDTask::
00142   computePDCommand(Vector const & curpos,
00143        Vector const & curvel,
00144        Vector & command)
00145   {
00146     Status st;
00147     if ( ! initialized_) {
00148       st.ok = false;
00149       st.errstr = "not initialized";
00150       return st;
00151     }
00152     
00153     errpos_ = goalpos_ - curpos;
00154     errvel_ = goalvel_ - curvel;
00155     
00156     if (SATURATION_NORM == saturation_policy_) {
00157       command = kp_[0] * errpos_;
00158       if ((maxvel_[0] > 1e-4) && (kd_[0] > 1e-4)) { // beware of div by zero
00159   double const sat(command.norm() / maxvel_[0] / kd_[0]);
00160   if (sat > 1.0) {
00161     command /= sat;
00162   }
00163       }
00164       command += kd_[0] * errvel_;
00165       return st;
00166     }    
00167     
00168     command = kp_.cwise() * errpos_;
00169     
00170     if (SATURATION_COMPONENT_WISE == saturation_policy_) {
00171       for (int ii(0); ii < command.rows(); ++ii) {
00172   if ((maxvel_[ii] > 1e-4) && (kd_[ii] > 1e-4)) { // beware of div by zero
00173     double const sat(fabs((command[ii] / maxvel_[ii]) / kd_[ii]));
00174     if (sat > 1.0) {
00175       command[ii] /= sat;
00176     }
00177   }
00178       }
00179     }
00180     
00181     else if (SATURATION_MAX_COMPONENT == saturation_policy_) {
00182       double saturation(0.0);
00183       for (int ii(0); ii < command.rows(); ++ii) {
00184   if ((maxvel_[ii] > 1e-4) && (kd_[ii] > 1e-4)) { // beware of div by zero
00185     double const sat(fabs((command[ii] / maxvel_[ii]) / kd_[ii]));
00186     if (sat > saturation) {
00187       saturation = sat;
00188     }
00189   }
00190       }
00191       if (saturation > 1.0) {
00192   command /= saturation;
00193       }
00194     }
00195     
00196     // else (...) : other saturation policies would go here. For now
00197     // we silently assume that any other value of saturation_policy
00198     // means SATURATION_OFF.
00199     
00200     command += kd_.cwise() * errvel_;
00201     
00202     return st;
00203   }
00204   
00205   
00206   DraftPIDTask::
00207   DraftPIDTask(std::string const & name)
00208     : PDTask(name, PDTask::SATURATION_COMPONENT_WISE),
00209       dt_seconds_(-1)
00210   {
00211     declareParameter("dt_seconds", &dt_seconds_, PARAMETER_FLAG_NOLOG);
00212     declareParameter("ki", &ki_, PARAMETER_FLAG_NOLOG);
00213     declareParameter("errsum", &errsum_);
00214     declareParameter("limitpos", &limitpos_, PARAMETER_FLAG_NOLOG);
00215     declareParameter("limitvel", &limitvel_, PARAMETER_FLAG_NOLOG);
00216     declareParameter("triggerpos", &triggerpos_);
00217   }
00218   
00219   
00220   Status DraftPIDTask::
00221   check(double const * param, double value) const
00222   {
00223     if (param == &dt_seconds_) {
00224       if (0 >= value) {
00225   return Status(false, "dt_seconds must be > 0");
00226       }
00227     }
00228     return Status();
00229   }
00230   
00231   
00232   Status DraftPIDTask::
00233   check(Vector const * param, Vector const & value) const
00234   {
00235     if (param == &triggerpos_) {
00236       return Status(false, "triggerpos is read-only");
00237     }
00238     if (param == &errsum_) {
00239       return Status(false, "errsum is read-only");
00240     }
00241     if ((param == &ki_) || (param == &limitpos_) || (param == &limitvel_)) {
00242       for (size_t ii(0); ii < value.rows(); ++ii) {
00243   if (0 > value[ii]) {
00244     return Status(false, "gains and limits must be >= 0");
00245   }
00246       }
00247       if (initialized_) {
00248   if (goalpos_.rows() != value.rows()) {
00249     return Status(false, "invalid dimension");
00250   }
00251       }
00252     }
00253     return Status();
00254   }
00255   
00256   
00257   Status DraftPIDTask::
00258   initDraftPIDTask(Vector const & initpos)
00259   {
00260     Status st(initPDTask(initpos));
00261     if ( ! st) {
00262       return st;
00263     }
00264     
00265     if (0 >= dt_seconds_) {
00266       return Status(false, "dt_seconds_ must be > 0");
00267     }
00268     for (size_t ii(0); ii < ki_.rows(); ++ii) {
00269       if (0 > ki_[ii]) {
00270   return Status(false, "ki must be >= 0");
00271       }
00272     }
00273     for (size_t ii(0); ii < limitpos_.rows(); ++ii) {
00274       if (0 > limitpos_[ii]) {
00275   return Status(false, "limitpos must be >= 0");
00276       }
00277     }
00278     for (size_t ii(0); ii < limitvel_.rows(); ++ii) {
00279       if (0 > limitvel_[ii]) {
00280   return Status(false, "limitvel must be >= 0");
00281       }
00282     }
00283     
00284     int const ndim(initpos.rows());
00285     if (ndim != ki_.rows()) {
00286       if ((ndim != 1) && (1 == ki_.rows())) {
00287   ki_ = ki_[0] * Vector::Ones(ndim);
00288       }
00289       else {
00290   return Status(false, "invalid ki dimension");
00291       }
00292     }
00293     if (ndim != limitpos_.rows()) {
00294       if ((ndim != 1) && (1 == limitpos_.rows())) {
00295   limitpos_ = limitpos_[0] * Vector::Ones(ndim);
00296       }
00297       else {
00298   return Status(false, "invalid limitpos dimension");
00299       }
00300     }
00301     if (ndim != limitvel_.rows()) {
00302       if ((ndim != 1) && (1 == limitvel_.rows())) {
00303   limitvel_ = limitvel_[0] * Vector::Ones(ndim);
00304       }
00305       else {
00306   return Status(false, "invalid limitvel dimension");
00307       }
00308     }
00309     
00310     errsum_ = Vector::Zero(ndim);
00311     triggerpos_ = - 1.0 * Vector::Ones(ndim);
00312     
00313     return st;
00314   }
00315   
00316   
00317   Status DraftPIDTask::
00318   computeDraftPIDCommand(Vector const & curpos,
00319        Vector const & curvel,
00320        Vector & command)
00321   {
00322     Status st(computePDCommand(curpos, curvel, command));
00323     if ( ! st) {
00324       return st;
00325     }
00326     
00327     // update "state" for each DOF: if triggerpos_[ii] is positive
00328     // after this loop, it means we should enable the integral term
00329     // for it further down.
00330     for (int ii(0); ii < triggerpos_.rows(); ++ii) {
00331       if (triggerpos_[ii] > 0) {
00332   // integral term currently ON
00333   if (fabs(errpos_[ii]) > triggerpos_[ii]) {
00334     // overshoot: switch off integration
00335     triggerpos_[ii] = -1;
00336   }
00337   else if (fabs(errvel_[ii]) > 2.0 * limitvel_[ii]) {
00338     // too fast: switch off integration
00339     triggerpos_[ii] = -1;
00340   }
00341   // else it remains on
00342       }
00343       else {
00344   // integral term currently OFF
00345   if ((fabs(errpos_[ii]) < limitpos_[ii])
00346       && (fabs(errvel_[ii]) < limitvel_[ii])) {
00347     // close to the goal: switch on integration
00348     triggerpos_[ii] = 2.0 * fabs(errpos_[ii]);
00349     errsum_[ii] = 0;
00350   }
00351       }
00352     }
00353     
00354     // for each DOF with integral term enabled, add it to the command
00355     for (int ii(0); ii < triggerpos_.rows(); ++ii) {
00356       if (triggerpos_[ii] > 0) {
00357   errsum_[ii] += errpos_[ii] * dt_seconds_;
00358   command[ii] += ki_[ii] * errsum_[ii];
00359       }
00360     }
00361     
00362     return st;
00363   }
00364   
00365   
00366   Status DraftPIDTask::
00367   init(Model const & model)
00368   {
00369     jacobian_ = Matrix::Identity(model.getNDOF(), model.getNDOF());
00370     actual_ = model.getState().position_;
00371     return initDraftPIDTask(model.getState().position_);
00372   }
00373   
00374   
00375   Status DraftPIDTask::
00376   update(Model const & model)
00377   {
00378     actual_ = model.getState().position_;
00379     return computeDraftPIDCommand(actual_,
00380           model.getState().velocity_,
00381           command_);
00382   }
00383   
00384   
00385   void DraftPIDTask::
00386   dbg(std::ostream & os,
00387       std::string const & title,
00388       std::string const & prefix) const
00389   {
00390     if ( ! title.empty()) {
00391       os << title << "\n";
00392     }
00393     os << prefix << "draft PID task: `" << instance_name_ << "'\n";
00394     pretty_print(errpos_, os, prefix + "  errpos", prefix + "    ");
00395     pretty_print(triggerpos_, os, prefix + "  triggerpos", prefix + "    ");
00396     pretty_print(ki_, os, prefix + "  ki", prefix + "    ");
00397     pretty_print(errsum_, os, prefix + "  errsum", prefix + "    ");
00398   }
00399 
00400   
00401   
00402   CartPosTask::
00403   CartPosTask(std::string const & name)
00404     : PDTask(name, PDTask::SATURATION_NORM),
00405       end_effector_name_(""),
00406       control_point_(Vector::Zero(3)),
00407       end_effector_node_(0)
00408   {
00409     declareParameter("end_effector", &end_effector_name_, PARAMETER_FLAG_NOLOG);
00410     declareParameter("control_point", &control_point_, PARAMETER_FLAG_NOLOG);
00411   }
00412   
00413   
00414   Status CartPosTask::
00415   init(Model const & model)
00416   {
00417     if (end_effector_name_.empty()) {
00418       return Status(false, "no end_effector");
00419     }
00420     if (3 != control_point_.rows()) {
00421       return Status(false, "control_point must have 3 dimensions");
00422     }
00423     end_effector_node_ = updateActual(model);
00424     if ( ! end_effector_node_) {
00425       return Status(false, "invalid end_effector");
00426     }
00427     return initPDTask(actual_);
00428   }
00429   
00430   
00431   Status CartPosTask::
00432   update(Model const & model)
00433   {
00434     end_effector_node_ = updateActual(model);
00435     if ( ! end_effector_node_) {
00436       return Status(false, "invalid end_effector");
00437     }
00438     
00439     Matrix Jfull;
00440     if ( ! model.computeJacobian(end_effector_node_, actual_[0], actual_[1], actual_[2], Jfull)) {
00441       return Status(false, "failed to compute Jacobian (unsupported joint type?)");
00442     }
00443     jacobian_ = Jfull.block(0, 0, 3, Jfull.cols());
00444     
00445     return computePDCommand(actual_,
00446           jacobian_ * model.getState().velocity_,
00447           command_);
00448   }
00449   
00450   
00451   Status CartPosTask::
00452   check(std::string const * param, std::string const & value) const
00453   {
00454     if (param == &end_effector_name_) {
00455       end_effector_node_ = 0; // lazy re-init... would be nice to detect errors here though, but need model
00456     }
00457     Status ok;
00458     return ok;
00459   }
00460   
00461   
00462   taoDNode const * CartPosTask::
00463   updateActual(Model const & model)
00464   {
00465     if ( ! end_effector_node_) {
00466       end_effector_node_ = model.getNodeByName(end_effector_name_);
00467     }
00468     if (end_effector_node_) {
00469       jspace::Transform ee_transform;
00470       model.computeGlobalFrame(end_effector_node_,
00471              control_point_[0],
00472              control_point_[1],
00473              control_point_[2],
00474              ee_transform);
00475       actual_ = ee_transform.translation();
00476     }
00477     return end_effector_node_;
00478   }
00479   
00480   
00481   JPosTask::
00482   JPosTask(std::string const & name)
00483     : PDTask(name, PDTask::SATURATION_COMPONENT_WISE)
00484   {
00485   }
00486   
00487   
00488   Status JPosTask::
00489   init(Model const & model)
00490   {
00491     jacobian_ = Matrix::Identity(model.getNDOF(), model.getNDOF());
00492     actual_ = model.getState().position_;
00493     return initPDTask(model.getState().position_);
00494   }
00495   
00496   
00497   Status JPosTask::
00498   update(Model const & model)
00499   {
00500     actual_ = model.getState().position_;
00501     return computePDCommand(actual_,
00502           model.getState().velocity_,
00503           command_);
00504   }
00505   
00506   
00507   SelectedJointPostureTask::
00508   SelectedJointPostureTask(std::string const & name)
00509     : Task(name),
00510       kp_(100.0),
00511       kd_(20.0),
00512       initialized_(false)
00513   {
00514     declareParameter("selection", &selection_, PARAMETER_FLAG_NOLOG);
00515     declareParameter("kp", &kp_, PARAMETER_FLAG_NOLOG);
00516     declareParameter("kd", &kd_, PARAMETER_FLAG_NOLOG);
00517   }
00518   
00519   
00520   Status SelectedJointPostureTask::
00521   init(Model const & model)
00522   {
00523     size_t const ndof(model.getNDOF());
00524     active_joints_.clear(); // in case we get called multiple times
00525     for (size_t ii(0); ii < selection_.rows(); ++ii) {
00526       if (ii >= ndof) {
00527   break;
00528       }
00529       if (selection_[ii] > 0.5) {
00530   active_joints_.push_back(ii);
00531       }
00532     }
00533     if (active_joints_.empty()) {
00534       return Status(false, "no active joints");
00535     }
00536     size_t const ndim(active_joints_.size());
00537     actual_ = Vector::Zero(ndim);
00538     command_ = Vector::Zero(ndim);
00539     jacobian_ = Matrix::Zero(ndim, ndof);
00540     for (size_t ii(0); ii < ndim; ++ii) {
00541       actual_[ii] = model.getState().position_[active_joints_[ii]];
00542       jacobian_.coeffRef(ii, active_joints_[ii]) = 1.0;
00543     }
00544     initialized_ = true;
00545     Status ok;
00546     return ok;
00547   }
00548   
00549   
00550   Status SelectedJointPostureTask::
00551   update(Model const & model)
00552   {
00553     Status st;
00554     if ( ! initialized_) {
00555       st.ok = false;
00556       st.errstr = "not initialized";
00557       return st;
00558     }
00559     Vector vel(actual_.rows());
00560     for (size_t ii(0); ii < active_joints_.size(); ++ii) {
00561       actual_[ii] = model.getState().position_[active_joints_[ii]];
00562       vel[ii] = model.getState().velocity_[active_joints_[ii]];
00563     }
00564     command_ = -kp_ * actual_ - kd_ * vel;
00565     return st;
00566   }
00567   
00568   
00569   Status SelectedJointPostureTask::
00570   check(double const * param, double value) const
00571   {
00572     Status st;
00573     if (((&kp_ == param) && (value < 0)) || ((&kd_ == param) && (value < 0))) {
00574       st.ok = false;
00575       st.errstr = "gains must be >= 0";
00576     }
00577     return st;
00578   }
00579   
00580   
00581   Status SelectedJointPostureTask::
00582   check(Vector const * param, Vector const & value) const
00583   {
00584     Status st;
00585     if ((&selection_ == param) && (value.rows() == 0)) {
00586       st.ok = false;
00587       st.errstr = "selection must not be empty";
00588     }
00589     return st;
00590   }
00591   
00592   
00593   TrajectoryTask::
00594   TrajectoryTask(std::string const & name, saturation_policy_t saturation_policy)
00595     : PDTask(name, saturation_policy),
00596       cursor_(0),
00597       dt_seconds_(-1)
00598   {
00599     declareParameter("dt_seconds", &dt_seconds_, PARAMETER_FLAG_NOLOG);
00600     declareParameter("trjgoal", &trjgoal_);
00601     declareParameter("maxacc", &maxacc_, PARAMETER_FLAG_NOLOG);
00602   }
00603   
00604   
00605   TrajectoryTask::
00606   ~TrajectoryTask()
00607   {
00608     delete cursor_;
00609   }
00610   
00611   
00612   Status TrajectoryTask::
00613   initTrajectoryTask(Vector const & initpos)
00614   {
00615     Status st(initPDTask(initpos));
00616     if ( ! st) {
00617       return st;
00618     }
00619     
00620     if (0 > dt_seconds_) {
00621       st.ok = false;
00622       st.errstr = "you did not (correctly) set dt_seconds";
00623       return st;
00624     }
00625     
00626     int const ndim(initpos.rows());
00627     if (ndim != maxacc_.rows()) {
00628       if ((ndim != 1) && (1 == maxacc_.rows())) {
00629   maxacc_ = maxacc_[0] * Vector::Ones(ndim);
00630       }
00631       else {
00632   return Status(false, "invalid maxacc dimension");
00633       }
00634     }
00635     
00636     if (cursor_) {
00637       if (cursor_->dt_seconds_ != dt_seconds_) {
00638   delete cursor_;
00639   cursor_ = 0;
00640       }
00641     }
00642     if ( ! cursor_) {
00643       cursor_ = new TypeIOTGCursor(ndim, dt_seconds_);
00644     }
00645     
00646     trjgoal_ = initpos;
00647     cursor_->position() = initpos;
00648     cursor_->velocity() = Vector::Zero(ndim);
00649     
00650     if (SATURATION_NORM == saturation_policy_) {
00651       qh_maxvel_ = maxvel_[0] * Vector::Ones(ndim);
00652     }
00653     else {
00654       qh_maxvel_ = maxvel_;
00655     }
00656     
00657     return st;
00658   }
00659   
00660   
00661   Status TrajectoryTask::
00662   computeTrajectoryCommand(Vector const & curpos,
00663          Vector const & curvel,
00664          Vector & command)
00665   {
00666     if ( ! cursor_) {
00667       return Status(false, "not initialized");
00668     }
00669     
00670     int const trjstatus(cursor_->next(qh_maxvel_, maxacc_, trjgoal_));
00671     if (0 > trjstatus) {
00672       std::ostringstream msg;
00673       msg << "trajectory generation error code "
00674     << trjstatus << ": " << otg_errstr(trjstatus);
00675       return Status(false, msg.str());
00676     }
00677     
00678     goalpos_ = cursor_->position();
00679     goalvel_ = cursor_->velocity();
00680     
00681     return computePDCommand(curpos, curvel, command);
00682   }
00683   
00684   
00685   Status TrajectoryTask::
00686   check(double const * param, double value) const
00687   {
00688     if ((param == &dt_seconds_) && (0 >= value)) {
00689       return Status(false, "dt_seconds must be > 0");
00690     }
00691     return Status(); // if we had one in the superclass, we'd call PDTask::check(param, value);
00692   }
00693   
00694   
00695   Status TrajectoryTask::
00696   check(Vector const * param, Vector const & value) const
00697   {
00698     Status st(PDTask::check(param, value));
00699     if ( ! st) {
00700       return st;
00701     }
00702     
00703     if ( ! cursor_) {
00704       return st;
00705     }
00706     
00707     if (param == &trjgoal_) {
00708       if (cursor_->ndof_ != value.rows()) {
00709   return Status(false, "invalid goal dimension");
00710       }
00711     }
00712     
00713     if (param == &maxacc_) {
00714       if (cursor_->ndof_ != value.rows()) {
00715   return Status(false, "invalid maxacc dimension");
00716       }
00717       for (size_t ii(0); ii < value.rows(); ++ii) {
00718   if (0 > value[ii]) {
00719     return Status(false, "maxacc must be >= 0");
00720   }
00721       }
00722     }
00723     
00724     if (param == &maxvel_) {
00725       if (SATURATION_NORM == saturation_policy_) {
00726   qh_maxvel_ = value[0] * Vector::Ones(cursor_->ndof_);
00727       }
00728       else {
00729   qh_maxvel_ = value;
00730       }
00731     }
00732     
00733     return st;
00734   }
00735   
00736   
00737   void TrajectoryTask::
00738   dbg(std::ostream & os,
00739       std::string const & title,
00740       std::string const & prefix) const
00741   {
00742     if ( ! title.empty()) {
00743       os << title << "\n";
00744     }
00745     os << prefix << "trajectory task: `" << instance_name_ << "'\n";
00746     if ( ! cursor_) {
00747       os << prefix << "  NOT INITIALIZED\n";
00748     }
00749     else {
00750       pretty_print(actual_, os, prefix + "  actual", prefix + "    ");
00751       pretty_print(cursor_->position(), os, prefix + "  carrot", prefix + "    ");
00752       pretty_print(trjgoal_, os, prefix + "  trjgoal", prefix + "    ");
00753     }
00754   }
00755   
00756   
00757   CartPosTrjTask::
00758   CartPosTrjTask(std::string const & name)
00759     : TrajectoryTask(name, PDTask::SATURATION_NORM),
00760       end_effector_id_(-1),
00761       control_point_(Vector::Zero(3))
00762   {
00763     declareParameter("end_effector_id", &end_effector_id_, PARAMETER_FLAG_NOLOG);
00764     declareParameter("control_point", &control_point_, PARAMETER_FLAG_NOLOG);
00765   }
00766   
00767   
00768   Status CartPosTrjTask::
00769   init(Model const & model)
00770   {
00771     if (0 > end_effector_id_) {
00772       return Status(false, "you did not (correctly) set end_effector_id");
00773     }
00774     if (3 != control_point_.rows()) {
00775       return Status(false, "control_point needs to be three dimensional");
00776     }
00777     if (0 == updateActual(model)) {
00778       return Status(false, "updateActual() failed, did you specify a valid end_effector_id?");
00779     }
00780     return initTrajectoryTask(actual_);
00781   }
00782   
00783   
00784   Status CartPosTrjTask::
00785   update(Model const & model)
00786   {
00787     taoDNode const * ee_node(updateActual(model));
00788     if (0 == ee_node) {
00789       return Status(false, "updateActual() failed, did you specify a valid end_effector_id?");
00790     }
00791     
00792     Matrix Jfull;
00793     if ( ! model.computeJacobian(ee_node, actual_[0], actual_[1], actual_[2], Jfull)) {
00794       return Status(false, "failed to compute Jacobian (unsupported joint type?)");
00795     }
00796     jacobian_ = Jfull.block(0, 0, 3, Jfull.cols());
00797     
00798     return computeTrajectoryCommand(actual_,
00799             jacobian_ * model.getState().velocity_,
00800             command_);
00801   }
00802   
00803   
00804   taoDNode const * CartPosTrjTask::
00805   updateActual(Model const & model)
00806   {
00807     taoDNode * ee_node(model.getNode(end_effector_id_));
00808     if (ee_node) {
00809       jspace::Transform ee_transform;
00810       model.computeGlobalFrame(ee_node,
00811              control_point_[0],
00812              control_point_[1],
00813              control_point_[2],
00814              ee_transform);
00815       actual_ = ee_transform.translation();
00816     }
00817     return ee_node;
00818   }
00819   
00820   
00821   JPosTrjTask::
00822   JPosTrjTask(std::string const & name)
00823     : TrajectoryTask(name, PDTask::SATURATION_COMPONENT_WISE)
00824   {
00825   }
00826   
00827   
00828   Status JPosTrjTask::
00829   init(Model const & model)
00830   {
00831     jacobian_ = Matrix::Identity(model.getNDOF(), model.getNDOF());
00832     actual_ = model.getState().position_;
00833     return initTrajectoryTask(model.getState().position_);
00834   }
00835   
00836   
00837   Status JPosTrjTask::
00838   update(Model const & model)
00839   {
00840     actual_ = model.getState().position_;
00841     return computeTrajectoryCommand(actual_, model.getState().velocity_, command_);
00842   }
00843   
00844   
00845   void JPosTrjTask::
00846   quickSetup(double dt_seconds, double kp, double kd, double maxvel, double maxacc)
00847   {
00848     dt_seconds_ = dt_seconds;
00849     kp_ = kp * Vector::Ones(1);
00850     kd_ = kd * Vector::Ones(1);
00851     maxvel_ = maxvel * Vector::Ones(1);
00852     maxacc_ = maxacc * Vector::Ones(1);
00853   }
00854   
00855   
00856   JointLimitTask::
00857   JointLimitTask(std::string const & name)
00858     : Task(name),
00859       dt_seconds_(-1)
00860   {
00861     declareParameter("dt_seconds", &dt_seconds_, PARAMETER_FLAG_NOLOG);
00862     declareParameter("upper_stop_deg", &upper_stop_deg_, PARAMETER_FLAG_NOLOG);
00863     declareParameter("upper_trigger_deg", &upper_trigger_deg_, PARAMETER_FLAG_NOLOG);
00864     declareParameter("lower_stop_deg", &lower_stop_deg_, PARAMETER_FLAG_NOLOG);
00865     declareParameter("lower_trigger_deg", &lower_trigger_deg_, PARAMETER_FLAG_NOLOG);
00866     declareParameter("kp", &kp_, PARAMETER_FLAG_NOLOG);
00867     declareParameter("kd", &kd_, PARAMETER_FLAG_NOLOG);
00868     declareParameter("maxvel", &maxvel_, PARAMETER_FLAG_NOLOG);
00869     declareParameter("maxacc", &maxacc_, PARAMETER_FLAG_NOLOG);
00870   }
00871   
00872   
00873   JointLimitTask::
00874   ~JointLimitTask()
00875   {
00876     for (size_t ii(0); ii < cursor_.size(); ++ii) {
00877       delete cursor_[ii];
00878     }
00879   }
00880   
00881   
00882   Status JointLimitTask::
00883   check(Vector const * param, Vector const & value) const
00884   {
00885     if ((param == &kp_) || (param == &kd_) || (param == &maxvel_) || (param == &maxacc_)) {
00886       for (size_t ii(0); ii < value.rows(); ++ii) {
00887   if (0 > value[ii]) {
00888     return Status(false, "gains and limits must be >= 0");
00889   }
00890       }
00891     }
00892     if ( ! cursor_.empty()) { // we are initialized
00893       if ((param == &kp_) || (param == &kd_) || (param == &maxvel_) || (param == &maxacc_)) {
00894   if (cursor_.size() != value.rows()) {
00895     return Status(false, "invalid dimension");
00896   }
00897       }
00898     }
00899     return Status();
00900   }
00901   
00902   
00903   Status JointLimitTask::
00904   check(double const * param, double const & value) const
00905   {
00906     if ((param == &dt_seconds_) && (0 >= value)) {
00907       return Status(false, "dt_seconds must be > 0");
00908     }
00909     return Status();
00910   }
00911   
00912   
00913   Status JointLimitTask::
00914   init(Model const & model)
00915   {
00916     if (dt_seconds_ <= 0) {
00917       return Status(false, "dt_seconds must be positive");
00918     }
00919     size_t const ndof(model.getNDOF());
00920     if (upper_stop_deg_.rows() != ndof) {
00921       return Status(false, "upper_stop dimension mismatch");
00922     }
00923     if (upper_trigger_deg_.rows() != ndof) {
00924       return Status(false, "upper_trigger dimension mismatch");
00925     }
00926     if (lower_stop_deg_.rows() != ndof) {
00927       return Status(false, "lower_stop dimension mismatch");
00928     }
00929     if (lower_trigger_deg_.rows() != ndof) {
00930       return Status(false, "lower_trigger dimension mismatch");
00931     }
00932     if (maxvel_.rows() != ndof) {
00933       return Status(false, "maxvel dimension mismatch");
00934     }
00935     if (maxacc_.rows() != ndof) {
00936       return Status(false, "maxacc dimension mismatch");
00937     }
00938     if (kp_.rows() != ndof) {
00939       return Status(false, "kp dimension mismatch");
00940     }
00941     if (kd_.rows() != ndof) {
00942       return Status(false, "kd dimension mismatch");
00943     }
00944     
00945     upper_stop_ = M_PI * upper_stop_deg_ / 180.0;
00946     upper_trigger_ = M_PI * upper_trigger_deg_ / 180.0;
00947     lower_stop_ = M_PI * lower_stop_deg_ / 180.0;
00948     lower_trigger_ = M_PI * lower_trigger_deg_ / 180.0;
00949     
00950     cursor_.assign(ndof, 0);
00951     goal_ = Vector::Zero(ndof);
00952     jacobian_.resize(0, 0);
00953     
00954     // builds/updates jacobian, initializes cursors and goals, set actual_
00955     updateState(model);
00956     
00957     Status ok;
00958     return ok;
00959   }
00960   
00961   
00962   Status JointLimitTask::
00963   update(Model const & model)
00964   {
00965     if (dt_seconds_ <= 0) {
00966       return Status(false, "not initialized");
00967     }
00968     
00969     updateState(model);
00970     command_.resize(jacobian_.rows());
00971     size_t task_index(0);
00972     
00973     for (size_t joint_index(0); joint_index < cursor_.size(); ++joint_index) {
00974       if (cursor_[joint_index]) {
00975   if (0 > cursor_[joint_index]->next(maxvel_[joint_index], maxacc_[joint_index], goal_[joint_index])) {
00976     return Status(false, "trajectory generation error");
00977   }
00978   double com(kp_[joint_index] * (cursor_[joint_index]->position()[0] - actual_[task_index]));
00979   if ((maxvel_[joint_index] > 1e-4) && (kd_[joint_index] > 1e-4)) {
00980     double const sat(fabs((com / maxvel_[joint_index]) / kd_[joint_index]));
00981     if (sat > 1.0) {
00982       com /= sat;
00983     }
00984   }
00985   command_[task_index]
00986     = com
00987     + kd_[joint_index] * (cursor_[joint_index]->velocity()[0] - model.getState().velocity_[joint_index]);
00988   ++task_index;
00989       }
00990     }
00991     
00992     Status ok;
00993     return ok;
00994   }
00995   
00996   
00997   void JointLimitTask::
00998   updateState(Model const & model)
00999   {
01000     size_t const ndof(model.getNDOF());
01001     Vector const & jpos(model.getState().position_);
01002     bool dimension_changed(false);
01003     size_t task_dimension(0);
01004     
01005     for (size_t ii(0); ii < ndof; ++ii) {
01006       if (cursor_[ii]) {
01007   ++task_dimension;
01008       }
01009       else {
01010   if (jpos[ii] > upper_trigger_[ii]) {
01011     ++task_dimension;
01012     dimension_changed = true;
01013     cursor_[ii] = new TypeIOTGCursor(1, dt_seconds_);
01014     cursor_[ii]->position()[0] = jpos[ii];
01015     cursor_[ii]->velocity()[0] = model.getState().velocity_[ii];
01016     goal_[ii] = upper_stop_[ii];
01017   }
01018   else if (jpos[ii] < lower_trigger_[ii]) {
01019     ++task_dimension;
01020     dimension_changed = true;
01021           cursor_[ii] = new TypeIOTGCursor(1, dt_seconds_);
01022           cursor_[ii]->position()[0] = jpos[ii];
01023           cursor_[ii]->velocity()[0] = model.getState().velocity_[ii];
01024           goal_[ii] = lower_stop_[ii];
01025   }
01026       }
01027     }
01028     
01029     if (dimension_changed) {
01030       jacobian_ = Matrix::Zero(task_dimension, ndof);
01031       size_t task_index(0);
01032       for (size_t joint_index(0); joint_index < ndof; ++joint_index) {
01033   if (cursor_[joint_index]) {
01034     jacobian_.coeffRef(task_index, joint_index) = 1.0;
01035     ++task_index;
01036   }
01037       }
01038     }
01039     
01040     actual_.resize(task_dimension);
01041     size_t task_index(0);
01042     for (size_t joint_index(0); joint_index < ndof; ++joint_index) {
01043       if (cursor_[joint_index]) {
01044   actual_[task_index] = jpos[joint_index];
01045   ++task_index;
01046       }
01047     }
01048   }
01049   
01050   
01051   void JointLimitTask::
01052   dbg(std::ostream & os,
01053       std::string const & title,
01054       std::string const & prefix) const
01055   {
01056     if ( ! title.empty()) {
01057       os << title << "\n";
01058     }
01059     os << prefix << "joint limit task: `" << instance_name_ << "'\n";
01060     if (cursor_.empty()) {
01061       os << prefix << "  NOT INITIALIZED\n";
01062     }
01063     pretty_print(actual_, os, prefix + "  actual", prefix + "    ");
01064     pretty_print(goal_, os, prefix + "  goal", prefix + "    ");
01065     pretty_print(jacobian_, os, prefix + "  jacobian", prefix + "    ");
01066   }
01067 
01068 
01069   OrientationTask::
01070   OrientationTask(std::string const & name)
01071     : Task(name),
01072       end_effector_id_(-1),
01073       kp_(50),
01074       kd_(5),
01075       maxvel_(0.5)
01076   {
01077     declareParameter("end_effector_id", &end_effector_id_, PARAMETER_FLAG_NOLOG);
01078     declareParameter("kp", &kp_, PARAMETER_FLAG_NOLOG);
01079     declareParameter("kd", &kd_, PARAMETER_FLAG_NOLOG);
01080     declareParameter("maxvel", &maxvel_, PARAMETER_FLAG_NOLOG);
01081     declareParameter("eepos", &eepos_);
01082   }
01083   
01084   
01085   Status OrientationTask::
01086   init(Model const & model)
01087   {
01088     if (0 > end_effector_id_) {
01089       return Status(false, "I need an end effector ID please");
01090     }
01091     if ( ! updateActual(model)) {
01092       return Status(false, "invalid end effector ID or failure to compute Jacobian");
01093     }
01094     goal_x_ = actual_x_;
01095     goal_y_ = actual_y_;
01096     goal_z_ = actual_z_;
01097     Status ok;
01098     return ok;
01099   }
01100   
01101   
01102   taoDNode const * OrientationTask::
01103   updateActual(Model const & model)
01104   {
01105     taoDNode * ee_node(model.getNode(end_effector_id_));
01106     if ( ! ee_node) {
01107       return 0;
01108     }
01109     
01110     jspace::Transform ee_transform;
01111     model.computeGlobalFrame(ee_node, Vector::Zero(3), ee_transform);
01112     Matrix Jfull;
01113     eepos_ = ee_transform.translation();
01114     if ( ! model.computeJacobian(ee_node,
01115          eepos_[0],
01116          eepos_[1],
01117          eepos_[2],
01118          Jfull)) {
01119       return 0;
01120     }
01121     
01122     jacobian_ = Jfull.block(3, 0, 3, Jfull.cols());
01123     
01124     actual_x_ = ee_transform.linear().block(0, 0, 3, 1);
01125     actual_y_ = ee_transform.linear().block(0, 1, 3, 1);
01126     actual_z_ = ee_transform.linear().block(0, 2, 3, 1);
01127 
01128     actual_.resize(9);
01129     actual_.block(0, 0, 3, 1) = actual_x_;
01130     actual_.block(3, 0, 3, 1) = actual_y_;
01131     actual_.block(6, 0, 3, 1) = actual_z_;
01132     
01133     velocity_ = jacobian_ * model.getState().velocity_;
01134     
01135     return ee_node;
01136   }
01137   
01138   
01139   Status OrientationTask::
01140   update(Model const & model)
01141   {
01142     if ( ! updateActual(model)) {
01143       return Status(false, "invalid end effector ID");
01144     }
01145     delta_ = actual_x_.cross(goal_x_) + actual_y_.cross(goal_y_) + actual_z_.cross(goal_z_);
01146     delta_ *= -0.5;
01147     
01148     command_ = -delta_ * kp_;
01149     if ((maxvel_ > 1e-4) && (kd_ > 1e-4)) {
01150       double const sat(fabs((command_.norm() / maxvel_) / kd_));
01151       if (sat > 1.0) {
01152   command_ /= sat;
01153       }
01154     }
01155     
01156     command_ -= velocity_ * kd_;
01157     
01158     Status ok;
01159     return ok;
01160   }
01161   
01162   
01163   void OrientationTask::
01164   dbg(std::ostream & os,
01165       std::string const & title,
01166       std::string const & prefix) const
01167   {
01168     if ( ! title.empty()) {
01169       os << title << "\n";
01170     }
01171     os << prefix << "orientation task: `" << instance_name_ << "'\n";
01172     pretty_print(velocity_, os, prefix + "  velocity", prefix + "    ");
01173     pretty_print(goal_x_, os, prefix + "  goal_x", prefix + "    ");
01174     pretty_print(goal_y_, os, prefix + "  goal_y", prefix + "    ");
01175     pretty_print(goal_z_, os, prefix + "  goal_z", prefix + "    ");
01176     Vector foo(3);
01177     foo << goal_x_.norm(), goal_y_.norm(), goal_z_.norm();
01178     pretty_print(foo, os, prefix + "  length of goal unit vectors", prefix + "    ");
01179     pretty_print(actual_x_, os, prefix + "  actual_x", prefix + "    ");
01180     pretty_print(actual_y_, os, prefix + "  actual_y", prefix + "    ");
01181     pretty_print(actual_z_, os, prefix + "  actual_z", prefix + "    ");
01182     foo << actual_x_.norm(), actual_y_.norm(), actual_z_.norm();
01183     pretty_print(foo, os, prefix + "  length of actual unit vectors", prefix + "    ");
01184     pretty_print(delta_, os, prefix + "  delta", prefix + "    ");
01185     pretty_print(command_, os, prefix + "  command", prefix + "    ");
01186   }
01187 
01188 }

Generated on Fri Aug 26 01:31:17 2011 for Stanford Whole-Body Control Framework by  doxygen 1.5.4