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