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