LoRCCar.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/io/LoRCCar.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::RCCar 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/LoRCCar.C $
00039 // $Id: LoRCCar.C 13911 2010-09-10 03:14:57Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/io/LoRCCar.H"
00046 #include "Robots/LoBot/control/LoTurnArbiter.H"
00047 #include "Robots/LoBot/config/LoConfigHelpers.H"
00048 #include "Robots/LoBot/misc/LoExcept.H"
00049 #include "Robots/LoBot/util/LoMath.H"
00050 #include "Robots/LoBot/util/LoTime.H"
00051 
00052 // INVT utilities
00053 #include "Util/log.H"
00054 
00055 //------------------------------ MACROS ---------------------------------
00056 
00057 // Sometimes, during development, the motors may not be available (e.g.,
00058 // when writing code at home instead of at iLab). Or, we may simply not
00059 // want to use the actual motors (e.g., we're testing some behaviour and
00060 // only want to see/visualize its votes without driving the motors).
00061 //
00062 // In these situations, it can be useful to have a convenient dummy motor
00063 // system available. The following symbol can be used for the
00064 // above-mentioned purpose.
00065 //
00066 // Un/comment following line when motors are un/available.
00067 //#define LOBOT_MOTOR_DEVMODE 1
00068 
00069 // The motor system's update method prints the current speed, heading,
00070 // etc. Sometimes, we may want to suppress this printing. For that,
00071 // uncomment the following line.
00072 //#define LOBOT_MOTOR_NO_PRINT 1
00073 
00074 //----------------------------- NAMESPACE -------------------------------
00075 
00076 namespace lobot {
00077 
00078 //-------------------------- INITIALIZATION -----------------------------
00079 
00080 RCCar::RCCar(const ModelManager& mgr)
00081    : base(mgr, Params::device(), Params::baud_rate()),
00082      m_speed_pid(Params::speed_gains()),
00083      m_rpm_filter(Params::rpm_filter_size())
00084 {}
00085 
00086 //-------------------------- MOTOR COMMANDS -----------------------------
00087 
00088 void RCCar::drive(float speed, int pwm)
00089 {
00090    if (Params::bypass_rpm_sensor())
00091       drive(pwm) ;
00092    else
00093       drive(speed) ;
00094 }
00095 
00096 void RCCar::drive(float speed)
00097 {
00098    if (is_zero(speed))
00099    {
00100       send_propeller(STOP) ;
00101       m_speed_pid.reset() ;
00102       m_rpm_filter.reset() ;
00103       return ;
00104    }
00105 
00106    float speed_cmd = m_speed_pid.cmd(speed - current_speed()) ;
00107    float rpm_cmd   = speed_cmd/Params::rpm_speed_factor() ;
00108    int pwm = sensors().motor_pwm() + round(rpm_cmd * Params::rpm_pwm_factor());
00109    drive(pwm) ;
00110 }
00111 
00112 void RCCar::drive(int pwm)
00113 {
00114    if (pwm < 0)
00115       send_propeller(REVERSE, clamp(-pwm, 10, 100) * 127/100) ;
00116    else if (pwm > 0)
00117       send_propeller(FORWARD, clamp( pwm, 10, 100) * 127/100) ;
00118    else
00119       send_propeller(STOP) ;
00120 }
00121 
00122 void RCCar::turn(float direction)
00123 {
00124    if (direction < 0) // negative angle ==> turn right
00125       send_propeller(RIGHT, round(-direction * Params::steering_pwm_factor()));
00126    else if (direction > 0) // positive angle ==> turn left
00127       send_propeller(LEFT,  round( direction * Params::steering_pwm_factor()));
00128    else
00129       send_propeller(STRAIGHT) ;
00130 }
00131 
00132 void RCCar::spin(float)
00133 {
00134    throw motor_error(IN_PLACE_TURNS_NOT_SUPPORTED) ;
00135 }
00136 
00137 void RCCar::off()
00138 {
00139    //LERROR("switching off motors...") ;
00140    send_propeller(OFF) ;
00141 
00142    // DEVNOTE: After sending the OFF command to the Propeller, we also
00143    // seem to need to give the command some time to "sink in." For some
00144    // strange and unknown reason, without this "sinking in time," the
00145    // motors don't actually stop.
00146    //
00147    // We could simply sleep for a while to ensure that the Propeller gets
00148    // the OFF command and responds properly. However, instead of blithely
00149    // sleeping for some time, we can call the update function to retrieve
00150    // the low-level motor state and print that out. This not only effects
00151    // the delay necessary for the "sink-in time" explained above but also
00152    // provides the user with some feedback that the motors really have
00153    // gone off.
00154    update() ;
00155 }
00156 
00157 //--------------------------- MOTOR STATUS ------------------------------
00158 
00159 bool RCCar::update_sensors()
00160 {
00161    int M = ((recv_propeller(GET_MOTOR_DIR) == REVERSE) ? -1 : +1)
00162       * recv_propeller(GET_MOTOR_PWM) * 100/127 ;
00163    int S = ((recv_propeller(GET_SERVO_DIR) == RIGHT) ? -1 : +1)
00164       * recv_propeller(GET_SERVO_PWM) ;
00165    float R = recv_propeller(GET_RPM, 4)/1000.0f ; // Propeller returns rpm*1000
00166 
00167    motor_pwm(M) ;
00168    servo_pwm(S) ;
00169    rpm(R) ;
00170    m_rpm_filter.add(R) ;
00171    speed(sign(M) * m_rpm_filter.value() * Params::rpm_speed_factor()) ;
00172    heading(round(S/Params::steering_pwm_factor())) ;
00173    time_stamp(current_time()) ;
00174 
00175 #ifndef LOBOT_MOTOR_NO_PRINT
00176    LERROR("drive = [%4d %8.2f %8.2f %7.2f]; turn = [%4d %6.1f]",
00177           M, R, m_rpm_filter.value(), current_speed(), S, current_heading()) ;
00178 #endif
00179    return true ; // bogus! but then we're not using this class anymore...
00180 }
00181 
00182 bool RCCar::stopped() const
00183 {
00184    return sensors().motor_pwm() == 0 ;
00185 }
00186 
00187 //--------------------- LOW-LEVEL MOTOR COMMANDS ------------------------
00188 
00189 #ifdef LOBOT_MOTOR_DEVMODE
00190 
00191 // Development mode dummy
00192 class fake_propeller {
00193    int motor_dir, motor_pwm ;
00194    int servo_dir, servo_pwm ;
00195 
00196    enum {
00197       FORWARD_CMD = 100,
00198       REVERSE_CMD = 101,
00199       STOP_CMD    = 102,
00200 
00201       LEFT_CMD     = 110,
00202       RIGHT_CMD    = 111,
00203       STRAIGHT_CMD = 112,
00204 
00205       OFF_CMD = 120,
00206 
00207       GET_MOTOR_DIR = 130,
00208       GET_MOTOR_PWM = 131,
00209       GET_SERVO_DIR = 132,
00210       GET_SERVO_PWM = 133,
00211       GET_RPM       = 134,
00212    } ;
00213 
00214 public:
00215    fake_propeller() ;
00216    void send(int cmd) ;
00217    void send(int cmd, int param) ;
00218    int  recv(int cmd) ;
00219 } ;
00220 
00221 fake_propeller::fake_propeller()
00222    : motor_dir(0), motor_pwm(0),
00223      servo_dir(0), servo_pwm(0)
00224 {}
00225 
00226 void fake_propeller::send(int cmd)
00227 {
00228    switch (cmd)
00229    {
00230       case STOP_CMD:
00231          motor_dir = STOP_CMD ;
00232          motor_pwm = 0 ;
00233          break ;
00234       case STRAIGHT_CMD:
00235          servo_dir = STRAIGHT_CMD ;
00236          servo_pwm = 0 ;
00237          break ;
00238       case OFF_CMD:
00239          motor_dir = STOP_CMD ;
00240          motor_pwm = 0 ;
00241          servo_dir = STRAIGHT_CMD ;
00242          servo_pwm = 0 ;
00243          break ;
00244    }
00245 }
00246 
00247 void fake_propeller::send(int cmd, int param)
00248 {
00249    switch (cmd)
00250    {
00251       case FORWARD_CMD:
00252          motor_dir = FORWARD_CMD ;
00253          motor_pwm = clamp(param, 0, 127) ;
00254          break ;
00255       case REVERSE_CMD:
00256          motor_dir = REVERSE_CMD ;
00257          motor_pwm = clamp(param, 0, 127) ;
00258          break ;
00259       case LEFT_CMD:
00260          servo_dir = LEFT_CMD ;
00261          servo_pwm = clamp(param, 0, 100) ;
00262          break ;
00263       case RIGHT_CMD:
00264          servo_dir = RIGHT_CMD ;
00265          servo_pwm = clamp(param, 0, 100) ;
00266          break ;
00267    }
00268 }
00269 
00270 int fake_propeller::recv(int cmd)
00271 {
00272    switch (cmd)
00273    {
00274       case GET_MOTOR_DIR:
00275          return motor_dir ;
00276       case GET_MOTOR_PWM:
00277          return motor_pwm ;
00278       case GET_SERVO_DIR:
00279          return servo_dir ;
00280       case GET_SERVO_PWM:
00281          return servo_pwm ;
00282       case GET_RPM:
00283          return 0 ;
00284    }
00285    return -1 ;
00286 }
00287 
00288 static fake_propeller propeller ;
00289 
00290 void RCCar::send_propeller(int cmd)
00291 {
00292    propeller.send(cmd) ;
00293 }
00294 
00295 void RCCar::send_propeller(int cmd, int param)
00296 {
00297    propeller.send(cmd, param) ;
00298 }
00299 
00300 int RCCar::recv_propeller(int cmd, int)
00301 {
00302    return propeller.recv(cmd) ;
00303 }
00304 
00305 #else // the real Propeller interface
00306 
00307 void RCCar::send_propeller(int cmd)
00308 {
00309    char buf[] = {cmd} ;
00310    m_serial.write(buf, sizeof(buf)) ;
00311 }
00312 
00313 void RCCar::send_propeller(int cmd, int param)
00314 {
00315    char buf[] = {cmd, param} ;
00316    m_serial.write(buf, sizeof(buf)) ;
00317 }
00318 
00319 // The recv_propeller() function will cause a strict aliasing warning
00320 // because of the way it casts the buf variable. We turn off the warning
00321 // to allow INVT builds to succeed (because it uses -Werror by default).
00322 //
00323 // DEVNOTE: In general, this is not a good thing to do. Ideally, we
00324 // should fix the return statement that performs the weird cast that
00325 // causes the compiler warning. However, in this case, we really do want
00326 // that cast the way it is.
00327 //
00328 // Moreover, this particular class, viz., the interface for the older R/C
00329 // car based robot platform is not really in use anymore (circa September
00330 // 2010). Nonetheless, it is a useful class to have around, if for
00331 // nothing else other than illustration as to how the Robolocust
00332 // framework can be adapted to different robot platforms. Therefore, we
00333 // let the function go unfixed and instead opt to turn off this
00334 // particular warning so that builds succeed without issue.
00335 //
00336 // DEVNOTE 2: The diagnostic pragma for ignoring warnings may not be
00337 // available on versions of GCC older than 4.3.0. It is important to
00338 // perform this check; otherwise, GCC will issue a warning about the
00339 // pragma directive and again cause the build to fail no thanks to INVT's
00340 // use of the -Werror option.
00341 //
00342 // DEVNOTE 3: GCC version 4.1.2, which ships with Debian 4.0 (etch),
00343 // seems not to care about strict aliasing and so does not complain when
00344 // compiling recv_propeller(). However, this may not be so on other
00345 // system with other versions of GCC. In that case, fixing the offending
00346 // return statement in recv_propeller() may be the only way out of the
00347 // quandary.
00348 #if __GNUC__ >= 4 && __GNUC_MINOR__ >= 3
00349 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
00350 #endif
00351 
00352 int RCCar::recv_propeller(int cmd, int n)
00353 {
00354    send_propeller(cmd) ;
00355 
00356    const int MAX_TRIES = 10 ;
00357    int tries = 0 ;
00358    for(;;)
00359    {
00360       usleep(50000) ;
00361 
00362       char buf[n + 1] ;
00363       std::fill_n(buf, n + 1, 0) ;
00364 
00365       if (m_serial.read(buf, n) >= n) {
00366          std::reverse(buf, buf + n) ;
00367          return *reinterpret_cast<int*>(buf) ;
00368       }
00369 
00370       if (++tries <= MAX_TRIES)
00371          continue ;
00372       throw motor_error(MOTOR_READ_FAILURE) ;
00373    }
00374    return 0 ; // just to keep the compiler happy
00375 }
00376 
00377 #endif // LOBOT_MOTOR_DEVMODE
00378 
00379 //----------------------------- CLEAN-UP --------------------------------
00380 
00381 RCCar::~RCCar(){}
00382 
00383 //-------------------------- KNOB TWIDDLING -----------------------------
00384 
00385 // Parameters initialization
00386 RCCar::Params::Params()
00387    : m_device(robot_conf<std::string>("serial_port", "/dev/ttyUSB0")),
00388      m_baud_rate(clamp(robot_conf("baud_rate", 38400), 50, 115200)),
00389      m_wheel_diameter(clamp(robot_conf("wheel_diameter", 139.70f),
00390                             10.0f, 1000.0f)),
00391      m_rpm_speed_factor(3.14159f * m_wheel_diameter/60000),
00392      m_rpm_pwm_factor(clamp(robot_conf("rpm_pwm_factor", 0.3f),
00393                             0.001f, 1.0f)),
00394      m_rpm_filter_size(clamp(robot_conf("rpm_filter_size", 10), 1, 100)),
00395      m_bypass_rpm_sensor(robot_conf("bypass_rpm_sensor", false)),
00396      m_speed_gains(get_conf("robot", "speed_gains",
00397                             make_triple(1.0f, 0.01f, 0.0f))),
00398      m_steering_pwm_factor(clamp(robot_conf("max_steering_pwm", 75.0f),
00399                                  50.0f, 90.0f)/TurnArbiter::turn_max())
00400 {}
00401 
00402 // Parameters clean-up
00403 RCCar::Params::~Params(){}
00404 
00405 //-----------------------------------------------------------------------
00406 
00407 } // end of namespace encapsulating this file's definitions
00408 
00409 //-----------------------------------------------------------------------
00410 
00411 /* So things look consistent in everyone's emacs... */
00412 /* Local Variables: */
00413 /* indent-tabs-mode: nil */
00414 /* End: */
Generated on Sun May 8 08:41:30 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3