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