jspace/jspace/Model.cpp

Go to the documentation of this file.
00001 /*
00002  * Stanford Whole-Body Control Framework http://stanford-wbc.sourceforge.net/
00003  *
00004  * Copyright (C) 2010 The Board of Trustees of The Leland Stanford Junior University. All rights reserved.
00005  *
00006  * This program is free software: you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public License
00008  * as published by the Free Software Foundation, either version 3 of
00009  * the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful, but
00012  * WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this program.  If not, see
00018  * <http://www.gnu.org/licenses/>
00019  */
00020 
00026 #include "Model.hpp"
00027 #include "tao_util.hpp"
00028 #include <tao/dynamics/taoNode.h>
00029 #include <tao/dynamics/taoJoint.h>
00030 #include <tao/dynamics/taoDynamics.h>
00031 
00032 #undef DEBUG
00033 
00034 
00035 static deVector3 const zero_gravity(0, 0, 0);
00036 static deVector3 const earth_gravity(0, 0, -9.81);
00037 
00038 
00039 // Beware: no bound checks!
00040 size_t squareToTriangularIndex(size_t irow, size_t icol, size_t dim)
00041 {
00042   if (0 == irow) {
00043     return icol;
00044   }
00045   if (0 == icol) {
00046     return irow;
00047   }
00048   if (irow > icol) {
00049     // should have a lookup table for icol * (icol + 1) / 2
00050     return irow + dim * icol - icol * (icol + 1) / 2;
00051   }
00052   return icol + dim * irow - irow * (irow + 1) / 2;
00053 }
00054 
00055 
00056 namespace jspace {
00057   
00058   
00059   Model::
00060   Model()
00061     : ndof_(0),
00062       kgm_tree_(0),
00063       cc_tree_(0)
00064   {
00065   }
00066   
00067   
00068   int Model::
00069   init(tao_tree_info_s * kgm_tree,
00070        tao_tree_info_s * cc_tree,
00071        std::ostream * msg)
00072   {
00073     int const status(tao_consistency_check(kgm_tree->root, msg));
00074     if (0 != status) {
00075       return status;
00076     }
00077     
00078     if (kgm_tree_) {
00079       if (msg) {
00080   *msg << "jspace::Model::init(): already initialized\n";
00081       }
00082       return -1;
00083     }
00084     
00085     if ( ! kgm_tree->sort()) {
00086       if (msg) {
00087   *msg << "jspace::Model::init(): could not sort KGM nodes according to IDs\n";
00088       }
00089       return -2;
00090     }
00091     
00092     if ((0 != cc_tree) && ( ! cc_tree->sort())) {
00093       if (msg) {
00094   *msg << "jspace::Model::init(): could not sort CC nodes according to IDs\n";
00095       }
00096       return -3;
00097     }
00098     
00099     // Create ancestry table of all nodes in the KGM tree, for correct
00100     // (and slightly more efficient) computation of the Jacobian.
00101     ancestry_table_.clear();  // just paranoid...
00102     typedef tao_tree_info_s::node_info_t::const_iterator cit_t;
00103     cit_t in(kgm_tree->info.begin());
00104     cit_t iend(kgm_tree->info.end());
00105     for (; in != iend; ++in) {
00106       // ...paranoid checks...
00107       if ( ! in->node) {
00108   if (msg) {
00109     *msg << "jspace::Model::init(): NULL node at entry #" << in->id << "\n";
00110   }
00111   return -4;
00112       }
00113       if (in->id != in->node->getID()) {
00114   if (msg) {
00115     *msg << "jspace::Model::init(): node ID of entry #" << in->id << " is " << in->node->getID() << "\n";
00116   }
00117   return -5;
00118       }
00119       ancestry_list_t & alist(ancestry_table_[in->node]); // first reference creates the instance
00120       // walk up the ancestry, append each parent to the list of
00121       // ancestors of this node
00122       for (taoDNode * node(in->node); 0 != node; node = node->getDParent()) {
00123   ancestry_entry_s entry;
00124   entry.id = node->getID();
00125   entry.joint = node->getJointList();
00126   if (0 != entry.joint) {
00127     alist.push_back(entry);
00128   }
00129       }
00130     }
00131     
00132     kgm_tree_ = kgm_tree;
00133     cc_tree_ = cc_tree;
00134     ndof_ = kgm_tree->info.size();
00135     
00136     return 0;
00137   }
00138   
00139   
00140   Model::
00141   ~Model()
00142   {
00143     delete kgm_tree_;
00144     delete cc_tree_;
00145   }
00146   
00147   
00148   void Model::
00149   update(State const & state)
00150   {
00151     setState(state);
00152     updateKinematics();
00153     updateDynamics();
00154   }
00155   
00156   
00157   void Model::
00158   setState(State const & state)
00159   {
00160     state_ = state;
00161     for (size_t ii(0); ii < ndof_; ++ii) {
00162       taoJoint * joint(kgm_tree_->info[ii].joint);
00163       joint->setQ(&const_cast<State&>(state).position_.coeffRef(ii));
00164       joint->zeroDQ();
00165       joint->zeroDDQ();
00166       joint->zeroTau();
00167     }
00168     if (cc_tree_) {
00169       for (size_t ii(0); ii < ndof_; ++ii) {
00170   taoJoint * joint(cc_tree_->info[ii].joint);
00171   joint->setQ(&const_cast<State&>(state).position_.coeffRef(ii));
00172   joint->setDQ(&const_cast<State&>(state).velocity_.coeffRef(ii));
00173   joint->zeroDDQ();
00174   joint->zeroTau();
00175       }
00176     }
00177   }
00178   
00179   
00180   size_t Model::
00181   getNNodes() const
00182   {
00183     return ndof_;
00184   }
00185   
00186   
00187   size_t Model::
00188   getNJoints() const
00189   {
00190     // one day this will be different...
00191     return ndof_;
00192   }
00193   
00194   
00195   size_t Model::
00196   getNDOF() const
00197   {
00198     // one day this will be different...
00199     return ndof_;
00200   }
00201   
00202   
00203   std::string Model::
00204   getNodeName(size_t id) const
00205   {
00206     std::string name("");
00207     if (ndof_ > id) {
00208       name = kgm_tree_->info[id].link_name;
00209     }
00210     return name;
00211   }
00212   
00213   
00214   std::string Model::
00215   getJointName(size_t id) const
00216   {
00217     std::string name("");
00218     if (ndof_ > id) {
00219       name = kgm_tree_->info[id].joint_name;
00220     }
00221     return name;
00222   }
00223   
00224   
00225   taoDNode * Model::
00226   getNode(size_t id) const
00227   {
00228     if (ndof_ > id) {
00229       return kgm_tree_->info[id].node;
00230     }
00231     return 0;
00232   }
00233   
00234   
00235   taoDNode * Model::
00236   getNodeByName(std::string const & name) const
00237   {
00238     for (size_t ii(0); ii < ndof_; ++ii) {
00239       if (name == kgm_tree_->info[ii].link_name) {
00240   return  kgm_tree_->info[ii].node;
00241       }
00242     }
00243     return 0;
00244   }
00245   
00246   
00247   taoDNode * Model::
00248   getNodeByJointName(std::string const & name) const
00249   {
00250     for (size_t ii(0); ii < ndof_; ++ii) {
00251       if (name == kgm_tree_->info[ii].joint_name) {
00252   return  kgm_tree_->info[ii].node;
00253       }
00254     }
00255     return 0;
00256   }
00257   
00258   
00259   void Model::
00260   getJointLimits(Vector & joint_limits_lower,
00261      Vector & joint_limits_upper) const
00262   {
00263     joint_limits_lower.resize(ndof_);
00264     joint_limits_upper.resize(ndof_);
00265     for (size_t ii(0); ii < ndof_; ++ii) {
00266       joint_limits_lower[ii] = kgm_tree_->info[ii].limit_lower;
00267       joint_limits_upper[ii] = kgm_tree_->info[ii].limit_upper;
00268     }
00269   }
00270   
00271   
00272   void Model::
00273   updateKinematics()
00274   {
00275     taoDynamics::updateTransformation(kgm_tree_->root);
00276     taoDynamics::globalJacobian(kgm_tree_->root);
00277     if (cc_tree_) {
00278       taoDynamics::updateTransformation(cc_tree_->root);
00279       taoDynamics::globalJacobian(cc_tree_->root);
00280     }
00281   }
00282   
00283   
00284   bool Model::
00285   getGlobalFrame(taoDNode const * node,
00286      Transform & global_transform) const
00287   {
00288     if ( ! node) {
00289       return false;
00290     }
00291     
00292     deFrame const * tao_frame(node->frameGlobal());
00293     deQuaternion const & tao_quat(tao_frame->rotation());
00294     deVector3 const & tao_trans(tao_frame->translation());
00295     
00296 #warning "TO DO: maybe the other way around..."
00297     // beware: Eigen::Quaternion(w, x, y, z) puts w first, whereas
00298     // deQuaternion(qx, qy, qz, qw) puts w last. Of course.
00299     global_transform = Translation(tao_trans[0], tao_trans[1], tao_trans[2]);
00300     global_transform *= Quaternion(tao_quat[3], tao_quat[0], tao_quat[1], tao_quat[2]);
00301     
00302     return true;
00303   }
00304   
00305   
00306   bool Model::
00307   computeGlobalFrame(taoDNode const * node,
00308          Transform const & local_transform,
00309          Transform & global_transform) const
00310   {
00311     if ( ! getGlobalFrame(node, global_transform)) {
00312       return false;
00313     }
00314     global_transform = global_transform * local_transform;
00315     return true;
00316   }
00317   
00318   
00319   bool Model::
00320   computeGlobalFrame(taoDNode const * node,
00321          Vector const & local_translation,
00322          Transform & global_transform) const
00323   {
00324     if ( ! getGlobalFrame(node, global_transform)) {
00325       return false;
00326     }
00327     global_transform.translation() += global_transform.linear() * local_translation;
00328     return true;
00329   }
00330   
00331   
00332   bool Model::
00333   computeGlobalFrame(taoDNode const * node,
00334          double local_x, double local_y, double local_z,
00335          Transform & global_transform) const
00336   {
00337     if ( ! getGlobalFrame(node, global_transform)) {
00338       return false;
00339     }
00340     global_transform.translation() += global_transform.linear() * Eigen::Vector3d(local_x, local_y, local_z);
00341     return true;
00342   }
00343 
00344     
00345   bool Model::
00346   computeGlobalCOMFrame(taoDNode const * node,
00347       Transform & global_com_transform) const
00348   {
00349     if ( ! node) {
00350       return false;
00351     }
00352     
00353     deVector3 const * com(const_cast<taoDNode*>(node)->center());
00354     if ( ! com) {
00355       return getGlobalFrame(node, global_com_transform);
00356     }
00357     
00361     return computeGlobalFrame(node, com->elementAt(0), com->elementAt(1), com->elementAt(2), global_com_transform);
00362   }
00363   
00364   
00365   bool Model::
00366   computeJacobian(taoDNode const * node,
00367       Matrix & jacobian) const
00368   {
00369     if ( ! node) {
00370       return false;
00371     }
00372     deVector3 const & gpos(node->frameGlobal()->translation());
00373     return computeJacobian(node, gpos[0], gpos[1], gpos[2], jacobian);
00374   }
00375   
00376   
00377   bool Model::
00378   computeJacobian(taoDNode const * node,
00379       double gx, double gy, double gz,
00380       Matrix & jacobian) const
00381   {
00382     if ( ! node) {
00383       return false;
00384     }
00385     ancestry_table_t::const_iterator iae(ancestry_table_.find(const_cast<taoDNode*>(node)));
00386     if (iae == ancestry_table_.end()) {
00387       return false;
00388     }
00389     ancestry_list_t const & alist(iae->second);
00390     
00391 #ifdef DEBUG
00392     fprintf(stderr, "computeJacobian()\ng: [% 4.2f % 4.2f % 4.2f]\n", gx, gy, gz);
00393 #endif // DEBUG
00394     
00395     // \todo Implement support for more than one joint per node, and
00396     //  more than one DOF per joint.
00397     jacobian = Matrix::Zero(6, ndof_);
00398     ancestry_list_t::const_iterator ia(alist.begin());
00399     ancestry_list_t::const_iterator iend(alist.end());
00400     for (; ia != iend; ++ia) {
00401       deVector6 Jg_col;
00402       ia->joint->getJgColumns(&Jg_col);
00403       int const icol(ia->id);
00404       
00405 #ifdef DEBUG
00406       fprintf(stderr, "iJg[%d]: [ % 4.2f % 4.2f % 4.2f % 4.2f % 4.2f % 4.2f]\n",
00407         icol,
00408         Jg_col.elementAt(0), Jg_col.elementAt(1), Jg_col.elementAt(2),
00409         Jg_col.elementAt(3), Jg_col.elementAt(4), Jg_col.elementAt(5));
00410 #endif // DEBUG
00411       
00412       for (size_t irow(0); irow < 6; ++irow) {
00413   jacobian.coeffRef(irow, icol) = Jg_col.elementAt(irow);
00414       }
00415       
00416       // Add the effect of the joint rotation on the translational
00417       // velocity at the global point (column-wise cross product with
00418       // [gx;gy;gz]). Note that Jg_col.elementAt(3) is the
00419       // contribution to omega_x etc, because the upper 3 elements of
00420       // Jg_col are v_x etc.  (And don't ask me why we have to
00421       // subtract the cross product, it probably got inverted
00422       // somewhere)
00423       jacobian.coeffRef(0, icol) -= -gz * Jg_col.elementAt(4) + gy * Jg_col.elementAt(5);
00424       jacobian.coeffRef(1, icol) -=  gz * Jg_col.elementAt(3) - gx * Jg_col.elementAt(5);
00425       jacobian.coeffRef(2, icol) -= -gy * Jg_col.elementAt(3) + gx * Jg_col.elementAt(4);
00426       
00427 #ifdef DEBUG
00428       fprintf(stderr, "0Jg[%d]: [ % 4.2f % 4.2f % 4.2f % 4.2f % 4.2f % 4.2f]\n",
00429         icol,
00430         jacobian.coeff(0, icol), jacobian.coeff(1, icol), jacobian.coeff(2, icol),
00431         jacobian.coeff(3, icol), jacobian.coeff(4, icol), jacobian.coeff(5, icol));
00432 #endif // DEBUG
00433       
00434     }
00435     return true;
00436   }
00437   
00438   
00439   void Model::
00440   updateDynamics()
00441   {
00442     computeGravity();
00443     computeCoriolisCentrifugal();
00444     computeMassInertia();
00445     computeInverseMassInertia();
00446   }
00447   
00448   
00449   bool Model::
00450   computeCOM(Vector & com, Matrix * opt_jcom) const
00451   {
00452     com = Vector::Zero(3);
00453     if (opt_jcom) {
00454       *opt_jcom = Matrix::Zero(3, ndof_);
00455     }
00456     double mtotal(0);
00457     for (size_t ii(0); ii < ndof_; ++ii) {
00458       taoDNode * const node(kgm_tree_->info[ii].node);
00459       deVector3 wpos;
00460       wpos.multiply(node->frameGlobal()->rotation(), *(node->center()));
00461       wpos += node->frameGlobal()->translation();
00462       if (opt_jcom) {
00463   Matrix wjcom;
00464   if ( ! computeJacobian(node, wpos[0], wpos[1], wpos[2], wjcom)) {
00465     return false;
00466   }
00467   *opt_jcom += *(node->mass()) * wjcom.block(0, 0, 3, wjcom.cols());
00468       }
00469       wpos *= *(node->mass());
00470       mtotal += *(node->mass());
00471       com += Vector::Map(&wpos[0], 3);
00472     }
00473     if (fabs(mtotal) > 1e-3) {
00474       com /= mtotal;
00475       if (opt_jcom) {
00476   *opt_jcom /= mtotal;
00477       }
00478     }
00479     return true;
00480   }
00481   
00482   
00483   void Model::
00484   computeGravity()
00485   {
00486     g_torque_.resize(ndof_);
00487     taoDynamics::invDynamics(kgm_tree_->root, &earth_gravity);
00488     for (size_t ii(0); ii < ndof_; ++ii) {
00489       kgm_tree_->info[ii].joint->getTau(&g_torque_[ii]);
00490     }
00491   }
00492   
00493   
00494   bool Model::
00495   disableGravityCompensation(size_t index, bool disable)
00496   {
00497     if (ndof_ <= index) {
00498       return true;
00499     }
00500     
00501     dof_set_t::const_iterator const idof(gravity_disabled_.find(index));
00502     
00503     if (idof == gravity_disabled_.end()) {
00504       // currently not disabled
00505       if (disable) {
00506   gravity_disabled_.insert(index);
00507       }
00508       return false;
00509     }
00510     
00511     // currently disabled
00512     if ( ! disable) {
00513       gravity_disabled_.erase(idof);
00514     }
00515     return true;
00516   }
00517   
00518   
00519   bool Model::
00520   getGravity(Vector & gravity) const
00521   {
00522     if (0 == g_torque_.size()) {
00523       return false;
00524     }
00525     gravity = g_torque_;
00526     // knock away gravity torque from links that are already otherwise compensated
00527     dof_set_t::const_iterator iend(gravity_disabled_.end());
00528     for (dof_set_t::const_iterator ii(gravity_disabled_.begin()); ii != iend; ++ii) {
00529       gravity[*ii] = 0;
00530     }
00531     return true;
00532   }
00533   
00534   
00535   void Model::
00536   computeCoriolisCentrifugal()
00537   {
00538     if (cc_tree_) {
00539       cc_torque_.resize(ndof_);
00540       taoDynamics::invDynamics(cc_tree_->root, &zero_gravity);
00541       for (size_t ii(0); ii < ndof_; ++ii) {
00542   cc_tree_->info[ii].joint->getTau(&cc_torque_[ii]);
00543       }
00544     }
00545   }
00546   
00547   
00548   bool Model::
00549   getCoriolisCentrifugal(Vector & coriolis_centrifugal) const
00550   {
00551     if ( ! cc_tree_) {
00552       return false;
00553     }
00554     if (0 == cc_torque_.size()) {
00555       return false;
00556     }
00557     coriolis_centrifugal = cc_torque_;
00558     return true;
00559   }
00560   
00561   
00562   void Model::
00563   computeMassInertia()
00564   {
00565     if (a_upper_triangular_.empty()) {
00566       a_upper_triangular_.resize(ndof_ * (ndof_ + 1) / 2);
00567     }
00568     
00569     deFloat const one(1);
00570     for (size_t irow(0); irow < ndof_; ++irow) {
00571       taoJoint * joint(kgm_tree_->info[irow].joint);
00572       
00573       // Compute one column of A by solving inverse dynamics of the
00574       // corresponding joint having a unit acceleration, while all the
00575       // others remain fixed. This works on the kgm_tree because it
00576       // has zero speeds, thus the Coriolis-centrifgual effects are
00577       // zero, and by using zero gravity we get pure system dynamics:
00578       // force = mass * acceleration (in matrix form).
00579       joint->setDDQ(&one);
00580       taoDynamics::invDynamics(kgm_tree_->root, &zero_gravity);
00581       joint->zeroDDQ();
00582       
00583       // Retrieve the column of A by reading the joint torques
00584       // required for the column-selecting unit acceleration (into a
00585       // flattened upper triangular matrix).
00586       
00587       for (size_t icol(0); icol <= irow; ++icol) {
00588   kgm_tree_->info[icol].joint->getTau(&a_upper_triangular_[squareToTriangularIndex(irow, icol, ndof_)]);
00589       }
00590     }
00591     
00592     // Reset all the torques.
00593     for (size_t ii(0); ii < ndof_; ++ii) {
00594       kgm_tree_->info[ii].joint->zeroTau();
00595     }
00596   }
00597   
00598   
00599   bool Model::
00600   getMassInertia(Matrix & mass_inertia) const
00601   {
00602     if (a_upper_triangular_.empty()) {
00603       return false;
00604     }
00605     
00606     mass_inertia.resize(ndof_, ndof_);
00607     for (size_t irow(0); irow < ndof_; ++irow) {
00608       for (size_t icol(0); icol <= irow; ++icol) {
00609   mass_inertia.coeffRef(irow, icol) = a_upper_triangular_[squareToTriangularIndex(irow, icol, ndof_)];
00610   if (irow != icol) {
00611     mass_inertia.coeffRef(icol, irow) = mass_inertia.coeff(irow, icol);
00612   }
00613       }
00614     }
00615     
00616     return true;
00617   }
00618   
00619   
00620   void Model::
00621   computeInverseMassInertia()
00622   {
00623     if (ainv_upper_triangular_.empty()) {
00624       ainv_upper_triangular_.resize(ndof_ * (ndof_ + 1) / 2);
00625     }
00626     
00627     deFloat const one(1);
00628     for (size_t irow(0); irow < ndof_; ++irow) {
00629       taoJoint * joint(kgm_tree_->info[irow].joint);
00630       
00631       // Compute one column of Ainv by solving forward dynamics of the
00632       // corresponding joint having a unit torque, while all the
00633       // others remain unactuated. This works on the kgm_tree because
00634       // it has zero speeds, thus the Coriolis-centrifgual effects are
00635       // zero, and by using zero gravity we get pure system dynamics:
00636       // acceleration = mass_inv * force (in matrix form).
00637       joint->setTau(&one);
00638       taoDynamics::fwdDynamics(kgm_tree_->root, &zero_gravity);
00639       joint->zeroTau();
00640       
00641       // Retrieve the column of Ainv by reading the joint
00642       // accelerations generated by the column-selecting unit torque
00643       // (into a flattened upper triangular matrix).
00644       for (size_t icol(0); icol <= irow; ++icol) {
00645   kgm_tree_->info[icol].joint->getDDQ(&ainv_upper_triangular_[squareToTriangularIndex(irow, icol, ndof_)]);
00646       }
00647     }
00648     
00649     // Reset all the accelerations.
00650     for (size_t ii(0); ii < ndof_; ++ii) {
00651       kgm_tree_->info[ii].joint->zeroDDQ();
00652     }
00653   }
00654   
00655   
00656   bool Model::
00657   getInverseMassInertia(Matrix & inverse_mass_inertia) const
00658   {
00659     if (ainv_upper_triangular_.empty()) {
00660       return false;
00661     }
00662     
00663     inverse_mass_inertia.resize(ndof_, ndof_);
00664     for (size_t irow(0); irow < ndof_; ++irow) {
00665       for (size_t icol(0); icol <= irow; ++icol) {
00666   inverse_mass_inertia.coeffRef(irow, icol) = ainv_upper_triangular_[squareToTriangularIndex(irow, icol, ndof_)];
00667   if (irow != icol) {
00668     inverse_mass_inertia.coeffRef(icol, irow) = inverse_mass_inertia.coeff(irow, icol);
00669   }
00670       }
00671     }
00672     
00673     return true;
00674   }
00675 
00676 }

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