ParticlePlume.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2010, ISR University of Coimbra.
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of the ISR University of Coimbra nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: Gonçalo Cabrita on 28/11/2010
00036 *********************************************************************/
00037 #include "ParticlePlume.h"
00038 
00039 #include <cstdlib>
00040 #include <math.h>
00041 
00042 particle_plume::ParticlePlume::ParticlePlume() : n_(), pn_("~")
00043 {
00044         // Get the private parameters...
00045         pn_.param<std::string>("global_frame_id", global_frame_id_, "map");
00046         pn_.param("bubble_radius", bubble_radius_, 0.10);
00047         pn_.param("max_particles_per_bubble", max_particles_per_bubble_, 1000);
00048         pn_.param("particle_life_time", particle_life_time_, 0);
00049         pn_.param("publish_frequency", publish_frequency_, 2.0);
00050         pn_.param("cell_size", cell_size_, 0.05);
00051         pn_.param<std::string>("sensor_model", sensor_model_, "Figaro 2620");
00052         
00053         // Subscribe to the chemical sensor topic
00054         nose_sub_.subscribe(n_, "nose", 10);
00055         tf_filter_ = new tf::MessageFilter<lse_sensor_msgs::Nostril>(nose_sub_, tf_, global_frame_id_, 10);
00056     tf_filter_->registerCallback( boost::bind(&ParticlePlume::noseCallback, this, _1) );
00057         
00058         // Advertise the plume as a PointCloud2
00059         plume_pub_.advertise(n_, "plume", 1);
00060         
00061         // Advertise the visited cells grid as a PointCloud2
00062         cells_pub_ = n_.advertise<nav_msgs::GridCells>("visited_cells", 1, true);
00063         
00064         cells_.header.frame_id = global_frame_id_;
00065         cells_.cell_width = cell_size_;
00066         cells_.cell_height = cell_size_;
00067         
00068         cells_changed_ = false;
00069 }
00070 
00071 particle_plume::ParticlePlume::~ParticlePlume()
00072 {
00073         // Do destruction stuff here!
00074 }
00075 
00076 void particle_plume::ParticlePlume::noseCallback(const boost::shared_ptr<const lse_sensor_msgs::Nostril>& msg)
00077 {
00078         if(msg->sensor_model.compare(sensor_model_)!=0) return;
00079         
00080         // Determine the number of particles proportional to the chemical concentrarion
00081         int number_of_particles = (int)((msg->reading-msg->clean_air) * max_particles_per_bubble_ / (msg->max_reading-msg->clean_air));
00082         if(number_of_particles<0) number_of_particles=0;
00083         
00084         geometry_msgs::PointStamped bubble_center;
00085         geometry_msgs::PointStamped nose;
00086         nose.header.frame_id = msg->header.frame_id;
00087         nose.header.stamp = msg->header.stamp;
00088         nose.point.x = 0.0;     
00089         nose.point.y = 0.0;
00090         nose.point.z = 0.0;
00091         
00092         try 
00093         {
00094                 // Transform the bubble center from the chemical sensor frame to the map frame
00095                 tf_.transformPoint(global_frame_id_, nose, bubble_center);
00096         }
00097         catch(tf::TransformException &ex) 
00098         {
00099                 ROS_ERROR("ParticlePlume -- Error: %s", ex.what());
00100                 return;
00101         }
00102         
00103         // If we have some particles to add...
00104         if(number_of_particles > 0)
00105         {
00106                 // Store the new data in the buffer 
00107                 BubbleData new_odor_reading;
00108                 new_odor_reading.number_of_particles = number_of_particles;
00109                 new_odor_reading.bubble_center = bubble_center;
00110         
00111                 odor_readings_.push_back(new_odor_reading);
00112         }
00113         
00114         // Update the visited cells data structure
00115         std::vector<CellCandidate> cell_candidates;
00116         std::vector<CellCandidate>::iterator cell_candidate;
00117         
00118         int start_cell_x = ceil((bubble_center.point.x - bubble_radius_) / cell_size_);
00119         int end_cell_x = ceil((bubble_center.point.x + bubble_radius_) / cell_size_);
00120         int start_cell_y = ceil((bubble_center.point.y - bubble_radius_) / cell_size_);
00121         int end_cell_y = ceil((bubble_center.point.y + bubble_radius_) / cell_size_);
00122         
00123         for(int x=start_cell_x ; x<end_cell_x ; x++)
00124         {
00125                 for(int y=start_cell_y ; y<end_cell_y ; y++)
00126                 {
00127                         CellCandidate new_cell;
00128                         new_cell.point.x = x*cell_size_;
00129                         new_cell.point.y = y*cell_size_;
00130                         new_cell.point.z = 0.0;
00131                         new_cell.push = true;
00132                         
00133                         cell_candidates.push_back(new_cell);
00134                 }
00135         }
00136         
00137         std::vector<geometry_msgs::Point>::iterator cell;
00138         for(int i=0 ; i<cells_.cells.size() ; i++)
00139         {
00140                 cell = cells_.cells.begin()+i;
00141                 for(cell_candidate = cell_candidates.begin() ; cell_candidate != cell_candidates.end() ; cell_candidate++)
00142                 {
00143                         if(cell_candidate->point.x == cell->x && cell_candidate->point.y == cell->y)
00144                         {
00145                                 cell_candidate->push = false;
00146                                 cells_birth_[i] = ros::Time::now();
00147                         }
00148                 }
00149         }
00150         
00151         for(cell_candidate = cell_candidates.begin() ; cell_candidate != cell_candidates.end() ; cell_candidate++)
00152         {
00153                 if(cell_candidate->push == true)
00154                 {
00155                         cells_.cells.push_back(cell_candidate->point);
00156                         cells_birth_.push_back(ros::Time::now());
00157                         cells_changed_ = true;
00158                 }
00159         }
00160 }
00161 
00162 void particle_plume::ParticlePlume::publishPlume()
00163 {
00164         ros::Rate r(publish_frequency_);
00165         
00166         std::vector<BubbleData>::iterator reading;
00167         std::vector<BubbleData>::iterator bubble_it;
00168         
00169         bool push;
00170         bool publish_plume;
00171         
00172         // As long as we're good to go...
00173         while(n_.ok())
00174         {       
00175                 publish_plume = false;
00176                 
00177                 // If we have new readings to consider...
00178                 if(odor_readings_.size() > 0)
00179                 {
00180                         pcl::PointCloud<Particle> new_plume;
00181                 
00182                         for(reading=odor_readings_.begin() ; reading!=odor_readings_.end() ; reading++)
00183                         {
00184                                 // Generate the cloud of particles for the current odor reading
00185                                 for(int i=0 ; i<reading->number_of_particles ; i++)
00186                                 {
00187                                         double r = drand()*bubble_radius_;
00188                                         double theta = drand()*M_PI;
00189                                         double phi = drand()*2*M_PI;
00190                                         
00191                                         Particle particle;
00192                                         particle.x = reading->bubble_center.point.x + r*sin(theta)*cos(phi);
00193                                         particle.y = reading->bubble_center.point.y + r*sin(theta)*sin(phi);
00194                                         particle.z = reading->bubble_center.point.z + r*cos(theta);
00195                                         particle.intensity = 100.0;
00196                                         
00197                                         // Now lets check if we can add the newly created particle to the cloud
00198                                         push = true;
00199                                         for(bubble_it=odor_readings_.begin() ; bubble_it!=reading ; bubble_it++)
00200                                         {
00201                                                 double dx = bubble_it->bubble_center.point.x - particle.x;
00202                                                 double dy = bubble_it->bubble_center.point.y - particle.y;
00203                                                 double dz = bubble_it->bubble_center.point.z - particle.z;
00204                                                 // If the particle is inside a newer bubble, dont add it
00205                                                 if(sqrt(dx*dx + dy*dy + dz*dz) <= bubble_radius_)
00206                                                 {
00207                                                         push = false;
00208                                                         break;
00209                                                 }
00210                                         }
00211                                         if(push == true) new_plume.points.push_back(particle);
00212                                 }
00213                         }
00214                         
00215                         // Now add the previous iteration points on plume_ to the new cloud on new_plume
00216                         for(int i=0 ; i<plume_.width ; i++)
00217                         {
00218                                 Particle particle;
00219                                 particle.x = plume_.points[i].x;
00220                                 particle.y = plume_.points[i].y;
00221                                 particle.z = plume_.points[i].z;
00222                                 particle.intensity = plume_.points[i].intensity;
00223                                 
00224                                 // Decrease the particle intensity if needed
00225                                 if(particle_life_time_ > 0 && particle.intensity > 0.0)
00226                                 {
00227                                         particle.intensity -= 100.0/publish_frequency_/(particle_life_time_*60.0);
00228                                         if(particle.intensity < 0.0) particle.intensity = 0.0;
00229                                 }
00230                                 
00231                                 // Now lets check if this particle still belongs on the clowd
00232                                 push = true;
00233                                 for(reading=odor_readings_.begin() ; reading!=odor_readings_.end() ; reading++)
00234                                 {               
00235                                         double dx = reading->bubble_center.point.x - particle.x;
00236                                         double dy = reading->bubble_center.point.y - particle.y;
00237                                         double dz = reading->bubble_center.point.z - particle.z;
00238                                         // If the particle is inside a newer bubble, dont add it
00239                                         if(sqrt(dx*dx + dy*dy + dz*dz) <= bubble_radius_)
00240                                         {
00241                                                 push=false;
00242                                                 break;
00243                                         }
00244                                 }
00245                                 if(push==true && particle.intensity > 0.0) new_plume.points.push_back(particle);
00246                         }
00247                         
00248                         // Clear the readings buffer
00249                         odor_readings_.clear();
00250                 
00251                         // Store the new plume and publish it...
00252                         plume_ = new_plume;
00253                         plume_.width = plume_.points.size();
00254                         plume_.height = 1;
00255                         
00256                         publish_plume = true;
00257                 }
00258                 // If we dont have new readings and we have a particle life time defined
00259                 else if(particle_life_time_ > 0)
00260                 {
00261                         pcl::PointCloud<Particle> new_plume;
00262                         for(int i=0 ; i<plume_.width ; i++)
00263                         {
00264                                 Particle particle;
00265                                 particle.x = plume_.points[i].x;
00266                                 particle.y = plume_.points[i].y;
00267                                 particle.z = plume_.points[i].z;
00268                                 particle.intensity = plume_.points[i].intensity;
00269                                 
00270                                 if(particle.intensity > 0.0)
00271                                 {
00272                                         particle.intensity -= 100.0/publish_frequency_/(particle_life_time_*60.0);
00273                                         if(particle.intensity < 0.0) particle.intensity = 0.0;
00274                                 }
00275                                 
00276                                 if(particle.intensity > 0.0) new_plume.points.push_back(particle);
00277                         }
00278                         // Store the new plume and publish it...
00279                         plume_ = new_plume;
00280                         plume_.width = plume_.points.size();
00281                         plume_.height = 1;
00282                 }
00283                 
00284                 //TODO: Delete old cells!
00285                 if(particle_life_time_ > 0)
00286                 {
00287                         std::vector<int> cells_to_delete;
00288                         for(int i=0 ; i<cells_birth_.size() ; i++)
00289                         {
00290                                 if((ros::Time::now() - cells_birth_[i]).toSec() > ros::Duration(particle_life_time_*60.0).toSec()) cells_to_delete.push_back(i);
00291                         }
00292                 
00293                         for(int i=cells_to_delete.size()-1 ; i>=0 ; i--)
00294                         {
00295                                 cells_.cells.erase(cells_.cells.begin()+cells_to_delete[i]);
00296                                 cells_birth_.erase(cells_birth_.begin()+cells_to_delete[i]);
00297                         }
00298                 }
00299                 
00300                 //if(publish_plume)
00301                 //{
00302                         plume_.header.stamp = ros::Time::now();
00303                         plume_.header.frame_id = global_frame_id_;      
00304                         plume_pub_.publish(plume_);
00305                 //}
00306                 
00307                 //if(cells_changed_)
00308                 //{
00309                         cells_.header.stamp = ros::Time::now();
00310                         cells_pub_.publish(cells_);
00311                         cells_changed_ = false;
00312                 //}
00313                 
00314                 // Spin...
00315                 ros::spinOnce();
00316                 // ...and sleep!
00317                 r.sleep();
00318         }
00319 }
00320 
00321 float particle_plume::ParticlePlume::drand()
00322 {
00323         return ((rand()+1.0)/(RAND_MAX+1.0));
00324 }
00325 
00326 double particle_plume::ParticlePlume::randomNormal()
00327 {
00328         return (sqrt(-2*log(drand()))*cos(2*M_PI*drand()));
00329 }
00330 
00331 // EOF
00332 


particle_plume
Author(s): Gonçalo Cabrita and Pedro Sousa
autogenerated on Mon Jan 6 2014 11:27:46