00001 /** 00002 \file Robots/LoBot/tti/LoTTIEstimator.C 00003 \brief This file defines the non-inline member functions and static 00004 data members of the lobot::TTIEstimator 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/tti/LoTTIEstimator.C $ 00039 // $Id: LoTTIEstimator.C 14018 2010-09-23 07:10:34Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/tti/LoTTIEstimator.H" 00046 #include "Robots/LoBot/config/LoConfigHelpers.H" 00047 00048 #include "Robots/LoBot/util/LoGL.H" 00049 #include "Robots/LoBot/util/LoString.H" 00050 #include "Robots/LoBot/util/LoMath.H" 00051 #include "Robots/LoBot/misc/LoExcept.H" 00052 #include "Robots/LoBot/misc/singleton.hh" 00053 #include "Robots/LoBot/util/range.hh" 00054 #include "Robots/LoBot/util/triple.hh" 00055 00056 // OpenGL headers 00057 #ifdef INVT_HAVE_LIBGLU 00058 #include <GL/glu.h> 00059 #endif 00060 00061 #ifdef INVT_HAVE_LIBGL 00062 #include <GL/gl.h> 00063 #endif 00064 00065 // Standard C++ headers 00066 #include <iomanip> 00067 #include <sstream> 00068 #include <numeric> 00069 #include <algorithm> 00070 #include <functional> 00071 #include <iterator> 00072 00073 //----------------------------- NAMESPACE ------------------------------- 00074 00075 namespace lobot { 00076 00077 //-------------------------- KNOB TWIDDLING ----------------------------- 00078 00079 namespace { 00080 00081 // Retrieve settings from tti_estimator section of config file 00082 template<typename T> 00083 static inline T conf(const std::string& key, T default_value) 00084 { 00085 return get_conf<T>("tti_estimator", key, default_value) ; 00086 } 00087 00088 // Overload for retrieving ranges 00089 template<typename T> 00090 static inline range<T> conf(const std::string& key, const range<T>& defval) 00091 { 00092 return get_conf<T>("tti_estimator", key, defval) ; 00093 } 00094 00095 // To minimize clutter in the LGMD drawable, we render either the TTI 00096 // belief or the actual and predicted times-to-impact or the actual and 00097 // predicted distances. The user picks what should be rendered. This 00098 // enumeration simply names the different possibilities. 00099 enum RenderMode { 00100 RENDER_OFF, 00101 RENDER_BELIEF, 00102 RENDER_TTI, 00103 RENDER_DISTANCE, 00104 } ; 00105 00106 /// This local class encapsulates various parameters that can be used to 00107 /// tweak different aspects of the Bayesian time-to-impact estimation. 00108 class TTIParams : public singleton<TTIParams> { 00109 /// The LGMD spike rate is a function of an approaching object's 00110 /// time-to-impact. When the object is far away, the LGMD's spike 00111 /// rate will be fairly low. As it approaches, the LGMD starts 00112 /// firing very rapidly. Shortly before impact, the LGMD firing 00113 /// rate reaches a peak and then drops off sharply until impact. 00114 /// 00115 /// The peak described above "partitions" the LGMD firing rate vs. 00116 /// time-to-impact curve into two distinct "phases." We refer to 00117 /// the first phase, wherein the curve rises to its peak, as 00118 /// LOOMING because the object is looming large in the LGMD's field 00119 /// of view. The second phase, we call BLANKING because feedforward 00120 /// inhibition kicks in after the peak to shutdown the LGMD right 00121 /// before impact. 00122 /// 00123 /// To recognize the transition from LOOMING to BLANKING, we 00124 /// monitor the second derivative of the LGMD signal to find the 00125 /// inflection point where the curve becomes concave down (thus 00126 /// indicating a local maximum near the signal's peak). Since the 00127 /// derivatives of a signal can be quite noisy, it would be best to 00128 /// filter the second derivative. 00129 /// 00130 /// This setting specifies the size of the low-pass filter used to 00131 /// filter the second derivative of the LGMD input signal. For 00132 /// example, if this value is 10, then the previous ten readings 00133 /// will be used to smooth the second derivative of the input 00134 /// signal. To disable this filtering (not recommended), use a 00135 /// filter size of one. 00136 int m_sder_filter_size ; 00137 00138 /// As mentioned above, the LGMD signal is partitioned into two 00139 /// phases, viz., LOOMING and BLANKING. We recognize transitions 00140 /// from LOOMING to BLANKING using the second derivative of the 00141 /// LGMD input signal and a spike rate threshold for the rising 00142 /// portion of the TTI-LGMD curve. 00143 /// 00144 /// To recognize the transition from BLANKING back to LOOMING, we 00145 /// wait for the LGMD signal to fall below some empirically 00146 /// determined threshold firing rate rather than relying on 00147 /// higher-order derivatives of the input signal. 00148 /// 00149 /// These two settings specify the above-mentioned firing rate 00150 /// thresholds for recognizing transitions from LOOMING to BLANKING 00151 /// and then back to LOOMING. 00152 float m_rising_threshold, m_falling_threshold ; 00153 00154 /// Once we have a time-to-impact estimate for each locust, we can 00155 /// combine that estimate with the robot's current motion to 00156 /// determine the distances to obstacles in each locust's 00157 /// direction, thus, converting a vision sensor to a range sensor. 00158 /// 00159 /// As mentioned earlier, the time-to-impact estimates are made on 00160 /// the basis of a Bayesian state estimation model that works by 00161 /// continually updating a probability distribution that specifies 00162 /// the likelihood of each TTI value. The TTI with the maximum 00163 /// likelihood is used to compute the corresponding distance. 00164 /// 00165 /// However, we only want to use TTI values when their likelihoods 00166 /// are significant. That is, if the maximum likelihood in the 00167 /// entire probability distribution is really low (e.g., 1%), then 00168 /// it might not be a good thing to use that TTI estimate to try 00169 /// and determine the corresponding distance. Instead, we want to 00170 /// wait until the confidence level of the most likely 00171 /// time-to-impact is a reasonably high value before using it. 00172 /// 00173 /// This setting specifies the minimum confidence we expect to see 00174 /// before a TTI estimate will be used to generate distance 00175 /// readings. It should be a number between zero and one. 00176 float m_confidence_threshold ; 00177 00178 /// This setting specifies the min and max bounds for the distance 00179 /// "readings" computed by the Bayesian time-to-impact estimator. 00180 /// The units for this setting is mm. 00181 range<float> m_distance_range ; 00182 00183 /// There are three visualization modes for the time-to-impact 00184 /// estimator. In the first mode, the estimator renders its current 00185 /// belief, i.e., the posterior probability distribution computed 00186 /// in each iteration of the Bayesian state update loop. 00187 /// 00188 /// In the second mode, the estimator shows the history of recent 00189 /// time-to-impact values predicted by the Bayesian state update. 00190 /// To help gauge how well the Bayes filter is doing, the estimator 00191 /// will also render the actual TTI values. 00192 /// 00193 /// In the third mode, the estimator shows the history of recent 00194 /// distance "readings" computed using the predicted 00195 /// times-to-impact and the actual distances to approaching 00196 /// obstacles (as reported by the laser range finder). 00197 /// 00198 /// This setting specifies which of the above render modes to use. 00199 /// The value of this setting should be a string. As described 00200 /// above, the currently supported modes are: 00201 /// off ==> don't perform any rendering 00202 /// bel ==> render current belief 00203 /// tti ==> render time-to-impact histories 00204 /// dis ==> render distance histories 00205 RenderMode m_render_mode ; 00206 00207 /// The labels used to show the current TTI and other relevant info 00208 /// can clutter the visualization quite a bit (especially when the 00209 /// locust model drawables are small). This flag can be used to 00210 /// turn these labels on/off. By default, the labels are off. 00211 bool m_render_labels ; 00212 00213 /// Private constructor because this is a singleton. 00214 TTIParams() ; 00215 00216 // Boilerplate code to make generic singleton design pattern work 00217 friend class singleton<TTIParams> ; 00218 00219 public: 00220 /// Accessing the various parameters. 00221 //@{ 00222 static int sder_filter_size() { 00223 return instance().m_sder_filter_size ; 00224 } 00225 static float rising_threshold() { 00226 return instance().m_rising_threshold ; 00227 } 00228 static float falling_threshold() { 00229 return instance().m_falling_threshold ; 00230 } 00231 static float confidence_threshold() { 00232 return instance().m_confidence_threshold ; 00233 } 00234 static const range<float>& distance_range() { 00235 return instance().m_distance_range ; 00236 } 00237 static RenderMode render_mode() {return instance().m_render_mode ;} 00238 static bool render_labels() {return instance().m_render_labels ;} 00239 //@} 00240 } ; 00241 00242 // Parameters initialization 00243 TTIParams::TTIParams() 00244 : m_sder_filter_size(clamp(conf("sder_filter_size", 5), 1, 100)), 00245 m_rising_threshold(clamp(conf("rising_threshold", 700.0f), 00246 200.0f, 1000.0f)), 00247 m_falling_threshold(clamp(conf("falling_threshold", 100.0f), 00248 10.0f, 250.0f)), 00249 m_confidence_threshold(clamp(conf("confidence_threshold", 0.3f), 00250 0.01f, 0.9f)), 00251 m_distance_range(clamp(conf("distance_range", make_range(50.0f, 5000.0f)), 00252 make_range(10.0f, 10000.0f))), 00253 m_render_mode(RENDER_OFF), 00254 m_render_labels(conf("render_labels", false)) 00255 { 00256 const std::string render_mode = 00257 downstring(conf<std::string>("render_mode", "off")) ; 00258 if (render_mode == "bel") 00259 m_render_mode = RENDER_BELIEF ; 00260 else if (render_mode == "tti") 00261 m_render_mode = RENDER_TTI ; 00262 else if (render_mode == "dis") 00263 m_render_mode = RENDER_DISTANCE ; 00264 } 00265 00266 // Shortcut 00267 typedef TTIParams Params ; 00268 00269 } // end of local anonymous namespace encapsulating above helpers 00270 00271 //-------------------------- INITIALIZATION ----------------------------- 00272 00273 SensorModel& TTIEstimator::looming_sensor_model() 00274 { 00275 static SensorModel S("looming") ; 00276 return S ; 00277 } 00278 00279 SensorModel& TTIEstimator::blanking_sensor_model() 00280 { 00281 static SensorModel S("blanking") ; 00282 return S ; 00283 } 00284 00285 // When a Bayesian time-to-impact estimator is created, we initialize the 00286 // belief to a uniform probability distribution (i.e., we use an 00287 // uninformed prior). 00288 TTIEstimator::TTIEstimator(const LocustModel* L) 00289 : Drawable(L->name(), L->geometry()), 00290 m_locust(L), 00291 m_tti(-1), m_confidence(0), 00292 m_direction(cos(L->direction()), sin(L->direction())), 00293 m_sder(Params::sder_filter_size()), 00294 m_distance(-1), 00295 m_sensor_model(& looming_sensor_model()) 00296 { 00297 m_lgmd[0] = m_lgmd[1] = 0 ; 00298 m_fder[0] = m_fder[1] = 0 ; 00299 00300 const int N = m_sensor_model->column_size() ; 00301 m_belief.reserve(N) ; 00302 std::fill_n(std::back_inserter(m_belief), N, 1.0f/N) ; 00303 00304 RenderMode render_mode = Params::render_mode() ; 00305 if (render_mode != RENDER_OFF) 00306 { 00307 if (render_mode == RENDER_TTI || render_mode == RENDER_DISTANCE) { 00308 m_actual.resize(m_geometry.width) ; 00309 m_predicted.resize(m_geometry.width) ; 00310 } 00311 (const_cast<LocustModel*>(L))->add_hook( 00312 RenderHook(render_hook, reinterpret_cast<unsigned long>(this))) ; 00313 } 00314 } 00315 00316 //--------------------------- STATE MACHINE ----------------------------- 00317 00318 // Check which phase of the LGMD signal the current firing rate indicates 00319 TTIEstimator::LGMDPhase TTIEstimator::lgmd_phase() const 00320 { 00321 if (m_sensor_model == & looming_sensor_model()) 00322 return LOOMING ; 00323 if (m_sensor_model == & blanking_sensor_model()) 00324 return BLANKING ; 00325 throw misc_error(LOGIC_ERROR) ; 00326 } 00327 00328 // Switch the sensor model to point to the correct likelihood profile 00329 // (based on LGMD firing rate phase as determined in update method). 00330 void TTIEstimator::sensor_model(const SensorModel* S) 00331 { 00332 viz_lock() ; 00333 m_sensor_model = S ; 00334 viz_unlock() ; 00335 } 00336 00337 //------------------- BAYES FILTER UPDATE EQUATIONS --------------------- 00338 00339 void TTIEstimator::copy_lgmd() 00340 { 00341 // The LGMD signal itself 00342 m_lgmd[1] = m_lgmd[0] ; 00343 m_lgmd[0] = m_locust->get_lgmd() ; 00344 00345 // First derivative of LGMD signal 00346 m_fder[1] = m_fder[0] ; 00347 m_fder[0] = m_lgmd[0] - m_lgmd[1] ; 00348 00349 // Second derivative of LGMD signal (filtered using weighted moving average) 00350 m_sder.add(m_fder[0] - m_fder[1]) ; 00351 00352 // Also copy actual time-to-impact or distance so we can later compare 00353 // with the predicted values. 00354 viz_lock() ; 00355 switch (Params::render_mode()) 00356 { 00357 case RENDER_TTI: 00358 m_actual.pop_front() ; 00359 m_actual.push_back(m_locust->tti()) ; 00360 break ; 00361 case RENDER_DISTANCE: 00362 m_actual.pop_front() ; 00363 m_actual.push_back(m_locust->distance()) ; 00364 break ; 00365 default: 00366 break ; 00367 } 00368 viz_unlock() ; 00369 } 00370 00371 // Helper function object to add a uniform distribution to the elements 00372 // of an existing probability distribution. 00373 // 00374 // DEVNOTE: Since both the existing and uniform distributions sum to 00375 // unity, the sum of the combined distribution will be two. Rather than 00376 // perform another normalization step after the addition of the two 00377 // distributions, this function object simply divides each probability it 00378 // computes by two and returns the (already) normalized final result. 00379 // Thus, clients don't have to go through another normalization step 00380 // after applying this function to an existing probability distribution. 00381 class add_uniform_distribution { 00382 float uniform_probability ; 00383 public: 00384 add_uniform_distribution(float uniform_probability) ; 00385 float operator()(float p) const { 00386 return (p + uniform_probability)/2 ; 00387 } 00388 } ; 00389 00390 add_uniform_distribution::add_uniform_distribution(float p) 00391 : uniform_probability(p) 00392 {} 00393 00394 // Recursive Bayesian update to get new belief, i.e., posterior 00395 // probability distribution, using current belief as the prior and the 00396 // latest sensor (i.e., LGMD) value. 00397 // 00398 // DEVNOTE: The client behaviour *must* call TTIEstimator::copy_lgmd() 00399 // before invoking this method. 00400 void TTIEstimator::update() 00401 { 00402 // Use the correct sensor model for the current phase of the LGMD 00403 // input signal. 00404 switch (lgmd_phase()) 00405 { 00406 case LOOMING: 00407 if (m_lgmd[0] > Params::rising_threshold() && m_sder.value() < 0) 00408 sensor_model(& blanking_sensor_model()) ; 00409 break ; 00410 case BLANKING: 00411 if (m_lgmd[0] < Params::falling_threshold()) // blanking over 00412 sensor_model(& looming_sensor_model()) ; 00413 break ; 00414 } 00415 00416 // Multiply the individual probabilities of the current belief with 00417 // the corresponding probabilities in the appropriate column vector of 00418 // the sensor model. 00419 Belief tmp(m_belief.size(), 0.0f) ; 00420 std::transform(m_belief.begin(), m_belief.end(), 00421 m_sensor_model->column_vector(m_lgmd[0]).begin(), 00422 tmp.begin(), std::multiplies<float>()) ; 00423 00424 // Normalize the intermediate belief obtained above. If the normalizer 00425 // is out-of-whack, reinit the belief to a uniform probability 00426 // distribution. 00427 float normalizer = std::accumulate(tmp.begin(), tmp.end(), 0.0f) ; 00428 if (normalizer <= 0) // something weird going on! 00429 std::fill(tmp.begin(), tmp.end(), 1.0f/tmp.size()) ; 00430 else 00431 std::transform(tmp.begin(), tmp.end(), tmp.begin(), 00432 std::bind2nd(std::multiplies<float>(), 1/normalizer)) ; 00433 00434 // Add a uniform distribution to the above result so as to model 00435 // random noise that diffuses the belief. If we don't do this, it 00436 // often happens that the belief reaches 100% for some state (0% for 00437 // all others) and then gets stuck in that state. 00438 // 00439 // In essence, this step ensures that we don't trust the sensor model 00440 // completely. After all, no model can be perfect... 00441 viz_lock() ; 00442 std::transform(tmp.begin(), tmp.end(), m_belief.begin(), 00443 add_uniform_distribution(1.0f/tmp.size())) ; 00444 00445 Belief::const_iterator max = std::max_element(m_belief.begin(), 00446 m_belief.end()) ; 00447 const float min = m_sensor_model->row_min() ; 00448 const float step = m_sensor_model->row_step(); 00449 m_tti = min + (max - m_belief.begin() + 1) * step ; 00450 m_confidence = *max ; 00451 viz_unlock() ; 00452 } 00453 00454 // Determine distance to obstacle in locust's direction by projecting 00455 // robot's current velocity vector onto locust's direction vector and 00456 // using the TTI estimate with max belief (subject to that estimate's 00457 // confidence being greater than the configured minimum). 00458 void TTIEstimator::compute_distance(const Vector& velocity) 00459 { 00460 float speed = magnitude(dot(m_direction, velocity) * m_direction) ; 00461 float distance = -1 ; 00462 float time = -1 ; 00463 if (m_confidence >= Params::confidence_threshold()) 00464 { 00465 time = m_tti ; 00466 if (! is_zero(speed)) 00467 distance = clamp(speed * time * 1000, Params::distance_range()) ; 00468 } 00469 00470 viz_lock() ; 00471 m_distance = distance ; 00472 switch (Params::render_mode()) 00473 { 00474 case RENDER_TTI: 00475 m_predicted.pop_front() ; 00476 m_predicted.push_back(time) ; 00477 break ; 00478 case RENDER_DISTANCE: 00479 m_predicted.pop_front() ; 00480 m_predicted.push_back(distance) ; 00481 break ; 00482 default: 00483 break ; 00484 } 00485 viz_unlock() ; 00486 } 00487 00488 //--------------------------- VISUALIZATION ----------------------------- 00489 00490 #ifdef INVT_HAVE_LIBGLU 00491 00492 // Quick helper to return a label for the current LGMD phase 00493 static std::string phase_label(int lgmd_phase) 00494 { 00495 std::ostringstream str ; 00496 switch (lgmd_phase) 00497 { 00498 case 0: // LOOMING 00499 str << "Loom" ; 00500 break ; 00501 case 1: // BLANKING 00502 str << "Blank" ; 00503 break ; 00504 default: // should never happen! 00505 str << "???" ; 00506 break ; 00507 } 00508 return str.str() ; 00509 } 00510 00511 // Helper function to convert a TTI value to an appropriate label 00512 static std::string tti_label(float tti) 00513 { 00514 using namespace std ; 00515 00516 std::ostringstream str ; 00517 if (tti < 0) 00518 str << "???" ; 00519 else 00520 str << fixed << setprecision(1) << tti << 's' ; 00521 return str.str() ; 00522 } 00523 00524 // Helper function to convert current belief's peak to appropriate label 00525 // showing TTI and confidence level. 00526 static std::string tti_label(float tti, float confidence) 00527 { 00528 using namespace std ; 00529 00530 std::ostringstream str ; 00531 str << fixed << setprecision(1) << tti << "s (" 00532 << fixed << setprecision(1) << (confidence * 100) << "%)" ; 00533 return str.str() ; 00534 } 00535 00536 // Quick helper to return a label for current distance estimate based on 00537 // TTI computation. 00538 static std::string distance_label(int distance) 00539 { 00540 std::ostringstream str ; 00541 if (distance < 0) 00542 str << "???" ; 00543 else 00544 str << distance << " mm" ; 00545 return str.str() ; 00546 } 00547 00548 // Helper function to show error in TTI estimate 00549 static std::string error_label(float actual, float predicted) 00550 { 00551 std::ostringstream str ; 00552 str << "Err: " ; 00553 if (actual < 0 || predicted < 0) // bogus TTI values 00554 str << '?' ; 00555 else 00556 str << round(100 * (predicted - actual)/actual) << '%' ; 00557 return str.str() ; 00558 } 00559 00560 // This function renders the TTI estimator's current state on the 00561 // lobot::LocustModel drawable so that we can see the Bayesian state 00562 // estimation superimposed on the LGMD spike trains. 00563 void TTIEstimator::render_hook(unsigned long client_data) 00564 { 00565 TTIEstimator* me = reinterpret_cast<TTIEstimator*>(client_data) ; 00566 switch (Params::render_mode()) 00567 { 00568 case RENDER_BELIEF: 00569 me->render_belief() ; 00570 break ; 00571 case RENDER_TTI: 00572 me->render_tti() ; 00573 break ; 00574 case RENDER_DISTANCE: 00575 me->render_distance() ; 00576 break ; 00577 default: 00578 break ; 00579 } 00580 } 00581 00582 // This function renders the probability distribution showing the 00583 // time-to-impact likelihoods for each TTI estimator. 00584 void TTIEstimator::render_belief() 00585 { 00586 // Make local copy of current TTI belief so that behaviour's thread 00587 // isn't held up waiting for visualization to complete. 00588 viz_lock() ; 00589 Belief B = m_belief ; 00590 const SensorModel* S = m_sensor_model ; 00591 LGMDPhase P = lgmd_phase() ; 00592 int D = round(m_distance) ; 00593 viz_unlock() ; 00594 00595 // Since TTI belief is overlaid on LGMD spikes visualization, we need 00596 // to reset the view volume before rendering. 00597 setup_view_volume(S->row_min(), S->row_max(), 0, 1) ; 00598 00599 // Render TTI belief 00600 glColor3f(1, 0, 0) ; 00601 glBegin(GL_LINE_STRIP) ; 00602 glVertex2f(S->row_min(), 0) ; 00603 float x = S->row_min() + S->row_step() ; 00604 for (unsigned int i = 0; i < B.size(); ++i, x += S->row_step()) 00605 glVertex2f(x, B[i]); 00606 glVertex2f(S->row_max(), 0) ; 00607 glEnd() ; 00608 restore_view_volume() ; 00609 00610 // Label current LGMD phase being used for Bayes filter plus the 00611 // current belief and distance computation. 00612 if (Params::render_labels()) 00613 { 00614 Belief::const_iterator max = std::max_element(B.begin(), B.end()) ; 00615 float tti = S->row_min() + (max - B.begin() + 1) * S->row_step() ; 00616 text_view_volume() ; 00617 glColor3f(0, 1, 1) ; 00618 draw_label(3, 24, phase_label(P).c_str()) ; 00619 draw_label(3, 40, tti_label(tti, *max).c_str()) ; 00620 draw_label(3, 56, distance_label(D).c_str()) ; 00621 restore_view_volume() ; 00622 } 00623 } 00624 00625 // Helpers for drawing history 00626 typedef std::deque<float> History ; 00627 00628 static void draw_history(const History& history) 00629 { 00630 const int N = history.size() ; 00631 History::const_iterator y = history.begin() ; 00632 glBegin(GL_LINE_STRIP) ; 00633 for (int x = 0; x < N; ++x, ++y) 00634 glVertex2f(x, *y) ; 00635 glEnd() ; 00636 } 00637 00638 static void draw_history(const History& actual, const History& predicted) 00639 { 00640 glPushAttrib(GL_COLOR_BUFFER_BIT) ; 00641 glColor3f(0.0f, 0.15f, 0.85f) ; 00642 draw_history(actual) ; 00643 00644 glColor3f(1, 0, 0) ; 00645 draw_history(predicted) ; 00646 glPopAttrib() ; 00647 } 00648 00649 // This function renders the actual and predicted time-to-impact values 00650 // so that users can gauge how well the Bayes filter's predictions match 00651 // the actual situation on the ground. 00652 void TTIEstimator::render_tti() 00653 { 00654 // Make local copies so as to not hold up client behaviour's thread 00655 viz_lock() ; 00656 const float max_probability = 00657 *(std::max_element(m_belief.begin(), m_belief.end())) ; 00658 History actual = m_actual ; 00659 History predicted = m_predicted ; 00660 viz_unlock() ; 00661 00662 // Setup view volume so that x-coordinates match the amount of TTI 00663 // history available and y-coordinates match the TTI range. 00664 setup_view_volume(0, m_geometry.width, 0, 60) ; 00665 00666 // Draw the actual and predicted TTI histories 00667 draw_history(actual, predicted) ; 00668 restore_view_volume() ; 00669 00670 // Label current values of actual and predicted times-to-impact 00671 if (Params::render_labels()) 00672 { 00673 float A = actual.back() ; 00674 float P = predicted.back() ; 00675 text_view_volume() ; 00676 glColor3f(0, 1, 1) ; 00677 draw_label(3, 24, tti_label(A).c_str()) ; 00678 draw_label(3, 40, tti_label(P, max_probability).c_str()) ; 00679 draw_label(3, 56, error_label(A, P).c_str()) ; 00680 restore_view_volume() ; 00681 } 00682 } 00683 00684 // This function renders the actual and predicted distances to obstacles 00685 // based on the time-to-impact estimates so that users can gauge how well 00686 // the Bayes filter's predictions match the actual situation on the 00687 // ground. 00688 void TTIEstimator::render_distance() 00689 { 00690 // Make local copies so as to not hold up client behaviour's thread 00691 viz_lock() ; 00692 History actual = m_actual ; 00693 History predicted = m_predicted ; 00694 viz_unlock() ; 00695 00696 // Setup view volume so that x-coordinates match the amount of 00697 // distance history available and y-coordinates match the distance 00698 // range. 00699 setup_view_volume(0, m_geometry.width, 0, Params::distance_range().max()) ; 00700 00701 // Draw the actual and predicted distance histories 00702 draw_history(actual, predicted) ; 00703 restore_view_volume() ; 00704 00705 // Label current values of actual and predicted times-to-impact 00706 if (Params::render_labels()) 00707 { 00708 float A = actual.back() ; 00709 float P = predicted.back() ; 00710 text_view_volume() ; 00711 glColor3f(0, 1, 1) ; 00712 draw_label(3, 24, distance_label(round(A)).c_str()) ; 00713 draw_label(3, 40, distance_label(round(P)).c_str()) ; 00714 draw_label(3, 56, error_label(A, P).c_str()) ; 00715 restore_view_volume() ; 00716 } 00717 } 00718 00719 #endif 00720 00721 //----------------------------- CLEAN-UP -------------------------------- 00722 00723 TTIEstimator::~TTIEstimator(){} 00724 00725 //----------------------------------------------------------------------- 00726 00727 } // end of namespace encapsulating this file's definitions 00728 00729 /* So things look consistent in everyone's emacs... */ 00730 /* Local Variables: */ 00731 /* indent-tabs-mode: nil */ 00732 /* End: */