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

robot/sim/IKORTester.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)2003 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: IKORTester.cpp 1033 2004-02-11 20:47:52Z jungd $
00019   $Revision: 1.14 $
00020   $Date: 2004-02-11 15:47:52 -0500 (Wed, 11 Feb 2004) $
00021   $Author: jungd $
00022  
00023 ****************************************************************************/
00024 
00025 #include <robot/sim/IKORTester>
00026 
00027 #include <base/Externalizer>
00028 #include <base/externalization_error>
00029 #include <base/Expression>
00030 #include <base/Matrix>
00031 #include <base/VDirectory>
00032 #include <base/VFile>
00033 
00034 #include <robot/AggregateControlInterface>
00035 #include <robot/sim/BasicEnvironment>
00036 #include <robot/control/kinematics/solution_error>
00037 #include <robot/control/kinematics/Optimizer>
00038 #include <robot/control/kinematics/ReferenceOpVectorFormObjective>
00039 #include <robot/control/kinematics/AnalyticLagrangianFSBetaOptimizer>
00040 #include <robot/control/kinematics/MPPseudoInvSolver>
00041 #include <robot/control/kinematics/LeastNormIKSolver>
00042 #include <robot/control/kinematics/FullSpaceSolver>
00043 #include <robot/control/kinematics/IKOR>
00044 
00045 
00046 using robot::sim::IKORTester;
00047 
00048 using base::Vector;
00049 using base::Orient;
00050 using base::Time;
00051 using base::PathName;
00052 using base::VFile;
00053 using base::VDirectory;
00054 using base::ExpressionMatrix;
00055 using base::externalization_error;
00056 using base::Path;
00057 using base::Trajectory;
00058 using robot::AggregateControlInterface;
00059 using robot::sim::BasicEnvironment;
00060 using robot::sim::IKORTest;
00061 using robot::control::kinematics::FullSpaceSolver;
00062 using robot::control::kinematics::ReferenceOpVectorFormObjective;
00063 using robot::control::kinematics::BetaFormConstraints;
00064 using robot::control::kinematics::AnalyticLagrangianFSBetaOptimizer;
00065 using robot::control::kinematics::InverseKinematicsSolver;
00066 using robot::control::kinematics::MPPseudoInvSolver;
00067 using robot::control::kinematics::LeastNormIKSolver;
00068 using robot::control::kinematics::FullSpaceSolver;
00069 using robot::control::kinematics::IKOR;
00070 
00071 
00072 
00073 
00074 
00075 IKORTester::IKORTester(ref<base::VFileSystem> fs, ref<base::Cache> cache)
00076   : filesystem(fs), cache(cache)
00077 {
00078 }
00079 
00080 
00081 void IKORTester::executeTests(ref<IKORTest> itest, bool saveResults, base::PathName alternateOutputFileName)
00082 {
00083   ref<SimulatedRobot> robot( narrow_ref<SimulatedRobot>(itest->getRobot()) );
00084   Int testManipulatorIndex = itest->getManipulatorIndex();
00085   Consoleln("Testing robot:" << robot->getRobotDescription()->getName() << " with manipulator:" 
00086                              << robot->getRobotDescription()->manipulators()[testManipulatorIndex]->getName());
00087 
00088   // duplicate the environment, so we can restore the environment to its initial state before saving
00089   ref<SimulatedBasicEnvironment> env( narrow_ref<SimulatedBasicEnvironment>(itest->getEnvironment()) );
00090   ref<SimulatedBasicEnvironment> initialenv( &base::clone(*env) );
00091   
00092   env->preSimulate();
00093   Time simTime(0);
00094   for(Int i=0; i<itest->numTests(); i++) {
00095     IKORTest::Test& test( itest->getTest(i) );
00096     Consoleln("Executing test: " << test.getName());
00097     simTime = executeTest(test, simTime, env, robot, testManipulatorIndex);
00098   }
00099   
00100   if (saveResults) {
00101     itest->setEnvironment(initialenv);
00102     itest->saveResults(true,alternateOutputFileName);
00103   }
00104 }
00105 
00106   
00107 
00108 
00109 
00110 
00111 
00112 
00113 base::Time IKORTester::executeTest(IKORTest::Test& test, Time simTime, ref<SimulatedBasicEnvironment> env, 
00114                                    ref<SimulatedRobot> robot, Int testManipulatorIndex)
00115 {
00116   // setup
00117   ref<const RobotDescription> robotDescr(robot->getRobotDescription());
00118   ref<const ManipulatorDescription> manipDescr(robotDescr->manipulators()[testManipulatorIndex]); 
00119 
00120   // get the kinematic chain for the robot and its first manipulator
00121   Matrix4 platformTransform( robot->getOrientation().getRotationMatrix3() );
00122   platformTransform.setTranslationComponent( robot->getPosition() );
00123   KinematicChain platformChain( robotDescr->platform()->getKinematicChain(3,platformTransform) ); // could have 0 dof
00124   KinematicChain robotChain(robotDescr->getKinematicChain(3,platformTransform,testManipulatorIndex,0));
00125   KinematicChain manipChain(manipDescr->getKinematicChain());
00126   
00127   // get control interfaces
00128   String manipIndexStr( base::intToString(testManipulatorIndex+1) );
00129   ref<ControlInterface> platformControl(robot->getControlInterface("platformPosition"));
00130   ref<ControlInterface> manipControl(robot->getControlInterface(String("manipulatorPosition")+manipIndexStr));
00131   ref<ControlInterface> toolControl(robot->getControlInterface(String("toolPosition")+manipIndexStr));
00132   ref<ControlInterface> toolGripControl(robot->getControlInterface(String("manipulatorToolGrip")+manipIndexStr));
00133 
00134   // put interfaces into an array in kinematic order (for aggregation)
00135   array<ref<ControlInterface> > interfaces;
00136   interfaces.push_back(platformControl);
00137   interfaces.push_back(manipControl);
00138 
00139   KinematicChain manipToolChain(manipChain); // combined manip & tool chain (==manipChain if no tool)
00140   KinematicChain toolChain;
00141   if (test.toolAttached) {
00142     // locate tool
00143     test.toolAttached = false;
00144     for(Int i=0; (i<env->numTools()) && !test.toolAttached; i++) {
00145       ref<const ToolDescription> td( env->getTool(i)->getToolDescription() );
00146 
00147       if (td->getName() == test.toolName) {
00148         test.toolAttached = true;
00149         env->placeToolInProximity( env->getTool(i), robot, 0);
00150         toolGripControl->setOutput(0,1); // tell manipulator to grasp tool
00151         toolChain = td->getKinematicChain();
00152 //!!! hack
00153 toolControl->setOutputs( zeroVector(toolChain.dof()) );
00154 Logln("debug: setting tool outputs to 0");
00155 //!!!
00156       }
00157     }
00158     if (!test.toolAttached) 
00159       throw std::logic_error(Exception(String("the specified tool '")+test.toolName+"' doesn't exist in the environment"));
00160     
00161     manipToolChain += toolChain;
00162   }
00163 
00164 
00165   
00166   KinematicChain chain(robotChain); // kinematic chain from world frame to tool end-effector
00167   if (test.toolAttached) {
00168     chain += toolChain;
00169     interfaces.push_back(toolControl);
00170   }
00171 
00172 
00173   // ControlInterace for all dofs: platform, manipulator & tool
00174   //  (I/O vector components match dofs of chain)
00175   ref<ControlInterface> control(NewObj AggregateControlInterface("robotPosition","RobotPositionControl", interfaces));
00176   
00177 
00178   Vector q( zeroVector(chain.dof()) );
00179 
00180   // put manipulator into initial configuration (if needed)
00181   if (test.initialConfigSpecified) {
00182 
00183     if (test.initq.size() != chain.dof())
00184       throw std::logic_error(Exception(String("the initial configuration specified has ")
00185                                        + base::intToString(test.initq.size())
00186                                        + " components, but the platform, manipulator & tool (if any) has "
00187                                        + base::intToString(chain.dof())+" d.o.f"));
00188 
00189     q = test.initq;
00190 
00191     // convert components of q corresponding to revolute joints to radians 
00192     for(Int v=0; v<chain.dof(); v++)
00193       if (chain.variableUnitType(v) == KinematicChain::Angle)
00194         q[v] = Math::degToRad(q[v]);
00195     
00196     Assertm(chain.dof() == control->outputSize(),"KinematicChain dof == aggregated ControlInterface output vector size");
00197     control->setOutputs(q);
00198   }
00199   else  // get config from robot
00200     q.reset( control->getInputs() );
00201 
00202 
00203 
00204   Trajectory traj(test.traj);
00205 
00206   // if units were specified for the trajectory, and they are not "meters", then
00207   //  convert to meters
00208   if (traj.getUnits() != "") {
00209 
00210     if (traj.getUnits() == "inches")
00211       traj.scalePosition(consts::metersPerInch);
00212     else
00213       if (traj.getUnits() != "meters") {
00214         Logln("Warning: unknown units specified for trajectory: '" << traj.getUnits() << "' - no conversion performed.");
00215       }
00216   }
00217 
00218   
00219   // all test are performed in the WorldFrame, so convert the trajectory to world frame coords.
00220   if (test.frame != Robot::WorldFrame) {
00221 
00222     // make this more elegant later!!!
00223     //!!! I think this may be broken - overhaul it! !!!
00224 Logln("warning: trajectory/path transformation to world frame not well tested");
00225     Vector mtq( manipToolChain.dof() );
00226     mtq = vectorRange(q, Range(platformChain.dof(),q.size()) );
00227 
00228     Matrix T(manipToolChain.getForwardKinematics(mtq)); // manip|tool ee -> manip mount frame transform
00229 
00230     Matrix4 toWorldTransform( robot->coordFrameTransform(test.frame, Robot::WorldFrame, 0, base::toMatrix4(T),
00231                                                          robot->getPosition(),
00232                                                          robot->getOrientation()) );
00233                                                          
00234                                                          
00235     traj.transform( toWorldTransform );
00236   }
00237 
00238 
00239 
00240   // if an overriding time interval was specified, replace the current trajectory time
00241   //  information with it
00242   Real stepsize = 0.01; // default
00243   if (test.timeIntervalSpecified) {
00244     // achieved by converting the trajectory to a Path (hence loosing the time information)
00245     //  and then back to a trajectory with the information specified from the interval
00246 Logln("warning: trajectory/path resamping not well tested");
00247     Path path(traj);
00248     Real start = test.timeInterval[0];
00249     Real end = test.timeInterval[1];
00250     Real duration = end-start;
00251 
00252     traj = Trajectory(path);
00253     traj.scaleTime(duration);
00254     traj.shiftTime(start);
00255 
00256     if (test.timeInterval[2] > 0) { // i.e. stepsize given
00257       stepsize = test.timeInterval[2];
00258       traj.resample( Int(duration/stepsize) );
00259     }
00260 //!!! this may break things too!! !!!
00261   }
00262 
00263   
00264   // if a limit on the maximum distance between any two trajectory points was specified, resample
00265   //  so that the inter-point distances are smaller than the maxdx specified
00266     if (test.maxdxSpecified)  // limit |dx|
00267       traj.resample(test.maxdx);
00268 
00269     
00270 
00271   //
00272   // finally, iterate over the trajectory, calculating dxs and using inverse-kinematics
00273   //  to solve for dqs
00274 
00275   ref<InverseKinematicsSolver>     ikSolver;
00276   if (test.solutionMethod == IKORTest::Test::PseudoInverse) {
00277     ikSolver = ref<InverseKinematicsSolver>(NewObj LeastNormIKSolver());
00278     if (test.jointWeightsSpecified) {
00279       Logln("Warning: joint parameter weights are ignored for the PseudoInverse solution method");
00280     }
00281   }
00282   else if (test.solutionMethod == IKORTest::Test::FullSpace) {
00283     bool nonHolonomicPlatformActive = (robotDescr->platform()->isMobile() && !robotDescr->platform()->isHolonomic());
00284     
00285     // get joint weights
00286     Vector jointWeights(test.jointWeightsSpecified? test.jointWeights : Vector() );
00287     if (jointWeights.size() == 0) { // not specified, set to (1,1,..,1)
00288       jointWeights.reset( Vector(chain.dof()) );
00289       for(Int i=0; i<jointWeights.size(); i++) jointWeights[i] = 1.0;
00290     }
00291     else
00292       if (jointWeights.size() != chain.dof())
00293         throw std::logic_error(Exception(String("the specified joint weight vector has ")
00294                                        + base::intToString(jointWeights.size())
00295                                        + " components, but the platform, manipulator & tool (if any) has "
00296                                        + base::intToString(chain.dof())+" d.o.f"));
00297       
00298     Real L = robotDescr->platform()->L();
00299     ikSolver = ref<InverseKinematicsSolver>(NewObj IKOR(chain, jointWeights, nonHolonomicPlatformActive, L));
00300   }
00301   Assert(ikSolver);
00302 
00303 
00304   // check obstacle avoidance is supported, if requested & get manip proximity sensor interface
00305   ref<ControlInterface> proximitySensorInterface;
00306   
00307   if (test.optConstraints.test(IKOR::ObstacleAvoidance)) {
00308     if (ikSolver->isConstraintTypeSupported(IKOR::ObstacleAvoidance, test.optMethod, test.optCriteria))
00309       proximitySensorInterface = robot->getControlInterface(String("manipulatorProximity")+manipIndexStr);
00310     else
00311       throw std::logic_error(Exception("obstacle avoidance constraints were requested, but the IK solver doesn't support them with the specificed optimizatio method and criteria"));
00312   }
00313   
00314 
00315   
00316   // clear arrays to hold the resulting platform/manipulator/tool trajectory
00317   test.qs.clear();
00318   test.xs.clear();
00319   test.dxs.clear();
00320   test.dqs.clear();
00321   test.times.clear();
00322   test.Js.clear();
00323   
00324   if (simTime.equals(0)) // advance initial simTime if necessary
00325     if (simTime < traj.time(0))
00326       simTime = traj.time(0);
00327 
00328   if (simTime > traj.time(0))
00329     throw std::logic_error(Exception("simulation time at start of trajectory is already greater than the specified start-time of the trajectory"));
00330 
00331 
00332   // get initial q and calculate x 
00333   q = control->getInputs();
00334   Vector x(calcEEVector(test,chain,q));
00335   test.qs.push_back(q);
00336   test.xs.push_back(x);
00337   test.times.push_back(simTime);
00338   test.Js.push_back( chain.getJacobian(q, test.orientationControl) );
00339 
00340   Logln("initial q=" << q << "  x=" << x);
00341 
00342   
00343   // if the initial ee position is coincident with the first trajectory point *and* the
00344   //  initial time is identical to the current simTime, skip the first trajectory point
00345   // However, if the positions are not coincident, but the times are - it is an error
00346   //  (cannot move intantaneously).  If the positions are coincident but the times are
00347   //  not, the initial dx will be 0.
00348 
00349   bool coincident=false; // position[/orientation] coincident?
00350   // check if the initial ee is co-incident with the beginning of the trajectory (to within tol)
00351   const Real tol = 0.01;
00352   Vector3 pos( base::toVector3(vectorRange(x,Range(0,3))) );
00353   Orient orient;
00354   if (!test.orientationControl) {
00355     if (pos.equals( traj.position(traj.time(0)),tol ))
00356       coincident=true;
00357   }
00358   else {
00359     orient = Orient( vectorRange(x,Range(3,6)), Orient::EulerRPY );
00360     if ( pos.equals( traj.position(traj.time(0)),tol ) && orient.equals( traj.orientation(traj.time(0)),tol ) )
00361       coincident=true;
00362   }
00363 
00364   
00365   if (!coincident) {
00366     if (traj.time(0) == simTime) {
00367       String posorient(base::toString(traj.position(0)));
00368       String eeposorient(base::toString(pos));
00369       if (test.orientationControl) {
00370         Orient trajo( traj.orientation(0) ); trajo.changeRepresentation(Orient::EulerRPY);
00371         Orient eeo(orient); eeo.changeRepresentation(trajo.representation());
00372         posorient += String("/")+base::toString( trajo );
00373         eeposorient += String("/")+base::toString( eeo );
00374       }
00375       throw std::logic_error(Exception(String("the initial trajectory time is equal to the initial simulation time, but initial trajectory position[/orientation] ")
00376                 +posorient+
00377                 +"(WorldFrame) is not coincident with the initial end-effector "
00378                 +eeposorient+
00379                 +"(WorldFrame) to within tolerance.  Can't move the end-effector instantaneously!"));
00380     }
00381   }
00382     
00383 
00384 
00385   // loop over traj
00386   bool abort=false;
00387   Int i=coincident?1:0;
00388   for(; (i<traj.numDistinguishedValues()) && (!abort); i++) { 
00389 
00390     Real s = traj.distinguishedValue(i);
00391     Time t = traj.time(s);
00392 
00393     // get trajectory pos/orient at time t and convert to Vector targetx
00394     Point3 targetPos( traj.position(t) );
00395     Orient targetOrient( test.orientationControl?traj.orientation(t):Orient() );
00396     Vector targetx(test.orientationControl?6:3);
00397     vectorRange(targetx, Range(0,3)) = base::fromVector3(targetPos);
00398     if (test.orientationControl) 
00399       vectorRange(targetx, Range(3,6)) = targetOrient.getVector(Orient::EulerRPY);
00400     Vector dx( targetx - x );
00401 //Debugln(DJ,"\nx=" << x << "\ntargetx=" << targetx << "\ndx=" << dx);
00402     test.dxs.push_back(dx);
00403     
00404     Vector dq( chain.dof() );
00405 
00406     
00407     if (test.optConstraints.test(IKOR::ObstacleAvoidance)) {
00408       // read proximity sensor data from manipulator and give it to the solver so that
00409       //  obstacle constraints can be imposed as appropriate
00410       // (note that the interface provides prox data by link of the manipulator, but the
00411       //  solver expects prox data by dof variable for the whole chain (platform & manip) )
00412       array<InverseKinematicsSolver::LinkProximityData> proxData( chain.dof() ); // elements initialize to no-proximity
00413       
00414       // indices correspond to links (including base), not parameters (some links may have 0-dof)
00415       Vector proximityInputs( proximitySensorInterface->getInputs() );
00416 
00417       // fill in proxData from proximityInputs
00418       //  interface only provides prox data for manip[/tool], so skip over platform dofs (if any)
00419       for(Int v=0; v < chain.dof(); v++) { // for each platf/manip[/tool] parameter variable
00420 
00421         if (v >= platformChain.dof()+1) { // only handling manip link prox sensors (excluding base)
00422         
00423           Int l = manipToolChain.linkIndexOfVariable(v - platformChain.dof()); // manip[/tool] link index
00424           InverseKinematicsSolver::LinkProximityData& pd(proxData[v]);
00425           
00426           pd.distance =    proximityInputs[l*5]; // see SimulatedRobot for interface input vector description
00427           pd.direction.resize(3);
00428           pd.direction[0] = proximityInputs[l*5+1];
00429           pd.direction[1] = proximityInputs[l*5+2];
00430           pd.direction[2] = proximityInputs[l*5+3];
00431           pd.intercept   = proximityInputs[l*5+4];
00432         }
00433       }
00434       
00435       Real d = 0.3;
00436       ikSolver->setProximitySensorData(proxData, d);
00437       
00438     }
00439     
00440 //Debugln(DJ,"t:" << simTime.seconds() << "  q:" << q << "  x:" << x << "  dx:" << dx );
00441     // attempt IK
00442     Matrix J;
00443     try {
00444       
00445       J = chain.getJacobian(q, test.orientationControl);
00446       dq = ikSolver->solve(dx, x, q, J, test.optMethod, test.optCriteria, test.optConstraints, Orient::EulerRPY);
00447       
00448 //!!! hack for now to limit |dq|
00449 Debugln(DJ,"|dq|=" << dq.magnitude());
00450 if (dq.magnitude() > 0.2) {
00451   dq /= ( dq.magnitude()/0.2 );
00452   Debugcln(DJ,"scaling dq");
00453 }
00454 //!!!      
00455       
00456     }
00457     catch (std::exception& e) {
00458       abort=true;
00459       test.failureString = e.what();
00460     }
00461 
00462     if (!abort) {
00463 //Debugcln(DJ,"==dq:" << dq);
00464       test.dqs.push_back(dq);
00465 
00466       // move the platform/manipulator/tool
00467       control->setOutputs( q + dq );
00468 
00469       // 'simulate'
00470       env->simulateForSimTime( t-simTime );
00471       simTime = t;
00472       
00473       // update q & x next iteration
00474       q = control->getInputs();
00475       test.qs.push_back(q); 
00476       test.times.push_back(simTime);
00477       x = calcEEVector(test,chain,q); // new ee pos/orient
00478       test.xs.push_back(x);
00479       test.Js.push_back(J);
00480     }
00481 
00482   } // end traj loop
00483 
00484 
00485   test.testCompleted = !abort;
00486   if (!test.testCompleted) {
00487     Logln("IK solution failed after " << i << " trajectory points.");
00488     Logln("time:" << simTime.seconds() << " trajectory position:" 
00489           << traj.position(simTime) << " (WorldFrame).");
00490     Logln("failure reason:\n" << test.failureString);
00491   }
00492   Logln("  final q=" << q << "  x=" << x << " (" << i << " steps)");
00493   
00494   test.resultsPresent = true;
00495   return simTime;
00496 }
00497 
00498 
00499 
00500 
00501 
00502 base::Vector IKORTester::calcEEVector(const IKORTest::Test& test, const KinematicChain& chain, const base::Vector& q) const
00503 {
00504   // calculate the EE position using the current joint parameters and the forward kinematics transform (Tn)
00505   Matrix Tn( chain.getForwardKinematics(q) );
00506   
00507   //  the position is the first 3 elements of column 4 of T
00508   //  the orientation is the 3x3 submatrix of T - converted into a EulerRPY vector
00509   Vector pos(3); pos = vectorRange(Vector(matrixColumn(Tn,3)), Range(0,3));
00510   Vector x(test.orientationControl?6:3);
00511   vectorRange(x, Range(0,3)) = pos;
00512 
00513   if (test.orientationControl) {
00514     Matrix rot(3,3); rot = matrixRange(Tn, Range(0,3), Range(0,3));
00515     Vector orient( Orient(rot).getVector(Orient::EulerRPY) );
00516     vectorRange(x, Range(3,x.size())) = orient;
00517   }
00518   return x; // WorldFrame
00519 }
00520 

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