00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
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
00117 ref<const RobotDescription> robotDescr(robot->getRobotDescription());
00118 ref<const ManipulatorDescription> manipDescr(robotDescr->manipulators()[testManipulatorIndex]);
00119
00120
00121 Matrix4 platformTransform( robot->getOrientation().getRotationMatrix3() );
00122 platformTransform.setTranslationComponent( robot->getPosition() );
00123 KinematicChain platformChain( robotDescr->platform()->getKinematicChain(3,platformTransform) );
00124 KinematicChain robotChain(robotDescr->getKinematicChain(3,platformTransform,testManipulatorIndex,0));
00125 KinematicChain manipChain(manipDescr->getKinematicChain());
00126
00127
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
00135 array<ref<ControlInterface> > interfaces;
00136 interfaces.push_back(platformControl);
00137 interfaces.push_back(manipControl);
00138
00139 KinematicChain manipToolChain(manipChain);
00140 KinematicChain toolChain;
00141 if (test.toolAttached) {
00142
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);
00151 toolChain = td->getKinematicChain();
00152
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);
00167 if (test.toolAttached) {
00168 chain += toolChain;
00169 interfaces.push_back(toolControl);
00170 }
00171
00172
00173
00174
00175 ref<ControlInterface> control(NewObj AggregateControlInterface("robotPosition","RobotPositionControl", interfaces));
00176
00177
00178 Vector q( zeroVector(chain.dof()) );
00179
00180
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
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
00200 q.reset( control->getInputs() );
00201
00202
00203
00204 Trajectory traj(test.traj);
00205
00206
00207
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
00220 if (test.frame != Robot::WorldFrame) {
00221
00222
00223
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));
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
00241
00242 Real stepsize = 0.01;
00243 if (test.timeIntervalSpecified) {
00244
00245
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) {
00257 stepsize = test.timeInterval[2];
00258 traj.resample( Int(duration/stepsize) );
00259 }
00260
00261 }
00262
00263
00264
00265
00266 if (test.maxdxSpecified)
00267 traj.resample(test.maxdx);
00268
00269
00270
00271
00272
00273
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
00286 Vector jointWeights(test.jointWeightsSpecified? test.jointWeights : Vector() );
00287 if (jointWeights.size() == 0) {
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
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
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))
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
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
00344
00345
00346
00347
00348
00349 bool coincident=false;
00350
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
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
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
00402 test.dxs.push_back(dx);
00403
00404 Vector dq( chain.dof() );
00405
00406
00407 if (test.optConstraints.test(IKOR::ObstacleAvoidance)) {
00408
00409
00410
00411
00412 array<InverseKinematicsSolver::LinkProximityData> proxData( chain.dof() );
00413
00414
00415 Vector proximityInputs( proximitySensorInterface->getInputs() );
00416
00417
00418
00419 for(Int v=0; v < chain.dof(); v++) {
00420
00421 if (v >= platformChain.dof()+1) {
00422
00423 Int l = manipToolChain.linkIndexOfVariable(v - platformChain.dof());
00424 InverseKinematicsSolver::LinkProximityData& pd(proxData[v]);
00425
00426 pd.distance = proximityInputs[l*5];
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
00441
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
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
00464 test.dqs.push_back(dq);
00465
00466
00467 control->setOutputs( q + dq );
00468
00469
00470 env->simulateForSimTime( t-simTime );
00471 simTime = t;
00472
00473
00474 q = control->getInputs();
00475 test.qs.push_back(q);
00476 test.times.push_back(simTime);
00477 x = calcEEVector(test,chain,q);
00478 test.xs.push_back(x);
00479 test.Js.push_back(J);
00480 }
00481
00482 }
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
00505 Matrix Tn( chain.getForwardKinematics(q) );
00506
00507
00508
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;
00519 }
00520