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: */