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