15 #include "nav2_rviz_plugins/nav2_panel.hpp"
17 #include <QtConcurrent/QtConcurrent>
18 #include <QVBoxLayout>
19 #include <QHBoxLayout>
30 #include "nav2_rviz_plugins/goal_common.hpp"
31 #include "nav2_rviz_plugins/utils.hpp"
32 #include "rclcpp/rclcpp.hpp"
33 #include "rviz_common/display_context.hpp"
34 #include "rviz_common/load_resource.hpp"
35 #include "yaml-cpp/yaml.h"
36 #include "geometry_msgs/msg/pose.hpp"
38 using namespace std::chrono_literals;
40 namespace nav2_rviz_plugins
42 using nav2_util::geometry_utils::orientationAroundZAxis;
45 GoalPoseUpdater GoalUpdater;
47 Nav2Panel::Nav2Panel(QWidget * parent)
53 start_reset_button_ =
new QPushButton;
54 pause_resume_button_ =
new QPushButton;
55 navigation_mode_button_ =
new QPushButton;
56 add_pose_button_ =
new QPushButton;
57 remove_pose_button_ =
new QPushButton;
58 save_waypoints_button_ =
new QPushButton;
59 load_waypoints_button_ =
new QPushButton;
60 pause_waypoint_button_ =
new QPushButton;
61 start_nav_to_pose_button_ =
new QPushButton;
62 navigation_status_indicator_ =
new QLabel;
63 localization_status_indicator_ =
new QLabel;
64 navigation_goal_status_indicator_ =
new QLabel;
65 navigation_feedback_indicator_ =
new QLabel;
66 waypoint_status_indicator_ =
new QLabel;
67 number_of_loops_ =
new QLabel;
68 nr_of_loops_ =
new QLineEdit;
69 store_initial_pose_checkbox_ =
new QCheckBox(
"Store initial_pose");
72 behavior_tree_file_ =
new QLineEdit;
73 behavior_tree_file_->setPlaceholderText(
"Leave empty for default behavior tree");
77 const char * startup_msg =
"Configure and activate all nav2 lifecycle nodes";
78 const char * shutdown_msg =
"Deactivate and cleanup all nav2 lifecycle nodes";
79 const char * cancel_msg =
"Cancel navigation";
80 const char * pause_msg =
"Deactivate all nav2 lifecycle nodes";
81 const char * resume_msg =
"Activate all nav2 lifecycle nodes";
82 const char * single_goal_msg =
83 "Change to waypoint Following / nav through poses style navigation";
84 const char * waypoint_goal_msg =
"Start following waypoints";
85 const char * nft_goal_msg =
"Start navigating through poses";
86 const char * cancel_waypoint_msg =
"Cancel waypoint / viapoint accumulation mode";
88 const QString navigation_active(
"<table><tr><td width=150><b>Navigation:</b></td>"
89 "<td><font color=green>active</color></td></tr></table>");
90 const QString navigation_inactive(
"<table><tr><td width=150><b>Navigation:</b></td>"
91 "<td>inactive</td></tr></table>");
92 const QString navigation_unknown(
"<table><tr><td width=150><b>Navigation:</b></td>"
93 "<td>unknown</td></tr></table>");
94 const QString localization_active(
"<table><tr><td width=150><b>Localization:</b></td>"
95 "<td><font color=green>active</color></td></tr></table>");
96 const QString localization_inactive(
"<table><tr><td width=150><b>Localization:</b></td>"
97 "<td>inactive</td></tr></table>");
98 const QString localization_unknown(
"<table><tr><td width=150><b>Localization:</b></td>"
99 "<td>unknown</td></tr></table>");
101 navigation_status_indicator_->setText(navigation_unknown);
102 localization_status_indicator_->setText(localization_unknown);
103 navigation_goal_status_indicator_->setText(nav2_rviz_plugins::getGoalStatusLabel());
104 number_of_loops_->setText(
"Num of loops");
105 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
106 navigation_status_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
107 localization_status_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
108 navigation_goal_status_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
109 navigation_feedback_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
110 waypoint_status_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
112 pre_initial_ =
new QState();
113 pre_initial_->setObjectName(
"pre_initial");
114 pre_initial_->assignProperty(start_reset_button_,
"text",
"Startup");
115 pre_initial_->assignProperty(start_reset_button_,
"enabled",
false);
117 pre_initial_->assignProperty(pause_resume_button_,
"text",
"Pause");
118 pre_initial_->assignProperty(pause_resume_button_,
"enabled",
false);
120 pre_initial_->assignProperty(start_nav_to_pose_button_,
"text",
"Start NavigateToPose");
122 pre_initial_->assignProperty(
123 navigation_mode_button_,
"text",
124 "Waypoint Following / NavigateThroughPoses Mode");
125 pre_initial_->assignProperty(navigation_mode_button_,
"enabled",
false);
127 pre_initial_->assignProperty(add_pose_button_,
"text",
"Add Pose");
128 pre_initial_->assignProperty(add_pose_button_,
"enabled",
false);
129 pre_initial_->assignProperty(remove_pose_button_,
"text",
"Remove Pose");
130 pre_initial_->assignProperty(remove_pose_button_,
"enabled",
false);
132 pre_initial_->assignProperty(save_waypoints_button_,
"text",
"Save");
133 pre_initial_->assignProperty(save_waypoints_button_,
"enabled",
false);
134 pre_initial_->assignProperty(load_waypoints_button_,
"text",
"Load");
135 pre_initial_->assignProperty(load_waypoints_button_,
"enabled",
false);
137 pre_initial_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
138 pre_initial_->assignProperty(pause_waypoint_button_,
"enabled",
false);
140 pre_initial_->assignProperty(nr_of_loops_,
"text",
"0");
141 pre_initial_->assignProperty(nr_of_loops_,
"enabled",
false);
143 pre_initial_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
145 initial_ =
new QState();
146 initial_->setObjectName(
"initial");
147 initial_->assignProperty(start_reset_button_,
"text",
"Startup");
148 initial_->assignProperty(start_reset_button_,
"toolTip", startup_msg);
149 initial_->assignProperty(start_reset_button_,
"enabled",
true);
151 initial_->assignProperty(pause_resume_button_,
"text",
"Pause");
152 initial_->assignProperty(pause_resume_button_,
"enabled",
false);
154 initial_->assignProperty(navigation_mode_button_,
"text",
155 "Waypoint Following / NavigateThroughPoses Mode");
156 initial_->assignProperty(navigation_mode_button_,
"enabled",
false);
158 initial_->assignProperty(add_pose_button_,
"enabled",
false);
159 initial_->assignProperty(remove_pose_button_,
"enabled",
false);
161 initial_->assignProperty(save_waypoints_button_,
"enabled",
false);
162 initial_->assignProperty(load_waypoints_button_,
"enabled",
false);
164 initial_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
165 initial_->assignProperty(pause_waypoint_button_,
"enabled",
false);
167 initial_->assignProperty(nr_of_loops_,
"text",
"0");
168 initial_->assignProperty(nr_of_loops_,
"enabled",
false);
170 initial_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
173 idle_ =
new QState();
174 idle_->setObjectName(
"idle");
175 idle_->assignProperty(start_reset_button_,
"text",
"Reset");
176 idle_->assignProperty(start_reset_button_,
"toolTip", shutdown_msg);
177 idle_->assignProperty(start_reset_button_,
"enabled",
true);
179 idle_->assignProperty(pause_resume_button_,
"text",
"Pause");
180 idle_->assignProperty(pause_resume_button_,
"enabled",
true);
181 idle_->assignProperty(pause_resume_button_,
"toolTip", pause_msg);
183 idle_->assignProperty(navigation_mode_button_,
"text",
184 "Waypoint Following / NavigateThroughPoses Mode");
185 idle_->assignProperty(navigation_mode_button_,
"enabled",
true);
186 idle_->assignProperty(navigation_mode_button_,
"toolTip", single_goal_msg);
188 idle_->assignProperty(add_pose_button_,
"enabled",
false);
189 idle_->assignProperty(remove_pose_button_,
"enabled",
false);
191 idle_->assignProperty(save_waypoints_button_,
"enabled",
false);
192 idle_->assignProperty(load_waypoints_button_,
"enabled",
false);
194 idle_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
195 idle_->assignProperty(pause_waypoint_button_,
"enabled",
false);
197 idle_->assignProperty(nr_of_loops_,
"text",
"0");
198 idle_->assignProperty(nr_of_loops_,
"enabled",
false);
200 idle_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
201 idle_->assignProperty(start_nav_to_pose_button_,
"enabled",
true);
204 accumulating_ =
new QState();
205 accumulating_->setObjectName(
"accumulating");
206 accumulating_->assignProperty(start_reset_button_,
"text",
"Cancel Accumulation");
207 accumulating_->assignProperty(start_reset_button_,
"toolTip", cancel_waypoint_msg);
208 accumulating_->assignProperty(start_reset_button_,
"enabled",
true);
210 accumulating_->assignProperty(pause_resume_button_,
"text",
"Start NavigateThroughPoses");
211 accumulating_->assignProperty(pause_resume_button_,
"enabled",
true);
212 accumulating_->assignProperty(pause_resume_button_,
"toolTip", nft_goal_msg);
214 accumulating_->assignProperty(navigation_mode_button_,
"text",
"Start Waypoint Following");
215 accumulating_->assignProperty(navigation_mode_button_,
"enabled",
true);
216 accumulating_->assignProperty(navigation_mode_button_,
"toolTip", waypoint_goal_msg);
218 accumulating_->assignProperty(add_pose_button_,
"enabled",
true);
219 accumulating_->assignProperty(remove_pose_button_,
"enabled",
true);
221 accumulating_->assignProperty(save_waypoints_button_,
"enabled",
true);
222 accumulating_->assignProperty(load_waypoints_button_,
"enabled",
true);
224 accumulating_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
225 accumulating_->assignProperty(pause_waypoint_button_,
"enabled",
false);
227 accumulating_->assignProperty(nr_of_loops_,
"text", QString::fromStdString(loop_no_));
228 accumulating_->assignProperty(nr_of_loops_,
"enabled",
true);
229 accumulating_->assignProperty(store_initial_pose_checkbox_,
"enabled",
true);
232 accumulated_wp_ =
new QState();
233 accumulated_wp_->setObjectName(
"accumulated_wp");
234 accumulated_wp_->assignProperty(start_reset_button_,
"text",
"Cancel");
235 accumulated_wp_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
236 accumulated_wp_->assignProperty(start_reset_button_,
"enabled",
true);
238 accumulated_wp_->assignProperty(pause_resume_button_,
"text",
"Start NavigateThroughPoses");
239 accumulated_wp_->assignProperty(pause_resume_button_,
"enabled",
false);
240 accumulated_wp_->assignProperty(pause_resume_button_,
"toolTip", nft_goal_msg);
242 accumulated_wp_->assignProperty(navigation_mode_button_,
"text",
"Start Waypoint Following");
243 accumulated_wp_->assignProperty(navigation_mode_button_,
"enabled",
false);
244 accumulated_wp_->assignProperty(navigation_mode_button_,
"toolTip", waypoint_goal_msg);
246 accumulated_wp_->assignProperty(add_pose_button_,
"enabled",
false);
247 accumulated_wp_->assignProperty(remove_pose_button_,
"enabled",
false);
249 accumulated_wp_->assignProperty(save_waypoints_button_,
"enabled",
false);
250 accumulated_wp_->assignProperty(load_waypoints_button_,
"enabled",
false);
252 accumulated_wp_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
253 accumulated_wp_->assignProperty(pause_waypoint_button_,
"enabled",
true);
255 accumulated_wp_->assignProperty(nr_of_loops_,
"text", QString::fromStdString(loop_no_));
256 accumulated_wp_->assignProperty(nr_of_loops_,
"enabled",
false);
257 accumulated_wp_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
260 accumulated_nav_through_poses_ =
new QState();
261 accumulated_nav_through_poses_->setObjectName(
"accumulated_nav_through_poses");
262 accumulated_nav_through_poses_->assignProperty(start_reset_button_,
"text",
"Cancel");
263 accumulated_nav_through_poses_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
265 accumulated_nav_through_poses_->assignProperty(pause_resume_button_,
266 "text",
"Start NavigateThroughPoses");
267 accumulated_nav_through_poses_->assignProperty(pause_resume_button_,
"enabled",
false);
268 accumulated_nav_through_poses_->assignProperty(pause_resume_button_,
"toolTip", nft_goal_msg);
270 accumulated_nav_through_poses_->assignProperty(navigation_mode_button_,
271 "text",
"Start Waypoint Following");
272 accumulated_nav_through_poses_->assignProperty(navigation_mode_button_,
274 accumulated_nav_through_poses_->assignProperty(navigation_mode_button_,
275 "toolTip", waypoint_goal_msg);
277 accumulated_nav_through_poses_->assignProperty(add_pose_button_,
"enabled",
false);
278 accumulated_nav_through_poses_->assignProperty(remove_pose_button_,
"enabled",
false);
280 accumulated_nav_through_poses_->assignProperty(save_waypoints_button_,
"enabled",
false);
281 accumulated_nav_through_poses_->assignProperty(load_waypoints_button_,
"enabled",
false);
283 accumulated_nav_through_poses_->assignProperty(pause_waypoint_button_,
284 "text",
"Pause Waypoint Following");
285 accumulated_nav_through_poses_->assignProperty(pause_waypoint_button_,
"enabled",
false);
287 accumulated_nav_through_poses_->assignProperty(nr_of_loops_,
"enabled",
false);
289 accumulated_nav_through_poses_->assignProperty(start_reset_button_,
"enabled",
true);
290 accumulated_nav_through_poses_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
292 accumulated_nav_through_poses_->assignProperty(
293 nr_of_loops_,
"text",
294 QString::fromStdString(loop_no_));
295 accumulated_nav_through_poses_->assignProperty(nr_of_loops_,
"enabled",
false);
296 accumulated_nav_through_poses_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
299 canceled_ =
new QState();
300 canceled_->setObjectName(
"canceled");
303 reset_ =
new QState();
304 reset_->setObjectName(
"reset");
307 running_ =
new QState();
308 running_->setObjectName(
"running");
309 running_->assignProperty(start_reset_button_,
"text",
"Cancel");
310 running_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
312 running_->assignProperty(pause_resume_button_,
"text",
"Pause");
313 running_->assignProperty(pause_resume_button_,
"enabled",
false);
315 running_->assignProperty(navigation_mode_button_,
316 "text",
"Waypoint Following / NavigateThroughPoses Mode");
317 running_->assignProperty(navigation_mode_button_,
"enabled",
false);
319 running_->assignProperty(start_nav_to_pose_button_,
"enabled",
false);
321 running_->assignProperty(add_pose_button_,
"enabled",
false);
322 running_->assignProperty(remove_pose_button_,
"enabled",
false);
324 running_->assignProperty(save_waypoints_button_,
"enabled",
false);
325 running_->assignProperty(load_waypoints_button_,
"enabled",
false);
327 running_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
328 running_->assignProperty(pause_waypoint_button_,
"enabled",
false);
330 running_->assignProperty(nr_of_loops_,
"text",
"0");
331 running_->assignProperty(nr_of_loops_,
"enabled",
false);
333 running_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
336 paused_ =
new QState();
337 paused_->setObjectName(
"pausing");
338 paused_->assignProperty(start_reset_button_,
"text",
"Reset");
339 paused_->assignProperty(start_reset_button_,
"toolTip", shutdown_msg);
341 paused_->assignProperty(pause_resume_button_,
"text",
"Resume");
342 paused_->assignProperty(pause_resume_button_,
"toolTip", resume_msg);
343 paused_->assignProperty(pause_resume_button_,
"enabled",
true);
345 paused_->assignProperty(navigation_mode_button_,
"text",
"");
346 paused_->assignProperty(navigation_mode_button_,
"enabled",
false);
348 paused_->assignProperty(start_nav_to_pose_button_,
"enabled",
false);
350 paused_->assignProperty(add_pose_button_,
"enabled",
false);
351 paused_->assignProperty(remove_pose_button_,
"enabled",
false);
353 paused_->assignProperty(save_waypoints_button_,
"enabled",
false);
354 paused_->assignProperty(load_waypoints_button_,
"enabled",
false);
356 paused_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
357 paused_->assignProperty(pause_waypoint_button_,
"enabled",
false);
359 paused_->assignProperty(nr_of_loops_,
"enabled",
false);
361 paused_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
364 resumed_ =
new QState();
365 resumed_->setObjectName(
"resuming");
368 resumed_wp_ =
new QState();
369 resumed_wp_->setObjectName(
"running");
370 resumed_wp_->assignProperty(start_reset_button_,
"text",
"Cancel");
371 resumed_wp_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
373 resumed_wp_->assignProperty(pause_resume_button_,
"text",
"Start NavigateThroughPoses");
374 resumed_wp_->assignProperty(pause_resume_button_,
"enabled",
false);
376 resumed_wp_->assignProperty(navigation_mode_button_,
"text",
"Start Waypoint Following");
377 resumed_wp_->assignProperty(navigation_mode_button_,
"enabled",
false);
379 resumed_wp_->assignProperty(add_pose_button_,
"enabled",
false);
380 resumed_wp_->assignProperty(remove_pose_button_,
"enabled",
false);
382 resumed_wp_->assignProperty(save_waypoints_button_,
"enabled",
true);
383 resumed_wp_->assignProperty(load_waypoints_button_,
"enabled",
false);
385 resumed_wp_->assignProperty(pause_waypoint_button_,
"text",
"Resume WaypointFollowing");
386 resumed_wp_->assignProperty(pause_waypoint_button_,
"enabled",
true);
388 resumed_wp_->assignProperty(nr_of_loops_,
"enabled",
false);
389 resumed_wp_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
391 QObject::connect(initial_, SIGNAL(exited()),
this, SLOT(onStartup()));
392 QObject::connect(canceled_, SIGNAL(exited()),
this, SLOT(onCancel()));
393 QObject::connect(reset_, SIGNAL(exited()),
this, SLOT(onShutdown()));
394 QObject::connect(paused_, SIGNAL(entered()),
this, SLOT(onPause()));
395 QObject::connect(resumed_, SIGNAL(exited()),
this, SLOT(onResume()));
396 QObject::connect(idle_, SIGNAL(entered()),
this, SLOT(onIdle()));
397 QObject::connect(accumulating_, SIGNAL(entered()),
this, SLOT(onAccumulating()));
398 QObject::connect(accumulated_wp_, SIGNAL(entered()),
this, SLOT(onAccumulatedWp()));
399 QObject::connect(resumed_wp_, SIGNAL(entered()),
this, SLOT(onResumedWp()));
401 accumulated_nav_through_poses_, SIGNAL(entered()),
this,
402 SLOT(onAccumulatedNTP()));
404 save_waypoints_button_,
405 &QPushButton::released,
406 this, &Nav2Panel::handleGoalSaver);
408 load_waypoints_button_,
409 &QPushButton::released,
411 &Nav2Panel::handleGoalLoader);
414 &QLineEdit::editingFinished,
416 &Nav2Panel::loophandler);
417 #if QT_VERSION >= QT_VERSION_CHECK(6, 10, 2)
419 store_initial_pose_checkbox_,
420 &QCheckBox::checkStateChanged,
422 &Nav2Panel::initialStateHandler);
425 store_initial_pose_checkbox_,
426 &QCheckBox::stateChanged,
428 &Nav2Panel::initialStateHandler);
432 initial_->addTransition(start_reset_button_, SIGNAL(clicked()), idle_);
433 idle_->addTransition(start_reset_button_, SIGNAL(clicked()), reset_);
434 running_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
435 paused_->addTransition(start_reset_button_, SIGNAL(clicked()), reset_);
436 idle_->addTransition(navigation_mode_button_, SIGNAL(clicked()), accumulating_);
437 accumulating_->addTransition(navigation_mode_button_, SIGNAL(clicked()), accumulated_wp_);
438 accumulating_->addTransition(
439 pause_resume_button_, SIGNAL(
440 clicked()), accumulated_nav_through_poses_);
441 accumulating_->addTransition(start_reset_button_, SIGNAL(clicked()), idle_);
442 accumulated_wp_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
443 accumulated_nav_through_poses_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
446 canceled_->addTransition(canceled_, SIGNAL(entered()), idle_);
447 reset_->addTransition(reset_, SIGNAL(entered()), initial_);
448 resumed_->addTransition(resumed_, SIGNAL(entered()), idle_);
451 idle_->addTransition(pause_resume_button_, SIGNAL(clicked()), paused_);
452 paused_->addTransition(pause_resume_button_, SIGNAL(clicked()), resumed_);
455 accumulated_wp_->addTransition(pause_waypoint_button_, SIGNAL(clicked()), resumed_wp_);
456 resumed_wp_->addTransition(pause_waypoint_button_, SIGNAL(clicked()), accumulated_wp_);
457 resumed_wp_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
462 ROSActionQTransition * idleTransition =
new ROSActionQTransition(QActionState::INACTIVE);
463 idleTransition->setTargetState(running_);
464 idle_->addTransition(idleTransition);
466 ROSActionQTransition * runningTransition =
new ROSActionQTransition(QActionState::ACTIVE);
467 runningTransition->setTargetState(idle_);
468 running_->addTransition(runningTransition);
470 ROSActionQTransition * idleAccumulatedWpTransition =
471 new ROSActionQTransition(QActionState::INACTIVE);
472 idleAccumulatedWpTransition->setTargetState(accumulated_wp_);
473 idle_->addTransition(idleAccumulatedWpTransition);
475 ROSActionQTransition * accumulatedWpTransition =
new ROSActionQTransition(QActionState::ACTIVE);
476 accumulatedWpTransition->setTargetState(idle_);
477 accumulated_wp_->addTransition(accumulatedWpTransition);
479 ROSActionQTransition * idleAccumulatedNTPTransition =
480 new ROSActionQTransition(QActionState::INACTIVE);
481 idleAccumulatedNTPTransition->setTargetState(accumulated_nav_through_poses_);
482 idle_->addTransition(idleAccumulatedNTPTransition);
484 ROSActionQTransition * accumulatedNTPTransition =
new ROSActionQTransition(QActionState::ACTIVE);
485 accumulatedNTPTransition->setTargetState(idle_);
486 accumulated_nav_through_poses_->addTransition(accumulatedNTPTransition);
488 auto options = rclcpp::NodeOptions().arguments(
489 {
"--ros-args",
"--remap",
"__node:=rviz_navigation_dialog_action_client",
"--"});
490 client_node_ = std::make_shared<rclcpp::Node>(
"_", options);
491 executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
492 executor_->add_node(client_node_);
494 client_nav_ = std::make_shared<nav2_lifecycle_manager::LifecycleManagerClient>(
495 "lifecycle_manager_navigation", client_node_);
496 client_loc_ = std::make_shared<nav2_lifecycle_manager::LifecycleManagerClient>(
497 "lifecycle_manager_localization", client_node_);
498 initial_thread_ =
new InitialThread(client_nav_, client_loc_);
499 connect(initial_thread_, &InitialThread::finished, initial_thread_, &QObject::deleteLater);
501 QSignalTransition * activeSignal =
new QSignalTransition(
503 &InitialThread::navigationActive);
504 activeSignal->setTargetState(idle_);
505 pre_initial_->addTransition(activeSignal);
506 QSignalTransition * inactiveSignal =
new QSignalTransition(
508 &InitialThread::navigationInactive);
509 inactiveSignal->setTargetState(initial_);
510 pre_initial_->addTransition(inactiveSignal);
513 initial_thread_, &InitialThread::navigationActive,
514 [
this, navigation_active] {
515 navigation_status_indicator_->setText(navigation_active);
518 initial_thread_, &InitialThread::navigationInactive,
519 [
this, navigation_inactive] {
520 navigation_status_indicator_->setText(navigation_inactive);
521 navigation_goal_status_indicator_->setText(nav2_rviz_plugins::getGoalStatusLabel());
522 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
525 initial_thread_, &InitialThread::localizationActive,
526 [
this, localization_active] {
527 localization_status_indicator_->setText(localization_active);
530 initial_thread_, &InitialThread::localizationInactive,
531 [
this, localization_inactive] {
532 localization_status_indicator_->setText(localization_inactive);
535 state_machine_.addState(pre_initial_);
536 state_machine_.addState(initial_);
537 state_machine_.addState(idle_);
538 state_machine_.addState(running_);
539 state_machine_.addState(canceled_);
540 state_machine_.addState(reset_);
541 state_machine_.addState(paused_);
542 state_machine_.addState(resumed_);
543 state_machine_.addState(accumulating_);
544 state_machine_.addState(accumulated_wp_);
545 state_machine_.addState(accumulated_nav_through_poses_);
546 state_machine_.addState(resumed_wp_);
548 state_machine_.setInitialState(pre_initial_);
551 QObject::connect(&state_machine_, SIGNAL(started()),
this, SLOT(startThread()));
552 state_machine_.start();
555 QVBoxLayout * main_layout =
new QVBoxLayout;
556 QHBoxLayout * side_layout =
new QHBoxLayout;
557 QVBoxLayout * status_layout =
new QVBoxLayout;
558 QHBoxLayout * logo_layout =
new QHBoxLayout;
560 imgDisplayLabel_ =
new QLabel(
"");
561 imgDisplayLabel_->setPixmap(
562 rviz_common::loadPixmap(
"package://nav2_rviz_plugins/icons/classes/nav2_logo_small.png"));
564 status_layout->addWidget(navigation_status_indicator_);
565 status_layout->addWidget(localization_status_indicator_);
566 status_layout->addWidget(navigation_goal_status_indicator_);
568 logo_layout->addWidget(imgDisplayLabel_, 5, Qt::AlignRight);
570 side_layout->addLayout(status_layout);
571 side_layout->addLayout(logo_layout);
573 main_layout->addLayout(side_layout);
574 main_layout->addWidget(navigation_feedback_indicator_);
575 main_layout->addWidget(waypoint_status_indicator_);
578 QHBoxLayout * bt_layout =
new QHBoxLayout;
579 QLabel * bt_label =
new QLabel(
"Behavior Tree XML:");
580 bt_layout->addWidget(bt_label);
581 bt_layout->addWidget(behavior_tree_file_);
582 main_layout->addLayout(bt_layout);
584 main_layout->addWidget(pause_resume_button_);
585 main_layout->addWidget(start_reset_button_);
586 main_layout->addWidget(navigation_mode_button_);
589 tools_tab_widget_ =
new QTabWidget;
592 QWidget * nav_to_pose_tab =
new QWidget;
593 QGridLayout * nav_to_pose_layout =
new QGridLayout;
595 nav_to_pose_layout->addWidget(
new QLabel(
"Frame ID:"), 0, 0);
596 nav_to_pose_frame_id_ =
new QLineEdit(
"map");
597 nav_to_pose_layout->addWidget(nav_to_pose_frame_id_, 0, 1);
599 nav_to_pose_layout->addWidget(
new QLabel(
"Position X:"), 1, 0);
600 nav_to_pose_x_ =
new QDoubleSpinBox;
601 nav_to_pose_x_->setRange(-1000.0, 1000.0);
602 nav_to_pose_x_->setDecimals(3);
603 nav_to_pose_x_->setSingleStep(0.1);
604 nav_to_pose_layout->addWidget(nav_to_pose_x_, 1, 1);
606 nav_to_pose_layout->addWidget(
new QLabel(
"Position Y:"), 2, 0);
607 nav_to_pose_y_ =
new QDoubleSpinBox;
608 nav_to_pose_y_->setRange(-1000.0, 1000.0);
609 nav_to_pose_y_->setDecimals(3);
610 nav_to_pose_y_->setSingleStep(0.1);
611 nav_to_pose_layout->addWidget(nav_to_pose_y_, 2, 1);
613 nav_to_pose_layout->addWidget(
new QLabel(
"Yaw (radians):"), 3, 0);
614 nav_to_pose_yaw_ =
new QDoubleSpinBox;
615 nav_to_pose_yaw_->setRange(-M_PI, M_PI);
616 nav_to_pose_yaw_->setDecimals(4);
617 nav_to_pose_yaw_->setSingleStep(0.01);
618 nav_to_pose_layout->addWidget(nav_to_pose_yaw_, 3, 1);
620 QObject::connect(start_nav_to_pose_button_,
621 &QPushButton::clicked,
this, &Nav2Panel::onSendNavToPose);
622 nav_to_pose_layout->addWidget(start_nav_to_pose_button_, 4, 0, 1, 2);
624 nav_to_pose_tab->setLayout(nav_to_pose_layout);
625 tools_tab_widget_->addTab(nav_to_pose_tab,
"NavigateToPose");
628 QWidget * nav_wp_tab =
new QWidget;
629 QVBoxLayout * nav_wp_layout =
new QVBoxLayout;
631 QLabel * poses_info =
new QLabel(
"Accumulated poses:");
632 nav_wp_layout->addWidget(poses_info);
634 nav_through_poses_tabs_ =
new QTabWidget;
635 nav_wp_layout->addWidget(nav_through_poses_tabs_);
637 QHBoxLayout * pose_buttons_layout =
new QHBoxLayout;
638 QObject::connect(add_pose_button_, &QPushButton::clicked,
this, &Nav2Panel::onAddNavThroughPose);
639 pose_buttons_layout->addWidget(add_pose_button_);
641 QObject::connect(remove_pose_button_,
642 &QPushButton::clicked,
this, &Nav2Panel::onRemoveNavThroughPose);
643 pose_buttons_layout->addWidget(remove_pose_button_);
645 nav_wp_layout->addLayout(pose_buttons_layout);
647 QHBoxLayout * file_buttons_layout =
new QHBoxLayout;
648 file_buttons_layout->addWidget(save_waypoints_button_);
649 file_buttons_layout->addWidget(load_waypoints_button_);
650 nav_wp_layout->addLayout(file_buttons_layout);
653 QLabel * wp_options_label =
new QLabel(
"<b>Waypoint Following Options:</b>");
654 nav_wp_layout->addWidget(wp_options_label);
656 QHBoxLayout * wp_controls_layout =
new QHBoxLayout;
657 wp_controls_layout->addWidget(pause_waypoint_button_);
658 nav_wp_layout->addLayout(wp_controls_layout);
660 QHBoxLayout * wp_loop_layout =
new QHBoxLayout;
661 wp_loop_layout->addWidget(number_of_loops_);
662 wp_loop_layout->addWidget(nr_of_loops_);
663 wp_loop_layout->addWidget(store_initial_pose_checkbox_);
664 nav_wp_layout->addLayout(wp_loop_layout);
666 nav_wp_tab->setLayout(nav_wp_layout);
667 tools_tab_widget_->addTab(nav_wp_tab,
"NavigateThroughPoses / Waypoint Following");
669 tools_tab_widget_->setTabEnabled(0,
false);
670 tools_tab_widget_->setTabEnabled(1,
false);
672 tools_tab_widget_->setCurrentIndex(0);
674 main_layout->addWidget(tools_tab_widget_);
675 main_layout->setContentsMargins(10, 10, 10, 10);
676 setLayout(main_layout);
678 navigation_action_client_ =
679 rclcpp_action::create_client<nav2_msgs::action::NavigateToPose>(
682 waypoint_follower_action_client_ =
683 rclcpp_action::create_client<nav2_msgs::action::FollowWaypoints>(
686 nav_through_poses_action_client_ =
687 rclcpp_action::create_client<nav2_msgs::action::NavigateThroughPoses>(
689 "navigate_through_poses");
692 tf2_buffer_ = nav2::create_transform_buffer(client_node_);
693 transform_listener_ = nav2::create_transform_listener(*tf2_buffer_);
695 navigation_goal_ = nav2_msgs::action::NavigateToPose::Goal();
696 waypoint_follower_goal_ = nav2_msgs::action::FollowWaypoints::Goal();
697 nav_through_poses_goal_ = nav2_msgs::action::NavigateThroughPoses::Goal();
699 wp_navigation_markers_pub_ =
700 client_node_->create_publisher<visualization_msgs::msg::MarkerArray>(
702 rclcpp::QoS(1).transient_local());
705 &GoalUpdater, SIGNAL(updateGoal(
double,
double,
double,QString)),
706 this, SLOT(onNewGoal(
double,
double,
double,QString)));
709 Nav2Panel::~Nav2Panel()
713 void Nav2Panel::initialStateHandler()
715 if (store_initial_pose_checkbox_->isChecked()) {
716 store_initial_pose_ =
true;
719 store_initial_pose_ =
false;
723 bool Nav2Panel::isLoopValueValid(std::string & loop_value)
726 if (loop_value.empty()) {
727 std::cout <<
"Loop value cannot be set to empty, setting to 0" << std::endl;
729 nr_of_loops_->setText(
"0");
734 for (
char & c : loop_value) {
735 if (isalpha(c) || isspace(c) || ispunct(c)) {
736 waypoint_status_indicator_->setText(
737 "<b> Note: </b> Set a valid value for the loop");
738 std::cout <<
"Set a valid value for the loop, check for alphabets and spaces" << std::endl;
739 navigation_mode_button_->setEnabled(
false);
746 }
catch (std::invalid_argument
const & ex) {
748 waypoint_status_indicator_->setText(
"<b> Note: </b> Set a valid value for the loop");
749 navigation_mode_button_->setEnabled(
false);
751 }
catch (std::out_of_range
const & ex) {
753 waypoint_status_indicator_->setText(
754 "<b> Note: </b> Loop value out of range, setting max possible value"
756 loop_value = std::to_string(std::numeric_limits<int>::max());
757 nr_of_loops_->setText(QString::fromStdString(loop_value));
762 void Nav2Panel::loophandler()
764 loop_no_ = nr_of_loops_->displayText().toStdString();
767 if (!isLoopValueValid(loop_no_)) {
772 navigation_mode_button_->setEnabled(
true);
775 if (!loop_no_.empty() && stoi(loop_no_) > 0) {
776 pause_resume_button_->setEnabled(
false);
778 pause_resume_button_->setEnabled(
true);
782 void Nav2Panel::handleGoalLoader()
784 std::cout <<
"Loading Waypoints!" << std::endl;
786 QString file = QFileDialog::getOpenFileName(
789 tr(
"yaml(*.yaml);;All Files (*)"));
791 YAML::Node available_waypoints;
794 available_waypoints = YAML::LoadFile(file.toStdString());
795 }
catch (
const std::exception & ex) {
796 std::cout << ex.what() <<
", please select a valid file" << std::endl;
800 const YAML::Node & waypoint_iter = available_waypoints[
"waypoints"];
801 for (YAML::const_iterator it = waypoint_iter.begin(); it != waypoint_iter.end(); ++it) {
802 auto waypoint = waypoint_iter[it->first.as<std::string>()];
803 auto pose = waypoint[
"pose"].as<std::vector<double>>();
804 auto orientation = waypoint[
"orientation"].as<std::vector<double>>();
805 acummulated_poses_.goals.push_back(convert_to_msg(pose, orientation));
808 syncTabsWithAccumulatedPoses();
809 updateWpNavigationMarkers();
812 geometry_msgs::msg::PoseStamped Nav2Panel::convert_to_msg(
813 std::vector<double> pose,
814 std::vector<double> orientation)
816 auto msg = geometry_msgs::msg::PoseStamped();
818 msg.header.frame_id =
"map";
821 msg.pose.position.x = pose[0];
822 msg.pose.position.y = pose[1];
823 msg.pose.position.z = pose[2];
825 msg.pose.orientation.w = orientation[0];
826 msg.pose.orientation.x = orientation[1];
827 msg.pose.orientation.y = orientation[2];
828 msg.pose.orientation.z = orientation[3];
833 void Nav2Panel::handleGoalSaver()
837 if (acummulated_poses_.goals.empty()) {
838 std::cout <<
"No accumulated Points to Save!" << std::endl;
841 std::cout <<
"Saving Waypoints!" << std::endl;
845 out << YAML::BeginMap;
846 out << YAML::Key <<
"waypoints";
847 out << YAML::BeginMap;
850 for (
unsigned int i = 0; i < acummulated_poses_.goals.size(); ++i) {
851 out << YAML::Key <<
"waypoint" + std::to_string(i);
852 out << YAML::BeginMap;
853 out << YAML::Key <<
"pose";
854 std::vector<double> pose =
855 {acummulated_poses_.goals[i].pose.position.x, acummulated_poses_.goals[i].pose.position.y,
856 acummulated_poses_.goals[i].pose.position.z};
857 out << YAML::Value << pose;
858 out << YAML::Key <<
"orientation";
859 std::vector<double> orientation =
860 {acummulated_poses_.goals[i].pose.orientation.w, acummulated_poses_.goals[i].pose.orientation.x,
861 acummulated_poses_.goals[i].pose.orientation.y,
862 acummulated_poses_.goals[i].pose.orientation.z};
863 out << YAML::Value << orientation;
868 QString file = QFileDialog::getSaveFileName(
871 tr(
"yaml(*.yaml);;All Files (*)"));
873 if (!file.toStdString().empty()) {
874 std::ofstream fout(file.toStdString() +
".yaml");
876 std::cout <<
"Saving waypoints succeeded" << std::endl;
878 std::cout <<
"Saving waypoints aborted" << std::endl;
883 Nav2Panel::onInitialize()
885 node_ptr_ = getDisplayContext()->getRosNodeAbstraction().lock();
886 if (node_ptr_ ==
nullptr) {
889 rclcpp::get_logger(
"nav2_panel"),
890 "Underlying ROS node no longer exists, initialization failed");
893 rclcpp::Node::SharedPtr node = node_ptr_->get_raw_node();
896 node->declare_parameter(
"base_frame", rclcpp::ParameterValue(std::string(
"base_footprint")));
897 node->get_parameter(
"base_frame", base_frame_);
900 navigation_feedback_sub_ =
901 node->create_subscription<nav2_msgs::action::NavigateToPose::Impl::FeedbackMessage>(
902 "navigate_to_pose/_action/feedback",
903 rclcpp::SystemDefaultsQoS(),
904 [
this](
const nav2_msgs::action::NavigateToPose::Impl::FeedbackMessage::ConstSharedPtr & msg) {
905 if (stoi(nr_of_loops_->displayText().toStdString()) > 0) {
906 if (goal_index_ == 0 && !loop_counter_stop_) {
908 loop_counter_stop_ =
true;
910 if (goal_index_ != 0) {
911 loop_counter_stop_ =
false;
913 navigation_feedback_indicator_->setText(
914 getNavToPoseFeedbackLabel(msg->feedback) + QString(
916 "</td></tr><tr><td width=150>Waypoint:</td><td>" +
917 toString(goal_index_ + 1)).c_str()) + QString(
919 "</td></tr><tr><td width=150>Loop:</td><td>" +
920 toString(loop_count_)).c_str()));
922 navigation_feedback_indicator_->setText(getNavToPoseFeedbackLabel(msg->feedback));
925 nav_through_poses_feedback_sub_ =
926 node->create_subscription<nav2_msgs::action::NavigateThroughPoses::Impl::FeedbackMessage>(
927 "navigate_through_poses/_action/feedback",
928 rclcpp::SystemDefaultsQoS(),
929 [
this](
const nav2_msgs::action::NavigateThroughPoses::Impl::FeedbackMessage::ConstSharedPtr &
931 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel(msg->feedback));
935 navigation_goal_status_sub_ = node->create_subscription<action_msgs::msg::GoalStatusArray>(
936 "navigate_to_pose/_action/status",
937 rclcpp::SystemDefaultsQoS(),
938 [
this](
const action_msgs::msg::GoalStatusArray::ConstSharedPtr & msg) {
939 navigation_goal_status_indicator_->setText(
940 nav2_rviz_plugins::getGoalStatusLabel(
"Feedback", msg->status_list.back().status));
943 loop_count_ == stoi(nr_of_loops_->displayText().toStdString()) &&
944 goal_index_ ==
static_cast<int>(store_poses_.goals.size()) - 1 &&
945 msg->status_list.back().status == action_msgs::msg::GoalStatus::STATUS_SUCCEEDED)
947 store_poses_ = nav_msgs::msg::Goals();
948 waypoint_status_indicator_->clear();
951 navigation_feedback_indicator_->setText(getNavToPoseFeedbackLabel());
954 nav_through_poses_goal_status_sub_ = node->create_subscription<action_msgs::msg::GoalStatusArray>(
955 "navigate_through_poses/_action/status",
956 rclcpp::SystemDefaultsQoS(),
957 [
this](
const action_msgs::msg::GoalStatusArray::ConstSharedPtr & msg) {
958 navigation_goal_status_indicator_->setText(
959 nav2_rviz_plugins::getGoalStatusLabel(
"Feedback", msg->status_list.back().status));
960 if (msg->status_list.back().status != action_msgs::msg::GoalStatus::STATUS_EXECUTING) {
961 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
967 Nav2Panel::startThread()
970 initial_thread_->start();
976 QFuture<bool> futureNav =
980 client_nav_.get(), std::placeholders::_1), server_timeout_);
981 QFuture<bool> futureLoc =
985 client_loc_.get(), std::placeholders::_1), server_timeout_);
989 Nav2Panel::onResume()
991 QFuture<bool> futureNav =
995 client_nav_.get(), std::placeholders::_1), server_timeout_);
996 QFuture<bool> futureLoc =
1000 client_loc_.get(), std::placeholders::_1), server_timeout_);
1006 tools_tab_widget_->setTabEnabled(0,
true);
1007 tools_tab_widget_->setTabEnabled(1,
false);
1008 tools_tab_widget_->setTabEnabled(2,
false);
1009 tools_tab_widget_->setCurrentIndex(0);
1013 Nav2Panel::onStartup()
1015 QFuture<bool> futureNav =
1019 client_nav_.get(), std::placeholders::_1), server_timeout_);
1020 QFuture<bool> futureLoc =
1024 client_loc_.get(), std::placeholders::_1), server_timeout_);
1028 Nav2Panel::onShutdown()
1030 QFuture<bool> futureNav =
1034 client_nav_.get(), std::placeholders::_1), server_timeout_);
1035 QFuture<bool> futureLoc =
1039 client_loc_.get(), std::placeholders::_1), server_timeout_);
1044 Nav2Panel::onCancel()
1046 QFuture<void> future =
1049 &Nav2Panel::onCancelButtonPressed,
1051 waypoint_status_indicator_->clear();
1052 store_poses_ = nav_msgs::msg::Goals();
1053 acummulated_poses_ = nav_msgs::msg::Goals();
1056 void Nav2Panel::onResumedWp()
1058 QFuture<void> future =
1061 &Nav2Panel::onCancelButtonPressed,
1063 acummulated_poses_ = store_poses_;
1064 loop_no_ = std::to_string(
1065 stoi(nr_of_loops_->displayText().toStdString()) -
1067 waypoint_status_indicator_->setText(
1068 QString(std::string(
"<b> Note: </b> Navigation is paused.").c_str()));
1072 Nav2Panel::onNewGoal(
double x,
double y,
double theta, QString frame)
1074 auto pose = geometry_msgs::msg::PoseStamped();
1077 pose.header.frame_id = frame.toStdString();
1078 pose.pose.position.x = x;
1079 pose.pose.position.y = y;
1080 pose.pose.position.z = 0.0;
1081 pose.pose.orientation = orientationAroundZAxis(theta);
1083 if (store_poses_.goals.empty()) {
1084 if (state_machine_.configuration().contains(accumulating_)) {
1085 waypoint_status_indicator_->clear();
1086 acummulated_poses_.goals.push_back(pose);
1087 syncTabsWithAccumulatedPoses();
1089 acummulated_poses_ = nav_msgs::msg::Goals();
1090 updateWpNavigationMarkers();
1091 std::cout <<
"Start navigation" << std::endl;
1092 startNavigation(pose);
1095 waypoint_status_indicator_->setText(
1096 QString(std::string(
"<b> Note: </b> Cannot set goal in pause state").c_str()));
1098 updateWpNavigationMarkers();
1102 Nav2Panel::onCancelButtonPressed()
1104 if (navigation_goal_handle_) {
1105 auto future_cancel = navigation_action_client_->async_cancel_goal(navigation_goal_handle_);
1107 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1108 rclcpp::FutureReturnCode::SUCCESS)
1110 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel goal");
1112 navigation_goal_handle_.reset();
1116 if (waypoint_follower_goal_handle_) {
1117 auto future_cancel =
1118 waypoint_follower_action_client_->async_cancel_goal(waypoint_follower_goal_handle_);
1120 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1121 rclcpp::FutureReturnCode::SUCCESS)
1123 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel waypoint follower");
1125 waypoint_follower_goal_handle_.reset();
1129 if (nav_through_poses_goal_handle_) {
1130 auto future_cancel =
1131 nav_through_poses_action_client_->async_cancel_goal(nav_through_poses_goal_handle_);
1133 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1134 rclcpp::FutureReturnCode::SUCCESS)
1136 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel nav through pose action");
1138 nav_through_poses_goal_handle_.reset();
1146 Nav2Panel::onAccumulatedWp()
1148 updateAccumulatedPosesFromTabs();
1150 if (acummulated_poses_.goals.empty()) {
1151 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1152 waypoint_status_indicator_->setText(
1153 "<b> Note: </b> Uh oh! Someone forgot to select the waypoints");
1158 if (!isLoopValueValid(loop_no_)) {
1159 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1164 waypoint_status_indicator_->clear();
1167 navigation_mode_button_->setEnabled(
false);
1168 pause_resume_button_->setEnabled(
false);
1172 if (store_poses_.goals.empty()) {
1173 std::cout <<
"Start waypoint" << std::endl;
1176 nr_of_loops_->setText(QString::fromStdString(loop_no_));
1179 geometry_msgs::msg::TransformStamped init_transform;
1182 if (store_initial_pose_) {
1184 init_transform = tf2_buffer_->lookupTransform(
1185 acummulated_poses_.goals[0].header.frame_id, base_frame_,
1186 tf2::TimePointZero);
1187 }
catch (
const tf2::TransformException & ex) {
1189 client_node_->get_logger(),
"Could not transform %s to %s: %s",
1190 acummulated_poses_.goals[0].header.frame_id.c_str(), base_frame_.c_str(), ex.what());
1195 geometry_msgs::msg::PoseStamped initial_pose;
1196 initial_pose.header = init_transform.header;
1197 initial_pose.pose.position.x = init_transform.transform.translation.x;
1198 initial_pose.pose.position.y = init_transform.transform.translation.y;
1199 initial_pose.pose.position.z = init_transform.transform.translation.z;
1200 initial_pose.pose.orientation.x = init_transform.transform.rotation.x;
1201 initial_pose.pose.orientation.y = init_transform.transform.rotation.y;
1202 initial_pose.pose.orientation.z = init_transform.transform.rotation.z;
1203 initial_pose.pose.orientation.w = init_transform.transform.rotation.w;
1206 acummulated_poses_.goals.insert(acummulated_poses_.goals.begin(), initial_pose);
1207 syncTabsWithAccumulatedPoses();
1208 updateWpNavigationMarkers();
1209 initial_pose_stored_ =
true;
1210 if (loop_count_ == 0) {
1213 }
else if (!store_initial_pose_ && initial_pose_stored_) {
1214 acummulated_poses_.goals.erase(
1215 acummulated_poses_.goals.begin(),
1216 acummulated_poses_.goals.begin());
1219 std::cout <<
"Resuming waypoint" << std::endl;
1222 startWaypointFollowing(acummulated_poses_.goals);
1223 store_poses_ = acummulated_poses_;
1227 Nav2Panel::onAccumulatedNTP()
1229 updateAccumulatedPosesFromTabs();
1231 std::cout <<
"Start navigate through poses" << std::endl;
1232 startNavThroughPoses(acummulated_poses_);
1236 Nav2Panel::onAccumulating()
1238 acummulated_poses_ = nav_msgs::msg::Goals();
1239 store_poses_ = nav_msgs::msg::Goals();
1242 initial_pose_stored_ =
false;
1243 loop_counter_stop_ =
true;
1245 updateWpNavigationMarkers();
1246 syncTabsWithAccumulatedPoses();
1248 nav_to_pose_frame_id_->setText(
"map");
1249 nav_to_pose_x_->setValue(0.0);
1250 nav_to_pose_y_->setValue(0.0);
1251 nav_to_pose_yaw_->setValue(0.0);
1253 tools_tab_widget_->setTabEnabled(0,
false);
1254 tools_tab_widget_->setTabEnabled(1,
true);
1255 tools_tab_widget_->setCurrentIndex(1);
1258 Nav2Panel::timerEvent(QTimerEvent * event)
1260 if (state_machine_.configuration().contains(accumulated_wp_)) {
1261 if (event->timerId() == timer_.timerId()) {
1262 if (!waypoint_follower_goal_handle_) {
1263 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1264 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1268 executor_->spin_some();
1269 auto status = waypoint_follower_goal_handle_->get_status();
1272 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1273 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1275 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1277 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1278 acummulated_poses_ = nav_msgs::msg::Goals();
1279 updateWpNavigationMarkers();
1283 }
else if (state_machine_.configuration().contains(accumulated_nav_through_poses_)) {
1284 if (event->timerId() == timer_.timerId()) {
1285 if (!nav_through_poses_goal_handle_) {
1286 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1287 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1291 executor_->spin_some();
1292 auto status = nav_through_poses_goal_handle_->get_status();
1295 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1296 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1298 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1300 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1301 acummulated_poses_ = nav_msgs::msg::Goals();
1302 updateWpNavigationMarkers();
1307 if (event->timerId() == timer_.timerId()) {
1308 if (!navigation_goal_handle_) {
1309 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1310 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1314 executor_->spin_some();
1315 auto status = navigation_goal_handle_->get_status();
1318 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1319 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1321 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1323 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1324 nav_to_pose_frame_id_->setText(
"map");
1325 nav_to_pose_x_->setValue(0.0);
1326 nav_to_pose_y_->setValue(0.0);
1327 nav_to_pose_yaw_->setValue(0.0);
1335 Nav2Panel::startWaypointFollowing(std::vector<geometry_msgs::msg::PoseStamped> poses)
1337 auto is_action_server_ready =
1338 waypoint_follower_action_client_->wait_for_action_server(std::chrono::seconds(5));
1339 if (!is_action_server_ready) {
1341 client_node_->get_logger(),
"follow_waypoints action server is not available."
1342 " Is the initial pose set?");
1347 waypoint_follower_goal_.poses = poses;
1348 waypoint_follower_goal_.goal_index = goal_index_;
1349 waypoint_follower_goal_.number_of_loops = stoi(loop_no_);
1352 client_node_->get_logger(),
"Sending a path of %zu waypoints:",
1353 waypoint_follower_goal_.poses.size());
1354 for (
auto waypoint : waypoint_follower_goal_.poses) {
1356 client_node_->get_logger(),
1357 "\t(%lf, %lf)", waypoint.pose.position.x, waypoint.pose.position.y);
1361 auto send_goal_options =
1362 nav2::ActionClient<nav2_msgs::action::FollowWaypoints>::SendGoalOptions();
1363 send_goal_options.result_callback = [
this](
auto) {
1364 waypoint_follower_goal_handle_.reset();
1367 send_goal_options.feedback_callback = [
this](
1368 WaypointFollowerGoalHandle::SharedPtr ,
1369 const std::shared_ptr<const nav2_msgs::action::FollowWaypoints::Feedback> feedback) {
1370 goal_index_ = feedback->current_waypoint;
1373 auto future_goal_handle =
1374 waypoint_follower_action_client_->async_send_goal(waypoint_follower_goal_, send_goal_options);
1375 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1376 rclcpp::FutureReturnCode::SUCCESS)
1378 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1383 waypoint_follower_goal_handle_ = future_goal_handle.get();
1384 if (!waypoint_follower_goal_handle_) {
1385 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1389 timer_.start(200,
this);
1393 Nav2Panel::startNavThroughPoses(nav_msgs::msg::Goals poses)
1395 auto is_action_server_ready =
1396 nav_through_poses_action_client_->wait_for_action_server(std::chrono::seconds(5));
1397 if (!is_action_server_ready) {
1399 client_node_->get_logger(),
"navigate_through_poses action server is not available."
1400 " Is the initial pose set?");
1404 nav_through_poses_goal_.poses = poses;
1405 nav_through_poses_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1407 if (nav_through_poses_goal_.behavior_tree.empty()) {
1409 client_node_->get_logger(),
1410 "NavigateThroughPoses will be called using the BT Navigator's default behavior tree.");
1413 client_node_->get_logger(),
1414 "NavigateThroughPoses will be called using behavior tree: %s",
1415 nav_through_poses_goal_.behavior_tree.c_str());
1419 client_node_->get_logger(),
"Sending a path of %zu waypoints:",
1420 nav_through_poses_goal_.poses.goals.size());
1421 for (
auto waypoint : nav_through_poses_goal_.poses.goals) {
1423 client_node_->get_logger(),
1424 "\t(%lf, %lf)", waypoint.pose.position.x, waypoint.pose.position.y);
1428 auto send_goal_options =
1429 nav2::ActionClient<nav2_msgs::action::NavigateThroughPoses>::SendGoalOptions();
1430 send_goal_options.result_callback = [
this](
auto) {
1431 nav_through_poses_goal_handle_.reset();
1434 auto future_goal_handle =
1435 nav_through_poses_action_client_->async_send_goal(nav_through_poses_goal_, send_goal_options);
1436 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1437 rclcpp::FutureReturnCode::SUCCESS)
1439 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1444 nav_through_poses_goal_handle_ = future_goal_handle.get();
1445 if (!nav_through_poses_goal_handle_) {
1446 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1450 timer_.start(200,
this);
1454 Nav2Panel::startNavigation(geometry_msgs::msg::PoseStamped pose)
1456 auto is_action_server_ready =
1457 navigation_action_client_->wait_for_action_server(std::chrono::seconds(5));
1458 if (!is_action_server_ready) {
1460 client_node_->get_logger(),
1461 "navigate_to_pose action server is not available."
1462 " Is the initial pose set?");
1467 navigation_goal_.pose = pose;
1468 navigation_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1470 if (navigation_goal_.behavior_tree.empty()) {
1472 client_node_->get_logger(),
1473 "NavigateToPose will be called using the BT Navigator's default behavior tree.");
1476 client_node_->get_logger(),
1477 "NavigateToPose will be called using behavior tree: %s",
1478 navigation_goal_.behavior_tree.c_str());
1482 auto send_goal_options =
1483 nav2::ActionClient<nav2_msgs::action::NavigateToPose>::SendGoalOptions();
1484 send_goal_options.result_callback = [
this](
auto) {
1485 navigation_goal_handle_.reset();
1488 auto future_goal_handle =
1489 navigation_action_client_->async_send_goal(navigation_goal_, send_goal_options);
1490 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1491 rclcpp::FutureReturnCode::SUCCESS)
1493 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1498 navigation_goal_handle_ = future_goal_handle.get();
1499 if (!navigation_goal_handle_) {
1500 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1504 timer_.start(200,
this);
1508 Nav2Panel::save(rviz_common::Config config)
const
1510 Panel::save(config);
1514 Nav2Panel::load(
const rviz_common::Config & config)
1516 Panel::load(config);
1520 Nav2Panel::resetUniqueId()
1526 Nav2Panel::getUniqueId()
1528 int temp_id = unique_id;
1534 Nav2Panel::updateWpNavigationMarkers()
1538 auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
1540 visualization_msgs::msg::Marker clear_all_marker;
1541 clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
1542 marker_array->markers.push_back(clear_all_marker);
1544 wp_navigation_markers_pub_->publish(std::move(marker_array));
1546 marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
1548 for (
size_t i = 0; i < acummulated_poses_.goals.size(); i++) {
1550 visualization_msgs::msg::Marker arrow_marker;
1551 arrow_marker.header = acummulated_poses_.goals[i].header;
1552 arrow_marker.id = getUniqueId();
1553 arrow_marker.type = visualization_msgs::msg::Marker::ARROW;
1554 arrow_marker.action = visualization_msgs::msg::Marker::ADD;
1555 arrow_marker.pose = acummulated_poses_.goals[i].pose;
1556 arrow_marker.scale.x = 0.3;
1557 arrow_marker.scale.y = 0.05;
1558 arrow_marker.scale.z = 0.02;
1559 arrow_marker.color.r = 0;
1560 arrow_marker.color.g = 255;
1561 arrow_marker.color.b = 0;
1562 arrow_marker.color.a = 1.0f;
1563 arrow_marker.lifetime = rclcpp::Duration(0s);
1564 arrow_marker.frame_locked =
false;
1565 marker_array->markers.push_back(arrow_marker);
1568 visualization_msgs::msg::Marker circle_marker;
1569 circle_marker.header = acummulated_poses_.goals[i].header;
1570 circle_marker.id = getUniqueId();
1571 circle_marker.type = visualization_msgs::msg::Marker::SPHERE;
1572 circle_marker.action = visualization_msgs::msg::Marker::ADD;
1573 circle_marker.pose = acummulated_poses_.goals[i].pose;
1574 circle_marker.scale.x = 0.05;
1575 circle_marker.scale.y = 0.05;
1576 circle_marker.scale.z = 0.05;
1577 circle_marker.color.r = 255;
1578 circle_marker.color.g = 0;
1579 circle_marker.color.b = 0;
1580 circle_marker.color.a = 1.0f;
1581 circle_marker.lifetime = rclcpp::Duration(0s);
1582 circle_marker.frame_locked =
false;
1583 marker_array->markers.push_back(circle_marker);
1586 visualization_msgs::msg::Marker marker_text;
1587 marker_text.header = acummulated_poses_.goals[i].header;
1588 marker_text.id = getUniqueId();
1589 marker_text.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
1590 marker_text.action = visualization_msgs::msg::Marker::ADD;
1591 marker_text.pose = acummulated_poses_.goals[i].pose;
1592 marker_text.pose.position.z += 0.2;
1593 marker_text.scale.x = 0.07;
1594 marker_text.scale.y = 0.07;
1595 marker_text.scale.z = 0.07;
1596 marker_text.color.r = 0;
1597 marker_text.color.g = 255;
1598 marker_text.color.b = 0;
1599 marker_text.color.a = 1.0f;
1600 marker_text.lifetime = rclcpp::Duration(0s);
1601 marker_text.frame_locked =
false;
1602 marker_text.text =
"wp_" + std::to_string(i + 1);
1603 marker_array->markers.push_back(marker_text);
1606 wp_navigation_markers_pub_->publish(std::move(marker_array));
1610 Nav2Panel::getNavToPoseFeedbackLabel(nav2_msgs::action::NavigateToPose::Feedback msg)
1612 return QString(std::string(
"<table>" + toLabel(msg) +
"</table>").c_str());
1616 Nav2Panel::getNavThroughPosesFeedbackLabel(nav2_msgs::action::NavigateThroughPoses::Feedback msg)
1620 "<table><tr><td width=150>Poses remaining:</td><td>" +
1621 std::to_string(msg.number_of_poses_remaining) +
1622 "</td></tr>" + toLabel(msg) +
"</table>").c_str());
1625 template<
typename T>
1626 inline std::string Nav2Panel::toLabel(T & msg)
1629 "<tr><td width=150>ETA:</td><td>" +
1630 toString(rclcpp::Duration(msg.estimated_time_remaining).seconds(), 0) +
" s"
1631 "</td></tr><tr><td width=150>Distance remaining:</td><td>" +
1632 toString(msg.distance_remaining, 2) +
" m"
1633 "</td></tr><tr><td width=150>Position error:</td><td>" +
1634 toString(msg.position_tracking_error, 2) +
" m"
1635 "</td></tr><tr><td width=150>Heading error:</td><td>" +
1636 toString(msg.heading_tracking_error, 2) +
" rad"
1637 "</td></tr><tr><td width=150>Time taken:</td><td>" +
1638 toString(rclcpp::Duration(msg.navigation_time).seconds(), 0) +
" s"
1639 "</td></tr><tr><td width=150>Recoveries:</td><td>" +
1640 std::to_string(msg.number_of_recoveries) +
1645 Nav2Panel::toString(
double val,
int precision)
1647 std::ostringstream out;
1648 out.precision(precision);
1649 out << std::fixed << val;
1654 Nav2Panel::onSendNavToPose()
1656 auto pose = geometry_msgs::msg::PoseStamped();
1657 pose.header.frame_id = nav_to_pose_frame_id_->text().toStdString();
1658 pose.header.stamp = client_node_->now();
1659 pose.pose.position.x = nav_to_pose_x_->value();
1660 pose.pose.position.y = nav_to_pose_y_->value();
1661 pose.pose.position.z = 0.0;
1662 pose.pose.orientation = orientationAroundZAxis(nav_to_pose_yaw_->value());
1664 auto is_action_server_ready =
1665 navigation_action_client_->wait_for_action_server(std::chrono::seconds(5));
1666 if (!is_action_server_ready) {
1668 client_node_->get_logger(),
1669 "navigate_to_pose action server is not available.");
1673 navigation_goal_.pose = pose;
1674 navigation_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1676 auto send_goal_options =
1677 nav2::ActionClient<nav2_msgs::action::NavigateToPose>::SendGoalOptions();
1678 send_goal_options.result_callback = [
this](
auto) {
1679 navigation_goal_handle_.reset();
1682 auto future_goal_handle =
1683 navigation_action_client_->async_send_goal(navigation_goal_, send_goal_options);
1684 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1685 rclcpp::FutureReturnCode::SUCCESS)
1687 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1691 navigation_goal_handle_ = future_goal_handle.get();
1692 if (!navigation_goal_handle_) {
1693 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1697 timer_.start(200,
this);
1701 Nav2Panel::onAddNavThroughPose()
1703 int index = nav_through_pose_tabs_.size();
1704 createNavThroughPoseTab(index);
1705 updateAccumulatedPosesFromTabs();
1709 Nav2Panel::onRemoveNavThroughPose()
1711 if (!nav_through_pose_tabs_.empty()) {
1712 int index = nav_through_poses_tabs_->currentIndex();
1713 if (index >= 0 && index <
static_cast<int>(nav_through_pose_tabs_.size())) {
1714 nav_through_poses_tabs_->removeTab(index);
1715 nav_through_pose_tabs_.erase(nav_through_pose_tabs_.begin() + index);
1717 for (
int i = 0; i < nav_through_poses_tabs_->count(); ++i) {
1718 nav_through_poses_tabs_->setTabText(i, QString(
"Pose %1").arg(i + 1));
1721 updateAccumulatedPosesFromTabs();
1727 Nav2Panel::createNavThroughPoseTab(
int index)
1729 QWidget * tab_widget =
new QWidget;
1730 QGridLayout * layout =
new QGridLayout;
1732 NavThroughPoseTab tab;
1734 layout->addWidget(
new QLabel(
"Frame ID:"), 0, 0);
1735 tab.frame_id_edit =
new QLineEdit(
"map");
1737 tab.frame_id_edit, &QLineEdit::textChanged,
this,
1738 &Nav2Panel::updateAccumulatedPosesFromTabs);
1739 layout->addWidget(tab.frame_id_edit, 0, 1);
1741 layout->addWidget(
new QLabel(
"Position X:"), 1, 0);
1742 tab.pos_x_spin =
new QDoubleSpinBox;
1743 tab.pos_x_spin->setRange(-1000.0, 1000.0);
1744 tab.pos_x_spin->setDecimals(3);
1745 tab.pos_x_spin->setSingleStep(0.1);
1747 tab.pos_x_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1748 &Nav2Panel::updateAccumulatedPosesFromTabs);
1749 layout->addWidget(tab.pos_x_spin, 1, 1);
1751 layout->addWidget(
new QLabel(
"Position Y:"), 2, 0);
1752 tab.pos_y_spin =
new QDoubleSpinBox;
1753 tab.pos_y_spin->setRange(-1000.0, 1000.0);
1754 tab.pos_y_spin->setDecimals(3);
1755 tab.pos_y_spin->setSingleStep(0.1);
1757 tab.pos_y_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1758 &Nav2Panel::updateAccumulatedPosesFromTabs);
1759 layout->addWidget(tab.pos_y_spin, 2, 1);
1761 layout->addWidget(
new QLabel(
"Yaw (radians):"), 3, 0);
1762 tab.yaw_spin =
new QDoubleSpinBox;
1763 tab.yaw_spin->setRange(-M_PI, M_PI);
1764 tab.yaw_spin->setDecimals(4);
1765 tab.yaw_spin->setSingleStep(0.01);
1767 tab.yaw_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1768 &Nav2Panel::updateAccumulatedPosesFromTabs);
1769 layout->addWidget(tab.yaw_spin, 3, 1);
1771 tab_widget->setLayout(layout);
1773 nav_through_poses_tabs_->addTab(tab_widget, QString(
"Pose %1").arg(index + 1));
1774 nav_through_pose_tabs_.push_back(tab);
1778 Nav2Panel::syncTabsWithAccumulatedPoses()
1780 while (nav_through_poses_tabs_->count() > 0) {
1781 nav_through_poses_tabs_->removeTab(0);
1783 nav_through_pose_tabs_.clear();
1785 for (
size_t i = 0; i < acummulated_poses_.goals.size(); ++i) {
1786 createNavThroughPoseTab(i);
1788 const auto & pose = acummulated_poses_.goals[i];
1790 nav_through_pose_tabs_[i].frame_id_edit->blockSignals(
true);
1791 nav_through_pose_tabs_[i].pos_x_spin->blockSignals(
true);
1792 nav_through_pose_tabs_[i].pos_y_spin->blockSignals(
true);
1793 nav_through_pose_tabs_[i].yaw_spin->blockSignals(
true);
1795 nav_through_pose_tabs_[i].frame_id_edit->setText(
1796 QString::fromStdString(pose.header.frame_id));
1797 nav_through_pose_tabs_[i].pos_x_spin->setValue(pose.pose.position.x);
1798 nav_through_pose_tabs_[i].pos_y_spin->setValue(pose.pose.position.y);
1801 pose.pose.orientation.x,
1802 pose.pose.orientation.y,
1803 pose.pose.orientation.z,
1804 pose.pose.orientation.w);
1805 tf2::Matrix3x3 m(q);
1806 double roll, pitch, yaw;
1807 m.getRPY(roll, pitch, yaw);
1808 nav_through_pose_tabs_[i].yaw_spin->setValue(yaw);
1810 nav_through_pose_tabs_[i].frame_id_edit->blockSignals(
false);
1811 nav_through_pose_tabs_[i].pos_x_spin->blockSignals(
false);
1812 nav_through_pose_tabs_[i].pos_y_spin->blockSignals(
false);
1813 nav_through_pose_tabs_[i].yaw_spin->blockSignals(
false);
1818 Nav2Panel::updateAccumulatedPosesFromTabs()
1820 acummulated_poses_.goals.clear();
1822 for (
const auto & tab : nav_through_pose_tabs_) {
1823 geometry_msgs::msg::PoseStamped pose;
1824 pose.header.frame_id = tab.frame_id_edit->text().toStdString();
1825 pose.header.stamp = client_node_->now();
1826 pose.pose.position.x = tab.pos_x_spin->value();
1827 pose.pose.position.y = tab.pos_y_spin->value();
1828 pose.pose.position.z = 0.0;
1829 pose.pose.orientation = orientationAroundZAxis(tab.yaw_spin->value());
1830 acummulated_poses_.goals.push_back(pose);
1833 updateWpNavigationMarkers();
1838 #include <pluginlib/class_list_macros.hpp>
bool pause(const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Make pause service call.
bool reset(const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Make reset service call.
bool startup(const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Make start up service call.
bool resume(const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Make resume service call.
Panel to interface to the nav2 stack.