00001 /** 00002 \file Robots/LoBot/control/LoForward.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::Forward class. 00005 */ 00006 00007 // //////////////////////////////////////////////////////////////////// // 00008 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00009 // by the 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/control/LoForward.C $ 00039 // $Id: LoForward.C 13521 2010-06-06 14:23:03Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/control/LoForward.H" 00046 00047 #include "Robots/LoBot/LoApp.H" 00048 #include "Robots/LoBot/config/LoConfigHelpers.H" 00049 #include "Robots/LoBot/thread/LoUpdateLock.H" 00050 00051 #include "Robots/LoBot/misc/LoExcept.H" 00052 #include "Robots/LoBot/misc/LoRegistry.H" 00053 #include "Robots/LoBot/util/LoGL.H" 00054 #include "Robots/LoBot/util/LoMath.H" 00055 00056 // OpenGL headers 00057 #ifdef INVT_HAVE_LIBGL 00058 #include <GL/gl.h> 00059 #endif 00060 00061 // Standard C++ headers 00062 #include <iomanip> 00063 #include <sstream> 00064 #include <algorithm> 00065 #include <limits> 00066 00067 //----------------------------- NAMESPACE ------------------------------- 00068 00069 namespace lobot { 00070 00071 //--------------------------- LOCAL HELPERS ----------------------------- 00072 00073 // Retrieve settings from forward section of config file 00074 template<typename T> 00075 static inline T conf(const std::string& key, const T& default_value) 00076 { 00077 return get_conf<T>(LOBE_FORWARD, key, default_value) ; 00078 } 00079 00080 // Overload of above function for retrieving ranges 00081 template<typename T> 00082 static inline range<T> 00083 conf(const std::string& key, const range<T>& default_value) 00084 { 00085 return get_conf<T>(LOBE_FORWARD, key, default_value) ; 00086 } 00087 00088 //-------------------------- INITIALIZATION ----------------------------- 00089 00090 Forward::Forward() 00091 : base(clamp(conf("update_delay", 1000), 100, 2500), 00092 LOBE_FORWARD, conf<std::string>("geometry", "480 0 140 140")), 00093 m_min_distance(0, -1) 00094 { 00095 start(LOBE_FORWARD) ; 00096 } 00097 00098 void Forward::pre_run() 00099 { 00100 if (Params::adaptive_mode()) 00101 { 00102 if (! App::lrf()) 00103 throw behavior_error(LASER_RANGE_FINDER_MISSING) ; 00104 } 00105 //else: in fixed mode, don't need the LRF 00106 } 00107 00108 //---------------------- THE BEHAVIOUR'S ACTION ------------------------- 00109 00110 // Depending on how it's configured, the forward driving behaviour will 00111 // either vote to drive at a fixed speed or, in adaptive mode, at a speed 00112 // based on distance readings from the LRF. 00113 void Forward::action() 00114 { 00115 // Steering decision is always to drive straight ahead 00116 // Speed, however, is decided based on operational mode 00117 TurnArbiter::Vote* T = new TurnArbiter::Vote(turn_vote_centered_at(0)) ; 00118 SpeedInfo S = Params::adaptive_mode() ? adaptive_speed() : fixed_speed() ; 00119 00120 // First, record votes and other info for visualization before 00121 // turning them over to the arbiters. Otherwise, it is possible the 00122 // votes might get deleted by the arbiter threads, which would cause 00123 // a segfault here when this thread attempts to dereference those 00124 // pointers. 00125 viz_lock() ; 00126 m_turn_vote = *T ; 00127 m_speed_vote = *S.second ; 00128 m_min_distance = S.first ; 00129 viz_unlock() ; 00130 00131 // Cast the turn and speed votes 00132 TurnArbiter::instance().vote(base::name, T) ; 00133 SpeedArbiter::instance().vote(base::name, S.second) ; 00134 } 00135 00136 // In fixed mode, simply return a speed vote using the cruising params 00137 Forward::SpeedInfo Forward::fixed_speed() const 00138 { 00139 return SpeedInfo(LRFData::Reading(0, -1), 00140 new SpeedArbiter::Vote(Params::cruising_speed(), 00141 Params::cruising_pwm())) ; 00142 } 00143 00144 // In adaptive mode, regulate speed based on distance to closest obstacle 00145 Forward::SpeedInfo Forward::adaptive_speed() const 00146 { 00147 // Make local copy of LRF data to avoid holding update lock for too long 00148 UpdateLock::begin_read() ; 00149 LRFData lrf(App::lrf()) ; 00150 UpdateLock::end_read() ; 00151 00152 // Find minimum average distance in configured FOV. 00153 // 00154 // NOTE: We multiply the average distance for each angle we consider 00155 // by the cosine of that angle in order to project that "distance 00156 // vector" onto the vector corresponding to straight ahead (i.e., zero 00157 // degree heading). 00158 const range<int> fov = Params::fov() ; 00159 const int step = Params::averaging_block() ; 00160 00161 LRFData::Reading min(std::numeric_limits<int>::min(), 00162 std::numeric_limits<int>::max()) ; 00163 for (int angle = fov.min(); angle <= fov.max(); angle += step) 00164 { 00165 const int m = clamp(angle - step/2, lrf.min_angle(), angle - 1) ; 00166 const int M = clamp(angle + step/2, angle + 1, lrf.max_angle()) ; 00167 const int d = round(lrf.average_distance(m, M)) ; 00168 if (d <= 0) // all bad readings in [angle - step/2, angle + step/2]?!? 00169 continue ; 00170 if (d < min.distance()) 00171 min = LRFData::Reading(angle, d) ; 00172 } 00173 00174 // Compute speed and PWM corresponding to min distance using linear 00175 // interpolation. 00176 const range<float> S = Params::speed_range() ; 00177 const range<int> P = Params::pwm_range() ; 00178 const range<int> D = Params::distance_range() ; 00179 00180 if (min.distance() == std::numeric_limits<int>::max()) // all bad readings? 00181 return SpeedInfo(LRFData::Reading(180, -1), 00182 new SpeedArbiter::Vote(S.min(), P.min())) ; 00183 00184 const float d = 00185 static_cast<float>(min.distance() - D.min())/(D.max() - D.min()) ; 00186 const float s = clamp(S.min() + d * (S.max() - S.min()), 00187 S.min(), S.max()) ; 00188 const int p = clamp(P.min() + round(d * (P.max() - P.min())), 00189 P.min(), P.max()) ; 00190 //LERROR("min distance = [%4d @ %4d], speed vote = %6.3f", 00191 //min.distance(), min.angle(), s) ; 00192 return SpeedInfo(min, new SpeedArbiter::Vote(s, p)) ; 00193 } 00194 00195 //--------------------------- VISUALIZATION ----------------------------- 00196 00197 #ifdef INVT_HAVE_LIBGL 00198 00199 // Helper to convert the minimum distance reading into a string label 00200 static std::string dist(int d) 00201 { 00202 std::ostringstream label ; 00203 label << d << " mm" ; 00204 return label.str() ; 00205 } 00206 00207 // Helper to convert the chosen speed value a string label 00208 static std::string speed(float s) 00209 { 00210 using namespace std ; 00211 00212 std::ostringstream label ; 00213 label << setw(6) << fixed << setprecision(3) << s << " m/s" ; 00214 return label.str() ; 00215 } 00216 00217 void Forward::render_me() 00218 { 00219 // Make local copy so that forward behaviour's thread isn't held up 00220 // waiting for visualization thread to complete. 00221 viz_lock() ; 00222 TurnArbiter::Vote V = m_turn_vote ; 00223 SpeedArbiter::Vote S = m_speed_vote ; 00224 LRFData::Reading D = m_min_distance ; 00225 viz_unlock() ; 00226 00227 unit_view_volume() ; 00228 glBegin(GL_LINES) ; 00229 // Render the turn vote's visualization 00230 for (TurnArbiter::Vote::iterator it = V.begin(); it; ++it) 00231 { 00232 float vote = it.value() ; 00233 float direction = it.direction() ; 00234 if (vote < 0) // voted against this direction 00235 { 00236 glColor3f(1, 0, 0) ; 00237 glVertex2i(0, 0) ; 00238 glVertex2f(-vote * cos(direction), -vote * sin(direction)) ; 00239 } 00240 else if (vote > 0) // voted for this direction 00241 { 00242 glColor3f(0, 1, 0) ; 00243 glVertex2i(0, 0) ; 00244 glVertex2f(vote * cos(direction), vote * sin(direction)) ; 00245 } 00246 } 00247 00248 // Render distance reading's direction corresponding to speed vote 00249 if (Params::adaptive_mode()) { 00250 glColor3f(1, 1, 1) ; 00251 glVertex2i(0, 0) ; 00252 glVertex2f(0.75f * cos(D.angle()), 0.75f * sin(D.angle())) ; 00253 } 00254 glEnd() ; 00255 00256 // Label the visualization so that it is easy to tell which behaviour 00257 // is being visualized. Also label the speed value and, if applicable, 00258 // the distance reading that resulted in the chosen speed. 00259 restore_view_volume() ; 00260 text_view_volume() ; 00261 glColor3f(0, 1, 1) ; 00262 draw_label(3, 12, "Forward") ; 00263 if (Params::adaptive_mode()) 00264 draw_label(3, m_geometry.height - 16, dist(D.distance()).c_str()) ; 00265 draw_label(3, m_geometry.height - 4, speed(S.speed()).c_str()) ; 00266 00267 restore_view_volume() ; 00268 } 00269 00270 #endif 00271 00272 //----------------------------- CLEAN-UP -------------------------------- 00273 00274 Forward::~Forward(){} 00275 00276 //-------------------------- KNOB TWIDDLING ----------------------------- 00277 00278 // Parameters initialization 00279 Forward::Params::Params() 00280 : m_cruising_speed(clamp(conf("cruising_speed", 0.5f), 0.1f, 10.0f)), 00281 m_cruising_pwm(clamp(conf("cruising_pwm", 30), 15, 100)), 00282 m_adaptive_mode(conf("adaptive_mode", false)), 00283 m_fov(clamp(conf("fov", range<int>(-60, 60)), range<int>(-90, 90))), 00284 m_averaging_block(clamp(conf("averaging_block", 10), 1, 20)), 00285 m_speed_range(clamp(conf("speed_range", range<float>(0.1f, 0.5f)), 00286 range<float>(0.05f, 0.5f))), 00287 m_pwm_range(clamp(conf("pwm_range", range<int>(25, 100)), 00288 range<int>(10, 100))), 00289 m_distance_range(clamp(conf("distance_range", range<int>(250, 2500)), 00290 range<int>(100, 5000))) 00291 {} 00292 00293 // Parameters clean-up 00294 Forward::Params::~Params(){} 00295 00296 //----------------------------------------------------------------------- 00297 00298 } // end of namespace encapsulating this file's definitions 00299 00300 /* So things look consistent in everyone's emacs... */ 00301 /* Local Variables: */ 00302 /* indent-tabs-mode: nil */ 00303 /* End: */