00001 /** 00002 \file Robots/LoBot/control/LoLGMDExtricateVFF.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::LGMDExtricateVFF 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/LoLGMDExtricateVFF.C $ 00039 // $Id: LoLGMDExtricateVFF.C 13840 2010-08-27 22:28:12Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/control/LoLGMDExtricateVFF.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 #include "Robots/LoBot/config/LoConfigHelpers.H" 00055 #include "Robots/LoBot/thread/LoUpdateLock.H" 00056 00057 #include "Robots/LoBot/misc/LoExcept.H" 00058 #include "Robots/LoBot/misc/LoRegistry.H" 00059 #include "Robots/LoBot/misc/singleton.hh" 00060 00061 #include "Robots/LoBot/util/LoGL.H" 00062 #include "Robots/LoBot/util/LoMath.H" 00063 #include "Robots/LoBot/util/range.hh" 00064 00065 // OpenGL headers 00066 #ifdef INVT_HAVE_LIBGL 00067 #include <GL/gl.h> 00068 #endif 00069 00070 // Standard C++ headers 00071 #include <iomanip> 00072 #include <sstream> 00073 #include <algorithm> 00074 #include <memory> 00075 00076 //----------------------------- NAMESPACE ------------------------------- 00077 00078 namespace lobot { 00079 00080 //-------------------------- KNOB TWIDDLING ----------------------------- 00081 00082 namespace { 00083 00084 // Retrieve settings from lgmd_extricate_vff section of config file 00085 template<typename T> 00086 static inline T conf(const std::string& key, const T& default_value) 00087 { 00088 return get_conf<T>(LOBE_LGMD_EXTRICATE_VFF, key, default_value) ; 00089 } 00090 00091 /// This inner class encapsulates various parameters that can be used to 00092 /// tweak different aspects of the lgmd_extricate_vff behaviour. 00093 class Params : public singleton<Params> { 00094 /// A locust whose LGMD spike rate exceeds a certain threshold will 00095 /// result in a repulsive force along the direction in which that 00096 /// locust is looking. Conversely, when a locust's LGMD spike rate 00097 /// falls below this threshold, it will cause an attractive force. 00098 /// When all the forces are put together, we will get a final force 00099 /// vector that can be used to drive and steer the robot. 00100 /// 00101 /// This setting specifies the value of the above-mentioned 00102 /// threshold in spikes per second (i.e., Hz). It should be a 00103 /// reasonable value that lies within the [min, max] spike range 00104 /// specified in the chosen LGMD model's settings section. In case 00105 /// the robot is connected to actual locusts, then this setting's 00106 /// value should be some reasonable empirically determined value. 00107 float m_threshold ; 00108 00109 /// To prevent this behaviour from becoming overly sensitive, we 00110 /// require the LGMD spike rates of a minimum number of locusts to 00111 /// exceed the above threshold before the behaviour engages its 00112 /// extrication algorithm. 00113 /// 00114 /// This setting specifies the above-mentioned minimum locust 00115 /// count. It should be a reasonable number <= the number of 00116 /// locusts actually present. 00117 int m_count ; 00118 00119 /// In some situations, it can be useful to amplify the magnitudes 00120 /// of the attractive and repulsive force vectors. These two 00121 /// settings specify the values for the amplification factors. 00122 /// Numbers greater then one will amplify the vectors; numbers 00123 /// between 0 and 1 will reduce the magnitudes of the force 00124 /// vectors; negative numbers will negate the directions of the 00125 /// force vectors. 00126 float m_att_amp, m_rep_amp ; 00127 00128 /// Users may specify what speed they would like the extrication to 00129 /// occur. 00130 /// 00131 /// CAUTION: It would be unwise to make this speed too high. 00132 float m_extricate_speed ; 00133 00134 /// In case the RPM sensor is configured to be off, we will need to 00135 /// specify the extricate "speed" in terms of PWM values as well. 00136 /// 00137 /// NOTE: All speed related behaviours should specify both a speed 00138 /// in m/s and a PWM value. 00139 /// 00140 /// CAUTION: For the extricate behaviour, it would be unwise to 00141 /// make the extrication PWM too high. 00142 int m_extricate_pwm ; 00143 00144 /// Usually, steering control is effected using the turn arbiter, 00145 /// which veers the robot in different directions while it moves, 00146 /// i.e., smooth car-like turns. However, the lgmd_extricate_vff 00147 /// behaviour also supports spin-style steering, i.e., momentarily 00148 /// stopping the robot and then turning it cw/ccw in-place. This flag 00149 /// turns on spin-style steering. By default, the behaviour uses the 00150 /// normal car-like steering mode. 00151 bool m_spin_style_steering ; 00152 00153 /// The number of milliseconds between successive iterations of this 00154 /// behaviour. 00155 /// 00156 /// WARNING: The ability to change a behaviour's update frequency is a 00157 /// very powerful feature whose misuse or abuse can wreak havoc! Be 00158 /// sure to use reasonable values for this setting. 00159 int m_update_delay ; 00160 00161 /// The location and size (within the Robolocust main window) of the 00162 /// lgmd_extricate_vff behaviour's visualization. 00163 Drawable::Geometry m_geometry ; 00164 00165 /// Private constructor because this is a singleton. 00166 Params() ; 00167 00168 // Boilerplate code to make generic singleton design pattern work 00169 friend class singleton<Params> ; 00170 00171 public: 00172 /// Accessing the various parameters. 00173 //@{ 00174 static float threshold() {return instance().m_threshold ;} 00175 static int count() {return instance().m_count ;} 00176 static float att_amp() {return instance().m_att_amp ;} 00177 static float rep_amp() {return instance().m_rep_amp ;} 00178 static float extricate_speed() {return instance().m_extricate_speed ;} 00179 static int extricate_pwm() {return instance().m_extricate_pwm ;} 00180 static bool spin_style_steering(){return instance().m_spin_style_steering;} 00181 static int update_delay() {return instance().m_update_delay ;} 00182 static Drawable::Geometry geometry() {return instance().m_geometry ;} 00183 //@} 00184 } ; 00185 00186 // Parameters initialization 00187 Params::Params() 00188 : m_threshold(conf("threshold", 250)), 00189 m_count(conf("count", 1)), 00190 m_att_amp(conf("attractive_amplifier", 1.0f)), 00191 m_rep_amp(conf("repulsive_amplifier", 1.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", 150), 1, 1000)), 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 LGMDExtricateVFF::LGMDExtricateVFF() 00204 : base(Params::update_delay(), LOBE_LGMD_EXTRICATE_VFF, Params::geometry()) 00205 { 00206 start(LOBE_LGMD_EXTRICATE_VFF) ; 00207 } 00208 00209 LGMDExtricateVFF::Command::Command() 00210 : drive(0), turn(0) 00211 {} 00212 00213 void LGMDExtricateVFF::pre_run() 00214 { 00215 if (! App::robot()) 00216 throw behavior_error(MOTOR_SYSTEM_MISSING) ; 00217 } 00218 00219 //---------------------- THE BEHAVIOUR'S ACTION ------------------------- 00220 00221 // Helper function object to compute the attractive and repulsive forces 00222 // for a block and maintain a running total. 00223 namespace { 00224 00225 // DEVNOTE: This function object is meant to be used with the STL 00226 // for_each algorithm. The goal is to compute the total attractive and 00227 // repulsive forces using the current spike rates of all the availbale 00228 // LGMD's. Also, we want to count how many locusts have exceeded the 00229 // preconfigured spiking threshold. 00230 // 00231 // Since std::for_each() passes its function object parameter by value, 00232 // the call site will not get back the force vectors and threshold count 00233 // computed by this function object unless special measures are taken to 00234 // facilitate the return of these quantities. 00235 // 00236 // One possibility is to use object references. That is, the att, rep and 00237 // num_rep data members defined below can be of type Vector& and int& 00238 // respectively. In LGMDExtricateVFF::action(), we can create local 00239 // variables for the result vectors and threshold count, passing 00240 // references to them to this function object's constructor. Thus, when 00241 // std::for_each() is done, LGMDExtricateVFF::action() will have the 00242 // results. 00243 // 00244 // Alternatively, recalling that the STL for_each algorithm returns the 00245 // function object passed to it at the end of the for_each() function, we 00246 // can have the caller not discard for_each's return value (as is 00247 // typically done) and, instead, use the function's return value to 00248 // retrieve the force vectors and threshold count computed. 00249 // 00250 // This second approach is the one adopted here. 00251 class compute_forces { 00252 mutable Vector att ; // attractive force 00253 mutable Vector rep ; // repulsive force 00254 mutable int num_rep ; // number of repulsive vectors 00255 public: 00256 compute_forces() ; 00257 void operator()(const LocustModel*) const ; 00258 00259 const Vector& attractive() const {return att ;} 00260 const Vector& repulsive() const {return rep ;} 00261 const int repulsive_count() const {return num_rep ;} 00262 } ; 00263 00264 compute_forces::compute_forces() 00265 : num_rep(0) 00266 {} 00267 00268 // When the LGMD spike rate for the locust under consideration exceeds 00269 // the spike threshold set in the config file, it means that particular 00270 // locust has sensed an object on a collisional trajectory towards it. 00271 // When the spike rate distance measurement is below the threshold, there 00272 // ought not be anything nearby. 00273 // 00274 // If we think of the spike rate threshold as a kind of membrane, then 00275 // the difference between an actual LGMD spike rate and the threshold is 00276 // a measure of the amount by which the object being sensed has 00277 // penetrated the membrane (if the difference is positive; if negative, 00278 // then it tells us how "far away" the object is from the membrane). 00279 // 00280 // This difference, which we can think of as an error, can be used to 00281 // size the force vectors. The greater the error, the greater the 00282 // magnitude of the force. 00283 void compute_forces::operator()(const LocustModel* L) const 00284 { 00285 float lgmd = L->get_lgmd() ; 00286 float e = lgmd - Params::threshold() ; // threshold "error" 00287 Vector f = Vector(cos(L->direction()), sin(L->direction())) ; 00288 if (e < 0) // LGMD is below threshold 00289 att += -Params::att_amp() * e * f ; 00290 else { // LGMD spike rate is above threshold 00291 rep += -Params::rep_amp() * e * f ; 00292 ++num_rep ; 00293 } 00294 } 00295 00296 } // end of local namespace encapsulating above helpers 00297 00298 // This version of the extricate behaviour monitors the LGMD spike rates 00299 // of all the locusts and issues appropriate turn and drive commands when 00300 // things get too close by computing virtual attractive and repulsive 00301 // forces on the basis of the spikes. 00302 void LGMDExtricateVFF::action() 00303 { 00304 // Compute the attractive and repulsive forces and also the number of 00305 // LGMD's that have exceeded the spiking threshold. 00306 UpdateLock::begin_read() ; 00307 compute_forces force_field = 00308 std::for_each(App::locusts().begin(), App::locusts().end(), 00309 compute_forces()) ; 00310 UpdateLock::end_read() ; 00311 00312 // If the danger zone has been penetrated, use the attractive and 00313 // repulsive forces to determine correct motor commands. 00314 Command C ; 00315 Vector A, R, F ; 00316 if (force_field.repulsive_count() >= Params::count()) 00317 { 00318 A = force_field.attractive() ; 00319 R = force_field.repulsive() ; 00320 F = A + R ; 00321 00322 const int T = random(TurnArbiter::turn_step(), TurnArbiter::turn_max()); 00323 switch (quadrant(direction(F))) 00324 { 00325 case 1: 00326 C.drive = 1 ; C.turn = T ; 00327 break ; 00328 case 2: 00329 C.drive = -1 ; C.turn = -T ; 00330 break ; 00331 case 3: 00332 C.drive = -1 ; C.turn = T ; 00333 break ; 00334 case 4: 00335 C.drive = 1 ; C.turn = -T ; 00336 break ; 00337 default: // hunh?!? quadrant() shouldn't return anything outside [1,4] 00338 throw misc_error(LOGIC_ERROR) ; 00339 } 00340 00341 if (Params::spin_style_steering()) 00342 SpinArbiter::instance().vote(base::name, 00343 new SpinArbiter::Vote(C.turn)) ; 00344 else 00345 { 00346 SpeedArbiter::instance().vote(base::name, 00347 new SpeedArbiter::Vote(C.drive * Params::extricate_speed(), 00348 C.drive * Params::extricate_pwm())) ; 00349 TurnArbiter::instance().vote(base::name, 00350 new TurnArbiter::Vote(turn_vote_centered_at(C.turn))) ; 00351 } 00352 00353 Metrics::Log log ; 00354 log << std::setw(Metrics::opw()) << std::left << base::name ; 00355 Map* M = App::map() ; 00356 if (M) 00357 log << M->current_pose() ; 00358 } 00359 00360 // Record stuff for visualization 00361 viz_lock() ; 00362 m_attractive = A ; 00363 m_repulsive = R ; 00364 m_total_force = F ; 00365 00366 m_cmd = C ; 00367 viz_unlock() ; 00368 } 00369 00370 //--------------------------- VISUALIZATION ----------------------------- 00371 00372 #ifdef INVT_HAVE_LIBGL 00373 00374 // Helper function to convert the magnitude of a vector into a string, 00375 // prefixing the supplied single character label to the mag string. 00376 static std::string mag(char label, const Vector& v) 00377 { 00378 using namespace std ; 00379 00380 std::ostringstream v_str ; 00381 v_str << label << ": " << fixed << setprecision(1) << magnitude(v) ; 00382 return v_str.str() ; 00383 } 00384 00385 // The visualization callback 00386 void LGMDExtricateVFF::render_me() 00387 { 00388 // Make local copies so that extricate thread isn't held up waiting 00389 // for visualization thread to complete. 00390 viz_lock() ; 00391 Command C = m_cmd ; 00392 Vector A = m_attractive ; 00393 Vector R = m_repulsive ; 00394 Vector T = m_total_force ; 00395 viz_unlock() ; 00396 00397 // Draw principal axes (to make it easier to understand what's going 00398 // on with the force vectors and turn command). 00399 unit_view_volume() ; 00400 glColor3f(1, 1, 1) ; 00401 glBegin(GL_LINES) ; 00402 glVertex2i(0, -1) ; 00403 glVertex2i(0, 1) ; 00404 glVertex2i(-1, 0) ; 00405 glVertex2i( 1, 0) ; 00406 glEnd() ; 00407 00408 // Render the extrication decision's visualization 00409 if (C.drive == 0 && C.turn == 0) 00410 ; // no extrication took place 00411 else 00412 { 00413 glColor3f(0, 1, 0) ; 00414 draw_vector(normalized(A)) ; 00415 00416 glColor3f(1, 0, 0) ; 00417 draw_vector(normalized(R)) ; 00418 00419 glColor3f(0.15f, 0.75f, 0.85f) ; 00420 draw_vector(normalized(T)) ; 00421 00422 glColor3f(1, 1, 1) ; 00423 draw_vector(Vector(0.75f * C.drive * cos(C.turn), 0.75f * sin(C.turn))) ; 00424 } 00425 00426 // Label the visualization so that it is easy to tell which behaviour 00427 // is being visualized. Also show the magnitudes of the forces. 00428 restore_view_volume() ; 00429 text_view_volume() ; 00430 glColor3f(0, 1, 1) ; 00431 draw_label(3, 12, "LGMD Ext. VFF") ; 00432 00433 glColor3f(0, 1, 0) ; 00434 draw_label(3, m_geometry.height - 28, mag('A', A).c_str()) ; 00435 00436 glColor3f(1, 0, 0) ; 00437 draw_label(3, m_geometry.height - 16, mag('R', R).c_str()) ; 00438 00439 glColor3f(0.15f, 0.75f, 0.85f) ; 00440 draw_label(3, m_geometry.height - 4, mag('T', T).c_str()) ; 00441 restore_view_volume() ; 00442 } 00443 00444 #endif 00445 00446 //----------------------------- CLEAN-UP -------------------------------- 00447 00448 LGMDExtricateVFF::~LGMDExtricateVFF(){} 00449 00450 //----------------------------------------------------------------------- 00451 00452 } // end of namespace encapsulating this file's definitions 00453 00454 /* So things look consistent in everyone's emacs... */ 00455 /* Local Variables: */ 00456 /* indent-tabs-mode: nil */ 00457 /* End: */