00001 /* 00002 MobileRobots Advanced Robotics Interface for Applications (ARIA) 00003 Copyright (C) 2004, 2005 ActivMedia Robotics LLC 00004 Copyright (C) 2006, 2007 MobileRobots Inc. 00005 00006 This program is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or 00009 (at your option) any later version. 00010 00011 This program is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program; if not, write to the Free Software 00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00019 00020 If you wish to redistribute ARIA under different terms, contact 00021 MobileRobots for information about a commercial version of ARIA at 00022 robots@mobilerobots.com or 00023 MobileRobots Inc, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481 00024 */ 00025 00026 #include "Aria.h" 00027 00061 /* 00062 * Action that drives the robot forward, but stops if obstacles are 00063 * detected by sonar. 00064 */ 00065 class ActionGo : public ArAction 00066 { 00067 public: 00068 // constructor, sets myMaxSpeed and myStopDistance 00069 ActionGo(double maxSpeed, double stopDistance); 00070 // destructor. does not need to do anything 00071 virtual ~ActionGo(void) {}; 00072 // called by the action resolver to obtain this action's requested behavior 00073 virtual ArActionDesired *fire(ArActionDesired currentDesired); 00074 // store the robot pointer, and it's ArSonarDevice object, or deactivate this action if there is no sonar. 00075 virtual void setRobot(ArRobot *robot); 00076 protected: 00077 // the sonar device object obtained from the robot by setRobot() 00078 ArRangeDevice *mySonar; 00079 00080 00081 /* Our current desired action: fire() modifies this object and returns 00082 to the action resolver a pointer to this object. 00083 This object is kept as a class member so that it persists after fire() 00084 returns (otherwise fire() would have to create a new object each invocation, 00085 but would never be able to delete that object). 00086 */ 00087 ArActionDesired myDesired; 00088 00089 double myMaxSpeed; 00090 double myStopDistance; 00091 }; 00092 00093 00094 /* Action that turns the robot away from obstacles detected by the 00095 * sonar. */ 00096 class ActionTurn : public ArAction 00097 { 00098 public: 00099 // constructor, sets the turnThreshold, and turnAmount 00100 ActionTurn(double turnThreshold, double turnAmount); 00101 // destructor, its just empty, we don't need to do anything 00102 virtual ~ActionTurn(void) {}; 00103 // fire, this is what the resolver calls to figure out what this action wants 00104 virtual ArActionDesired *fire(ArActionDesired currentDesired); 00105 // sets the robot pointer, also gets the sonar device, or deactivates this action if there is no sonar. 00106 virtual void setRobot(ArRobot *robot); 00107 protected: 00108 // this is to hold the sonar device form the robot 00109 ArRangeDevice *mySonar; 00110 // what the action wants to do; used by the action resover after fire() 00111 ArActionDesired myDesired; 00112 // distance at which to start turning 00113 double myTurnThreshold; 00114 // amount to turn when turning is needed 00115 double myTurnAmount; 00116 // remember which turn direction we requested, to help keep turns smooth 00117 int myTurning; // -1 == left, 1 == right, 0 == none 00118 }; 00119 00120 /* 00121 Note the use of constructor chaining with 00122 ArAction(actionName). Also note how it uses setNextArgument, which makes it so that 00123 other parts of the program could find out what parameters this action has, and possibly modify them. 00124 */ 00125 ActionGo::ActionGo(double maxSpeed, double stopDistance) : 00126 ArAction("Go") 00127 { 00128 mySonar = NULL; 00129 myMaxSpeed = maxSpeed; 00130 myStopDistance = stopDistance; 00131 setNextArgument(ArArg("maximum speed", &myMaxSpeed, "Maximum speed to go.")); 00132 setNextArgument(ArArg("stop distance", &myStopDistance, "Distance at which to stop.")); 00133 } 00134 00135 /* 00136 Override ArAction::setRobot() to get the sonar device from the robot, or deactivate this action if it is missing. 00137 You must also call ArAction::setRobot() to properly store 00138 the ArRobot pointer in the ArAction base class. 00139 */ 00140 void ActionGo::setRobot(ArRobot *robot) 00141 { 00142 ArAction::setRobot(robot); 00143 mySonar = robot->findRangeDevice("sonar"); 00144 if (robot == NULL) 00145 { 00146 ArLog::log(ArLog::Terse, "actionExample: ActionGo: Warning: I found no sonar, deactivating."); 00147 deactivate(); 00148 } 00149 } 00150 00151 /* 00152 This fire is the whole point of the action. 00153 currentDesired is the combined desired action from other actions 00154 previously processed by the action resolver. In this case, we're 00155 not interested in that, we will set our desired 00156 forward velocity in the myDesired member, and return it. 00157 00158 Note that myDesired must be a class member, since this method 00159 will return a pointer to myDesired to the caller. If we had 00160 declared the desired action as a local variable in this method, 00161 the pointer we returned would be invalid after this method 00162 returned. 00163 */ 00164 ArActionDesired *ActionGo::fire(ArActionDesired currentDesired) 00165 { 00166 double range; 00167 double speed; 00168 00169 // reset the actionDesired (must be done), to clear 00170 // its previous values. 00171 myDesired.reset(); 00172 00173 // if the sonar is null we can't do anything, so deactivate 00174 if (mySonar == NULL) 00175 { 00176 deactivate(); 00177 return NULL; 00178 } 00179 // get the range of the sonar 00180 range = mySonar->currentReadingPolar(-70, 70) - myRobot->getRobotRadius(); 00181 // if the range is greater than the stop distance, find some speed to go 00182 if (range > myStopDistance) 00183 { 00184 // just an arbitrary speed based on the range 00185 speed = range * .3; 00186 // if that speed is greater than our max, cap it 00187 if (speed > myMaxSpeed) 00188 speed = myMaxSpeed; 00189 // now set the velocity 00190 myDesired.setVel(speed); 00191 } 00192 // the range was less than the stop distance, so request stop 00193 else 00194 { 00195 myDesired.setVel(0); 00196 } 00197 // return a pointer to the actionDesired to the resolver to make our request 00198 return &myDesired; 00199 } 00200 00201 00202 /* 00203 This is the ActionTurn constructor, note the use of constructor chaining 00204 with the ArAction. also note how it uses setNextArgument, which makes 00205 it so that other things can see what parameters this action has, and 00206 set them. It also initializes the classes variables. 00207 */ 00208 ActionTurn::ActionTurn(double turnThreshold, double turnAmount) : 00209 ArAction("Turn") 00210 { 00211 myTurnThreshold = turnThreshold; 00212 myTurnAmount = turnAmount; 00213 setNextArgument(ArArg("turn threshold (mm)", &myTurnThreshold, "The number of mm away from obstacle to begin turnning.")); 00214 setNextArgument(ArArg("turn amount (deg)", &myTurnAmount, "The number of degress to turn if turning.")); 00215 myTurning = 0; 00216 } 00217 00218 /* 00219 Sets the myRobot pointer (all setRobot overloaded functions must do this), 00220 finds the sonar device from the robot, and if the sonar isn't there, 00221 then it deactivates itself. 00222 */ 00223 void ActionTurn::setRobot(ArRobot *robot) 00224 { 00225 ArAction::setRobot(robot); 00226 mySonar = robot->findRangeDevice("sonar"); 00227 if (mySonar == NULL) 00228 { 00229 ArLog::log(ArLog::Terse, "actionExample: ActionTurn: Warning: I found no sonar, deactivating."); 00230 deactivate(); 00231 } 00232 } 00233 00234 /* 00235 This is the guts of the Turn action. 00236 */ 00237 ArActionDesired *ActionTurn::fire(ArActionDesired currentDesired) 00238 { 00239 double leftRange, rightRange; 00240 // reset the actionDesired (must be done) 00241 myDesired.reset(); 00242 // if the sonar is null we can't do anything, so deactivate 00243 if (mySonar == NULL) 00244 { 00245 deactivate(); 00246 return NULL; 00247 } 00248 // Get the left readings and right readings off of the sonar 00249 leftRange = (mySonar->currentReadingPolar(0, 100) - 00250 myRobot->getRobotRadius()); 00251 rightRange = (mySonar->currentReadingPolar(-100, 0) - 00252 myRobot->getRobotRadius()); 00253 // if neither left nor right range is within the turn threshold, 00254 // reset the turning variable and don't turn 00255 if (leftRange > myTurnThreshold && rightRange > myTurnThreshold) 00256 { 00257 myTurning = 0; 00258 myDesired.setDeltaHeading(0); 00259 } 00260 // if we're already turning some direction, keep turning that direction 00261 else if (myTurning) 00262 { 00263 myDesired.setDeltaHeading(myTurnAmount * myTurning); 00264 } 00265 // if we're not turning already, but need to, and left is closer, turn right 00266 // and set the turning variable so we turn the same direction for as long as 00267 // we need to 00268 else if (leftRange < rightRange) 00269 { 00270 myTurning = -1; 00271 myDesired.setDeltaHeading(myTurnAmount * myTurning); 00272 } 00273 // if we're not turning already, but need to, and right is closer, turn left 00274 // and set the turning variable so we turn the same direction for as long as 00275 // we need to 00276 else 00277 { 00278 myTurning = 1; 00279 myDesired.setDeltaHeading(myTurnAmount * myTurning); 00280 } 00281 // return a pointer to the actionDesired, so resolver knows what to do 00282 return &myDesired; 00283 } 00284 00285 00286 00287 int main(int argc, char** argv) 00288 { 00289 Aria::init(); 00290 00291 ArSimpleConnector conn(&argc, argv); 00292 ArRobot robot; 00293 ArSonarDevice sonar; 00294 00295 // Create instances of the actions defined above, plus ArActionStallRecover, 00296 // a predefined action from Aria. 00297 ActionGo go(500, 350); 00298 ActionTurn turn(400, 10); 00299 ArActionStallRecover recover; 00300 00301 00302 // Parse all command-line arguments 00303 if(!Aria::parseArgs()) 00304 { 00305 Aria::logOptions(); 00306 return 1; 00307 } 00308 00309 // Connect to the robot 00310 if(!conn.connectRobot(&robot)) 00311 { 00312 ArLog::log(ArLog::Terse, "actionExample: Could not connect to robot! Exiting."); 00313 return 2; 00314 } 00315 00316 // Add the range device to the robot. You should add all the range 00317 // devices and such before you add actions 00318 robot.addRangeDevice(&sonar); 00319 00320 00321 // Add our actions in order. The second argument is the priority, 00322 // with higher priority actions going first, and possibly pre-empting lower 00323 // priority actions. 00324 robot.addAction(&recover, 100); 00325 robot.addAction(&go, 50); 00326 robot.addAction(&turn, 49); 00327 00328 // Enable the motors, disable amigobot sounds 00329 robot.enableMotors(); 00330 00331 // Run the robot processing cycle. 00332 // 'true' means to return if it loses connection, 00333 // after which we exit the program. 00334 robot.run(true); 00335 00336 Aria::shutdown(); 00337 return 0; 00338 }