Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

robot/control/kinematics/IKOR.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)2002 David Jung <opensim@pobox.com>
00003 
00004   This program/file is free software; you can redistribute it and/or modify
00005   it under the terms of the GNU General Public License as published by
00006   the Free Software Foundation; either version 2 of the License, or
00007   (at your option) any later version.
00008 
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012   GNU General Public License for more details. (http://www.gnu.org)
00013 
00014   You should have received a copy of the GNU General Public License
00015   along with this program; if not, write to the Free Software
00016   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018   $Id: IKOR.cpp 1080 2004-07-28 19:51:26Z jungd $
00019   $Revision: 1.17 $
00020   $Date: 2004-07-28 15:51:26 -0400 (Wed, 28 Jul 2004) $
00021   $Author: jungd $
00022 
00023 ****************************************************************************/
00024 
00025 #include <robot/control/kinematics/IKOR>
00026 
00027 #include <robot/control/kinematics/solution_error>
00028 #include <robot/control/kinematics/SVDFullSpaceSolver>
00029 #include <robot/control/kinematics/Optimizer>
00030 #include <robot/control/kinematics/ReferenceOpVectorFormObjective>
00031 #include <robot/control/kinematics/AnalyticLagrangianFSBetaOptimizer>
00032 #include <robot/control/kinematics/AnalyticLagrangianNullSpaceBetaOptimizer>
00033 
00034 
00035 using robot::control::kinematics::IKOR;
00036 
00037 using base::Vector;
00038 using base::Orient;
00039 using base::dot;
00040 using robot::control::kinematics::SVDFullSpaceSolver;
00041 using robot::control::kinematics::ReferenceOpVectorFormObjective;
00042 using robot::control::kinematics::BetaFormConstraints;
00043 using robot::control::kinematics::AnalyticLagrangianFSBetaOptimizer;
00044 using robot::control::kinematics::AnalyticLagrangianNullSpaceBetaOptimizer;
00045 
00046 
00047 const Real small = 5.0e-05;
00048 
00049 
00050 IKOR::IKOR(const robot::KinematicChain& chain, const Vector& jointWeights,
00051            bool nonHolonomicPlatformActive, Real platformL)
00052   : chain(chain), weights(jointWeights),
00053     nonHolonomicPlatformActive(nonHolonomicPlatformActive), L(platformL)
00054 {
00055   if (weights.size() == 0) {
00056     weights.reset(Vector(chain.dof()));
00057     for(Int i=0; i<weights.size(); i++) weights[i]=1.0;
00058   }
00059 
00060   solver = ref<SVDFullSpaceSolver>(NewObj SVDFullSpaceSolver());
00061 }
00062 
00063 
00064 
00065 bool IKOR::isConstraintTypeSupported(OptimizationConstraint  optConstraint,
00066                                          OptimizationMethod      optMethod,
00067                                          OptimizationCriterion   optCriterion)
00068 {
00069   if (optMethod==DefaultMethod) optMethod=Lagrangian;
00070   if (optCriterion==DefaultCriterion) optCriterion=LeastNorm;
00071 
00072   if (optMethod == Lagrangian) {
00073 
00074     if (optCriterion == LeastNorm) {
00075 
00076       return (   (optConstraint == JointLimits)
00077               || (optConstraint == ObstacleAvoidance) );
00078 
00079     }
00080 
00081   }
00082 
00083   return false;
00084 }
00085 
00086 
00087 Vector IKOR::solve(const Vector& dx, const Vector& x, const Vector& q,
00088                    const base::Matrix& J,
00089                    OptimizationMethod    optMethod,
00090                    OptimizationCriterion optCriterion,
00091                    OptimizationConstraints optConstraints,
00092                    base::Orient::Representation  orientationRepresentation)
00093 {
00094   if (optMethod==DefaultMethod) optMethod=Lagrangian;
00095   if (optCriterion==DefaultCriterion) optCriterion=LeastNorm;
00096   if (optConstraints.test(DefaultConstraints)) optConstraints.reset();
00097 
00098   const Int N = J.size1(); // rows
00099   const Int M = J.size2(); // cols
00100 
00101   Assert(dx.size() == x.size());
00102   Assert(q.size() == M);
00103 
00104   // First, convert the orientation component of dx (if any) to
00105   //  angular velocity (omega)
00106   Vector newdx;
00107   if (N>3) {
00108     Vector dpos(3);
00109     dpos = vectorRange(dx,Range(0,3));
00110     Orient orient(vectorRange(x,Range(3,x.size())),orientationRepresentation);
00111     Orient dorient(vectorRange(dx,Range(3,dx.size())),orientationRepresentation);
00112     Vector omega(3);
00113     omega = orient.getBinv()*Vector(dorient); // w = B(o)^-1 . do/dt
00114     newdx.resize(6);
00115     vectorRange(newdx,Range(0,3)) = dpos;
00116     vectorRange(newdx,Range(3,6)) = omega;
00117   }
00118   else
00119     newdx.reset(dx);
00120 
00121   Assert(newdx.size() == N);
00122 
00123   bool nullSpaceMotion = base::equals(dx,zeroVector(N),small);
00124 
00125 
00126   // First use the FullSpaceSolver to obtain the g Vectors
00127   array<Int> dependentRowsEliminated;
00128   const Vector& b(newdx);
00129   Matrix gs;
00130   try {
00131     gs = solver->solve(J, b, dependentRowsEliminated); // gis are the columns Vectors of gs
00132   } catch (std::exception& e) {
00133     throw solution_error(Exception(String("Unable to generate solution space for the given dx in the current state (impossible motion?) - FSP failed:")+e.what()));
00134   }
00135 
00136   // Which concrete Optimizer and corresponding Objective and Constraints
00137   //  get instantiated, depends on the opt<X> parameters
00138   ref<Optimizer>              optimizer;
00139   ref<Optimizer::Objective>   objective;
00140   ref<Optimizer::Constraints> constraints;
00141 
00142   ref<LagrangianOptimizer> lagOptimizer;
00143 
00144 
00145   //
00146   // First we run the optimization with only constraints
00147   //  due to dependent rows (& the platform non-holonomic constraint if appropriate)
00148   // Next, we test for violation of any other constrant types
00149   //  such as joint limit or obstacle constraints.
00150   //  If any constraints were violated we add them in and run the optimization
00151   //  again.  This is repeated until all constraints are satisfied.
00152 
00153 
00154   if (optMethod==Lagrangian) {
00155 
00156     // First setup the objective function
00157     //  - the objective is defined in terms of the Matrix B and Vector dZr
00158     Matrix B(M,M);
00159     Vector dZr(M);
00160 
00161     switch (optCriterion) {
00162     case LeastNorm: weightedLeastNorm(B,dZr, weights); break;
00163     case LeastFlow: throw std::runtime_error(Exception("LeastFlow criteria not implemented for Larganrian method."));
00164     default:
00165       throw std::invalid_argument(Exception("unsupported/unknown criteria for Largangian Method"));
00166     }
00167 
00168     ref<ReferenceOpVectorFormObjective> rovfObjective(NewObj ReferenceOpVectorFormObjective());
00169     objective = rovfObjective;
00170     rovfObjective->setB(B);
00171     rovfObjective->setdZr(dZr);
00172 
00173 
00174     // setup constraints for first optimization step
00175     ref<BetaFormConstraints> betaconstraints(NewObj BetaFormConstraints());
00176     constraints = betaconstraints;
00177     betaconstraints->clear();
00178 
00179     if (nonHolonomicPlatformActive)
00180       addNonholonomicConstraint(gs, q, betaconstraints);
00181 
00182 
00183     if (dependentRowsEliminated.size() > 0)
00184       addDependentRowConstraints(dependentRowsEliminated, gs, J, b, betaconstraints);
00185 
00186 
00187     // create the Optimizer
00188     if (!nullSpaceMotion)
00189       lagOptimizer = ref<AnalyticLagrangianFSBetaOptimizer>(NewObj AnalyticLagrangianFSBetaOptimizer());
00190     else
00191       lagOptimizer = ref<AnalyticLagrangianNullSpaceBetaOptimizer>(NewObj AnalyticLagrangianNullSpaceBetaOptimizer());
00192     lagOptimizer->setGs(gs);
00193     optimizer = lagOptimizer;
00194 
00195   }
00196   else
00197     throw std::invalid_argument(Exception("unimplemented optimization method"));
00198 
00199 
00200   // Now use the optimizer, objective and constraints to narrow the solution
00201   //  space down to a single dq Vector
00202   Vector dq(M);
00203 
00204   try {
00205 //Debugln(DJ,"++++++++++++ optimizing1");
00206     dq = optimizer->optimize(objective, constraints);
00207 
00208   } catch (solution_error& e) {
00209     dq = zeroVector(M);
00210     throw solution_error(Exception(String("Unable to solve for the given dx in the current state - optimization failed:")+e.what()),e);
00211   }
00212 
00213 //!!! this loop will probably cause some constraints to be added in more than once - FIX
00214 
00215   // Now loop adding extra constraints for any constraint types that are violated,
00216   //  until all are satisfied.
00217   Int numConstraints;
00218   do {
00219     numConstraints = constraints->numConstraints(); // initial no. of constraints
00220 
00221 
00222 
00223     // check for other constraint violations and add them
00224     if (optMethod == Lagrangian) {
00225 
00226       ref<BetaFormConstraints> betaconstraints(narrow_ref<BetaFormConstraints>(constraints));
00227 
00228       // add constraint that are always active
00229       betaconstraints->clear();
00230       if (nonHolonomicPlatformActive)
00231         addNonholonomicConstraint(gs, q, betaconstraints);
00232       if (dependentRowsEliminated.size() > 0)
00233         addDependentRowConstraints(dependentRowsEliminated, gs, J, b, betaconstraints);
00234 
00235       // add constraints for any other constraint types that
00236       // are violated
00237       if (optConstraints.test(JointLimits)) {
00238         addJointLimitConstraints(gs, q, dq, betaconstraints);
00239       }
00240 
00241       if (optConstraints.test(ObstacleAvoidance))
00242         addObstacleAvoidanceConstraints(gs, q, dq, betaconstraints);
00243 
00244     }
00245     else
00246       throw std::invalid_argument(Exception("unimplemented optimization method"));
00247 
00248 
00249     if ( constraints->numConstraints() > numConstraints) {
00250 
00251       // more were added, run the optimizer again
00252       try {
00253 Debugcln(DJ,"optimizing2:");
00254 Debugcln(DJ,"constraints:\n" << *constraints);
00255 Debugcln(DJ," extra constraints:" << (constraints->numConstraints()-numConstraints));
00256         dq = optimizer->optimize(objective, constraints);
00257 
00258       } catch (solution_error& e) {
00259         dq = zeroVector(M);
00260         throw solution_error(Exception(String("Unable to solve for the given dx in the current state - optimization failed:")+e.what()),e);
00261       }
00262 
00263     }
00264 
00265 
00266   } while ( constraints->numConstraints() > numConstraints); // keep repeating as long as more were added
00267 //Debugcln(DJ,"opt done dq=" << dq);
00268 
00269   return dq;
00270 }
00271 
00272 
00273 void IKOR::setProximitySensorData(const array<LinkProximityData>& proximityData, Real d)
00274 {
00275   proximitySensorData = proximityData;
00276   this->d = d;
00277 }
00278 
00279 
00280 
00281 void IKOR::setParameter(const String& name, Real value)
00282 {
00283   throw std::invalid_argument(Exception("unknown parameter name"));
00284 }
00285 
00286 
00287 
00288 void IKOR::weightedLeastNorm(Matrix& B, Vector& dZr, Vector weights)
00289 {
00290   // for least norm criteria, B is the identity & dZr=0
00291   dZr = zeroVector(dZr.size());
00292 
00293   // set B to identity
00294   for(Int j=0; j<B.size2(); j++)
00295     for(Int i=0; i<B.size1(); i++)
00296       B(i,j) = (i!=j)?0:weights[i];
00297 }
00298 
00299 
00300 IKOR::RankLossBetaConstraint::RankLossBetaConstraint(Int row, const Matrix& A, const Vector& b, const Matrix& gs)
00301   : row(row)
00302 {
00303   setName(className());
00304 
00305   const Int span=gs.size2();
00306   beta.resize(span);
00307   Vector Ar(matrixRow(A,row));
00308 
00309   for(Int i=0; i<span; i++)
00310     beta[i] = dot(Ar, matrixRow(gs,i) ) / b[row];
00311 
00312 }
00313 
00314 
00315 void IKOR::addDependentRowConstraints(const array<Int>& rows, const Matrix& gs, const Matrix& A, const Vector& b, ref<BetaFormConstraints> constraints)
00316 {
00317   // add one beta per row
00318   for(Int i=0; i<rows.size(); i++)
00319     constraints->addConstraint(ref<BetaFormConstraints::BetaFormConstraint>(NewObj RankLossBetaConstraint(rows[i], A, b, gs)) );
00320 }
00321 
00322 
00323 
00324 
00325 void IKOR::addJointLimitConstraints(const Matrix& gs, const Vector& q, const Vector& dq, ref<BetaFormConstraints> constraints)
00326 {
00327   // iterate through the joints and check if they will be moved out of limits by dq.
00328   //  If so, create a constraint
00329   const Int M = gs.size1();
00330   Vector tq(q+dq); // target q
00331 
00332   for(Int v=0; v<M; v++) { // for each variable/component of q
00333 
00334     // check if there are limits
00335     Real minValue = chain.variableMinLimit(v);
00336     Real maxValue = chain.variableMaxLimit(v);
00337     bool hasMinLimit = true;
00338     bool hasMaxLimit = true;
00339     if (chain.variableUnitType(v)==KinematicChain::Angle) { // angle
00340       if (  (Math::equals(minValue, KinematicChain::unboundedMinAngleLimit))
00341           ||(Math::equals(maxValue, KinematicChain::unboundedMaxAngleLimit)) ) {
00342         hasMinLimit = hasMaxLimit = false;
00343       }
00344     }
00345     else { // distance
00346       if (Math::equals(minValue, KinematicChain::unboundedMinDistLimit,999))
00347         hasMinLimit = false;
00348       if (Math::equals(maxValue, KinematicChain::unboundedMaxDistLimit,999))
00349         hasMaxLimit = false;
00350     }
00351 
00352 
00353     if (hasMinLimit && (tq[v] < minValue)) {
00354       Debugcln(IKOR,"v:" << v << " m" << Math::radToDeg(minValue) << " > " << Math::radToDeg(tq[v]) );
00355       constraints->addConstraint(
00356          ref<BetaFormConstraints::BetaFormConstraint>(NewObj JointLimitBetaConstraint(v,minValue-tq[v],gs))
00357        );
00358     }
00359 
00360     if (hasMaxLimit && (tq[v] > maxValue)) {
00361       Debugcln(IKOR,"v:" << v << " m" << Math::radToDeg(maxValue) << " < " << Math::radToDeg(tq[v]) );
00362       constraints->addConstraint(
00363         ref<BetaFormConstraints::BetaFormConstraint>(NewObj JointLimitBetaConstraint(v,maxValue-tq[v],gs))
00364       );
00365     }
00366 
00367   }
00368   //Debugcln(IKOR,"");
00369 }
00370 
00371 
00372 
00373 void IKOR::addNonholonomicConstraint(const Matrix& gs, const Vector& q, ref<BetaFormConstraints> constraints)
00374 {
00375   // assume the platform parameters are the first three components of q, namely xp, yp, and thetap
00376   //  (i.e. 2D platform) - (in the paper they are the last three)
00377 
00378   Vector alpha(gs.size2());
00379 
00380   Real sqm = Math::sin(q[2]);
00381   Real cqm = Math::cos(q[2]);
00382   for(Int i=0; i<gs.size2(); i++) {
00383     Vector gi( matrixColumn(gs,i) );
00384     alpha[i] = -sqm*gi[0] + cqm*gi[1] - L*gi[2];
00385   }
00386 //Debugln(DJ,"alpha=" << alpha);
00387   constraints->setAlphaConstraint(alpha);
00388 }
00389 
00390 
00391 
00392 
00393 IKOR::PushAwayBetaConstraint::PushAwayBetaConstraint(Int v, Real Xvx,
00394                                                      const Vector& n, Real L,
00395                                                      const Matrix& gs,
00396                                                      const robot::KinematicChain& chain, const Vector& q)
00397   : v(v), Xvx(Xvx), n(n), L(L)
00398 {
00399   setName(className());
00400 
00401   // see "Resolving Kinematic Redundancy with Constraints Using the FSP (Full Space Parameterization) Approach,
00402   //   Pin, Tulloch et.al. for a description of the obstacle constraint formulation
00403 
00404   // compute JXq, the Jacobian of the chain truncated at the push-away point, evaluated at q
00405   Int l = chain.linkIndexOfVariable(v);
00406   KinematicChain chainX; // truncated chain
00407   for(Int li=0; li<l-1; li++) // copy links before l
00408     chainX += chain[li];
00409 
00410   // now add shortened l link
00411 //Not sure how to achieve this. For now assume that the link is DH type and that d is 0, then
00412 //just shorten a
00413   KinematicChain::Link ll(chain[l]);
00414   Assertm(ll.isDHType() && Math::equals(ll.getD(),0), "is DH link & d==0 (current impl. can't handle general case)");
00415   ll.setA(Xvx);
00416   chainX += ll;
00417 
00418   Vector ql( chainX.dof() );
00419   ql = vectorRange( q, Range(0,ql.size()  ) );
00420 
00421 //!!! debug -- dump 'push-away' point
00422 //Matrix t( chainX.getForwardKinematics(ql) );
00423 //Debugln(DJ,"push away point (v=" << v << ":l=" << l << ")=" << matrixColumn(t,3) << " n=" << n);
00424 //!!!
00425 
00426 
00427   Matrix JXq( chainX.getJacobian( ql, false ) );
00428 //Debugln(DJ,"v=" << v << " l=" << l << "chainX.size()=" << chainX.size() << " chainX.dof()=" << chainX.dof());
00429   const Int span=gs.size2();
00430   Int m( ql.size() ); // name used in paper
00431   beta.resize(span);
00432 //Debugln(DJ,"\nJX(q).q=" << JXq*ql);
00433   // now evaluate beta (refer to paper for variable meanings)
00434   for(Int k=0; k<span; k++) {
00435     beta[k] = 0;
00436     Vector gk( matrixColumn(gs, k) );
00437     for(Int i=0; i<3; i++) {
00438       for(Int j=0; j<m; j++) {
00439 //      Debugln(DJ,"k=" << k << " beta.size()=" << beta.size() << " span=" << span);
00440 //      Debugcln(DJ,"i=" << i << " j=" << j << " JXq.size1()=" << JXq.size1() << " JXq.size2()=" << JXq.size2()
00441 //      << " gk.size()=" << gk.size() << " n.size()=" << n.size());
00442         beta[k] += JXq(i,j) * gk[j] * n[i];
00443 
00444       }
00445     }
00446     beta[k] /= L;
00447   }
00448 
00449 }
00450 
00451 
00452 
00453 
00454 void IKOR::addObstacleAvoidanceConstraints(const Matrix& gs, const Vector& q, const Vector& dq, ref<BetaFormConstraints> constraints)
00455 {
00456   if (proximitySensorData.size() == 0) return; // no proximity sensor data provided
00457 
00458   // if any data provided, must be data for each dof
00459   Assert( proximitySensorData.size() == chain.dof() );
00460 
00461   // check for obstacle proximity to each 'link'
00462   for(Int v=0; v<chain.dof(); v++) { // for each variable (i.e. element of q)
00463 
00464     LinkProximityData& pd( proximitySensorData[v] );
00465 
00466     if (pd.distance < d) {
00467       if (!pd.direction.equals( zeroVector(3) )) { // ignore if direction is unknown
00468         // something is within the proximity sensor range of this 'link' & within the 'danger' envelope
00469         Real L = d - pd.distance + 1e-4; // desired 'push-away' distance
00470         Vector n( -pd.direction ); // normal - direction to push point Xj - point closest to obstacle
00471 
00472         // Before adding the constraint, we need to check for a special case where the end of the link
00473         //  is the closest to the object.  In this case, the end of the previous link (which is
00474         //  conincident) may have already created an identical constraint (if it is revolute, the
00475         //  closest point will be at the joint).  In this case, we don't need a second identical constraint.
00476         if ( Math::equals(pd.intercept,0) && (chain.linkIndexOfVariable(v) > 0) ) {
00477           if ( chain[chain.linkIndexOfVariable(v)].type() == KinematicChain::Link::Revolute )
00478             return; // skip this constraint
00479         }
00480 
00481 Debugln(DJ,"proximity: variable " << v << " (l=" << chain.linkIndexOfVariable(v) << "):\nL=" << L << " n=" << n << " lXj=" << pd.intercept << " d=" << d << " dist=" << pd.distance);
00482         constraints->addConstraint(ref<BetaFormConstraints::BetaFormConstraint>(NewObj PushAwayBetaConstraint(v,pd.intercept, n, L, gs, chain, q)) );
00483       }
00484 
00485     }
00486 
00487   }
00488 
00489 }
00490 
00491 

Generated on Thu Jul 29 15:56:32 2004 for OpenSim by doxygen 1.3.6