00001 /** 00002 \file Robots/LoBot/control/LoLGMDExtricateSimple.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::LGMDExtricateSimple 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/LoLGMDExtricateSimple.C $ 00039 // $Id: LoLGMDExtricateSimple.C 13840 2010-08-27 22:28:12Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/control/LoLGMDExtricateSimple.H" 00046 #include "Robots/LoBot/control/LoMetrics.H" 00047 #include "Robots/LoBot/control/LoTurnArbiter.H" 00048 #include "Robots/LoBot/control/LoSpinArbiter.H" 00049 #include "Robots/LoBot/control/LoSpeedArbiter.H" 00050 00051 #include "Robots/LoBot/LoApp.H" 00052 #include "Robots/LoBot/slam/LoMap.H" 00053 #include "Robots/LoBot/lgmd/LocustModel.H" 00054 00055 #include "Robots/LoBot/config/LoConfigHelpers.H" 00056 #include "Robots/LoBot/thread/LoUpdateLock.H" 00057 00058 #include "Robots/LoBot/misc/LoRegistry.H" 00059 #include "Robots/LoBot/util/LoGL.H" 00060 #include "Robots/LoBot/misc/LoVector.H" 00061 #include "Robots/LoBot/util/LoMath.H" 00062 #include "Robots/LoBot/util/LoSTL.H" 00063 #include "Robots/LoBot/misc/singleton.hh" 00064 #include "Robots/LoBot/util/range.hh" 00065 00066 // OpenGL headers 00067 #ifdef INVT_HAVE_LIBGL 00068 #include <GL/gl.h> 00069 #endif 00070 00071 // Boost headers 00072 #include <boost/bind.hpp> 00073 00074 // Standard C++ headers 00075 #include <iomanip> 00076 #include <algorithm> 00077 #include <functional> 00078 #include <vector> 00079 #include <iterator> 00080 #include <utility> 00081 00082 //----------------------------- NAMESPACE ------------------------------- 00083 00084 namespace lobot { 00085 00086 //-------------------------- KNOB TWIDDLING ----------------------------- 00087 00088 namespace { 00089 00090 // Retrieve settings from lgmd_extricate_simple section of config file 00091 template<typename T> 00092 inline T conf(const std::string& key, const T& default_value) 00093 { 00094 return get_conf<T>(LOBE_LGMD_EXTRICATE_SIMPLE, key, default_value) ; 00095 } 00096 00097 /// This local class encapsulates various parameters that can be used 00098 /// to tweak different aspects of this behaviour. 00099 class Params : public singleton<Params> { 00100 /// If this extricate behaviour were to spin the robot in each and 00101 /// every iteration, it might make the robot zigzag about excessively. 00102 /// Therefore, we have the behaviour check the total range of LGMD 00103 /// spike rates, i.e., max - min, and act only when this range crosses 00104 /// a threshold, i.e., when the range of spiking activity has a 00105 /// significant differential in it. If the range is lower than the 00106 /// above-mentioned threshold, it means all the locusts are 00107 /// experiencing a similar level of spiking and so extrication would 00108 /// simply result in erratic, noisy behaviour. 00109 /// 00110 /// This setting specifies the minimum range across all the LGMD 00111 /// readings that we expect to see before the simple, reactive 00112 /// LGMD-based extrication will kick in. It should be a positive 00113 /// floating point number. Its units are Hertz. 00114 float m_threshold ; 00115 00116 /// Once the above range threshold has been crossed, the behaviour can 00117 /// simply spin the robot towards the direction corresponding to 00118 /// minimal activity. However, there might be several readings that 00119 /// are the same or reasonably close to the minimum. In this 00120 /// situation, we would like to spin the robot by the least amount. 00121 /// 00122 /// The following setting specifies the maximum spike rate starting at 00123 /// the minimum that we consider "reasonably low." This extricate 00124 /// behaviour then looks at all the "reasonably low" LGMD readings and 00125 /// chooses the one that will result in the minimum amount of spin. 00126 /// 00127 /// The value of this setting should be a floating point number 00128 /// expressed in Hertz. 00129 float m_window ; 00130 00131 /// Users may specify what speed they would like the extrication to 00132 /// occur. 00133 /// 00134 /// CAUTION: It would be unwise to make this speed too high. 00135 float m_extricate_speed ; 00136 00137 /// In case the RPM sensor is configured to be off, we will need to 00138 /// specify the extricate "speed" in terms of PWM values as well. 00139 /// 00140 /// NOTE: All speed related behaviours should specify both a speed 00141 /// in m/s and a PWM value. 00142 /// 00143 /// CAUTION: For the extricate behaviour, it would be unwise to 00144 /// make the extrication PWM too high. 00145 int m_extricate_pwm ; 00146 00147 /// Usually, steering control is effected using the turn arbiter, 00148 /// which veers the robot in different directions while it moves, 00149 /// i.e., smooth car-like turns. However, the lgmd_extricate_tti 00150 /// behaviour also supports spin-style steering, i.e., momentarily 00151 /// stopping the robot and then turning it cw/ccw in-place. This flag 00152 /// turns on spin-style steering. By default, the behaviour uses the 00153 /// normal car-like steering mode. 00154 bool m_spin_style_steering ; 00155 00156 /// The number of milliseconds between successive iterations of this 00157 /// behaviour. 00158 /// 00159 /// WARNING: The ability to change a behaviour's update frequency is a 00160 /// very powerful feature whose misuse or abuse can wreak havoc! Be 00161 /// sure to use reasonable values for this setting. 00162 int m_update_delay ; 00163 00164 /// The location and size (within the Robolocust main window) of the 00165 /// goal behaviour's visualization. 00166 typedef Drawable::Geometry Geom ; // just for properly aligning accessors 00167 Geom m_geometry ; 00168 00169 /// Private constructor because this is a singleton. 00170 Params() ; 00171 00172 // Boilerplate code to make generic singleton design pattern work 00173 friend class singleton<Params> ; 00174 00175 public: 00176 /// Accessing the various parameters. 00177 //@{ 00178 static float threshold() {return instance().m_threshold ;} 00179 static float window() {return instance().m_window ;} 00180 static float extricate_speed() {return instance().m_extricate_speed ;} 00181 static int extricate_pwm() {return instance().m_extricate_pwm ;} 00182 static bool spin_style_steering(){return instance().m_spin_style_steering;} 00183 static int update_delay() {return instance().m_update_delay ;} 00184 static Geom geom() {return instance().m_geometry ;} 00185 //@} 00186 } ; 00187 00188 // Parameters initialization 00189 Params::Params() 00190 : m_threshold(clamp(conf("threshold", 50.0f), 0.1f, 1000.0f)), 00191 m_window(clamp(conf("window", 25.0f), 0.1f, 100.0f)), 00192 m_extricate_speed(clamp(conf("extricate_speed", 0.15f), 0.1f, 0.75f)), 00193 m_extricate_pwm(clamp(conf("extricate_pwm", 25), 10, 50)), 00194 m_spin_style_steering(conf("spin_style_steering", false)), 00195 m_update_delay(clamp(conf("update_delay", 1000), 1, 5000)), 00196 m_geometry(conf<std::string>("geometry", "0 0 10 10")) 00197 {} 00198 00199 } // end of local anonymous namespace encapsulating above helpers 00200 00201 //-------------------------- INITIALIZATION ----------------------------- 00202 00203 LGMDExtricateSimple::LGMDExtricateSimple() 00204 : base(Params::update_delay(), LOBE_LGMD_EXTRICATE_SIMPLE, Params::geom()) 00205 { 00206 start(LOBE_LGMD_EXTRICATE_SIMPLE) ; 00207 } 00208 00209 LGMDExtricateSimple::Command::Command() 00210 : drive(0), turn(0) 00211 {} 00212 00213 void LGMDExtricateSimple::pre_run() 00214 { 00215 if (! App::robot()) 00216 throw behavior_error(MOTOR_SYSTEM_MISSING) ; 00217 } 00218 00219 //---------------------- THE BEHAVIOUR'S ACTION ------------------------- 00220 00221 // Stuff a locust's direction and current LGMD spike rate into an STL pair 00222 static std::pair<float, float> get_locust_data(const LocustModel* L) 00223 { 00224 return std::make_pair(L->direction(), L->get_lgmd()) ; 00225 } 00226 00227 // Compare locust data according to their directions 00228 static bool 00229 dir_cmp(const std::pair<float, float>& a, const std::pair<float, float>& b) 00230 { 00231 return abs(a.first) < abs(b.first) ; 00232 } 00233 00234 // Compare locust data according to their LGMD spike rates 00235 static bool 00236 lgmd_cmp(const std::pair<float, float>& a, const std::pair<float, float>& b) 00237 { 00238 return a.second < b.second ; 00239 } 00240 00241 // This version of the extricate behaviour monitors the LGMD spike rates 00242 // of all the locusts and spins the robot to face the direction 00243 // corresponding to minimal spiking activity. 00244 void LGMDExtricateSimple::action() 00245 { 00246 typedef std::pair<float, float> DLP ; // locust direction-LGMD pair 00247 typedef std::vector<DLP> DLV ; 00248 DLV dlv ; 00249 00250 // Copy each locust's direction and current LGMD spike rate 00251 UpdateLock::begin_read() ; 00252 const App::LocustModels& L = App::locusts() ; 00253 dlv.reserve(L.size()) ; 00254 std::transform(L.begin(), L.end(), std::back_inserter(dlv), 00255 get_locust_data) ; 00256 UpdateLock::end_read() ; 00257 00258 // Sort the locusts according to their spike rates 00259 std::sort(dlv.begin(), dlv.end(), lgmd_cmp) ; 00260 00261 // Issue extrication commands only if spike rate range is significant, 00262 // i.e., there is enough variation in spiking across all the locusts. 00263 Command C ; 00264 if (dlv.back().second - dlv.front().second >= Params::threshold()) 00265 { 00266 // Starting at locust with least spiking activity, find locust 00267 // whose spike rate exceeds acceptable "window" of low readings. 00268 DLV::iterator end = std::find_if(dlv.begin(), dlv.end(), 00269 boost::bind(std::greater<float>(), 00270 boost::bind(get_second<DLP>, _1), 00271 dlv.front().second + Params::window())) ; 00272 00273 // Within the window of spikes acceptable as low, find the one that 00274 // involves least amount of turn from current heading and vote for 00275 // that direction. 00276 DLV::const_iterator min = std::min_element(dlv.begin(), end, dir_cmp) ; 00277 if (Params::spin_style_steering()) 00278 { 00279 C.turn = round(min->first) ; 00280 SpinArbiter::instance(). 00281 vote(base::name, new SpinArbiter::Vote(C.turn)) ; 00282 } 00283 else 00284 { 00285 const int T = random(TurnArbiter::turn_step(), 00286 TurnArbiter::turn_max()) ; 00287 switch (quadrant(min->first)) 00288 { 00289 case 1: 00290 C.drive = 1 ; C.turn = T ; 00291 break ; 00292 case 2: 00293 C.drive = -1 ; C.turn = -T ; 00294 break ; 00295 case 3: 00296 C.drive = -1 ; C.turn = T ; 00297 break ; 00298 case 4: 00299 C.drive = 1 ; C.turn = -T ; 00300 break ; 00301 default: // quadrant() shouldn't return anything outside [1,4] 00302 throw misc_error(LOGIC_ERROR) ; 00303 } 00304 SpeedArbiter::instance().vote(base::name, 00305 new SpeedArbiter::Vote(C.drive * Params::extricate_speed(), 00306 C.drive * Params::extricate_pwm())) ; 00307 TurnArbiter::instance().vote(base::name, 00308 new TurnArbiter::Vote(turn_vote_centered_at(C.turn))) ; 00309 } 00310 00311 // Metrics logging 00312 Metrics::Log log ; 00313 log << std::setw(Metrics::opw()) << std::left << base::name ; 00314 Map* M = App::map() ; 00315 if (M) 00316 log << M->current_pose() ; 00317 } 00318 00319 // Record stuff for visualization 00320 viz_lock() ; 00321 m_cmd = C ; 00322 viz_unlock() ; 00323 } 00324 00325 //--------------------------- VISUALIZATION ----------------------------- 00326 00327 #ifdef INVT_HAVE_LIBGL 00328 00329 // The visualization callback 00330 void LGMDExtricateSimple::render_me() 00331 { 00332 // Make local copies so that extricate thread isn't held up waiting 00333 // for visualization thread to complete. 00334 viz_lock() ; 00335 Command C = m_cmd ; 00336 viz_unlock() ; 00337 00338 // Draw principal axes (to make it easier to understand what's going 00339 // on with the force vectors and turn command). 00340 unit_view_volume() ; 00341 glColor3f(1, 1, 1) ; 00342 glBegin(GL_LINES) ; 00343 glVertex2i(0, -1) ; 00344 glVertex2i(0, 1) ; 00345 glVertex2i(-1, 0) ; 00346 glVertex2i( 1, 0) ; 00347 glEnd() ; 00348 00349 // Render the extrication decision's visualization 00350 if (C.drive == 0 && C.turn == 0) 00351 ; // no extrication took place 00352 else 00353 { 00354 Vector v(0.75f * cos(C.turn), 0.75f * sin(C.turn)) ; 00355 if (C.drive == 0) // differential steering 00356 glColor3f(1, 0, 0) ; 00357 else // normal, i.e., car-like, steering 00358 { 00359 v.i *= C.drive ; 00360 glColor3f(1, 1, 1) ; 00361 } 00362 draw_vector(v) ; 00363 } 00364 00365 // Label the visualization so that it is easy to tell which behaviour 00366 // is being visualized. Also show the magnitudes of the forces. 00367 restore_view_volume() ; 00368 text_view_volume() ; 00369 glColor3f(0, 1, 1) ; 00370 draw_label(3, 12, "LGMD Ext. Sim.") ; 00371 restore_view_volume() ; 00372 } 00373 00374 #endif 00375 00376 //----------------------------- CLEAN-UP -------------------------------- 00377 00378 LGMDExtricateSimple::~LGMDExtricateSimple(){} 00379 00380 //----------------------------------------------------------------------- 00381 00382 } // end of namespace encapsulating this file's definitions 00383 00384 /* So things look consistent in everyone's emacs... */ 00385 /* Local Variables: */ 00386 /* indent-tabs-mode: nil */ 00387 /* End: */