moveit2
The MoveIt Motion Planning Framework for ROS 2.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
trajectory_visualization.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2008, 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: Dave Coleman */
36
37#include <boost/algorithm/string.hpp>
38#include <boost/algorithm/string/replace.hpp>
39
41
44#include <rviz_default_plugins/robot/robot.hpp>
47#include <rviz_common/display_context.hpp>
48#include <rviz_common/properties/bool_property.hpp>
49#include <rviz_common/properties/color_property.hpp>
50#include <rviz_common/properties/editable_enum_property.hpp>
51#include <rviz_common/properties/float_property.hpp>
52#include <rviz_common/properties/int_property.hpp>
53#include <rviz_common/properties/property.hpp>
54#include <rviz_common/properties/ros_topic_property.hpp>
55#include <rviz_common/properties/string_property.hpp>
56#include <rviz_default_plugins/robot/robot_link.hpp>
57#include <rviz_common/window_manager_interface.hpp>
58
59#include <rclcpp/qos.hpp>
60
61#include <string>
62
63using namespace std::placeholders;
64
65namespace moveit_rviz_plugin
66{
67
68TrajectoryVisualization::TrajectoryVisualization(rviz_common::properties::Property* widget,
69 rviz_common::Display* display)
70 : animating_path_(false)
71 , drop_displaying_trajectory_(false)
72 , current_state_(-1)
73 , display_(display)
74 , widget_(widget)
75 , trajectory_slider_panel_(nullptr)
76 , trajectory_slider_dock_panel_(nullptr)
77 , logger_(moveit::getLogger("moveit.ros.trajectory_visualization"))
78{
79 trajectory_topic_property_ = new rviz_common::properties::RosTopicProperty(
80 "Trajectory Topic", "/display_planned_path",
81 rosidl_generator_traits::data_type<moveit_msgs::msg::DisplayTrajectory>(),
82 "The topic on which the moveit_msgs::msg::DisplayTrajectory messages are received", widget,
83 SLOT(changedTrajectoryTopic()), this);
84
86 new rviz_common::properties::BoolProperty("Show Robot Visual", true,
87 "Indicates whether the geometry of the robot as defined for "
88 "visualisation purposes should be displayed",
89 widget, SLOT(changedDisplayPathVisualEnabled()), this);
90
92 new rviz_common::properties::BoolProperty("Show Robot Collision", false,
93 "Indicates whether the geometry of the robot as defined "
94 "for collision detection purposes should be displayed",
95 widget, SLOT(changedDisplayPathCollisionEnabled()), this);
96
97 robot_path_alpha_property_ = new rviz_common::properties::FloatProperty(
98 "Robot Alpha", 0.5f, "Specifies the alpha for the robot links", widget, SLOT(changedRobotPathAlpha()), this);
99 robot_path_alpha_property_->setMin(0.0);
100 robot_path_alpha_property_->setMax(1.0);
101
103 new rviz_common::properties::EditableEnumProperty("State Display Time", "3x",
104 "Replay speed of trajectory. Either as factor of its time"
105 "parameterization or as constant time (s) per waypoint.",
106 widget, SLOT(changedStateDisplayTime()), this);
107 state_display_time_property_->addOptionStd("3x");
108 state_display_time_property_->addOptionStd("1x");
109 state_display_time_property_->addOptionStd("0.5x");
110 state_display_time_property_->addOptionStd("0.05s");
111 state_display_time_property_->addOptionStd("0.1s");
112 state_display_time_property_->addOptionStd("0.5s");
113
114 use_sim_time_property_ = new rviz_common::properties::BoolProperty(
115 "Use Sim Time", false, "Indicates whether simulation time or wall-time is used for state display timing.", widget,
116 nullptr, this);
117
118 loop_display_property_ = new rviz_common::properties::BoolProperty("Loop Animation", false,
119 "Indicates whether the last received path "
120 "is to be animated in a loop",
121 widget, SLOT(changedLoopDisplay()), this);
122
123 trail_display_property_ = new rviz_common::properties::BoolProperty("Show Trail", false, "Show a path trail", widget,
124 SLOT(changedShowTrail()), this);
125
126 trail_step_size_property_ = new rviz_common::properties::IntProperty("Trail Step Size", 1,
127 "Specifies the step size of the samples "
128 "shown in the trajectory trail.",
129 widget, SLOT(changedTrailStepSize()), this);
130 trail_step_size_property_->setMin(1);
131
132 interrupt_display_property_ = new rviz_common::properties::BoolProperty(
133 "Interrupt Display", false,
134 "Immediately show newly planned trajectory, interrupting the currently displayed one.", widget);
135
136 robot_color_property_ = new rviz_common::properties::ColorProperty(
137 "Robot Color", QColor(150, 50, 150), "The color of the animated robot", widget, SLOT(changedRobotColor()), this);
138
139 enable_robot_color_property_ = new rviz_common::properties::BoolProperty(
140 "Color Enabled", false, "Specifies whether robot coloring is enabled", widget, SLOT(enabledRobotColor()), this);
141}
142
153
154void TrajectoryVisualization::onInitialize(const rclcpp::Node::SharedPtr& node, Ogre::SceneNode* scene_node,
155 rviz_common::DisplayContext* context)
156{
157 // Save pointers for later use
158 scene_node_ = scene_node;
159 context_ = context;
160 node_ = node;
161
162 auto ros_node_abstraction = context_->getRosNodeAbstraction().lock();
163 if (!ros_node_abstraction)
164 {
165 RVIZ_COMMON_LOG_WARNING("Unable to lock weak_ptr from DisplayContext in TrajectoryVisualization constructor");
166 return;
167 }
168 trajectory_topic_property_->initialize(ros_node_abstraction);
169
170 // Load trajectory robot
171 display_path_robot_ = std::make_shared<RobotStateVisualization>(scene_node_, context_, "Planned Path", widget_);
173 display_path_robot_->setCollisionVisible(display_path_collision_enabled_property_->getBool());
174 display_path_robot_->setVisible(false);
175
176 rviz_common::WindowManagerInterface* window_context = context_->getWindowManager();
177 if (window_context)
178 {
179 trajectory_slider_panel_ = new TrajectoryPanel(window_context->getParentWindow());
181 window_context->addPane(display_->getName() + " - Trajectory Slider", trajectory_slider_panel_);
182 trajectory_slider_dock_panel_->setIcon(display_->getIcon());
183 connect(trajectory_slider_dock_panel_, SIGNAL(visibilityChanged(bool)), this,
184 SLOT(trajectorySliderPanelVisibilityChange(bool)));
186 }
187
188 trajectory_topic_sub_ = node_->create_subscription<moveit_msgs::msg::DisplayTrajectory>(
189 trajectory_topic_property_->getStdString(), rclcpp::ServicesQoS(),
190 [this](const moveit_msgs::msg::DisplayTrajectory::ConstSharedPtr& msg) { return incomingDisplayTrajectory(msg); });
191}
192
193void TrajectoryVisualization::setName(const QString& name)
194{
196 trajectory_slider_dock_panel_->setWindowTitle(name + " - Slider");
197}
198
199void TrajectoryVisualization::onRobotModelLoaded(const moveit::core::RobotModelConstPtr& robot_model)
200{
201 robot_model_ = robot_model;
202
203 // Error check
204 if (!robot_model_)
205 {
206 RCLCPP_ERROR_STREAM(logger_, "No robot model found");
207 return;
208 }
209
210 // Load robot state
211 robot_state_ = std::make_shared<moveit::core::RobotState>(robot_model_);
212 robot_state_->setToDefaultValues();
213
214 // Load rviz robot
215 display_path_robot_->load(*robot_model_->getURDF());
216 enabledRobotColor(); // force-refresh to account for saved display configuration
217 // perform post-poned subscription to trajectory topic
218 // Check if topic name is empty
219 if (trajectory_topic_sub_->get_topic_name())
220 changedTrajectoryTopic();
221}
222
234
239
240void TrajectoryVisualization::changedLoopDisplay()
241{
245}
246
247void TrajectoryVisualization::changedShowTrail()
248{
250
251 if (!trail_display_property_->getBool())
252 return;
253 robot_trajectory::RobotTrajectoryPtr t = trajectory_message_to_display_;
254 if (!t)
256 if (!t)
257 return;
258
259 int stepsize = trail_step_size_property_->getInt();
260 // always include last trajectory point
261 trajectory_trail_.resize(
262 static_cast<int>(std::ceil((t->getWayPointCount() + stepsize - 1) / static_cast<float>(stepsize))));
263 for (std::size_t i = 0; i < trajectory_trail_.size(); ++i)
264 {
265 int waypoint_i = std::min(i * stepsize, t->getWayPointCount() - 1); // limit to last trajectory point
266 auto r =
267 std::make_unique<RobotStateVisualization>(scene_node_, context_, "Trail Robot " + std::to_string(i), nullptr);
268 r->load(*robot_model_->getURDF());
269 r->setVisualVisible(display_path_visual_enabled_property_->getBool());
270 r->setCollisionVisible(display_path_collision_enabled_property_->getBool());
271 r->setAlpha(robot_path_alpha_property_->getFloat());
272 r->update(t->getWayPointPtr(waypoint_i), default_attached_object_color_);
273 if (enable_robot_color_property_->getBool())
274 setRobotColor(&(r->getRobot()), robot_color_property_->getColor());
275 r->setVisible(display_->isEnabled() && (!animating_path_ || waypoint_i <= current_state_));
276 r->updateAttachedObjectColors(default_attached_object_color_);
277 trajectory_trail_[i] = std::move(r);
278 }
279}
280
281void TrajectoryVisualization::changedTrailStepSize()
282{
283 if (trail_display_property_->getBool())
284 changedShowTrail();
285}
286
287void TrajectoryVisualization::changedRobotPathAlpha()
288{
289 display_path_robot_->setAlpha(robot_path_alpha_property_->getFloat());
290 for (const RobotStateVisualizationUniquePtr& r : trajectory_trail_)
291 r->setAlpha(robot_path_alpha_property_->getFloat());
292}
293
294void TrajectoryVisualization::changedTrajectoryTopic()
295{
296 // post-pone subscription if robot_state_ is not yet defined, i.e. onRobotModelLoaded() not yet called
297 if (!trajectory_topic_property_->getStdString().empty() && robot_state_)
298 {
299 trajectory_topic_sub_ = node_->create_subscription<moveit_msgs::msg::DisplayTrajectory>(
300 trajectory_topic_property_->getStdString(), rclcpp::ServicesQoS(),
301 [this](const moveit_msgs::msg::DisplayTrajectory::ConstSharedPtr& msg) {
302 return incomingDisplayTrajectory(msg);
303 });
304 }
305}
306
307void TrajectoryVisualization::changedDisplayPathVisualEnabled()
308{
309 if (display_->isEnabled())
310 {
313 for (const RobotStateVisualizationUniquePtr& r : trajectory_trail_)
314 r->setVisualVisible(display_path_visual_enabled_property_->getBool());
315 }
316}
317
318void TrajectoryVisualization::changedStateDisplayTime()
319{
320}
321
322void TrajectoryVisualization::changedDisplayPathCollisionEnabled()
323{
324 if (display_->isEnabled())
325 {
326 display_path_robot_->setCollisionVisible(display_path_collision_enabled_property_->getBool());
328 for (const RobotStateVisualizationUniquePtr& r : trajectory_trail_)
329 r->setCollisionVisible(display_path_collision_enabled_property_->getBool());
330 }
331}
332
334{
335 changedRobotPathAlpha(); // set alpha property
336
338 display_path_robot_->setCollisionVisible(display_path_collision_enabled_property_->getBool());
340 for (const RobotStateVisualizationUniquePtr& r : trajectory_trail_)
341 {
342 r->setVisualVisible(display_path_visual_enabled_property_->getBool());
343 r->setCollisionVisible(display_path_collision_enabled_property_->getBool());
344 r->setVisible(true);
345 }
346
347 changedTrajectoryTopic(); // load topic at startup if default used
348}
349
351{
352 display_path_robot_->setVisible(false);
353 for (const RobotStateVisualizationUniquePtr& r : trajectory_trail_)
354 r->setVisible(false);
356 animating_path_ = false;
359}
360
362{
363 // update() starts a new trajectory as soon as it is available
364 // interrupting may cause the newly received trajectory to interrupt
365 // hence, only interrupt when current_state_ already advanced past first
366 if (current_state_ > 0)
367 animating_path_ = false;
368}
369
371{
372 constexpr char default_time_string[] = "3x";
373 constexpr double default_time_value = -3.0;
374
375 std::string tm = state_display_time_property_->getStdString();
376 boost::trim(tm);
377
378 double type;
379
380 if (tm.back() == 'x')
381 {
382 type = -1.0f;
383 }
384 else if (tm.back() == 's')
385 {
386 type = 1.0f;
387 }
388 else
389 {
390 state_display_time_property_->setStdString(default_time_string);
391 return default_time_value;
392 }
393
394 tm.resize(tm.size() - 1);
395 boost::trim_right(tm);
396
397 double value;
398 try
399 {
400 value = std::stof(tm);
401 }
402 catch (const std::invalid_argument& ex)
403 {
404 state_display_time_property_->setStdString(default_time_string);
405 return default_time_value;
406 }
407
408 if (value <= 0)
409 {
410 state_display_time_property_->setStdString(default_time_string);
411 return default_time_value;
412 }
413
414 return type * value;
415}
416
421
422void TrajectoryVisualization::update(double wall_dt, double sim_dt)
423{
425 {
426 animating_path_ = false;
430 }
431 if (!animating_path_)
432 { // finished last animation?
433 std::scoped_lock lock(update_trajectory_message_);
434
435 // new trajectory available to display?
437 {
438 animating_path_ = true;
440 changedShowTrail();
443 }
445 {
446 if (loop_display_property_->getBool())
447 { // do loop? -> start over too
448 animating_path_ = true;
449 }
450 else if (trajectory_slider_panel_ && trajectory_slider_panel_->isVisible())
451 {
452 if (static_cast<unsigned int>(trajectory_slider_panel_->getSliderPosition()) >=
453 displaying_trajectory_message_->getWayPointCount() - 1)
454 {
455 return; // nothing more to do
456 }
457 else
458 {
459 animating_path_ = true;
460 }
461 }
462 }
464
465 if (animating_path_)
466 {
467 // restart animation
468 current_state_ = -1;
469 }
470 }
471
472 if (animating_path_)
473 {
474 int previous_state = current_state_;
475 int waypoint_count = displaying_trajectory_message_->getWayPointCount();
476 if (use_sim_time_property_->getBool())
477 {
478 current_state_time_ += sim_dt;
479 }
480 else
481 {
482 current_state_time_ += wall_dt;
483 }
484 double tm = getStateDisplayTime();
485
487 {
489 current_state_time_ = displaying_trajectory_message_->getWayPointDurationFromPrevious(current_state_);
490 }
491 else if (current_state_ < 0)
492 { // special case indicating restart of animation
493 current_state_ = 0;
495 }
496 else if (tm < 0.0)
497 {
498 // using realtime factors: skip to next waypoint based on elapsed display time
499 const double rt_factor = -tm; // negative tm is the intended realtime factor
500 while (current_state_ < waypoint_count &&
501 (tm = displaying_trajectory_message_->getWayPointDurationFromPrevious(current_state_ + 1) / rt_factor) <
503 {
505 // if we are stuck in the while loop we should move the robot along the path to keep up
506 if (tm < current_state_time_)
509 }
510 }
511 else if (current_state_time_ > tm)
512 { // fixed display time per state
515 }
516
517 if (current_state_ == previous_state)
518 return;
519
520 if (current_state_ < waypoint_count)
521 {
525 for (std::size_t i = 0; i < trajectory_trail_.size(); ++i)
526 {
527 trajectory_trail_[i]->setVisible(
528 std::min(waypoint_count - 1, static_cast<int>(i) * trail_step_size_property_->getInt()) <= current_state_);
529 }
530 }
531 else
532 {
533 animating_path_ = false; // animation finished
535 { // make sure we move the slider to the end
536 // so the user can re-play
538 }
539 display_path_robot_->update(displaying_trajectory_message_->getWayPointPtr(waypoint_count - 1));
540 display_path_robot_->setVisible(loop_display_property_->getBool());
543 }
544 }
545 // set visibility
546 display_path_robot_->setVisible(display_->isEnabled() && displaying_trajectory_message_ &&
549 display_path_robot_->updateAttachedObjectColors(default_attached_object_color_);
550}
551
552void TrajectoryVisualization::incomingDisplayTrajectory(const moveit_msgs::msg::DisplayTrajectory::ConstSharedPtr& msg)
553{
554 // Error check
555 if (!robot_state_ || !robot_model_)
556 {
557 RCLCPP_ERROR_STREAM(logger_, "No robot state or robot model loaded");
558 return;
559 }
560
561 if (!msg->model_id.empty() && msg->model_id != robot_model_->getName())
562 {
563 RCLCPP_WARN(logger_, "Received a trajectory to display for model '%s' but model '%s' was expected",
564 msg->model_id.c_str(), robot_model_->getName().c_str());
565 }
566
568
569 auto t = std::make_shared<robot_trajectory::RobotTrajectory>(robot_model_, "");
570 try
571 {
572 for (std::size_t i = 0; i < msg->trajectory.size(); ++i)
573 {
574 if (t->empty())
575 {
576 t->setRobotTrajectoryMsg(*robot_state_, msg->trajectory_start, msg->trajectory[i]);
577 }
578 else
579 {
581 tmp.setRobotTrajectoryMsg(t->getLastWayPoint(), msg->trajectory[i]);
582 t->append(tmp, 0.0);
583 }
584 }
585 display_->setStatus(rviz_common::properties::StatusProperty::Ok, "Trajectory", "");
586 }
587 catch (const moveit::Exception& e)
588 {
589 display_->setStatus(rviz_common::properties::StatusProperty::Error, "Trajectory", e.what());
590 return;
591 }
592
593 if (!t->empty())
594 {
595 std::scoped_lock lock(update_trajectory_message_);
597 if (interrupt_display_property_->getBool())
599 }
600}
601
602void TrajectoryVisualization::changedRobotColor()
603{
604 if (enable_robot_color_property_->getBool())
605 setRobotColor(&(display_path_robot_->getRobot()), robot_color_property_->getColor());
606}
607
608void TrajectoryVisualization::enabledRobotColor()
609{
610 if (enable_robot_color_property_->getBool())
611 {
612 setRobotColor(&(display_path_robot_->getRobot()), robot_color_property_->getColor());
613 }
614 else
615 {
616 unsetRobotColor(&(display_path_robot_->getRobot()));
617 }
618}
619
620void TrajectoryVisualization::unsetRobotColor(rviz_default_plugins::robot::Robot* robot)
621{
622 for (auto& link : robot->getLinks())
623 link.second->unsetColor();
624}
625
627{
628 default_attached_object_color_.r = color.redF();
629 default_attached_object_color_.g = color.greenF();
630 default_attached_object_color_.b = color.blueF();
631 default_attached_object_color_.a = color.alphaF();
632
634 {
635 display_path_robot_->setDefaultAttachedObjectColor(default_attached_object_color_);
636 display_path_robot_->updateAttachedObjectColors(default_attached_object_color_);
637 }
638 for (const RobotStateVisualizationUniquePtr& r : trajectory_trail_)
639 r->updateAttachedObjectColors(default_attached_object_color_);
640}
641
642void TrajectoryVisualization::setRobotColor(rviz_default_plugins::robot::Robot* robot, const QColor& color)
643{
644 for (auto& link : robot->getLinks())
645 robot->getLink(link.first)->setColor(color.redF(), color.greenF(), color.blueF());
646}
647
648void TrajectoryVisualization::trajectorySliderPanelVisibilityChange(bool enable)
649{
651 return;
652
653 if (enable)
654 {
656 }
657 else
658 {
660 }
661}
662
664{
665 robot_model_.reset();
666 robot_state_.reset();
667}
668
669} // namespace moveit_rviz_plugin
This may be thrown if unrecoverable errors occur.
rviz_common::properties::BoolProperty * enable_robot_color_property_
void incomingDisplayTrajectory(const moveit_msgs::msg::DisplayTrajectory::ConstSharedPtr &msg)
ROS callback for an incoming path message.
rviz_common::properties::BoolProperty * display_path_visual_enabled_property_
rviz_common::properties::BoolProperty * interrupt_display_property_
robot_trajectory::RobotTrajectoryPtr trajectory_message_to_display_
robot_trajectory::RobotTrajectoryPtr displaying_trajectory_message_
rviz_common::PanelDockWidget * trajectory_slider_dock_panel_
void onInitialize(const rclcpp::Node::SharedPtr &node, Ogre::SceneNode *scene_node, rviz_common::DisplayContext *context)
rviz_common::properties::BoolProperty * trail_display_property_
virtual void update(double wall_dt, double sim_dt)
rviz_common::properties::BoolProperty * display_path_collision_enabled_property_
rviz_common::properties::RosTopicProperty * trajectory_topic_property_
rviz_common::properties::BoolProperty * use_sim_time_property_
TrajectoryVisualization(rviz_common::properties::Property *widget, rviz_common::Display *display)
Playback a trajectory from a planned path.
rviz_common::properties::BoolProperty * loop_display_property_
rviz_common::properties::ColorProperty * robot_color_property_
void setRobotColor(rviz_default_plugins::robot::Robot *robot, const QColor &color)
rviz_common::properties::EditableEnumProperty * state_display_time_property_
rviz_common::properties::IntProperty * trail_step_size_property_
std::vector< RobotStateVisualizationUniquePtr > trajectory_trail_
void unsetRobotColor(rviz_default_plugins::robot::Robot *robot)
void onRobotModelLoaded(const moveit::core::RobotModelConstPtr &robot_model)
rviz_common::properties::FloatProperty * robot_path_alpha_property_
rclcpp::Subscription< moveit_msgs::msg::DisplayTrajectory >::SharedPtr trajectory_topic_sub_
double getStateDisplayTime()
get time to show each single robot state
Maintain a sequence of waypoints and the time durations between these waypoints.
RobotTrajectory & setRobotTrajectoryMsg(const moveit::core::RobotState &reference_state, const trajectory_msgs::msg::JointTrajectory &trajectory)
Copy the content of the trajectory message into this class. The trajectory message itself is not requ...
RobotTrajectory & append(const RobotTrajectory &source, double dt, size_t start_index=0, size_t end_index=std::numeric_limits< std::size_t >::max())
Add a specified part of a trajectory to the end of the current trajectory. The default (when start_in...
Main namespace for MoveIt.