00001 /** 00002 \file Robots/LoBot/control/LoLGMDExtricateEMD.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::LGMDExtricateEMD 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/LoLGMDExtricateEMD.C $ 00039 // $Id: LoLGMDExtricateEMD.C 13840 2010-08-27 22:28:12Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/control/LoLGMDExtricateEMD.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/config/LoConfigHelpers.H" 00054 #include "Robots/LoBot/thread/LoUpdateLock.H" 00055 00056 #include "Robots/LoBot/misc/LoExcept.H" 00057 #include "Robots/LoBot/misc/LoRegistry.H" 00058 #include "Robots/LoBot/misc/singleton.hh" 00059 00060 #include "Robots/LoBot/util/LoGL.H" 00061 #include "Robots/LoBot/util/LoMath.H" 00062 00063 // OpenGL headers 00064 #ifdef INVT_HAVE_LIBGL 00065 #include <GL/gl.h> 00066 #endif 00067 00068 // Standard C++ headers 00069 #include <iomanip> 00070 #include <sstream> 00071 #include <algorithm> 00072 #include <memory> 00073 00074 //----------------------------- NAMESPACE ------------------------------- 00075 00076 namespace lobot { 00077 00078 //-------------------------- KNOB TWIDDLING ----------------------------- 00079 00080 namespace { 00081 00082 // Retrieve settings from extricate section of config file 00083 template<typename T> 00084 inline T conf(const std::string& key, const T& default_value) 00085 { 00086 return get_conf<T>(LOBE_LGMD_EXTRICATE_EMD, key, default_value) ; 00087 } 00088 00089 /// This local class encapsulates various parameters that can be used 00090 /// to tweak different aspects of the lgmd_extricate_emd behaviour. 00091 class Params : public singleton<Params> { 00092 /// This behaviour's extrication commands will kick in only when the 00093 /// magnitude of the spiking vector computed by the EMD array exceeds 00094 /// the threshold specified by this setting. 00095 float m_threshold ; 00096 00097 /// Users may specify what speed they would like the extrication to 00098 /// occur. 00099 /// 00100 /// CAUTION: It would be unwise to make this speed too high. 00101 float m_extricate_speed ; 00102 00103 /// In case the RPM sensor is configured to be off, we will need to 00104 /// specify the extricate "speed" in terms of PWM values as well. 00105 /// 00106 /// NOTE: All speed related behaviours should specify both a speed in 00107 /// m/s and a PWM value. 00108 /// 00109 /// CAUTION: For the extricate behaviour, it would be unwise to make 00110 /// the extrication PWM too high. 00111 int m_extricate_pwm ; 00112 00113 /// Usually, steering control is effected using the turn arbiter, 00114 /// which veers the robot towards different direction while it moves. 00115 /// However, the lgmd_extricate behaviour also supports spin-style 00116 /// steering, i.e., momentarily stopping the robot and then turning it 00117 /// cw/ccw in-place. This flag turns on spin-style steering. By 00118 /// default, the behaviour uses the normal steering mode. 00119 bool m_spin_style_steering ; 00120 00121 /// The number of milliseconds between successive iterations of this 00122 /// behaviour. 00123 /// 00124 /// WARNING: The ability to change a behaviour's update frequency is a 00125 /// very powerful feature whose misuse or abuse can wreak havoc! Be 00126 /// sure to use reasonable values for this setting. 00127 int m_update_delay ; 00128 00129 /// The location and size (within the Robolocust main window) of the 00130 /// lgmd_extricate_emd behaviour's visualization. 00131 Drawable::Geometry m_geometry ; 00132 00133 /// Private constructor because this is a singleton. 00134 Params() ; 00135 00136 // Boilerplate code to make generic singleton design pattern work 00137 friend class singleton<Params> ; 00138 00139 public: 00140 /// Accessing the various parameters. 00141 //@{ 00142 static float threshold() {return instance().m_threshold ;} 00143 static float extricate_speed() {return instance().m_extricate_speed ;} 00144 static int extricate_pwm() {return instance().m_extricate_pwm ;} 00145 static bool spin_style_steering(){return instance().m_spin_style_steering;} 00146 static int update_delay() {return instance().m_update_delay ;} 00147 static Drawable::Geometry geometry() {return instance().m_geometry ;} 00148 //@} 00149 } ; 00150 00151 // Parameters initialization 00152 Params::Params() 00153 : m_threshold(conf("threshold", 1000.0f)), 00154 m_extricate_speed(clamp(conf("extricate_speed", 0.15f), 0.1f, 0.75f)), 00155 m_extricate_pwm(clamp(conf("extricate_pwm", 25), 10, 50)), 00156 m_spin_style_steering(conf("spin_style_steering", false)), 00157 m_update_delay(clamp(conf("update_delay", 150), 1, 1000)), 00158 m_geometry(conf<std::string>("geometry", "0 0 10 10")) 00159 {} 00160 00161 } // end of local anonymous namespace encapsulating above helpers 00162 00163 //-------------------------- INITIALIZATION ----------------------------- 00164 00165 LGMDExtricateEMD::LGMDExtricateEMD() 00166 : base(Params::update_delay(), LOBE_LGMD_EXTRICATE_EMD, Params::geometry()) 00167 { 00168 start(LOBE_LGMD_EXTRICATE_EMD) ; 00169 } 00170 00171 void LGMDExtricateEMD::pre_run() 00172 { 00173 if (! App::robot()) 00174 throw behavior_error(MOTOR_SYSTEM_MISSING) ; 00175 00176 UpdateLock::begin_read() ; 00177 const App::LocustModels& L = App::locusts() ; 00178 const int N = L.size() - 1 ; 00179 if (N < 1) { 00180 UpdateLock::end_read() ; 00181 throw behavior_error(NOT_ENOUGH_LOCUSTS) ; 00182 } 00183 00184 m_emds.reserve(N) ; 00185 for (int i = 0; i < N; ++i) { 00186 float direction = (L[i]->direction() + L[i + 1]->direction())/2.0f ; 00187 m_emds.push_back(new LMD(L[i], L[i + 1], direction)) ; 00188 } 00189 UpdateLock::end_read() ; 00190 } 00191 00192 LGMDExtricateEMD::Command::Command() 00193 : drive(0), turn(0) 00194 {} 00195 00196 //---------------------- THE BEHAVIOUR'S ACTION ------------------------- 00197 00198 // This version of the extricate behaviour feeds the LGMD spikes of all 00199 // the locusts into an array of elementary motion detectors and issues 00200 // appropriate turn and drive commands based on the output of the EMD 00201 // array. 00202 void LGMDExtricateEMD::action() 00203 { 00204 // Use EMD array to determine the direction in which maximal spiking 00205 // activity is currently ocurring. 00206 UpdateLock::begin_read() ; 00207 const int N = m_emds.size() ; 00208 Vector emd ; 00209 for (int i = 0; i < N; ++i) 00210 emd += m_emds[i]->update() ; 00211 UpdateLock::end_read() ; 00212 00213 // Issue motor commands based on EMD vector's direction and magnitude 00214 Command C ; 00215 if (magnitude(emd) >= Params::threshold()) 00216 { 00217 const int T = random(TurnArbiter::turn_step(), TurnArbiter::turn_max()); 00218 int s = 0 ; // in-place turn amount when spin-style steering is in effect 00219 switch (octant(direction(emd))) 00220 { 00221 case 1: // obstacle in front and on the left 00222 C.drive = -1 ; C.turn = T ; s = -T ; 00223 break ; 00224 case 2: // obstacle on the left 00225 case 3: // obstacle on the left 00226 case 4: // obstacle behind and on the left 00227 C.drive = 1 ; C.turn = -T ; s = -T ; 00228 break ; 00229 case 5: // obstacle behind and on the right 00230 case 6: // obstacle on the right 00231 case 7: // obstacle on the right 00232 C.drive = 1 ; C.turn = T ; s = T ; 00233 break ; 00234 case 8: // obstacle in front and on the right 00235 C.drive = -1 ; C.turn = -T ; s = T ; 00236 break ; 00237 default: // hunh?!? octant() shouldn't return anything outside [1,8] 00238 throw misc_error(LOGIC_ERROR) ; 00239 } 00240 00241 if (Params::spin_style_steering()) 00242 SpinArbiter::instance().vote(base::name, new SpinArbiter::Vote(s)) ; 00243 else 00244 { 00245 SpeedArbiter::instance().vote(base::name, 00246 new SpeedArbiter::Vote(C.drive * Params::extricate_speed(), 00247 C.drive * Params::extricate_pwm())) ; 00248 TurnArbiter::instance().vote(base::name, 00249 new TurnArbiter::Vote(turn_vote_centered_at(C.turn))) ; 00250 } 00251 00252 Metrics::Log log ; 00253 log << std::setw(Metrics::opw()) << std::left << base::name ; 00254 Map* M = App::map() ; 00255 if (M) 00256 log << M->current_pose() ; 00257 } 00258 00259 // Record stuff for visualization 00260 viz_lock() ; 00261 m_emd_vector = emd ; 00262 m_cmd = C ; 00263 viz_unlock() ; 00264 } 00265 00266 //--------------------------- VISUALIZATION ----------------------------- 00267 00268 #ifdef INVT_HAVE_LIBGL 00269 00270 // Helper function to convert the magnitude of a vector into a string, 00271 // prefixing the supplied single character label to the mag string. 00272 static std::string mag(char label, const Vector& v) 00273 { 00274 using namespace std ; 00275 00276 std::ostringstream v_str ; 00277 v_str << label << ": " << fixed << setprecision(1) << magnitude(v) ; 00278 return v_str.str() ; 00279 } 00280 00281 // The visualization callback 00282 void LGMDExtricateEMD::render_me() 00283 { 00284 // Make local copies so that extricate thread isn't held up waiting 00285 // for visualization thread to complete. 00286 viz_lock() ; 00287 Command C = m_cmd ; 00288 Vector E = m_emd_vector ; 00289 viz_unlock() ; 00290 00291 // Draw principal axes and diagonals marking the octants (to make it 00292 // easier to understand what's going on with the EMD vector and turn 00293 // command). 00294 unit_view_volume() ; 00295 glColor3f(1, 1, 1) ; 00296 glBegin(GL_LINES) ; 00297 glVertex2i(0, -1) ; 00298 glVertex2i(0, 1) ; 00299 glVertex2i(-1, 0) ; 00300 glVertex2i( 1, 0) ; 00301 glEnd() ; 00302 00303 glPushAttrib(GL_LINE_BIT) ; 00304 glEnable(GL_LINE_STIPPLE) ; 00305 glLineStipple(1, 0x0F0F) ; 00306 glBegin(GL_LINES) ; 00307 glVertex2i(-1, +1) ; 00308 glVertex2i(+1, -1) ; 00309 00310 glVertex2i(+1, +1) ; 00311 glVertex2i(-1, -1) ; 00312 glEnd() ; 00313 glPopAttrib() ; 00314 00315 // Render the extrication decision's visualization 00316 if (C.drive == 0 && C.turn == 0) 00317 ; // no extrication took place 00318 else 00319 { 00320 glColor3f(1, 0, 0) ; 00321 draw_vector(normalized(E)) ; 00322 00323 glColor3f(0, 1, 0) ; 00324 draw_vector(Vector(0.75f * C.drive * cos(C.turn), 0.75f * sin(C.turn))) ; 00325 } 00326 00327 // Label the visualization so that it is easy to tell which behaviour 00328 // is being visualized. Also show the magnitudes of the forces. 00329 restore_view_volume() ; 00330 text_view_volume() ; 00331 glColor3f(0, 1, 1) ; 00332 draw_label(3, 12, "LGMD Ext. EMD") ; 00333 00334 glColor3f(0.15f, 0.75f, 0.85f) ; 00335 draw_label(3, m_geometry.height - 4, mag('E', E).c_str()) ; 00336 restore_view_volume() ; 00337 } 00338 00339 #endif 00340 00341 //----------------------------- CLEAN-UP -------------------------------- 00342 00343 LGMDExtricateEMD::~LGMDExtricateEMD() 00344 { 00345 purge_container(m_emds) ; 00346 } 00347 00348 //----------------------------------------------------------------------- 00349 00350 } // end of namespace encapsulating this file's definitions 00351 00352 /* So things look consistent in everyone's emacs... */ 00353 /* Local Variables: */ 00354 /* indent-tabs-mode: nil */ 00355 /* End: */