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 }
1.4.7