00001 /** 00002 \file Robots/LoBot/control/LoLGMDExtricateTTI.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::LGMDExtricateTTI 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/LoLGMDExtricateTTI.C $ 00039 // $Id: LoLGMDExtricateTTI.C 14022 2010-09-23 18:49:14Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/control/LoLGMDExtricateTTI.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 <limits> 00073 00074 //----------------------------- NAMESPACE ------------------------------- 00075 00076 namespace lobot { 00077 00078 //-------------------------- KNOB TWIDDLING ----------------------------- 00079 00080 namespace { 00081 00082 // Retrieve settings from lgmd_extricate_tti 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_TTI, key, default_value) ; 00087 } 00088 00089 /// This inner class encapsulates various parameters that can be used 00090 /// to tweak different aspects of the lgmd_extricate_tti behaviour. 00091 class Params : public singleton<Params> { 00092 /// In order to build a virtual force field made up of repulsive as 00093 /// well as attractive forces, we use the distance estimates plus a 00094 /// threshold. When a distance estimate is below this threshold it 00095 /// will result in a repulsive force along the direction in which 00096 /// that locust is looking. Conversely, when a distance estimate 00097 /// exceeds the same threshold, it will cause an attractive force 00098 /// in that locust's direction. The sum of all these force vectors 00099 /// will result in a vector that can be used to drive and steer the 00100 /// robot away from obstacles. 00101 /// 00102 /// This setting specifies the value of the above-mentioned 00103 /// threshold in millimeters. 00104 float m_threshold ; 00105 00106 /// To prevent this behaviour from becoming overly sensitive, we 00107 /// require the distance estimates corresponding to some minimum 00108 /// number of locusts to fall below the above threshold before the 00109 /// behaviour engages its extrication algorithm. 00110 /// 00111 /// This setting specifies the minimum number of distance estimates 00112 /// that must fall below the distance threshold before extrication 00113 /// will occur. It should be a reasonable number <= the number of 00114 /// locusts actually present. 00115 int m_count ; 00116 00117 /// In some situations, it can be useful to amplify the magnitudes 00118 /// of the attractive and repulsive force vectors. These two 00119 /// settings specify the values for the amplification factors. 00120 /// Numbers greater then one will amplify the vectors; numbers 00121 /// between 0 and 1 will reduce the magnitudes of the force 00122 /// vectors; negative numbers will negate the directions of the 00123 /// force vectors. 00124 float m_att_amp, m_rep_amp ; 00125 00126 /// When the robot is backing up due to an extrication command, the 00127 /// LGMD's fire a small amount. Often, the lgmd_extricate_tti 00128 /// behaviour construes this as further actionable input and issues 00129 /// yet more back-up commands, which can result in extended periods 00130 /// of reverse driving. These are entirely spurious extrications and 00131 /// should not occur. This setting helps with the above-mentioned 00132 /// problem by specifying a threshold speed (in m/s) that must be met 00133 /// before lgmd_extricate_tti will kick in (or interfere). 00134 /// 00135 /// By setting this to a small positive quantity such as 0.05 or 0.1, 00136 /// we can ensure that lgmd_extricate_tti remains passive when the 00137 /// robot is backing up (wherein it will report a negative speed). To 00138 /// disable this threshold check, simply use a negative number with a 00139 /// magnitude larger than the robot's top speed (e.g., -10). 00140 /// 00141 /// NOTE: Using a large positive number for this setting (e.g., 10) 00142 /// will result in effectively disabling the lgmd_extricate_tti 00143 /// behaviour. In fact, setting this configuration value to something 00144 /// greater than the robot's average cruising speed will pretty much 00145 /// disable the behaviour. An easier way to do that is to simply not 00146 /// include it in the config file's list of active behaviours. That 00147 /// way, it won't uselessly consume CPU and RAM. Therefore, it would 00148 /// be best to stick to a value such as 0.05 or 0.1m/s. 00149 float m_interference_threshold ; 00150 00151 /// Users may specify what speed they would like the extrication to 00152 /// occur. 00153 /// 00154 /// CAUTION: It would be unwise to make this speed too high. 00155 float m_extricate_speed ; 00156 00157 /// In case the RPM sensor is configured to be off, we will need to 00158 /// specify the extricate "speed" in terms of PWM values as well. 00159 /// 00160 /// NOTE: All speed related behaviours should specify both a speed 00161 /// in m/s and a PWM value. 00162 /// 00163 /// CAUTION: For the extricate behaviour, it would be unwise to 00164 /// make the extrication PWM too high. 00165 int m_extricate_pwm ; 00166 00167 /// Usually, steering control is effected using the turn arbiter, 00168 /// which veers the robot in different directions while it moves, 00169 /// i.e., smooth car-like turns. However, the lgmd_extricate_tti 00170 /// behaviour also supports spin-style steering, i.e., momentarily 00171 /// stopping the robot and then turning it cw/ccw in-place. This flag 00172 /// turns on spin-style steering. By default, the behaviour uses the 00173 /// normal car-like steering mode. 00174 bool m_spin_style_steering ; 00175 00176 /// Normally, this behaviour will only log an entry stating that it 00177 /// made a steering decision. However, with this flag, we can make it 00178 /// also log the states of each of its time-to-impact estimators. 00179 bool m_log_tti_predictions ; 00180 00181 /// The number of milliseconds between successive iterations of this 00182 /// behaviour. 00183 /// 00184 /// WARNING: The ability to change a behaviour's update frequency is a 00185 /// very powerful feature whose misuse or abuse can wreak havoc! Be 00186 /// sure to use reasonable values for this setting. 00187 int m_update_delay ; 00188 00189 /// The location and size (within the Robolocust main window) of the 00190 /// lgmd_extricate_tti behaviour's visualization. 00191 typedef Drawable::Geometry Geom ; // just so accessor definitions line-up 00192 Drawable::Geometry m_geometry ; 00193 00194 /// Private constructor because this is a singleton. 00195 Params() ; 00196 00197 // Boilerplate code to make generic singleton design pattern work 00198 friend class singleton<Params> ; 00199 00200 public: 00201 /// Accessing the various parameters. 00202 //@{ 00203 static float threshold() {return instance().m_threshold ;} 00204 static int count() {return instance().m_count ;} 00205 static float att_amp() {return instance().m_att_amp ;} 00206 static float rep_amp() {return instance().m_rep_amp ;} 00207 static float extricate_speed() {return instance().m_extricate_speed ;} 00208 static int extricate_pwm() {return instance().m_extricate_pwm ;} 00209 static bool spin_style_steering(){return instance().m_spin_style_steering;} 00210 static bool log_tti_predictions(){return instance().m_log_tti_predictions;} 00211 static int update_delay() {return instance().m_update_delay ;} 00212 static Geom geometry() {return instance().m_geometry ;} 00213 static float interference_threshold() { 00214 return instance().m_interference_threshold ; 00215 } 00216 //@} 00217 } ; 00218 00219 // Parameters initialization 00220 Params::Params() 00221 : m_threshold(clamp(conf("threshold", 300.0f), 50.0f, 1000.0f)), 00222 m_count(clamp(conf("count", 3), 1, 180)), 00223 m_att_amp(conf("attractive_amplifier", 1.0f)), 00224 m_rep_amp(conf("repulsive_amplifier", 1.0f)), 00225 m_interference_threshold(conf("interference_threshold", 0.1f)), 00226 m_extricate_speed(clamp(conf("extricate_speed", 0.15f), 0.1f, 0.75f)), 00227 m_extricate_pwm(clamp(conf("extricate_pwm", 25), 10, 50)), 00228 m_spin_style_steering(conf("spin_style_steering", false)), 00229 m_log_tti_predictions(conf("log_tti_predictions", false)), 00230 m_update_delay(clamp(conf("update_delay", 150), 1, 1000)), 00231 m_geometry(conf<std::string>("geometry", "0 0 -1 -1")) 00232 {} 00233 00234 } // end of local anonymous namespace encapsulating above helpers 00235 00236 //-------------------------- INITIALIZATION ----------------------------- 00237 00238 LGMDExtricateTTI::LGMDExtricateTTI() 00239 : base(Params::update_delay(), LOBE_LGMD_EXTRICATE_TTI, Params::geometry()) 00240 { 00241 start(LOBE_LGMD_EXTRICATE_TTI) ; 00242 } 00243 00244 LGMDExtricateTTI::Command::Command() 00245 : drive(0), turn(0) 00246 {} 00247 00248 // Before the lgmd_extricate_tti behaviour's thread begins its regular 00249 // action processing loop, we must ensure that the sensor model for the 00250 // Bayesian time-to-impact estimation is properly setup and that the TTI 00251 // estimators for each locust have been created and are ready for use. 00252 void LGMDExtricateTTI::pre_run() 00253 { 00254 if (! App::robot()) 00255 throw behavior_error(MOTOR_SYSTEM_MISSING) ; 00256 00257 const App::LocustModels& L = App::locusts() ; 00258 const int N = L.size() ; 00259 m_tti.reserve(N) ; 00260 for (int i = 0; i < N; ++i) 00261 m_tti.push_back(new TTIEstimator(L[i])) ; 00262 } 00263 00264 //---------------------- THE BEHAVIOUR'S ACTION ------------------------- 00265 00266 static void log_tti_predictions(float speed, const TTIEstimator* E) 00267 { 00268 using std::setw ; using std::left ; using std::right ; 00269 using std::fixed ; using std::setprecision ; 00270 00271 Metrics::Log log ; 00272 log << setw(Metrics::opw()) << left << LOBE_LGMD_EXTRICATE_TTI 00273 << "TTI estimator info:" << Metrics::newl() ; 00274 00275 log << " current robot speed = " << fixed << setprecision(3) 00276 << speed << " m/s" << Metrics::newl(); 00277 00278 log << " locust direction = " << fixed << setprecision(0) 00279 << E->locust_direction() << " degrees" << Metrics::newl() ; 00280 log << " LGMD spike rate = " << fixed << setprecision(0) 00281 << E->lgmd() << " Hz" << Metrics::newl() ; 00282 00283 log << " actual time-to-impact = " 00284 << fixed << setprecision(1) << setw(4) << right 00285 << E->actual_tti() << " seconds"<< Metrics::newl() ; 00286 log << "predicted time-to-impact = " 00287 << fixed << setprecision(1) << setw(4) << right 00288 << E->predicted_tti()<<" seconds"<< Metrics::newl() ; 00289 00290 log << " actual distance = " 00291 << fixed << setprecision(0) << setw(4) << right 00292 << E->actual_distance() << " mm" << Metrics::newl() ; 00293 log << " predicted distance = " 00294 << fixed << setprecision(0) << setw(4) << right 00295 << E->predicted_distance()<< " mm" << Metrics::newl() ; 00296 00297 log << " prediction confidence = " << fixed << setprecision(1) 00298 << E->confidence() * 100 << "%" ; 00299 } 00300 00301 // This version of the extricate behaviour feeds the LGMD spikes of the 00302 // individual locusts into time-to-impact estimators and then computes 00303 // distances to obstacles based on these TTI estimates, which, in turn, 00304 // are used to build a vector force field composed of attractive and 00305 // repulsive force vectors. The sum of all the force vectors result in 00306 // the final steering vector. 00307 void LGMDExtricateTTI::action() 00308 { 00309 // To avoid holding update lock for an extended period, copy latest 00310 // LGMD spike rates to TTI estimators before running Bayesian state 00311 // estimation update equations. 00312 const int N = m_tti.size() ; 00313 UpdateLock::begin_read() ; 00314 for (int i = 0; i < N; ++i) 00315 m_tti[i]->copy_lgmd() ; 00316 00317 float speed = App::robot()->current_speed() ; 00318 float heading = App::robot()->current_heading() ; 00319 UpdateLock::end_read() ; 00320 00321 // Don't interfere with a potentially ongoing extrication... 00322 if (speed < Params::interference_threshold()) { 00323 record_viz() ; // no extrication, therefore, nothing to record 00324 return ; 00325 } 00326 00327 // Update TTI estimate for each locust and use the TTI to compute 00328 // distance to obstacle in that locust's direction. 00329 Vector velocity(speed * cos(heading), speed * sin(heading)) ; 00330 int count = 0 ; // number of "distance readings" below configured threshold 00331 for (int i = 0; i < N; ++i) 00332 { 00333 TTIEstimator* estimator = m_tti[i] ; 00334 estimator->update() ; 00335 estimator->compute_distance(velocity) ; 00336 float D = estimator->distance() ; 00337 if (D > 0 && D <= Params::threshold()) 00338 ++count ; 00339 if (Params::log_tti_predictions()) 00340 log_tti_predictions(speed, estimator) ; 00341 } 00342 00343 // Issue motor commands if we have the requisite number of distance 00344 // readings below the "danger zone" threshold. 00345 Command C ; 00346 Vector A, R, F ; 00347 if (count >= Params::count()) 00348 { 00349 // Compute attractive, repulsive and total force vectors 00350 for (int i = 0; i < N; ++i) 00351 { 00352 const TTIEstimator* estimator = m_tti[i] ; 00353 float distance = estimator->distance() ; 00354 if (distance < 0) // no distance estimate from this locust 00355 continue ; 00356 float e = distance - Params::threshold() ; // distance "error" 00357 if (e < 0) // distance reading is inside danger zone 00358 R += (Params::rep_amp() * e) * estimator->direction() ; 00359 else // distance reading is outside danger zone 00360 A += (Params::att_amp() * e) * estimator->direction() ; 00361 } 00362 F = A + R ; 00363 00364 // Decide drive and turn commands based on above force field 00365 const int T = random(TurnArbiter::turn_step(), TurnArbiter::turn_max()) ; 00366 switch (quadrant(direction(F))) 00367 { 00368 case 1: 00369 C.drive = 1 ; C.turn = T ; 00370 break ; 00371 case 2: 00372 C.drive = -1 ; C.turn = -T ; 00373 break ; 00374 case 3: 00375 C.drive = -1 ; C.turn = T ; 00376 break ; 00377 case 4: 00378 C.drive = 1 ; C.turn = -T ; 00379 break ; 00380 default: // hunh?!? quadrant() shouldn't return anything outside [1,4] 00381 throw misc_error(LOGIC_ERROR) ; 00382 } 00383 00384 if (Params::spin_style_steering()) 00385 SpinArbiter::instance().vote(base::name, 00386 new SpinArbiter::Vote(C.turn)) ; 00387 else 00388 { 00389 SpeedArbiter::instance().vote(base::name, 00390 new SpeedArbiter::Vote(C.drive * Params::extricate_speed(), 00391 C.drive * Params::extricate_pwm())) ; 00392 TurnArbiter::instance().vote(base::name, 00393 new TurnArbiter::Vote(turn_vote_centered_at(C.turn))) ; 00394 } 00395 00396 Metrics::Log log ; 00397 log << std::setw(Metrics::opw()) << std::left << base::name ; 00398 Map* M = App::map() ; 00399 if (M) 00400 log << M->current_pose() ; 00401 } 00402 record_viz(A, R, F, C) ; 00403 } 00404 00405 // Record stuff for visualization 00406 void 00407 LGMDExtricateTTI:: 00408 record_viz(const Vector& att, const Vector& rep, const Vector& tot, 00409 const LGMDExtricateTTI::Command& cmd) 00410 { 00411 viz_lock() ; 00412 m_attractive = att ; 00413 m_repulsive = rep ; 00414 m_total_force = tot ; 00415 m_cmd = cmd ; 00416 viz_unlock() ; 00417 } 00418 00419 //--------------------------- VISUALIZATION ----------------------------- 00420 00421 #ifdef INVT_HAVE_LIBGL 00422 00423 // Helper function to convert the magnitude of a vector into a string, 00424 // prefixing the supplied single character label to the mag string. 00425 static std::string mag(char label, const Vector& v) 00426 { 00427 using namespace std ; 00428 00429 std::ostringstream v_str ; 00430 v_str << label << ": " << fixed << setprecision(1) << magnitude(v) ; 00431 return v_str.str() ; 00432 } 00433 00434 // This function renders the extrication decision as well as the faux 00435 // distance "readings" returned by the LGMD "range" sensor. 00436 void LGMDExtricateTTI::render_me() 00437 { 00438 // Make local copies of required visualization info so that extricate 00439 // thread isn't held up waiting for visualization to complete. 00440 const int N = m_tti.size() ; 00441 00442 typedef std::pair<Vector, float> DDP ; // direction-distance pair 00443 std::vector<DDP> ddp ; 00444 ddp.reserve(N) ; 00445 float normalizer = std::numeric_limits<float>::min() ; 00446 00447 viz_lock() ; 00448 Command C = m_cmd ; 00449 Vector A = m_attractive ; 00450 Vector R = m_repulsive ; 00451 Vector T = m_total_force ; 00452 00453 for (int i = 0; i < N; ++i) { 00454 ddp.push_back(DDP(m_tti[i]->direction(), m_tti[i]->distance())) ; 00455 if (normalizer < m_tti[i]->distance()) 00456 normalizer = m_tti[i]->distance() ; 00457 } 00458 viz_unlock() ; 00459 normalizer = 1/normalizer ; 00460 00461 // Draw principal axes to make it easier to understand what's going on 00462 // with the force vectors and turn command. 00463 unit_view_volume() ; 00464 glColor3f(1, 1, 1) ; 00465 glBegin(GL_LINES) ; 00466 glVertex2i(0, -1) ; 00467 glVertex2i(0, 1) ; 00468 glVertex2i(-1, 0) ; 00469 glVertex2i( 1, 0) ; 00470 glEnd() ; 00471 00472 // Render the extrication decision's visualization 00473 if (C.drive == 0 && C.turn == 0) 00474 ; // no extrication took place 00475 else 00476 { 00477 // Render the TTI-based distance estimates 00478 glBegin(GL_LINES) ; 00479 for (int i = 0; i < N; ++i) 00480 { 00481 if (ddp[i].second < 0) // no distance estimate in this direction 00482 continue ; 00483 Vector d = (normalizer * ddp[i].second) * ddp[i].first ; 00484 if (ddp[i].second > Params::threshold()) 00485 glColor3f(0, 0.8f, 0.2f) ; // greenish 00486 else 00487 glColor3f(0.8f, 0, 0.2f) ; // reddish 00488 glVertex2i(0, 0) ; 00489 glVertex2f(d.i, d.j) ; 00490 } 00491 glEnd() ; 00492 00493 // Render force field vectors 00494 glColor3f(0, 1, 0) ; 00495 draw_vector(normalized(A)) ; 00496 00497 glColor3f(1, 0, 0) ; 00498 draw_vector(normalized(R)) ; 00499 00500 glColor3f(0.15f, 0.75f, 0.85f) ; 00501 draw_vector(normalized(T)) ; 00502 00503 // Render drive and turn commands 00504 glColor3f(1, 1, 1) ; 00505 draw_vector(Vector(0.75f * C.drive * cos(C.turn), 0.75f * sin(C.turn))) ; 00506 } 00507 00508 // Label the visualization so that it is easy to tell which behaviour 00509 // is being visualized. Also show the magnitudes of the forces. 00510 restore_view_volume() ; 00511 text_view_volume() ; 00512 glColor3f(0, 1, 1) ; 00513 draw_label(3, 12, "LGMD Ext. TTI") ; 00514 00515 glColor3f(0, 1, 0) ; 00516 draw_label(3, m_geometry.height - 28, mag('A', A).c_str()) ; 00517 00518 glColor3f(1, 0, 0) ; 00519 draw_label(3, m_geometry.height - 16, mag('R', R).c_str()) ; 00520 00521 glColor3f(0.15f, 0.75f, 0.85f) ; 00522 draw_label(3, m_geometry.height - 4, mag('T', T).c_str()) ; 00523 restore_view_volume() ; 00524 } 00525 00526 #endif 00527 00528 //----------------------------- CLEAN-UP -------------------------------- 00529 00530 LGMDExtricateTTI::~LGMDExtricateTTI() 00531 { 00532 purge_container(m_tti) ; 00533 } 00534 00535 //----------------------------------------------------------------------- 00536 00537 } // end of namespace encapsulating this file's definitions 00538 00539 /* So things look consistent in everyone's emacs... */ 00540 /* Local Variables: */ 00541 /* indent-tabs-mode: nil */ 00542 /* End: */