LoRoombaCM.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:41:30 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3