pointcloud_octomap_updater.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2011, 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 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 
00035 /* Author: Jon Binney, Ioan Sucan */
00036 
00037 #include <cmath>
00038 #include <moveit/pointcloud_octomap_updater/pointcloud_octomap_updater.h>
00039 #include <moveit/occupancy_map_monitor/occupancy_map_monitor.h>
00040 #include <message_filters/subscriber.h>
00041 #include <pcl_conversions/pcl_conversions.h>
00042 #include <XmlRpcException.h>
00043 
00044 namespace occupancy_map_monitor
00045 {
00046 
00047 PointCloudOctomapUpdater::PointCloudOctomapUpdater() : OccupancyMapUpdater("PointCloudUpdater"),
00048                                                        private_nh_("~"),
00049                                                        scale_(1.0),
00050                                                        padding_(0.0),
00051                                                        max_range_(std::numeric_limits<double>::infinity()),
00052                                                        point_subsample_(1),
00053                                                        point_cloud_subscriber_(NULL),
00054                                                        point_cloud_filter_(NULL)
00055 {
00056 }
00057 
00058 PointCloudOctomapUpdater::~PointCloudOctomapUpdater()
00059 {
00060   stopHelper();
00061 }
00062 
00063 bool PointCloudOctomapUpdater::setParams(XmlRpc::XmlRpcValue &params)
00064 {
00065   try
00066   {
00067     if (!params.hasMember("point_cloud_topic"))
00068       return false;
00069     point_cloud_topic_ = static_cast<const std::string&>(params["point_cloud_topic"]);
00070 
00071     readXmlParam(params, "max_range", &max_range_);
00072     readXmlParam(params, "padding_offset", &padding_);
00073     readXmlParam(params, "padding_scale", &scale_);
00074     readXmlParam(params, "point_subsample", &point_subsample_);
00075     if (params.hasMember("filtered_cloud_topic"))
00076       filtered_cloud_topic_ = static_cast<const std::string&>(params["filtered_cloud_topic"]);
00077   }
00078   catch (XmlRpc::XmlRpcException &ex)
00079   {
00080     ROS_ERROR("XmlRpc Exception: %s", ex.getMessage().c_str());
00081     return false;
00082   }
00083 
00084   return true;
00085 }
00086 
00087 bool PointCloudOctomapUpdater::initialize()
00088 {
00089   tf_ = monitor_->getTFClient();
00090   shape_mask_.reset(new point_containment_filter::ShapeMask());
00091   shape_mask_->setTransformCallback(boost::bind(&PointCloudOctomapUpdater::getShapeTransform, this, _1, _2));
00092   if (!filtered_cloud_topic_.empty())
00093     filtered_cloud_publisher_ = private_nh_.advertise<sensor_msgs::PointCloud2>(filtered_cloud_topic_, 10, false);
00094   return true;
00095 }
00096 
00097 void PointCloudOctomapUpdater::start()
00098 {
00099   if (point_cloud_subscriber_)
00100     return;
00101   /* subscribe to point cloud topic using tf filter*/
00102   point_cloud_subscriber_ = new message_filters::Subscriber<sensor_msgs::PointCloud2>(root_nh_, point_cloud_topic_, 5);
00103   if (tf_ && !monitor_->getMapFrame().empty())
00104   {
00105     point_cloud_filter_ = new tf::MessageFilter<sensor_msgs::PointCloud2>(*point_cloud_subscriber_, *tf_, monitor_->getMapFrame(), 5);
00106     point_cloud_filter_->registerCallback(boost::bind(&PointCloudOctomapUpdater::cloudMsgCallback, this, _1));
00107     ROS_INFO("Listening to '%s' using message filter with target frame '%s'", point_cloud_topic_.c_str(), point_cloud_filter_->getTargetFramesString().c_str());
00108   }
00109   else
00110   {
00111     point_cloud_subscriber_->registerCallback(boost::bind(&PointCloudOctomapUpdater::cloudMsgCallback, this, _1));
00112     ROS_INFO("Listening to '%s'", point_cloud_topic_.c_str());
00113   }
00114 }
00115 
00116 void PointCloudOctomapUpdater::stopHelper()
00117 {
00118   delete point_cloud_filter_;
00119   delete point_cloud_subscriber_;
00120 }
00121 
00122 void PointCloudOctomapUpdater::stop()
00123 {
00124   stopHelper();
00125   point_cloud_filter_ = NULL;
00126   point_cloud_subscriber_ = NULL;
00127 }
00128 
00129 ShapeHandle PointCloudOctomapUpdater::excludeShape(const shapes::ShapeConstPtr &shape)
00130 {
00131   ShapeHandle h = 0;
00132   if (shape_mask_)
00133     h = shape_mask_->addShape(shape, scale_, padding_);
00134   else
00135     ROS_ERROR("Shape filter not yet initialized!");
00136   return h;
00137 }
00138 
00139 void PointCloudOctomapUpdater::forgetShape(ShapeHandle handle)
00140 {
00141   if (shape_mask_)
00142     shape_mask_->removeShape(handle);
00143 }
00144 
00145 bool PointCloudOctomapUpdater::getShapeTransform(ShapeHandle h, Eigen::Affine3d &transform) const
00146 {
00147   ShapeTransformCache::const_iterator it = transform_cache_.find(h);
00148   if (it == transform_cache_.end())
00149   {
00150     ROS_ERROR("Internal error. Shape filter handle %u not found", h);
00151     return false;
00152   }
00153   transform = it->second;
00154   return true;
00155 }
00156 
00157 void PointCloudOctomapUpdater::updateMask(const pcl::PointCloud<pcl::PointXYZ> &cloud, const Eigen::Vector3d &sensor_origin, std::vector<int> &mask)
00158 {
00159 }
00160 
00161 void PointCloudOctomapUpdater::cloudMsgCallback(const sensor_msgs::PointCloud2::ConstPtr &cloud_msg)
00162 {
00163   ROS_DEBUG("Received a new point cloud message");
00164   ros::WallTime start = ros::WallTime::now();
00165 
00166   if (monitor_->getMapFrame().empty())
00167     monitor_->setMapFrame(cloud_msg->header.frame_id);
00168 
00169   /* get transform for cloud into map frame */
00170   tf::StampedTransform map_H_sensor;
00171   if (monitor_->getMapFrame() == cloud_msg->header.frame_id)
00172     map_H_sensor.setIdentity();
00173   else
00174   {
00175     if (tf_)
00176     {
00177       try
00178       {
00179         tf_->lookupTransform(monitor_->getMapFrame(), cloud_msg->header.frame_id, cloud_msg->header.stamp, map_H_sensor);
00180       }
00181       catch (tf::TransformException& ex)
00182       {
00183         ROS_ERROR_STREAM("Transform error of sensor data: " << ex.what() << "; quitting callback");
00184         return;
00185       }
00186     }
00187     else
00188       return;
00189   }
00190 
00191   /* convert cloud message to pcl cloud object */
00192   pcl::PointCloud<pcl::PointXYZ> cloud;
00193   pcl::fromROSMsg(*cloud_msg, cloud);
00194 
00195   /* compute sensor origin in map frame */
00196   const tf::Vector3 &sensor_origin_tf = map_H_sensor.getOrigin();
00197   octomap::point3d sensor_origin(sensor_origin_tf.getX(), sensor_origin_tf.getY(), sensor_origin_tf.getZ());
00198   Eigen::Vector3d sensor_origin_eigen(sensor_origin_tf.getX(), sensor_origin_tf.getY(), sensor_origin_tf.getZ());
00199 
00200   if (!updateTransformCache(cloud_msg->header.frame_id, cloud_msg->header.stamp))
00201   {
00202     ROS_ERROR_THROTTLE(1, "Transform cache was not updated. Self-filtering may fail.");
00203     return;
00204   }
00205 
00206   /* mask out points on the robot */
00207   shape_mask_->maskContainment(cloud, sensor_origin_eigen, 0.0, max_range_, mask_);
00208   updateMask(cloud, sensor_origin_eigen, mask_);
00209 
00210   octomap::KeySet free_cells, occupied_cells, model_cells, clip_cells;
00211   boost::scoped_ptr<pcl::PointCloud<pcl::PointXYZ> > filtered_cloud;
00212   if (!filtered_cloud_topic_.empty())
00213     filtered_cloud.reset(new pcl::PointCloud<pcl::PointXYZ>());
00214 
00215   tree_->lockRead();
00216 
00217   try
00218   {
00219     /* do ray tracing to find which cells this point cloud indicates should be free, and which it indicates
00220      * should be occupied */
00221     for (unsigned int row = 0; row < cloud.height; row += point_subsample_)
00222     {
00223       unsigned int row_c = row * cloud.width;
00224       for (unsigned int col = 0; col < cloud.width; col += point_subsample_)
00225       {
00226         //if (mask_[row_c + col] == point_containment_filter::ShapeMask::CLIP)
00227         //  continue;
00228         const pcl::PointXYZ &p = cloud(col, row);
00229 
00230         /* check for NaN */
00231         if (!isnan(p.x) && !isnan(p.y) && !isnan(p.z))
00232     {
00233       /* transform to map frame */
00234       tf::Vector3 point_tf = map_H_sensor * tf::Vector3(p.x, p.y, p.z);
00235 
00236       /* occupied cell at ray endpoint if ray is shorter than max range and this point
00237          isn't on a part of the robot*/
00238       if (mask_[row_c + col] == point_containment_filter::ShapeMask::INSIDE)
00239         model_cells.insert(tree_->coordToKey(point_tf.getX(), point_tf.getY(), point_tf.getZ()));
00240       else if (mask_[row_c + col] == point_containment_filter::ShapeMask::CLIP)
00241         clip_cells.insert(tree_->coordToKey(point_tf.getX(), point_tf.getY(), point_tf.getZ()));
00242       else
00243           {
00244             occupied_cells.insert(tree_->coordToKey(point_tf.getX(), point_tf.getY(), point_tf.getZ()));
00245             if (filtered_cloud)
00246               filtered_cloud->push_back(p);
00247           }
00248         }
00249       }
00250     }
00251 
00252     /* compute the free cells along each ray that ends at an occupied cell */
00253     for (octomap::KeySet::iterator it = occupied_cells.begin(), end = occupied_cells.end(); it != end; ++it)
00254       if (tree_->computeRayKeys(sensor_origin, tree_->keyToCoord(*it), key_ray_))
00255         free_cells.insert(key_ray_.begin(), key_ray_.end());
00256 
00257     /* compute the free cells along each ray that ends at a model cell */
00258     for (octomap::KeySet::iterator it = model_cells.begin(), end = model_cells.end(); it != end; ++it)
00259       if (tree_->computeRayKeys(sensor_origin, tree_->keyToCoord(*it), key_ray_))
00260         free_cells.insert(key_ray_.begin(), key_ray_.end());
00261 
00262     /* compute the free cells along each ray that ends at a clipped cell */
00263     for (octomap::KeySet::iterator it = clip_cells.begin(), end = clip_cells.end(); it != end; ++it)
00264       if (tree_->computeRayKeys(sensor_origin, tree_->keyToCoord(*it), key_ray_))
00265         free_cells.insert(key_ray_.begin(), key_ray_.end());
00266   }
00267   catch (...)
00268   {
00269     tree_->unlockRead();
00270     return;
00271   }
00272 
00273   tree_->unlockRead();
00274 
00275   /* cells that overlap with the model are not occupied */
00276   for (octomap::KeySet::iterator it = model_cells.begin(), end = model_cells.end(); it != end; ++it)
00277     occupied_cells.erase(*it);
00278 
00279   /* occupied cells are not free */
00280   for (octomap::KeySet::iterator it = occupied_cells.begin(), end = occupied_cells.end(); it != end; ++it)
00281     free_cells.erase(*it);
00282 
00283   tree_->lockWrite();
00284 
00285   try
00286   {
00287     /* mark free cells only if not seen occupied in this cloud */
00288     for (octomap::KeySet::iterator it = free_cells.begin(), end = free_cells.end(); it != end; ++it)
00289       tree_->updateNode(*it, false);
00290 
00291     /* now mark all occupied cells */
00292     for (octomap::KeySet::iterator it = occupied_cells.begin(), end = occupied_cells.end(); it != end; ++it)
00293       tree_->updateNode(*it, true);
00294 
00295     // set the logodds to the minimum for the cells that are part of the model
00296     const float lg = tree_->getClampingThresMinLog() - tree_->getClampingThresMaxLog();
00297     for (octomap::KeySet::iterator it = model_cells.begin(), end = model_cells.end(); it != end; ++it)
00298       tree_->updateNode(*it, lg);
00299   }
00300   catch (...)
00301   {
00302     ROS_ERROR("Internal error while updating octree");
00303   }
00304   tree_->unlockWrite();
00305   ROS_DEBUG("Processed point cloud in %lf ms", (ros::WallTime::now() - start).toSec() * 1000.0);
00306   tree_->triggerUpdateCallback();
00307 
00308   if (filtered_cloud)
00309   {
00310     sensor_msgs::PointCloud2 filtered_cloud_msg;
00311     pcl::toROSMsg(*filtered_cloud, filtered_cloud_msg);
00312     filtered_cloud_msg.header = cloud_msg->header;
00313     filtered_cloud_publisher_.publish(filtered_cloud_msg);
00314   }
00315 }
00316 
00317 }


perception
Author(s): Ioan Sucan , Jon Binney , Suat Gedikli
autogenerated on Wed Aug 26 2015 12:43:21