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/duration.hpp"
34 #include "rclcpp/executors/single_threaded_executor.hpp"
35 #include "rclcpp/future_return_code.hpp"
36 #include "rclcpp/logger.hpp"
37 #include "rclcpp/logging.hpp"
38 #include "rclcpp/node.hpp"
39 #include "rclcpp/node_options.hpp"
40 #include "rclcpp/parameter_value.hpp"
41 #include "rclcpp/qos.hpp"
42 #include "rviz_common/display_context.hpp"
43 #include "rviz_common/load_resource.hpp"
44 #include "yaml-cpp/yaml.h"
45 #include "geometry_msgs/msg/pose.hpp"
47 using namespace std::chrono_literals;
49 namespace nav2_rviz_plugins
51 using nav2_util::geometry_utils::orientationAroundZAxis;
54 GoalPoseUpdater GoalUpdater;
56 Nav2Panel::Nav2Panel(QWidget * parent)
62 start_reset_button_ =
new QPushButton;
63 pause_resume_button_ =
new QPushButton;
64 navigation_mode_button_ =
new QPushButton;
65 add_pose_button_ =
new QPushButton;
66 remove_pose_button_ =
new QPushButton;
67 save_waypoints_button_ =
new QPushButton;
68 load_waypoints_button_ =
new QPushButton;
69 pause_waypoint_button_ =
new QPushButton;
70 start_nav_to_pose_button_ =
new QPushButton;
71 navigation_status_indicator_ =
new QLabel;
72 navigation_goal_status_indicator_ =
new QLabel;
73 navigation_feedback_indicator_ =
new QLabel;
74 waypoint_status_indicator_ =
new QLabel;
75 number_of_loops_ =
new QLabel;
76 nr_of_loops_ =
new QLineEdit;
77 store_initial_pose_checkbox_ =
new QCheckBox(
"Store initial_pose");
80 behavior_tree_file_ =
new QLineEdit;
81 behavior_tree_file_->setPlaceholderText(
"Leave empty for default behavior tree");
85 const char * startup_msg =
"Configure and activate all nav2 lifecycle nodes";
86 const char * shutdown_msg =
"Deactivate and cleanup all nav2 lifecycle nodes";
87 const char * cancel_msg =
"Cancel navigation";
88 const char * pause_msg =
"Deactivate all nav2 lifecycle nodes";
89 const char * resume_msg =
"Activate all nav2 lifecycle nodes";
90 const char * single_goal_msg =
91 "Change to waypoint Following / nav through poses style navigation";
92 const char * waypoint_goal_msg =
"Start following waypoints";
93 const char * nft_goal_msg =
"Start navigating through poses";
94 const char * cancel_waypoint_msg =
"Cancel waypoint / viapoint accumulation mode";
96 const QString navigation_active(
"<table><tr><td width=150><b>Navigation:</b></td>"
97 "<td><font color=green>active</color></td></tr></table>");
98 const QString navigation_inactive(
"<table><tr><td width=150><b>Navigation:</b></td>"
99 "<td>inactive</td></tr></table>");
100 const QString navigation_unknown(
"<table><tr><td width=150><b>Navigation:</b></td>"
101 "<td>unknown</td></tr></table>");
103 navigation_status_indicator_->setText(navigation_unknown);
104 navigation_goal_status_indicator_->setText(nav2_rviz_plugins::getGoalStatusLabel());
105 number_of_loops_->setText(
"Num of loops");
106 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
107 navigation_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_nav2", client_node_);
496 initial_thread_ =
new InitialThread(client_nav_);
497 connect(initial_thread_, &InitialThread::finished, initial_thread_, &QObject::deleteLater);
499 QSignalTransition * activeSignal =
new QSignalTransition(
501 &InitialThread::navigationActive);
502 activeSignal->setTargetState(idle_);
503 pre_initial_->addTransition(activeSignal);
504 QSignalTransition * inactiveSignal =
new QSignalTransition(
506 &InitialThread::navigationInactive);
507 inactiveSignal->setTargetState(initial_);
508 pre_initial_->addTransition(inactiveSignal);
511 initial_thread_, &InitialThread::navigationActive,
512 [
this, navigation_active] {
513 navigation_status_indicator_->setText(navigation_active);
516 initial_thread_, &InitialThread::navigationInactive,
517 [
this, navigation_inactive] {
518 navigation_status_indicator_->setText(navigation_inactive);
519 navigation_goal_status_indicator_->setText(nav2_rviz_plugins::getGoalStatusLabel());
520 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
522 state_machine_.addState(pre_initial_);
523 state_machine_.addState(initial_);
524 state_machine_.addState(idle_);
525 state_machine_.addState(running_);
526 state_machine_.addState(canceled_);
527 state_machine_.addState(reset_);
528 state_machine_.addState(paused_);
529 state_machine_.addState(resumed_);
530 state_machine_.addState(accumulating_);
531 state_machine_.addState(accumulated_wp_);
532 state_machine_.addState(accumulated_nav_through_poses_);
533 state_machine_.addState(resumed_wp_);
535 state_machine_.setInitialState(pre_initial_);
538 QObject::connect(&state_machine_, SIGNAL(started()),
this, SLOT(startThread()));
539 state_machine_.start();
542 QVBoxLayout * main_layout =
new QVBoxLayout;
543 QHBoxLayout * side_layout =
new QHBoxLayout;
544 QVBoxLayout * status_layout =
new QVBoxLayout;
545 QHBoxLayout * logo_layout =
new QHBoxLayout;
547 imgDisplayLabel_ =
new QLabel(
"");
548 imgDisplayLabel_->setPixmap(
549 rviz_common::loadPixmap(
"package://nav2_rviz_plugins/icons/classes/nav2_logo_small.png"));
551 status_layout->addWidget(navigation_status_indicator_);
552 status_layout->addWidget(navigation_goal_status_indicator_);
554 logo_layout->addWidget(imgDisplayLabel_, 5, Qt::AlignRight);
556 side_layout->addLayout(status_layout);
557 side_layout->addLayout(logo_layout);
559 main_layout->addLayout(side_layout);
560 main_layout->addWidget(navigation_feedback_indicator_);
561 main_layout->addWidget(waypoint_status_indicator_);
564 QHBoxLayout * bt_layout =
new QHBoxLayout;
565 QLabel * bt_label =
new QLabel(
"Behavior Tree XML:");
566 bt_layout->addWidget(bt_label);
567 bt_layout->addWidget(behavior_tree_file_);
568 main_layout->addLayout(bt_layout);
570 main_layout->addWidget(pause_resume_button_);
571 main_layout->addWidget(start_reset_button_);
572 main_layout->addWidget(navigation_mode_button_);
575 tools_tab_widget_ =
new QTabWidget;
578 QWidget * nav_to_pose_tab =
new QWidget;
579 QGridLayout * nav_to_pose_layout =
new QGridLayout;
581 nav_to_pose_layout->addWidget(
new QLabel(
"Frame ID:"), 0, 0);
582 nav_to_pose_frame_id_ =
new QLineEdit(
"map");
583 nav_to_pose_layout->addWidget(nav_to_pose_frame_id_, 0, 1);
585 nav_to_pose_layout->addWidget(
new QLabel(
"Position X:"), 1, 0);
586 nav_to_pose_x_ =
new QDoubleSpinBox;
587 nav_to_pose_x_->setRange(-1000.0, 1000.0);
588 nav_to_pose_x_->setDecimals(3);
589 nav_to_pose_x_->setSingleStep(0.1);
590 nav_to_pose_layout->addWidget(nav_to_pose_x_, 1, 1);
592 nav_to_pose_layout->addWidget(
new QLabel(
"Position Y:"), 2, 0);
593 nav_to_pose_y_ =
new QDoubleSpinBox;
594 nav_to_pose_y_->setRange(-1000.0, 1000.0);
595 nav_to_pose_y_->setDecimals(3);
596 nav_to_pose_y_->setSingleStep(0.1);
597 nav_to_pose_layout->addWidget(nav_to_pose_y_, 2, 1);
599 nav_to_pose_layout->addWidget(
new QLabel(
"Yaw (radians):"), 3, 0);
600 nav_to_pose_yaw_ =
new QDoubleSpinBox;
601 nav_to_pose_yaw_->setRange(-M_PI, M_PI);
602 nav_to_pose_yaw_->setDecimals(4);
603 nav_to_pose_yaw_->setSingleStep(0.01);
604 nav_to_pose_layout->addWidget(nav_to_pose_yaw_, 3, 1);
606 QObject::connect(start_nav_to_pose_button_,
607 &QPushButton::clicked,
this, &Nav2Panel::onSendNavToPose);
608 nav_to_pose_layout->addWidget(start_nav_to_pose_button_, 4, 0, 1, 2);
610 nav_to_pose_tab->setLayout(nav_to_pose_layout);
611 tools_tab_widget_->addTab(nav_to_pose_tab,
"NavigateToPose");
614 QWidget * nav_wp_tab =
new QWidget;
615 QVBoxLayout * nav_wp_layout =
new QVBoxLayout;
617 QLabel * poses_info =
new QLabel(
"Accumulated poses:");
618 nav_wp_layout->addWidget(poses_info);
620 nav_through_poses_tabs_ =
new QTabWidget;
621 nav_wp_layout->addWidget(nav_through_poses_tabs_);
623 QHBoxLayout * pose_buttons_layout =
new QHBoxLayout;
624 QObject::connect(add_pose_button_, &QPushButton::clicked,
this, &Nav2Panel::onAddNavThroughPose);
625 pose_buttons_layout->addWidget(add_pose_button_);
627 QObject::connect(remove_pose_button_,
628 &QPushButton::clicked,
this, &Nav2Panel::onRemoveNavThroughPose);
629 pose_buttons_layout->addWidget(remove_pose_button_);
631 nav_wp_layout->addLayout(pose_buttons_layout);
633 QHBoxLayout * file_buttons_layout =
new QHBoxLayout;
634 file_buttons_layout->addWidget(save_waypoints_button_);
635 file_buttons_layout->addWidget(load_waypoints_button_);
636 nav_wp_layout->addLayout(file_buttons_layout);
639 QLabel * wp_options_label =
new QLabel(
"<b>Waypoint Following Options:</b>");
640 nav_wp_layout->addWidget(wp_options_label);
642 QHBoxLayout * wp_controls_layout =
new QHBoxLayout;
643 wp_controls_layout->addWidget(pause_waypoint_button_);
644 nav_wp_layout->addLayout(wp_controls_layout);
646 QHBoxLayout * wp_loop_layout =
new QHBoxLayout;
647 wp_loop_layout->addWidget(number_of_loops_);
648 wp_loop_layout->addWidget(nr_of_loops_);
649 wp_loop_layout->addWidget(store_initial_pose_checkbox_);
650 nav_wp_layout->addLayout(wp_loop_layout);
652 nav_wp_tab->setLayout(nav_wp_layout);
653 tools_tab_widget_->addTab(nav_wp_tab,
"NavigateThroughPoses / Waypoint Following");
655 tools_tab_widget_->setTabEnabled(0,
false);
656 tools_tab_widget_->setTabEnabled(1,
false);
658 tools_tab_widget_->setCurrentIndex(0);
660 main_layout->addWidget(tools_tab_widget_);
661 main_layout->setContentsMargins(10, 10, 10, 10);
662 setLayout(main_layout);
664 navigation_action_client_ =
665 rclcpp_action::create_client<nav2_msgs::action::NavigateToPose>(
668 waypoint_follower_action_client_ =
669 rclcpp_action::create_client<nav2_msgs::action::FollowWaypoints>(
672 nav_through_poses_action_client_ =
673 rclcpp_action::create_client<nav2_msgs::action::NavigateThroughPoses>(
675 "navigate_through_poses");
678 tf2_buffer_ = nav2::create_transform_buffer(client_node_);
679 transform_listener_ = nav2::create_transform_listener(*tf2_buffer_);
681 navigation_goal_ = nav2_msgs::action::NavigateToPose::Goal();
682 waypoint_follower_goal_ = nav2_msgs::action::FollowWaypoints::Goal();
683 nav_through_poses_goal_ = nav2_msgs::action::NavigateThroughPoses::Goal();
685 wp_navigation_markers_pub_ =
686 client_node_->create_publisher<visualization_msgs::msg::MarkerArray>(
688 rclcpp::QoS(1).transient_local());
691 &GoalUpdater, SIGNAL(updateGoal(
double,
double,
double,QString)),
692 this, SLOT(onNewGoal(
double,
double,
double,QString)));
695 Nav2Panel::~Nav2Panel()
699 void Nav2Panel::initialStateHandler()
701 if (store_initial_pose_checkbox_->isChecked()) {
702 store_initial_pose_ =
true;
705 store_initial_pose_ =
false;
709 bool Nav2Panel::isLoopValueValid(std::string & loop_value)
712 if (loop_value.empty()) {
713 std::cout <<
"Loop value cannot be set to empty, setting to 0" << std::endl;
715 nr_of_loops_->setText(
"0");
720 for (
char & c : loop_value) {
721 if (isalpha(c) || isspace(c) || ispunct(c)) {
722 waypoint_status_indicator_->setText(
723 "<b> Note: </b> Set a valid value for the loop");
724 std::cout <<
"Set a valid value for the loop, check for alphabets and spaces" << std::endl;
725 navigation_mode_button_->setEnabled(
false);
732 }
catch (std::invalid_argument
const & ex) {
734 waypoint_status_indicator_->setText(
"<b> Note: </b> Set a valid value for the loop");
735 navigation_mode_button_->setEnabled(
false);
737 }
catch (std::out_of_range
const & ex) {
739 waypoint_status_indicator_->setText(
740 "<b> Note: </b> Loop value out of range, setting max possible value"
742 loop_value = std::to_string(std::numeric_limits<int>::max());
743 nr_of_loops_->setText(QString::fromStdString(loop_value));
748 void Nav2Panel::loophandler()
750 loop_no_ = nr_of_loops_->displayText().toStdString();
753 if (!isLoopValueValid(loop_no_)) {
758 navigation_mode_button_->setEnabled(
true);
761 if (!loop_no_.empty() && stoi(loop_no_) > 0) {
762 pause_resume_button_->setEnabled(
false);
764 pause_resume_button_->setEnabled(
true);
768 void Nav2Panel::handleGoalLoader()
770 std::cout <<
"Loading Waypoints!" << std::endl;
772 QString file = QFileDialog::getOpenFileName(
775 tr(
"yaml(*.yaml);;All Files (*)"));
777 YAML::Node available_waypoints;
780 available_waypoints = YAML::LoadFile(file.toStdString());
781 }
catch (
const std::exception & ex) {
782 std::cout << ex.what() <<
", please select a valid file" << std::endl;
786 const YAML::Node & waypoint_iter = available_waypoints[
"waypoints"];
787 for (YAML::const_iterator it = waypoint_iter.begin(); it != waypoint_iter.end(); ++it) {
788 auto waypoint = waypoint_iter[it->first.as<std::string>()];
789 auto pose = waypoint[
"pose"].as<std::vector<double>>();
790 auto orientation = waypoint[
"orientation"].as<std::vector<double>>();
791 acummulated_poses_.goals.push_back(convert_to_msg(pose, orientation));
794 syncTabsWithAccumulatedPoses();
795 updateWpNavigationMarkers();
798 geometry_msgs::msg::PoseStamped Nav2Panel::convert_to_msg(
799 std::vector<double> pose,
800 std::vector<double> orientation)
802 auto msg = geometry_msgs::msg::PoseStamped();
804 msg.header.frame_id =
"map";
807 msg.pose.position.x = pose[0];
808 msg.pose.position.y = pose[1];
809 msg.pose.position.z = pose[2];
811 msg.pose.orientation.w = orientation[0];
812 msg.pose.orientation.x = orientation[1];
813 msg.pose.orientation.y = orientation[2];
814 msg.pose.orientation.z = orientation[3];
819 void Nav2Panel::handleGoalSaver()
823 if (acummulated_poses_.goals.empty()) {
824 std::cout <<
"No accumulated Points to Save!" << std::endl;
827 std::cout <<
"Saving Waypoints!" << std::endl;
831 out << YAML::BeginMap;
832 out << YAML::Key <<
"waypoints";
833 out << YAML::BeginMap;
836 for (
unsigned int i = 0; i < acummulated_poses_.goals.size(); ++i) {
837 out << YAML::Key <<
"waypoint" + std::to_string(i);
838 out << YAML::BeginMap;
839 out << YAML::Key <<
"pose";
840 std::vector<double> pose =
841 {acummulated_poses_.goals[i].pose.position.x, acummulated_poses_.goals[i].pose.position.y,
842 acummulated_poses_.goals[i].pose.position.z};
843 out << YAML::Value << pose;
844 out << YAML::Key <<
"orientation";
845 std::vector<double> orientation =
846 {acummulated_poses_.goals[i].pose.orientation.w, acummulated_poses_.goals[i].pose.orientation.x,
847 acummulated_poses_.goals[i].pose.orientation.y,
848 acummulated_poses_.goals[i].pose.orientation.z};
849 out << YAML::Value << orientation;
854 QString file = QFileDialog::getSaveFileName(
857 tr(
"yaml(*.yaml);;All Files (*)"));
859 if (!file.toStdString().empty()) {
860 std::ofstream fout(file.toStdString() +
".yaml");
862 std::cout <<
"Saving waypoints succeeded" << std::endl;
864 std::cout <<
"Saving waypoints aborted" << std::endl;
869 Nav2Panel::onInitialize()
871 node_ptr_ = getDisplayContext()->getRosNodeAbstraction().lock();
872 if (node_ptr_ ==
nullptr) {
875 rclcpp::get_logger(
"nav2_panel"),
876 "Underlying ROS node no longer exists, initialization failed");
879 rclcpp::Node::SharedPtr node = node_ptr_->get_raw_node();
882 node->declare_parameter(
"base_frame", rclcpp::ParameterValue(std::string(
"base_footprint")));
883 node->get_parameter(
"base_frame", base_frame_);
886 navigation_feedback_sub_ =
887 node->create_subscription<nav2_msgs::action::NavigateToPose::Impl::FeedbackMessage>(
888 "navigate_to_pose/_action/feedback",
889 rclcpp::SystemDefaultsQoS(),
890 [
this](
const nav2_msgs::action::NavigateToPose::Impl::FeedbackMessage::ConstSharedPtr & msg) {
891 if (stoi(nr_of_loops_->displayText().toStdString()) > 0) {
892 if (goal_index_ == 0 && !loop_counter_stop_) {
894 loop_counter_stop_ =
true;
896 if (goal_index_ != 0) {
897 loop_counter_stop_ =
false;
899 navigation_feedback_indicator_->setText(
900 getNavToPoseFeedbackLabel(msg->feedback) + QString(
902 "</td></tr><tr><td width=150>Waypoint:</td><td>" +
903 toString(goal_index_ + 1)).c_str()) + QString(
905 "</td></tr><tr><td width=150>Loop:</td><td>" +
906 toString(loop_count_)).c_str()));
908 navigation_feedback_indicator_->setText(getNavToPoseFeedbackLabel(msg->feedback));
911 nav_through_poses_feedback_sub_ =
912 node->create_subscription<nav2_msgs::action::NavigateThroughPoses::Impl::FeedbackMessage>(
913 "navigate_through_poses/_action/feedback",
914 rclcpp::SystemDefaultsQoS(),
915 [
this](
const nav2_msgs::action::NavigateThroughPoses::Impl::FeedbackMessage::ConstSharedPtr &
917 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel(msg->feedback));
921 navigation_goal_status_sub_ = node->create_subscription<action_msgs::msg::GoalStatusArray>(
922 "navigate_to_pose/_action/status",
923 rclcpp::SystemDefaultsQoS(),
924 [
this](
const action_msgs::msg::GoalStatusArray::ConstSharedPtr & msg) {
925 navigation_goal_status_indicator_->setText(
926 nav2_rviz_plugins::getGoalStatusLabel(
"Feedback", msg->status_list.back().status));
929 loop_count_ == stoi(nr_of_loops_->displayText().toStdString()) &&
930 goal_index_ ==
static_cast<int>(store_poses_.goals.size()) - 1 &&
931 msg->status_list.back().status == action_msgs::msg::GoalStatus::STATUS_SUCCEEDED)
933 store_poses_ = nav_msgs::msg::Goals();
934 waypoint_status_indicator_->clear();
937 navigation_feedback_indicator_->setText(getNavToPoseFeedbackLabel());
940 nav_through_poses_goal_status_sub_ = node->create_subscription<action_msgs::msg::GoalStatusArray>(
941 "navigate_through_poses/_action/status",
942 rclcpp::SystemDefaultsQoS(),
943 [
this](
const action_msgs::msg::GoalStatusArray::ConstSharedPtr & msg) {
944 navigation_goal_status_indicator_->setText(
945 nav2_rviz_plugins::getGoalStatusLabel(
"Feedback", msg->status_list.back().status));
946 if (msg->status_list.back().status != action_msgs::msg::GoalStatus::STATUS_EXECUTING) {
947 navigation_feedback_indicator_->setText(getNavThroughPosesFeedbackLabel());
953 Nav2Panel::startThread()
956 initial_thread_->start();
962 QFuture<bool> futureNav =
966 client_nav_.get(), std::placeholders::_1), server_timeout_);
970 Nav2Panel::onResume()
972 QFuture<bool> futureNav =
976 client_nav_.get(), std::placeholders::_1), server_timeout_);
982 tools_tab_widget_->setTabEnabled(0,
true);
983 tools_tab_widget_->setTabEnabled(1,
false);
984 tools_tab_widget_->setTabEnabled(2,
false);
985 tools_tab_widget_->setCurrentIndex(0);
989 Nav2Panel::onStartup()
991 QFuture<bool> futureNav =
995 client_nav_.get(), std::placeholders::_1), server_timeout_);
999 Nav2Panel::onShutdown()
1001 QFuture<bool> futureNav =
1005 client_nav_.get(), std::placeholders::_1), server_timeout_);
1010 Nav2Panel::onCancel()
1012 QFuture<void> future =
1015 &Nav2Panel::onCancelButtonPressed,
1017 waypoint_status_indicator_->clear();
1018 store_poses_ = nav_msgs::msg::Goals();
1019 acummulated_poses_ = nav_msgs::msg::Goals();
1022 void Nav2Panel::onResumedWp()
1024 QFuture<void> future =
1027 &Nav2Panel::onCancelButtonPressed,
1029 acummulated_poses_ = store_poses_;
1030 loop_no_ = std::to_string(
1031 stoi(nr_of_loops_->displayText().toStdString()) -
1033 waypoint_status_indicator_->setText(
1034 QString(std::string(
"<b> Note: </b> Navigation is paused.").c_str()));
1038 Nav2Panel::onNewGoal(
double x,
double y,
double theta, QString frame)
1040 auto pose = geometry_msgs::msg::PoseStamped();
1043 pose.header.frame_id = frame.toStdString();
1044 pose.pose.position.x = x;
1045 pose.pose.position.y = y;
1046 pose.pose.position.z = 0.0;
1047 pose.pose.orientation = orientationAroundZAxis(theta);
1049 if (store_poses_.goals.empty()) {
1050 if (state_machine_.configuration().contains(accumulating_)) {
1051 waypoint_status_indicator_->clear();
1052 acummulated_poses_.goals.push_back(pose);
1053 syncTabsWithAccumulatedPoses();
1055 acummulated_poses_ = nav_msgs::msg::Goals();
1056 updateWpNavigationMarkers();
1057 std::cout <<
"Start navigation" << std::endl;
1058 startNavigation(pose);
1061 waypoint_status_indicator_->setText(
1062 QString(std::string(
"<b> Note: </b> Cannot set goal in pause state").c_str()));
1064 updateWpNavigationMarkers();
1068 Nav2Panel::onCancelButtonPressed()
1070 if (navigation_goal_handle_) {
1071 auto future_cancel = navigation_action_client_->async_cancel_goal(navigation_goal_handle_);
1073 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1074 rclcpp::FutureReturnCode::SUCCESS)
1076 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel goal");
1078 navigation_goal_handle_.reset();
1082 if (waypoint_follower_goal_handle_) {
1083 auto future_cancel =
1084 waypoint_follower_action_client_->async_cancel_goal(waypoint_follower_goal_handle_);
1086 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1087 rclcpp::FutureReturnCode::SUCCESS)
1089 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel waypoint follower");
1091 waypoint_follower_goal_handle_.reset();
1095 if (nav_through_poses_goal_handle_) {
1096 auto future_cancel =
1097 nav_through_poses_action_client_->async_cancel_goal(nav_through_poses_goal_handle_);
1099 if (executor_->spin_until_future_complete(future_cancel, server_timeout_) !=
1100 rclcpp::FutureReturnCode::SUCCESS)
1102 RCLCPP_ERROR(client_node_->get_logger(),
"Failed to cancel nav through pose action");
1104 nav_through_poses_goal_handle_.reset();
1112 Nav2Panel::onAccumulatedWp()
1114 updateAccumulatedPosesFromTabs();
1116 if (acummulated_poses_.goals.empty()) {
1117 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1118 waypoint_status_indicator_->setText(
1119 "<b> Note: </b> Uh oh! Someone forgot to select the waypoints");
1124 if (!isLoopValueValid(loop_no_)) {
1125 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1130 waypoint_status_indicator_->clear();
1133 navigation_mode_button_->setEnabled(
false);
1134 pause_resume_button_->setEnabled(
false);
1138 if (store_poses_.goals.empty()) {
1139 std::cout <<
"Start waypoint" << std::endl;
1142 nr_of_loops_->setText(QString::fromStdString(loop_no_));
1145 geometry_msgs::msg::TransformStamped init_transform;
1148 if (store_initial_pose_) {
1150 init_transform = tf2_buffer_->lookupTransform(
1151 acummulated_poses_.goals[0].header.frame_id, base_frame_,
1152 tf2::TimePointZero);
1153 }
catch (
const tf2::TransformException & ex) {
1155 client_node_->get_logger(),
"Could not transform %s to %s: %s",
1156 acummulated_poses_.goals[0].header.frame_id.c_str(), base_frame_.c_str(), ex.what());
1161 geometry_msgs::msg::PoseStamped initial_pose;
1162 initial_pose.header = init_transform.header;
1163 initial_pose.pose.position.x = init_transform.transform.translation.x;
1164 initial_pose.pose.position.y = init_transform.transform.translation.y;
1165 initial_pose.pose.position.z = init_transform.transform.translation.z;
1166 initial_pose.pose.orientation.x = init_transform.transform.rotation.x;
1167 initial_pose.pose.orientation.y = init_transform.transform.rotation.y;
1168 initial_pose.pose.orientation.z = init_transform.transform.rotation.z;
1169 initial_pose.pose.orientation.w = init_transform.transform.rotation.w;
1172 acummulated_poses_.goals.insert(acummulated_poses_.goals.begin(), initial_pose);
1173 syncTabsWithAccumulatedPoses();
1174 updateWpNavigationMarkers();
1175 initial_pose_stored_ =
true;
1176 if (loop_count_ == 0) {
1179 }
else if (!store_initial_pose_ && initial_pose_stored_) {
1180 acummulated_poses_.goals.erase(
1181 acummulated_poses_.goals.begin(),
1182 acummulated_poses_.goals.begin());
1185 std::cout <<
"Resuming waypoint" << std::endl;
1188 startWaypointFollowing(acummulated_poses_.goals);
1189 store_poses_ = acummulated_poses_;
1193 Nav2Panel::onAccumulatedNTP()
1195 updateAccumulatedPosesFromTabs();
1197 std::cout <<
"Start navigate through poses" << std::endl;
1198 startNavThroughPoses(acummulated_poses_);
1202 Nav2Panel::onAccumulating()
1204 acummulated_poses_ = nav_msgs::msg::Goals();
1205 store_poses_ = nav_msgs::msg::Goals();
1208 initial_pose_stored_ =
false;
1209 loop_counter_stop_ =
true;
1211 updateWpNavigationMarkers();
1212 syncTabsWithAccumulatedPoses();
1214 nav_to_pose_frame_id_->setText(
"map");
1215 nav_to_pose_x_->setValue(0.0);
1216 nav_to_pose_y_->setValue(0.0);
1217 nav_to_pose_yaw_->setValue(0.0);
1219 tools_tab_widget_->setTabEnabled(0,
false);
1220 tools_tab_widget_->setTabEnabled(1,
true);
1221 tools_tab_widget_->setCurrentIndex(1);
1224 Nav2Panel::timerEvent(QTimerEvent * event)
1226 if (state_machine_.configuration().contains(accumulated_wp_)) {
1227 if (event->timerId() == timer_.timerId()) {
1228 if (!waypoint_follower_goal_handle_) {
1229 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1230 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1235 auto goal_handle = waypoint_follower_goal_handle_;
1236 executor_->spin_some();
1237 auto status = goal_handle->get_status();
1240 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1241 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1243 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1245 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1246 acummulated_poses_ = nav_msgs::msg::Goals();
1247 updateWpNavigationMarkers();
1251 }
else if (state_machine_.configuration().contains(accumulated_nav_through_poses_)) {
1252 if (event->timerId() == timer_.timerId()) {
1253 if (!nav_through_poses_goal_handle_) {
1254 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1255 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1260 auto goal_handle = nav_through_poses_goal_handle_;
1261 executor_->spin_some();
1262 auto status = goal_handle->get_status();
1265 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1266 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1268 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1270 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1271 acummulated_poses_ = nav_msgs::msg::Goals();
1272 updateWpNavigationMarkers();
1277 if (event->timerId() == timer_.timerId()) {
1278 if (!navigation_goal_handle_) {
1279 RCLCPP_DEBUG(client_node_->get_logger(),
"Waiting for Goal");
1280 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1285 auto goal_handle = navigation_goal_handle_;
1286 executor_->spin_some();
1287 auto status = goal_handle->get_status();
1290 if (status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED ||
1291 status == action_msgs::msg::GoalStatus::STATUS_EXECUTING)
1293 state_machine_.postEvent(
new ROSActionQEvent(QActionState::ACTIVE));
1295 state_machine_.postEvent(
new ROSActionQEvent(QActionState::INACTIVE));
1296 nav_to_pose_frame_id_->setText(
"map");
1297 nav_to_pose_x_->setValue(0.0);
1298 nav_to_pose_y_->setValue(0.0);
1299 nav_to_pose_yaw_->setValue(0.0);
1307 Nav2Panel::startWaypointFollowing(std::vector<geometry_msgs::msg::PoseStamped> poses)
1309 auto is_action_server_ready =
1310 waypoint_follower_action_client_->wait_for_action_server(std::chrono::seconds(5));
1311 if (!is_action_server_ready) {
1313 client_node_->get_logger(),
"follow_waypoints action server is not available."
1314 " Is the initial pose set?");
1319 waypoint_follower_goal_.poses = poses;
1320 waypoint_follower_goal_.goal_index = goal_index_;
1321 waypoint_follower_goal_.number_of_loops = stoi(loop_no_);
1324 client_node_->get_logger(),
"Sending a path of %zu waypoints:",
1325 waypoint_follower_goal_.poses.size());
1326 for (
auto waypoint : waypoint_follower_goal_.poses) {
1328 client_node_->get_logger(),
1329 "\t(%lf, %lf)", waypoint.pose.position.x, waypoint.pose.position.y);
1333 auto send_goal_options =
1334 nav2::ActionClient<nav2_msgs::action::FollowWaypoints>::SendGoalOptions();
1335 send_goal_options.result_callback = [
this](
const auto & result) {
1338 if (waypoint_follower_goal_handle_ &&
1339 waypoint_follower_goal_handle_->get_goal_id() == result.goal_id)
1341 waypoint_follower_goal_handle_.reset();
1345 send_goal_options.feedback_callback = [
this](
1346 WaypointFollowerGoalHandle::SharedPtr ,
1347 const std::shared_ptr<const nav2_msgs::action::FollowWaypoints::Feedback> feedback) {
1348 goal_index_ = feedback->current_waypoint;
1351 auto future_goal_handle =
1352 waypoint_follower_action_client_->async_send_goal(waypoint_follower_goal_, send_goal_options);
1353 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1354 rclcpp::FutureReturnCode::SUCCESS)
1356 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1361 waypoint_follower_goal_handle_ = future_goal_handle.get();
1362 if (!waypoint_follower_goal_handle_) {
1363 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1367 timer_.start(200,
this);
1371 Nav2Panel::startNavThroughPoses(nav_msgs::msg::Goals poses)
1373 auto is_action_server_ready =
1374 nav_through_poses_action_client_->wait_for_action_server(std::chrono::seconds(5));
1375 if (!is_action_server_ready) {
1377 client_node_->get_logger(),
"navigate_through_poses action server is not available."
1378 " Is the initial pose set?");
1382 nav_through_poses_goal_.poses = poses;
1383 nav_through_poses_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1385 if (nav_through_poses_goal_.behavior_tree.empty()) {
1387 client_node_->get_logger(),
1388 "NavigateThroughPoses will be called using the BT Navigator's default behavior tree.");
1391 client_node_->get_logger(),
1392 "NavigateThroughPoses will be called using behavior tree: %s",
1393 nav_through_poses_goal_.behavior_tree.c_str());
1397 client_node_->get_logger(),
"Sending a path of %zu waypoints:",
1398 nav_through_poses_goal_.poses.goals.size());
1399 for (
auto waypoint : nav_through_poses_goal_.poses.goals) {
1401 client_node_->get_logger(),
1402 "\t(%lf, %lf)", waypoint.pose.position.x, waypoint.pose.position.y);
1406 auto send_goal_options =
1407 nav2::ActionClient<nav2_msgs::action::NavigateThroughPoses>::SendGoalOptions();
1408 send_goal_options.result_callback = [
this](
const auto & result) {
1411 if (nav_through_poses_goal_handle_ &&
1412 nav_through_poses_goal_handle_->get_goal_id() == result.goal_id)
1414 nav_through_poses_goal_handle_.reset();
1418 auto future_goal_handle =
1419 nav_through_poses_action_client_->async_send_goal(nav_through_poses_goal_, send_goal_options);
1420 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1421 rclcpp::FutureReturnCode::SUCCESS)
1423 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1428 nav_through_poses_goal_handle_ = future_goal_handle.get();
1429 if (!nav_through_poses_goal_handle_) {
1430 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1434 timer_.start(200,
this);
1438 Nav2Panel::startNavigation(geometry_msgs::msg::PoseStamped pose)
1440 auto is_action_server_ready =
1441 navigation_action_client_->wait_for_action_server(std::chrono::seconds(5));
1442 if (!is_action_server_ready) {
1444 client_node_->get_logger(),
1445 "navigate_to_pose action server is not available."
1446 " Is the initial pose set?");
1451 navigation_goal_.pose = pose;
1452 navigation_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1454 if (navigation_goal_.behavior_tree.empty()) {
1456 client_node_->get_logger(),
1457 "NavigateToPose will be called using the BT Navigator's default behavior tree.");
1460 client_node_->get_logger(),
1461 "NavigateToPose will be called using behavior tree: %s",
1462 navigation_goal_.behavior_tree.c_str());
1466 auto send_goal_options =
1467 nav2::ActionClient<nav2_msgs::action::NavigateToPose>::SendGoalOptions();
1468 send_goal_options.result_callback = [
this](
const auto & result) {
1471 if (navigation_goal_handle_ && navigation_goal_handle_->get_goal_id() == result.goal_id) {
1472 navigation_goal_handle_.reset();
1476 auto future_goal_handle =
1477 navigation_action_client_->async_send_goal(navigation_goal_, send_goal_options);
1478 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1479 rclcpp::FutureReturnCode::SUCCESS)
1481 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1486 navigation_goal_handle_ = future_goal_handle.get();
1487 if (!navigation_goal_handle_) {
1488 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1492 timer_.start(200,
this);
1496 Nav2Panel::save(rviz_common::Config config)
const
1498 Panel::save(config);
1502 Nav2Panel::load(
const rviz_common::Config & config)
1504 Panel::load(config);
1508 Nav2Panel::resetUniqueId()
1514 Nav2Panel::getUniqueId()
1516 int temp_id = unique_id;
1522 Nav2Panel::updateWpNavigationMarkers()
1526 auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
1528 visualization_msgs::msg::Marker clear_all_marker;
1529 clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
1530 marker_array->markers.push_back(clear_all_marker);
1532 wp_navigation_markers_pub_->publish(std::move(marker_array));
1534 marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
1536 for (
size_t i = 0; i < acummulated_poses_.goals.size(); i++) {
1538 visualization_msgs::msg::Marker arrow_marker;
1539 arrow_marker.header = acummulated_poses_.goals[i].header;
1540 arrow_marker.id = getUniqueId();
1541 arrow_marker.type = visualization_msgs::msg::Marker::ARROW;
1542 arrow_marker.action = visualization_msgs::msg::Marker::ADD;
1543 arrow_marker.pose = acummulated_poses_.goals[i].pose;
1544 arrow_marker.scale.x = 0.3;
1545 arrow_marker.scale.y = 0.05;
1546 arrow_marker.scale.z = 0.02;
1547 arrow_marker.color.r = 0;
1548 arrow_marker.color.g = 255;
1549 arrow_marker.color.b = 0;
1550 arrow_marker.color.a = 1.0f;
1551 arrow_marker.lifetime = rclcpp::Duration(0s);
1552 arrow_marker.frame_locked =
false;
1553 marker_array->markers.push_back(arrow_marker);
1556 visualization_msgs::msg::Marker circle_marker;
1557 circle_marker.header = acummulated_poses_.goals[i].header;
1558 circle_marker.id = getUniqueId();
1559 circle_marker.type = visualization_msgs::msg::Marker::SPHERE;
1560 circle_marker.action = visualization_msgs::msg::Marker::ADD;
1561 circle_marker.pose = acummulated_poses_.goals[i].pose;
1562 circle_marker.scale.x = 0.05;
1563 circle_marker.scale.y = 0.05;
1564 circle_marker.scale.z = 0.05;
1565 circle_marker.color.r = 255;
1566 circle_marker.color.g = 0;
1567 circle_marker.color.b = 0;
1568 circle_marker.color.a = 1.0f;
1569 circle_marker.lifetime = rclcpp::Duration(0s);
1570 circle_marker.frame_locked =
false;
1571 marker_array->markers.push_back(circle_marker);
1574 visualization_msgs::msg::Marker marker_text;
1575 marker_text.header = acummulated_poses_.goals[i].header;
1576 marker_text.id = getUniqueId();
1577 marker_text.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
1578 marker_text.action = visualization_msgs::msg::Marker::ADD;
1579 marker_text.pose = acummulated_poses_.goals[i].pose;
1580 marker_text.pose.position.z += 0.2;
1581 marker_text.scale.x = 0.07;
1582 marker_text.scale.y = 0.07;
1583 marker_text.scale.z = 0.07;
1584 marker_text.color.r = 0;
1585 marker_text.color.g = 255;
1586 marker_text.color.b = 0;
1587 marker_text.color.a = 1.0f;
1588 marker_text.lifetime = rclcpp::Duration(0s);
1589 marker_text.frame_locked =
false;
1590 marker_text.text =
"wp_" + std::to_string(i + 1);
1591 marker_array->markers.push_back(marker_text);
1594 wp_navigation_markers_pub_->publish(std::move(marker_array));
1598 Nav2Panel::getNavToPoseFeedbackLabel(nav2_msgs::action::NavigateToPose::Feedback msg)
1600 return QString(std::string(
"<table>" + toLabel(msg) +
"</table>").c_str());
1604 Nav2Panel::getNavThroughPosesFeedbackLabel(nav2_msgs::action::NavigateThroughPoses::Feedback msg)
1608 "<table><tr><td width=150>Poses remaining:</td><td>" +
1609 std::to_string(msg.number_of_poses_remaining) +
1610 "</td></tr>" + toLabel(msg) +
"</table>").c_str());
1613 template<
typename T>
1614 inline std::string Nav2Panel::toLabel(T & msg)
1617 "<tr><td width=150>ETA:</td><td>" +
1618 toString(rclcpp::Duration(msg.estimated_time_remaining).seconds(), 0) +
" s"
1619 "</td></tr><tr><td width=150>Distance remaining:</td><td>" +
1620 toString(msg.distance_remaining, 2) +
" m"
1621 "</td></tr><tr><td width=150>Position error:</td><td>" +
1622 toString(msg.position_tracking_error, 2) +
" m"
1623 "</td></tr><tr><td width=150>Heading error:</td><td>" +
1624 toString(msg.heading_tracking_error, 2) +
" rad"
1625 "</td></tr><tr><td width=150>Time taken:</td><td>" +
1626 toString(rclcpp::Duration(msg.navigation_time).seconds(), 0) +
" s"
1627 "</td></tr><tr><td width=150>Recoveries:</td><td>" +
1628 std::to_string(msg.number_of_recoveries) +
1633 Nav2Panel::toString(
double val,
int precision)
1635 std::ostringstream out;
1636 out.precision(precision);
1637 out << std::fixed << val;
1642 Nav2Panel::onSendNavToPose()
1644 auto pose = geometry_msgs::msg::PoseStamped();
1645 pose.header.frame_id = nav_to_pose_frame_id_->text().toStdString();
1646 pose.header.stamp = client_node_->now();
1647 pose.pose.position.x = nav_to_pose_x_->value();
1648 pose.pose.position.y = nav_to_pose_y_->value();
1649 pose.pose.position.z = 0.0;
1650 pose.pose.orientation = orientationAroundZAxis(nav_to_pose_yaw_->value());
1652 auto is_action_server_ready =
1653 navigation_action_client_->wait_for_action_server(std::chrono::seconds(5));
1654 if (!is_action_server_ready) {
1656 client_node_->get_logger(),
1657 "navigate_to_pose action server is not available.");
1661 navigation_goal_.pose = pose;
1662 navigation_goal_.behavior_tree = behavior_tree_file_->text().toStdString();
1664 auto send_goal_options =
1665 nav2::ActionClient<nav2_msgs::action::NavigateToPose>::SendGoalOptions();
1666 send_goal_options.result_callback = [
this](
const auto & result) {
1669 if (navigation_goal_handle_ && navigation_goal_handle_->get_goal_id() == result.goal_id) {
1670 navigation_goal_handle_.reset();
1674 auto future_goal_handle =
1675 navigation_action_client_->async_send_goal(navigation_goal_, send_goal_options);
1676 if (executor_->spin_until_future_complete(future_goal_handle, server_timeout_) !=
1677 rclcpp::FutureReturnCode::SUCCESS)
1679 RCLCPP_ERROR(client_node_->get_logger(),
"Send goal call failed");
1683 navigation_goal_handle_ = future_goal_handle.get();
1684 if (!navigation_goal_handle_) {
1685 RCLCPP_ERROR(client_node_->get_logger(),
"Goal was rejected by server");
1689 timer_.start(200,
this);
1693 Nav2Panel::onAddNavThroughPose()
1695 int index = nav_through_pose_tabs_.size();
1696 createNavThroughPoseTab(index);
1697 updateAccumulatedPosesFromTabs();
1701 Nav2Panel::onRemoveNavThroughPose()
1703 if (!nav_through_pose_tabs_.empty()) {
1704 int index = nav_through_poses_tabs_->currentIndex();
1705 if (index >= 0 && index <
static_cast<int>(nav_through_pose_tabs_.size())) {
1706 nav_through_poses_tabs_->removeTab(index);
1707 nav_through_pose_tabs_.erase(nav_through_pose_tabs_.begin() + index);
1709 for (
int i = 0; i < nav_through_poses_tabs_->count(); ++i) {
1710 nav_through_poses_tabs_->setTabText(i, QString(
"Pose %1").arg(i + 1));
1713 updateAccumulatedPosesFromTabs();
1719 Nav2Panel::createNavThroughPoseTab(
int index)
1721 QWidget * tab_widget =
new QWidget;
1722 QGridLayout * layout =
new QGridLayout;
1724 NavThroughPoseTab tab;
1726 layout->addWidget(
new QLabel(
"Frame ID:"), 0, 0);
1727 tab.frame_id_edit =
new QLineEdit(
"map");
1729 tab.frame_id_edit, &QLineEdit::textChanged,
this,
1730 &Nav2Panel::updateAccumulatedPosesFromTabs);
1731 layout->addWidget(tab.frame_id_edit, 0, 1);
1733 layout->addWidget(
new QLabel(
"Position X:"), 1, 0);
1734 tab.pos_x_spin =
new QDoubleSpinBox;
1735 tab.pos_x_spin->setRange(-1000.0, 1000.0);
1736 tab.pos_x_spin->setDecimals(3);
1737 tab.pos_x_spin->setSingleStep(0.1);
1739 tab.pos_x_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1740 &Nav2Panel::updateAccumulatedPosesFromTabs);
1741 layout->addWidget(tab.pos_x_spin, 1, 1);
1743 layout->addWidget(
new QLabel(
"Position Y:"), 2, 0);
1744 tab.pos_y_spin =
new QDoubleSpinBox;
1745 tab.pos_y_spin->setRange(-1000.0, 1000.0);
1746 tab.pos_y_spin->setDecimals(3);
1747 tab.pos_y_spin->setSingleStep(0.1);
1749 tab.pos_y_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1750 &Nav2Panel::updateAccumulatedPosesFromTabs);
1751 layout->addWidget(tab.pos_y_spin, 2, 1);
1753 layout->addWidget(
new QLabel(
"Yaw (radians):"), 3, 0);
1754 tab.yaw_spin =
new QDoubleSpinBox;
1755 tab.yaw_spin->setRange(-M_PI, M_PI);
1756 tab.yaw_spin->setDecimals(4);
1757 tab.yaw_spin->setSingleStep(0.01);
1759 tab.yaw_spin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this,
1760 &Nav2Panel::updateAccumulatedPosesFromTabs);
1761 layout->addWidget(tab.yaw_spin, 3, 1);
1763 tab_widget->setLayout(layout);
1765 nav_through_poses_tabs_->addTab(tab_widget, QString(
"Pose %1").arg(index + 1));
1766 nav_through_pose_tabs_.push_back(tab);
1770 Nav2Panel::syncTabsWithAccumulatedPoses()
1772 while (nav_through_poses_tabs_->count() > 0) {
1773 nav_through_poses_tabs_->removeTab(0);
1775 nav_through_pose_tabs_.clear();
1777 for (
size_t i = 0; i < acummulated_poses_.goals.size(); ++i) {
1778 createNavThroughPoseTab(i);
1780 const auto & pose = acummulated_poses_.goals[i];
1782 nav_through_pose_tabs_[i].frame_id_edit->blockSignals(
true);
1783 nav_through_pose_tabs_[i].pos_x_spin->blockSignals(
true);
1784 nav_through_pose_tabs_[i].pos_y_spin->blockSignals(
true);
1785 nav_through_pose_tabs_[i].yaw_spin->blockSignals(
true);
1787 nav_through_pose_tabs_[i].frame_id_edit->setText(
1788 QString::fromStdString(pose.header.frame_id));
1789 nav_through_pose_tabs_[i].pos_x_spin->setValue(pose.pose.position.x);
1790 nav_through_pose_tabs_[i].pos_y_spin->setValue(pose.pose.position.y);
1793 pose.pose.orientation.x,
1794 pose.pose.orientation.y,
1795 pose.pose.orientation.z,
1796 pose.pose.orientation.w);
1797 tf2::Matrix3x3 m(q);
1798 double roll, pitch, yaw;
1799 m.getRPY(roll, pitch, yaw);
1800 nav_through_pose_tabs_[i].yaw_spin->setValue(yaw);
1802 nav_through_pose_tabs_[i].frame_id_edit->blockSignals(
false);
1803 nav_through_pose_tabs_[i].pos_x_spin->blockSignals(
false);
1804 nav_through_pose_tabs_[i].pos_y_spin->blockSignals(
false);
1805 nav_through_pose_tabs_[i].yaw_spin->blockSignals(
false);
1810 Nav2Panel::updateAccumulatedPosesFromTabs()
1812 acummulated_poses_.goals.clear();
1814 for (
const auto & tab : nav_through_pose_tabs_) {
1815 geometry_msgs::msg::PoseStamped pose;
1816 pose.header.frame_id = tab.frame_id_edit->text().toStdString();
1817 pose.header.stamp = client_node_->now();
1818 pose.pose.position.x = tab.pos_x_spin->value();
1819 pose.pose.position.y = tab.pos_y_spin->value();
1820 pose.pose.position.z = 0.0;
1821 pose.pose.orientation = orientationAroundZAxis(tab.yaw_spin->value());
1822 acummulated_poses_.goals.push_back(pose);
1825 updateWpNavigationMarkers();
1830 #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.