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 
670  if (!scene.is_diff && parent_scene_)
671  {
672  // clear maintained (diff) scene_ and set the full new scene in parent_scene_ instead
673  scene_->clearDiffs();
674  result = parent_scene_->setPlanningSceneMsg(scene);
675  // There were no callbacks for individual object changes, so rebuild the octree masks
678  }
679  else
680  {
681  result = scene_->setPlanningSceneDiffMsg(scene);
682  }
683 
684  if (octomap_monitor_)
685  {
686  if (!scene.is_diff && scene.world.octomap.octomap.data.empty())
687  {
688  octomap_monitor_->getOcTreePtr()->lockWrite();
689  octomap_monitor_->getOcTreePtr()->clear();
690  octomap_monitor_->getOcTreePtr()->unlockWrite();
691  }
692  }
693  robot_model_ = scene_->getRobotModel();
694 
695  if (octomap_monitor_)
696  {
697  excludeAttachedBodiesFromOctree(); // in case updates have happened to the attached bodies, put them in
698  excludeWorldObjectsFromOctree(); // in case updates have happened to the attached bodies, put them in
699  }
700  }
701 
702  // if we have a diff, try to more accurately determine the update type
703  if (scene.is_diff)
704  {
705  bool no_other_scene_upd = (scene.name.empty() || scene.name == old_scene_name) &&
706  scene.allowed_collision_matrix.entry_names.empty() && scene.link_padding.empty() &&
707  scene.link_scale.empty();
708  if (no_other_scene_upd)
709  {
710  upd = UPDATE_NONE;
711  if (!moveit::core::isEmpty(scene.world))
712  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_GEOMETRY));
713 
714  if (!scene.fixed_frame_transforms.empty())
715  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_TRANSFORMS));
716 
717  if (!moveit::core::isEmpty(scene.robot_state))
718  {
719  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_STATE));
720  if (!scene.robot_state.attached_collision_objects.empty() || !scene.robot_state.is_diff)
721  upd = (SceneUpdateType)(static_cast<int>(upd) | static_cast<int>(UPDATE_GEOMETRY));
722  }
723  }
724  }
726  return result;
727 }
728 
730  const moveit_msgs::msg::PlanningSceneWorld::ConstSharedPtr& world)
731 {
732  if (scene_)
733  {
735  {
736  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
737  last_update_time_ = rclcpp::Clock().now();
738  scene_->getWorldNonConst()->clearObjects();
739  scene_->processPlanningSceneWorldMsg(*world);
740  if (octomap_monitor_)
741  {
742  if (world->octomap.octomap.data.empty())
743  {
744  octomap_monitor_->getOcTreePtr()->lockWrite();
745  octomap_monitor_->getOcTreePtr()->clear();
746  octomap_monitor_->getOcTreePtr()->unlockWrite();
747  }
748  }
749  }
751  }
752 }
753 
754 void PlanningSceneMonitor::collisionObjectCallback(const moveit_msgs::msg::CollisionObject::ConstSharedPtr& obj)
755 {
756  if (!scene_)
757  return;
758 
760  {
761  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
762  last_update_time_ = rclcpp::Clock().now();
763  if (!scene_->processCollisionObjectMsg(*obj))
764  return;
765  }
767 }
768 
769 void PlanningSceneMonitor::attachObjectCallback(const moveit_msgs::msg::AttachedCollisionObject::ConstSharedPtr& obj)
770 {
771  if (scene_)
772  {
774  {
775  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
776  last_update_time_ = rclcpp::Clock().now();
777  scene_->processAttachedCollisionObjectMsg(*obj);
778  }
780  }
781 }
782 
784 {
785  if (!octomap_monitor_)
786  return;
787 
788  std::scoped_lock _(shape_handles_lock_);
789 
791  const std::vector<const moveit::core::LinkModel*>& links = getRobotModel()->getLinkModelsWithCollisionGeometry();
792  auto start = std::chrono::system_clock::now();
793  bool warned = false;
794  for (const moveit::core::LinkModel* link : links)
795  {
796  std::vector<shapes::ShapeConstPtr> shapes = link->getShapes(); // copy shared ptrs on purpuse
797  for (std::size_t j = 0; j < shapes.size(); ++j)
798  {
799  // merge mesh vertices up to 0.1 mm apart
800  if (shapes[j]->type == shapes::MESH)
801  {
802  shapes::Mesh* m = static_cast<shapes::Mesh*>(shapes[j]->clone());
803  m->mergeVertices(1e-4);
804  shapes[j].reset(m);
805  }
806 
808  if (h)
809  link_shape_handles_[link].push_back(std::make_pair(h, j));
810  }
811 
812  if (!warned && ((std::chrono::system_clock::now() - start) > std::chrono::seconds(30)))
813  {
814  RCLCPP_WARN(LOGGER, "It is likely there are too many vertices in collision geometry");
815  warned = true;
816  }
817  }
818 }
819 
821 {
822  if (!octomap_monitor_)
823  return;
824 
825  std::scoped_lock _(shape_handles_lock_);
826 
827  for (std::pair<const moveit::core::LinkModel* const,
828  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>& link_shape_handle :
830  for (std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>& it : link_shape_handle.second)
831  octomap_monitor_->forgetShape(it.first);
832  link_shape_handles_.clear();
833 }
834 
836 {
837  if (!octomap_monitor_)
838  return;
839 
840  std::scoped_lock _(shape_handles_lock_);
841 
842  // clear information about any attached body, without referring to the AttachedBody* ptr (could be invalid)
843  for (std::pair<const moveit::core::AttachedBody* const,
844  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>& attached_body_shape_handle :
846  for (std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>& it : attached_body_shape_handle.second)
847  octomap_monitor_->forgetShape(it.first);
849 }
850 
852 {
853  std::scoped_lock _(shape_handles_lock_);
854 
856  // add attached objects again
857  std::vector<const moveit::core::AttachedBody*> ab;
858  scene_->getCurrentState().getAttachedBodies(ab);
859  for (const moveit::core::AttachedBody* body : ab)
861 }
862 
864 {
865  if (!octomap_monitor_)
866  return;
867 
868  std::scoped_lock _(shape_handles_lock_);
869 
870  // clear information about any attached object
871  for (std::pair<const std::string, std::vector<std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>>>&
872  collision_body_shape_handle : collision_body_shape_handles_)
873  for (std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>& it :
874  collision_body_shape_handle.second)
875  octomap_monitor_->forgetShape(it.first);
877 }
878 
880 {
881  std::scoped_lock _(shape_handles_lock_);
882 
884  for (const std::pair<const std::string, collision_detection::World::ObjectPtr>& it : *scene_->getWorld())
885  excludeWorldObjectFromOctree(it.second);
886 }
887 
889 {
890  if (!octomap_monitor_)
891  return;
892 
893  std::scoped_lock _(shape_handles_lock_);
894  bool found = false;
895  for (std::size_t i = 0; i < attached_body->getShapes().size(); ++i)
896  {
897  if (attached_body->getShapes()[i]->type == shapes::PLANE || attached_body->getShapes()[i]->type == shapes::OCTREE)
898  continue;
899  occupancy_map_monitor::ShapeHandle h = octomap_monitor_->excludeShape(attached_body->getShapes()[i]);
900  if (h)
901  {
902  found = true;
903  attached_body_shape_handles_[attached_body].push_back(std::make_pair(h, i));
904  }
905  }
906  if (found)
907  RCLCPP_DEBUG(LOGGER, "Excluding attached body '%s' from monitored octomap", attached_body->getName().c_str());
908 }
909 
911 {
912  if (!octomap_monitor_)
913  return;
914 
915  std::scoped_lock _(shape_handles_lock_);
916 
917  AttachedBodyShapeHandles::iterator it = attached_body_shape_handles_.find(attached_body);
918  if (it != attached_body_shape_handles_.end())
919  {
920  for (std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>& shape_handle : it->second)
921  octomap_monitor_->forgetShape(shape_handle.first);
922  RCLCPP_DEBUG(LOGGER, "Including attached body '%s' in monitored octomap", attached_body->getName().c_str());
924  }
925 }
926 
927 void PlanningSceneMonitor::excludeWorldObjectFromOctree(const collision_detection::World::ObjectConstPtr& obj)
928 {
929  if (!octomap_monitor_)
930  return;
931 
932  std::scoped_lock _(shape_handles_lock_);
933 
934  bool found = false;
935  for (std::size_t i = 0; i < obj->shapes_.size(); ++i)
936  {
937  if (obj->shapes_[i]->type == shapes::PLANE || obj->shapes_[i]->type == shapes::OCTREE)
938  continue;
939  occupancy_map_monitor::ShapeHandle h = octomap_monitor_->excludeShape(obj->shapes_[i]);
940  if (h)
941  {
942  collision_body_shape_handles_[obj->id_].push_back(std::make_pair(h, &obj->global_shape_poses_[i]));
943  found = true;
944  }
945  }
946  if (found)
947  RCLCPP_DEBUG(LOGGER, "Excluding collision object '%s' from monitored octomap", obj->id_.c_str());
948 }
949 
950 void PlanningSceneMonitor::includeWorldObjectInOctree(const collision_detection::World::ObjectConstPtr& obj)
951 {
952  if (!octomap_monitor_)
953  return;
954 
955  std::scoped_lock _(shape_handles_lock_);
956 
957  CollisionBodyShapeHandles::iterator it = collision_body_shape_handles_.find(obj->id_);
958  if (it != collision_body_shape_handles_.end())
959  {
960  for (std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>& shape_handle : it->second)
961  octomap_monitor_->forgetShape(shape_handle.first);
962  RCLCPP_DEBUG(LOGGER, "Including collision object '%s' in monitored octomap", obj->id_.c_str());
964  }
965 }
966 
968  bool just_attached)
969 {
970  if (!octomap_monitor_)
971  return;
972 
973  if (just_attached)
974  excludeAttachedBodyFromOctree(attached_body);
975  else
976  includeAttachedBodyInOctree(attached_body);
977 }
978 
979 void PlanningSceneMonitor::currentWorldObjectUpdateCallback(const collision_detection::World::ObjectConstPtr& obj,
981 {
982  if (!octomap_monitor_)
983  return;
985  return;
986 
991  else
992  {
995  }
996 }
997 
998 bool PlanningSceneMonitor::waitForCurrentRobotState(const rclcpp::Time& t, double wait_time)
999 {
1000  if (t.nanoseconds() == 0)
1001  return false;
1002  RCLCPP_DEBUG(LOGGER, "sync robot state to: %.3fs", fmod(t.seconds(), 10.));
1003 
1005  {
1006  // Wait for next robot update in state monitor.
1007  bool success = current_state_monitor_->waitForCurrentState(t, wait_time);
1008 
1009  /* As robot updates are passed to the planning scene only in throttled fashion, there might
1010  be still an update pending. If so, explicitly update the planning scene here.
1011  If waitForCurrentState failed, we didn't get any new state updates within wait_time. */
1012  if (success)
1013  {
1014  std::unique_lock<std::mutex> lock(state_pending_mutex_);
1015  if (state_update_pending_) // enforce state update
1016  {
1017  state_update_pending_ = false;
1018  last_robot_state_update_wall_time_ = std::chrono::system_clock::now();
1019  lock.unlock();
1021  }
1022  return true;
1023  }
1024 
1025  RCLCPP_WARN(LOGGER, "Failed to fetch current robot state.");
1026  return false;
1027  }
1028 
1029  // Sometimes there is no state monitor. In this case state updates are received as part of scene updates only.
1030  // However, scene updates are only published if the robot actually moves. Hence we need a timeout!
1031  // As publishing planning scene updates is throttled (2Hz by default), a 1s timeout is a suitable default.
1032  auto start = node_->get_clock()->now();
1033  auto timeout = rclcpp::Duration::from_seconds(wait_time);
1034  std::shared_lock<std::shared_mutex> lock(scene_update_mutex_);
1035  rclcpp::Time prev_robot_motion_time = last_robot_motion_time_;
1036  while (last_robot_motion_time_ < t && // Wait until the state update actually reaches the scene.
1037  timeout > rclcpp::Duration(0, 0))
1038  {
1039  RCLCPP_DEBUG(LOGGER, "last robot motion: %f ago", (t - last_robot_motion_time_).seconds());
1040  new_scene_update_condition_.wait_for(lock, std::chrono::nanoseconds(timeout.nanoseconds()));
1041  timeout = timeout - (node_->get_clock()->now() - start); // compute remaining wait_time
1042  }
1043  bool success = last_robot_motion_time_ >= t;
1044  // suppress warning if we received an update at all
1045  if (!success && prev_robot_motion_time != last_robot_motion_time_)
1046  RCLCPP_WARN(LOGGER, "Maybe failed to update robot state, time diff: %.3fs", (t - last_robot_motion_time_).seconds());
1047 
1048  RCLCPP_DEBUG(LOGGER, "sync done: robot motion: %f scene update: %f", (t - last_robot_motion_time_).seconds(),
1049  (t - last_update_time_).seconds());
1050  return success;
1051 }
1052 
1054 {
1055  scene_update_mutex_.lock_shared();
1056  if (octomap_monitor_)
1057  octomap_monitor_->getOcTreePtr()->lockRead();
1058 }
1059 
1061 {
1062  if (octomap_monitor_)
1063  octomap_monitor_->getOcTreePtr()->unlockRead();
1064  scene_update_mutex_.unlock_shared();
1065 }
1066 
1068 {
1069  scene_update_mutex_.lock();
1070  if (octomap_monitor_)
1071  octomap_monitor_->getOcTreePtr()->lockWrite();
1072 }
1073 
1075 {
1076  if (octomap_monitor_)
1077  octomap_monitor_->getOcTreePtr()->unlockWrite();
1078  scene_update_mutex_.unlock();
1079 }
1080 
1081 void PlanningSceneMonitor::startSceneMonitor(const std::string& scene_topic)
1082 {
1083  stopSceneMonitor();
1084 
1085  RCLCPP_INFO(LOGGER, "Starting planning scene monitor");
1086  // listen for planning scene updates; these messages include transforms, so no need for filters
1087  if (!scene_topic.empty())
1088  {
1089  planning_scene_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::PlanningScene>(
1090  scene_topic, rclcpp::SystemDefaultsQoS(), [this](const moveit_msgs::msg::PlanningScene::ConstSharedPtr& scene) {
1091  return newPlanningSceneCallback(scene);
1092  });
1093  RCLCPP_INFO(LOGGER, "Listening to '%s'", planning_scene_subscriber_->get_topic_name());
1094  }
1095 }
1096 
1098 {
1100  {
1101  RCLCPP_INFO(LOGGER, "Stopping planning scene monitor");
1103  }
1104 }
1105 
1106 bool PlanningSceneMonitor::getShapeTransformCache(const std::string& target_frame, const rclcpp::Time& target_time,
1108 {
1109  try
1110  {
1111  std::scoped_lock _(shape_handles_lock_);
1112 
1113  for (const std::pair<const moveit::core::LinkModel* const,
1114  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>& link_shape_handle :
1116  {
1117  if (tf_buffer_->canTransform(target_frame, link_shape_handle.first->getName(), target_time,
1118  shape_transform_cache_lookup_wait_time_))
1119  {
1120  Eigen::Isometry3d ttr = tf2::transformToEigen(
1121  tf_buffer_->lookupTransform(target_frame, link_shape_handle.first->getName(), target_time));
1122  for (std::size_t j = 0; j < link_shape_handle.second.size(); ++j)
1123  cache[link_shape_handle.second[j].first] =
1124  ttr * link_shape_handle.first->getCollisionOriginTransforms()[link_shape_handle.second[j].second];
1125  }
1126  }
1127  for (const std::pair<const moveit::core::AttachedBody* const,
1128  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, std::size_t>>>&
1129  attached_body_shape_handle : attached_body_shape_handles_)
1130  {
1131  if (tf_buffer_->canTransform(target_frame, attached_body_shape_handle.first->getAttachedLinkName(), target_time,
1132  shape_transform_cache_lookup_wait_time_))
1133  {
1134  Eigen::Isometry3d transform = tf2::transformToEigen(tf_buffer_->lookupTransform(
1135  target_frame, attached_body_shape_handle.first->getAttachedLinkName(), target_time));
1136  for (std::size_t k = 0; k < attached_body_shape_handle.second.size(); ++k)
1137  cache[attached_body_shape_handle.second[k].first] =
1138  transform *
1139  attached_body_shape_handle.first->getShapePosesInLinkFrame()[attached_body_shape_handle.second[k].second];
1140  }
1141  }
1142  {
1143  if (tf_buffer_->canTransform(target_frame, scene_->getPlanningFrame(), target_time,
1144  shape_transform_cache_lookup_wait_time_))
1145  {
1146  Eigen::Isometry3d transform =
1147  tf2::transformToEigen(tf_buffer_->lookupTransform(target_frame, scene_->getPlanningFrame(), target_time));
1148  for (const std::pair<const std::string,
1149  std::vector<std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>>>&
1150  collision_body_shape_handle : collision_body_shape_handles_)
1151  for (const std::pair<occupancy_map_monitor::ShapeHandle, const Eigen::Isometry3d*>& it :
1152  collision_body_shape_handle.second)
1153  cache[it.first] = transform * (*it.second);
1154  }
1155  }
1156  }
1157  catch (tf2::TransformException& ex)
1158  {
1159  rclcpp::Clock steady_clock = rclcpp::Clock();
1160  RCLCPP_ERROR_THROTTLE(LOGGER, steady_clock, 1000, "Transform error: %s", ex.what());
1161  return false;
1162  }
1163  return true;
1164 }
1165 
1166 void PlanningSceneMonitor::startWorldGeometryMonitor(const std::string& collision_objects_topic,
1167  const std::string& planning_scene_world_topic,
1168  const bool load_octomap_monitor)
1169 {
1171  RCLCPP_INFO(LOGGER, "Starting world geometry update monitor for collision objects, attached objects, octomap "
1172  "updates.");
1173 
1174  // Listen to the /collision_objects topic to detect requests to add/remove/update collision objects to/from the world
1175  if (!collision_objects_topic.empty())
1176  {
1177  collision_object_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::CollisionObject>(
1178  collision_objects_topic, rclcpp::SystemDefaultsQoS(),
1179  [this](const moveit_msgs::msg::CollisionObject::ConstSharedPtr& obj) { return collisionObjectCallback(obj); });
1180  RCLCPP_INFO(LOGGER, "Listening to '%s'", collision_objects_topic.c_str());
1181  }
1182 
1183  if (!planning_scene_world_topic.empty())
1184  {
1185  planning_scene_world_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::PlanningSceneWorld>(
1186  planning_scene_world_topic, rclcpp::SystemDefaultsQoS(),
1187  [this](const moveit_msgs::msg::PlanningSceneWorld::ConstSharedPtr& world) {
1188  return newPlanningSceneWorldCallback(world);
1189  });
1190  RCLCPP_INFO(LOGGER, "Listening to '%s' for planning scene world geometry", planning_scene_world_topic.c_str());
1191  }
1192 
1193  // Ocotomap monitor is optional
1194  if (load_octomap_monitor)
1195  {
1196  if (!octomap_monitor_)
1197  {
1199  std::make_unique<occupancy_map_monitor::OccupancyMapMonitor>(node_, tf_buffer_, scene_->getPlanningFrame());
1203 
1204  octomap_monitor_->setTransformCacheCallback([this](const std::string& frame, const rclcpp::Time& stamp,
1206  return getShapeTransformCache(frame, stamp, cache);
1207  });
1208  octomap_monitor_->setUpdateCallback([this] { octomapUpdateCallback(); });
1209  }
1210  octomap_monitor_->startMonitor();
1211  }
1212 }
1213 
1215 {
1217  {
1218  RCLCPP_INFO(LOGGER, "Stopping world geometry monitor");
1220  }
1222  {
1223  RCLCPP_INFO(LOGGER, "Stopping world geometry monitor");
1225  }
1226  if (octomap_monitor_)
1227  octomap_monitor_->stopMonitor();
1228 }
1229 
1230 void PlanningSceneMonitor::startStateMonitor(const std::string& joint_states_topic,
1231  const std::string& attached_objects_topic)
1232 {
1233  stopStateMonitor();
1234  if (scene_)
1235  {
1237  {
1239  std::make_shared<CurrentStateMonitor>(pnode_, getRobotModel(), tf_buffer_, use_sim_time_);
1240  }
1241  current_state_monitor_->addUpdateCallback(
1242  [this](const sensor_msgs::msg::JointState::ConstSharedPtr& joint_state) { return onStateUpdate(joint_state); });
1243  current_state_monitor_->startStateMonitor(joint_states_topic);
1244 
1245  {
1246  std::unique_lock<std::mutex> lock(state_pending_mutex_);
1247  if (dt_state_update_.count() > 0)
1248  // ROS original: state_update_timer_.start();
1249  // TODO: re-enable WallTimer start()
1250  state_update_timer_ =
1251  pnode_->create_wall_timer(dt_state_update_, [this]() { return stateUpdateTimerCallback(); });
1252  }
1253 
1254  if (!attached_objects_topic.empty())
1255  {
1256  // using regular message filter as there's no header
1257  attached_collision_object_subscriber_ = pnode_->create_subscription<moveit_msgs::msg::AttachedCollisionObject>(
1258  attached_objects_topic, rclcpp::SystemDefaultsQoS(),
1259  [this](const moveit_msgs::msg::AttachedCollisionObject::ConstSharedPtr& obj) {
1260  return attachObjectCallback(obj);
1261  });
1262  RCLCPP_INFO(LOGGER, "Listening to '%s' for attached collision objects",
1263  attached_collision_object_subscriber_->get_topic_name());
1264  }
1265  }
1266  else
1267  RCLCPP_ERROR(LOGGER, "Cannot monitor robot state because planning scene is not configured");
1268 }
1269 
1271 {
1273  current_state_monitor_->stopStateMonitor();
1276 
1277  // stop must be called with state_pending_mutex_ unlocked to avoid deadlock
1278  if (state_update_timer_)
1279  state_update_timer_->cancel();
1280  {
1281  std::unique_lock<std::mutex> lock(state_pending_mutex_);
1282  state_update_pending_ = false;
1283  }
1284 }
1285 
1286 void PlanningSceneMonitor::onStateUpdate(const sensor_msgs::msg::JointState::ConstSharedPtr& /*joint_state */)
1287 {
1288  const std::chrono::system_clock::time_point& n = std::chrono::system_clock::now();
1289  std::chrono::duration<double> dt = n - last_robot_state_update_wall_time_;
1290 
1291  bool update = false;
1292  {
1293  std::unique_lock<std::mutex> lock(state_pending_mutex_);
1294 
1295  if (dt.count() < dt_state_update_.count())
1296  {
1297  state_update_pending_ = true;
1298  }
1299  else
1300  {
1301  state_update_pending_ = false;
1302  last_robot_state_update_wall_time_ = n;
1303  update = true;
1304  }
1305  }
1306  // run the state update with state_pending_mutex_ unlocked
1307  if (update)
1309 }
1310 
1311 void PlanningSceneMonitor::stateUpdateTimerCallback()
1312 {
1313  if (state_update_pending_)
1314  {
1315  bool update = false;
1316 
1317  std::chrono::system_clock::time_point n = std::chrono::system_clock::now();
1318  std::chrono::duration<double> dt = n - last_robot_state_update_wall_time_;
1319 
1320  {
1321  // lock for access to dt_state_update_ and state_update_pending_
1322  std::unique_lock<std::mutex> lock(state_pending_mutex_);
1323  if (state_update_pending_ && dt.count() >= dt_state_update_.count())
1324  {
1325  state_update_pending_ = false;
1326  last_robot_state_update_wall_time_ = std::chrono::system_clock::now();
1327  auto sec = std::chrono::duration<double>(last_robot_state_update_wall_time_.time_since_epoch()).count();
1328  update = true;
1329  RCLCPP_DEBUG(LOGGER, "performPendingStateUpdate: %f", fmod(sec, 10));
1330  }
1331  }
1332 
1333  // run the state update with state_pending_mutex_ unlocked
1334  if (update)
1335  {
1337  RCLCPP_DEBUG(LOGGER, "performPendingStateUpdate done");
1338  }
1339  }
1340 }
1341 
1343 {
1344  if (!octomap_monitor_)
1345  return;
1346 
1348  {
1349  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
1350  last_update_time_ = rclcpp::Clock().now();
1351  octomap_monitor_->getOcTreePtr()->lockRead();
1352  try
1353  {
1354  scene_->processOctomapPtr(octomap_monitor_->getOcTreePtr(), Eigen::Isometry3d::Identity());
1355  octomap_monitor_->getOcTreePtr()->unlockRead();
1356  }
1357  catch (...)
1358  {
1359  octomap_monitor_->getOcTreePtr()->unlockRead(); // unlock and rethrow
1360  throw;
1361  }
1362  }
1364 }
1365 
1367 {
1368  bool update = false;
1369  if (hz > std::numeric_limits<double>::epsilon())
1370  {
1371  std::unique_lock<std::mutex> lock(state_pending_mutex_);
1372  dt_state_update_ = std::chrono::duration<double>(1.0 / hz);
1373  // ROS original: state_update_timer_.start();
1374  // TODO: re-enable WallTimer start()
1375  state_update_timer_ = pnode_->create_wall_timer(dt_state_update_, [this]() { return stateUpdateTimerCallback(); });
1376  }
1377  else
1378  {
1379  // stop must be called with state_pending_mutex_ unlocked to avoid deadlock
1380  // ROS original: state_update_timer_.stop();
1381  // TODO: re-enable WallTimer stop()
1382  if (state_update_timer_)
1383  state_update_timer_->cancel();
1384  std::unique_lock<std::mutex> lock(state_pending_mutex_);
1385  dt_state_update_ = std::chrono::duration<double>(0.0);
1386  if (state_update_pending_)
1387  update = true;
1388  }
1389  RCLCPP_INFO(LOGGER, "Updating internal planning scene state at most every %lf seconds", dt_state_update_.count());
1390 
1391  if (update)
1393 }
1394 
1396 {
1397  rclcpp::Time time = node_->now();
1398  rclcpp::Clock steady_clock = rclcpp::Clock(RCL_STEADY_TIME);
1400  {
1401  std::vector<std::string> missing;
1402  if (!current_state_monitor_->haveCompleteState(missing) &&
1403  (time - current_state_monitor_->getMonitorStartTime()).seconds() > 1.0)
1404  {
1405  std::string missing_str = boost::algorithm::join(missing, ", ");
1406  RCLCPP_WARN_THROTTLE(LOGGER, steady_clock, 1000, "The complete state of the robot is not yet known. Missing %s",
1407  missing_str.c_str());
1408  }
1409 
1410  {
1411  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
1413  RCLCPP_DEBUG(LOGGER, "robot state update %f", fmod(last_robot_motion_time_.seconds(), 10.));
1414  current_state_monitor_->setToCurrentState(scene_->getCurrentStateNonConst());
1415  scene_->getCurrentStateNonConst().update(); // compute all transforms
1416  }
1418  }
1419  else
1420  RCLCPP_ERROR_THROTTLE(LOGGER, steady_clock, 1000,
1421  "State monitor is not active. Unable to set the planning scene state");
1422 }
1423 
1424 void PlanningSceneMonitor::addUpdateCallback(const std::function<void(SceneUpdateType)>& fn)
1425 {
1426  std::scoped_lock lock(update_lock_);
1427  if (fn)
1428  update_callbacks_.push_back(fn);
1429 }
1430 
1432 {
1433  std::scoped_lock lock(update_lock_);
1434  update_callbacks_.clear();
1435 }
1436 
1438 {
1440  RCLCPP_DEBUG(LOGGER, "Maximum frequency for publishing a planning scene is now %lf Hz",
1442 }
1443 
1444 void PlanningSceneMonitor::getUpdatedFrameTransforms(std::vector<geometry_msgs::msg::TransformStamped>& transforms)
1445 {
1446  const std::string& target = getRobotModel()->getModelFrame();
1447 
1448  std::vector<std::string> all_frame_names;
1449  tf_buffer_->_getFrameStrings(all_frame_names);
1450  for (const std::string& all_frame_name : all_frame_names)
1451  {
1452  if (all_frame_name == target || getRobotModel()->hasLinkModel(all_frame_name))
1453  continue;
1454 
1455  geometry_msgs::msg::TransformStamped f;
1456  try
1457  {
1458  f = tf_buffer_->lookupTransform(target, all_frame_name, tf2::TimePointZero);
1459  }
1460  catch (tf2::TransformException& ex)
1461  {
1462  RCLCPP_WARN(LOGGER, "Unable to transform object from frame '%s' to planning frame'%s' (%s)",
1463  all_frame_name.c_str(), target.c_str(), ex.what());
1464  continue;
1465  }
1466  f.header.frame_id = all_frame_name;
1467  f.child_frame_id = target;
1468  transforms.push_back(f);
1469  }
1470 }
1471 
1473 {
1474  if (scene_)
1475  {
1476  std::vector<geometry_msgs::msg::TransformStamped> transforms;
1477  getUpdatedFrameTransforms(transforms);
1478  {
1479  std::unique_lock<std::shared_mutex> ulock(scene_update_mutex_);
1480  scene_->getTransformsNonConst().setTransforms(transforms);
1481  last_update_time_ = rclcpp::Clock().now();
1482  }
1484  }
1485 }
1486 
1488 {
1489  if (octomap_monitor_)
1490  octomap_monitor_->publishDebugInformation(flag);
1491 }
1492 
1493 void PlanningSceneMonitor::configureCollisionMatrix(const planning_scene::PlanningScenePtr& scene)
1494 {
1495  if (!scene || robot_description_.empty())
1496  return;
1497  // TODO: Uncomment when XmlRpc is refactored
1498  // collision_detection::AllowedCollisionMatrix& acm = scene->getAllowedCollisionMatrixNonConst();
1499 
1500  // read overriding values from the param server
1501 
1502  // first we do default collision operations
1503  if (!node_->has_parameter(robot_description_ + "_planning.default_collision_operations"))
1504  RCLCPP_DEBUG(LOGGER, "No additional default collision operations specified");
1505  else
1506  {
1507  RCLCPP_DEBUG(LOGGER, "Reading additional default collision operations");
1508 
1509  // TODO: codebase wide refactoring for XmlRpc
1510  // XmlRpc::XmlRpcValue coll_ops;
1511  // node_->get_parameter(robot_description_ + "_planning/default_collision_operations", coll_ops);
1512 
1513  // if (coll_ops.getType() != XmlRpc::XmlRpcValue::TypeArray)
1514  // {
1515  // RCLCPP_WARN(LOGGER, "default_collision_operations is not an array");
1516  // return;
1517  // }
1518 
1519  // if (coll_ops.size() == 0)
1520  // {
1521  // RCLCPP_WARN(LOGGER, "No collision operations in default collision operations");
1522  // return;
1523  // }
1524 
1525  // for (int i = 0; i < coll_ops.size(); ++i) // NOLINT(modernize-loop-convert)
1526  // {
1527  // if (!coll_ops[i].hasMember("object1") || !coll_ops[i].hasMember("object2") ||
1528  // !coll_ops[i].hasMember("operation"))
1529  // {
1530  // RCLCPP_WARN(LOGGER, "All collision operations must have two objects and an operation");
1531  // continue;
1532  // }
1533  // acm.setEntry(std::string(coll_ops[i]["object1"]), std::string(coll_ops[i]["object2"]),
1534  // std::string(coll_ops[i]["operation"]) == "disable");
1535  // }
1536  }
1537 }
1538 
1540 {
1541  if (robot_description_.empty())
1542  {
1543  default_robot_padd_ = 0.0;
1544  default_robot_scale_ = 1.0;
1545  default_object_padd_ = 0.0;
1546  default_attached_padd_ = 0.0;
1547  return;
1548  }
1549 
1550  // Ensure no leading slash creates a bad param server address
1551  static const std::string ROBOT_DESCRIPTION =
1552  (robot_description_[0] == '.') ? robot_description_.substr(1) : robot_description_;
1553 
1554  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_robot_padding", default_robot_padd_, 0.0);
1555  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_robot_scale", default_robot_scale_, 1.0);
1556  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_object_padding", default_object_padd_, 0.0);
1557  node_->get_parameter_or(ROBOT_DESCRIPTION + "_planning.default_attached_padding", default_attached_padd_, 0.0);
1558  default_robot_link_padd_ = std::map<std::string, double>();
1559  default_robot_link_scale_ = std::map<std::string, double>();
1560  // TODO: enable parameter type support to std::map
1561  // node_->get_parameter_or(robot_description + "_planning/default_robot_link_padding", default_robot_link_padd_,
1562  // std::map<std::string, double>());
1563  // node_->get_parameter_or(robot_description + "_planning/default_robot_link_scale", default_robot_link_scale_,
1564  // std::map<std::string, double>());
1565 
1566  RCLCPP_DEBUG_STREAM(LOGGER, "Loaded " << default_robot_link_padd_.size() << " default link paddings");
1567  RCLCPP_DEBUG_STREAM(LOGGER, "Loaded " << default_robot_link_scale_.size() << " default link scales");
1568 }
1569 } // namespace planning_scene_monitor
void setupScene(const rclcpp::Node::SharedPtr &node, const planning_scene::PlanningScenePtr &scene)
Fetch plugin name from parameter server and activate the plugin for the given scene.
std::shared_lock< std::shared_mutex > ReadLock
Definition: occupancy_map.h:88
Represents an action that occurred on an object in the world. Several bits may be set indicating seve...
Definition: world.h:265
std::function< void(const ObjectConstPtr &, Action)> ObserverCallbackFn
Definition: world.h:301
This may be thrown during construction of objects if errors occur.
Definition: exceptions.h:46
Object defining bodies that can be attached to robot links.
Definition: attached_body.h:58
const std::string & getName() const
Get the name of the attached body.
Definition: attached_body.h:74
const std::vector< shapes::ShapeConstPtr > & getShapes() const
Get the shapes that make up this attached body.
A link from the robot. Contains the constant transform applied to the link and its geometry.
Definition: link_model.h:72
This class maintains the representation of the environment as seen by a planning instance....
static 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