Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

Pilot.cc

Go to the documentation of this file.
00001 #include "Motion/WalkMC.h"
00002 #ifdef TGT_HAS_WALK
00003 
00004 #include <vector>
00005 
00006 #include "Crew/Pilot.h"
00007 #include "DualCoding/VRmixin.h"
00008 #include "Events/PilotEvent.h"
00009 #include "Shared/mathutils.h" // for isnan fix
00010 
00011 namespace DualCoding {
00012 
00013 void Pilot::doStart() {
00014   if (verbosity & PVstart)
00015     cout << "Pilot starting up: walk_id= " << walk_id <<"  waypointwalk_id= " << waypointwalk_id << endl;
00016   setAgent(Point(0,0,0,allocentric),0,false,false);
00017   VRmixin::particleFilter->displayParticles(50);
00018   addNode(new RunMotionModel)->start();
00019   addNode(new CollisionChecker)->start();
00020 }
00021 
00022 void Pilot::doStop() {
00023   pilotAbort();
00024   if (verbosity & PVstart)
00025     cout << "Pilot is shutting down." << endl;
00026   motman->removeMotion(walk_id);
00027   motman->removeMotion(waypointwalk_id);
00028   waypointwalk_id = MotionManager::invalid_MC_ID;
00029   walk_id = MotionManager::invalid_MC_ID;
00030   delete defaultLandmarkExtractor;
00031   defaultLandmarkExtractor = NULL;
00032 
00033   // The following teardown() call is necessary because setup
00034   // hard-wires the MC_ID for all the walk nodes.  If we leave the
00035   // waypoint walk motion command active when Tekkotsu shuts down,
00036   // it crashes and generates a nasty backtrace.  But if we remove
00037   // the motion command, there's no easy way to put it back because
00038   // setup won't be called again unless we do this teardown() call.
00039   teardown();
00040 }
00041 
00042 void Pilot::doEvent() {
00043   if (event->getGeneratorID() == EventBase::timerEGID && event->getSourceID() == 9999)
00044     executeRequest();
00045   else
00046     cout << "Pilot got unexpected event: " << event->getDescription() << endl;
00047 }
00048 
00049 void Pilot::unwindForNextRequest() {
00050   erouter->addTimer(this,9999,1,false); // allow time to unwind event stack before executing next request
00051 }
00052 
00053 void Pilot::setAgent(const Point &loc, AngTwoPi heading, bool quiet, bool updateWaypoint) {
00054   if (updateWaypoint) {
00055     cout << "Updating waypoint walk to " << loc << " hdg=" << heading << endl;
00056     MMAccessor<WaypointWalkMC> wp_acc(waypointwalk_id);
00057     wp_acc->setCurPos(loc.coordX(), loc.coordY(), heading);
00058   }
00059   VRmixin::mapBuilder->setAgent(loc, heading, quiet);
00060   // Synching the estimate (below) is a no-op if setAgent was
00061   // invoked by Pilot::RunMotionModel, but not if the user called
00062   // setAgent directly.
00063   VRmixin::particleFilter->setPosition(loc.coordX(), loc.coordY(), heading);
00064   VRmixin::particleFilter->synchEstimateToAgent();
00065 }
00066 
00067 unsigned int Pilot::executeRequest(BehaviorBase* requestingBehavior, const PilotRequest& req) {
00068   requests.push(new PilotRequest(req));
00069   const unsigned int reqID = ++idCounter;
00070   requests.back()->requestID = reqID;
00071   requests.back()->requestingBehavior = requestingBehavior;
00072   if (curReq == NULL)
00073     unwindForNextRequest();
00074   else if (verbosity & PVqueued)
00075     cout << "Pilot request " << requests.back()->requestID << " queued." << endl;
00076   return reqID;
00077 }
00078 
00079 
00080 void Pilot::executeRequest() {
00081   if (curReq == NULL && !requests.empty()) {
00082     curReq = requests.front();
00083     if (verbosity & PVexecute)
00084       cout << "Pilot executing request " << curReq->requestID
00085      << ": " << PilotTypes::RequestTypeNames[curReq->requestType] << endl;
00086     VRmixin::particleFilter->synchEstimateToAgent();
00087     if ( ! curReq->walkParameters.empty() ) {
00088       MMAccessor<WaypointWalkMC>(VRmixin::pilot->getWaypointwalk_id())->WalkMC::loadFile(curReq->walkParameters.c_str());
00089       MMAccessor<WalkMC>(VRmixin::pilot->getWalk_id())->loadFile(curReq->walkParameters.c_str());
00090     }
00091     dispatch_->start();
00092   }
00093 }
00094 
00095 void Pilot::requestComplete(PilotTypes::ErrorType_t errorType) {
00096   if (curReq == NULL) {
00097     cout << "Pilot::requestComplete had NULL curReq !!!!!!!!!!!!!!!!" << endl;
00098     return;
00099   }
00100   const BehaviorBase* requester = curReq->requestingBehavior;
00101   PilotEvent e(EventBase::pilotEGID,
00102          (requester==NULL) ? curReq->requestID : (size_t)requester,
00103          EventBase::statusETID);
00104   e.requestType = curReq->requestType;
00105   e.errorType = errorType;
00106   if (verbosity & PVcomplete)
00107     cout << "Pilot request " << curReq->requestID << " complete: "
00108    << PilotTypes::ErrorTypeNames[errorType] <<  endl;
00109   requestComplete(e);
00110 }
00111 
00112 void Pilot::requestComplete(const PilotEvent &e) {
00113   //if ( curReq->trackRequest != NULL )
00114   //  VRmixin::lookout.stopTrack();
00115   dispatch_->finish();
00116   // now clean up everything before posting the event
00117   delete curReq->landmarkExtractor;
00118   delete curReq->searchObjectExtractor;
00119   delete curReq;
00120   curReq = NULL;
00121   requests.pop();
00122   // post the event and allow time to unwind before starting on next event in the queue
00123   erouter->postEvent(e);
00124   unwindForNextRequest();
00125 }
00126 
00127 void Pilot::setWorldBounds(const Shape<PolygonData> &bounds) {
00128   VRmixin::particleFilter->setWorldBounds(bounds);
00129 }
00130 
00131 void Pilot::setDefaultLandmarks(const std::vector<ShapeRoot> &landmarks) {
00132   defaultLandmarks = landmarks;
00133 }
00134 
00135 void Pilot::setDefaultLandmarkExtractor(const MapBuilderRequest &mapreq) {
00136   defaultLandmarkExtractor = new MapBuilderRequest(mapreq);
00137   defaultLandmarkExtractor->clearLocal = false;
00138 }
00139 
00140 void Pilot::pilotPop() {
00141   if ( curReq != NULL ) {
00142     if ( verbosity & PVpop )
00143       cout << "Pilot popping current request." << endl;
00144     dispatch_->stop();
00145     VRmixin::pilot->requestComplete(PilotTypes::abort);
00146   }
00147 }
00148 
00149 void Pilot::pilotAbort() {
00150   if ( curReq != NULL && (verbosity & PVabort) )
00151     cout << "Pilot aborting." << endl;
00152   dispatch_->stop();
00153   curReq = NULL;
00154   while (!requests.empty()) {
00155     delete requests.front();
00156     requests.pop();
00157   }
00158 }
00159 
00160 void Pilot::setupLandmarkExtractor() {
00161 
00162   if ( curReq->landmarkExitTest == NULL )
00163     curReq->landmarkExitTest = &Pilot::defaultLandmarkExitTest;
00164   if ( curReq->landmarkExtractor != NULL )  // user supplied his own, so go with that
00165     return;
00166   else if ( defaultLandmarkExtractor != NULL ) {
00167     curReq->landmarkExtractor = new MapBuilderRequest(*defaultLandmarkExtractor);
00168     return;
00169   }
00170   vector<ShapeRoot> landmarkTemp;
00171   if ( ! curReq->landmarks.empty() )
00172     landmarkTemp = curReq->landmarks;
00173   else if ( ! defaultLandmarks.empty() )
00174     landmarkTemp = defaultLandmarks;
00175   else {  // the user specified no landmarks, so look in worldShS for possibilities
00176 
00177     NEW_SHAPEVEC(markers, MarkerData, select_type<MarkerData>(VRmixin::worldShS));
00178     landmarkTemp.insert(landmarkTemp.begin(), markers.begin(), markers.end());
00179     NEW_SHAPEVEC(apriltags, AprilTagData, select_type<AprilTagData>(VRmixin::worldShS));
00180     landmarkTemp.insert(landmarkTemp.begin(), apriltags.begin(), apriltags.end());
00181     curReq->landmarks = landmarkTemp;  // save what we found so the navigation planner can use them
00182   }
00183   if ( !landmarkTemp.empty() ) {
00184     curReq->landmarkExtractor = new MapBuilderRequest(MapBuilderRequest::localMap);
00185     curReq->landmarkExtractor->clearVerbosity = -1U;
00186     curReq->landmarkExtractor->setVerbosity = MapBuilder::MBVimportShapes;
00187     if ( ! select_type<AprilTagData>(landmarkTemp).empty() )
00188       curReq->landmarkExtractor->setAprilTagFamily();  // HACK: should be using the tag's actual family, not default family, but we don't currently store that info
00189     // iterate through landmarkTemp and add all the marker types to landmarkExtractor
00190     for ( vector<ShapeRoot>::const_iterator it = landmarkTemp.begin();
00191     it != landmarkTemp.end(); it++ ) {
00192       if ( (*it)->getType() == markerDataType ) {
00193   const Shape<MarkerData> &m = ShapeRootTypeConst(*it, MarkerData);
00194   curReq->landmarkExtractor->addMarkerType(m->getMarkerType());
00195   curReq->landmarkExtractor->objectColors[markerDataType].insert(ProjectInterface::getColorIndex(m->getColor()));
00196       }
00197     }
00198   }
00199 }
00200 
00201 bool Pilot::defaultLandmarkExitTest() {
00202   return ( VRmixin::localShS.allShapes().size() >= 2 );
00203 }
00204 
00205 std::vector<ShapeRoot>
00206   Pilot::calculateVisibleLandmarks(const DualCoding::Point &currentLocation,
00207            AngTwoPi currentOrientation, AngTwoPi maxTurn,
00208            const std::vector<DualCoding::ShapeRoot> &possibleLandmarks) {
00209   std::vector<ShapeRoot> result;
00210   for(unsigned int i = 0; i<possibleLandmarks.size(); i++)
00211     if ( isLandmarkViewable(possibleLandmarks[i], currentLocation, currentOrientation, maxTurn) )
00212       result.push_back(possibleLandmarks[i]);
00213   return result;
00214 }
00215 
00216 bool Pilot::isLandmarkViewable(const DualCoding::ShapeRoot &selectedLandmark,
00217              const DualCoding::Point &currentLocation,
00218              AngTwoPi currentOrientation, AngTwoPi maxTurn) {
00219   AngSignPi bearing = (selectedLandmark->getCentroid() - currentLocation).atanYX();
00220 
00221   // is the camera within our possible field of view (in front of us)?
00222   if ( fabs(bearing) > (CameraHorizFOV/2.0+maxTurn) )
00223     return false;
00224 
00225   // If there are no walls, assume all landmarks are visible,
00226   // but this isn't really true for poster-type markers where
00227   // we need to know the viewing angle.  Will fix this someday.
00228   Shape<PolygonData> boundary;   // either worldBounds or wall
00229   GET_SHAPE(worldBounds, PolygonData, VRmixin::worldShS);
00230   if ( worldBounds.isValid() )
00231     boundary = worldBounds;
00232   else {
00233     GET_SHAPE(wall, PolygonData, VRmixin::worldShS);
00234     boundary = wall;
00235   }
00236   if ( ! boundary.isValid() )
00237     return true;
00238   // do we intersect with the world bounds when looking at marker?
00239   LineData lineOfSight(VRmixin::worldShS, currentLocation, selectedLandmark->getCentroid());
00240   if ( boundary->intersectsLine(lineOfSight) )
00241     return false;
00242 
00243   // this landmark is viewable
00244   return true;
00245 }
00246 
00247 void Pilot::CollisionDispatch::doStart() {
00248   switch ( VRmixin::pilot->curReq->collisionAction ) {
00249   case collisionStop:
00250   case collisionReplan:
00251     erouter->addListener(this, EventBase::pilotEGID, (size_t)VRmixin::pilot, EventBase::statusETID);
00252     break;
00253   default:
00254     break;
00255   }
00256 }
00257 
00258 void Pilot::CollisionDispatch::doEvent() {
00259   // If we're listening for Pilot collisions then we want to stop or
00260   // replan, so rebroadcast with this as the source node to trigger
00261   PilotEvent e(*event);
00262   e.setSourceID((size_t)this);
00263   erouter->postEvent(e);
00264 }
00265 
00266 void Pilot::TerminateDueToCollision::doStart() {
00267   PilotEvent e(*event);  // copy and convert from an internal Pilot event to a public one
00268   e.setSourceID((size_t)VRmixin::pilot->curReq->requestingBehavior);
00269   VRmixin::pilot->requestComplete(e);
00270 }
00271 
00272 //================ Background processes ================
00273 
00274 void Pilot::RunMotionModel::doStart() {
00275   erouter->addTimer(this, 1, 250, true);  // do odometry update every 250 msec
00276 }
00277 
00278 void Pilot::RunMotionModel::doEvent() {
00279   if (event->getGeneratorID() == EventBase::timerEGID) {
00280     VRmixin::particleFilter->updateMotion();
00281     LocalizationParticle estimate = VRmixin::particleFilter->getEstimate();
00282     VRmixin::mapBuilder->setAgent(Point(estimate.x, estimate.y, 0, allocentric), estimate.theta, true);
00283   }
00284 }
00285 
00286 void Pilot::CollisionChecker::doStart() {
00287   enableDetection();
00288 }
00289 
00290 void Pilot::CollisionChecker::enableDetection() {
00291 #ifdef TGT_IS_CREATE
00292   // std::cout << "Enabled collision detection." << std::endl;
00293   erouter->addListener(this, EventBase::buttonEGID, BumpLeftButOffset, EventBase::activateETID);
00294   erouter->addListener(this, EventBase::buttonEGID, BumpRightButOffset, EventBase::activateETID);
00295   erouter->addListener(this, EventBase::buttonEGID, OvercurrentLeftWheelOffset, EventBase::activateETID);
00296   erouter->addListener(this, EventBase::buttonEGID, OvercurrentRightWheelOffset, EventBase::activateETID);
00297   erouter->addTimer(this, overcurrentResetTimer, 1500, true); // reset overcurrentCounter frequently
00298 #else
00299   std::cout << "Warning: Pilot has no collision check method defined for this robot type" << std::endl;
00300 #endif
00301 }
00302 
00303 void Pilot::CollisionChecker::disableDetection(unsigned int howlong=-1U) {
00304 #ifdef TGT_IS_CREATE
00305   // std::cout << "Disabling collision detection for " << howlong << " ms." << std::endl;
00306   erouter->removeListener(this, EventBase::buttonEGID);
00307 #endif
00308   erouter->addTimer(this, 1, howlong, false);
00309 }
00310 
00311 void Pilot::CollisionChecker::doEvent() {
00312   if ( event->getGeneratorID() == EventBase::timerEGID ) {
00313     if ( event->getSourceID() == collisionEnableTimer )
00314       enableDetection();
00315 #ifdef TGT_IS_CREATE
00316     else if ( event->getSourceID() == overcurrentResetTimer )
00317       overcurrentCounter = 0;
00318     return;
00319 #endif
00320   }
00321   // If we get here, event must have been a button press (bump sesnsor or overcurrent)
00322   if ( VRmixin::pilot->curReq == NULL || 
00323        VRmixin::pilot->curReq->collisionAction == collisionIgnore )
00324     return;
00325 #ifdef TGT_IS_CREATE
00326   if ( event->getSourceID() == BumpLeftButOffset ||
00327        event->getSourceID() == BumpRightButOffset )
00328     reportCollision();
00329   else // overcurrent -- could be transient.  Count 'em and see.
00330     if ( ++overcurrentCounter >= 12 ) {
00331       overcurrentCounter = 0;
00332       reportCollision();
00333     }
00334 #endif
00335 }
00336 
00337 void Pilot::CollisionChecker::reportCollision() {
00338   if ( VRmixin::pilot->verbosity & PVcollision )
00339     cout << "Pilot: collision detected!" << endl;
00340   disableDetection(1000); // ignore any further collisions for the next second
00341   PilotEvent e(EventBase::pilotEGID, (size_t)VRmixin::pilot, EventBase::statusETID);
00342   e.errorType = collisionDetected;
00343   // Note: in the future we might want to add info about where on the
00344   // robot's body the collision occurred.
00345   erouter->postEvent(e);
00346 }
00347 
00348 
00349 //================ Navigation plans and steps ================
00350 
00351 std::string Pilot::NavigationPlan::toString() const {
00352   ostringstream os;
00353   os << "Navigation plan (" << steps.size() << " steps):" << std::endl;
00354   for ( size_t i=0; i<steps.size(); i++ )
00355     os << "  " << steps[i].toString() << endl;
00356   return os.str();
00357 }
00358 
00359 void Pilot::NavigationPlan::addNavigationStep(NavigationStepType_t type, const DualCoding::Point &waypoint, AngTwoPi orientation) {
00360   NavigationStep step(type, waypoint);
00361   if ( type == localizeStep )
00362     step.visibleLandmarks = calculateVisibleLandmarks(waypoint, orientation, M_PI/2, VRmixin::pilot->curReq->landmarks);
00363   if ( type != localizeStep || !step.visibleLandmarks.empty() )
00364     steps.push_back(step);
00365 }
00366 
00367 std::string Pilot::NavigationStep::toString() const {
00368   ostringstream os;
00369   os << "NavigationStep[";
00370   switch (type) {
00371   case localizeStep: os << "localizeStep"; break;
00372   case turnStep: os << "turnStep"; break;
00373   case travelStep: os << "travelStep"; break;
00374   case turnObjStep: os << "turnObjStep"; break;
00375   case acquireObjStep: os << "acquireObjStep"; break;
00376   default:
00377     os << "unknown step type";
00378   }
00379   if ( type == localizeStep ) {
00380     os << ", visible=(";
00381     for (size_t i = 0; i < visibleLandmarks.size(); i++)
00382       os << ( (i>0) ? "," : "")  << visibleLandmarks[i]->getName();
00383     os << ")";
00384   } else
00385     os << ", waypoint=" << waypoint;
00386   os << "]";
00387   return os.str();
00388 }
00389 
00390 std::ostream& operator<<(std::ostream& os, const Pilot::NavigationStep &step) { return os << step.toString(); }
00391 std::ostream& operator<<(std::ostream& os, const Pilot::NavigationPlan &plan) { return os << plan.toString(); }
00392 
00393 
00394 void Pilot::PlanPath::doPlan(NavigationPlan &plan, Point initPos, AngTwoPi initHead, PilotRequest req) {
00395   plan.path.clear();
00396   {
00397     GET_SHAPE(plannedPath, GraphicsData, worldShS);
00398     plannedPath.deleteShape();
00399     GET_SHAPE(plannerTrees, GraphicsData, worldShS);
00400     plannerTrees.deleteShape();
00401   }
00402   GET_SHAPE(worldBounds, PolygonData, worldShS);
00403 
00404   const ShapeRoot &target = req.targetShape;
00405   Point targetLoc = target->getCentroid();
00406   if ( targetLoc.getRefFrameType() == egocentric ) {
00407     targetLoc.applyTransform(VRmixin::mapBuilder->localToWorldMatrix);
00408     targetLoc.setRefFrameType(allocentric);
00409   }
00410 
00411   const bool planForHeading = ! std::isnan((float)req.targetHeading);
00412   GenericRRTBase::PlannerResult<2> result;
00413   void *pathResult;
00414   size_t pathResultSize;
00415   if ( !planForHeading ) {
00416     std::cout << "Planning path from " << initPos << " to point: " << targetLoc << std::endl;
00417     float const robotRadius = 6 * 25.4f * 1.25;   // ***HACK*** should be pulling this from RobotInfo
00418     ShapeSpacePlannerXY planner(worldShS, worldBounds, req.obstacleInflation, robotRadius);
00419     if ( req.displayObstacles )
00420       planner.addObstaclesToShapeSpace(worldShS);
00421     pathResult = new std::vector<ShapeSpacePlannerXY::NodeValue_t>;
00422     std::vector<ShapeSpacePlannerXY::NodeType_t> *treeStartResult =
00423       req.displayTree ? new std::vector<ShapeSpacePlannerXY::NodeType_t> : NULL;
00424     std::vector<ShapeSpacePlannerXY::NodeType_t> *treeEndResult =
00425       req.displayTree ? new std::vector<ShapeSpacePlannerXY::NodeType_t> : NULL;
00426     result = planner.planPath(initPos, targetLoc, req.maxRRTIterations,
00427             (std::vector<ShapeSpacePlannerXY::NodeValue_t>*)pathResult, treeStartResult, treeEndResult);
00428     pathResultSize = ((std::vector<ShapeSpacePlannerXY::NodeValue_t>*)pathResult)->size();
00429     if ( req.displayTree ) {
00430       NEW_SHAPE(plannerTrees, GraphicsData, new GraphicsData(worldShS));
00431       planner.plotTree(*treeStartResult, plannerTrees, rgb(0,0,0));
00432       planner.plotTree(*treeEndResult, plannerTrees, rgb(0,192,0));
00433       delete treeStartResult;
00434       delete treeEndResult;
00435     }
00436   } else {
00437 
00438     // Plan for target heading
00439     std::cout << "Planning path from " << initPos << " to point " << targetLoc
00440               << " heading " << float(req.targetHeading)/M_PI*180 << " deg." << std::endl;
00441     float gateDistance = 6 * 25.4;  // half a robot width
00442     Point gateLoc = targetLoc - Point(cos(req.targetHeading),sin(req.targetHeading),0,allocentric) * gateDistance;
00443     ShapeSpacePlannerXYTheta planner(worldShS, worldBounds, req.obstacleInflation);
00444     if ( req.displayObstacles )
00445       planner.addObstaclesToShapeSpace(worldShS);
00446     pathResult = new std::vector<ShapeSpacePlannerXYTheta::NodeValue_t>;
00447     std::vector<ShapeSpacePlannerXYTheta::NodeType_t> *treeStartResult = req.displayTree ? new std::vector<ShapeSpacePlannerXYTheta::NodeType_t> : NULL;
00448     std::vector<ShapeSpacePlannerXYTheta::NodeType_t> *treeEndResult = req.displayTree ? new std::vector<ShapeSpacePlannerXYTheta::NodeType_t> : NULL;
00449     result = planner.planPath(initPos, gateLoc, initHead, req.targetHeading, req.maxRRTIterations,
00450             (std::vector<ShapeSpacePlannerXYTheta::NodeValue_t>*)pathResult, 
00451             treeStartResult, treeEndResult);
00452     ((std::vector<ShapeSpacePlannerXYTheta::NodeValue_t>*)pathResult)->
00453       push_back(ShapeSpacePlannerXYTheta::NodeValue_t(std::pair<float,float>(targetLoc.coordX(),targetLoc.coordY()),req.targetHeading));
00454     pathResultSize = ((std::vector<ShapeSpacePlannerXYTheta::NodeValue_t>*)pathResult)->size();
00455     if ( req.displayTree ) {
00456       NEW_SHAPE(plannerTrees, GraphicsData, new GraphicsData(worldShS));
00457       planner.plotTree(*treeStartResult, plannerTrees, rgb(0,0,0));
00458       planner.plotTree(*treeEndResult, plannerTrees, rgb(0,192,0));
00459       delete treeStartResult;
00460       delete treeEndResult;
00461     }
00462   }
00463 
00464   if ( result.code != GenericRRTBase::SUCCESS ) {
00465     switch ( result.code ) {
00466     case GenericRRTBase::START_COLLIDES:
00467       std::cout << "Navigation path planning failed: start state " << result.movingObstacle->toString()
00468     << " is in collision with " << result.collidingObstacle->toString() << ".\n";
00469       break;
00470     case GenericRRTBase::END_COLLIDES:
00471       std::cout << "Navigation path planning failed: end state " << result.movingObstacle->toString()
00472     << " is in collision with " << result.collidingObstacle->toString() << ".\n";
00473       break;
00474     case GenericRRTBase::MAX_ITER:
00475       std::cout << "Navigation path planning failed: max iterations " << req.maxRRTIterations << " exceeded.\n";
00476       break;
00477     default: break; // dummy to suppress compiler warning
00478     }
00479     postStateSignal<GenericRRTBase::PlanPathResultCode>(result.code);
00480     return;
00481   }
00482 
00483   // Extract the path into a vector of points
00484   for ( size_t i = 0; i < pathResultSize; i++ ) {
00485     const std::pair<float,float> &pt = ( !planForHeading )
00486       ? (*(std::vector<ShapeSpacePlannerXY::NodeValue_t>*)pathResult)[i]
00487       : (*(std::vector<ShapeSpacePlannerXYTheta::NodeValue_t>*)pathResult)[i].first;
00488     plan.path.push_back(Point(pt.first, pt.second, 0, allocentric));
00489     if ( verbosity & PVshowPath ) {
00490       cout << "path[" << i << "] = " << plan.path[i];
00491       if ( planForHeading )
00492   cout << " hdg " << float(AngSignPi((*(std::vector<ShapeSpacePlannerXYTheta::NodeValue_t>*)pathResult)[i].second)) * 180/M_PI
00493        << " deg.";
00494       cout << endl;
00495     }
00496   }
00497   
00498   // Display the path if requested
00499   if ( req.displayPath ) {
00500     NEW_SHAPE(plannedPath, GraphicsData, new GraphicsData(worldShS));
00501     if ( !planForHeading ) 
00502       ShapeSpacePlannerXY::plotPath((*(std::vector<ShapeSpacePlannerXY::NodeValue_t>*)pathResult),
00503             plannedPath, rgb(0,0,255));
00504     else
00505       ShapeSpacePlannerXYTheta::plotPath((*(std::vector<ShapeSpacePlannerXYTheta::NodeValue_t>*)pathResult),
00506            plannedPath, rgb(0,0,255));
00507   }
00508   
00509   // Now execute the plan, unless requested not to
00510   if ( req.executePath )
00511     postStateCompletion();
00512   else
00513     postStateFailure();
00514 }
00515 
00516 void Pilot::ConstructNavPlan::doAnalysis(NavigationPlan &plan, Point initPos, AngTwoPi initHead, PilotRequest req) {
00517 
00518   AngTwoPi orientation = initHead;
00519   Point currentPosition = initPos;
00520   plan.steps.clear();
00521 
00522   for (unsigned int i = 1; i<(plan.path.size()); i++) {
00523 
00524     // Calculate vector between this and next point on path
00525     const Point delta = (plan.path[i] - plan.path[i-1]);
00526     const float distanceBetween = delta.xyNorm();
00527     const AngSignPi headingChange = AngSignPi(delta.atanYX() - orientation);
00528     orientation += headingChange;
00529     
00530     // If this step involves a non-negligible heading change, turn  first
00531     if( fabs(headingChange) > M_PI/18 )
00532       plan.addNavigationStep(turnStep, plan.path[i], orientation);
00533     // If we have a pan/tilt and we made a large turn, relocalize
00534 #ifdef TGT_HAS_HEAD
00535     if ( fabs(headingChange) > M_PI / 6 )
00536       if ( req.landmarks.size() > 0)
00537         plan.addNavigationStep(localizeStep, plan.path[i], orientation);
00538 #endif
00539 
00540     // Calculate the number of times to localize during travel, but
00541     // don't try to localize if we have no landmarks.
00542     float maxDistanceBetween = 500;   //!< farthest we're willing to travel before localizing again
00543     if ( req.landmarks.empty() ) {
00544       maxDistanceBetween = 1e10f;
00545     }
00546     int numberOfLocalizations = (int)floor(distanceBetween / maxDistanceBetween);
00547 
00548     Point deltaStep = delta.unitVector() * maxDistanceBetween;
00549     Point currentPoint = plan.path[i-1];
00550     for ( int j=0; j < numberOfLocalizations; j++ ) {
00551       currentPoint += deltaStep;
00552       plan.addNavigationStep(travelStep, currentPoint, orientation);
00553       plan.addNavigationStep(localizeStep, currentPoint, orientation);
00554     }
00555     plan.addNavigationStep(travelStep, plan.path[i], orientation);
00556   }
00557   plan.currentStep = plan.steps.begin();
00558   postStateCompletion();
00559 }
00560 
00561 void Pilot::ExecutePlan::ExecuteStep::doExecute(NavigationPlan &plan, 
00562                                                 DisplacementInstruction &nextDisplacementInstruction,
00563                                                 const bool pushType) {
00564 
00565   DualCoding::Point position = theAgent->getCentroid();
00566 
00567   if (plan.steps.size() == 0) {
00568     std::cout << "Warning: empty path in Pilot::ExecutePlan::doStart" << std::endl;
00569     postStateCompletion();
00570     return;
00571   }
00572 
00573   switch (plan.currentStep->type) {
00574 
00575   case localizeStep:
00576     if (!pushType)
00577         postStateSignal<NavigationStepType_t>(localizeStep);
00578     else
00579         postStateSuccess();
00580     break;
00581 
00582   case travelStep: {
00583     position = theAgent->getCentroid();
00584     fmat::Column<3> pos = position.getCoords();
00585     fmat::Column<3> next = plan.currentStep->waypoint.getCoords();      
00586     if ( verbosity & PVnavStep )
00587       cout << ">>> Travel: at " << pos << " hdg " << float(AngSignPi(theAgent->getOrientation()))*180/M_PI 
00588      << " deg., next pos. is " << next;
00589     if (position.xyDistanceFrom(plan.currentStep->waypoint) < allowableDistanceError) {
00590       if ( verbosity & PVnavStep )
00591   cout << ".   Close enough!" << endl;
00592       postStateSuccess();
00593     }
00594     else {
00595       fmat::Column<2> disp = fmat::SubVector<2>(next) - fmat::SubVector<2>(pos);
00596       nextDisplacementInstruction.nextPoint = disp;
00597       if ( verbosity & PVnavStep )
00598   cout << ", disp=" << disp << endl;
00599 
00600       std::vector<NavigationStep>::const_iterator m, n;
00601       m = plan.steps.begin();
00602       n = m;
00603       n++;
00604       if (((m == plan.currentStep) || (n == plan.currentStep)) && pushType)
00605           postStateSignal<NavigationStepType_t>(acquireObjStep);
00606       else
00607           postStateSignal<NavigationStepType_t>(travelStep);
00608     }
00609     break;
00610   }
00611 
00612   case turnStep: {
00613     position = theAgent->getCentroid();
00614     fmat::Column<3> pos = position.getCoords();
00615     fmat::Column<3> next = plan.currentStep->waypoint.getCoords();
00616     fmat::Column<2> disp = fmat::SubVector<2>(next) - fmat::SubVector<2>(pos);
00617     AngSignPi angle = (atan2(disp[1], disp[0]) - theAgent->getOrientation());
00618     if (fabs(angle) < allowableAngularError) {
00619       if ( verbosity & PVnavStep )
00620         cout << ">>> Turn: " << float(angle)*180/M_PI << " deg is smaller than minimum "
00621        << float(allowableAngularError)*180/M_PI << " deg (skipped)" << endl;
00622       postStateSuccess();
00623     } else {
00624       if ( verbosity & PVnavStep )
00625         cout << ">>> Turn: " << float(angle)*180/M_PI << " deg" << endl;
00626       nextDisplacementInstruction.angleToTurn = angle;
00627       if (pushType && (plan.currentStep != plan.steps.begin()))
00628           postStateSignal<NavigationStepType_t>(turnObjStep);
00629       else
00630           postStateSignal<NavigationStepType_t>(turnStep);
00631     }
00632     break;
00633     }
00634 
00635   default:
00636     cout << "Incorrect step type " << plan.currentStep->type << "in ExecuteStep" << endl;
00637   }
00638 }
00639 
00640 //================ LocalizationUtility ================
00641 
00642 const float Pilot::LocalizationUtility::Localize::minConfidentWeight = -30;
00643 
00644 #ifdef TGT_HAS_HEAD
00645 void Pilot::LocalizationUtility::ChooseGazePoints::setupGazePoints(std::deque<Point> &gazePoints) {
00646   std::vector<ShapeRoot> visibleLandmarks =
00647     calculateVisibleLandmarks(theAgent->getCentroid(), theAgent->getOrientation(), 
00648             M_PI/2, // assume head can turn 90 degrees left or right
00649             VRmixin::pilot->curReq->landmarks);
00650   gazePoints = std::deque<Point>();
00651   for ( unsigned int i = 0; i < visibleLandmarks.size(); i++ ) {
00652     // convert to egocentric coordinates
00653     Point allovec = visibleLandmarks[i]->getCentroid() - theAgent->getCentroid();
00654     Point egovec = Point(fmat::rotationZ(- theAgent->getOrientation()) * allovec.getCoords());
00655     egovec.setRefFrameType(egocentric);
00656     gazePoints.push_back(egovec);
00657   }
00658 }
00659 
00660 #else  // robot has no head, so plan on turning the body
00661 void Pilot::LocalizationUtility::ChooseGazePoints::setupGazeHeadings
00662                 (const AngTwoPi initialHeading, std::deque<AngTwoPi> &gazeHeadings, const AngTwoPi maxTurn) {
00663   const AngTwoPi robotHeading = theAgent->getOrientation();
00664   std::vector<ShapeRoot> visibleLandmarks =
00665     calculateVisibleLandmarks(theAgent->getCentroid(), robotHeading, 
00666             maxTurn, // assume body can turn the full +/- 180 degrees
00667             VRmixin::pilot->curReq->landmarks);
00668   gazeHeadings = std::deque<AngTwoPi>();
00669   for ( unsigned int i = 0; i < visibleLandmarks.size(); i++ ) {
00670     Point allovec = visibleLandmarks[i]->getCentroid() - theAgent->getCentroid();
00671     gazeHeadings.push_back(AngTwoPi(allovec.atanYX()));
00672   }
00673   // Landmark headings are allocentric, but to minimize turn distance
00674   // we want to sort them by their bearing relative to the robot.
00675   std::sort(gazeHeadings.begin(), gazeHeadings.end(), BearingLessThan(robotHeading));
00676   // Reverse the list if the closest landmark is to the right instead of the left
00677   if ( gazeHeadings.size() > 1 &&
00678        abs(AngSignPi(gazeHeadings.front()-robotHeading)) > abs(AngSignPi(gazeHeadings.back()-robotHeading)) )
00679     reverse(gazeHeadings.begin(), gazeHeadings.end());
00680 }
00681 
00682 void Pilot::LocalizationUtility::PrepareForNextGazePoint::prepareForNext(std::deque<AngTwoPi> &gazeHeadings, const AngTwoPi maxTurn) {
00683   if ( gazeHeadings.empty() ) {
00684     postStateFailure();
00685     return;
00686   }
00687   const AngTwoPi curHeading = VRmixin::theAgent->getOrientation();
00688   const AngTwoPi newHeading = gazeHeadings.front();
00689   gazeHeadings.pop_front();
00690   const AngSignPi turnAmount = AngSignPi(newHeading - curHeading);
00691   // std::cout << "*** curHeading=" << curHeading << "  newHeading=" << newHeading
00692   //          << "  turnAmount=" << turnAmount << "  maxturn=" << maxTurn << std::endl;
00693   if (fabs(turnAmount) > maxTurn) {
00694     postStateSuccess();
00695     return;
00696   }
00697   MMAccessor<WaypointWalkMC> wp_acc(getMC());
00698   wp_acc->getWaypointList().clear();
00699 #ifdef TGT_IS_CREATE
00700   float const va = 1.0f;  // optimal turn speed for CREATE odometry
00701   wp_acc->addEgocentricWaypoint(0, 0, turnAmount, true, 0, va);
00702 #else
00703   // no other headless robots supported yet, so just use Create values for now
00704   float const va = 1.0f;
00705   wp_acc->addEgocentricWaypoint(0, 0, turnAmount, true, 0, va);
00706 #endif // TGT_IS_CREATE
00707   motman->setPriority(VRmixin::pilot->getWaypointwalk_id(),MotionManager::kStdPriority);
00708 }
00709 
00710 #endif // TGT_HAS_HEAD
00711 
00712 void Pilot::LocalizationUtility::Localize::doStart() {
00713   VRmixin::particleFilter->updateSensors(particleFilter->getSensorModel(),false,true);
00714   const ShapeBasedParticleFilter::particle_type &estimate = VRmixin::particleFilter->getEstimate();
00715   if ( estimate.weight < minConfidentWeight ) {
00716     std::cout << "  Particle filter bestWeight=" << estimate.weight
00717               << " < minConfidentWeight=" << minConfidentWeight << ": resample." << std::endl;
00718     VRmixin::particleFilter->updateSensors(particleFilter->getSensorModel(),false,true);
00719   }
00720   if ( estimate.weight < minConfidentWeight ) {
00721     std::cout << "  Particle filter bestWeight=" << estimate.weight
00722               << " < minConfidentWeight=" << minConfidentWeight << ": resample once more." << std::endl;
00723     VRmixin::particleFilter->updateSensors(particleFilter->getSensorModel(),false,true);
00724   }
00725   AngTwoPi heading = theAgent->getOrientation();
00726   cout << "Agent now at " << theAgent->getCentroid() << " hdg " << heading
00727        << " (= " << float(heading)*180/M_PI << " deg.)" 
00728        << "  std. dev. " << sqrt(((ShapeBasedParticleFilter*)particleFilter)->getVariance().x) << " mm, "
00729        << ((ShapeBasedParticleFilter*)particleFilter)->getVariance().y * 180 / M_PI << " deg."
00730        << "  bestWeight=" << estimate.weight
00731        << endl;
00732   VRmixin::mapBuilder->setAgent(Point(estimate.x,estimate.y,0,allocentric), estimate.theta, true);
00733   if ( VRmixin::pilot->curReq->displayIndividualParticles > 0 )
00734     VRmixin::particleFilter->displayIndividualParticles(VRmixin::pilot->curReq->displayIndividualParticles);
00735   else if ( VRmixin::pilot->curReq->displayParticles > 0 )
00736     VRmixin::particleFilter->displayParticles(VRmixin::pilot->curReq->displayParticles);
00737   else {
00738     GET_SHAPE(particles, GraphicsData, worldShS);
00739     if ( particles.isValid() )
00740       VRmixin::particleFilter->displayParticles(50);
00741   }
00742 }
00743 
00744 //================ WalkMachine ================
00745 
00746 void Pilot::WalkMachine::WalkMachine_WaypointWalker::doStart() {
00747   PilotRequest &req = *VRmixin::pilot->curReq;
00748   MMAccessor<WaypointWalkMC> wp_acc(getMC());
00749   wp_acc->getWaypointList().clear();
00750 #ifdef TGT_IS_CREATE
00751   float vx = (req.forwardSpeed > 0) ? req.forwardSpeed : 50.f;
00752   float va = (req.turnSpeed > 0) ? req.turnSpeed : 1.0f;
00753   if (req.da != 0)
00754     wp_acc->addEgocentricWaypoint(0, 0, req.da, true, vx, va);
00755   if (req.dx != 0)
00756     wp_acc->addEgocentricWaypoint(req.dx, 0, (req.dx >= 0) ? 0 : -(float)M_PI, true, vx, va);
00757 #else
00758   // these values are for Chiara
00759   float vx = (req.forwardSpeed > 0) ? req.forwardSpeed : 15.f;
00760   float va = (req.turnSpeed > 0) ? req.turnSpeed : 0.1f;
00761   float daCorrected = req.da + ((req.dx >= 0) ? 0 : -(float)M_PI);
00762   wp_acc->addEgocentricWaypoint(req.dx, req.dy, daCorrected, true, vx, va);
00763 #endif
00764   motman->setPriority(VRmixin::pilot->getWaypointwalk_id(),MotionManager::kStdPriority);
00765 }
00766 
00767 
00768 //================ WaypointWalkmachine ================
00769 
00770 void Pilot::WaypointWalkMachine::WaypointWalkMachine_WaypointWalker::doStart() {
00771   PilotRequest &req = *VRmixin::pilot->curReq;
00772   MMAccessor<WaypointWalkMC> wp_acc(getMC());
00773   if(req.clearWaypoints)
00774     wp_acc->clearWaypointList();
00775   wp_acc->appendWaypoints(*req.waypointList);
00776   motman->setPriority(VRmixin::VRmixin::pilot->getWaypointwalk_id(),MotionManager::kStdPriority);
00777 }
00778 
00779 //================ SetVelocityMachine ================
00780 
00781 void Pilot::SetVelocityMachine::SetVelocityMachine_Walker::doStart() {
00782   PilotRequest &preq = *VRmixin::pilot->curReq;
00783   MotionManager::MC_ID wid = VRmixin::pilot->getWalk_id();
00784   MMAccessor<WalkMC>(wid)->setTargetVelocity(preq.forwardSpeed, preq.strafeSpeed, preq.turnSpeed);
00785   motman->setPriority(wid,MotionManager::kStdPriority);
00786 }
00787 
00788 void Pilot::cancelVelocity() {
00789   if ( VRmixin::pilot->curReq != NULL && VRmixin::pilot->curReq->requestType == PilotTypes::setVelocity ) {
00790     MotionManager::MC_ID wid = VRmixin::pilot->getWalk_id();
00791     erouter->postEvent(EventBase(EventBase::motmanEGID, wid, EventBase::statusETID, 0));  // force WalkNode to complete
00792   }
00793   else
00794     std::cout << "Warning: call to Pilot::cancelVelocity() when not executing a setVelocity request: ignored." << std::endl;
00795 }
00796 
00797 //================ GoToShapeMachine ================
00798 
00799 const float Pilot::ExecutePlan::allowableDistanceError = 50;
00800 const float Pilot::ExecutePlan::allowableAngularError = 0.15;
00801 
00802 void Pilot::GoToShapeMachine::CheckParameters::doStart() {
00803 
00804   PilotRequest &req = *VRmixin::pilot->curReq;
00805   if ( ! req.targetShape.isValid() ) {
00806     cout << "Pilot request goToShape fails: no valid targetShape specified" << endl;
00807     postStateFailure();
00808     return;
00809   }
00810   if ( req.targetShape->isObstacle() ) {
00811     cout << "Warning: Pilot marking targetShape '" << req.targetShape->getName()
00812    << "' (" << req.targetShape->getId() << ") as non-obstacle to permit path planning." << endl;
00813     req.targetShape->setObstacle(false);
00814   }
00815   // arguments look good; okay to proceed
00816   postStateSuccess();
00817 }
00818 
00819 //================PushObjectMachine================
00820 void Pilot::PushObjectMachine::CheckParameters::doStart() {
00821 
00822   PilotRequest &req = *VRmixin::pilot->curReq;
00823   if ( ! req.objectShape.isValid() ) {
00824     cout << "Pilot request pushObject fails: no valid objectShape specified" << endl;
00825     postStateFailure();
00826     return;
00827   if ( ! req.targetShape.isValid() ) {
00828     cout << "Pilot request pushObject fails: no valid targetShape specified" << endl;
00829     postStateFailure();
00830     return;
00831   }
00832   if ( req.targetShape->isObstacle() ) {
00833     cout << "Warning: Pilot marking targetShape '" << req.targetShape->getName()
00834    << "' (" << req.targetShape->getId() << ") as non-obstacle to permit path planning." << endl;
00835     req.targetShape->setObstacle(false);
00836   }
00837   }
00838   // arguments look good; okay to proceed
00839   postStateSuccess();
00840 }
00841 
00842 
00843 //=== Pilot static variables 
00844   unsigned int Pilot::verbosity = -1U & ~Pilot::PVnavStep;
00845 
00846 } // namespace
00847 
00848 #endif

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:45 2012 by Doxygen 1.6.3