pointcloud_octomap_updater.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2011, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Jon Binney, Ioan Sucan */
36 
37 #include <cmath>
41 #include <tf2/LinearMath/Vector3.h>
44 #include <XmlRpcException.h>
45 
46 #include <memory>
47 
48 namespace occupancy_map_monitor
49 {
50 static const std::string LOGNAME = "occupancy_map_monitor";
52  : OccupancyMapUpdater("PointCloudUpdater")
53  , private_nh_("~")
54  , scale_(1.0)
55  , padding_(0.0)
56  , max_range_(std::numeric_limits<double>::infinity())
57  , point_subsample_(1)
58  , max_update_rate_(0)
59  , point_cloud_subscriber_(nullptr)
60  , point_cloud_filter_(nullptr)
61 {
62 }
63 
64 PointCloudOctomapUpdater::~PointCloudOctomapUpdater()
65 {
66  stopHelper();
67 }
68 
69 bool PointCloudOctomapUpdater::setParams(XmlRpc::XmlRpcValue& params)
70 {
71  try
72  {
73  if (!params.hasMember("point_cloud_topic"))
74  return false;
75  point_cloud_topic_ = static_cast<const std::string&>(params["point_cloud_topic"]);
76 
77  readXmlParam(params, "max_range", &max_range_);
78  readXmlParam(params, "padding_offset", &padding_);
79  readXmlParam(params, "padding_scale", &scale_);
80  readXmlParam(params, "point_subsample", &point_subsample_);
81  if (params.hasMember("max_update_rate"))
82  readXmlParam(params, "max_update_rate", &max_update_rate_);
83  if (params.hasMember("filtered_cloud_topic"))
84  filtered_cloud_topic_ = static_cast<const std::string&>(params["filtered_cloud_topic"]);
85  if (params.hasMember("ns"))
86  ns_ = static_cast<const std::string&>(params["ns"]);
87  }
88  catch (XmlRpc::XmlRpcException& ex)
89  {
90  ROS_ERROR_STREAM_NAMED(LOGNAME, "XmlRpc Exception: " << ex.getMessage());
91  return false;
92  }
93 
94  return true;
95 }
96 
97 bool PointCloudOctomapUpdater::initialize()
98 {
99  tf_buffer_ = std::make_shared<tf2_ros::Buffer>();
100  tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_, root_nh_);
101  shape_mask_ = std::make_unique<point_containment_filter::ShapeMask>();
102  shape_mask_->setTransformCallback(
103  [this](ShapeHandle shape, Eigen::Isometry3d& tf) { return getShapeTransform(shape, tf); });
104 
105  std::string prefix = "";
106  if (!ns_.empty())
107  prefix = ns_ + "/";
108  if (!filtered_cloud_topic_.empty())
109  filtered_cloud_publisher_ =
110  private_nh_.advertise<sensor_msgs::PointCloud2>(prefix + filtered_cloud_topic_, 10, false);
111  return true;
112 }
113 
114 void PointCloudOctomapUpdater::start()
115 {
116  if (point_cloud_subscriber_)
117  return;
118  /* subscribe to point cloud topic using tf filter*/
119  point_cloud_subscriber_ = new message_filters::Subscriber<sensor_msgs::PointCloud2>(root_nh_, point_cloud_topic_, 5);
120  if (tf_listener_ && tf_buffer_ && !monitor_->getMapFrame().empty())
121  {
122  point_cloud_filter_ = new tf2_ros::MessageFilter<sensor_msgs::PointCloud2>(*point_cloud_subscriber_, *tf_buffer_,
123  monitor_->getMapFrame(), 5, root_nh_);
124  point_cloud_filter_->registerCallback(
125  [this](const sensor_msgs::PointCloud2::ConstPtr& cloud) { cloudMsgCallback(cloud); });
126  ROS_INFO_NAMED(LOGNAME, "Listening to '%s' using message filter with target frame '%s'", point_cloud_topic_.c_str(),
127  point_cloud_filter_->getTargetFramesString().c_str());
128  }
129  else
130  {
131  point_cloud_subscriber_->registerCallback(
132  [this](const sensor_msgs::PointCloud2::ConstPtr& cloud) { cloudMsgCallback(cloud); });
133  ROS_INFO_NAMED(LOGNAME, "Listening to '%s'", point_cloud_topic_.c_str());
134  }
135 }
136 
137 void PointCloudOctomapUpdater::stopHelper()
138 {
139  delete point_cloud_filter_;
140  delete point_cloud_subscriber_;
141 }
142 
143 void PointCloudOctomapUpdater::stop()
144 {
145  stopHelper();
146  point_cloud_filter_ = nullptr;
147  point_cloud_subscriber_ = nullptr;
148 }
149 
150 ShapeHandle PointCloudOctomapUpdater::excludeShape(const shapes::ShapeConstPtr& shape)
151 {
152  ShapeHandle h = 0;
153  if (shape_mask_)
154  h = shape_mask_->addShape(shape, scale_, padding_);
155  else
156  ROS_ERROR_NAMED(LOGNAME, "Shape filter not yet initialized!");
157  return h;
158 }
159 
160 void PointCloudOctomapUpdater::forgetShape(ShapeHandle handle)
161 {
162  if (shape_mask_)
163  shape_mask_->removeShape(handle);
164 }
165 
166 bool PointCloudOctomapUpdater::getShapeTransform(ShapeHandle h, Eigen::Isometry3d& transform) const
167 {
168  ShapeTransformCache::const_iterator it = transform_cache_.find(h);
169  if (it != transform_cache_.end())
170  {
171  transform = it->second;
172  }
173  return it != transform_cache_.end();
174 }
175 
176 void PointCloudOctomapUpdater::updateMask(const sensor_msgs::PointCloud2& /*cloud*/,
177  const Eigen::Vector3d& /*sensor_origin*/, std::vector<int>& /*mask*/)
178 {
179 }
180 
181 void PointCloudOctomapUpdater::cloudMsgCallback(const sensor_msgs::PointCloud2::ConstPtr& cloud_msg)
182 {
183  ROS_DEBUG_NAMED(LOGNAME, "Received a new point cloud message");
185 
186  if (max_update_rate_ > 0)
187  {
188  // ensure we are not updating the octomap representation too often
189  if (ros::Time::now() - last_update_time_ <= ros::Duration(1.0 / max_update_rate_))
190  return;
191  last_update_time_ = ros::Time::now();
192  }
193 
194  if (monitor_->getMapFrame().empty())
195  monitor_->setMapFrame(cloud_msg->header.frame_id);
196 
197  /* get transform for cloud into map frame */
199  if (monitor_->getMapFrame() == cloud_msg->header.frame_id)
200  map_h_sensor.setIdentity();
201  else
202  {
203  if (tf_buffer_)
204  {
205  try
206  {
207  tf2::fromMsg(tf_buffer_->lookupTransform(monitor_->getMapFrame(), cloud_msg->header.frame_id,
208  cloud_msg->header.stamp),
209  map_h_sensor);
210  }
211  catch (tf2::TransformException& ex)
212  {
213  ROS_ERROR_STREAM_NAMED(LOGNAME, "Transform error of sensor data: " << ex.what() << "; quitting callback");
214  return;
215  }
216  }
217  else
218  return;
219  }
220 
221  /* compute sensor origin in map frame */
222  const tf2::Vector3& sensor_origin_tf = map_h_sensor.getOrigin();
223  octomap::point3d sensor_origin(sensor_origin_tf.getX(), sensor_origin_tf.getY(), sensor_origin_tf.getZ());
224  Eigen::Vector3d sensor_origin_eigen(sensor_origin_tf.getX(), sensor_origin_tf.getY(), sensor_origin_tf.getZ());
225 
226  if (!updateTransformCache(cloud_msg->header.frame_id, cloud_msg->header.stamp))
227  return;
228 
229  /* mask out points on the robot */
230  shape_mask_->maskContainment(*cloud_msg, sensor_origin_eigen, 0.0, max_range_, mask_);
231  updateMask(*cloud_msg, sensor_origin_eigen, mask_);
232 
233  octomap::KeySet free_cells, occupied_cells, model_cells, clip_cells;
234  std::unique_ptr<sensor_msgs::PointCloud2> filtered_cloud;
235 
236  // We only use these iterators if we are creating a filtered_cloud for
237  // publishing. We cannot default construct these, so we use unique_ptr's
238  // to defer construction
239  std::unique_ptr<sensor_msgs::PointCloud2Iterator<float>> iter_filtered_x;
240  std::unique_ptr<sensor_msgs::PointCloud2Iterator<float>> iter_filtered_y;
241  std::unique_ptr<sensor_msgs::PointCloud2Iterator<float>> iter_filtered_z;
242 
243  if (!filtered_cloud_topic_.empty())
244  {
245  filtered_cloud = std::make_unique<sensor_msgs::PointCloud2>();
246  filtered_cloud->header = cloud_msg->header;
247  sensor_msgs::PointCloud2Modifier pcd_modifier(*filtered_cloud);
248  pcd_modifier.setPointCloud2FieldsByString(1, "xyz");
249  pcd_modifier.resize(cloud_msg->width * cloud_msg->height);
250 
251  // we have created a filtered_out, so we can create the iterators now
252  iter_filtered_x = std::make_unique<sensor_msgs::PointCloud2Iterator<float>>(*filtered_cloud, "x");
253  iter_filtered_y = std::make_unique<sensor_msgs::PointCloud2Iterator<float>>(*filtered_cloud, "y");
254  iter_filtered_z = std::make_unique<sensor_msgs::PointCloud2Iterator<float>>(*filtered_cloud, "z");
255  }
256  size_t filtered_cloud_size = 0;
257 
258  tree_->lockRead();
259 
260  try
261  {
262  /* do ray tracing to find which cells this point cloud indicates should be free, and which it indicates
263  * should be occupied */
264  for (unsigned int row = 0; row < cloud_msg->height; row += point_subsample_)
265  {
266  unsigned int row_c = row * cloud_msg->width;
267  sensor_msgs::PointCloud2ConstIterator<float> pt_iter(*cloud_msg, "x");
268  // set iterator to point at start of the current row
269  pt_iter += row_c;
270 
271  for (unsigned int col = 0; col < cloud_msg->width; col += point_subsample_, pt_iter += point_subsample_)
272  {
273  // if (mask_[row_c + col] == point_containment_filter::ShapeMask::CLIP)
274  // continue;
275 
276  /* check for NaN */
277  if (!std::isnan(pt_iter[0]) && !std::isnan(pt_iter[1]) && !std::isnan(pt_iter[2]))
278  {
279  /* occupied cell at ray endpoint if ray is shorter than max range and this point
280  isn't on a part of the robot*/
281  if (mask_[row_c + col] == point_containment_filter::ShapeMask::INSIDE)
282  {
283  // transform to map frame
284  tf2::Vector3 point_tf = map_h_sensor * tf2::Vector3(pt_iter[0], pt_iter[1], pt_iter[2]);
285  model_cells.insert(tree_->coordToKey(point_tf.getX(), point_tf.getY(), point_tf.getZ()));
286  }
287  else if (mask_[row_c + col] == point_containment_filter::ShapeMask::CLIP)
288  {
289  tf2::Vector3 clipped_point_tf =
290  map_h_sensor * (tf2::Vector3(pt_iter[0], pt_iter[1], pt_iter[2]).normalize() * max_range_);
291  clip_cells.insert(
292  tree_->coordToKey(clipped_point_tf.getX(), clipped_point_tf.getY(), clipped_point_tf.getZ()));
293  }
294  else
295  {
296  tf2::Vector3 point_tf = map_h_sensor * tf2::Vector3(pt_iter[0], pt_iter[1], pt_iter[2]);
297  occupied_cells.insert(tree_->coordToKey(point_tf.getX(), point_tf.getY(), point_tf.getZ()));
298  // build list of valid points if we want to publish them
299  if (filtered_cloud)
300  {
301  **iter_filtered_x = pt_iter[0];
302  **iter_filtered_y = pt_iter[1];
303  **iter_filtered_z = pt_iter[2];
304  ++filtered_cloud_size;
305  ++*iter_filtered_x;
306  ++*iter_filtered_y;
307  ++*iter_filtered_z;
308  }
309  }
310  }
311  }
312  }
313 
314  /* compute the free cells along each ray that ends at an occupied cell */
315  for (const octomap::OcTreeKey& occupied_cell : occupied_cells)
316  if (tree_->computeRayKeys(sensor_origin, tree_->keyToCoord(occupied_cell), key_ray_))
317  free_cells.insert(key_ray_.begin(), key_ray_.end());
318 
319  /* compute the free cells along each ray that ends at a model cell */
320  for (const octomap::OcTreeKey& model_cell : model_cells)
321  if (tree_->computeRayKeys(sensor_origin, tree_->keyToCoord(model_cell), key_ray_))
322  free_cells.insert(key_ray_.begin(), key_ray_.end());
323 
324  /* compute the free cells along each ray that ends at a clipped cell */
325  for (const octomap::OcTreeKey& clip_cell : clip_cells)
326  if (tree_->computeRayKeys(sensor_origin, tree_->keyToCoord(clip_cell), key_ray_))
327  free_cells.insert(key_ray_.begin(), key_ray_.end());
328  }
329  catch (...)
330  {
331  tree_->unlockRead();
332  return;
333  }
334 
335  tree_->unlockRead();
336 
337  /* cells that overlap with the model are not occupied */
338  for (const octomap::OcTreeKey& model_cell : model_cells)
339  occupied_cells.erase(model_cell);
340 
341  /* occupied cells are not free */
342  for (const octomap::OcTreeKey& occupied_cell : occupied_cells)
343  free_cells.erase(occupied_cell);
344 
345  tree_->lockWrite();
346 
347  try
348  {
349  /* mark free cells only if not seen occupied in this cloud */
350  for (const octomap::OcTreeKey& free_cell : free_cells)
351  tree_->updateNode(free_cell, false);
352 
353  /* now mark all occupied cells */
354  for (const octomap::OcTreeKey& occupied_cell : occupied_cells)
355  tree_->updateNode(occupied_cell, true);
356 
357  // set the logodds to the minimum for the cells that are part of the model
358  const float lg = tree_->getClampingThresMinLog() - tree_->getClampingThresMaxLog();
359  for (const octomap::OcTreeKey& model_cell : model_cells)
360  tree_->updateNode(model_cell, lg);
361  }
362  catch (...)
363  {
364  ROS_ERROR_NAMED(LOGNAME, "Internal error while updating octree");
365  }
366  tree_->unlockWrite();
367  ROS_DEBUG_NAMED(LOGNAME, "Processed point cloud in %lf ms", (ros::WallTime::now() - start).toSec() * 1000.0);
368  tree_->triggerUpdateCallback();
369 
370  if (filtered_cloud)
371  {
372  sensor_msgs::PointCloud2Modifier pcd_modifier(*filtered_cloud);
373  pcd_modifier.resize(filtered_cloud_size);
374  filtered_cloud_publisher_.publish(*filtered_cloud);
375  }
376 }
377 } // namespace occupancy_map_monitor
point_cloud2_iterator.h
tf2_ros::MessageFilter< sensor_msgs::PointCloud2 >
XmlRpc::XmlRpcException::getMessage
const std::string & getMessage() const
tf2::fromMsg
void fromMsg(const A &, B &b)
tf2::Stamped
ROS_ERROR_NAMED
#define ROS_ERROR_NAMED(name,...)
Vector3.h
transform
template Halfspace< double > transform(const Halfspace< double > &a, const Transform3< double > &tf)
sensor_msgs::PointCloud2ConstIterator
message_filters::Subscriber< sensor_msgs::PointCloud2 >
octomath::Vector3
octomap::KeySet
unordered_ns::unordered_set< OcTreeKey, OcTreeKey::KeyHash > KeySet
ROS_INFO_NAMED
#define ROS_INFO_NAMED(name,...)
point_containment_filter::ShapeMask::CLIP
@ CLIP
Definition: shape_mask.h:93
ROS_DEBUG_NAMED
#define ROS_DEBUG_NAMED(name,...)
ros::WallTime::now
static WallTime now()
ROS_ERROR_STREAM_NAMED
#define ROS_ERROR_STREAM_NAMED(name, args)
point_containment_filter::ShapeMask::INSIDE
@ INSIDE
Definition: shape_mask.h:91
octomap::OcTreeKey
ros::WallTime
XmlRpc::XmlRpcException
occupancy_map_monitor.h
start
ROSCPP_DECL void start()
occupancy_map_monitor::ShapeHandle
unsigned int ShapeHandle
XmlRpc::XmlRpcValue::hasMember
bool hasMember(const std::string &name) const
pointcloud_octomap_updater.h
occupancy_map_monitor::PointCloudOctomapUpdater::PointCloudOctomapUpdater
PointCloudOctomapUpdater()
Definition: pointcloud_octomap_updater.cpp:83
std
sensor_msgs::PointCloud2Modifier
occupancy_map_monitor::LOGNAME
static const std::string LOGNAME
occupancy_map_monitor
tf2_geometry_msgs.h
shapes::ShapeConstPtr
std::shared_ptr< const Shape > ShapeConstPtr
XmlRpcException.h
tf2::TransformException
ros::Duration
Transform.h
XmlRpc::XmlRpcValue
ros::Time::now
static Time now()


perception
Author(s): Ioan Sucan , Jon Binney , Suat Gedikli
autogenerated on Wed Feb 21 2024 03:26:19