jspace/jspace/inertia_util.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 "inertia_util.hpp"
00027 #include "tao_dump.hpp"
00028 #include <tao/dynamics/tao.h>
00029 #include <sstream>
00030 
00031 using namespace std;
00032 
00033 namespace jspace {
00034   
00035   
00036   void inertia_parallel_axis_transform(double in_ixx, double in_ixy, double in_ixz,
00037                double in_iyy, double in_iyz, double in_izz,
00038                double trans_x, double trans_y, double trans_z, double mass,
00039                double & out_ixx, double & out_ixy, double & out_ixz,
00040                double & out_iyy, double & out_iyz, double & out_izz)
00041   {
00042     double const xx(trans_x * trans_x);
00043     double const yy(trans_y * trans_y);
00044     double const zz(trans_z * trans_z);
00045     
00046     out_ixx = in_ixx + mass * (yy + zz);
00047     out_iyy = in_iyy + mass * (xx + zz);
00048     out_izz = in_izz + mass * (xx + yy);
00049     
00050     out_ixy = in_ixy - mass * (trans_x * trans_y);
00051     out_ixz = in_ixz - mass * (trans_x * trans_z);
00052     out_iyz = in_iyz - mass * (trans_y * trans_z);
00053   }
00054   
00055   
00056   void inertia_parallel_axis_transform(deMatrix3 const & in_inertia,
00057                deVector3 const & translation, double mass,
00058                deMatrix3 & out_inertia)
00059   {
00060     inertia_parallel_axis_transform(in_inertia.elementAt(0, 0),
00061             in_inertia.elementAt(0, 1),
00062             in_inertia.elementAt(0, 2),
00063             in_inertia.elementAt(1, 1),
00064             in_inertia.elementAt(1, 2),
00065             in_inertia.elementAt(2, 2),
00066             translation[0],
00067             translation[1],
00068             translation[2],
00069             mass,
00070             out_inertia.elementAt(0, 0),
00071             out_inertia.elementAt(0, 1),
00072             out_inertia.elementAt(0, 2),
00073             out_inertia.elementAt(1, 1),
00074             out_inertia.elementAt(1, 2),
00075             out_inertia.elementAt(2, 2));
00076   }
00077   
00078   
00079   void inertia_similarity_transform(double in_ixx, double in_ixy, double in_ixz,
00080             double in_iyy, double in_iyz, double in_izz,
00081             deQuaternion const & rotation,
00082             deMatrix3 & out_inertia)
00083   {
00084     out_inertia.set(in_ixx, in_ixy, in_ixz,
00085         in_ixy, in_iyy, in_iyz,
00086         in_ixz, in_iyz, in_izz);
00087     deMatrix3 aa, bb, cc;
00088     aa.set(rotation);
00089     bb.transpose(aa);
00090     cc.multiply(aa, out_inertia); // cc = rotation * inertia
00091     aa.multiply(cc, bb);  // aa = cc * rotation_transpose
00092     //AAARGH no! copy-paste error! out_inertia += aa;
00093     out_inertia = aa;   // could directly to the ops in out_inertia, aa is just tmp
00094   }
00095   
00096   
00097   void inertia_similarity_transform(deMatrix3 const & in_inertia,
00098             deQuaternion const & rotation,
00100             deMatrix3 & out_inertia)
00101   {
00102     inertia_similarity_transform(in_inertia.elementAt(0, 0), in_inertia.elementAt(0, 1), in_inertia.elementAt(0, 2),
00103          in_inertia.elementAt(1, 1), in_inertia.elementAt(1, 2), in_inertia.elementAt(2, 2),
00104          rotation, out_inertia);
00105   }
00106   
00107   
00108   void inertia_similarity_transform(double in_ixx, double in_ixy, double in_ixz,
00109             double in_iyy, double in_iyz, double in_izz,
00110             double rot_qx, double rot_qy, double rot_qz, double rot_qw,
00111             double & out_ixx, double & out_ixy, double & out_ixz,
00112             double & out_iyy, double & out_iyz, double & out_izz)
00113   {
00114     deQuaternion quat;
00115     quat.set(rot_qx, rot_qy, rot_qz, rot_qw);
00116     deMatrix3 out;
00117     inertia_similarity_transform(in_ixx, in_ixy, in_ixz, in_iyy, in_iyz, in_izz, quat, out);
00118     out_ixx = out.elementAt(0, 0);
00119     out_ixy = out.elementAt(0, 1);
00120     out_ixz = out.elementAt(0, 2);
00121     out_iyy = out.elementAt(1, 1);
00122     out_iyz = out.elementAt(1, 2);
00123     out_izz = out.elementAt(2, 2);
00124   }
00125   
00126   
00127   void fuse_mass_properties(double orig_mass, deMatrix3 const & orig_inertia, deFrame const & orig_com,
00128           double adtl_mass, deMatrix3 const & adtl_inertia, deFrame const & adtl_com,
00129           deFrame const & home_of_adtl_wrt_orig,
00130           double & fused_mass, deMatrix3 & fused_inertia, deFrame & fused_com)
00131   {
00139     
00141     // 1. compute new total mass
00142     
00144     fused_mass = orig_mass + adtl_mass;
00148     
00150     // 2. compute new COM
00151     
00153     deFrame adtl_com_wrt_o;
00154     adtl_com_wrt_o.multiply(home_of_adtl_wrt_orig, deFrame(adtl_com));
00155     fused_com.translation() = orig_com.translation();
00156     fused_com.translation() *= orig_mass / fused_mass;
00157     {
00158       deVector3 bar;
00159       bar = adtl_com_wrt_o.translation();
00160       bar *= adtl_mass / fused_mass;
00161       fused_com.translation() += bar;
00162     }
00163     fused_com.rotation() = orig_com.rotation(); // is this even taken into account anywhere?
00167     
00169     // 3. parallel axis transform of original inertia to new COM
00170     
00172     deVector3 orig_to_new_com;
00173     orig_to_new_com = fused_com.translation();
00174     orig_to_new_com -= orig_com.translation();
00175     deMatrix3 orig_inertia_at_new_com; // can probably reuse same instance
00176     inertia_parallel_axis_transform(orig_inertia, orig_to_new_com, orig_mass, orig_inertia_at_new_com);
00180     
00182     // 4. similarity transform of additional inertia to original axes
00183     
00185     deMatrix3 adtl_inertia_at_new_com;
00186     inertia_similarity_transform(adtl_inertia,
00187          home_of_adtl_wrt_orig.rotation(), // XXXX ignore possible adtl com frame rot
00188          adtl_inertia_at_new_com);
00193     
00195     // 5. parallel axis of additional inertia to new COM (delta COM expressed in original frame)
00196     //    ---> translate by the delta of old to new COM_adtl in orig frame
00197     
00199     deVector3 adtl_to_new_com;
00200     adtl_to_new_com = adtl_com_wrt_o.translation();
00201     adtl_to_new_com -= fused_com.translation();
00202     inertia_parallel_axis_transform(adtl_inertia, adtl_to_new_com, adtl_mass, adtl_inertia_at_new_com);
00207     
00209     // 6. add inertias
00210     //    ---> add inertia due to the point masses at the COM?
00211     //    ---> no, masses already taken into account during parallel axis theorem application!
00212     
00214     fused_inertia = orig_inertia_at_new_com;
00215     fused_inertia += adtl_inertia_at_new_com;
00217   }
00218   
00219   
00220   void fuse_mass_properties(deMassProp /*const*/ & original,
00221           deMassProp /*const*/ & additional,
00222           deFrame const & home_of_additional_wrt_original,
00224           deMassProp & fused)
00225   {
00226     deFrame orig_com, adtl_com;
00227     orig_com.translation() = *original.center();
00228     adtl_com.translation() = *additional.center();
00229     double fused_mass;
00230     deMatrix3 fused_inertia;
00231     deFrame fused_com;
00232     fuse_mass_properties(*original.mass(), *original.inertia(), orig_com,
00233        *additional.mass(), *additional.inertia(), adtl_com,
00234        home_of_additional_wrt_original,
00235        fused_mass, fused_inertia, fused_com);
00236     fused.set(&fused_mass, &fused_com.translation(), &fused_inertia);
00237   }
00238   
00239   
00240   void mass_inertia_explicit_form(Model const & model, Matrix & mass_inertia,
00241           std::ostream * dbgos)
00242     throw(std::runtime_error)
00243   {
00244     size_t const ndof(model.getNDOF());
00245     mass_inertia = Matrix::Zero(ndof, ndof);
00246     
00247     if (dbgos) {
00248       *dbgos << "jspace::mass_inertia_explicit_form()\n"
00249        << "  ndof = " << ndof << "\n";
00250     }
00251     
00252     for (size_t ii(0); ii < ndof; ++ii) {
00253       
00254       if (dbgos) {
00255   *dbgos << "  computing contributions of node " << ii << "\n";
00256       }
00257       
00258       taoDNode * node(model.getNode(ii));
00259       if ( ! node) {
00260   ostringstream msg;
00261   msg << "jspace::mass_inertia_explicit_form(): no node for index " << ii;
00262   throw runtime_error(msg.str());
00263       }
00264       
00265       Transform global_com;
00266       Matrix Jacobian;
00267       deVector3 const * com(node->center());
00268       if (com) {
00269   if ( ! model.computeGlobalFrame(node, com->elementAt(0), com->elementAt(1), com->elementAt(2), global_com)) {
00270     ostringstream msg;
00271     msg << "jspace::mass_inertia_explicit_form(): computeGlobalFrame() of COM failed for index " << ii;
00272     throw runtime_error(msg.str());
00273   }
00274       }
00275       else {
00276   // pretend the COM is at the node origin
00277   if ( ! model.getGlobalFrame(node, global_com)) {
00278     ostringstream msg;
00279     msg << "jspace::mass_inertia_explicit_form(): getGlobalFrame() failed for index " << ii;
00280     throw runtime_error(msg.str());
00281   }
00282       }
00283       if ( ! model.computeJacobian(node, global_com.translation(), Jacobian)) {
00284   ostringstream msg;
00285   msg << "jspace::mass_inertia_explicit_form(): computeJacobian() of COM failed for index " << ii;
00286   throw runtime_error(msg.str());
00287       }
00288       
00289       if (dbgos) {
00290   if (com) {
00291     *dbgos << "    local COM: " << *com << "\n";
00292   }
00293   else {
00294     *dbgos << "    local COM: null\n";
00295   }
00296   *dbgos << "    global COM: " << pretty_string(global_com.translation()) << "\n"
00297          << "    Jacobian:\n" << pretty_string(Jacobian, "        ");
00298       }
00299       
00300       if (Jacobian.rows() != 6) {
00301   ostringstream msg;
00302   msg << "jspace::mass_inertia_explicit_form(): Jacobian of node " << ii
00303       << " has " << Jacobian.rows() << " rows instead of 6";
00304   throw runtime_error(msg.str());
00305       }
00306       if (static_cast<size_t>(Jacobian.cols()) != ndof) {
00307   ostringstream msg;
00308   msg << "jspace::mass_inertia_explicit_form(): Jacobian of node " << ii
00309       << " has " << Jacobian.cols() << " columns instead of " << ndof;
00310   throw runtime_error(msg.str());
00311       }
00312       
00313       // A problem that we will be having with TAO's inertia info a
00314       // bit further down is that it contains the contribution from
00315       // the mass, and we have to remove that before proceeding with
00316       // the rotational contribution to the mass-inertia matrix. Thus
00317       // the introduction of Im.
00318       
00319       Eigen::Matrix3d Im(Eigen::Matrix3d::Zero());
00320       deFloat const * mass(node->mass());
00321       if (mass) {
00322   Matrix const mass_contrib(Jacobian.block(0, 0, 3, ndof).transpose() * Jacobian.block(0, 0, 3, ndof));
00323   mass_inertia += *mass * mass_contrib;
00324   if (com) {
00325     double const xx(pow(com->elementAt(0), 2));
00326     double const yy(pow(com->elementAt(1), 2));
00327     double const zz(pow(com->elementAt(2), 2));
00328     double const xy(com->elementAt(0) * com->elementAt(1));
00329     double const xz(com->elementAt(0) * com->elementAt(2));
00330     double const yz(com->elementAt(1) * com->elementAt(2));
00331     Im <<
00332       yy+zz, -xy, -xz,
00333       -xy, zz+xx, -yz,
00334       -xz, -yz, xx+yy;
00335     Im *= *mass;
00336   }
00337   if (dbgos) {
00338     *dbgos << "    mass: " << *mass << "\n"
00339      << "    JvT x Jv:\n" << pretty_string(mass_contrib, "        ")
00340      << "    contribution of mass:\n" << pretty_string(*mass * mass_contrib, "        ")
00341      << "    Im (for subsequent use):\n" << pretty_string(Im, "        ");
00342   }
00343       }
00344       else if (dbgos) {
00345   *dbgos << "    no mass\n";
00346       }
00347       
00348       // I think this is expressed wrt node origin, not COM. But in
00349       // any case, what matters is the instantaneous rotational
00350       // velocity due to joint ii, which is the same throughout the
00351       // entire link. Given that the COM frame is assumed to be
00352       // aligned with the node origin frame anyway, all we have to do
00353       // is express the rotational contribution of the Jacobian in the
00354       // local frame and use that.
00355       
00356       deMatrix3 const * inertia(node->inertia());
00357       if (inertia) {
00358   
00359   // NOTE: global_com.rotation() would require SVD, but we know
00360   // that we are dealing with an affine transform, so taking
00361   // what Eign2 calls the "linear" part is fine, because that's
00362   // simply the 3x3 upper left block of the homogeneous
00363   // transformation matrix. Hopefully anyway.
00364   
00365   // Use the transpose to get the inverse of the rotation.
00366   Matrix const J_omega(global_com.linear().transpose() * Jacobian.block(3, 0, 3, ndof));
00367   
00368   Eigen::Matrix3d Ic;
00369   Ic <<
00370     inertia->elementAt(0, 0), inertia->elementAt(0, 1), inertia->elementAt(0, 2),
00371     inertia->elementAt(1, 0), inertia->elementAt(1, 1), inertia->elementAt(1, 2),
00372     inertia->elementAt(2, 0), inertia->elementAt(2, 1), inertia->elementAt(2, 2);
00373   mass_inertia += J_omega.transpose() * (Ic - Im) * J_omega;
00374   if (dbgos) {
00375     *dbgos << "    Ic:\n" << pretty_string(Ic, "        ")
00376      << "    Ic - Im:\n" << pretty_string(Ic - Im, "        ")
00377      << "    J_omega:\n" << pretty_string(J_omega, "        ")
00378      << "    JoT x (Ic-Im) x Jo:\n"
00379      << pretty_string(J_omega.transpose() * (Ic - Im) * J_omega, "        ");
00380   }
00381       }
00382       else if (dbgos) {
00383   *dbgos << "    no inertia\n";
00384       }
00385       
00386       if (dbgos) {
00387   *dbgos << "  mass_inertia so far:\n" << pretty_string(mass_inertia, "    ");
00388       }
00389     }
00390   }
00391   
00392 }

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