base_assembler.h
Go to the documentation of this file.
00001 /*********************************************************************
00002 * Software License Agreement (BSD License)
00003 *
00004 *  Copyright (c) 2008, Willow Garage, Inc.
00005 *  All rights reserved.
00006 *
00007 *  Redistribution and use in source and binary forms, with or without
00008 *  modification, are permitted provided that the following conditions
00009 *  are met:
00010 *
00011 *   * Redistributions of source code must retain the above copyright
00012 *     notice, this list of conditions and the following disclaimer.
00013 *   * Redistributions in binary form must reproduce the above
00014 *     copyright notice, this list of conditions and the following
00015 *     disclaimer in the documentation and/or other materials provided
00016 *     with the distribution.
00017 *   * Neither the name of the Willow Garage nor the names of its
00018 *     contributors may be used to endorse or promote products derived
00019 *     from this software without specific prior written permission.
00020 *
00021 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 *  POSSIBILITY OF SUCH DAMAGE.
00033 *********************************************************************/
00034 
00036 
00037 #include "ros/ros.h"
00038 #include "tf/transform_listener.h"
00039 #include "tf/message_filter.h"
00040 #include "sensor_msgs/PointCloud.h"
00041 #include "message_filters/subscriber.h"
00042 
00043 #include <deque>
00044 
00045 // Service
00046 #include "laser_assembler/AssembleScans.h"
00047 
00048 #include "boost/thread.hpp"
00049 #include "math.h"
00050 
00051 namespace laser_assembler
00052 {
00053 
00057 template<class T>
00058 class BaseAssembler
00059 {
00060 public:
00061   BaseAssembler(const std::string& max_size_param_name);
00062   ~BaseAssembler() ;
00063 
00071   void start(const std::string& in_topic_name);
00072   void start();
00073 
00074 
00079   virtual unsigned int GetPointsInScan(const T& scan) = 0 ;
00080 
00089   virtual void ConvertToCloud(const std::string& fixed_frame_id, const T& scan_in, sensor_msgs::PointCloud& cloud_out) = 0 ;
00090 
00091 protected:
00092   tf::TransformListener* tf_ ;
00093   tf::MessageFilter<T>* tf_filter_;
00094 
00095   ros::NodeHandle private_ns_;
00096   ros::NodeHandle n_;
00097 
00098 private:
00099   // ROS Input/Ouptut Handling
00100   ros::ServiceServer build_cloud_server_;
00101   ros::ServiceServer assemble_scans_server_;
00102   message_filters::Subscriber<T> scan_sub_;
00103   message_filters::Connection tf_filter_connection_;
00104 
00106   //void scansCallback(const tf::MessageNotifier<T>::MessagePtr& scan_ptr, const T& testA)
00107   virtual void msgCallback(const boost::shared_ptr<const T>& scan_ptr) ;
00108 
00110   bool buildCloud(AssembleScans::Request& req, AssembleScans::Response& resp) ;
00111   bool assembleScans(AssembleScans::Request& req, AssembleScans::Response& resp) ;
00112 
00113 
00115   std::deque<sensor_msgs::PointCloud> scan_hist_ ;
00116   boost::mutex scan_hist_mutex_ ;
00117 
00119   unsigned int total_pts_ ;
00120 
00122   unsigned int max_scans_ ;
00123 
00125   std::string fixed_frame_ ;
00126 
00128   unsigned int downsample_factor_ ;
00129 
00130 } ;
00131 
00132 template <class T>
00133 BaseAssembler<T>::BaseAssembler(const std::string& max_size_param_name) : private_ns_("~")
00134 {
00135   // **** Initialize TransformListener ****
00136   double tf_cache_time_secs ;
00137   private_ns_.param("tf_cache_time_secs", tf_cache_time_secs, 10.0) ;
00138   if (tf_cache_time_secs < 0)
00139     ROS_ERROR("Parameter tf_cache_time_secs<0 (%f)", tf_cache_time_secs) ;
00140 
00141   tf_ = new tf::TransformListener(n_, ros::Duration(tf_cache_time_secs));
00142   ROS_INFO("TF Cache Time: %f Seconds", tf_cache_time_secs) ;
00143 
00144   // ***** Set max_scans *****
00145   const int default_max_scans = 400 ;
00146   int tmp_max_scans ;
00147   private_ns_.param(max_size_param_name, tmp_max_scans, default_max_scans);
00148   if (tmp_max_scans < 0)
00149   {
00150     ROS_ERROR("Parameter max_scans<0 (%i)", tmp_max_scans) ;
00151     tmp_max_scans = default_max_scans ;
00152   }
00153   max_scans_ = tmp_max_scans ;
00154   ROS_INFO("Max Scans in History: %u", max_scans_) ;
00155   total_pts_ = 0 ;    // We're always going to start with no points in our history
00156 
00157   // ***** Set fixed_frame *****
00158   private_ns_.param("fixed_frame", fixed_frame_, std::string("ERROR_NO_NAME"));
00159   ROS_INFO("Fixed Frame: %s", fixed_frame_.c_str()) ;
00160   if (fixed_frame_ == "ERROR_NO_NAME")
00161     ROS_ERROR("Need to set parameter fixed_frame") ;
00162 
00163   // ***** Set downsample_factor *****
00164   int tmp_downsample_factor ;
00165   private_ns_.param("downsample_factor", tmp_downsample_factor, 1);
00166   if (tmp_downsample_factor < 1)
00167   {
00168     ROS_ERROR("Parameter downsample_factor<1: %i", tmp_downsample_factor) ;
00169     tmp_downsample_factor = 1 ;
00170   }
00171   downsample_factor_ = tmp_downsample_factor ;
00172   if (downsample_factor_ != 1)
00173     ROS_WARN("Downsample set to [%u]. Note that this is an unreleased/unstable feature", downsample_factor_);
00174 
00175   // ***** Start Services *****
00176   build_cloud_server_    = n_.advertiseService("build_cloud",    &BaseAssembler<T>::buildCloud,    this);
00177   assemble_scans_server_ = n_.advertiseService("assemble_scans", &BaseAssembler<T>::assembleScans, this);
00178 
00179   // ***** Start Listening to Data *****
00180   // (Well, don't start listening just yet. Keep this as null until we actually start listening, when start() is called)
00181   tf_filter_ = NULL;
00182 
00183 }
00184 
00185 template <class T>
00186 void BaseAssembler<T>::start(const std::string& in_topic_name)
00187 {
00188   ROS_DEBUG("Called start(string). Starting to listen on message_filter::Subscriber the input stream");
00189   if (tf_filter_)
00190     ROS_ERROR("assembler::start() was called twice!. This is bad, and could leak memory") ;
00191   else
00192   {
00193     scan_sub_.subscribe(n_, in_topic_name, 10);
00194     tf_filter_ = new tf::MessageFilter<T>(scan_sub_, *tf_, fixed_frame_, 10);
00195     tf_filter_->registerCallback( boost::bind(&BaseAssembler<T>::msgCallback, this, _1) );
00196   }
00197 }
00198 
00199 template <class T>
00200 void BaseAssembler<T>::start()
00201 {
00202   ROS_DEBUG("Called start(). Starting tf::MessageFilter, but not initializing Subscriber");
00203   if (tf_filter_)
00204     ROS_ERROR("assembler::start() was called twice!. This is bad, and could leak memory") ;
00205   else
00206   {
00207     scan_sub_.subscribe(n_, "bogus", 10);
00208     tf_filter_ = new tf::MessageFilter<T>(scan_sub_, *tf_, fixed_frame_, 10);
00209     tf_filter_->registerCallback( boost::bind(&BaseAssembler<T>::msgCallback, this, _1) );
00210   }
00211 }
00212 
00213 template <class T>
00214 BaseAssembler<T>::~BaseAssembler()
00215 {
00216   if (tf_filter_)
00217     delete tf_filter_;
00218 
00219   delete tf_ ;
00220 }
00221 
00222 template <class T>
00223 void BaseAssembler<T>::msgCallback(const boost::shared_ptr<const T>& scan_ptr)
00224 {
00225   ROS_DEBUG("starting msgCallback");
00226   const T scan = *scan_ptr ;
00227 
00228   sensor_msgs::PointCloud cur_cloud ;
00229 
00230   // Convert the scan data into a universally known datatype: PointCloud
00231   try
00232   {
00233     ConvertToCloud(fixed_frame_, scan, cur_cloud) ;              // Convert scan into a point cloud
00234   }
00235   catch(tf::TransformException& ex)
00236   {
00237     ROS_WARN("Transform Exception %s", ex.what()) ;
00238     return ;
00239   }
00240 
00241   // Add the current scan (now of type PointCloud) into our history of scans
00242   scan_hist_mutex_.lock() ;
00243   if (scan_hist_.size() == max_scans_)                           // Is our deque full?
00244   {
00245     total_pts_ -= scan_hist_.front().points.size () ;            // We're removing an elem, so this reduces our total point count
00246     scan_hist_.pop_front() ;                                     // The front of the deque has the oldest elem, so we can get rid of it
00247   }
00248   scan_hist_.push_back(cur_cloud) ;                              // Add the newest scan to the back of the deque
00249   total_pts_ += cur_cloud.points.size () ;                       // Add the new scan to the running total of points
00250 
00251   //printf("Scans: %4u  Points: %10u\n", scan_hist_.size(), total_pts_) ;
00252 
00253   scan_hist_mutex_.unlock() ;
00254   ROS_DEBUG("done with msgCallback");
00255 }
00256 
00257 template <class T>
00258 bool BaseAssembler<T>::buildCloud(AssembleScans::Request& req, AssembleScans::Response& resp)
00259 {
00260   ROS_WARN("Service 'build_cloud' is deprecated. Call 'assemble_scans' instead");
00261   return assembleScans(req, resp);
00262 }
00263 
00264 
00265 template <class T>
00266 bool BaseAssembler<T>::assembleScans(AssembleScans::Request& req, AssembleScans::Response& resp)
00267 {
00268   //printf("Starting Service Request\n") ;
00269 
00270   scan_hist_mutex_.lock() ;
00271   // Determine where in our history we actually are
00272   unsigned int i = 0 ;
00273 
00274   // Find the beginning of the request. Probably should be a search
00275   while ( i < scan_hist_.size() &&                                                    // Don't go past end of deque
00276           scan_hist_[i].header.stamp < req.begin )                                    // Keep stepping until we've exceeded the start time
00277   {
00278     i++ ;
00279   }
00280   unsigned int start_index = i ;
00281 
00282   unsigned int req_pts = 0 ;                                                          // Keep a total of the points in the current request
00283   // Find the end of the request
00284   while ( i < scan_hist_.size() &&                                                    // Don't go past end of deque
00285           scan_hist_[i].header.stamp < req.end )                                      // Don't go past the end-time of the request
00286   {
00287     req_pts += (scan_hist_[i].points.size ()+downsample_factor_-1)/downsample_factor_ ;
00288     i += downsample_factor_ ;
00289   }
00290   unsigned int past_end_index = i ;
00291 
00292   if (start_index == past_end_index)
00293   {
00294     resp.cloud.header.frame_id = fixed_frame_ ;
00295     resp.cloud.header.stamp = req.end ;
00296     resp.cloud.points.resize (0) ;
00297     resp.cloud.channels.resize (0) ;
00298   }
00299   else
00300   {
00301     // Note: We are assuming that channel information is consistent across multiple scans. If not, then bad things (segfaulting) will happen
00302     // Allocate space for the cloud
00303     resp.cloud.points.resize (req_pts);
00304     const unsigned int num_channels = scan_hist_[start_index].channels.size ();
00305     resp.cloud.channels.resize (num_channels) ;
00306     for (i = 0; i<num_channels; i++)
00307     {
00308       resp.cloud.channels[i].name = scan_hist_[start_index].channels[i].name ;
00309       resp.cloud.channels[i].values.resize (req_pts) ;
00310     }
00311     //resp.cloud.header.stamp = req.end ;
00312     resp.cloud.header.frame_id = fixed_frame_ ;
00313     unsigned int cloud_count = 0 ;
00314     for (i=start_index; i<past_end_index; i+=downsample_factor_)
00315     {
00316 
00317       // Sanity check: Each channel should be the same length as the points vector
00318       for (unsigned int chan_ind = 0; chan_ind < scan_hist_[i].channels.size(); chan_ind++)
00319       {
00320         if (scan_hist_[i].points.size () != scan_hist_[i].channels[chan_ind].values.size())
00321           ROS_FATAL("Trying to add a malformed point cloud. Cloud has %u points, but channel %u has %u elems", (int)scan_hist_[i].points.size (), chan_ind, (int)scan_hist_[i].channels[chan_ind].values.size ());
00322       }
00323 
00324       for(unsigned int j=0; j<scan_hist_[i].points.size (); j+=downsample_factor_)
00325       {
00326         resp.cloud.points[cloud_count].x = scan_hist_[i].points[j].x ;
00327         resp.cloud.points[cloud_count].y = scan_hist_[i].points[j].y ;
00328         resp.cloud.points[cloud_count].z = scan_hist_[i].points[j].z ;
00329 
00330         for (unsigned int k=0; k<num_channels; k++)
00331           resp.cloud.channels[k].values[cloud_count] = scan_hist_[i].channels[k].values[j] ;
00332 
00333         cloud_count++ ;
00334       }
00335       resp.cloud.header.stamp = scan_hist_[i].header.stamp;
00336     }
00337   }
00338   scan_hist_mutex_.unlock() ;
00339 
00340   ROS_DEBUG("Point Cloud Results: Aggregated from index %u->%u. BufferSize: %lu. Points in cloud: %u", start_index, past_end_index, scan_hist_.size(), (int)resp.cloud.points.size ()) ;
00341   return true ;
00342 }
00343 
00344 }


laser_assembler
Author(s): Vijay Pradeep
autogenerated on Fri Jan 3 2014 11:27:46