moveit2
The MoveIt Motion Planning Framework for ROS 2.
planning_scene_monitor.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: Ioan Sucan */
36 
41 #include <moveit_msgs/srv/get_planning_scene.hpp>
42 
43 #include <rclcpp/qos.hpp>
44 
45 // TODO: Remove conditional includes when released to all active distros.
46 #if __has_include(<tf2/exceptions.hpp>)
47 #include <tf2/exceptions.hpp>
48 #else
49 #include <tf2/exceptions.h>
50 #endif
51 #if __has_include(<tf2/LinearMath/Transform.hpp>)
52 #include <tf2/LinearMath/Transform.hpp>
53 #else
54 #include <tf2/LinearMath/Transform.h>
55 #endif
56 #include <tf2_eigen/tf2_eigen.hpp>
57 #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
58 
59 #include <boost/algorithm/string/join.hpp>
60 #include <memory>
61 
62 #include <std_msgs/msg/string.hpp>
63 
64 #include <chrono>
65 using namespace std::chrono_literals;
66 
67 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit_ros.planning_scene_monitor.planning_scene_monitor");
68 
69 namespace planning_scene_monitor
70 {
71 const std::string PlanningSceneMonitor::DEFAULT_JOINT_STATES_TOPIC = "joint_states";
72 const std::string PlanningSceneMonitor::DEFAULT_ATTACHED_COLLISION_OBJECT_TOPIC = "attached_collision_object";
73 const std::string PlanningSceneMonitor::DEFAULT_COLLISION_OBJECT_TOPIC = "collision_object";
74 const std::string PlanningSceneMonitor::DEFAULT_PLANNING_SCENE_WORLD_TOPIC = "planning_scene_world";
75 const std::string PlanningSceneMonitor::DEFAULT_PLANNING_SCENE_TOPIC = "planning_scene";
76 const std::string PlanningSceneMonitor::DEFAULT_PLANNING_SCENE_SERVICE = "get_planning_scene";
77 const std::string PlanningSceneMonitor::MONITORED_PLANNING_SCENE_TOPIC = "monitored_planning_scene";
78 
79 PlanningSceneMonitor::PlanningSceneMonitor(const rclcpp::Node::SharedPtr& node, const std::string& robot_description,
80  const std::string& name)
81  : PlanningSceneMonitor(node, planning_scene::PlanningScenePtr(), robot_description, name)
82 {
83 }
84 
85 PlanningSceneMonitor::PlanningSceneMonitor(const rclcpp::Node::SharedPtr& node,
86  const planning_scene::PlanningScenePtr& scene,
87  const std::string& robot_description, const std::string& name)
88  : PlanningSceneMonitor(node, scene, std::make_shared<robot_model_loader::RobotModelLoader>(node, robot_description),
89  name)
90 {
91 }
92 
93 PlanningSceneMonitor::PlanningSceneMonitor(const rclcpp::Node::SharedPtr& node,
94  const robot_model_loader::RobotModelLoaderPtr& rm_loader,
95  const std::string& name)
96  : PlanningSceneMonitor(node, planning_scene::PlanningScenePtr(), rm_loader, name)
97 {
98 }
99 
100 PlanningSceneMonitor::PlanningSceneMonitor(const rclcpp::Node::SharedPtr& node,
101  const planning_scene::PlanningScenePtr& scene,
102  const robot_model_loader::RobotModelLoaderPtr& rm_loader,
103  const std::string& name)
104  : monitor_name_(name)
105  , node_(node)
106  , private_executor_(std::make_shared<rclcpp::executors::SingleThreadedExecutor>())
107  , tf_buffer_(std::make_shared<tf2_ros::Buffer>(node->get_clock()))
108  , dt_state_update_(0.0)
109  , shape_transform_cache_lookup_wait_time_(0, 0)
110  , rm_loader_(rm_loader)
111 {
112  std::vector<std::string> new_args = rclcpp::NodeOptions().arguments();
113  new_args.push_back("--ros-args");
114  new_args.push_back("-r");
115  // Add random ID to prevent warnings about multiple publishers within the same node
116  new_args.push_back(std::string("__node:=") + node_->get_name() + "_private_" +
117  std::to_string(reinterpret_cast<std::size_t>(this)));
118  new_args.push_back("--");
119  pnode_ = std::make_shared<rclcpp::Node>("_", node_->get_namespace(), rclcpp::NodeOptions().arguments(new_args));
120  tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
121  tf_buffer_->setUsingDedicatedThread(true);
122  // use same callback queue as root_nh_
123  // root_nh_.setCallbackQueue(&queue_);
124  // nh_.setCallbackQueue(&queue_);
125  // spinner_.reset(new ros::AsyncSpinner(1, &queue_));
126  // spinner_->start();
127  initialize(scene);
128 
129  use_sim_time_ = node->get_parameter("use_sim_time").as_bool();
130 }
131 
133 {
134  if (scene_)
135  {
136  scene_->setCollisionObjectUpdateCallback(collision_detection::World::ObserverCallbackFn());
137  scene_->setAttachedBodyUpdateCallback(moveit::core::AttachedBodyCallback());
138  }
143 
144  private_executor_->cancel();
145  if (private_executor_thread_.joinable())
147  private_executor_.reset();
148 
149  current_state_monitor_.reset();
150  scene_const_.reset();
151  scene_.reset();
152  parent_scene_.reset();
153  robot_model_.reset();
154  rm_loader_.reset();
155 }
156 
157 planning_scene::PlanningScenePtr PlanningSceneMonitor::copyPlanningScene(const moveit_msgs::msg::PlanningScene& diff)
158 {
159  // We cannot use LockedPlanningSceneRO for RAII because it requires a PSMPtr
160  // Instead assume clone will not throw
161  lockSceneRead();
163  unlockSceneRead();
164 
165  if (!moveit::core::isEmpty(diff))
166  scene->setPlanningSceneDiffMsg(diff);
167  return scene;
168 }
169 
170 void PlanningSceneMonitor::initialize(const planning_scene::PlanningScenePtr& scene)
171 {
172  if (monitor_name_.empty())
173  monitor_name_ = "planning_scene_monitor";
174  robot_description_ = rm_loader_->getRobotDescription();
175  if (rm_loader_->getModel())
176  {
177  robot_model_ = rm_loader_->getModel();
178  scene_ = scene;
179  if (!scene_)
180  {
181  try
182  {
183  scene_ = std::make_shared<planning_scene::PlanningScene>(rm_loader_->getModel());
186 
187  scene_->getCollisionEnvNonConst()->setPadding(default_robot_padd_);
188  scene_->getCollisionEnvNonConst()->setScale(default_robot_scale_);
189  for (const std::pair<const std::string, double>& it : default_robot_link_padd_)
190  {
191  scene_->getCollisionEnvNonConst()->setLinkPadding(it.first, it.second);
192  }
193  for (const std::pair<const std::string, double>& it : default_robot_link_scale_)
194  {
195  scene_->getCollisionEnvNonConst()->setLinkScale(it.first, it.second);
196  }
197  }
198  catch (moveit::ConstructException& e)
199  {
200  RCLCPP_ERROR(LOGGER, "Configuration of planning scene failed");
201  scene_.reset();
202  }
203  }
204  // scene_const_ is set regardless if scene_ is null or not
206  if (scene_)
207  {
208  // The scene_ is loaded on the collision loader only if it was correctly instantiated
209  collision_loader_.setupScene(node_, scene_);
210  scene_->setAttachedBodyUpdateCallback([this](moveit::core::AttachedBody* body, bool attached) {
212  });
213  scene_->setCollisionObjectUpdateCallback(
214  [this](const collision_detection::World::ObjectConstPtr& object, collision_detection::World::Action action) {
216  });
217  }
218  }
219  else
220  {
221  RCLCPP_ERROR(LOGGER, "Robot model not loaded");
222  }
223 
226 
227  last_update_time_ = last_robot_motion_time_ = rclcpp::Clock().now();
228  last_robot_state_update_wall_time_ = std::chrono::system_clock::now();
229  dt_state_update_ = std::chrono::duration<double>(0.03);
230 
231  double temp_wait_time = 0.05;
232 
233  if (!robot_description_.empty())
234  node_->get_parameter_or(robot_description_ + "_planning.shape_transform_cache_lookup_wait_time", temp_wait_time,
235  temp_wait_time);
236 
237  shape_transform_cache_lookup_wait_time_ = rclcpp::Duration::from_seconds(temp_wait_time);
238 
239  state_update_pending_.store(false);
240  // Period for 0.1 sec
241  using std::chrono::nanoseconds;
242  state_update_timer_ = pnode_->create_wall_timer(dt_state_update_, [this]() { return stateUpdateTimerCallback(); });
243  private_executor_->add_node(pnode_);
244 
245  // start executor on a different thread now
246  private_executor_thread_ = std::thread([this]() { private_executor_->spin(); });
247 
248  auto declare_parameter = [this](const std::string& param_name, auto default_val,
249  const std::string& description) -> auto
250  {
251  rcl_interfaces::msg::ParameterDescriptor desc;
252  desc.set__description(description);
253  return pnode_->declare_parameter(param_name, default_val, desc);
254  };
255 
256  try
257  {
258  // Set up publishing parameters
259  bool publish_planning_scene =
260  declare_parameter("publish_planning_scene", false, "Set to True to publish Planning Scenes");
261  bool publish_geometry_updates = declare_parameter("publish_geometry_updates", false,
262  "Set to True to publish geometry updates of the planning scene");
263  bool publish_state_updates =
264  declare_parameter("publish_state_updates", false, "Set to True to publish state updates of the planning scene");
265  bool publish_transform_updates = declare_parameter(
266  "publish_transforms_updates", false, "Set to True to publish transform updates of the planning scene");
267  double publish_planning_scene_hz = declare_parameter(
268  "publish_planning_scene_hz", 4.0, "Set the maximum frequency at which planning scene updates are published");
269  updatePublishSettings(publish_geometry_updates, publish_state_updates, publish_transform_updates,
270  publish_planning_scene, publish_planning_scene_hz);
271  }
272  catch (const rclcpp::exceptions::InvalidParameterTypeException& e)
273  {
274  RCLCPP_ERROR_STREAM(LOGGER, "Invalid parameter type in PlanningSceneMonitor: " << e.what());
275  RCLCPP_ERROR(LOGGER, "Dynamic publishing parameters won't be available");
276  return;
277  }
278 
279  auto psm_parameter_set_callback = [this](const std::vector<rclcpp::Parameter>& parameters) -> auto
280  {
281  auto result = rcl_interfaces::msg::SetParametersResult();
282  result.successful = true;
283 
284  bool publish_planning_scene = false, publish_geometry_updates = false, publish_state_updates = false,
285  publish_transform_updates = false;
286  double publish_planning_scene_hz = 4.0;
287  bool declared_params_valid = pnode_->get_parameter("publish_planning_scene", publish_planning_scene) &&
288  pnode_->get_parameter("publish_geometry_updates", publish_geometry_updates) &&
289  pnode_->get_parameter("publish_state_updates", publish_state_updates) &&
290  pnode_->get_parameter("publish_transforms_updates", publish_transform_updates) &&
291  pnode_->get_parameter("publish_planning_scene_hz", publish_planning_scene_hz);
292 
293  if (!declared_params_valid)
294  {
295  RCLCPP_ERROR(LOGGER, "Initially declared parameters are invalid - failed to process update callback");
296  result.successful = false;
297  return result;
298  }
299 
300  for (const auto& parameter : parameters)
301  {
302  const auto& name = parameter.get_name();
303  const auto& type = parameter.get_type();
304 
305  // Only allow already declared parameters with same value type
306  if (!pnode_->has_parameter(name) || pnode_->get_parameter(name).get_type() != type)
307  {
308  RCLCPP_ERROR(LOGGER, "Invalid parameter in PlanningSceneMonitor parameter callback");
309  result.successful = false;
310  return result;
311  }
312 
313  // Update parameter values
314  if (name == "planning_scene_monitor.publish_planning_scene")
315  publish_planning_scene = parameter.as_bool();
316  else if (name == "planning_scene_monitor.publish_geometry_updates")
317  publish_geometry_updates = parameter.as_bool();
318  else if (name == "planning_scene_monitor.publish_state_updates")
319  publish_state_updates = parameter.as_bool();
320  else if (name == "planning_scene_monitor.publish_transforms_updates")
321  publish_transform_updates = parameter.as_bool();
322  else if (name == "planning_scene_monitor.publish_planning_scene_hz")
323  publish_planning_scene_hz = parameter.as_double();
324  }
325 
326  if (result.successful)
327  updatePublishSettings(publish_geometry_updates, publish_state_updates, publish_transform_updates,
328  publish_planning_scene, publish_planning_scene_hz);
329  return result;
330  };
331 
332  callback_handler_ = pnode_->add_on_set_parameters_callback(psm_parameter_set_callback);
333 }
334 
336 {
337  if (scene_)
338  {
339  if (flag)
340  {
341  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
342  if (scene_)
343  {
344  scene_->setAttachedBodyUpdateCallback(moveit::core::AttachedBodyCallback());
345  scene_->setCollisionObjectUpdateCallback(collision_detection::World::ObserverCallbackFn());
346  scene_->decoupleParent();
348  scene_ = parent_scene_->diff();
350  scene_->setAttachedBodyUpdateCallback([this](moveit::core::AttachedBody* body, bool attached) {
352  });
353  scene_->setCollisionObjectUpdateCallback(
354  [this](const collision_detection::World::ObjectConstPtr& object,
356  }
357  }
358  else
359  {
361  {
362  RCLCPP_WARN(LOGGER, "Diff monitoring was stopped while publishing planning scene diffs. "
363  "Stopping planning scene diff publisher");
365  }
366  {
367  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
368  if (scene_)
369  {
370  scene_->decoupleParent();
371  parent_scene_.reset();
372  // remove the '+' added by .diff() at the end of the scene name
373  if (!scene_->getName().empty())
374  {
375  if (scene_->getName()[scene_->getName().length() - 1] == '+')
376  scene_->setName(scene_->getName().substr(0, scene_->getName().length() - 1));
377  }
378  }
379  }
380  }
381  }
382 }
383 
385 {
387  {
388  std::unique_ptr<std::thread> copy;
389  copy.swap(publish_planning_scene_);
390  new_scene_update_condition_.notify_all();
391  copy->join();
392  monitorDiffs(false);
394  RCLCPP_INFO(LOGGER, "Stopped publishing maintained planning scene.");
395  }
396 }
397 
399  const std::string& planning_scene_topic)
400 {
401  publish_update_types_ = update_type;
403  {
404  planning_scene_publisher_ = pnode_->create_publisher<moveit_msgs::msg::PlanningScene>(planning_scene_topic, 100);
405  RCLCPP_INFO(LOGGER, "Publishing maintained planning scene on '%s'", planning_scene_topic.c_str());
406  monitorDiffs(true);
407  publish_planning_scene_ = std::make_unique<std::thread>([this] { scenePublishingThread(); });
408  }
409 }
410 
411 void PlanningSceneMonitor::scenePublishingThread()
412 {
413  RCLCPP_DEBUG(LOGGER, "Started scene publishing thread ...");
414 
415  // publish the full planning scene once
416  {
417  moveit_msgs::msg::PlanningScene msg;
418  {
420  if (octomap_monitor_)
421  lock = octomap_monitor_->getOcTreePtr()->reading();
422  scene_->getPlanningSceneMsg(msg);
423  }
424  planning_scene_publisher_->publish(msg);
425  RCLCPP_DEBUG(LOGGER, "Published the full planning scene: '%s'", msg.name.c_str());
426  }
427 
428  do
429  {
430  moveit_msgs::msg::PlanningScene msg;
431  bool publish_msg = false;
432  bool is_full = false;
433  rclcpp::Rate rate(publish_planning_scene_frequency_);
434  {
435  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
437  new_scene_update_condition_.wait(ulock);
439  {
441  {
443  is_full = true;
444  else
445  {
447  if (octomap_monitor_)
448  lock = octomap_monitor_->getOcTreePtr()->reading();
449  scene_->getPlanningSceneDiffMsg(msg);
451  {
452  msg.robot_state.attached_collision_objects.clear();
453  msg.robot_state.is_diff = true;
454  }
455  }
456  std::scoped_lock prevent_shape_cache_updates(shape_handles_lock_); // we don't want the
457  // transform cache to
458  // update while we are
459  // potentially changing
460  // attached bodies
461  scene_->setAttachedBodyUpdateCallback(moveit::core::AttachedBodyCallback());
462  scene_->setCollisionObjectUpdateCallback(collision_detection::World::ObserverCallbackFn());
463  scene_->pushDiffs(parent_scene_);
464  scene_->clearDiffs();
465  scene_->setAttachedBodyUpdateCallback([this](moveit::core::AttachedBody* body, bool attached) {
467  });
468  scene_->setCollisionObjectUpdateCallback(
469  [this](const collision_detection::World::ObjectConstPtr& object,
471  if (octomap_monitor_)
472  {
473  excludeAttachedBodiesFromOctree(); // in case updates have happened to the attached bodies, put them in
474  excludeWorldObjectsFromOctree(); // in case updates have happened to the attached bodies, put them in
475  }
476  if (is_full)
477  {
479  if (octomap_monitor_)
480  lock = octomap_monitor_->getOcTreePtr()->reading();
481  scene_->getPlanningSceneMsg(msg);
482  }
483  // also publish timestamp of this robot_state
484  msg.robot_state.joint_state.header.stamp = last_robot_motion_time_;
485  publish_msg = true;
486  }
488  }
489  }
490  if (publish_msg)
491  {
492  planning_scene_publisher_->publish(msg);
493  if (is_full)
494  RCLCPP_DEBUG(LOGGER, "Published full planning scene: '%s'", msg.name.c_str());
495  rate.sleep();
496  }
497  } while (publish_planning_scene_);
498 }
499 
500 void PlanningSceneMonitor::getMonitoredTopics(std::vector<std::string>& topics) const
501 {
502  // TODO(anasarrak): Do we need this for ROS2?
503  topics.clear();
505  {
506  const std::string& t = current_state_monitor_->getMonitoredTopic();
507  if (!t.empty())
508  topics.push_back(t);
509  }
511  topics.push_back(planning_scene_subscriber_->get_topic_name());
513  // TODO (anasarrak) This has been changed to subscriber on Moveit, look at
514  // https://github.com/ros-planning/moveit/pull/1406/files/cb9488312c00e9c8949d89b363766f092330954d#diff-fb6e26ecc9a73d59dbdae3f3e08145e6
515  topics.push_back(collision_object_subscriber_->get_topic_name());
517  topics.push_back(planning_scene_world_subscriber_->get_topic_name());
518 }
519 
520 namespace
521 {
522 bool sceneIsParentOf(const planning_scene::PlanningSceneConstPtr& scene,
523  const planning_scene::PlanningScene* possible_parent)
524 {
525  if (scene && scene.get() == possible_parent)
526  return true;
527  if (scene)
528  return sceneIsParentOf(scene->getParent(), possible_parent);
529  return false;
530 }
531 } // namespace
532 
533 bool PlanningSceneMonitor::updatesScene(const planning_scene::PlanningScenePtr& scene) const
534 {
535  return sceneIsParentOf(scene_const_, scene.get());
536 }
537 
538 bool PlanningSceneMonitor::updatesScene(const planning_scene::PlanningSceneConstPtr& scene) const
539 {
540  return sceneIsParentOf(scene_const_, scene.get());
541 }
542 
544 {
545  // do not modify update functions while we are calling them
546  std::scoped_lock lock(update_lock_);
547 
548  for (std::function<void(SceneUpdateType)>& update_callback : update_callbacks_)
549  update_callback(update_type);
550  new_scene_update_ = (SceneUpdateType)(static_cast<int>(new_scene_update_) | static_cast<int>(update_type));
551  new_scene_update_condition_.notify_all();
552 }
553 
554 bool PlanningSceneMonitor::requestPlanningSceneState(const std::string& service_name)
555 {
556  if (get_scene_service_ && get_scene_service_->get_service_name() == service_name)
557  {
558  RCLCPP_FATAL_STREAM(LOGGER, "requestPlanningSceneState() to self-provided service '" << service_name << "'");
559  throw std::runtime_error("requestPlanningSceneState() to self-provided service: " + service_name);
560  }
561  // use global namespace for service
562  auto client = pnode_->create_client<moveit_msgs::srv::GetPlanningScene>(service_name);
563  auto srv = std::make_shared<moveit_msgs::srv::GetPlanningScene::Request>();
564  srv->components.components = srv->components.SCENE_SETTINGS | srv->components.ROBOT_STATE |
565  srv->components.ROBOT_STATE_ATTACHED_OBJECTS | srv->components.WORLD_OBJECT_NAMES |
566  srv->components.WORLD_OBJECT_GEOMETRY | srv->components.OCTOMAP |
567  srv->components.TRANSFORMS | srv->components.ALLOWED_COLLISION_MATRIX |
568  srv->components.LINK_PADDING_AND_SCALING | srv->components.OBJECT_COLORS;
569 
570  // Make sure client is connected to server
571  RCLCPP_DEBUG(LOGGER, "Waiting for GetPlanningScene service `%s` to exist.", service_name.c_str());
572  if (client->wait_for_service(std::chrono::seconds(5)))
573  {
574  RCLCPP_DEBUG(LOGGER, "Sending service request to `%s`.", service_name.c_str());
575  auto service_result = client->async_send_request(srv);
576  const auto service_result_status = service_result.wait_for(std::chrono::seconds(5));
577  if (service_result_status == std::future_status::ready) // Success
578  {
579  RCLCPP_DEBUG(LOGGER, "Service request succeeded, applying new planning scene");
580  newPlanningSceneMessage(service_result.get()->scene);
581  return true;
582  }
583  if (service_result_status == std::future_status::timeout) // Timeout
584  {
585  RCLCPP_INFO(LOGGER, "GetPlanningScene service call to %s timed out. at %s:%d", service_name.c_str(), __FILE__,
586  __LINE__);
587  return false;
588  }
589  }
590 
591  // If we are here, service is not available or call failed
592  RCLCPP_INFO(LOGGER,
593  "Failed to call service %s, have you launched move_group or called psm.providePlanningSceneService()?",
594  service_name.c_str());
595 
596  return false;
597 }
598 
599 void PlanningSceneMonitor::providePlanningSceneService(const std::string& service_name)
600 {
601  // Load the service
602  get_scene_service_ = pnode_->create_service<moveit_msgs::srv::GetPlanningScene>(
603  service_name, [this](const moveit_msgs::srv::GetPlanningScene::Request::SharedPtr& req,
604  const moveit_msgs::srv::GetPlanningScene::Response::SharedPtr& res) {
605  return getPlanningSceneServiceCallback(req, res);
606  });
607 }
608 
609 void PlanningSceneMonitor::getPlanningSceneServiceCallback(
610  const moveit_msgs::srv::GetPlanningScene::Request::SharedPtr& req,
611  const moveit_msgs::srv::GetPlanningScene::Response::SharedPtr& res)
612 {
613  if (req->components.components & moveit_msgs::msg::PlanningSceneComponents::TRANSFORMS)
615 
616  moveit_msgs::msg::PlanningSceneComponents all_components;
617  all_components.components = UINT_MAX; // Return all scene components if nothing is specified.
618 
619  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
620  scene_->getPlanningSceneMsg(res->scene, req->components.components ? req->components : all_components);
621 }
622 
623 void PlanningSceneMonitor::updatePublishSettings(bool publish_geom_updates, bool publish_state_updates,
624  bool publish_transform_updates, bool publish_planning_scene,
625  double publish_planning_scene_hz)
626 {
628  if (publish_geom_updates)
629  event = (PlanningSceneMonitor::SceneUpdateType)(static_cast<int>(event) |
630  static_cast<int>(PlanningSceneMonitor::UPDATE_GEOMETRY));
631  if (publish_state_updates)
632  event = (PlanningSceneMonitor::SceneUpdateType)(static_cast<int>(event) |
633  static_cast<int>(PlanningSceneMonitor::UPDATE_STATE));
634  if (publish_transform_updates)
635  event = (PlanningSceneMonitor::SceneUpdateType)(static_cast<int>(event) |
636  static_cast<int>(PlanningSceneMonitor::UPDATE_TRANSFORMS));
637  if (publish_planning_scene)
638  {
639  setPlanningScenePublishingFrequency(publish_planning_scene_hz);
641  }
642  else
644 }
645 
646 void PlanningSceneMonitor::newPlanningSceneCallback(const moveit_msgs::msg::PlanningScene::ConstSharedPtr& scene)
647 {
649 }
650 
652 {
653  bool removed = false;
654  {
655  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
656  removed = scene_->getWorldNonConst()->removeObject(scene_->OCTOMAP_NS);
657 
658  if (octomap_monitor_)
659  {
660  octomap_monitor_->getOcTreePtr()->lockWrite();
661  octomap_monitor_->getOcTreePtr()->clear();
662  octomap_monitor_->getOcTreePtr()->unlockWrite();
663  }
664  else
665  {
666  RCLCPP_WARN(LOGGER, "Unable to clear octomap since no octomap monitor has been initialized");
667  } // Lift the scoped lock before calling triggerSceneUpdateEvent to avoid deadlock
668  }
669 
670  if (removed)
672 }
673 
674 bool PlanningSceneMonitor::newPlanningSceneMessage(const moveit_msgs::msg::PlanningScene& scene)
675 {
676  if (!scene_)
677  return false;
678 
679  bool result;
680 
682  std::string old_scene_name;
683  {
684  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
685  // we don't want the transform cache to update while we are potentially changing attached bodies
686  std::scoped_lock prevent_shape_cache_updates(shape_handles_lock_);
687 
688  last_update_time_ = rclcpp::Clock().now();
689  last_robot_motion_time_ = scene.robot_state.joint_state.header.stamp;
690  RCLCPP_DEBUG(LOGGER, "scene update %f robot stamp: %f", fmod(last_update_time_.seconds(), 10.),
691  fmod(last_robot_motion_time_.seconds(), 10.));
692  old_scene_name = scene_->getName();
693 
694  if (!scene.is_diff && parent_scene_)
695  {
696  // If there is no new robot_state, transfer RobotState from current scene to parent scene
697  if (scene.robot_state.is_diff)
698  parent_scene_->setCurrentState(scene_->getCurrentState());
699  // clear maintained (diff) scene_ and set the full new scene in parent_scene_ instead
700  scene_->clearDiffs();
701  result = parent_scene_->setPlanningSceneMsg(scene);
702  // There were no callbacks for individual object changes, so rebuild the octree masks
705  }
706  else
707  {
708  result = scene_->setPlanningSceneDiffMsg(scene);
709  }
710 
711  if (octomap_monitor_)
712  {
713  if (!scene.is_diff && scene.world.octomap.octomap.data.empty())
714  {
715  octomap_monitor_->getOcTreePtr()->lockWrite();
716  octomap_monitor_->getOcTreePtr()->clear();
717  octomap_monitor_->getOcTreePtr()->unlockWrite();
718  }
719  }
720  robot_model_ = scene_->getRobotModel();
721 
722  if (octomap_monitor_)
723  {
724  excludeAttachedBodiesFromOctree(); // in case updates have happened to the attached bodies, put them in
725  excludeWorldObjectsFromOctree(); // in case updates have happened to the attached bodies, put them in
726  }
727  }
728 
729  // if we have a diff, try to more accurately determine the update type
730  if (scene.is_diff)
731  {
732  bool no_other_scene_upd = (scene.name.empty() || scene.name == old_scene_name) &&
733  scene.allowed_collision_matrix.entry_names.empty() && scene.link_padding.empty() &&
734  scene.link_scale.empty();
735  if (no_other_scene_upd)
736  {
737  upd = UPDATE_NONE;
738  if (!moveit::core::isEmpty(scene.world))
739  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_GEOMETRY));
740 
741  if (!scene.fixed_frame_transforms.empty())
742  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_TRANSFORMS));
743 
744  if (!moveit::core::isEmpty(scene.robot_state))
745  {
746  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_STATE));
747  if (!scene.robot_state.attached_collision_objects.empty() || !scene.robot_state.is_diff)
748  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_GEOMETRY));
749  }
750  }
751  }
753  return result;
754 }
755 
757  const moveit_msgs::msg::PlanningSceneWorld::ConstSharedPtr& world)
758 {
759  if (scene_)
760  {
762  {
763  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
764  last_update_time_ = rclcpp::Clock().now();
765  scene_->getWorldNonConst()->clearObjects();
766  scene_->processPlanningSceneWorldMsg(*world);
767  if (octomap_monitor_)
768  {
769  if (world->octomap.octomap.data.empty())
770  {
771  octomap_monitor_->getOcTreePtr()->lockWrite();
772  octomap_monitor_->getOcTreePtr()->clear();
773  octomap_monitor_->getOcTreePtr()->unlockWrite();
774  }
775  }
776  }
778  }
779 }
780 
781 void PlanningSceneMonitor::collisionObjectCallback(const moveit_msgs::msg::CollisionObject::ConstSharedPtr& obj)
782 {
783  if (!scene_)
784  return;
785 
787  {
788  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
789  last_update_time_ = rclcpp::Clock().now();
790  if (!scene_->processCollisionObjectMsg(*obj))
791  return;
792  }
794 }
795 
796 void PlanningSceneMonitor::attachObjectCallback(const moveit_msgs::msg::AttachedCollisionObject::ConstSharedPtr& obj)
797 {
798  if (scene_)
799  {
801  {
802  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
803  last_update_time_ = rclcpp::Clock().now();
804  scene_->processAttachedCollisionObjectMsg(*obj);
805  }
807  }
808 }
809 
811 {
812  if (!octomap_monitor_)
813  return;
814 
815  std::scoped_lock _(shape_handles_lock_);
816 
818  const std::vector<const moveit::core::LinkModel*>& links = getRobotModel()->getLinkModelsWithCollisionGeometry();
819  auto start = std::chrono::system_clock::now();
820  bool warned = false;
821  for (const moveit::core::LinkModel* link : links)
822  {
823  std::vector<shapes::ShapeConstPtr> shapes = link->getShapes(); // copy shared ptrs on purpuse
824  for (std::size_t j = 0; j < shapes.size(); ++j)
825  {
826  // merge mesh vertices up to 0.1 mm apart
827  if (shapes[j]->type == shapes::MESH)
828  {
829  shapes::Mesh* m = static_cast<shapes::Mesh*>(shapes[j]->clone());
830  m->mergeVertices(1e-4);
831  shapes[j].reset(m);
832  }
833 
835  if (h)
836  link_shape_handles_[link].push_back(std::make_pair(h, j));
837  }
838 
839  if (!warned && ((std::chrono::system_clock::now() - start) > std::chrono::seconds(30)))
840  {
841  RCLCPP_WARN(LOGGER, "It is likely there are too many vertices in collision geometry");
842  warned = true;
843  }
844  }
845 }
846 
848 {
849  if (!octomap_monitor_)
850  return;
851 
852  std::scoped_lock _(shape_handles_lock_);
853 
854  for (std::pair<const moveit::core::LinkModel* const,
855  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>& link_shape_handle :
857  for (std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>& it : link_shape_handle.second)
858  octomap_monitor_->forgetShape(it.first);
859  link_shape_handles_.clear();
860 }
861 
863 {
864  if (!octomap_monitor_)
865  return;
866 
867  std::scoped_lock _(shape_handles_lock_);
868 
869  // clear information about any attached body, without referring to the AttachedBody* ptr (could be invalid)
870  for (std::pair<const moveit::core::AttachedBody* const,
871  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>& attached_body_shape_handle :
873  for (std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>& it : attached_body_shape_handle.second)
874  octomap_monitor_->forgetShape(it.first);
876 }
877 
879 {
880  std::scoped_lock _(shape_handles_lock_);
881 
883  // add attached objects again
884  std::vector<const moveit::core::AttachedBody*> ab;
885  scene_->getCurrentState().getAttachedBodies(ab);
886  for (const moveit::core::AttachedBody* body : ab)
888 }
889 
891 {
892  if (!octomap_monitor_)
893  return;
894 
895  std::scoped_lock _(shape_handles_lock_);
896 
897  // clear information about any attached object
898  for (std::pair<const std::string, std::vector<std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>>>&
899  collision_body_shape_handle : collision_body_shape_handles_)
900  for (std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>& it :
901  collision_body_shape_handle.second)
902  octomap_monitor_->forgetShape(it.first);
904 }
905 
907 {
908  std::scoped_lock _(shape_handles_lock_);
909 
911  for (const std::pair<const std::string, collision_detection::World::ObjectPtr>& it : *scene_->getWorld())
912  excludeWorldObjectFromOctree(it.second);
913 }
914 
916 {
917  if (!octomap_monitor_)
918  return;
919 
920  std::scoped_lock _(shape_handles_lock_);
921  bool found = false;
922  for (std::size_t i = 0; i < attached_body->getShapes().size(); ++i)
923  {
924  if (attached_body->getShapes()[i]->type == shapes::PLANE || attached_body->getShapes()[i]->type == shapes::OCTREE)
925  continue;
926  occupancy_map_monitor::ShapeHandle h = octomap_monitor_->excludeShape(attached_body->getShapes()[i]);
927  if (h)
928  {
929  found = true;
930  attached_body_shape_handles_[attached_body].push_back(std::make_pair(h, i));
931  }
932  }
933  if (found)
934  RCLCPP_DEBUG(LOGGER, "Excluding attached body '%s' from monitored octomap", attached_body->getName().c_str());
935 }
936 
938 {
939  if (!octomap_monitor_)
940  return;
941 
942  std::scoped_lock _(shape_handles_lock_);
943 
944  AttachedBodyShapeHandles::iterator it = attached_body_shape_handles_.find(attached_body);
945  if (it != attached_body_shape_handles_.end())
946  {
947  for (std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>& shape_handle : it->second)
948  octomap_monitor_->forgetShape(shape_handle.first);
949  RCLCPP_DEBUG(LOGGER, "Including attached body '%s' in monitored octomap", attached_body->getName().c_str());
951  }
952 }
953 
954 void PlanningSceneMonitor::excludeWorldObjectFromOctree(const collision_detection::World::ObjectConstPtr& obj)
955 {
956  if (!octomap_monitor_)
957  return;
958 
959  std::scoped_lock _(shape_handles_lock_);
960 
961  bool found = false;
962  for (std::size_t i = 0; i < obj->shapes_.size(); ++i)
963  {
964  if (obj->shapes_[i]->type == shapes::PLANE || obj->shapes_[i]->type == shapes::OCTREE)
965  continue;
966  occupancy_map_monitor::ShapeHandle h = octomap_monitor_->excludeShape(obj->shapes_[i]);
967  if (h)
968  {
969  collision_body_shape_handles_[obj->id_].push_back(std::make_pair(h, &obj->global_shape_poses_[i]));
970  found = true;
971  }
972  }
973  if (found)
974  RCLCPP_DEBUG(LOGGER, "Excluding collision object '%s' from monitored octomap", obj->id_.c_str());
975 }
976 
977 void PlanningSceneMonitor::includeWorldObjectInOctree(const collision_detection::World::ObjectConstPtr& obj)
978 {
979  if (!octomap_monitor_)
980  return;
981 
982  std::scoped_lock _(shape_handles_lock_);
983 
984  CollisionBodyShapeHandles::iterator it = collision_body_shape_handles_.find(obj->id_);
985  if (it != collision_body_shape_handles_.end())
986  {
987  for (std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>& shape_handle : it->second)
988  octomap_monitor_->forgetShape(shape_handle.first);
989  RCLCPP_DEBUG(LOGGER, "Including collision object '%s' in monitored octomap", obj->id_.c_str());
991  }
992 }
993 
995  bool just_attached)
996 {
997  if (!octomap_monitor_)
998  return;
999 
1000  if (just_attached)
1001  excludeAttachedBodyFromOctree(attached_body);
1002  else
1003  includeAttachedBodyInOctree(attached_body);
1004 }
1005 
1006 void PlanningSceneMonitor::currentWorldObjectUpdateCallback(const collision_detection::World::ObjectConstPtr& obj,
1008 {
1009  if (!octomap_monitor_)
1010  return;
1012  return;
1013 
1018  else
1019  {
1022  }
1023 }
1024 
1025 bool PlanningSceneMonitor::waitForCurrentRobotState(const rclcpp::Time& t, double wait_time)
1026 {
1027  if (t.nanoseconds() == 0)
1028  return false;
1029  RCLCPP_DEBUG(LOGGER, "sync robot state to: %.3fs", fmod(t.seconds(), 10.));
1030 
1032  {
1033  // Wait for next robot update in state monitor.
1034  bool success = current_state_monitor_->waitForCurrentState(t, wait_time);
1035 
1036  /* As robot updates are passed to the planning scene only in throttled fashion, there might
1037  be still an update pending. If so, explicitly update the planning scene here.
1038  If waitForCurrentState failed, we didn't get any new state updates within wait_time. */
1039  if (success)
1040  {
1041  if (state_update_pending_.load()) // perform state update
1042  {
1044  }
1045  return true;
1046  }
1047 
1048  RCLCPP_WARN(LOGGER, "Failed to fetch current robot state.");
1049  return false;
1050  }
1051 
1052  // Sometimes there is no state monitor. In this case state updates are received as part of scene updates only.
1053  // However, scene updates are only published if the robot actually moves. Hence we need a timeout!
1054  // As publishing planning scene updates is throttled (2Hz by default), a 1s timeout is a suitable default.
1055  auto start = node_->get_clock()->now();
1056  auto timeout = rclcpp::Duration::from_seconds(wait_time);
1057  std::shared_lock<std::shared_mutex> lock(scene_update_mutex_);
1058  rclcpp::Time prev_robot_motion_time = last_robot_motion_time_;
1059  while (last_robot_motion_time_ < t && // Wait until the state update actually reaches the scene.
1060  timeout > rclcpp::Duration(0, 0))
1061  {
1062  RCLCPP_DEBUG(LOGGER, "last robot motion: %f ago", (t - last_robot_motion_time_).seconds());
1063  new_scene_update_condition_.wait_for(lock, std::chrono::nanoseconds(timeout.nanoseconds()));
1064  timeout = timeout - (node_->get_clock()->now() - start); // compute remaining wait_time
1065  }
1066  bool success = last_robot_motion_time_ >= t;
1067  // suppress warning if we received an update at all
1068  if (!success && prev_robot_motion_time != last_robot_motion_time_)
1069  RCLCPP_WARN(LOGGER, "Maybe failed to update robot state, time diff: %.3fs", (t - last_robot_motion_time_).seconds());
1070 
1071  RCLCPP_DEBUG(LOGGER, "sync done: robot motion: %f scene update: %f", (t - last_robot_motion_time_).seconds(),
1072  (t - last_update_time_).seconds());
1073  return success;
1074 }
1075 
1077 {
1078  scene_update_mutex_.lock_shared();
1079  if (octomap_monitor_)
1080  octomap_monitor_->getOcTreePtr()->lockRead();
1081 }
1082 
1084 {
1085  if (octomap_monitor_)
1086  octomap_monitor_->getOcTreePtr()->unlockRead();
1087  scene_update_mutex_.unlock_shared();
1088 }
1089 
1091 {
1092  scene_update_mutex_.lock();
1093  if (octomap_monitor_)
1094  octomap_monitor_->getOcTreePtr()->lockWrite();
1095 }
1096 
1098 {
1099  if (octomap_monitor_)
1100  octomap_monitor_->getOcTreePtr()->unlockWrite();
1101  scene_update_mutex_.unlock();
1102 }
1103 
1104 void PlanningSceneMonitor::startSceneMonitor(const std::string& scene_topic)
1105 {
1106  stopSceneMonitor();
1107 
1108  RCLCPP_INFO(LOGGER, "Starting planning scene monitor");
1109  // listen for planning scene updates; these messages include transforms, so no need for filters
1110  if (!scene_topic.empty())
1111  {
1112  planning_scene_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::PlanningScene>(
1113  scene_topic, rclcpp::ServicesQoS(), [this](const moveit_msgs::msg::PlanningScene::ConstSharedPtr& scene) {
1114  return newPlanningSceneCallback(scene);
1115  });
1116  RCLCPP_INFO(LOGGER, "Listening to '%s'", planning_scene_subscriber_->get_topic_name());
1117  }
1118 }
1119 
1121 {
1123  {
1124  RCLCPP_INFO(LOGGER, "Stopping planning scene monitor");
1126  }
1127 }
1128 
1129 bool PlanningSceneMonitor::getShapeTransformCache(const std::string& target_frame, const rclcpp::Time& target_time,
1131 {
1132  try
1133  {
1134  std::scoped_lock _(shape_handles_lock_);
1135 
1136  for (const std::pair<const moveit::core::LinkModel* const,
1137  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>& link_shape_handle :
1139  {
1140  if (tf_buffer_->canTransform(target_frame, link_shape_handle.first->getName(), target_time,
1141  shape_transform_cache_lookup_wait_time_))
1142  {
1143  Eigen::Isometry3d ttr = tf2::transformToEigen(
1144  tf_buffer_->lookupTransform(target_frame, link_shape_handle.first->getName(), target_time));
1145  for (std::size_t j = 0; j < link_shape_handle.second.size(); ++j)
1146  cache[link_shape_handle.second[j].first] =
1147  ttr * link_shape_handle.first->getCollisionOriginTransforms()[link_shape_handle.second[j].second];
1148  }
1149  }
1150  for (const std::pair<const moveit::core::AttachedBody* const,
1151  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>&
1152  attached_body_shape_handle : attached_body_shape_handles_)
1153  {
1154  if (tf_buffer_->canTransform(target_frame, attached_body_shape_handle.first->getAttachedLinkName(), target_time,
1155  shape_transform_cache_lookup_wait_time_))
1156  {
1157  Eigen::Isometry3d transform = tf2::transformToEigen(tf_buffer_->lookupTransform(
1158  target_frame, attached_body_shape_handle.first->getAttachedLinkName(), target_time));
1159  for (std::size_t k = 0; k < attached_body_shape_handle.second.size(); ++k)
1160  cache[attached_body_shape_handle.second[k].first] =
1161  transform *
1162  attached_body_shape_handle.first->getShapePosesInLinkFrame()[attached_body_shape_handle.second[k].second];
1163  }
1164  }
1165  {
1166  if (tf_buffer_->canTransform(target_frame, scene_->getPlanningFrame(), target_time,
1167  shape_transform_cache_lookup_wait_time_))
1168  {
1169  Eigen::Isometry3d transform =
1170  tf2::transformToEigen(tf_buffer_->lookupTransform(target_frame, scene_->getPlanningFrame(), target_time));
1171  for (const std::pair<const std::string,
1172  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>>>&
1173  collision_body_shape_handle : collision_body_shape_handles_)
1174  for (const std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>& it :
1175  collision_body_shape_handle.second)
1176  cache[it.first] = transform * (*it.second);
1177  }
1178  }
1179  }
1180  catch (tf2::TransformException& ex)
1181  {
1182  rclcpp::Clock steady_clock = rclcpp::Clock();
1183  RCLCPP_ERROR_THROTTLE(LOGGER, steady_clock, 1000, "Transform error: %s", ex.what());
1184  return false;
1185  }
1186  return true;
1187 }
1188 
1189 void PlanningSceneMonitor::startWorldGeometryMonitor(const std::string& collision_objects_topic,
1190  const std::string& planning_scene_world_topic,
1191  const bool load_octomap_monitor)
1192 {
1194  RCLCPP_INFO(LOGGER, "Starting world geometry update monitor for collision objects, attached objects, octomap "
1195  "updates.");
1196 
1197  // Listen to the /collision_objects topic to detect requests to add/remove/update collision objects to/from the world
1198  if (!collision_objects_topic.empty())
1199  {
1200  collision_object_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::CollisionObject>(
1201  collision_objects_topic, rclcpp::ServicesQoS(),
1202  [this](const moveit_msgs::msg::CollisionObject::ConstSharedPtr& obj) { return collisionObjectCallback(obj); });
1203  RCLCPP_INFO(LOGGER, "Listening to '%s'", collision_objects_topic.c_str());
1204  }
1205 
1206  if (!planning_scene_world_topic.empty())
1207  {
1208  planning_scene_world_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::PlanningSceneWorld>(
1209  planning_scene_world_topic, rclcpp::ServicesQoS(),
1210  [this](const moveit_msgs::msg::PlanningSceneWorld::ConstSharedPtr& world) {
1211  return newPlanningSceneWorldCallback(world);
1212  });
1213  RCLCPP_INFO(LOGGER, "Listening to '%s' for planning scene world geometry", planning_scene_world_topic.c_str());
1214  }
1215 
1216  // Ocotomap monitor is optional
1217  if (load_octomap_monitor)
1218  {
1219  if (!octomap_monitor_)
1220  {
1222  std::make_unique<occupancy_map_monitor::OccupancyMapMonitor>(node_, tf_buffer_, scene_->getPlanningFrame());
1226 
1227  octomap_monitor_->setTransformCacheCallback([this](const std::string& frame, const rclcpp::Time& stamp,
1229  return getShapeTransformCache(frame, stamp, cache);
1230  });
1231  octomap_monitor_->setUpdateCallback([this] { octomapUpdateCallback(); });
1232  }
1233  octomap_monitor_->startMonitor();
1234  }
1235 }
1236 
1238 {
1240  {
1241  RCLCPP_INFO(LOGGER, "Stopping world geometry monitor");
1243  }
1245  {
1246  RCLCPP_INFO(LOGGER, "Stopping world geometry monitor");
1248  }
1249  if (octomap_monitor_)
1250  octomap_monitor_->stopMonitor();
1251 }
1252 
1253 void PlanningSceneMonitor::startStateMonitor(const std::string& joint_states_topic,
1254  const std::string& attached_objects_topic)
1255 {
1256  stopStateMonitor();
1257  if (scene_)
1258  {
1260  {
1262  std::make_shared<CurrentStateMonitor>(pnode_, getRobotModel(), tf_buffer_, use_sim_time_);
1263  }
1264  current_state_monitor_->addUpdateCallback(
1265  [this](const sensor_msgs::msg::JointState::ConstSharedPtr& joint_state) { return onStateUpdate(joint_state); });
1266  current_state_monitor_->startStateMonitor(joint_states_topic);
1267 
1268  {
1269  std::unique_lock<std::mutex> lock(state_update_mutex_);
1270  if (dt_state_update_.count() > 0)
1271  // ROS original: state_update_timer_.start();
1272  // TODO: re-enable WallTimer start()
1273  state_update_timer_ =
1274  pnode_->create_wall_timer(dt_state_update_, [this]() { return stateUpdateTimerCallback(); });
1275  }
1276 
1277  if (!attached_objects_topic.empty())
1278  {
1279  // using regular message filter as there's no header
1280  attached_collision_object_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::AttachedCollisionObject>(
1281  attached_objects_topic, rclcpp::ServicesQoS(),
1282  [this](const moveit_msgs::msg::AttachedCollisionObject::ConstSharedPtr& obj) {
1283  return attachObjectCallback(obj);
1284  });
1285  RCLCPP_INFO(LOGGER, "Listening to '%s' for attached collision objects",
1286  attached_collision_object_subscriber_->get_topic_name());
1287  }
1288  }
1289  else
1290  RCLCPP_ERROR(LOGGER, "Cannot monitor robot state because planning scene is not configured");
1291 }
1292 
1294 {
1296  current_state_monitor_->stopStateMonitor();
1299 
1300  if (state_update_timer_)
1301  state_update_timer_->cancel();
1302  state_update_pending_.store(false);
1303 }
1304 
1305 void PlanningSceneMonitor::onStateUpdate(const sensor_msgs::msg::JointState::ConstSharedPtr& /*joint_state */)
1306 {
1307  state_update_pending_.store(true);
1308 
1309  // Read access to last_robot_state_update_wall_time_ and dt_state_update_ is unprotected here
1310  // as reading invalid values is not critical (just postpones the next state update)
1311  // only update every dt_state_update_ seconds
1312  if (std::chrono::system_clock::now() - last_robot_state_update_wall_time_ >= dt_state_update_)
1314 }
1315 
1316 void PlanningSceneMonitor::stateUpdateTimerCallback()
1317 {
1318  // Read access to last_robot_state_update_wall_time_ and dt_state_update_ is unprotected here
1319  // as reading invalid values is not critical (just postpones the next state update)
1320  if (state_update_pending_.load() &&
1321  std::chrono::system_clock::now() - last_robot_state_update_wall_time_ >= dt_state_update_)
1323 }
1324 
1326 {
1327  if (!octomap_monitor_)
1328  return;
1329 
1331  {
1332  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
1333  last_update_time_ = rclcpp::Clock().now();
1334  octomap_monitor_->getOcTreePtr()->lockRead();
1335  try
1336  {
1337  scene_->processOctomapPtr(octomap_monitor_->getOcTreePtr(), Eigen::Isometry3d::Identity());
1338  octomap_monitor_->getOcTreePtr()->unlockRead();
1339  }
1340  catch (...)
1341  {
1342  octomap_monitor_->getOcTreePtr()->unlockRead(); // unlock and rethrow
1343  throw;
1344  }
1345  }
1347 }
1348 
1350 {
1351  bool update = false;
1352  if (hz > std::numeric_limits<double>::epsilon())
1353  {
1354  std::unique_lock<std::mutex> lock(state_update_mutex_);
1355  dt_state_update_ = std::chrono::duration<double>(1.0 / hz);
1356  // ROS original: state_update_timer_.start();
1357  // TODO: re-enable WallTimer start()
1358  state_update_timer_ = pnode_->create_wall_timer(dt_state_update_, [this]() { return stateUpdateTimerCallback(); });
1359  }
1360  else
1361  {
1362  // stop must be called with state_update_mutex_ unlocked to avoid deadlock
1363  // ROS original: state_update_timer_.stop();
1364  // TODO: re-enable WallTimer stop()
1365  if (state_update_timer_)
1366  state_update_timer_->cancel();
1367  std::unique_lock<std::mutex> lock(state_update_mutex_);
1368  dt_state_update_ = std::chrono::duration<double>(0.0);
1369  if (state_update_pending_.load())
1370  update = true;
1371  }
1372  RCLCPP_INFO(LOGGER, "Updating internal planning scene state at most every %lf seconds", dt_state_update_.count());
1373 
1374  if (update)
1376 }
1377 
1379 {
1380  rclcpp::Time time = node_->now();
1381  rclcpp::Clock steady_clock = rclcpp::Clock(RCL_STEADY_TIME);
1383  {
1384  std::vector<std::string> missing;
1385  if (!current_state_monitor_->haveCompleteState(missing) &&
1386  (time - current_state_monitor_->getMonitorStartTime()).seconds() > 1.0)
1387  {
1388  std::string missing_str = boost::algorithm::join(missing, ", ");
1389  RCLCPP_WARN_THROTTLE(LOGGER, steady_clock, 1000, "The complete state of the robot is not yet known. Missing %s",
1390  missing_str.c_str());
1391  }
1392 
1393  {
1394  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_, std::defer_lock);
1395  if (!skip_update_if_locked)
1396  {
1397  ulock.lock();
1398  }
1399  else if (!ulock.try_lock())
1400  {
1401  // Return if we can't lock scene_update_mutex, thus not blocking CurrentStateMonitor
1402  return;
1403  }
1405  RCLCPP_DEBUG(LOGGER, "robot state update %f", fmod(last_robot_motion_time_.seconds(), 10.));
1406  current_state_monitor_->setToCurrentState(scene_->getCurrentStateNonConst());
1407  scene_->getCurrentStateNonConst().update(); // compute all transforms
1408  }
1409 
1410  // Update state_update_mutex_ and last_robot_state_update_wall_time_
1411  {
1412  std::unique_lock<std::mutex> lock(state_update_mutex_);
1413  last_robot_state_update_wall_time_ = std::chrono::system_clock::now();
1414  state_update_pending_.store(false);
1415  }
1416 
1418  }
1419  else
1420  RCLCPP_ERROR_THROTTLE(LOGGER, steady_clock, 1000,
1421  "State monitor is not active. Unable to set the planning scene state");
1422 }
1423 
1424 void PlanningSceneMonitor::addUpdateCallback(const std::function<void(SceneUpdateType)>& fn)
1425 {
1426  std::scoped_lock lock(update_lock_);
1427  if (fn)
1428  update_callbacks_.push_back(fn);
1429 }
1430 
1432 {
1433  std::scoped_lock lock(update_lock_);
1434  update_callbacks_.clear();
1435 }
1436 
1438 {
1440  RCLCPP_DEBUG(LOGGER, "Maximum frequency for publishing a planning scene is now %lf Hz",
1442 }
1443 
1444 void PlanningSceneMonitor::getUpdatedFrameTransforms(std::vector<geometry_msgs::msg::TransformStamped>& transforms)
1445 {
1446  const std::string& target = getRobotModel()->getModelFrame();
1447 
1448  std::vector<std::string> all_frame_names;
1449  tf_buffer_->_getFrameStrings(all_frame_names);
1450  for (const std::string& all_frame_name : all_frame_names)
1451  {
1452  if (all_frame_name == target || getRobotModel()->hasLinkModel(all_frame_name))
1453  continue;
1454 
1455  geometry_msgs::msg::TransformStamped f;
1456  try
1457  {
1458  f = tf_buffer_->lookupTransform(target, all_frame_name, tf2::TimePointZero);
1459  }
1460  catch (tf2::TransformException& ex)
1461  {
1462  RCLCPP_WARN(LOGGER, "Unable to transform object from frame '%s' to planning frame'%s' (%s)",
1463  all_frame_name.c_str(), target.c_str(), ex.what());
1464  continue;
1465  }
1466  f.header.frame_id = all_frame_name;
1467  f.child_frame_id = target;
1468  transforms.push_back(f);
1469  }
1470 }
1471 
1473 {
1474  if (scene_)
1475  {
1476  std::vector<geometry_msgs::msg::TransformStamped> transforms;
1477  getUpdatedFrameTransforms(transforms);
1478  {
1479  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
1480  scene_->getTransformsNonConst().setTransforms(transforms);
1481  last_update_time_ = rclcpp::Clock().now();
1482  }
1484  }
1485 }
1486 
1488 {
1489  if (octomap_monitor_)
1490  octomap_monitor_->publishDebugInformation(flag);
1491 }
1492 
1493 void PlanningSceneMonitor::configureCollisionMatrix(const planning_scene::PlanningScenePtr& scene)
1494 {
1495  if (!scene || robot_description_.empty())
1496  return;
1497  // TODO: Uncomment when XmlRpc is refactored
1498  // collision_detection::AllowedCollisionMatrix& acm = scene->getAllowedCollisionMatrixNonConst();
1499 
1500  // read overriding values from the param server
1501 
1502  // first we do default collision operations
1503  if (!node_->has_parameter(robot_description_ + "_planning.default_collision_operations"))
1504  RCLCPP_DEBUG(LOGGER, "No additional default collision operations specified");
1505  else
1506  {
1507  RCLCPP_DEBUG(LOGGER, "Reading additional default collision operations");
1508 
1509  // TODO: codebase wide refactoring for XmlRpc
1510  // XmlRpc::XmlRpcValue coll_ops;
1511  // node_->get_parameter(robot_description_ + "_planning/default_collision_operations", coll_ops);
1512 
1513  // if (coll_ops.getType() != XmlRpc::XmlRpcValue::TypeArray)
1514  // {
1515  // RCLCPP_WARN(LOGGER, "default_collision_operations is not an array");
1516  // return;
1517  // }
1518 
1519  // if (coll_ops.size() == 0)
1520  // {
1521  // RCLCPP_WARN(LOGGER, "No collision operations in default collision operations");
1522  // return;
1523  // }
1524 
1525  // for (int i = 0; i < coll_ops.size(); ++i) // NOLINT(modernize-loop-convert)
1526  // {
1527  // if (!coll_ops[i].hasMember("object1") || !coll_ops[i].hasMember("object2") ||
1528  // !coll_ops[i].hasMember("operation"))
1529  // {
1530  // RCLCPP_WARN(LOGGER, "All collision operations must have two objects and an operation");
1531  // continue;
1532  // }
1533  // acm.setEntry(std::string(coll_ops[i]["object1"]), std::string(coll_ops[i]["object2"]),
1534  // std::string(coll_ops[i]["operation"]) == "disable");
1535  // }
1536  }
1537 }
1538 
1540 {
1541  if (robot_description_.empty())
1542  {
1543  default_robot_padd_ = 0.0;
1544  default_robot_scale_ = 1.0;
1545  default_object_padd_ = 0.0;
1546  default_attached_padd_ = 0.0;
1547  return;
1548  }
1549 
1550  // Ensure no leading slash creates a bad param server address
1551  static const std::string ROBOT_DESCRIPTION =
1552  (robot_description_[0] == '.') ? robot_description_.substr(1) : robot_description_;
1553 
1554  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_robot_padding", default_robot_padd_, 0.0);
1555  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_robot_scale", default_robot_scale_, 1.0);
1556  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_object_padding", default_object_padd_, 0.0);
1557  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_attached_padding", default_attached_padd_, 0.0);
1558  default_robot_link_padd_ = std::map<std::string, double>();
1559  default_robot_link_scale_ = std::map<std::string, double>();
1560  // TODO: enable parameter type support to std::map
1561  // node_->get_parameter_or(robot_description + "_planning/default_robot_link_padding", default_robot_link_padd_,
1562  // std::map<std::string, double>());
1563  // node_->get_parameter_or(robot_description + "_planning/default_robot_link_scale", default_robot_link_scale_,
1564  // std::map<std::string, double>());
1565 
1566  RCLCPP_DEBUG_STREAM(LOGGER, "Loaded " << default_robot_link_padd_.size() << " default link paddings");
1567  RCLCPP_DEBUG_STREAM(LOGGER, "Loaded " << default_robot_link_scale_.size() << " default link scales");
1568 }
1569 } // namespace planning_scene_monitor
void setupScene(const rclcpp::Node::SharedPtr &node, const planning_scene::PlanningScenePtr &scene)
Fetch plugin name from parameter server and activate the plugin for the given scene.
std::shared_lock< std::shared_mutex > ReadLock
Definition: occupancy_map.h:88
Represents an action that occurred on an object in the world. Several bits may be set indicating seve...
Definition: world.h:265
std::function< void(const ObjectConstPtr &, Action)> ObserverCallbackFn
Definition: world.h:301
This may be thrown during construction of objects if errors occur.
Definition: exceptions.h:46
Object defining bodies that can be attached to robot links.
Definition: attached_body.h:58
const std::string & getName() const
Get the name of the attached body.
Definition: attached_body.h:74
const std::vector< shapes::ShapeConstPtr > & getShapes() const
Get the shapes that make up this attached body.
A link from the robot. Contains the constant transform applied to the link and its geometry.
Definition: link_model.h:72
This class maintains the representation of the environment as seen by a planning instance....
static PlanningScenePtr clone(const PlanningSceneConstPtr &scene)
Clone a planning scene. Even if the scene scene depends on a parent, the cloned scene will not.
static const std::string OCTOMAP_NS
PlanningSceneMonitor Subscribes to the topic planning_scene.
const planning_scene::PlanningScenePtr & getPlanningScene()
Avoid this function! Returns an unsafe pointer to the current planning scene.
void lockSceneRead()
Lock the scene for reading (multiple threads can lock for reading at the same time)
void lockSceneWrite()
Lock the scene for writing (only one thread can lock for writing and no other thread can lock for rea...
void unlockSceneRead()
Unlock the scene from reading (multiple threads can lock for reading at the same time)
double default_attached_padd_
default attached padding
void currentWorldObjectUpdateCallback(const collision_detection::World::ObjectConstPtr &object, collision_detection::World::Action action)
Callback for a change in the world maintained by the planning scene.
std::unique_ptr< occupancy_map_monitor::OccupancyMapMonitor > octomap_monitor_
planning_scene::PlanningScenePtr copyPlanningScene(const moveit_msgs::msg::PlanningScene &diff=moveit_msgs::msg::PlanningScene())
Returns a copy of the current planning scene.
const moveit::core::RobotModelConstPtr & getRobotModel() const
void attachObjectCallback(const moveit_msgs::msg::AttachedCollisionObject::ConstSharedPtr &obj)
Callback for a new attached object msg.
std::map< std::string, double > default_robot_link_padd_
default robot link padding
void collisionObjectCallback(const moveit_msgs::msg::CollisionObject::ConstSharedPtr &obj)
Callback for a new collision object msg.
void configureDefaultPadding()
Configure the default padding.
PlanningSceneMonitor(const PlanningSceneMonitor &)=delete
PlanningSceneMonitor cannot be copy-constructed.
rclcpp::Subscription< moveit_msgs::msg::AttachedCollisionObject >::SharedPtr attached_collision_object_subscriber_
void stopPublishingPlanningScene()
Stop publishing the maintained planning scene.
void excludeAttachedBodyFromOctree(const moveit::core::AttachedBody *attached_body)
void addUpdateCallback(const std::function< void(SceneUpdateType)> &fn)
Add a function to be called when an update to the scene is received.
bool newPlanningSceneMessage(const moveit_msgs::msg::PlanningScene &scene)
void setPlanningScenePublishingFrequency(double hz)
Set the maximum frequency at which planning scenes are being published.
void includeWorldObjectInOctree(const collision_detection::World::ObjectConstPtr &obj)
rclcpp::Service< moveit_msgs::srv::GetPlanningScene >::SharedPtr get_scene_service_
void initialize(const planning_scene::PlanningScenePtr &scene)
Initialize the planning scene monitor.
void getMonitoredTopics(std::vector< std::string > &topics) const
Get the topic names that the monitor is listening to.
void triggerSceneUpdateEvent(SceneUpdateType update_type)
This function is called every time there is a change to the planning scene.
void newPlanningSceneWorldCallback(const moveit_msgs::msg::PlanningSceneWorld::ConstSharedPtr &world)
Callback for a new planning scene world.
planning_scene::PlanningSceneConstPtr scene_const_
rclcpp::Subscription< moveit_msgs::msg::CollisionObject >::SharedPtr collision_object_subscriber_
void includeAttachedBodyInOctree(const moveit::core::AttachedBody *attached_body)
void octomapUpdateCallback()
Callback for octomap updates.
rclcpp::Subscription< moveit_msgs::msg::PlanningScene >::SharedPtr planning_scene_subscriber_
void clearUpdateCallbacks()
Clear the functions to be called when an update to the scene is received.
void providePlanningSceneService(const std::string &service_name=DEFAULT_PLANNING_SCENE_SERVICE)
Create an optional service for getting the complete planning scene This is useful for satisfying the ...
std::shared_mutex scene_update_mutex_
if diffs are monitored, this is the pointer to the parent scene
void setStateUpdateFrequency(double hz)
Update the scene using the monitored state at a specified frequency, in Hz. This function has an effe...
std::shared_ptr< tf2_ros::TransformListener > tf_listener_
void stopWorldGeometryMonitor()
Stop the world geometry monitor.
void startStateMonitor(const std::string &joint_states_topic=DEFAULT_JOINT_STATES_TOPIC, const std::string &attached_objects_topic=DEFAULT_ATTACHED_COLLISION_OBJECT_TOPIC)
Start the current state monitor.
@ UPDATE_STATE
The state in the monitored scene was updated.
@ UPDATE_GEOMETRY
The geometry of the scene was updated. This includes receiving new octomaps, collision objects,...
@ UPDATE_TRANSFORMS
The maintained set of fixed transforms in the monitored scene was updated.
std::shared_ptr< rclcpp::executors::SingleThreadedExecutor > private_executor_
bool getShapeTransformCache(const std::string &target_frame, const rclcpp::Time &target_time, occupancy_map_monitor::ShapeTransformCache &cache) const
void startSceneMonitor(const std::string &scene_topic=DEFAULT_PLANNING_SCENE_TOPIC)
Start the scene monitor (ROS topic-based)
void startPublishingPlanningScene(SceneUpdateType event, const std::string &planning_scene_topic=MONITORED_PLANNING_SCENE_TOPIC)
Start publishing the maintained planning scene. The first message set out is a complete planning scen...
bool waitForCurrentRobotState(const rclcpp::Time &t, double wait_time=1.)
Wait for robot state to become more recent than time t.
std::map< std::string, double > default_robot_link_scale_
default robot link scale
rclcpp::Time last_robot_motion_time_
Last time the state was updated.
void updateSceneWithCurrentState(bool skip_update_if_locked=false)
Update the scene using the monitored state. This function is automatically called when an update to t...
std::string monitor_name_
The name of this scene monitor.
bool updatesScene(const planning_scene::PlanningSceneConstPtr &scene) const
Return true if the scene scene can be updated directly or indirectly by this monitor....
std::recursive_mutex update_lock_
lock access to update_callbacks_
rclcpp::Time last_update_time_
mutex for stored scene
bool requestPlanningSceneState(const std::string &service_name=DEFAULT_PLANNING_SCENE_SERVICE)
Request a full planning scene state using a service call Be careful not to use this in conjunction wi...
void monitorDiffs(bool flag)
By default, the maintained planning scene does not reason about diffs. When the flag passed in is tru...
void currentStateAttachedBodyUpdateCallback(moveit::core::AttachedBody *attached_body, bool just_attached)
Callback for a change for an attached object of the current state of the planning scene.
std::unique_ptr< std::thread > publish_planning_scene_
std::shared_ptr< rclcpp::Node > node_
Last time the robot has moved.
void configureCollisionMatrix(const planning_scene::PlanningScenePtr &scene)
Configure the collision matrix for a particular scene.
void startWorldGeometryMonitor(const std::string &collision_objects_topic=DEFAULT_COLLISION_OBJECT_TOPIC, const std::string &planning_scene_world_topic=DEFAULT_PLANNING_SCENE_WORLD_TOPIC, const bool load_octomap_monitor=true)
Start the OccupancyMapMonitor and listening for:
rclcpp::Publisher< moveit_msgs::msg::PlanningScene >::SharedPtr planning_scene_publisher_
void excludeWorldObjectFromOctree(const collision_detection::World::ObjectConstPtr &obj)
std::vector< std::function< void(SceneUpdateType)> > update_callbacks_
void updateFrameTransforms()
Update the transforms for the frames that are not part of the kinematic model using tf....
planning_scene::PlanningScenePtr parent_scene_
std::shared_ptr< tf2_ros::Buffer > tf_buffer_
void unlockSceneWrite()
Lock the scene from writing (only one thread can lock for writing and no other thread can lock for re...
rclcpp::Subscription< moveit_msgs::msg::PlanningSceneWorld >::SharedPtr planning_scene_world_subscriber_
rclcpp::Parameter declare_parameter(const rclcpp::Node::SharedPtr &node, const std::string &parameter_name)
bool isEmpty(const moveit_msgs::msg::PlanningScene &msg)
Check if a message includes any information about a planning scene, or whether it is empty.
std::function< void(AttachedBody *body, bool attached)> AttachedBodyCallback
Definition: attached_body.h:51
std::map< ShapeHandle, Eigen::Isometry3d, std::less< ShapeHandle >, Eigen::aligned_allocator< std::pair< const ShapeHandle, Eigen::Isometry3d > > > ShapeTransformCache
scene
Definition: pick.py:52
This namespace includes the central class for representing planning contexts.
name
Definition: setup.py:7
description
Definition: setup.py:19
Definition: world.h:49
const rclcpp::Logger LOGGER
Definition: async_test.h:31