Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

WaypointEngine.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_WaypointEngine_h_
00003 #define INCLUDED_WaypointEngine_h_
00004 
00005 #include "IPC/ListMemBuf.h"
00006 #include "Shared/LoadSave.h"
00007 #include "Shared/Config.h"
00008 #include "Shared/get_time.h"
00009 #include "WaypointList.h"
00010 #include <cmath>
00011 
00012 //! Provides computation and management of a desired path through a series of waypoints
00013 /*! This is a generalized set of data structures and management code -
00014  *  it doesn't actually say anything about @e how you get from one
00015  *  waypoint to the other, it will just tell you where you should be
00016  *  going at any given time.
00017  *
00018  *  So, for instance, you may be interested in WaypointWalk, which
00019  *  will use a WalkMC to traverse the waypoints.  Future development
00020  *  may include a WaypointPush, to push an object along a path instead
00021  *  of just moving the body along a path.
00022  *
00023  *  Although general curves between waypoints are not supported, you
00024  *  can use either circular arcs or straight lines.
00025  *
00026  *  The Waypoint class holds the actual data about each waypoint.  You
00027  *  can specify waypoints in 3 ways: egocentric, offset, and absolute.
00028  *
00029  *  <table cellspacing=0 cellpadding=0 width="750" class="figures" align="center" border="0"><tr>
00030  *  <td class="figure"><img src="Waypoint_Ego.png"><div style="padding:10px;">
00031  *    @b Egocentric: the x and y parameters are relative to the body
00032  *    itself; x is always forward and y is always left.  Handy for
00033  *    turtle/logo style specification of instructions
00034  *  </div></td><td class="figure"><img src="Waypoint_Off.png"><div style="padding:10px;">
00035  *    @b Offset: the x and y parameters are relative to the current body
00036  *    position, but not its heading.
00037  *  </div></td><td class="figure"><img src="Waypoint_Abs.png"><div style="padding:10px;">
00038  *    @b Absolute: the x and y parameters are direct coordinates
00039  *  </div></td></tr></table>
00040  *
00041  *  These specify the @e position of the next waypoint, but we also need
00042  *  to be able to specify the @e orientation (heading) of the robot.  This
00043  *  is done by specifying an angle and a mode which controls how that
00044  *  angle is interpreted: Waypoint::angleIsRelative, which can be @c true or @c false.
00045  *
00046  *  <table cellspacing=0 cellpadding=0 width="500" class="figures" align="center" border="0"><tr>
00047  *  <td class="figure"><img src="Waypoint_AngleRel.png"><div style="padding:10px;">
00048  *  <code>angleIsRelative==</code>@b true: The angle is relative to the path, so that @c 0 will keep
00049  *  the robot pointed in the direction of travel, even when arcing.  Similarly, @f$\pi/2\ (90^\circ)@f$
00050  *  would cause the robot to walk sideways.
00051  *  </div></td><td class="figure"><img src="Waypoint_AngleAbs.png"><div style="padding:10px;">
00052  *  <code>angleIsRelative==</code>@b false: The angle is relative to the world coordinate system, so a 
00053  *  constant heading is maintained throughout execution of the path.
00054  *  </div></td></tr></table>
00055  *
00056  *  The final orientation of the robot is simply the heading it was
00057  *  facing when it reaches the end point.  To turn in place, you can
00058  *  use a (0,0) egocentric or offset waypoint with an angle parameter.
00059  *
00060  *  In order to execute curves, you can supply an arc value:
00061  *
00062  *  <table cellspacing=0 cellpadding=0 width="417" class="figures" align="center" border="0"><tr>
00063  *  <td class="figure"><img src="Waypoint_Arc.png"><div style="padding:10px;">
00064  *  Here you see the results of 3 different arc values @f$(60^\circ,120^\circ,180^\circ)@f$.  Note how
00065  *  the arc parameter corresponds to the angle of the circle which is swept.<br>
00066  *  </div></td></tr></table>
00067  *
00068  *  There are two ways to specify arcs.  The @c add*Waypoint functions
00069  *  use the position arguments to specify the <em>end point</em> of
00070  *  the arc, and the arc parameter serves to "bow" the path.  The @c
00071  *  add*Arc functions specify the <em>center of the circle</em> as the
00072  *  position, and the end point is inferred from the amount of the arc
00073  *  to sweep.
00074  *
00075  *  Beware that arcs greater than @f$180^\circ@f$ are entirely
00076  *  possible, but will create larger and larger circles which may
00077  *  cause the robot to initially start moving @e away from the
00078  *  destination.  This isn't necessarily a bad thing, but may be
00079  *  unanticipated.  Values approaching @f$2\pi\ (360^\circ)@f$ may
00080  *  cause numerical instability yielding infinitely large circles.
00081  *  Values larger than @f$2\pi\ (360^\circ)@f$ will be normalized to
00082  *  the range @f$[0,2\pi)@f$.
00083  *
00084  *  Dead reckoning is very prone to accruing error.  It is highly
00085  *  recommended that you calibrate the locomotion mechanism carefully
00086  *  (see WalkCalibration, available under the "Walk Edit" menu with a
00087  *  run-time help menu) and implement some form of localization to
00088  *  handle the inevitable drift.
00089  *
00090  *  If you have a localization module in place, you can use the
00091  *  setCurPos() function to update the position of the robot within
00092  *  the world.  WaypointEngine provides two ways to handle this
00093  *  ensuing discrepency from the path the robot had been tracing:
00094  *
00095  *  <table cellspacing=0 cellpadding=0 width="325" class="figures" align="center" border="0"><tr>
00096  *  <td class="figure"><img src="Waypoint_Error.png"><div style="padding:10px;">
00097  *  The effect of the Waypoint::trackPath flag.  When @c true, the robot will attempt to catch up
00098  *  to its "ideal" location after a perturbation.  When @c false, the robot will ignore the "ideal"
00099  *  path, and just go straight to the destination from wherever perturbations may push it.
00100  *  </div></td></tr></table>
00101  *
00102  *  trackPath is a per-waypoint setting, setTracking() sets the
00103  *  default value for any new waypoints which are thereafter created
00104  *  (the default default is false ;)
00105  *
00106  *  Waypoint list files are a fairly straightforward plain text format.
00107  *  The extension .wyp is suggested.
00108  *
00109  *  The waypoint file format is:
00110  *  - '<tt>\#WyP</tt>' - header to verify file type
00111  *  - A series of entries:
00112  *    - '<tt>max_turn_speed </tt><i>num</i>' - sets the maximum error-correction turning speed used for all following waypoints
00113  *    - '<tt>track_path </tt><i>bool</i>' - sets trackpath mode on or off for all following waypoints (see Waypoint::trackPath)
00114  *    - '<tt>add_point </tt>{<tt>ego</tt>|<tt>off</tt>|<tt>abs</tt>}<tt> </tt><i>x_val</i><tt> </tt><i>y_val</i><tt> </tt>{<tt>hold</tt>|<tt>follow</tt>}<tt> </tt><i>angle_val</i><tt> </tt><i>speed_val</i><tt> </tt><i>arc_val</i>' - adds the waypoint with the parameters given, see Waypoint, similar to add*Waypoint functions
00115  *    - '<tt>add_arc </tt>{<tt>ego</tt>|<tt>off</tt>|<tt>abs</tt>}<tt> </tt><i>x_val</i><tt> </tt><i>y_val</i><tt> </tt>{<tt>hold</tt>|<tt>follow</tt>}<tt> </tt><i>angle_val</i><tt> </tt><i>speed_val</i><tt> </tt><i>arc_val</i>' - adds the waypoint with the parameters given, see Waypoint, similar to add*Arc functions
00116  *  - '<tt>\#END</tt>' - footer to verify ending of file
00117  */
00118 
00119 class WaypointEngine : public LoadSave {
00120 public:
00121 
00122   typedef std::vector<Waypoint> WaypointList_t; //!< convenient shorthand
00123   typedef std::vector<Waypoint>::iterator WaypointListIter_t; //!< convenient shorthand
00124   typedef std::vector<Waypoint>::const_iterator WaypointListConstIter_t; //!< convenient shorthand
00125 
00126   //! constructor
00127   WaypointEngine()
00128     : LoadSave(), waypoints(), isRunning(false), isLooping(false), isTracking(false),
00129       curWaypoint(waypoints.end()), waypointTime(0), waypointDistance(0), pathLength(0), arcRadius(0),
00130       lastUpdateTime(0), Pcorr(.5f), defaultTurnSpeed((float)M_PI/30)
00131   {init();}
00132   //! constructor
00133   WaypointEngine(char * f)
00134     : LoadSave(), waypoints(), isRunning(false), isLooping(false), isTracking(false),
00135       curWaypoint(waypoints.end()), waypointTime(0), waypointDistance(0), pathLength(0), arcRadius(0),
00136       lastUpdateTime(0), Pcorr(.5f), defaultTurnSpeed((float)M_PI/30)
00137   {init(); loadFile(f); }
00138 
00139   //! returns a rough overestimate of the size needed
00140   /*! pretends we need to switch max_turn_speed and track_path on
00141     every point, and the longest options are given for every point */
00142   virtual unsigned int getBinSize() const;
00143   virtual unsigned int loadBuffer(const char buf[], unsigned int len, const char* filename=NULL);
00144   virtual unsigned int saveBuffer(char buf[], unsigned int len) const;
00145   virtual unsigned int loadFile(const char * filename) { return LoadSave::loadFile(config->motion.makePath(filename).c_str()); }
00146   virtual unsigned int saveFile(const char * filename) const { return LoadSave::saveFile(config->motion.makePath(filename).c_str()); }
00147 
00148   virtual void go();      //!< starts walking towards the first waypoint
00149   virtual void pause();   //!< halts execution of waypoint list
00150   virtual void unpause(); //!< resumes execution of waypoint list from last paused location
00151 
00152   virtual void setIsLooping(bool isl) { isLooping=isl; } //!< sets #isLooping
00153   virtual bool getIsLooping() const { return isLooping; } //!< returns #isLooping
00154 
00155   virtual WaypointList_t& getWaypointList() { return waypoints; } //!< returns a reference to #waypoints
00156   virtual const WaypointList_t& getWaypointList() const { return waypoints; } //!< returns a const reference to #waypoints
00157 
00158   virtual WaypointListIter_t getCurWaypointID() const { return curWaypoint; } //!< returns id value of current waypoint (#curWaypoint)
00159 
00160   virtual float getCurX() const { return curPos[0]; } //!< returns current x position
00161   virtual float getCurY() const { return curPos[1]; } //!< returns current y position
00162   virtual float getCurA() const { return curPos[2]; } //!< returns current heading
00163   //! sets the current position (for instance your localization module has an update)
00164   virtual void setCurPos(float x, float y, float a) {
00165     curPos[0]=x; curPos[1]=y; curPos[2]=a;
00166   }
00167 
00168   virtual void setTracking(bool b) { isTracking=b; } //!< sets the #isTracking flag, only affects future waypoints which are added, not currently listed waypoints (use getWaypointList() to modify existing waypoints)
00169   virtual bool getTracking() const { return isTracking; } //!< returns #isTracking
00170 
00171   //! call this on each opportunity to check current location and correct velocities
00172   /*! @return #isRunning for convenience of checking if anything is happening */
00173   virtual bool cycle(); 
00174 
00175   //!these are for convenience - can also directly edit the waypoint list using access from getWaypointList()
00176   //!@name Adding Waypoints
00177 
00178   //! adds a waypoint to the end of the list, allows you to specify turtle-style instructions
00179   /*! <img src="Waypoint_Ego.png">
00180    *  @param forward distance forward to move (negative to move backward of course)
00181    *  @param left distance to the left to move (negative to move right of course)
00182    *  @param angle angle of attack to use on the path
00183    *  @param angleIsRelative controls interpretation of @a angle; true means angle specifies an offset from the bearing of the target waypoint, false means maintain an absolute heading
00184    *  @param fwdSpeed is the speed to move at; millimeters per second
00185          *  @param turnSpeed is the speed to turn; radians per second */
00186   virtual void addEgocentricWaypoint(float forward, float left, float angle, bool angleIsRelative, float fwdSpeed, float turnSpeed=-1.f) {
00187     waypoints.push_back(Waypoint(forward,left,Waypoint::POSTYPE_EGOCENTRIC,angle,angleIsRelative,fwdSpeed,isTracking,turnSpeed>=0?turnSpeed:defaultTurnSpeed));
00188   }
00189   //! adds a waypoint to the end of the list, allows you to set locations relative to the location of the previous waypoint (or starting position)
00190   /*! <img src="Waypoint_Off.png">
00191    *  @param x distance delta along x axis of the waypoint
00192    *  @param y distance delta along y axis of the waypoint
00193    *  @param angle angle of attack to use on the path
00194    *  @param angleIsRelative controls interpretation of @a angle; true means angle specifies an offset from the bearing of the target waypoint, false means maintain an absolute heading
00195    *  @param fwdSpeed is the speed to move at; millimeters per second
00196          *  @param turnSpeed is the speed to turn; radians per second */
00197   virtual void addOffsetWaypoint(float x, float y, float angle, bool angleIsRelative, float fwdSpeed, float turnSpeed=-1.f) {
00198     waypoints.push_back(Waypoint(x,y,Waypoint::POSTYPE_OFFSET,angle,angleIsRelative,fwdSpeed,isTracking,turnSpeed>=0?turnSpeed:defaultTurnSpeed));
00199   }
00200   //! adds a waypoint to the end of the list, allows you to set locations relative to the world coordinate frame
00201   /*! <img src="Waypoint_Abs.png">
00202    *  @param x position along x axis of the waypoint
00203    *  @param y position along y axis of the waypoint
00204    *  @param angle angle of attack to use on the path
00205    *  @param angleIsRelative controls interpretation of @a angle; true means angle specifies an offset from the bearing of the target waypoint, false means maintain an absolute heading
00206    *  @param fwdSpeed is the speed to move at; milimeters per second
00207          *  @param turnSpeed is the speed to turn; radians per second */
00208   virtual void addAbsoluteWaypoint(float x, float y, float angle, bool angleIsRelative, float fwdSpeed, float turnSpeed=-1.f) {
00209     waypoints.push_back(Waypoint(x,y,Waypoint::POSTYPE_ABSOLUTE,angle,angleIsRelative,fwdSpeed,isTracking,turnSpeed>=0?turnSpeed:defaultTurnSpeed));
00210   }
00211 
00212   //! adds a waypoint to the end of the list, using an arcing path to get there, allows you to specify turtle-style instructions to specify the focus of the arc
00213   /*! <img src="Waypoint_Ego.png">
00214    *  If you would rather specify the ending point and then "bow" the path, try addEgocentricWaypoint() followed by setting the Waypoint::arc field directly
00215    *  @param forward distance in front of the center of the circle of the arc
00216    *  @param left distance to the left of the center of the circle of the arc
00217    *  @param angle angle of attack to use on the path
00218    *  @param angleIsRelative controls interpretation of @a angle; true means angle specifies an offset from the bearing of the target waypoint, false means maintain an absolute heading
00219    *  @param speed is the speed to move at; millimeters per second
00220    *  @param arc is the number of radians the arc fills; arcs near 0 (or multiples of 360) may cause numeric instability */
00221   virtual void addEgocentricArc(float forward, float left, float angle, bool angleIsRelative, float speed, float arc) {
00222     addEgocentricWaypoint(forward,left,angle,angleIsRelative,speed);
00223     fixArc(arc);
00224   }
00225   //! adds a waypoint to the end of the list, using an arcing path to get there, allows you to specify locations relative to previous waypoint to specify the focus of the arc
00226   /*! <img src="Waypoint_Off.png">
00227    *  If you would rather specify the ending point and then "bow" the path, try addOffsetWaypoint() followed by setting the Waypoint::arc field directly
00228    *  @param x distance delta along x of the center of the circle of the arc
00229    *  @param y distance delta along y of the center of the circle of the arc
00230    *  @param angle angle of attack to use on the path
00231    *  @param angleIsRelative controls interpretation of @a angle; true means angle specifies an offset from the bearing of the target waypoint, false means maintain an absolute heading
00232    *  @param speed is the speed to move at; millimeters per second
00233    *  @param arc is the number of radians the arc fills; arcs near 0 (or multiples of 360) may cause numeric instability */
00234   virtual void addOffsetArc(float x, float y, float angle, bool angleIsRelative, float speed, float arc) {
00235     addOffsetWaypoint(x,y,angle,angleIsRelative,speed);
00236     fixArc(arc);
00237   }
00238   //! adds a waypoint to the end of the list, using an arcing path to get there, allows you to specify absolute locations to specify the focus of the arc
00239   /*! <img src="Waypoint_Abs.png">
00240    *  If you would rather specify the ending point and then "bow" the path, try addAbsoluteWaypoint() followed by setting the Waypoint::arc field directly
00241    *  @param x position along x of the center of the circle of the arc
00242    *  @param y position along y of the center of the circle of the arc
00243    *  @param angle angle of attack to use on the path
00244    *  @param angleIsRelative controls interpretation of @a angle; true means angle specifies an offset from the bearing of the target waypoint, false means maintain an absolute heading
00245    *  @param speed is the speed to move at; millimeters per second
00246    *  @param arc is the number of radians the arc fills; arcs near 0 (or multiples of 360) may cause numeric instability */
00247   virtual void addAbsoluteArc(float x, float y, float angle, bool angleIsRelative, float speed, float arc) {
00248     addAbsoluteWaypoint(x,y,angle,angleIsRelative,speed);
00249     fixArc(arc);
00250   }
00251   
00252   virtual void appendWaypoints(const WaypointList_t wpl) {
00253     waypoints.insert(waypoints.end(), wpl.begin(), wpl.end());
00254   }
00255   
00256   virtual void clearWaypointList() {
00257     waypoints.clear();
00258   }
00259   //@}
00260   
00261   //! will set the currently active waypoint to another waypoint; correctly calculates location of intermediate waypoints so target location will be the same as if the intervening waypoints had actually been executed
00262   virtual void setTargetWaypoint(WaypointListIter_t iter);
00263   
00264   //!if @a it follows the current waypoint, applies all the waypoints between #curWaypoint and @a it and returns result as an absolute position (angle field stores heading); otherwise calls the other calcAbsoluteCoords(WaypointListIter_t, float, float, float)
00265   Waypoint calcAbsoluteCoords(WaypointListIter_t it) {
00266     //find out if 'it' is coming up, or already passed
00267     bool isAhead=false;
00268     for(WaypointListIter_t c=curWaypoint; c!=waypoints.end(); c++)
00269       if(c==it) {
00270         isAhead=true;
00271         break;
00272       }
00273     if(!isAhead)
00274       return calcAbsoluteCoords(it,pathStartPos[0],pathStartPos[1],pathStartPos[2]);
00275     Waypoint cur(targetPos[0],targetPos[1],Waypoint::POSTYPE_ABSOLUTE,targetPos[2],false,0,isTracking,defaultTurnSpeed);
00276     if(it==curWaypoint)
00277       return cur;
00278     for(WaypointListIter_t c=curWaypoint+1; c!=waypoints.end(); c++) {
00279       cur.apply(*c,eps);
00280       if(c==it)
00281         break;
00282     }
00283     return cur;
00284   }
00285 
00286   //!starts at (@a sx, @a sy, heading=@a sa) and then applies all the waypoints up through @a it and returns result as an absolute position (angle field stores heading)
00287   /*! This is replicated in WaypointList, so any updates should be made there as well */
00288   Waypoint calcAbsoluteCoords(WaypointListIter_t it,float sx, float sy, float sa) {
00289     Waypoint cur(sx,sy,Waypoint::POSTYPE_ABSOLUTE,sa,false,0,isTracking,defaultTurnSpeed);
00290     for(WaypointListIter_t c=waypoints.begin(); c!=waypoints.end(); c++ ) {
00291       cur.apply(*c,eps);
00292       if(c==it)
00293         break;
00294     }
00295     return cur;
00296   }
00297   
00298 
00299 
00300 protected:
00301   void init(); //!< basic memory initialization
00302 
00303   //! assumes the last waypoint is actually center of circle, adjusts it to be the endpoint of following @a arc radians around that circle instead
00304   void fixArc(float arc);
00305   
00306   //! based on current velocity and time since last call, dead reckons current location in #curPos
00307   /*! doesn't take acceleration into account, but should... :( */
00308   void computeCurrentPosition(unsigned int t);
00309   void checkNextWaypoint(unsigned int t);  //!< checks to see if #curPos is within #eps of #targetPos; if so, setTargetWaypoint() to next waypoint
00310   void computeIdeal(unsigned int t);       //!< computes the ideal location (#idealPos) if we were following the intended path at the intended speed
00311   void computeNewVelocity(unsigned int t); //!< computes the velocity which should be used given the current position (#curPos) relative to the ideal position (#idealPos)
00312 
00313   WaypointList_t waypoints; //!< storage for the waypoints
00314 
00315   bool isRunning;  //!< true if we're currently executing the path
00316   bool isLooping;  //!< true if we should loop when done
00317   bool isTracking; //!< new waypoints will use trackPath mode
00318   WaypointListIter_t curWaypoint;  //!< index of current waypoint
00319   unsigned int waypointTime; //!< time we started working on current waypoint
00320   float waypointDistance;    //!< distance from #sourcePos to #targetPos
00321   float pathLength;          //!< distance to be traveled from #sourcePos to #targetPos (may differ from #waypointDistance due to arcing)
00322   float arcRadius;           //!< radius of current arc, may be inf or NaN if using a straight line; can also be negative depending on direction!
00323   unsigned int lastUpdateTime; //!< time we last updated curPos
00324   float pathStartPos[3]; //!< position when started execution of current path (aka origin offset for relative positions which preceed an absolute waypoint)
00325   float sourcePos[3]; //!< source position of the robot relative to the origin, aka absolute position of previous waypoint
00326   float targetPos[3]; //!< target position of the robot relative to the origin, aka absolute position of next waypoint
00327   float idealPos[4];  //!< ideal position of the robot relative to the origin, (x, y, heading, last element is desired direction of motion)
00328   float curPos[3];    //!< current position of the robot relative to the origin
00329   float curVel[3];    //!< current velocity
00330   float eps[3];       //!< epsilon - "close enough" to register a hit on the waypoint
00331   float Pcorr;        //!< proportional correction factor for tracking path
00332   float defaultTurnSpeed;    //!< maximum turning speed for new waypoints
00333 };
00334 
00335 
00336 /*! @file
00337  * @brief Defines WaypointEngine, which provides computation and management of a desired path through a series of waypoints
00338  * @author ejt (Creator)
00339  */
00340 
00341 #endif

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