15 #include "nav2_rviz_plugins/nav2_panel.hpp"
17 #include <QtConcurrent/QtConcurrent>
18 #include <QVBoxLayout>
19 #include <QHBoxLayout>
31 #include "nav2_rviz_plugins/goal_common.hpp"
32 #include "nav2_rviz_plugins/utils.hpp"
33 #include "rclcpp/rclcpp.hpp"
34 #include "rviz_common/display_context.hpp"
35 #include "rviz_common/load_resource.hpp"
36 #include "yaml-cpp/yaml.h"
37 #include "geometry_msgs/msg/pose.hpp"
39 using namespace std::chrono_literals;
41 namespace nav2_rviz_plugins
43 using nav2_util::geometry_utils::orientationAroundZAxis;
46 GoalPoseUpdater GoalUpdater;
48 Nav2Panel::Nav2Panel(QWidget * parent)
54 start_reset_button_ =
new QPushButton;
55 pause_resume_button_ =
new QPushButton;
56 navigation_mode_button_ =
new QPushButton;
57 add_pose_button_ =
new QPushButton;
58 remove_pose_button_ =
new QPushButton;
59 save_waypoints_button_ =
new QPushButton;
60 load_waypoints_button_ =
new QPushButton;
61 pause_waypoint_button_ =
new QPushButton;
62 start_nav_to_pose_button_ =
new QPushButton;
63 navigation_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>");
95 navigation_status_indicator_->setText(navigation_unknown);
96 navigation_goal_status_indicator_->setText(nav2_rviz_plugins::getGoalStatusLabel());
97 number_of_loops_->setText(
"Num of loops");
98 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
99 navigation_status_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
100 navigation_goal_status_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
101 navigation_feedback_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
102 waypoint_status_indicator_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
104 pre_initial_ =
new QState();
105 pre_initial_->setObjectName(
"pre_initial");
106 pre_initial_->assignProperty(start_reset_button_,
"text",
"Startup");
107 pre_initial_->assignProperty(start_reset_button_,
"enabled",
false);
109 pre_initial_->assignProperty(pause_resume_button_,
"text",
"Pause");
110 pre_initial_->assignProperty(pause_resume_button_,
"enabled",
false);
112 pre_initial_->assignProperty(start_nav_to_pose_button_,
"text",
"Start NavigateToPose");
114 pre_initial_->assignProperty(
115 navigation_mode_button_,
"text",
116 "Waypoint Following / NavigateThroughPoses Mode");
117 pre_initial_->assignProperty(navigation_mode_button_,
"enabled",
false);
119 pre_initial_->assignProperty(add_pose_button_,
"text",
"Add Pose");
120 pre_initial_->assignProperty(add_pose_button_,
"enabled",
false);
121 pre_initial_->assignProperty(remove_pose_button_,
"text",
"Remove Pose");
122 pre_initial_->assignProperty(remove_pose_button_,
"enabled",
false);
124 pre_initial_->assignProperty(save_waypoints_button_,
"text",
"Save");
125 pre_initial_->assignProperty(save_waypoints_button_,
"enabled",
false);
126 pre_initial_->assignProperty(load_waypoints_button_,
"text",
"Load");
127 pre_initial_->assignProperty(load_waypoints_button_,
"enabled",
false);
129 pre_initial_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
130 pre_initial_->assignProperty(pause_waypoint_button_,
"enabled",
false);
132 pre_initial_->assignProperty(nr_of_loops_,
"text",
"0");
133 pre_initial_->assignProperty(nr_of_loops_,
"enabled",
false);
135 pre_initial_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
137 initial_ =
new QState();
138 initial_->setObjectName(
"initial");
139 initial_->assignProperty(start_reset_button_,
"text",
"Startup");
140 initial_->assignProperty(start_reset_button_,
"toolTip", startup_msg);
141 initial_->assignProperty(start_reset_button_,
"enabled",
true);
143 initial_->assignProperty(pause_resume_button_,
"text",
"Pause");
144 initial_->assignProperty(pause_resume_button_,
"enabled",
false);
146 initial_->assignProperty(navigation_mode_button_,
"text",
147 "Waypoint Following / NavigateThroughPoses Mode");
148 initial_->assignProperty(navigation_mode_button_,
"enabled",
false);
150 initial_->assignProperty(add_pose_button_,
"enabled",
false);
151 initial_->assignProperty(remove_pose_button_,
"enabled",
false);
153 initial_->assignProperty(save_waypoints_button_,
"enabled",
false);
154 initial_->assignProperty(load_waypoints_button_,
"enabled",
false);
156 initial_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
157 initial_->assignProperty(pause_waypoint_button_,
"enabled",
false);
159 initial_->assignProperty(nr_of_loops_,
"text",
"0");
160 initial_->assignProperty(nr_of_loops_,
"enabled",
false);
162 initial_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
165 idle_ =
new QState();
166 idle_->setObjectName(
"idle");
167 idle_->assignProperty(start_reset_button_,
"text",
"Reset");
168 idle_->assignProperty(start_reset_button_,
"toolTip", shutdown_msg);
169 idle_->assignProperty(start_reset_button_,
"enabled",
true);
171 idle_->assignProperty(pause_resume_button_,
"text",
"Pause");
172 idle_->assignProperty(pause_resume_button_,
"enabled",
true);
173 idle_->assignProperty(pause_resume_button_,
"toolTip", pause_msg);
175 idle_->assignProperty(navigation_mode_button_,
"text",
176 "Waypoint Following / NavigateThroughPoses Mode");
177 idle_->assignProperty(navigation_mode_button_,
"enabled",
true);
178 idle_->assignProperty(navigation_mode_button_,
"toolTip", single_goal_msg);
180 idle_->assignProperty(add_pose_button_,
"enabled",
false);
181 idle_->assignProperty(remove_pose_button_,
"enabled",
false);
183 idle_->assignProperty(save_waypoints_button_,
"enabled",
false);
184 idle_->assignProperty(load_waypoints_button_,
"enabled",
false);
186 idle_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
187 idle_->assignProperty(pause_waypoint_button_,
"enabled",
false);
189 idle_->assignProperty(nr_of_loops_,
"text",
"0");
190 idle_->assignProperty(nr_of_loops_,
"enabled",
false);
192 idle_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
193 idle_->assignProperty(start_nav_to_pose_button_,
"enabled",
true);
196 accumulating_ =
new QState();
197 accumulating_->setObjectName(
"accumulating");
198 accumulating_->assignProperty(start_reset_button_,
"text",
"Cancel Accumulation");
199 accumulating_->assignProperty(start_reset_button_,
"toolTip", cancel_waypoint_msg);
200 accumulating_->assignProperty(start_reset_button_,
"enabled",
true);
202 accumulating_->assignProperty(pause_resume_button_,
"text",
"Start NavigateThroughPoses");
203 accumulating_->assignProperty(pause_resume_button_,
"enabled",
true);
204 accumulating_->assignProperty(pause_resume_button_,
"toolTip", nft_goal_msg);
206 accumulating_->assignProperty(navigation_mode_button_,
"text",
"Start Waypoint Following");
207 accumulating_->assignProperty(navigation_mode_button_,
"enabled",
true);
208 accumulating_->assignProperty(navigation_mode_button_,
"toolTip", waypoint_goal_msg);
210 accumulating_->assignProperty(add_pose_button_,
"enabled",
true);
211 accumulating_->assignProperty(remove_pose_button_,
"enabled",
true);
213 accumulating_->assignProperty(save_waypoints_button_,
"enabled",
true);
214 accumulating_->assignProperty(load_waypoints_button_,
"enabled",
true);
216 accumulating_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
217 accumulating_->assignProperty(pause_waypoint_button_,
"enabled",
false);
219 accumulating_->assignProperty(nr_of_loops_,
"text", QString::fromStdString(loop_no_));
220 accumulating_->assignProperty(nr_of_loops_,
"enabled",
true);
221 accumulating_->assignProperty(store_initial_pose_checkbox_,
"enabled",
true);
224 accumulated_wp_ =
new QState();
225 accumulated_wp_->setObjectName(
"accumulated_wp");
226 accumulated_wp_->assignProperty(start_reset_button_,
"text",
"Cancel");
227 accumulated_wp_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
228 accumulated_wp_->assignProperty(start_reset_button_,
"enabled",
true);
230 accumulated_wp_->assignProperty(pause_resume_button_,
"text",
"Start NavigateThroughPoses");
231 accumulated_wp_->assignProperty(pause_resume_button_,
"enabled",
false);
232 accumulated_wp_->assignProperty(pause_resume_button_,
"toolTip", nft_goal_msg);
234 accumulated_wp_->assignProperty(navigation_mode_button_,
"text",
"Start Waypoint Following");
235 accumulated_wp_->assignProperty(navigation_mode_button_,
"enabled",
false);
236 accumulated_wp_->assignProperty(navigation_mode_button_,
"toolTip", waypoint_goal_msg);
238 accumulated_wp_->assignProperty(add_pose_button_,
"enabled",
false);
239 accumulated_wp_->assignProperty(remove_pose_button_,
"enabled",
false);
241 accumulated_wp_->assignProperty(save_waypoints_button_,
"enabled",
false);
242 accumulated_wp_->assignProperty(load_waypoints_button_,
"enabled",
false);
244 accumulated_wp_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
245 accumulated_wp_->assignProperty(pause_waypoint_button_,
"enabled",
true);
247 accumulated_wp_->assignProperty(nr_of_loops_,
"text", QString::fromStdString(loop_no_));
248 accumulated_wp_->assignProperty(nr_of_loops_,
"enabled",
false);
249 accumulated_wp_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
252 accumulated_nav_through_poses_ =
new QState();
253 accumulated_nav_through_poses_->setObjectName(
"accumulated_nav_through_poses");
254 accumulated_nav_through_poses_->assignProperty(start_reset_button_,
"text",
"Cancel");
255 accumulated_nav_through_poses_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
257 accumulated_nav_through_poses_->assignProperty(pause_resume_button_,
258 "text",
"Start NavigateThroughPoses");
259 accumulated_nav_through_poses_->assignProperty(pause_resume_button_,
"enabled",
false);
260 accumulated_nav_through_poses_->assignProperty(pause_resume_button_,
"toolTip", nft_goal_msg);
262 accumulated_nav_through_poses_->assignProperty(navigation_mode_button_,
263 "text",
"Start Waypoint Following");
264 accumulated_nav_through_poses_->assignProperty(navigation_mode_button_,
266 accumulated_nav_through_poses_->assignProperty(navigation_mode_button_,
267 "toolTip", waypoint_goal_msg);
269 accumulated_nav_through_poses_->assignProperty(add_pose_button_,
"enabled",
false);
270 accumulated_nav_through_poses_->assignProperty(remove_pose_button_,
"enabled",
false);
272 accumulated_nav_through_poses_->assignProperty(save_waypoints_button_,
"enabled",
false);
273 accumulated_nav_through_poses_->assignProperty(load_waypoints_button_,
"enabled",
false);
275 accumulated_nav_through_poses_->assignProperty(pause_waypoint_button_,
276 "text",
"Pause Waypoint Following");
277 accumulated_nav_through_poses_->assignProperty(pause_waypoint_button_,
"enabled",
false);
279 accumulated_nav_through_poses_->assignProperty(nr_of_loops_,
"enabled",
false);
281 accumulated_nav_through_poses_->assignProperty(start_reset_button_,
"enabled",
true);
282 accumulated_nav_through_poses_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
284 accumulated_nav_through_poses_->assignProperty(
285 nr_of_loops_,
"text",
286 QString::fromStdString(loop_no_));
287 accumulated_nav_through_poses_->assignProperty(nr_of_loops_,
"enabled",
false);
288 accumulated_nav_through_poses_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
291 canceled_ =
new QState();
292 canceled_->setObjectName(
"canceled");
295 reset_ =
new QState();
296 reset_->setObjectName(
"reset");
299 running_ =
new QState();
300 running_->setObjectName(
"running");
301 running_->assignProperty(start_reset_button_,
"text",
"Cancel");
302 running_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
304 running_->assignProperty(pause_resume_button_,
"text",
"Pause");
305 running_->assignProperty(pause_resume_button_,
"enabled",
false);
307 running_->assignProperty(navigation_mode_button_,
308 "text",
"Waypoint Following / NavigateThroughPoses Mode");
309 running_->assignProperty(navigation_mode_button_,
"enabled",
false);
311 running_->assignProperty(start_nav_to_pose_button_,
"enabled",
false);
313 running_->assignProperty(add_pose_button_,
"enabled",
false);
314 running_->assignProperty(remove_pose_button_,
"enabled",
false);
316 running_->assignProperty(save_waypoints_button_,
"enabled",
false);
317 running_->assignProperty(load_waypoints_button_,
"enabled",
false);
319 running_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
320 running_->assignProperty(pause_waypoint_button_,
"enabled",
false);
322 running_->assignProperty(nr_of_loops_,
"text",
"0");
323 running_->assignProperty(nr_of_loops_,
"enabled",
false);
325 running_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
328 paused_ =
new QState();
329 paused_->setObjectName(
"pausing");
330 paused_->assignProperty(start_reset_button_,
"text",
"Reset");
331 paused_->assignProperty(start_reset_button_,
"toolTip", shutdown_msg);
333 paused_->assignProperty(pause_resume_button_,
"text",
"Resume");
334 paused_->assignProperty(pause_resume_button_,
"toolTip", resume_msg);
335 paused_->assignProperty(pause_resume_button_,
"enabled",
true);
337 paused_->assignProperty(navigation_mode_button_,
"text",
"");
338 paused_->assignProperty(navigation_mode_button_,
"enabled",
false);
340 paused_->assignProperty(start_nav_to_pose_button_,
"enabled",
false);
342 paused_->assignProperty(add_pose_button_,
"enabled",
false);
343 paused_->assignProperty(remove_pose_button_,
"enabled",
false);
345 paused_->assignProperty(save_waypoints_button_,
"enabled",
false);
346 paused_->assignProperty(load_waypoints_button_,
"enabled",
false);
348 paused_->assignProperty(pause_waypoint_button_,
"text",
"Pause Waypoint Following");
349 paused_->assignProperty(pause_waypoint_button_,
"enabled",
false);
351 paused_->assignProperty(nr_of_loops_,
"enabled",
false);
353 paused_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
356 resumed_ =
new QState();
357 resumed_->setObjectName(
"resuming");
360 resumed_wp_ =
new QState();
361 resumed_wp_->setObjectName(
"running");
362 resumed_wp_->assignProperty(start_reset_button_,
"text",
"Cancel");
363 resumed_wp_->assignProperty(start_reset_button_,
"toolTip", cancel_msg);
365 resumed_wp_->assignProperty(pause_resume_button_,
"text",
"Start NavigateThroughPoses");
366 resumed_wp_->assignProperty(pause_resume_button_,
"enabled",
false);
368 resumed_wp_->assignProperty(navigation_mode_button_,
"text",
"Start Waypoint Following");
369 resumed_wp_->assignProperty(navigation_mode_button_,
"enabled",
false);
371 resumed_wp_->assignProperty(add_pose_button_,
"enabled",
false);
372 resumed_wp_->assignProperty(remove_pose_button_,
"enabled",
false);
374 resumed_wp_->assignProperty(save_waypoints_button_,
"enabled",
true);
375 resumed_wp_->assignProperty(load_waypoints_button_,
"enabled",
false);
377 resumed_wp_->assignProperty(pause_waypoint_button_,
"text",
"Resume WaypointFollowing");
378 resumed_wp_->assignProperty(pause_waypoint_button_,
"enabled",
true);
380 resumed_wp_->assignProperty(nr_of_loops_,
"enabled",
false);
381 resumed_wp_->assignProperty(store_initial_pose_checkbox_,
"enabled",
false);
383 QObject::connect(initial_, SIGNAL(exited()),
this, SLOT(onStartup()));
384 QObject::connect(canceled_, SIGNAL(exited()),
this, SLOT(onCancel()));
385 QObject::connect(reset_, SIGNAL(exited()),
this, SLOT(onShutdown()));
386 QObject::connect(paused_, SIGNAL(entered()),
this, SLOT(onPause()));
387 QObject::connect(resumed_, SIGNAL(exited()),
this, SLOT(onResume()));
388 QObject::connect(idle_, SIGNAL(entered()),
this, SLOT(onIdle()));
389 QObject::connect(accumulating_, SIGNAL(entered()),
this, SLOT(onAccumulating()));
390 QObject::connect(accumulated_wp_, SIGNAL(entered()),
this, SLOT(onAccumulatedWp()));
391 QObject::connect(resumed_wp_, SIGNAL(entered()),
this, SLOT(onResumedWp()));
393 accumulated_nav_through_poses_, SIGNAL(entered()),
this,
394 SLOT(onAccumulatedNTP()));
396 save_waypoints_button_,
397 &QPushButton::released,
398 this, &Nav2Panel::handleGoalSaver);
400 load_waypoints_button_,
401 &QPushButton::released,
403 &Nav2Panel::handleGoalLoader);
406 &QLineEdit::editingFinished,
408 &Nav2Panel::loophandler);
409 #if QT_VERSION >= QT_VERSION_CHECK(6, 10, 2)
411 store_initial_pose_checkbox_,
412 &QCheckBox::checkStateChanged,
414 &Nav2Panel::initialStateHandler);
417 store_initial_pose_checkbox_,
418 &QCheckBox::stateChanged,
420 &Nav2Panel::initialStateHandler);
424 initial_->addTransition(start_reset_button_, SIGNAL(clicked()), idle_);
425 idle_->addTransition(start_reset_button_, SIGNAL(clicked()), reset_);
426 running_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
427 paused_->addTransition(start_reset_button_, SIGNAL(clicked()), reset_);
428 idle_->addTransition(navigation_mode_button_, SIGNAL(clicked()), accumulating_);
429 accumulating_->addTransition(navigation_mode_button_, SIGNAL(clicked()), accumulated_wp_);
430 accumulating_->addTransition(
431 pause_resume_button_, SIGNAL(
432 clicked()), accumulated_nav_through_poses_);
433 accumulating_->addTransition(start_reset_button_, SIGNAL(clicked()), idle_);
434 accumulated_wp_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
435 accumulated_nav_through_poses_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
438 canceled_->addTransition(canceled_, SIGNAL(entered()), idle_);
439 reset_->addTransition(reset_, SIGNAL(entered()), initial_);
440 resumed_->addTransition(resumed_, SIGNAL(entered()), idle_);
443 idle_->addTransition(pause_resume_button_, SIGNAL(clicked()), paused_);
444 paused_->addTransition(pause_resume_button_, SIGNAL(clicked()), resumed_);
447 accumulated_wp_->addTransition(pause_waypoint_button_, SIGNAL(clicked()), resumed_wp_);
448 resumed_wp_->addTransition(pause_waypoint_button_, SIGNAL(clicked()), accumulated_wp_);
449 resumed_wp_->addTransition(start_reset_button_, SIGNAL(clicked()), canceled_);
454 ROSActionQTransition * idleTransition =
new ROSActionQTransition(QActionState::INACTIVE);
455 idleTransition->setTargetState(running_);
456 idle_->addTransition(idleTransition);
458 ROSActionQTransition * runningTransition =
new ROSActionQTransition(QActionState::ACTIVE);
459 runningTransition->setTargetState(idle_);
460 running_->addTransition(runningTransition);
462 ROSActionQTransition * idleAccumulatedWpTransition =
463 new ROSActionQTransition(QActionState::INACTIVE);
464 idleAccumulatedWpTransition->setTargetState(accumulated_wp_);
465 idle_->addTransition(idleAccumulatedWpTransition);
467 ROSActionQTransition * accumulatedWpTransition =
new ROSActionQTransition(QActionState::ACTIVE);
468 accumulatedWpTransition->setTargetState(idle_);
469 accumulated_wp_->addTransition(accumulatedWpTransition);
471 ROSActionQTransition * idleAccumulatedNTPTransition =
472 new ROSActionQTransition(QActionState::INACTIVE);
473 idleAccumulatedNTPTransition->setTargetState(accumulated_nav_through_poses_);
474 idle_->addTransition(idleAccumulatedNTPTransition);
476 ROSActionQTransition * accumulatedNTPTransition =
new ROSActionQTransition(QActionState::ACTIVE);
477 accumulatedNTPTransition->setTargetState(idle_);
478 accumulated_nav_through_poses_->addTransition(accumulatedNTPTransition);
480 auto options = rclcpp::NodeOptions().arguments(
481 {
"--ros-args",
"--remap",
"__node:=rviz_navigation_dialog_action_client",
"--"});
482 client_node_ = std::make_shared<rclcpp::Node>(
"_", options);
483 executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
484 executor_->add_node(client_node_);
486 client_nav_ = std::make_shared<nav2_lifecycle_manager::LifecycleManagerClient>(
487 "lifecycle_manager_nav2", client_node_);
488 initial_thread_ =
new InitialThread(client_nav_);
489 connect(initial_thread_, &InitialThread::finished, initial_thread_, &QObject::deleteLater);
491 QSignalTransition * activeSignal =
new QSignalTransition(
493 &InitialThread::navigationActive);
494 activeSignal->setTargetState(idle_);
495 pre_initial_->addTransition(activeSignal);
496 QSignalTransition * inactiveSignal =
new QSignalTransition(
498 &InitialThread::navigationInactive);
499 inactiveSignal->setTargetState(initial_);
500 pre_initial_->addTransition(inactiveSignal);
503 initial_thread_, &InitialThread::navigationActive,
504 [
this, navigation_active] {
505 navigation_status_indicator_->setText(navigation_active);
508 initial_thread_, &InitialThread::navigationInactive,
509 [
this, navigation_inactive] {
510 navigation_status_indicator_->setText(navigation_inactive);
511 navigation_goal_status_indicator_->setText(nav2_rviz_plugins::getGoalStatusLabel());
512 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
514 state_machine_.addState(pre_initial_);
515 state_machine_.addState(initial_);
516 state_machine_.addState(idle_);
517 state_machine_.addState(running_);
518 state_machine_.addState(canceled_);
519 state_machine_.addState(reset_);
520 state_machine_.addState(paused_);
521 state_machine_.addState(resumed_);
522 state_machine_.addState(accumulating_);
523 state_machine_.addState(accumulated_wp_);
524 state_machine_.addState(accumulated_nav_through_poses_);
525 state_machine_.addState(resumed_wp_);
527 state_machine_.setInitialState(pre_initial_);
530 QObject::connect(&state_machine_, SIGNAL(started()),
this, SLOT(startThread()));
531 state_machine_.start();
534 QVBoxLayout * main_layout =
new QVBoxLayout;
535 QHBoxLayout * side_layout =
new QHBoxLayout;
536 QVBoxLayout * status_layout =
new QVBoxLayout;
537 QHBoxLayout * logo_layout =
new QHBoxLayout;
539 imgDisplayLabel_ =
new QLabel(
"");
540 imgDisplayLabel_->setPixmap(
541 rviz_common::loadPixmap(
"package://nav2_rviz_plugins/icons/classes/nav2_logo_small.png"));
543 status_layout->addWidget(navigation_status_indicator_);
544 status_layout->addWidget(navigation_goal_status_indicator_);
546 logo_layout->addWidget(imgDisplayLabel_, 5, Qt::AlignRight);
548 side_layout->addLayout(status_layout);
549 side_layout->addLayout(logo_layout);
551 main_layout->addLayout(side_layout);
552 main_layout->addWidget(navigation_feedback_indicator_);
553 main_layout->addWidget(waypoint_status_indicator_);
556 QHBoxLayout * bt_layout =
new QHBoxLayout;
557 QLabel * bt_label =
new QLabel(
"Behavior Tree XML:");
558 bt_layout->addWidget(bt_label);
559 bt_layout->addWidget(behavior_tree_file_);
560 main_layout->addLayout(bt_layout);
562 main_layout->addWidget(pause_resume_button_);
563 main_layout->addWidget(start_reset_button_);
564 main_layout->addWidget(navigation_mode_button_);
567 tools_tab_widget_ =
new QTabWidget;
570 QWidget * nav_to_pose_tab =
new QWidget;
571 QGridLayout * nav_to_pose_layout =
new QGridLayout;
573 nav_to_pose_layout->addWidget(
new QLabel(
"Frame ID:"), 0, 0);
574 nav_to_pose_frame_id_ =
new QLineEdit(
"map");
575 nav_to_pose_layout->addWidget(nav_to_pose_frame_id_, 0, 1);
577 nav_to_pose_layout->addWidget(
new QLabel(
"Position X:"), 1, 0);
578 nav_to_pose_x_ =
new QDoubleSpinBox;
579 nav_to_pose_x_->setRange(-1000.0, 1000.0);
580 nav_to_pose_x_->setDecimals(3);
581 nav_to_pose_x_->setSingleStep(0.1);
582 nav_to_pose_layout->addWidget(nav_to_pose_x_, 1, 1);
584 nav_to_pose_layout->addWidget(
new QLabel(
"Position Y:"), 2, 0);
585 nav_to_pose_y_ =
new QDoubleSpinBox;
586 nav_to_pose_y_->setRange(-1000.0, 1000.0);
587 nav_to_pose_y_->setDecimals(3);
588 nav_to_pose_y_->setSingleStep(0.1);
589 nav_to_pose_layout->addWidget(nav_to_pose_y_, 2, 1);
591 nav_to_pose_layout->addWidget(
new QLabel(
"Yaw (radians):"), 3, 0);
592 nav_to_pose_yaw_ =
new QDoubleSpinBox;
593 nav_to_pose_yaw_->setRange(-M_PI, M_PI);
594 nav_to_pose_yaw_->setDecimals(4);
595 nav_to_pose_yaw_->setSingleStep(0.01);
596 nav_to_pose_layout->addWidget(nav_to_pose_yaw_, 3, 1);
598 QObject::connect(start_nav_to_pose_button_,
599 &QPushButton::clicked,
this, &Nav2Panel::onSendNavToPose);
600 nav_to_pose_layout->addWidget(start_nav_to_pose_button_, 4, 0, 1, 2);
602 nav_to_pose_tab->setLayout(nav_to_pose_layout);
603 tools_tab_widget_->addTab(nav_to_pose_tab,
"NavigateToPose");
606 QWidget * nav_wp_tab =
new QWidget;
607 QVBoxLayout * nav_wp_layout =
new QVBoxLayout;
609 QLabel * poses_info =
new QLabel(
"Accumulated poses:");
610 nav_wp_layout->addWidget(poses_info);
612 nav_through_poses_tabs_ =
new QTabWidget;
613 nav_wp_layout->addWidget(nav_through_poses_tabs_);
615 QHBoxLayout * pose_buttons_layout =
new QHBoxLayout;
616 QObject::connect(add_pose_button_, &QPushButton::clicked,
this, &Nav2Panel::onAddNavThroughPose);
617 pose_buttons_layout->addWidget(add_pose_button_);
619 QObject::connect(remove_pose_button_,
620 &QPushButton::clicked,
this, &Nav2Panel::onRemoveNavThroughPose);
621 pose_buttons_layout->addWidget(remove_pose_button_);
623 nav_wp_layout->addLayout(pose_buttons_layout);
625 QHBoxLayout * file_buttons_layout =
new QHBoxLayout;
626 file_buttons_layout->addWidget(save_waypoints_button_);
627 file_buttons_layout->addWidget(load_waypoints_button_);
628 nav_wp_layout->addLayout(file_buttons_layout);
631 QLabel * wp_options_label =
new QLabel(
"<b>Waypoint Following Options:</b>");
632 nav_wp_layout->addWidget(wp_options_label);
634 QHBoxLayout * wp_controls_layout =
new QHBoxLayout;
635 wp_controls_layout->addWidget(pause_waypoint_button_);
636 nav_wp_layout->addLayout(wp_controls_layout);
638 QHBoxLayout * wp_loop_layout =
new QHBoxLayout;
639 wp_loop_layout->addWidget(number_of_loops_);
640 wp_loop_layout->addWidget(nr_of_loops_);
641 wp_loop_layout->addWidget(store_initial_pose_checkbox_);
642 nav_wp_layout->addLayout(wp_loop_layout);
644 nav_wp_tab->setLayout(nav_wp_layout);
645 tools_tab_widget_->addTab(nav_wp_tab,
"NavigateThroughPoses / Waypoint Following");
647 tools_tab_widget_->setTabEnabled(0,
false);
648 tools_tab_widget_->setTabEnabled(1,
false);
650 tools_tab_widget_->setCurrentIndex(0);
652 main_layout->addWidget(tools_tab_widget_);
653 main_layout->setContentsMargins(10, 10, 10, 10);
654 setLayout(main_layout);
656 navigation_action_client_ =
657 rclcpp_action::create_client<nav2_msgs::action::NavigateToPose>(
660 waypoint_follower_action_client_ =
661 rclcpp_action::create_client<nav2_msgs::action::FollowWaypoints>(
664 nav_through_poses_action_client_ =
665 rclcpp_action::create_client<nav2_msgs::action::NavigateThroughPoses>(
667 "navigate_through_poses");
670 tf2_buffer_ = nav2::create_transform_buffer(client_node_);
671 transform_listener_ = nav2::create_transform_listener(*tf2_buffer_);
673 navigation_goal_ = nav2_msgs::action::NavigateToPose::Goal();
674 waypoint_follower_goal_ = nav2_msgs::action::FollowWaypoints::Goal();
675 nav_through_poses_goal_ = nav2_msgs::action::NavigateThroughPoses::Goal();
677 wp_navigation_markers_pub_ =
678 client_node_->create_publisher<visualization_msgs::msg::MarkerArray>(
680 rclcpp::QoS(1).transient_local());
683 &GoalUpdater, SIGNAL(updateGoal(
double,
double,
double,QString)),
684 this, SLOT(onNewGoal(
double,
double,
double,QString)));
687 Nav2Panel::~Nav2Panel()
691 void Nav2Panel::initialStateHandler()
693 if (store_initial_pose_checkbox_->isChecked()) {
694 store_initial_pose_ =
true;
697 store_initial_pose_ =
false;
701 bool Nav2Panel::isLoopValueValid(std::string & loop_value)
704 if (loop_value.empty()) {
705 std::cout <<
"Loop value cannot be set to empty, setting to 0" << std::endl;
707 nr_of_loops_->setText(
"0");
712 for (
char & c : loop_value) {
713 if (isalpha(c) || isspace(c) || ispunct(c)) {
714 waypoint_status_indicator_->setText(
715 "<b> Note: </b> Set a valid value for the loop");
716 std::cout <<
"Set a valid value for the loop, check for alphabets and spaces" << std::endl;
717 navigation_mode_button_->setEnabled(
false);
724 }
catch (std::invalid_argument
const & ex) {
726 waypoint_status_indicator_->setText(
"<b> Note: </b> Set a valid value for the loop");
727 navigation_mode_button_->setEnabled(
false);
729 }
catch (std::out_of_range
const & ex) {
731 waypoint_status_indicator_->setText(
732 "<b> Note: </b> Loop value out of range, setting max possible value"
734 loop_value = std::to_string(std::numeric_limits<int>::max());
735 nr_of_loops_->setText(QString::fromStdString(loop_value));
740 void Nav2Panel::loophandler()
742 loop_no_ = nr_of_loops_->displayText().toStdString();
745 if (!isLoopValueValid(loop_no_)) {
750 navigation_mode_button_->setEnabled(
true);
753 if (!loop_no_.empty() && stoi(loop_no_) > 0) {
754 pause_resume_button_->setEnabled(
false);
756 pause_resume_button_->setEnabled(
true);
760 void Nav2Panel::handleGoalLoader()
762 std::cout <<
"Loading Waypoints!" << std::endl;
764 QString file = QFileDialog::getOpenFileName(
767 tr(
"yaml(*.yaml);;All Files (*)"));
769 YAML::Node available_waypoints;
772 available_waypoints = YAML::LoadFile(file.toStdString());
773 }
catch (
const std::exception & ex) {
774 std::cout << ex.what() <<
", please select a valid file" << std::endl;
778 const YAML::Node & waypoint_iter = available_waypoints[
"waypoints"];
779 for (YAML::const_iterator it = waypoint_iter.begin(); it != waypoint_iter.end(); ++it) {
780 auto waypoint = waypoint_iter[it->first.as<std::string>()];
781 auto pose = waypoint[
"pose"].as<std::vector<double>>();
782 auto orientation = waypoint[
"orientation"].as<std::vector<double>>();
783 acummulated_poses_.goals.push_back(convert_to_msg(pose, orientation));
786 syncTabsWithAccumulatedPoses();
787 updateWpNavigationMarkers();
790 geometry_msgs::msg::PoseStamped Nav2Panel::convert_to_msg(
791 std::vector<double> pose,
792 std::vector<double> orientation)
794 auto msg = geometry_msgs::msg::PoseStamped();
796 msg.header.frame_id =
"map";
799 msg.pose.position.x = pose[0];
800 msg.pose.position.y = pose[1];
801 msg.pose.position.z = pose[2];
803 msg.pose.orientation.w = orientation[0];
804 msg.pose.orientation.x = orientation[1];
805 msg.pose.orientation.y = orientation[2];
806 msg.pose.orientation.z = orientation[3];
811 void Nav2Panel::handleGoalSaver()
815 if (acummulated_poses_.goals.empty()) {
816 std::cout <<
"No accumulated Points to Save!" << std::endl;
819 std::cout <<
"Saving Waypoints!" << std::endl;
823 out << YAML::BeginMap;
824 out << YAML::Key <<
"waypoints";
825 out << YAML::BeginMap;
828 for (
unsigned int i = 0; i < acummulated_poses_.goals.size(); ++i) {
829 out << YAML::Key <<
"waypoint" + std::to_string(i);
830 out << YAML::BeginMap;
831 out << YAML::Key <<
"pose";
832 std::vector<double> pose =
833 {acummulated_poses_.goals[i].pose.position.x, acummulated_poses_.goals[i].pose.position.y,
834 acummulated_poses_.goals[i].pose.position.z};
835 out << YAML::Value << pose;
836 out << YAML::Key <<
"orientation";
837 std::vector<double> orientation =
838 {acummulated_poses_.goals[i].pose.orientation.w, acummulated_poses_.goals[i].pose.orientation.x,
839 acummulated_poses_.goals[i].pose.orientation.y,
840 acummulated_poses_.goals[i].pose.orientation.z};
841 out << YAML::Value << orientation;
846 QString file = QFileDialog::getSaveFileName(
849 tr(
"yaml(*.yaml);;All Files (*)"));
851 if (!file.toStdString().empty()) {
852 std::ofstream fout(file.toStdString() +
".yaml");
854 std::cout <<
"Saving waypoints succeeded" << std::endl;
856 std::cout <<
"Saving waypoints aborted" << std::endl;
861 Nav2Panel::onInitialize()
863 node_ptr_ = getDisplayContext()->getRosNodeAbstraction().lock();
864 if (node_ptr_ ==
nullptr) {
867 rclcpp::get_logger(
"nav2_panel"),
868 "Underlying ROS node no longer exists, initialization failed");
871 rclcpp::Node::SharedPtr node = node_ptr_->get_raw_node();
874 node->declare_parameter(
"base_frame", rclcpp::ParameterValue(std::string(
"base_footprint")));
875 node->get_parameter(
"base_frame", base_frame_);
878 navigation_feedback_sub_ =
879 node->create_subscription<nav2_msgs::action::NavigateToPose::Impl::FeedbackMessage>(
880 "navigate_to_pose/_action/feedback",
881 rclcpp::SystemDefaultsQoS(),
882 [
this](
const nav2_msgs::action::NavigateToPose::Impl::FeedbackMessage::ConstSharedPtr & msg) {
883 if (stoi(nr_of_loops_->displayText().toStdString()) > 0) {
884 if (goal_index_ == 0 && !loop_counter_stop_) {
886 loop_counter_stop_ =
true;
888 if (goal_index_ != 0) {
889 loop_counter_stop_ =
false;
891 navigation_feedback_indicator_->setText(
892 getNavToPoseFeedbackLabel(msg->feedback) + QString(
894 "</td></tr><tr><td width=150>Waypoint:</td><td>" +
895 toString(goal_index_ + 1)).c_str()) + QString(
897 "</td></tr><tr><td width=150>Loop:</td><td>" +
898 toString(loop_count_)).c_str()));
900 navigation_feedback_indicator_->setText(getNavToPoseFeedbackLabel(msg->feedback));
903 nav_through_poses_feedback_sub_ =
904 node->create_subscription<nav2_msgs::action::NavigateThroughPoses::Impl::FeedbackMessage>(
905 "navigate_through_poses/_action/feedback",
906 rclcpp::SystemDefaultsQoS(),
907 [
this](
const nav2_msgs::action::NavigateThroughPoses::Impl::FeedbackMessage::ConstSharedPtr &
909 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel(msg->feedback));
913 navigation_goal_status_sub_ = node->create_subscription<action_msgs::msg::GoalStatusArray>(
914 "navigate_to_pose/_action/status",
915 rclcpp::SystemDefaultsQoS(),
916 [
this](
const action_msgs::msg::GoalStatusArray::ConstSharedPtr & msg) {
917 navigation_goal_status_indicator_->setText(
918 nav2_rviz_plugins::getGoalStatusLabel(
"Feedback", msg->status_list.back().status));
921 loop_count_ == stoi(nr_of_loops_->displayText().toStdString()) &&
922 goal_index_ ==
static_cast<int>(store_poses_.goals.size()) - 1 &&
923 msg->status_list.back().status == action_msgs::msg::GoalStatus::STATUS_SUCCEEDED)
925 store_poses_ = nav_msgs::msg::Goals();
926 waypoint_status_indicator_->clear();
929 navigation_feedback_indicator_->setText(getNavToPoseFeedbackLabel());
932 nav_through_poses_goal_status_sub_ = node->create_subscription<action_msgs::msg::GoalStatusArray>(
933 "navigate_through_poses/_action/status",
934 rclcpp::SystemDefaultsQoS(),
935 [
this](
const action_msgs::msg::GoalStatusArray::ConstSharedPtr & msg) {
936 navigation_goal_status_indicator_->setText(
937 nav2_rviz_plugins::getGoalStatusLabel(
"Feedback", msg->status_list.back().status));
938 if (msg->status_list.back().status != action_msgs::msg::GoalStatus::STATUS_EXECUTING) {
939 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
945 Nav2Panel::startThread()
948 initial_thread_->start();
954 QFuture<bool> futureNav =
958 client_nav_.get(), std::placeholders::_1), server_timeout_);
962 Nav2Panel::onResume()
964 QFuture<bool> futureNav =
968 client_nav_.get(), std::placeholders::_1), server_timeout_);
974 tools_tab_widget_->setTabEnabled(0,
true);
975 tools_tab_widget_->setTabEnabled(1,
false);
976 tools_tab_widget_->setTabEnabled(2,
false);
977 tools_tab_widget_->setCurrentIndex(0);
981 Nav2Panel::onStartup()
983 QFuture<bool> futureNav =
987 client_nav_.get(), std::placeholders::_1), server_timeout_);
991 Nav2Panel::onShutdown()
993 QFuture<bool> futureNav =
997 client_nav_.get(), std::placeholders::_1), server_timeout_);
1002 Nav2Panel::onCancel()
1004 QFuture<void> future =
1007 &Nav2Panel::onCancelButtonPressed,
1009 waypoint_status_indicator_->clear();
1010 store_poses_ = nav_msgs::msg::Goals();
1011 acummulated_poses_ = nav_msgs::msg::Goals();
1014 void Nav2Panel::onResumedWp()
1016 QFuture<void> future =
1019 &Nav2Panel::onCancelButtonPressed,
1021 acummulated_poses_ = store_poses_;
1022 loop_no_ = std::to_string(
1023 stoi(nr_of_loops_->displayText().toStdString()) -
1025 waypoint_status_indicator_->setText(
1026 QString(std::string(
"<b> Note: </b> Navigation is paused.").c_str()));
1030 Nav2Panel::onNewGoal(
double x,
double y,
double theta, QString frame)
1032 auto pose = geometry_msgs::msg::PoseStamped();
1035 pose.header.frame_id = frame.toStdString();
1036 pose.pose.position.x = x;
1037 pose.pose.position.y = y;
1038 pose.pose.position.z = 0.0;
1039 pose.pose.orientation = orientationAroundZAxis(theta);
1041 if (store_poses_.goals.empty()) {
1042 if (state_machine_.configuration().contains(accumulating_)) {
1043 waypoint_status_indicator_->clear();
1044 acummulated_poses_.goals.push_back(pose);
1045 syncTabsWithAccumulatedPoses();
1047 acummulated_poses_ = nav_msgs::msg::Goals();
1048 updateWpNavigationMarkers();
1049 std::cout <<
"Start navigation" << std::endl;
1050 startNavigation(pose);
1053 waypoint_status_indicator_->setText(
1054 QString(std::string(
"<b> Note: </b> Cannot set goal in pause state").c_str()));
1056 updateWpNavigationMarkers();
1060 Nav2Panel::onCancelButtonPressed()
1062 if (navigation_goal_handle_) {
1063 auto future_cancel = navigation_action_client_->async_cancel_goal(navigation_goal_handle_);
1065 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1066 rclcpp::FutureReturnCode::SUCCESS)
1068 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel goal");
1070 navigation_goal_handle_.reset();
1074 if (waypoint_follower_goal_handle_) {
1075 auto future_cancel =
1076 waypoint_follower_action_client_->async_cancel_goal(waypoint_follower_goal_handle_);
1078 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1079 rclcpp::FutureReturnCode::SUCCESS)
1081 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel waypoint follower");
1083 waypoint_follower_goal_handle_.reset();
1087 if (nav_through_poses_goal_handle_) {
1088 auto future_cancel =
1089 nav_through_poses_action_client_->async_cancel_goal(nav_through_poses_goal_handle_);
1091 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1092 rclcpp::FutureReturnCode::SUCCESS)
1094 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel nav through pose action");
1096 nav_through_poses_goal_handle_.reset();
1104 Nav2Panel::onAccumulatedWp()
1106 updateAccumulatedPosesFromTabs();
1108 if (acummulated_poses_.goals.empty()) {
1109 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1110 waypoint_status_indicator_->setText(
1111 "<b> Note: </b> Uh oh! Someone forgot to select the waypoints");
1116 if (!isLoopValueValid(loop_no_)) {
1117 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1122 waypoint_status_indicator_->clear();
1125 navigation_mode_button_->setEnabled(
false);
1126 pause_resume_button_->setEnabled(
false);
1130 if (store_poses_.goals.empty()) {
1131 std::cout <<
"Start waypoint" << std::endl;
1134 nr_of_loops_->setText(QString::fromStdString(loop_no_));
1137 geometry_msgs::msg::TransformStamped init_transform;
1140 if (store_initial_pose_) {
1142 init_transform = tf2_buffer_->lookupTransform(
1143 acummulated_poses_.goals[0].header.frame_id, base_frame_,
1144 tf2::TimePointZero);
1145 }
catch (
const tf2::TransformException & ex) {
1147 client_node_->get_logger(),
"Could not transform %s to %s: %s",
1148 acummulated_poses_.goals[0].header.frame_id.c_str(), base_frame_.c_str(), ex.what());
1153 geometry_msgs::msg::PoseStamped initial_pose;
1154 initial_pose.header = init_transform.header;
1155 initial_pose.pose.position.x = init_transform.transform.translation.x;
1156 initial_pose.pose.position.y = init_transform.transform.translation.y;
1157 initial_pose.pose.position.z = init_transform.transform.translation.z;
1158 initial_pose.pose.orientation.x = init_transform.transform.rotation.x;
1159 initial_pose.pose.orientation.y = init_transform.transform.rotation.y;
1160 initial_pose.pose.orientation.z = init_transform.transform.rotation.z;
1161 initial_pose.pose.orientation.w = init_transform.transform.rotation.w;
1164 acummulated_poses_.goals.insert(acummulated_poses_.goals.begin(), initial_pose);
1165 syncTabsWithAccumulatedPoses();
1166 updateWpNavigationMarkers();
1167 initial_pose_stored_ =
true;
1168 if (loop_count_ == 0) {
1171 }
else if (!store_initial_pose_ && initial_pose_stored_) {
1172 acummulated_poses_.goals.erase(
1173 acummulated_poses_.goals.begin(),
1174 acummulated_poses_.goals.begin());
1177 std::cout <<
"Resuming waypoint" << std::endl;
1180 startWaypointFollowing(acummulated_poses_.goals);
1181 store_poses_ = acummulated_poses_;
1185 Nav2Panel::onAccumulatedNTP()
1187 updateAccumulatedPosesFromTabs();
1189 std::cout <<
"Start navigate through poses" << std::endl;
1190 startNavThroughPoses(acummulated_poses_);
1194 Nav2Panel::onAccumulating()
1196 acummulated_poses_ = nav_msgs::msg::Goals();
1197 store_poses_ = nav_msgs::msg::Goals();
1200 initial_pose_stored_ =
false;
1201 loop_counter_stop_ =
true;
1203 updateWpNavigationMarkers();
1204 syncTabsWithAccumulatedPoses();
1206 nav_to_pose_frame_id_->setText(
"map");
1207 nav_to_pose_x_->setValue(0.0);
1208 nav_to_pose_y_->setValue(0.0);
1209 nav_to_pose_yaw_->setValue(0.0);
1211 tools_tab_widget_->setTabEnabled(0,
false);
1212 tools_tab_widget_->setTabEnabled(1,
true);
1213 tools_tab_widget_->setCurrentIndex(1);
1216 Nav2Panel::timerEvent(QTimerEvent * event)
1218 if (state_machine_.configuration().contains(accumulated_wp_)) {
1219 if (event->timerId() == timer_.timerId()) {
1220 if (!waypoint_follower_goal_handle_) {
1221 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1222 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1226 executor_->spin_some();
1227 auto status = waypoint_follower_goal_handle_->get_status();
1230 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1231 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1233 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1235 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1236 acummulated_poses_ = nav_msgs::msg::Goals();
1237 updateWpNavigationMarkers();
1241 }
else if (state_machine_.configuration().contains(accumulated_nav_through_poses_)) {
1242 if (event->timerId() == timer_.timerId()) {
1243 if (!nav_through_poses_goal_handle_) {
1244 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1245 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1249 executor_->spin_some();
1250 auto status = nav_through_poses_goal_handle_->get_status();
1253 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1254 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1256 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1258 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1259 acummulated_poses_ = nav_msgs::msg::Goals();
1260 updateWpNavigationMarkers();
1265 if (event->timerId() == timer_.timerId()) {
1266 if (!navigation_goal_handle_) {
1267 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1268 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1272 executor_->spin_some();
1273 auto status = navigation_goal_handle_->get_status();
1276 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1277 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1279 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1281 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1282 nav_to_pose_frame_id_->setText(
"map");
1283 nav_to_pose_x_->setValue(0.0);
1284 nav_to_pose_y_->setValue(0.0);
1285 nav_to_pose_yaw_->setValue(0.0);
1293 Nav2Panel::startWaypointFollowing(std::vector<geometry_msgs::msg::PoseStamped> poses)
1295 auto is_action_server_ready =
1296 waypoint_follower_action_client_->wait_for_action_server(std::chrono::seconds(5));
1297 if (!is_action_server_ready) {
1299 client_node_->get_logger(),
"follow_waypoints action server is not available."
1300 " Is the initial pose set?");
1305 waypoint_follower_goal_.poses = poses;
1306 waypoint_follower_goal_.goal_index = goal_index_;
1307 waypoint_follower_goal_.number_of_loops = stoi(loop_no_);
1310 client_node_->get_logger(),
"Sending a path of %zu waypoints:",
1311 waypoint_follower_goal_.poses.size());
1312 for (
auto waypoint : waypoint_follower_goal_.poses) {
1314 client_node_->get_logger(),
1315 "\t(%lf, %lf)", waypoint.pose.position.x, waypoint.pose.position.y);
1319 auto send_goal_options =
1320 nav2::ActionClient<nav2_msgs::action::FollowWaypoints>::SendGoalOptions();
1321 send_goal_options.result_callback = [
this](
auto) {
1322 waypoint_follower_goal_handle_.reset();
1325 send_goal_options.feedback_callback = [
this](
1326 WaypointFollowerGoalHandle::SharedPtr ,
1327 const std::shared_ptr<const nav2_msgs::action::FollowWaypoints::Feedback> feedback) {
1328 goal_index_ = feedback->current_waypoint;
1331 auto future_goal_handle =
1332 waypoint_follower_action_client_->async_send_goal(waypoint_follower_goal_, send_goal_options);
1333 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1334 rclcpp::FutureReturnCode::SUCCESS)
1336 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1341 waypoint_follower_goal_handle_ = future_goal_handle.get();
1342 if (!waypoint_follower_goal_handle_) {
1343 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1347 timer_.start(200,
this);
1351 Nav2Panel::startNavThroughPoses(nav_msgs::msg::Goals poses)
1353 auto is_action_server_ready =
1354 nav_through_poses_action_client_->wait_for_action_server(std::chrono::seconds(5));
1355 if (!is_action_server_ready) {
1357 client_node_->get_logger(),
"navigate_through_poses action server is not available."
1358 " Is the initial pose set?");
1362 nav_through_poses_goal_.poses = poses;
1363 nav_through_poses_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1365 if (nav_through_poses_goal_.behavior_tree.empty()) {
1367 client_node_->get_logger(),
1368 "NavigateThroughPoses will be called using the BT Navigator's default behavior tree.");
1371 client_node_->get_logger(),
1372 "NavigateThroughPoses will be called using behavior tree: %s",
1373 nav_through_poses_goal_.behavior_tree.c_str());
1377 client_node_->get_logger(),
"Sending a path of %zu waypoints:",
1378 nav_through_poses_goal_.poses.goals.size());
1379 for (
auto waypoint : nav_through_poses_goal_.poses.goals) {
1381 client_node_->get_logger(),
1382 "\t(%lf, %lf)", waypoint.pose.position.x, waypoint.pose.position.y);
1386 auto send_goal_options =
1387 nav2::ActionClient<nav2_msgs::action::NavigateThroughPoses>::SendGoalOptions();
1388 send_goal_options.result_callback = [
this](
auto) {
1389 nav_through_poses_goal_handle_.reset();
1392 auto future_goal_handle =
1393 nav_through_poses_action_client_->async_send_goal(nav_through_poses_goal_, send_goal_options);
1394 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1395 rclcpp::FutureReturnCode::SUCCESS)
1397 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1402 nav_through_poses_goal_handle_ = future_goal_handle.get();
1403 if (!nav_through_poses_goal_handle_) {
1404 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1408 timer_.start(200,
this);
1412 Nav2Panel::startNavigation(geometry_msgs::msg::PoseStamped pose)
1414 auto is_action_server_ready =
1415 navigation_action_client_->wait_for_action_server(std::chrono::seconds(5));
1416 if (!is_action_server_ready) {
1418 client_node_->get_logger(),
1419 "navigate_to_pose action server is not available."
1420 " Is the initial pose set?");
1425 navigation_goal_.pose = pose;
1426 navigation_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1428 if (navigation_goal_.behavior_tree.empty()) {
1430 client_node_->get_logger(),
1431 "NavigateToPose will be called using the BT Navigator's default behavior tree.");
1434 client_node_->get_logger(),
1435 "NavigateToPose will be called using behavior tree: %s",
1436 navigation_goal_.behavior_tree.c_str());
1440 auto send_goal_options =
1441 nav2::ActionClient<nav2_msgs::action::NavigateToPose>::SendGoalOptions();
1442 send_goal_options.result_callback = [
this](
auto) {
1443 navigation_goal_handle_.reset();
1446 auto future_goal_handle =
1447 navigation_action_client_->async_send_goal(navigation_goal_, send_goal_options);
1448 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1449 rclcpp::FutureReturnCode::SUCCESS)
1451 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1456 navigation_goal_handle_ = future_goal_handle.get();
1457 if (!navigation_goal_handle_) {
1458 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1462 timer_.start(200,
this);
1466 Nav2Panel::save(rviz_common::Config config)
const
1468 Panel::save(config);
1472 Nav2Panel::load(
const rviz_common::Config & config)
1474 Panel::load(config);
1478 Nav2Panel::resetUniqueId()
1484 Nav2Panel::getUniqueId()
1486 int temp_id = unique_id;
1492 Nav2Panel::updateWpNavigationMarkers()
1496 auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
1498 visualization_msgs::msg::Marker clear_all_marker;
1499 clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
1500 marker_array->markers.push_back(clear_all_marker);
1502 wp_navigation_markers_pub_->publish(std::move(marker_array));
1504 marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
1506 for (
size_t i = 0; i < acummulated_poses_.goals.size(); i++) {
1508 visualization_msgs::msg::Marker arrow_marker;
1509 arrow_marker.header = acummulated_poses_.goals[i].header;
1510 arrow_marker.id = getUniqueId();
1511 arrow_marker.type = visualization_msgs::msg::Marker::ARROW;
1512 arrow_marker.action = visualization_msgs::msg::Marker::ADD;
1513 arrow_marker.pose = acummulated_poses_.goals[i].pose;
1514 arrow_marker.scale.x = 0.3;
1515 arrow_marker.scale.y = 0.05;
1516 arrow_marker.scale.z = 0.02;
1517 arrow_marker.color.r = 0;
1518 arrow_marker.color.g = 255;
1519 arrow_marker.color.b = 0;
1520 arrow_marker.color.a = 1.0f;
1521 arrow_marker.lifetime = rclcpp::Duration(0s);
1522 arrow_marker.frame_locked =
false;
1523 marker_array->markers.push_back(arrow_marker);
1526 visualization_msgs::msg::Marker circle_marker;
1527 circle_marker.header = acummulated_poses_.goals[i].header;
1528 circle_marker.id = getUniqueId();
1529 circle_marker.type = visualization_msgs::msg::Marker::SPHERE;
1530 circle_marker.action = visualization_msgs::msg::Marker::ADD;
1531 circle_marker.pose = acummulated_poses_.goals[i].pose;
1532 circle_marker.scale.x = 0.05;
1533 circle_marker.scale.y = 0.05;
1534 circle_marker.scale.z = 0.05;
1535 circle_marker.color.r = 255;
1536 circle_marker.color.g = 0;
1537 circle_marker.color.b = 0;
1538 circle_marker.color.a = 1.0f;
1539 circle_marker.lifetime = rclcpp::Duration(0s);
1540 circle_marker.frame_locked =
false;
1541 marker_array->markers.push_back(circle_marker);
1544 visualization_msgs::msg::Marker marker_text;
1545 marker_text.header = acummulated_poses_.goals[i].header;
1546 marker_text.id = getUniqueId();
1547 marker_text.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
1548 marker_text.action = visualization_msgs::msg::Marker::ADD;
1549 marker_text.pose = acummulated_poses_.goals[i].pose;
1550 marker_text.pose.position.z += 0.2;
1551 marker_text.scale.x = 0.07;
1552 marker_text.scale.y = 0.07;
1553 marker_text.scale.z = 0.07;
1554 marker_text.color.r = 0;
1555 marker_text.color.g = 255;
1556 marker_text.color.b = 0;
1557 marker_text.color.a = 1.0f;
1558 marker_text.lifetime = rclcpp::Duration(0s);
1559 marker_text.frame_locked =
false;
1560 marker_text.text =
"wp_" + std::to_string(i + 1);
1561 marker_array->markers.push_back(marker_text);
1564 wp_navigation_markers_pub_->publish(std::move(marker_array));
1568 Nav2Panel::getNavToPoseFeedbackLabel(nav2_msgs::action::NavigateToPose::Feedback msg)
1570 return QString(std::string(
"<table>" + toLabel(msg) +
"</table>").c_str());
1574 Nav2Panel::getNavThroughPosesFeedbackLabel(nav2_msgs::action::NavigateThroughPoses::Feedback msg)
1578 "<table><tr><td width=150>Poses remaining:</td><td>" +
1579 std::to_string(msg.number_of_poses_remaining) +
1580 "</td></tr>" + toLabel(msg) +
"</table>").c_str());
1583 template<
typename T>
1584 inline std::string Nav2Panel::toLabel(T & msg)
1587 "<tr><td width=150>ETA:</td><td>" +
1588 toString(rclcpp::Duration(msg.estimated_time_remaining).seconds(), 0) +
" s"
1589 "</td></tr><tr><td width=150>Distance remaining:</td><td>" +
1590 toString(msg.distance_remaining, 2) +
" m"
1591 "</td></tr><tr><td width=150>Position error:</td><td>" +
1592 toString(msg.position_tracking_error, 2) +
" m"
1593 "</td></tr><tr><td width=150>Heading error:</td><td>" +
1594 toString(msg.heading_tracking_error, 2) +
" rad"
1595 "</td></tr><tr><td width=150>Time taken:</td><td>" +
1596 toString(rclcpp::Duration(msg.navigation_time).seconds(), 0) +
" s"
1597 "</td></tr><tr><td width=150>Recoveries:</td><td>" +
1598 std::to_string(msg.number_of_recoveries) +
1603 Nav2Panel::toString(
double val,
int precision)
1605 std::ostringstream out;
1606 out.precision(precision);
1607 out << std::fixed << val;
1612 Nav2Panel::onSendNavToPose()
1614 auto pose = geometry_msgs::msg::PoseStamped();
1615 pose.header.frame_id = nav_to_pose_frame_id_->text().toStdString();
1616 pose.header.stamp = client_node_->now();
1617 pose.pose.position.x = nav_to_pose_x_->value();
1618 pose.pose.position.y = nav_to_pose_y_->value();
1619 pose.pose.position.z = 0.0;
1620 pose.pose.orientation = orientationAroundZAxis(nav_to_pose_yaw_->value());
1622 auto is_action_server_ready =
1623 navigation_action_client_->wait_for_action_server(std::chrono::seconds(5));
1624 if (!is_action_server_ready) {
1626 client_node_->get_logger(),
1627 "navigate_to_pose action server is not available.");
1631 navigation_goal_.pose = pose;
1632 navigation_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1634 auto send_goal_options =
1635 nav2::ActionClient<nav2_msgs::action::NavigateToPose>::SendGoalOptions();
1636 send_goal_options.result_callback = [
this](
auto) {
1637 navigation_goal_handle_.reset();
1640 auto future_goal_handle =
1641 navigation_action_client_->async_send_goal(navigation_goal_, send_goal_options);
1642 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1643 rclcpp::FutureReturnCode::SUCCESS)
1645 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1649 navigation_goal_handle_ = future_goal_handle.get();
1650 if (!navigation_goal_handle_) {
1651 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1655 timer_.start(200,
this);
1659 Nav2Panel::onAddNavThroughPose()
1661 int index = nav_through_pose_tabs_.size();
1662 createNavThroughPoseTab(index);
1663 updateAccumulatedPosesFromTabs();
1667 Nav2Panel::onRemoveNavThroughPose()
1669 if (!nav_through_pose_tabs_.empty()) {
1670 int index = nav_through_poses_tabs_->currentIndex();
1671 if (index >= 0 && index <
static_cast<int>(nav_through_pose_tabs_.size())) {
1672 nav_through_poses_tabs_->removeTab(index);
1673 nav_through_pose_tabs_.erase(nav_through_pose_tabs_.begin() + index);
1675 for (
int i = 0; i < nav_through_poses_tabs_->count(); ++i) {
1676 nav_through_poses_tabs_->setTabText(i, QString(
"Pose %1").arg(i + 1));
1679 updateAccumulatedPosesFromTabs();
1685 Nav2Panel::createNavThroughPoseTab(
int index)
1687 QWidget * tab_widget =
new QWidget;
1688 QGridLayout * layout =
new QGridLayout;
1690 NavThroughPoseTab tab;
1692 layout->addWidget(
new QLabel(
"Frame ID:"), 0, 0);
1693 tab.frame_id_edit =
new QLineEdit(
"map");
1695 tab.frame_id_edit, &QLineEdit::textChanged,
this,
1696 &Nav2Panel::updateAccumulatedPosesFromTabs);
1697 layout->addWidget(tab.frame_id_edit, 0, 1);
1699 layout->addWidget(
new QLabel(
"Position X:"), 1, 0);
1700 tab.pos_x_spin =
new QDoubleSpinBox;
1701 tab.pos_x_spin->setRange(-1000.0, 1000.0);
1702 tab.pos_x_spin->setDecimals(3);
1703 tab.pos_x_spin->setSingleStep(0.1);
1705 tab.pos_x_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1706 &Nav2Panel::updateAccumulatedPosesFromTabs);
1707 layout->addWidget(tab.pos_x_spin, 1, 1);
1709 layout->addWidget(
new QLabel(
"Position Y:"), 2, 0);
1710 tab.pos_y_spin =
new QDoubleSpinBox;
1711 tab.pos_y_spin->setRange(-1000.0, 1000.0);
1712 tab.pos_y_spin->setDecimals(3);
1713 tab.pos_y_spin->setSingleStep(0.1);
1715 tab.pos_y_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1716 &Nav2Panel::updateAccumulatedPosesFromTabs);
1717 layout->addWidget(tab.pos_y_spin, 2, 1);
1719 layout->addWidget(
new QLabel(
"Yaw (radians):"), 3, 0);
1720 tab.yaw_spin =
new QDoubleSpinBox;
1721 tab.yaw_spin->setRange(-M_PI, M_PI);
1722 tab.yaw_spin->setDecimals(4);
1723 tab.yaw_spin->setSingleStep(0.01);
1725 tab.yaw_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1726 &Nav2Panel::updateAccumulatedPosesFromTabs);
1727 layout->addWidget(tab.yaw_spin, 3, 1);
1729 tab_widget->setLayout(layout);
1731 nav_through_poses_tabs_->addTab(tab_widget, QString(
"Pose %1").arg(index + 1));
1732 nav_through_pose_tabs_.push_back(tab);
1736 Nav2Panel::syncTabsWithAccumulatedPoses()
1738 while (nav_through_poses_tabs_->count() > 0) {
1739 nav_through_poses_tabs_->removeTab(0);
1741 nav_through_pose_tabs_.clear();
1743 for (
size_t i = 0; i < acummulated_poses_.goals.size(); ++i) {
1744 createNavThroughPoseTab(i);
1746 const auto & pose = acummulated_poses_.goals[i];
1748 nav_through_pose_tabs_[i].frame_id_edit->blockSignals(
true);
1749 nav_through_pose_tabs_[i].pos_x_spin->blockSignals(
true);
1750 nav_through_pose_tabs_[i].pos_y_spin->blockSignals(
true);
1751 nav_through_pose_tabs_[i].yaw_spin->blockSignals(
true);
1753 nav_through_pose_tabs_[i].frame_id_edit->setText(
1754 QString::fromStdString(pose.header.frame_id));
1755 nav_through_pose_tabs_[i].pos_x_spin->setValue(pose.pose.position.x);
1756 nav_through_pose_tabs_[i].pos_y_spin->setValue(pose.pose.position.y);
1759 pose.pose.orientation.x,
1760 pose.pose.orientation.y,
1761 pose.pose.orientation.z,
1762 pose.pose.orientation.w);
1763 tf2::Matrix3x3 m(q);
1764 double roll, pitch, yaw;
1765 m.getRPY(roll, pitch, yaw);
1766 nav_through_pose_tabs_[i].yaw_spin->setValue(yaw);
1768 nav_through_pose_tabs_[i].frame_id_edit->blockSignals(
false);
1769 nav_through_pose_tabs_[i].pos_x_spin->blockSignals(
false);
1770 nav_through_pose_tabs_[i].pos_y_spin->blockSignals(
false);
1771 nav_through_pose_tabs_[i].yaw_spin->blockSignals(
false);
1776 Nav2Panel::updateAccumulatedPosesFromTabs()
1778 acummulated_poses_.goals.clear();
1780 for (
const auto & tab : nav_through_pose_tabs_) {
1781 geometry_msgs::msg::PoseStamped pose;
1782 pose.header.frame_id = tab.frame_id_edit->text().toStdString();
1783 pose.header.stamp = client_node_->now();
1784 pose.pose.position.x = tab.pos_x_spin->value();
1785 pose.pose.position.y = tab.pos_y_spin->value();
1786 pose.pose.position.z = 0.0;
1787 pose.pose.orientation = orientationAroundZAxis(tab.yaw_spin->value());
1788 acummulated_poses_.goals.push_back(pose);
1791 updateWpNavigationMarkers();
1796 #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.