00001 /** 00002 \file Robots/LoBot/control/LoOpenPath.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::OpenPath 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/LoOpenPath.C $ 00039 // $Id: LoOpenPath.C 14306 2010-12-09 00:11:01Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/control/LoOpenPath.H" 00046 #include "Robots/LoBot/control/LoSpinArbiter.H" 00047 00048 #include "Robots/LoBot/LoApp.H" 00049 #include "Robots/LoBot/ui/LoLaserViz.H" 00050 #include "Robots/LoBot/config/LoConfigHelpers.H" 00051 #include "Robots/LoBot/thread/LoUpdateLock.H" 00052 00053 #include "Robots/LoBot/misc/LoExcept.H" 00054 #include "Robots/LoBot/misc/LoRegistry.H" 00055 #include "Robots/LoBot/misc/singleton.hh" 00056 00057 #include "Robots/LoBot/util/LoGL.H" 00058 #include "Robots/LoBot/util/LoMath.H" 00059 #include "Robots/LoBot/util/range.hh" 00060 00061 // OpenGL headers 00062 #ifdef INVT_HAVE_LIBGLU 00063 #include <GL/glu.h> 00064 #endif 00065 00066 #ifdef INVT_HAVE_LIBGL 00067 #include <GL/gl.h> 00068 #endif 00069 00070 // Standard C++ headers 00071 #include <algorithm> 00072 #include <map> 00073 #include <functional> 00074 #include <utility> 00075 00076 // Standard C headers 00077 #include <math.h> 00078 00079 //----------------------------- NAMESPACE ------------------------------- 00080 00081 namespace lobot { 00082 00083 //-------------------------- KNOB TWIDDLING ----------------------------- 00084 00085 namespace { 00086 00087 // Retrieve settings from open_path section of config file 00088 template<typename T> 00089 inline T conf(const std::string& key, const T& default_value) 00090 { 00091 return get_conf<T>(LOBE_OPEN_PATH, key, default_value) ; 00092 } 00093 00094 // Overload of above function for retrieving ranges 00095 template<typename T> 00096 inline range<T> conf(const std::string& key, const range<T>& default_value) 00097 { 00098 return get_conf<T>(LOBE_OPEN_PATH, key, default_value) ; 00099 } 00100 00101 /// This inner class encapsulates various parameters that can be used 00102 /// to tweak different aspects of the open path behaviour. 00103 class Params : public singleton<Params> { 00104 /// The width of the robot. This setting specifies the minimum 00105 /// width (in mm) that each open path must be. 00106 float m_path_width ; 00107 00108 /// We will only consider open paths that are at least the length 00109 /// (in mm) specified by this setting. 00110 float m_min_path_length ; 00111 00112 /// A derived parameter that keeps track of the angular range about 00113 /// a given direction that must be searched for candidate open 00114 /// paths. 00115 int m_alpha ; 00116 00117 /// Instead of looking for open paths across the laser range 00118 /// finder's entire FOV, we can restrict this behaviour to search 00119 /// only some subportion of it. This setting specifies the angular 00120 /// range to search for open paths. It is a single number 00121 /// specifying the angular range for each side of zero degrees. 00122 int m_fov ; 00123 00124 /// Instead of looking for open paths at each angle, we can use 00125 /// this parameter to skip some angles. 00126 int m_step ; 00127 00128 /// To ensure that the robot doesn't keep turning unnecessarily, we 00129 /// mark some angular range in front of the robot as a dead zone. 00130 /// When the most open path lies in this angular range, the open 00131 /// path behaviour will simply keep driving straight and not affect 00132 /// the steering. 00133 /// 00134 /// To disable this policy, simply specify a range that lies 00135 /// outside the laser range finder's FOV (e.g., 260 to 280). That 00136 /// way, all open paths will lie outside of the dead zone and the 00137 /// robot will always steer towards the most open path in every 00138 /// iteration of this behaviour. 00139 /// 00140 /// To have the robot never turn towards the most open path, simply 00141 /// use the entire FOV of the laser range finder. Then, since all 00142 /// open paths will lie in the dead zone, the robot will never turn 00143 /// towards the mostopen path. (Note, however, that an easier way 00144 /// to do this is to simply disable the open path behaviour.) 00145 /// 00146 /// This setting expects two integers. The first specifies the 00147 /// minimum of the dead zone's angular range (usually a negative 00148 /// number indicating an angle on the robot's right). The second 00149 /// number is the dead zone's maximum (usually a positive number 00150 /// for a direction on the left of the robot). 00151 range<int> m_dead_zone ; 00152 00153 /// Usually, steering control is effected using the turn arbiter, 00154 /// which veers the robot in different directions while it moves, 00155 /// i.e., smooth car-like turns. However, the lgmd_extricate_tti 00156 /// behaviour also supports spin-style steering, i.e., momentarily 00157 /// stopping the robot and then turning it cw/ccw in-place. This flag 00158 /// turns on spin-style steering. By default, the behaviour uses the 00159 /// normal car-like steering mode. 00160 bool m_spin_style_steering ; 00161 00162 /// The number of milliseconds between successive iterations of this 00163 /// behaviour. 00164 /// 00165 /// WARNING: The ability to change a behaviour's update frequency is a 00166 /// very powerful feature whose misuse or abuse can wreak havoc! Be 00167 /// sure to use reasonable values for this setting. 00168 int m_update_delay ; 00169 00170 /// The location and size (within the Robolocust main window) of the 00171 /// goal behaviour's visualization. 00172 typedef Drawable::Geometry Geom ; // just for properly aligning accessors 00173 Geom m_geometry ; 00174 00175 /// Private constructor because this is a singleton. 00176 Params() ; 00177 00178 // Boilerplate code to make generic singleton design pattern work 00179 friend class singleton<Params> ; 00180 00181 public: 00182 /// Accessing the various parameters. 00183 //@{ 00184 static float path_width() {return instance().m_path_width ;} 00185 static float min_path_length() {return instance().m_min_path_length ;} 00186 static int alpha() {return instance().m_alpha ;} 00187 static int fov() {return instance().m_fov ;} 00188 static int step() {return instance().m_step ;} 00189 static range<int> dead_zone() {return instance().m_dead_zone ;} 00190 static bool spin_style_steering(){return instance().m_spin_style_steering;} 00191 static int update_delay() {return instance().m_update_delay ;} 00192 static Geom geometry() {return instance().m_geometry ;} 00193 //@} 00194 } ; 00195 00196 // Parameters initialization 00197 Params::Params() 00198 : m_path_width(clamp(conf("path_width", 175.0f), 150.0f, 500.0f)), 00199 m_min_path_length(clamp(conf("min_path_length", 250.0f), 00200 100.0f, 2500.0f)), 00201 m_alpha(round(atan(m_path_width/2/m_min_path_length))), 00202 m_fov(clamp(conf("fov", 90), 0, 180)), 00203 m_step(clamp(conf("step", 10), 1, 45)), 00204 m_dead_zone(conf("dead_zone", range<int>(-20, 20))), 00205 m_spin_style_steering(conf("spin_style_steering", false)), 00206 m_update_delay(clamp(conf("update_delay", 600), 250, 10000)), 00207 m_geometry(conf<std::string>("geometry", "0 0 10 10")) 00208 {} 00209 00210 } // end of local anonymous namespace encapsulating above helpers 00211 00212 //-------------------------- INITIALIZATION ----------------------------- 00213 00214 OpenPath::OpenPath() 00215 : base(Params::update_delay(), LOBE_OPEN_PATH, Params::geometry()) 00216 { 00217 start(LOBE_OPEN_PATH) ; 00218 } 00219 00220 void OpenPath::pre_run() 00221 { 00222 if (! App::lrf()) 00223 throw behavior_error(LASER_RANGE_FINDER_MISSING) ; 00224 if (! App::robot()) 00225 throw behavior_error(MOTOR_SYSTEM_MISSING) ; 00226 00227 Drawable* v = App::laser_viz() ; 00228 if (v) 00229 v->add_hook(RenderHook(render_paths, 00230 reinterpret_cast<unsigned long>(this))) ; 00231 } 00232 00233 OpenPath::PathInfo::PathInfo(float length, float width) 00234 : m_info(length, width, length * width) 00235 { 00236 if (length < 0 || width < 0) 00237 m_info.third = -1 ; 00238 } 00239 00240 //---------------------- THE BEHAVIOUR'S ACTION ------------------------- 00241 00242 void OpenPath::action() 00243 { 00244 // Make local copy of LRF data to avoid holding update lock for too 00245 // long... 00246 UpdateLock::begin_read() ; 00247 LRFData lrf(App::lrf()) ; 00248 UpdateLock::end_read() ; 00249 00250 // The list of candidate open paths is stored as a mapping between LRF 00251 // measurement angles and the corresponding path lengths along those 00252 // directions. 00253 Paths paths ; 00254 00255 // Find the candidate open paths in each direction supported by LRF 00256 int fov = std::max(lrf.min_angle() + Params::alpha(), -Params::fov()) ; 00257 for (int angle = 0; angle >= fov; angle -= Params::step()) 00258 { 00259 PathInfo p = open_path(angle, lrf) ; 00260 if (p.area() > 0) 00261 paths.insert(std::make_pair(angle, p)) ; 00262 } 00263 00264 fov = std::min(lrf.max_angle() - Params::alpha(), Params::fov()) ; 00265 for (int angle = Params::step(); angle <= fov; angle += Params::step()) 00266 { 00267 PathInfo p = open_path(angle, lrf) ; 00268 if (p.area() > 0) 00269 paths.insert(std::make_pair(angle, p)) ; 00270 } 00271 viz_lock() ; 00272 m_paths = paths ; // record raw paths for visualization 00273 viz_unlock() ; 00274 //dump(paths, "OpenPath::action", "paths(raw)") ; 00275 00276 // Find most open path (i.e., path with max length) 00277 Paths::const_iterator max = 00278 std::max_element(paths.begin(), paths.end(), map_value_compare(paths)) ; 00279 //LERROR("max reading = [%4d %8.1f]", max->first, max->second) ; 00280 00281 // Steer towards most open path only if it is significantly to the 00282 // left or right of the robot. Otherwise, just keep going straight. 00283 if (! Params::dead_zone().in(max->first)) 00284 { 00285 if (Params::spin_style_steering()) 00286 SpinArbiter::instance().vote(base::name, 00287 new SpinArbiter::Vote(max->first)) ; 00288 else 00289 { 00290 const int T = TurnArbiter::turn_max() ; 00291 TurnArbiter::Vote* V = new TurnArbiter::Vote( 00292 turn_vote_centered_at(clamp(max->first, -T, T))) ; 00293 //V->dump("OpenPath::action") ; 00294 00295 // Record the above vote for visualization before turning it over 00296 // to the turn arbiter. Otherwise, it is possible the vote might 00297 // get deleted by the turn arbiter, which would cause a segfault 00298 // here when this thread attempts to dereference the pointer. 00299 viz_lock() ; 00300 m_vote = *V ; 00301 viz_unlock() ; 00302 00303 TurnArbiter::instance().vote(base::name, V) ; 00304 } 00305 } 00306 } 00307 00308 // This function returns an open path (if available) at the specified 00309 // direction. 00310 OpenPath::PathInfo OpenPath::open_path(int theta, const LRFData& lrf) const 00311 { 00312 const float w = Params::path_width()/2 ; 00313 00314 float L = lrf.max_distance() * 2 ; 00315 for (int x = theta - Params::alpha(); x <= theta + Params::alpha(); ++x) 00316 { 00317 int D = lrf[x] ; 00318 if (D < 0) 00319 continue ; 00320 if (D * sin(abs(theta - x)) <= w) { 00321 float d = D * cos(abs(theta - x)) ; 00322 if (d < L) 00323 L = d ; 00324 } 00325 } 00326 00327 if (L > lrf.max_distance() || L < Params::min_path_length()) 00328 return PathInfo(-1, -1) ; 00329 return PathInfo(L, Params::path_width()) ; 00330 } 00331 //--------------------------- VISUALIZATION ----------------------------- 00332 00333 #ifdef INVT_HAVE_LIBGL 00334 00335 void OpenPath::render_me() 00336 { 00337 // Make local copy so that open path behaviour's thread isn't held up 00338 // waiting for visualization thread to complete. 00339 viz_lock() ; 00340 TurnArbiter::Vote V = m_vote ; 00341 viz_unlock() ; 00342 00343 // Render the votes visualization 00344 unit_view_volume() ; 00345 glBegin(GL_LINES) ; 00346 for (TurnArbiter::Vote::iterator it = V.begin(); it; ++it) 00347 { 00348 float vote = it.value() ; 00349 float direction = it.direction() ; 00350 if (vote < 0) // voted against this direction 00351 { 00352 glColor3f(1, 0, 0) ; 00353 glVertex2i(0, 0) ; 00354 glVertex2f(-vote * cos(direction), -vote * sin(direction)) ; 00355 } 00356 else if (vote > 0) // voted for this direction 00357 { 00358 glColor3f(0, 1, 0) ; 00359 glVertex2i(0, 0) ; 00360 glVertex2f(vote * cos(direction), vote * sin(direction)) ; 00361 } 00362 } 00363 glEnd() ; 00364 00365 // Label the visualization so that it is easy to tell which behaviour 00366 // is being visualized. 00367 restore_view_volume() ; 00368 text_view_volume() ; 00369 glColor3f(0, 1, 1) ; 00370 draw_label(3, 12, "Open Path") ; 00371 restore_view_volume() ; 00372 } 00373 00374 void OpenPath::render_paths(unsigned long client_data) 00375 { 00376 (reinterpret_cast<OpenPath*>(client_data))->render_paths() ; 00377 } 00378 00379 void OpenPath::render_paths() 00380 { 00381 // Make local copy so that open path behaviour's thread isn't held up 00382 // waiting for visualization thread to complete. 00383 viz_lock() ; 00384 Paths P = m_paths ; 00385 viz_unlock() ; 00386 00387 // Render the open paths, overlaying them on the LRF visualization 00388 glPushMatrix() ; 00389 glRotatef(get_conf("laser_viz", "lrf_direction", 90.0f), 0, 0, 1) ; 00390 glBegin(GL_LINES) ; 00391 glColor3f(1, 1, 1) ; 00392 for (Paths::const_iterator it = P.begin(); it != P.end(); ++it) 00393 { 00394 glVertex2i(0, 0) ; 00395 glVertex2f(it->second.length() * cos(it->first), 00396 it->second.length() * sin(it->first)); 00397 } 00398 glEnd() ; 00399 glPopMatrix() ; 00400 } 00401 00402 #endif 00403 00404 //----------------------------- CLEAN-UP -------------------------------- 00405 00406 OpenPath::~OpenPath(){} 00407 00408 //----------------------------------------------------------------------- 00409 00410 } // end of namespace encapsulating this file's definitions 00411 00412 /* So things look consistent in everyone's emacs... */ 00413 /* Local Variables: */ 00414 /* indent-tabs-mode: nil */ 00415 /* End: */