actionExample.cpp

An example program demonstrating how to make and use new actions.

This example program creates two new actions, Go and Turn. Go will drive the robot forward safely, while Turn will avoid obstacles detected by the sonar by turning. This program also adds a predefined action from Aria which tries to recover from stalls (hit something and can't move forward) by backing and turning.

Each of these actions have the normal constructor and destructor, note that the constructors use constructor chaining to create their ArAction part correctly. Each action then also implements the essential virtual method, fire(). This fire function is called by the action resolver, and returns values that, in combination with other actions' desired behavior, determine the driving commands sent to the robot.

Also note that each of these actions override the setRobot function; these implementations obtain the sonar device from the robot in addition to doing the needed caching of the robot pointer. This is what you should do if you care about the presence or absence of a particular sensor. If you don't care about any particular sensor you could just use one of the checkRangeDevice... methods in ArRobot (there are four of them). Also note that these are very naive actions, they are simply an example of how to use actions.

See the Actions Actions section of the Aria reference manual overview for more details about actions.

Note that actions must take a small amount of time to execute, to avoid delaying the robot synchronization cycle.

00001 #include "Aria.h"
00002 
00036 /* 
00037  * Action that drives the robot forward, but stops if obstacles are
00038  * detected by sonar. 
00039  */
00040 class ActionGo : public ArAction
00041 {
00042 public:
00043   // constructor, sets myMaxSpeed and myStopDistance
00044   ActionGo(double maxSpeed, double stopDistance);
00045   // destructor. does not need to do anything
00046   virtual ~ActionGo(void) {};
00047   // called by the action resolver to obtain this action's requested behavior
00048   virtual ArActionDesired *fire(ArActionDesired currentDesired);
00049   // store the robot pointer, and it's ArSonarDevice object, or deactivate this action if there is no sonar.
00050   virtual void setRobot(ArRobot *robot);
00051 protected:
00052   // the sonar device object obtained from the robot by setRobot()
00053   ArRangeDevice *mySonar;
00054 
00055 
00056   /* Our current desired action: fire() modifies this object and returns
00057       to the action resolver a pointer to this object.
00058       This object is kept as a class member so that it persists after fire()
00059       returns (otherwise fire() would have to create a new object each invocation,
00060       but would never be able to delete that object).
00061   */
00062   ArActionDesired myDesired;
00063 
00064   double myMaxSpeed;
00065   double myStopDistance;
00066 };
00067 
00068 
00069 /* Action that turns the robot away from obstacles detected by the 
00070  * sonar. */
00071 class ActionTurn : public ArAction
00072 {
00073 public:
00074   // constructor, sets the turnThreshold, and turnAmount
00075   ActionTurn(double turnThreshold, double turnAmount);
00076   // destructor, its just empty, we don't need to do anything
00077   virtual ~ActionTurn(void) {};
00078   // fire, this is what the resolver calls to figure out what this action wants
00079   virtual ArActionDesired *fire(ArActionDesired currentDesired);
00080   // sets the robot pointer, also gets the sonar device, or deactivates this action if there is no sonar.
00081   virtual void setRobot(ArRobot *robot);
00082 protected:
00083   // this is to hold the sonar device form the robot
00084   ArRangeDevice *mySonar;
00085   // what the action wants to do; used by the action resover after fire()
00086   ArActionDesired myDesired;
00087   // distance at which to start turning
00088   double myTurnThreshold;
00089   // amount to turn when turning is needed
00090   double myTurnAmount;
00091   // remember which turn direction we requested, to help keep turns smooth
00092   int myTurning; // -1 == left, 1 == right, 0 == none
00093 };
00094 
00095 /*
00096   Note the use of constructor chaining with 
00097   ArAction(actionName). Also note how it uses setNextArgument, which makes it so that 
00098   other parts of the program could find out what parameters this action has, and possibly modify them.
00099 */
00100 ActionGo::ActionGo(double maxSpeed, double stopDistance) :
00101   ArAction("Go")
00102 {
00103   mySonar = NULL;
00104   myMaxSpeed = maxSpeed;
00105   myStopDistance = stopDistance;
00106   setNextArgument(ArArg("maximum speed", &myMaxSpeed, "Maximum speed to go."));
00107   setNextArgument(ArArg("stop distance", &myStopDistance, "Distance at which to stop."));
00108 }
00109 
00110 /*
00111   Override ArAction::setRobot() to get the sonar device from the robot, or deactivate this action if it is missing.
00112   You must also call ArAction::setRobot() to properly store
00113   the ArRobot pointer in the ArAction base class.
00114 */
00115 void ActionGo::setRobot(ArRobot *robot)
00116 {
00117   ArAction::setRobot(robot);
00118   mySonar = robot->findRangeDevice("sonar");
00119   if (robot == NULL)
00120     {
00121       ArLog::log(ArLog::Terse, "actionExample: ActionGo: Warning: I found no sonar, deactivating.");
00122       deactivate();
00123     }
00124 }
00125 
00126 /*
00127   This fire is the whole point of the action.
00128   currentDesired is the combined desired action from other actions
00129   previously processed by the action resolver.  In this case, we're
00130   not interested in that, we will set our desired 
00131   forward velocity in the myDesired member, and return it.
00132 
00133   Note that myDesired must be a class member, since this method
00134   will return a pointer to myDesired to the caller. If we had
00135   declared the desired action as a local variable in this method,
00136   the pointer we returned would be invalid after this method
00137   returned.
00138 */
00139 ArActionDesired *ActionGo::fire(ArActionDesired currentDesired)
00140 {
00141   double range;
00142   double speed;
00143 
00144   // reset the actionDesired (must be done), to clear
00145   // its previous values.
00146   myDesired.reset();
00147 
00148   // if the sonar is null we can't do anything, so deactivate
00149   if (mySonar == NULL)
00150   {
00151     deactivate();
00152     return NULL;
00153   }
00154   // get the range of the sonar
00155   range = mySonar->currentReadingPolar(-70, 70) - myRobot->getRobotRadius();
00156   // if the range is greater than the stop distance, find some speed to go
00157   if (range > myStopDistance)
00158   {
00159     // just an arbitrary speed based on the range
00160     speed = range * .3;
00161     // if that speed is greater than our max, cap it
00162     if (speed > myMaxSpeed)
00163       speed = myMaxSpeed;
00164     // now set the velocity
00165     myDesired.setVel(speed);
00166   }
00167   // the range was less than the stop distance, so request stop
00168   else
00169   {
00170     myDesired.setVel(0);
00171   }
00172   // return a pointer to the actionDesired to the resolver to make our request
00173   return &myDesired;
00174 }
00175 
00176 
00177 /*
00178   This is the ActionTurn constructor, note the use of constructor chaining 
00179   with the ArAction. also note how it uses setNextArgument, which makes 
00180   it so that other things can see what parameters this action has, and 
00181   set them.  It also initializes the classes variables.
00182 */
00183 ActionTurn::ActionTurn(double turnThreshold, double turnAmount) :
00184   ArAction("Turn")
00185 {
00186   myTurnThreshold = turnThreshold;
00187   myTurnAmount = turnAmount;
00188   setNextArgument(ArArg("turn threshold (mm)", &myTurnThreshold, "The number of mm away from obstacle to begin turnning."));
00189   setNextArgument(ArArg("turn amount (deg)", &myTurnAmount, "The number of degress to turn if turning."));
00190   myTurning = 0;
00191 }
00192 
00193 /*
00194   Sets the myRobot pointer (all setRobot overloaded functions must do this),
00195   finds the sonar device from the robot, and if the sonar isn't there, 
00196   then it deactivates itself.
00197 */
00198 void ActionTurn::setRobot(ArRobot *robot)
00199 {
00200   ArAction::setRobot(robot);
00201   mySonar = robot->findRangeDevice("sonar");
00202   if (mySonar == NULL)
00203   {
00204     ArLog::log(ArLog::Terse, "actionExample: ActionTurn: Warning: I found no sonar, deactivating.");
00205     deactivate(); 
00206   }
00207 }
00208 
00209 /*
00210   This is the guts of the Turn action.
00211 */
00212 ArActionDesired *ActionTurn::fire(ArActionDesired currentDesired)
00213 {
00214   double leftRange, rightRange;
00215   // reset the actionDesired (must be done)
00216   myDesired.reset();
00217   // if the sonar is null we can't do anything, so deactivate
00218   if (mySonar == NULL)
00219   {
00220     deactivate();
00221     return NULL;
00222   }
00223   // Get the left readings and right readings off of the sonar
00224   leftRange = (mySonar->currentReadingPolar(0, 100) - 
00225         myRobot->getRobotRadius());
00226   rightRange = (mySonar->currentReadingPolar(-100, 0) - 
00227         myRobot->getRobotRadius());
00228   // if neither left nor right range is within the turn threshold,
00229   // reset the turning variable and don't turn
00230   if (leftRange > myTurnThreshold && rightRange > myTurnThreshold)
00231   {
00232     myTurning = 0;
00233     myDesired.setDeltaHeading(0);
00234   }
00235   // if we're already turning some direction, keep turning that direction
00236   else if (myTurning)
00237   {
00238     myDesired.setDeltaHeading(myTurnAmount * myTurning);
00239   }
00240   // if we're not turning already, but need to, and left is closer, turn right
00241   // and set the turning variable so we turn the same direction for as long as
00242   // we need to
00243   else if (leftRange < rightRange)
00244   {
00245     myTurning = -1;
00246     myDesired.setDeltaHeading(myTurnAmount * myTurning);
00247   }
00248   // if we're not turning already, but need to, and right is closer, turn left
00249   // and set the turning variable so we turn the same direction for as long as
00250   // we need to
00251   else 
00252   {
00253     myTurning = 1;
00254     myDesired.setDeltaHeading(myTurnAmount * myTurning);
00255   }
00256   // return a pointer to the actionDesired, so resolver knows what to do
00257   return &myDesired;
00258 }
00259 
00260 
00261 
00262 int main(int argc, char** argv)
00263 {
00264   Aria::init();
00265 
00266   ArSimpleConnector conn(&argc, argv);
00267   ArRobot robot;
00268   ArSonarDevice sonar;
00269 
00270   // Create instances of the actions defined above, plus ArActionStallRecover, 
00271   // a predefined action from Aria.
00272   ActionGo go(500, 350);
00273   ActionTurn turn(400, 10);
00274   ArActionStallRecover recover;
00275 
00276     
00277   // Parse all command-line arguments
00278   if(!Aria::parseArgs())
00279   {
00280     Aria::logOptions();
00281     return 1;
00282   }
00283   
00284   // Connect to the robot
00285   if(!conn.connectRobot(&robot))
00286   {
00287     ArLog::log(ArLog::Terse, "actionExample: Could not connect to robot! Exiting.");
00288     return 2;
00289   }
00290 
00291   // Add the range device to the robot. You should add all the range 
00292   // devices and such before you add actions
00293   robot.addRangeDevice(&sonar);
00294 
00295  
00296   // Add our actions in order. The second argument is the priority, 
00297   // with higher priority actions going first, and possibly pre-empting lower
00298   // priority actions.
00299   robot.addAction(&recover, 100);
00300   robot.addAction(&go, 50);
00301   robot.addAction(&turn, 49);
00302 
00303   // Enable the motors, disable amigobot sounds
00304   robot.enableMotors();
00305 
00306   // Run the robot processing cycle.
00307   // 'true' means to return if it loses connection,
00308   // after which we exit the program.
00309   robot.run(true);
00310   
00311   Aria::shutdown();
00312   return 0;
00313 }

Generated on Fri Jul 31 12:36:37 2009 for Aria by  doxygen 1.4.7