00001 /** 00002 \file Robots/LoBot/io/LoRoombaCM.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::RoombaCM class. 00005 */ 00006 00007 // //////////////////////////////////////////////////////////////////// // 00008 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00009 // University of Southern California (USC) and the iLab at USC. // 00010 // See http://iLab.usc.edu for information about this project. // 00011 // //////////////////////////////////////////////////////////////////// // 00012 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00013 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00014 // in Visual Environments, and Applications'' by Christof Koch and // 00015 // Laurent Itti, California Institute of Technology, 2001 (patent // 00016 // pending; application number 09/912,225 filed July 23, 2001; see // 00017 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00018 // //////////////////////////////////////////////////////////////////// // 00019 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00022 // redistribute it and/or modify it under the terms of the GNU General // 00023 // Public License as published by the Free Software Foundation; either // 00024 // version 2 of the License, or (at your option) any later version. // 00025 // // 00026 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00027 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00028 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00029 // PURPOSE. See the GNU General Public License for more details. // 00030 // // 00031 // You should have received a copy of the GNU General Public License // 00032 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00033 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00034 // Boston, MA 02111-1307 USA. // 00035 // //////////////////////////////////////////////////////////////////// // 00036 // 00037 // Primary maintainer for this file: mviswana usc edu 00038 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/io/LoRoombaCM.C $ 00039 // $Id: LoRoombaCM.C 13783 2010-08-12 19:36:05Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/io/LoRoombaCM.H" 00046 #include "Robots/LoBot/control/LoTurnArbiter.H" 00047 #include "Robots/LoBot/config/LoConfigHelpers.H" 00048 00049 #include "Robots/LoBot/thread/LoShutdown.H" 00050 #include "Robots/LoBot/thread/LoPause.H" 00051 00052 #include "Robots/LoBot/misc/LoExcept.H" 00053 #include "Robots/LoBot/misc/singleton.hh" 00054 00055 #include "Robots/LoBot/util/LoMath.H" 00056 #include "Robots/LoBot/util/LoBits.H" 00057 #include "Robots/LoBot/util/LoTime.H" 00058 00059 #include "Robots/LoBot/irccm/LoOpenInterface.h" 00060 00061 // INVT utilities 00062 #include "Util/log.H" 00063 00064 // Standard C++ headers 00065 #include <algorithm> 00066 #include <vector> 00067 #include <utility> 00068 00069 // Standard C headers 00070 #include <ctype.h> 00071 00072 //------------------------------ MACROS --------------------------------- 00073 00074 // Sometimes, during development, the motors may not be available (e.g., 00075 // when writing code at home instead of at iLab). Or, we may simply not 00076 // want to use the actual motors (e.g., we're testing some behaviour and 00077 // only want to see/visualize its votes without driving the motors). 00078 // 00079 // In these situations, it can be useful to have a convenient dummy motor 00080 // system available. The following symbol can be used for the 00081 // above-mentioned purpose. 00082 // 00083 // Un/comment following line when motors are un/available. 00084 //#define LOBOT_MOTOR_DEVMODE 1 00085 00086 // As explained in the header file, this class implements a separate 00087 // thread that queues commands and then sends them one-by-one to the 00088 // low-level controller whenever the low-level queries it for another 00089 // command to execute. This design is necessitated by the fact that the 00090 // iRobot Create's Command Module sports a single USART that must be 00091 // multiplexed between its USB port and the robot's internal serial port. 00092 // 00093 // Thus, the high-level portion of the motor system is forced to buffer 00094 // commands before despatching them to the low-level. Needless to say, if 00095 // this buffer of pending motor commands becomes too large (because, say, 00096 // the low-level is operating somewhat sluggishly and hasn't been issuing 00097 // its command queries often enough), we could end up with the robot 00098 // executing very old/outdated commands that do not at all reflect the 00099 // current situation on the ground. 00100 // 00101 // Therefore, it would be a good idea to restrict the size of the pending 00102 // commands buffer. This constant specifies the maximum number of 00103 // commands that can be held in the pending buffer. Once this number is 00104 // reached, new commands that get queued will result in the oldest one 00105 // being knocked out. 00106 static const unsigned int LOBOT_MAX_PENDING = 4 ; 00107 00108 //----------------------------- NAMESPACE ------------------------------- 00109 00110 namespace lobot { 00111 00112 //-------------------------- KNOB TWIDDLING ----------------------------- 00113 00114 /// This local class encapsulates various parameters that can be used 00115 /// to tweak different aspects of the motor interface. 00116 namespace { 00117 00118 class Params : public singleton<Params> { 00119 /// The robot's motors are controlled by sending commands over a 00120 /// serial port. This setting specifies the serial port device that 00121 /// should be used. 00122 std::string m_device ; 00123 00124 /// This setting specifies the serial port communication rate. 00125 int m_baud_rate ; 00126 00127 /// The iRobot Create/Roomba turns by using an appropriate turn 00128 /// radius. However, the higher layers of the Robolocust controller 00129 /// work in terms of a steering direction rather than a turn 00130 /// radius. Therefore, we have to somehow convert the high-level 00131 /// steering angles to corresponding low-level turn radii. 00132 /// 00133 /// This conversion is rather ad hoc. We simply perform a linear 00134 /// interpolation on the magnitude of the turn angle, which would 00135 /// be in the range [0, T] (where T is the turn_max parameter 00136 /// specified in the turn_arbiter section), to get a number in the 00137 /// range [M, m], where m and M and are, respectively, the minimum 00138 /// and maximum turn radii we want. 00139 /// 00140 /// The Create/Roomba supports turn radii of 0 to 2000mm. However, 00141 /// these min and max parameters should be smaller than the above 00142 /// range, i.e., min_turn_radius should be > zero and 00143 /// max_turn_radius should be < 2000. 00144 /// 00145 /// Both parameters are specified in millimeters. 00146 int m_min_turn_radius, m_max_turn_radius ; 00147 00148 /// For the Roomba platform, we compute the robot's current speed 00149 /// by asking the Roomba how much distance it has covered since 00150 /// the previous update of the speed. We then divide this distance 00151 /// by the amount of time that elapsed since the previous update. 00152 /// 00153 /// For the most part this computation works quite well, with the 00154 /// computed values being quite close to the actual/requested 00155 /// speed. However, there is enough jitteriness in these speed 00156 /// readings to warrant a simple low-pass filter to smooth out the 00157 /// kinks. 00158 /// 00159 /// The above-mentioned low-pass filter is effected by using a 00160 /// weighted moving average. This setting specifies the number of 00161 /// previous readings that should be considered for computing this 00162 /// average. 00163 /// 00164 /// NOTE: To turn the speed filter off, set this config value to 00165 /// one. 00166 int m_speed_filter_size ; 00167 00168 /// The Roomba platform is equipped with a pair of Sharp GP2D15 IR 00169 /// proximity detectors on its rear to help the robot avoid bumping 00170 /// into things as it backs up. However, these two sensors have to be 00171 /// explicitly turned on. This flag can be used to configure the rear 00172 /// bump sensors. 00173 bool m_enable_rear_bumps ; 00174 00175 /// When the Roomba platform's rear bump sensors are enabled, the 00176 /// robot will respond to rear bump events in one of two ways: 00177 /// 00178 /// - just stop the robot when either of the two rear bumpers is 00179 /// active 00180 /// 00181 /// - stop the robot, spin it in-place a small amount either 00182 /// clockwise or counterclockwise depending on which side the 00183 /// obstacle appears and then move it forward a little bit 00184 /// 00185 /// By default, the low-level controller will respond to rear bump 00186 /// events by simply stopping the robot. However, this flag can be 00187 /// used to have the robot spin and move-up as well. 00188 bool m_rear_bumps_spin ; 00189 00190 /// Users can configure Robolocust to print useful info such as 00191 /// current speed and heading every time the robot's sensorimotor 00192 /// system gets updated. By default, the Robolocust controller 00193 /// prints nothing. To enable this printing, switch on this flag. 00194 bool m_print_status ; 00195 00196 /// Private constructor because this is a singleton. 00197 Params() ; 00198 00199 // Boilerplate code to make generic singleton design pattern work 00200 friend class singleton<Params> ; 00201 00202 public: 00203 /// Accessing the various parameters. 00204 //@{ 00205 static const std::string& device() {return instance().m_device ;} 00206 static int baud_rate() {return instance().m_baud_rate ;} 00207 static int min_turn_radius() {return instance().m_min_turn_radius ;} 00208 static int max_turn_radius() {return instance().m_max_turn_radius ;} 00209 static int speed_filter_size() {return instance().m_speed_filter_size;} 00210 static bool enable_rear_bumps() {return instance().m_enable_rear_bumps;} 00211 static bool rear_bumps_spin() {return instance().m_rear_bumps_spin ;} 00212 static bool print_status() {return instance().m_print_status ;} 00213 //@} 00214 } ; 00215 00216 // Parameters initialization 00217 Params::Params() 00218 : m_device(robot_conf<std::string>("serial_port", "/dev/ttyUSB0")), 00219 m_baud_rate(clamp(robot_conf("baud_rate", 57600), 50, 115200)), 00220 m_min_turn_radius(clamp(robot_conf("min_turn_radius", 200), 100, 500)), 00221 m_max_turn_radius(clamp(robot_conf("max_turn_radius", 1000), 00222 m_min_turn_radius + 100, 2000)), 00223 m_speed_filter_size(clamp(robot_conf("speed_filter_size", 10), 1, 100)), 00224 m_enable_rear_bumps(robot_conf("enable_rear_bumps", false)), 00225 m_rear_bumps_spin(robot_conf("rear_bumps_spin", false)), 00226 m_print_status(robot_conf("print_status", false)) 00227 {} 00228 00229 } // end of local anonymous namespace encapsulating above helper class 00230 00231 //-------------------------- INITIALIZATION ----------------------------- 00232 00233 RoombaCM::RoombaCM(const ModelManager& mgr) 00234 : base(mgr, Params::device(), Params::baud_rate()), 00235 m_prev_time_stamp(0), 00236 m_speed_filter(Params::speed_filter_size()), 00237 #ifdef LOBOT_MOTOR_DEVMODE // no Comm thread 00238 m_comm(0) 00239 #else 00240 m_comm(new Comm(&m_serial)) 00241 #endif 00242 {} 00243 00244 //-------------------------- MOTOR COMMANDS ----------------------------- 00245 00246 // The iRobot Create/Roomba already has built-in PID, etc. and doesn't 00247 // need (or even support?) drive commands in terms of PWM. Therefore, we 00248 // ignore the PWM parameter. 00249 // 00250 // For the drive speed, since the Roomba works in mm/s and supports 00251 // speeds of up to 500mm/s, we need to convert the input speed, which is 00252 // in m/s, to mm/s and ensure that it is in the proper range. 00253 void RoombaCM::drive(float speed, int) 00254 { 00255 if (speed > 0) 00256 send_roomba(LOBOT_CMD_FORWARD, clamp(round( speed * 1000), 0, 500)) ; 00257 else if (speed < 0) 00258 send_roomba(LOBOT_CMD_REVERSE, clamp(round(-speed * 1000), 0, 500)) ; 00259 else { // speed == zero ==> stop the robot 00260 send_roomba(LOBOT_CMD_STOP) ; 00261 m_speed_filter.reset() ; 00262 } 00263 } 00264 00265 // To turn the iRobot Create, we have to specify the desired turn radius 00266 // to its drive command. However, the higher (C++) layers of the 00267 // Robolocust controller work in terms of steering directions. Therefore, 00268 // we need to somehow convert steering directions to appropriate turn 00269 // radii. 00270 // 00271 // This method achieves the above-mentioned conversion, albeit in a 00272 // rather ad hoc manner. We simply use a linear interpolation of (the 00273 // absolute value of) the turn direction, which is in the range [0, T], 00274 // where T is the maximum steering angle supported by the turn arbiter, 00275 // to a corresponding number in the range [M, m], where m and M are the 00276 // minimum and maximum turn radii we would like. 00277 // 00278 // NOTE: We go from [0, T] to [M, m], i.e., larger turn angles get 00279 // converted to smaller turn radii. This makes sense because larger turn 00280 // angles mean hard turns, which are effected by smaller turn radii. 00281 // Similarly, smaller turn angles (medium or soft turns) will be effected 00282 // by larger turn radii. 00283 void RoombaCM::turn(float direction) 00284 { 00285 const float T = TurnArbiter::turn_max() ; 00286 const int m = Params::min_turn_radius() ; 00287 const int M = Params::max_turn_radius() ; 00288 00289 int turn_radius = round(M + abs(direction) * (m - M)/T) ; 00290 00291 if (direction < 0) // negative angle ==> turn right 00292 send_roomba(LOBOT_CMD_RIGHT, clamp(turn_radius, 100, 2000)) ; 00293 else if (direction > 0) // positive angle ==> turn left 00294 send_roomba(LOBOT_CMD_LEFT, clamp(turn_radius, 100, 2000)) ; 00295 else // direction == zero ==> drive straight ahead 00296 send_roomba(LOBOT_CMD_STRAIGHT) ; 00297 } 00298 00299 // The Roomba platform supports in-place turns 00300 void RoombaCM::spin(float angle) 00301 { 00302 // Massage spin amount to lie within [-360, 360] 00303 int rotation = round(clamp_angle(angle)) ; 00304 if (angle < 0) 00305 rotation -= 360 ; 00306 00307 // Then, simply pass to low-level controller 00308 if (rotation) 00309 send_roomba(LOBOT_CMD_SPIN, rotation) ; 00310 } 00311 00312 void RoombaCM::off() 00313 { 00314 drive(0) ; 00315 } 00316 00317 //--------------------------- ROBOT STATUS ------------------------------ 00318 00319 // Quick helper to tell whether the turn radius returned by the Roomba 00320 // corresponds to driving straight ahead or not. 00321 static inline bool is_straight(int radius) 00322 { 00323 return (radius == LOBOT_OI_DRIVE_STRAIGHT) 00324 || (radius == LOBOT_OI_DRIVE_STRAIGHT2) 00325 || (radius == 0) ; 00326 } 00327 00328 // Main thread's update cycle to retrieve robot's current state. 00329 // 00330 // The low-level Command Module control program keeps streaming sensor 00331 // data to the high level. The Comm thread takes care of buffering these 00332 // sensor packets. So, in the main thread, we only need to query the Comm 00333 // thread for the next pending sensor packet. 00334 #ifdef LOBOT_MOTOR_DEVMODE 00335 00336 bool RoombaCM::update_sensors(){return false ;} 00337 00338 #else 00339 00340 bool RoombaCM::update_sensors() 00341 { 00342 bool new_sensor_packet_available = false ; 00343 00344 Comm::Sensors S ; 00345 if (m_comm->sensors(&S)) 00346 { 00347 new_sensor_packet_available = true ; 00348 time_stamp(S.time_stamp) ; 00349 00350 int dist = make_word(S.bytes[LOBOT_SENSORS_DISTANCE_HI], 00351 S.bytes[LOBOT_SENSORS_DISTANCE_LO]) ; 00352 float vel = dist/static_cast<float>(S.time_stamp - m_prev_time_stamp) ; 00353 m_speed_filter.add(vel) ; 00354 speed(m_speed_filter.value()) ; 00355 m_prev_time_stamp = S.time_stamp ; 00356 00357 const float T = TurnArbiter::turn_max() ; 00358 const int m = Params::min_turn_radius() ; 00359 const int M = Params::max_turn_radius() ; 00360 int turn_radius = make_word(S.bytes[LOBOT_SENSORS_REQUESTED_RADIUS_HI], 00361 S.bytes[LOBOT_SENSORS_REQUESTED_RADIUS_LO]) ; 00362 if (is_straight(turn_radius)) 00363 heading(0) ; 00364 else 00365 heading(sign(turn_radius) * (abs(turn_radius) - M) * T/(m - M)) ; 00366 00367 /* 00368 LERROR("sensor packet [dvrt]: %5dmm %6.3fm/s %6dmm %14lldms", 00369 dist, vel, turn_radius, S.time_stamp) ; 00370 // */ 00371 00372 const int bumps = S.bytes[LOBOT_SENSORS_BUMPS] ; 00373 bump_left (bumps & LOBOT_OI_BUMP_LEFT) ; 00374 bump_right(bumps & LOBOT_OI_BUMP_RIGHT) ; 00375 bump_rear_left (bumps & LOBOT_BUMP_REAR_LEFT) ; 00376 bump_rear_right(bumps & LOBOT_BUMP_REAR_RIGHT) ; 00377 00378 wheel_drop_left( 00379 S.bytes[LOBOT_SENSORS_WHEEL_DROPS] & LOBOT_OI_WHEEL_DROP_LEFT) ; 00380 wheel_drop_right( 00381 S.bytes[LOBOT_SENSORS_WHEEL_DROPS] & LOBOT_OI_WHEEL_DROP_RIGHT) ; 00382 wheel_drop_caster( 00383 S.bytes[LOBOT_SENSORS_WHEEL_DROPS] & LOBOT_OI_WHEEL_DROP_CASTER) ; 00384 00385 wall(S.bytes[LOBOT_SENSORS_WALL]) ; 00386 virtual_wall(S.bytes[LOBOT_SENSORS_VIRTUAL_WALL]) ; 00387 wall_signal(make_uword(S.bytes[LOBOT_SENSORS_WALL_SIGNAL_HI], 00388 S.bytes[LOBOT_SENSORS_WALL_SIGNAL_LO])) ; 00389 00390 cliff_left (S.bytes[LOBOT_SENSORS_CLIFF_LEFT]) ; 00391 cliff_right(S.bytes[LOBOT_SENSORS_CLIFF_RIGHT]) ; 00392 cliff_front_left (S.bytes[LOBOT_SENSORS_CLIFF_FRONT_LEFT]) ; 00393 cliff_front_right(S.bytes[LOBOT_SENSORS_CLIFF_FRONT_RIGHT]) ; 00394 00395 cliff_left_signal( 00396 make_uword(S.bytes[LOBOT_SENSORS_CLIFF_LEFT_SIGNAL_HI], 00397 S.bytes[LOBOT_SENSORS_CLIFF_LEFT_SIGNAL_LO])) ; 00398 cliff_right_signal( 00399 make_uword(S.bytes[LOBOT_SENSORS_CLIFF_RIGHT_SIGNAL_HI], 00400 S.bytes[LOBOT_SENSORS_CLIFF_RIGHT_SIGNAL_LO])) ; 00401 cliff_front_left_signal( 00402 make_uword(S.bytes[LOBOT_SENSORS_CLIFF_FRONT_LEFT_SIGNAL_HI], 00403 S.bytes[LOBOT_SENSORS_CLIFF_FRONT_LEFT_SIGNAL_LO])) ; 00404 cliff_front_right_signal( 00405 make_uword(S.bytes[LOBOT_SENSORS_CLIFF_FRONT_RIGHT_SIGNAL_HI], 00406 S.bytes[LOBOT_SENSORS_CLIFF_FRONT_RIGHT_SIGNAL_LO])) ; 00407 00408 infrared(S.bytes[LOBOT_SENSORS_INFRARED_BYTE] & 0xFF) ; 00409 distance(dist) ; 00410 angle(make_word(S.bytes[LOBOT_SENSORS_ANGLE_HI], 00411 S.bytes[LOBOT_SENSORS_ANGLE_LO])) ; 00412 spin_flag(S.bytes[LOBOT_SENSORS_SPIN_FLAG]) ; 00413 00414 battery_charge(make_uword(S.bytes[LOBOT_SENSORS_BATTERY_CHARGE_HI], 00415 S.bytes[LOBOT_SENSORS_BATTERY_CHARGE_LO])) ; 00416 requested_speed(make_word(S.bytes[LOBOT_SENSORS_REQUESTED_SPEED_HI], 00417 S.bytes[LOBOT_SENSORS_REQUESTED_SPEED_LO])) ; 00418 requested_radius(turn_radius) ; 00419 } 00420 00421 if (Params::print_status()) 00422 LERROR("drive = [%7.3f]; turn = [%6.1f]", 00423 current_speed(), current_heading()) ; 00424 00425 return new_sensor_packet_available ; 00426 } 00427 00428 #endif 00429 00430 //--------------------- LOW-LEVEL MOTOR COMMANDS ------------------------ 00431 00432 #ifdef LOBOT_MOTOR_DEVMODE 00433 00434 // Development mode dummy 00435 class fake_roomba { 00436 short int m_speed, m_turn_radius ; 00437 public: 00438 fake_roomba() ; 00439 void send(int cmd) ; 00440 void send(int cmd, short int param) ; 00441 int recv(int cmd) ; 00442 } ; 00443 00444 fake_roomba::fake_roomba() 00445 : m_speed(0), m_turn_radius(0x8000) 00446 {} 00447 00448 void fake_roomba::send(int cmd) 00449 { 00450 switch (cmd) 00451 { 00452 case LOBOT_CMD_STOP: 00453 m_speed = 0 ; 00454 m_turn_radius = 0x8000 ; 00455 break ; 00456 case LOBOT_CMD_STRAIGHT: 00457 m_turn_radius = 0x8000 ; 00458 break ; 00459 } 00460 } 00461 00462 void fake_roomba::send(int cmd, short int param) 00463 { 00464 switch (cmd) 00465 { 00466 case LOBOT_CMD_FORWARD: 00467 m_speed = param ; 00468 m_turn_radius = 0x8000 ; 00469 break ; 00470 case LOBOT_CMD_REVERSE: 00471 m_speed = -param ; 00472 m_turn_radius = 0x8000 ; 00473 break ; 00474 case LOBOT_CMD_LEFT: 00475 m_turn_radius = param ; 00476 break ; 00477 case LOBOT_CMD_RIGHT: 00478 m_turn_radius = -param ; 00479 break ; 00480 } 00481 } 00482 00483 int fake_roomba::recv(int cmd) 00484 { 00485 return -1 ; 00486 } 00487 00488 static fake_roomba roomba ; 00489 00490 void RoombaCM::send_roomba(int cmd, int param) 00491 { 00492 roomba.send(cmd, param) ; 00493 } 00494 00495 #else // the real iRobot Create/Roomba interface 00496 00497 void RoombaCM::send_roomba(int cmd, int param) 00498 { 00499 m_comm->buffer(cmd, param) ; 00500 } 00501 00502 #endif // LOBOT_MOTOR_DEVMODE 00503 00504 //---------------- LOW-LEVEL COMMUNICATIONS INTERFACE ------------------- 00505 00506 // Constructor 00507 RoombaCM::Comm::Comm(lobot::Serial* S) 00508 : m_serial(S) 00509 { 00510 start("roomba_comm_thread") ; 00511 00512 if (Params::enable_rear_bumps()) 00513 buffer(LOBOT_CMD_ENABLE_REAR_BUMPS, Params::rear_bumps_spin()) ; 00514 } 00515 00516 // Command constructor 00517 RoombaCM::Comm::Cmd::Cmd(int cmd, int param) 00518 { 00519 bytes[0] = (cmd & 0x00FF); // the command code 00520 bytes[1] = (param & 0xFF00) >> 8 ; // parameter's high byte 00521 bytes[2] = (param & 0x00FF) ; // parameter's low byte 00522 bytes[3] = bytes[0] ^ bytes[1] ^ bytes[2] ; // XOR parity checksum 00523 } 00524 00525 // Command copy constructor 00526 RoombaCM::Comm::Cmd::Cmd(const RoombaCM::Comm::Cmd::Cmd& C) 00527 { 00528 std::copy(C.bytes, C.bytes + LOBOT_CMD_SIZE, bytes) ; 00529 } 00530 00531 // Sensors constructors 00532 RoombaCM::Comm::Sensors::Sensors() 00533 : time_stamp(0) 00534 { 00535 std::fill(bytes, bytes + LOBOT_SENSORS_SIZE, 0) ; 00536 } 00537 00538 RoombaCM::Comm::Sensors::Sensors(const char sensor_data[]) 00539 : time_stamp(current_time()) 00540 { 00541 std::copy(sensor_data, sensor_data + LOBOT_SENSORS_SIZE, bytes) ; 00542 } 00543 00544 // Sensors copy constructor 00545 RoombaCM::Comm::Sensors::Sensors(const RoombaCM::Comm::Sensors& S) 00546 : time_stamp(S.time_stamp) 00547 { 00548 std::copy(S.bytes, S.bytes + LOBOT_SENSORS_SIZE, bytes) ; 00549 } 00550 00551 // Sensors assignment operator 00552 RoombaCM::Comm::Sensors& 00553 RoombaCM::Comm::Sensors:: 00554 operator=(const RoombaCM::Comm::Sensors& S) 00555 { 00556 if (&S != this) { 00557 time_stamp = S.time_stamp ; 00558 std::copy(S.bytes, S.bytes + LOBOT_SENSORS_SIZE, bytes) ; 00559 } 00560 return *this ; 00561 } 00562 00563 // Add a new command to be sent to the low-level controller to the 00564 // internal command queue. 00565 void RoombaCM::Comm::buffer(int cmd, int param) 00566 { 00567 AutoMutex M(m_cmd_mutex) ; 00568 m_commands.push(Cmd(cmd, param)) ; 00569 if (m_commands.size() > LOBOT_MAX_PENDING) 00570 m_commands.pop() ; 00571 } 00572 00573 // Return next pending sensor packet 00574 bool RoombaCM::Comm::sensors(RoombaCM::Comm::Sensors* s) 00575 { 00576 bool sensor_data_available = false ; 00577 00578 AutoMutex M(m_sensors_mutex) ; 00579 if (! m_sensors.empty()) { 00580 *s = m_sensors.front() ; 00581 m_sensors.pop() ; 00582 sensor_data_available = true ; 00583 } 00584 return sensor_data_available ; 00585 } 00586 00587 // Send next pending command to low-level Command Module control program 00588 // for further processing by Create robot. 00589 void RoombaCM::Comm::send_cmd() 00590 { 00591 Cmd C(Pause::is_set() ? LOBOT_CMD_STOP : LOBOT_CMD_NOP) ; 00592 00593 // Inner block to ensure mutex is released when no longer required 00594 { 00595 AutoMutex M(m_cmd_mutex) ; 00596 if (! m_commands.empty()) { 00597 C = m_commands.front() ; 00598 m_commands.pop() ; 00599 } 00600 } 00601 00602 /* 00603 LERROR("sending [%02hhX (%c) %02hhX %02hhX %02hhX] to Command Module", 00604 C.bytes[0], C.bytes[0], C.bytes[1], C.bytes[2], C.bytes[3]) ; 00605 // */ 00606 m_serial->write(C.bytes, LOBOT_CMD_SIZE) ; 00607 } 00608 00609 // Store sensor data sent by low-level Command Module control program 00610 void RoombaCM::Comm::recv_sensors() 00611 { 00612 // Ideally, we would simply issue one call to the serial port read 00613 // function asking it to return the requisite number of bytes. 00614 // Unfortunately, that doesn't always work. Often, it reads less than 00615 // the full number of bytes sent by the low-level controller despite 00616 // the fact that all the bytes really are present in the serial 00617 // "stream." Then, of course, the Comm thread's main loop swallows up 00618 // the remaining bytes (thinking of them as bogus ACK messages from 00619 // the low-level instead of as bytes that are part of the sensor 00620 // data), which eventually leads the update function to assume that 00621 // no sensor data was received. 00622 // 00623 // Don't quite know why a single read doesn't work. Perhaps there's 00624 // some low-level handshaking going on between the Command Module's 00625 // USART buffer and the computer's serial buffer which causes the 00626 // sensor data bytes to be sent in erratic chunks? Who knows... 00627 // 00628 // Whatever the reason, the workaround is reasonably straightforward: 00629 // simply keep issuing serial port read operations until the required 00630 // number of bytes have been read. This bit of hackery seems to work 00631 // quite well. 00632 // 00633 // DEVNOTE: The loop to keep reading until the specified number of 00634 // bytes has been received is in the lobot::Serial::read_full() API. 00635 char sensors[LOBOT_SENSORS_SIZE] ; 00636 m_serial->read_full(sensors, LOBOT_SENSORS_SIZE) ; 00637 00638 // Now that all the sensor data bytes are in place, we store them in a 00639 // RoombaCM::Comm::Sensors structure for later retrieval by the main 00640 // thread (see RoombaCM::update()). 00641 AutoMutex M(m_sensors_mutex) ; 00642 m_sensors.push(Sensors(sensors)) ; 00643 if (m_sensors.size() > LOBOT_MAX_PENDING) 00644 m_sensors.pop() ; 00645 //LERROR("%lu sensor packets pending", m_sensors.size()) ; 00646 } 00647 00648 // The low-level controller sends several different acknowledgement 00649 // messages, most of which are of little concern to the high-level 00650 // controller (as of now, i.e., circa end-Jan 2010). So, we simply 00651 // discard those acknowledgements. 00652 // 00653 // As the low level's acknowledgement messages are often accompanied by 00654 // several data bytes, discarding the entire message involves reading 00655 // those bytes so as to clear them from the serial port. Note that we 00656 // could simply flush the serial port's input queue. However, that 00657 // doesn't always work. See the comment in the recv_sensors() function. 00658 // That is why we have to explicitly read the full number of data bytes 00659 // for each acknowlegdement to get the whole thing out of the way. 00660 // 00661 // This helper class implements a map that specifies the number of data 00662 // bytes to be read and discarded for each different acknowledgement 00663 // message. Using this map obviates the need for a long switch-case 00664 // construct in which every unnecessary acknowledgement is handled the 00665 // same way, viz., by eating the requisite number of bytes. 00666 // 00667 // DEVNOTE: We could use an STL map here. However, since there are a 00668 // fairly small number of entries in this map, we simply code it using an 00669 // array and a linear search function because an STL map has extra 00670 // overhead (implementing binary tree, etc.). If the number of 00671 // acknowledgements becomes large, then it might make sense to 00672 // reimplement this helper using an STL map. 00673 class EatMap { 00674 // The map contains entries specifying the number of data bytes for 00675 // each acknowledgement message sent by the low-level controller. 00676 typedef std::pair<int, int> MapEntry ; 00677 std::vector<MapEntry> m_map ; 00678 00679 // Search for the specified acknowledgement message and return the 00680 // corresponding number of data bytes. Returns zero if the map doesn't 00681 // contain the requested info (i.e., bad/unknown ack ID). 00682 int lookup(int ack_id) const ; 00683 00684 public: 00685 // Inititalize the map. 00686 EatMap() ; 00687 00688 // Check if the map contains the number of bytes corresponding to the 00689 // given acknowledgement message. 00690 bool contains(int ack_id) const {return lookup(ack_id) > 0 ;} 00691 00692 // Retrieve the number of data bytes for the given acknowledgement. 00693 int operator[](int ack_id) const {return lookup(ack_id) ;} 00694 } ; 00695 00696 // Init the map showing the number of data bytes for each acknowledgement 00697 // message sent by the low-level controller. 00698 // 00699 // DEVNOTE: Every time we add a new acknowledgement, remember to update 00700 // this function! 00701 EatMap::EatMap() 00702 { 00703 m_map.push_back(MapEntry(LOBOT_ACK_SENSORS, LOBOT_SENSORS_SIZE)) ; 00704 m_map.push_back(MapEntry(LOBOT_ACK_WHEEL_DROPS, LOBOT_WHEEL_DROPS_SIZE)) ; 00705 m_map.push_back(MapEntry(LOBOT_ACK_CLIFFS, LOBOT_CLIFFS_SIZE)) ; 00706 m_map.push_back(MapEntry(LOBOT_ACK_BUMPS, LOBOT_BUMPS_SIZE)) ; 00707 m_map.push_back(MapEntry(LOBOT_ACK_REMOTE, LOBOT_REMOTE_SIZE)) ; 00708 } 00709 00710 // Look for the number of data bytes corresponding to the given 00711 // acknowledgement. If the map doesn't have this info, return zero. 00712 int EatMap::lookup(int ack_id) const 00713 { 00714 const int n = m_map.size() ; 00715 for (int i = 0; i < n; ++i) 00716 if (m_map[i].first == ack_id) 00717 return m_map[i].second ; 00718 return 0 ; 00719 } 00720 00721 // A quick helper function to print a message letting the user know that 00722 // the low-level controller sent some screwy acknowledgement message. 00723 static inline void bogus_ack(char c) 00724 { 00725 LERROR("received bogus ACK: 0x%02hhX (%hhd '%c')", 00726 c, c, isprint(c) ? c : '?') ; 00727 } 00728 00729 // The Comm thread's raison d'etre: wait for the low-level controller to 00730 // send a READY message and then, in return, send it the next pending 00731 // high-level motor command. Additionally, the Comm thread also responds 00732 // to acknowledgement messages from the low-level controller that send 00733 // sensor data and notifications of low-level actions (such as stopping 00734 // the motors in response to wheel drops). 00735 // 00736 // DEVNOTE: Since all Robolocust threads need to periodically check the 00737 // system-wide shutdown signal, we cannot blithely issue a blocking read 00738 // on the serial port to wait for the low-level control program to send 00739 // the READY and other acknowledgement messages. If we do that, we could 00740 // potentially hold up the lobot controller's shutdown sequence. Here's 00741 // how: 00742 // 00743 // Let's say the low-level control program dies (e.g., the user either 00744 // deliberately or inadvertently presses the Command Module's Reset 00745 // button or powers off the Create robot). Now, this sender thread here 00746 // will remain blocked forever waiting for messages from the low level 00747 // that will never arrive. 00748 // 00749 // In the meantime, let's say the user kills the lobot application. All 00750 // the other threads will shutdown. But this one won't because it's still 00751 // stuck in a blocking read operation. The main thread will also keep 00752 // waiting for this one to exit, which, of course, will never happen. And 00753 // so the application remains stuck. 00754 // 00755 // NOTE: Of course, the user can always kill -9 and force the process to 00756 // die. But that's a rather nasty non-solution. :) 00757 // 00758 // The long and short of it is that this function needs to first check if 00759 // there is actually any data available on the serial port before 00760 // performing a blocking read. If there is no data, it can sleep a short 00761 // while and repeat its loop, thereby continuing to check the shutdown 00762 // signal. 00763 // 00764 // DEVNOTE 2: Actually, the shutdown fiasco described above can still 00765 // occur if something goes wrong between the data availability check and 00766 // the attempt to read it that causes the data to disappear and the read 00767 // to block forever. However, the chances of that happening are 00768 // practically (not entirely mind you) zero. But the chances of a 00769 // malfunctioning low-level as described above are mucho high; high 00770 // enough to warrant some sort of safeguard against an unruly thread 00771 // refusing to heed the shutdown signal. 00772 void RoombaCM::Comm::run() 00773 { 00774 // Create the map specifying the number of data bytes for each 00775 // acknowledgement so that we know how many bytes to discard for the 00776 // acks in which we are not interested. 00777 const EatMap eat_map ; 00778 00779 // Comm thread main loop: retrieve acks from low-level controller and 00780 // respond appropriately. 00781 while (! Shutdown::signaled()) 00782 { 00783 try 00784 { 00785 if (m_serial->ready()) // don't block! else can miss shdn signal 00786 { 00787 char c = 0 ; 00788 if (m_serial->read(&c, 1) >= 1) 00789 switch (c) 00790 { 00791 case LOBOT_ACK_READY: 00792 send_cmd() ; 00793 break ; 00794 case LOBOT_ACK_SENSORS: 00795 recv_sensors() ; 00796 break ; 00797 default: { 00798 const int n = eat_map[c] ; 00799 if (n > 0) 00800 m_serial->eat(n) ; 00801 else 00802 bogus_ack(c) ;} 00803 break ; 00804 } 00805 } 00806 } 00807 catch (uhoh& e) 00808 { 00809 LERROR("%s encountered an error: %s", name().c_str(), e.what()) ; 00810 // non-fatal error (?) 00811 // just keep going (?) 00812 } 00813 usleep(50000) ; 00814 } 00815 00816 // The stop command sent as part of the high-level controller's 00817 // shutdown sequence will probably not get sent because the Comm 00818 // thread might well have exited by the time the main program gets 00819 // around to sending the final stop command. So, we take care of that 00820 // here in the Comm thread right before it exits... 00821 // 00822 // NOTE: We can't just send the stop command to the low level whenever 00823 // we feel like! We must first wait for the low level to send an 00824 // ACK_READY. This means we must use a loop pretty much identical to 00825 // the one above. However, since shutdown has been signaled, this loop 00826 // should not go on for too long... 00827 // 00828 // NOTE 2: Actually, we might want to send more than just the STOP 00829 // command before winding down the high-level controller. To be able 00830 // to do that conveniently, we simply buffer the shutdown/quit/cleanup 00831 // commands and send them out one-by-one. 00832 // 00833 // However, when shutting down, we don't want other threads continuing 00834 // to buffer commands. Therefore, we lock the commands buffer before 00835 // beginning the shutdown sequence. Furthermore, because the buffer() 00836 // and send_cmd() functions also internally lock the commands buffer, 00837 // we cannot reuse them here. Instead, we must reimplement the 00838 // necessary parts of their functionality right here. 00839 00840 // Lock commands buffer and clear it so that no other threads can send 00841 // any more commands to low-level. 00842 AutoMutex M(m_cmd_mutex) ; 00843 while (! m_commands.empty()) 00844 m_commands.pop() ; 00845 00846 // Buffer commands to be sent to low-level as part of the shutdown 00847 // sequence... 00848 m_commands.push(Cmd(LOBOT_CMD_STOP)) ; 00849 if (Params::enable_rear_bumps()) 00850 m_commands.push(Cmd(LOBOT_CMD_DISABLE_REAR_BUMPS)) ; 00851 00852 // Wait for ACK_READY from low-level and send above buffered commands 00853 const int MAX_TRIES = 10 ; 00854 for (int i = 0; i < MAX_TRIES; ++i) 00855 { 00856 try 00857 { 00858 if (m_serial->ready()) 00859 { 00860 char c = 0 ; 00861 if (m_serial->read(&c, 1) >= 1) 00862 switch (c) 00863 { 00864 case LOBOT_ACK_READY: { 00865 // Send next shutdown related command to low-level 00866 Cmd C(m_commands.front()) ; 00867 m_commands.pop() ; 00868 m_serial->write(C.bytes, LOBOT_CMD_SIZE) ; 00869 if (m_commands.empty()) // all shdn related commands sent 00870 return ;} // quit low-level comm interface thread 00871 break ; 00872 default: { // not interested in any other ACKs during shdn 00873 const int n = eat_map[c] ; 00874 if (n > 0) 00875 m_serial->eat(n) ; 00876 else 00877 bogus_ack(c) ;} 00878 break ; 00879 } 00880 } 00881 } 00882 catch (uhoh& e) 00883 { 00884 LERROR("%s encountered an error: %s", name().c_str(), e.what()) ; 00885 // non-fatal error (?) 00886 // just keep going (?) 00887 } 00888 00889 usleep(50000) ; 00890 } 00891 LERROR("%s: on shdn, unable to send '%c' cmd; low-level dead?", 00892 name().c_str(), m_commands.front().bytes[0]) ; 00893 } 00894 00895 // Low-level communications interface thread clean-up 00896 RoombaCM::Comm::~Comm(){} 00897 00898 //----------------------------- CLEAN-UP -------------------------------- 00899 00900 RoombaCM::~RoombaCM(){} 00901 00902 //----------------------------------------------------------------------- 00903 00904 } // end of namespace encapsulating this file's definitions 00905 00906 /* So things look consistent in everyone's emacs... */ 00907 /* Local Variables: */ 00908 /* indent-tabs-mode: nil */ 00909 /* End: */