SMACC2
Loading...
Searching...
No Matches
cp_waypoints_navigator.cpp
Go to the documentation of this file.
1// Copyright 2025 Robosoft Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/*****************************************************************************************************************
16 *
17 * Authors: Pablo Inigo Blasco, Brett Aldrich
18 *
19 ******************************************************************************************************************/
20
21#include <tf2/transform_datatypes.h>
22#include <yaml-cpp/yaml.h>
23
24#include <ament_index_cpp/get_package_share_directory.hpp>
25#include <cl_nav2z/cl_nav2z.hpp>
26#include <cl_nav2z/common.hpp>
32#include <fstream>
33#include <rclcpp/rclcpp.hpp>
34
35namespace cl_nav2z
36{
37using namespace std::chrono_literals;
38using namespace smacc2;
39
40CpWaypointNavigatorBase::CpWaypointNavigatorBase() : currentWaypoint_(0), waypoints_(0) {}
41
43
45
47
49{
50 this->requiresComponent(nav2ActionInterface_, ComponentRequirement::HARD);
51}
52
60
68
71{
74 RCLCPP_WARN(
75 getLogger(), "[CpWaypointNavigator] Goal result received, incrementing waypoint index: %ld",
78
79 this->notifyGoalReached();
80
82}
83
85{
88}
89
91{
93 if (currentWaypoint_ >= (long)waypoints_.size() - 1)
94 currentWaypoint_ = (long)waypoints_.size() - 1;
95}
96
98{
99 bool found = false;
100
101 auto previousWaypoint = currentWaypoint_;
102
103 while (!found && currentWaypoint_ < (long)waypoints_.size())
104 {
105 auto & nextName = waypointsNames_[currentWaypoint_];
106 RCLCPP_INFO(
107 getLogger(), "[CpWaypointNavigator] seeking ,%ld/%ld candidate waypoint: %s",
108 currentWaypoint_, waypoints_.size(), nextName.c_str());
109 if (name == nextName)
110 {
111 found = true;
112 RCLCPP_INFO(
113 getLogger(), "[CpWaypointNavigator] found target waypoint: %s == %s-> found",
114 nextName.c_str(), name.c_str());
115 }
116 else
117 {
118 RCLCPP_INFO(
119 getLogger(), "[CpWaypointNavigator] current waypoint: %s != %s -> forward",
120 nextName.c_str(), name.c_str());
122 }
123 }
124
125 if (found)
126 {
127 if (currentWaypoint_ >= (long)waypoints_.size() - 1)
128 currentWaypoint_ = (long)waypoints_.size() - 1;
129 }
130 else // search backwards
131 {
132 currentWaypoint_ = previousWaypoint;
133 while (!found && currentWaypoint_ > 0)
134 {
135 auto & nextName = waypointsNames_[currentWaypoint_];
136 RCLCPP_INFO(
137 getLogger(), "[CpWaypointNavigator] seeking , candidate waypoint: %s", nextName.c_str());
138 if (name == nextName)
139 {
140 found = true;
141 RCLCPP_INFO(
142 getLogger(), "[CpWaypointNavigator] found target waypoint: %s == %s-> found",
143 nextName.c_str(), name.c_str());
144 }
145 else
146 {
147 RCLCPP_INFO(
148 getLogger(), "[CpWaypointNavigator] current waypoint: %s != %s -> rewind",
149 nextName.c_str(), name.c_str());
151 }
152 }
153 }
154
155 RCLCPP_INFO(
156 getLogger(), "[CpWaypointNavigator] seekName( %s), previous index: %ld, after index: %ld",
157 name.c_str(), previousWaypoint, currentWaypoint_);
158}
159
161 std::string parameter_name, std::string yaml_file_package_name)
162{
163 // if it is the first time and the waypoints navigator is not configured
164 std::string planfilepath;
165 planfilepath = getNode()->declare_parameter(parameter_name, planfilepath);
166 RCLCPP_INFO(getLogger(), "waypoints plan parameter: %s", planfilepath.c_str());
167 if (getNode()->get_parameter(parameter_name, planfilepath))
168 {
169 std::string package_share_directory =
170 ament_index_cpp::get_package_share_directory(yaml_file_package_name);
171
172 RCLCPP_INFO(getLogger(), "file macro path: %s", planfilepath.c_str());
173
174 boost::replace_all(planfilepath, "$(pkg_share)", package_share_directory);
175
176 RCLCPP_INFO(getLogger(), "package share path: %s", package_share_directory.c_str());
177 RCLCPP_INFO(getLogger(), "waypoints plan file: %s", planfilepath.c_str());
178
179 this->loadWayPointsFromFile(planfilepath);
180 RCLCPP_INFO(getLogger(), "waypoints plan: %s", planfilepath.c_str());
181 }
182 else
183 {
184 RCLCPP_ERROR(getLogger(), "waypoints plan file not found: NONE");
185 }
186}
187
189{
190 if (succeededNav2ZClientConnection_.connected())
191 {
192 this->succeededNav2ZClientConnection_.disconnect();
193 this->cancelledNav2ZClientConnection_.disconnect();
194 this->abortedNav2ZClientConnection_.disconnect();
195 }
196}
197
198std::optional<std::shared_future<
199 std::shared_ptr<rclcpp_action::ClientGoalHandle<nav2_msgs::action::NavigateToPose>>>>
200CpWaypointNavigator::sendNextGoal(std::optional<NavigateNextWaypointOptions> options)
201{
202 if (currentWaypoint_ >= 0 && currentWaypoint_ < (int)waypoints_.size())
203 {
204 auto & next = waypoints_[currentWaypoint_];
205
206 std::string nextName;
207 if ((long)waypointsNames_.size() > currentWaypoint_)
208 {
210 RCLCPP_INFO(
211 getLogger(), "[CpWaypointNavigator] sending goal, waypoint: %s", nextName.c_str());
212 }
213 else
214 {
215 RCLCPP_INFO(
216 getLogger(), "[CpWaypointNavigator] sending goal, waypoint: %ld", currentWaypoint_);
217 }
218
219 nav2_msgs::action::NavigateToPose::Goal goal;
220 CpPose * p;
221 this->requiresComponent(p, ComponentRequirement::HARD);
222
223 // configuring goal
224 goal.pose.header.frame_id = p->getReferenceFrame();
225 goal.pose.pose = next;
226
227 cl_nav2z::CpPlannerSwitcher * plannerSwitcher;
228 this->requiresComponent(plannerSwitcher, ComponentRequirement::HARD);
229
230 plannerSwitcher->setDefaultPlanners(false);
231 if (options && options->controllerName_)
232 {
233 RCLCPP_WARN(
234 getLogger(), "[WaypointsNavigator] override controller: %s",
235 options->controllerName_->c_str());
236
237 plannerSwitcher->setDesiredController(*options->controllerName_);
238 }
239 else
240 {
241 RCLCPP_WARN(getLogger(), "[WaypointsNavigator] Configuring default planners");
242 }
243
244 cl_nav2z::CpGoalCheckerSwitcher * goalCheckerSwitcher;
245 this->requiresComponent(goalCheckerSwitcher, ComponentRequirement::SOFT);
246
247 if (options && options->goalCheckerName_)
248 {
249 RCLCPP_WARN(
250 getLogger(), "[WaypointsNavigator] override goal checker: %s",
251 options->goalCheckerName_->c_str());
252
253 goalCheckerSwitcher->setGoalCheckerId(*options->goalCheckerName_);
254 }
255 else
256 {
257 RCLCPP_WARN(getLogger(), "[WaypointsNavigator] Configuring default goal checker");
258 goalCheckerSwitcher->setGoalCheckerId("goal_checker");
259 }
260
261 plannerSwitcher->commitPublish();
262
263 RCLCPP_INFO(getLogger(), "[WaypointsNavigator] Getting odom tracker");
264
266 requiresComponent(odomTracker, ComponentRequirement::SOFT);
267
268 if (odomTracker != nullptr)
269 {
270 RCLCPP_INFO(getLogger(), "[WaypointsNavigator] Storing path in odom tracker");
271
272 auto pathname = this->owner_->getStateMachine()->getCurrentState()->getName() + " - " +
273 getName() + " - " + nextName;
274 odomTracker->pushPath(pathname);
275 // Stamped pose: the bare-Pose overload would mislabel these map-frame
276 // coordinates with the odom frame (see cb_navigate_global_position.cpp)
277 odomTracker->setStartPoint(p->toPoseStampedMsg());
279 }
280
281 // Set up navigation result handling
282 if (!succeededNav2ZClientConnection_.connected())
283 {
290 }
291
292 return nav2ActionInterface_->sendGoal(goal);
293 }
294 else
295 {
296 RCLCPP_WARN(
297 getLogger(),
298 "[CpWaypointsNavigator] All waypoints were consumed. There is no more waypoints available.");
299 }
300
301 return std::nullopt;
302}
303
305{
306 // when it is the last waypoint post an finalization EOF event
307 if (currentWaypoint_ == (long)waypoints_.size() - 1)
308 {
309 RCLCPP_WARN(getLogger(), "[CpWaypointNavigator] Last waypoint reached, posting EOF event. ");
311 }
312}
313
316{
317 if (r.code == rclcpp_action::ResultCode::SUCCEEDED)
318 {
319 this->onGoalReached(r);
320 }
321 else if (r.code == rclcpp_action::ResultCode::ABORTED)
322 {
323 this->onGoalAborted(r);
324 }
325 else if (r.code == rclcpp_action::ResultCode::CANCELED)
326 {
327 this->onGoalCancelled(r);
328 }
329 else
330 {
331 this->onGoalAborted(r);
332 }
333}
334
335void CpWaypointNavigatorBase::insertWaypoint(int index, geometry_msgs::msg::Pose & newpose)
336{
337 if (index >= 0 && index <= (int)waypoints_.size())
338 {
339 waypoints_.insert(waypoints_.begin(), index, newpose);
340 }
341}
342
343void CpWaypointNavigatorBase::setWaypoints(const std::vector<geometry_msgs::msg::Pose> & waypoints)
344{
345 this->waypoints_ = waypoints;
346}
347
348void CpWaypointNavigatorBase::setWaypoints(const std::vector<Pose2D> & waypoints)
349{
350 waypoints_.clear();
351 waypointsNames_.clear();
352 int i = 0;
353 for (auto & p : waypoints)
354 {
355 geometry_msgs::msg::Pose pose;
356 pose.position.x = p.x_;
357 pose.position.y = p.y_;
358 pose.position.z = 0.0;
359 tf2::Quaternion q;
360 q.setRPY(0, 0, p.yaw_);
361 pose.orientation = tf2::toMsg(q);
362
363 waypoints_.push_back(pose);
364 waypointsNames_.push_back(std::to_string(i++));
365 }
366}
367
369{
370 if (index >= 0 && index < (int)waypoints_.size())
371 {
372 waypoints_.erase(waypoints_.begin() + index);
373 }
374}
375
376const std::vector<geometry_msgs::msg::Pose> & CpWaypointNavigatorBase::getWaypoints() const
377{
378 return waypoints_;
379}
380
381geometry_msgs::msg::Pose CpWaypointNavigatorBase::getPose(int index) const
382{
383 if (index >= 0 && index < (int)waypoints_.size())
384 {
385 return waypoints_[index];
386 }
387 else
388 {
389 throw std::out_of_range("Waypoint index out of range");
390 }
391}
392geometry_msgs::msg::Pose CpWaypointNavigatorBase::getCurrentPose() const
393{
394 if (currentWaypoint_ >= 0 && currentWaypoint_ < (int)waypoints_.size())
395 {
397 }
398 else
399 {
400 throw std::out_of_range("Waypoint index out of range");
401 }
402}
403
404std::optional<geometry_msgs::msg::Pose> CpWaypointNavigatorBase::getNamedPose(
405 std::string name) const
406{
407 if (this->waypointsNames_.size() > 0)
408 {
409 for (int i = 0; i < (int)this->waypointsNames_.size(); i++)
410 {
411 if (this->waypointsNames_[i] == name)
412 {
413 return this->waypoints_[i];
414 }
415 }
416 }
417
418 return std::nullopt;
419}
420
421const std::vector<std::string> & CpWaypointNavigatorBase::getWaypointNames() const
422{
423 return waypointsNames_;
424}
425
426std::optional<std::string> CpWaypointNavigatorBase::getCurrentWaypointName() const
427{
428 if (currentWaypoint_ >= 0 && currentWaypoint_ < (int)waypointsNames_.size())
429 {
431 }
432 return std::nullopt;
433}
434
436
437#define HAVE_NEW_YAMLCPP
439{
440 RCLCPP_INFO_STREAM(getLogger(), "[CpWaypointNavigatorBase] Loading file:" << filepath);
441 this->waypoints_.clear();
442 std::ifstream ifs(filepath.c_str(), std::ifstream::in);
443 if (ifs.good() == false)
444 {
445 throw std::string("Waypoints file not found");
446 }
447
448 try
449 {
450#ifdef HAVE_NEW_YAMLCPP
451 YAML::Node node = YAML::Load(ifs);
452#else
453 YAML::Parser parser(ifs);
454 parser.GetNextDocument(node);
455#endif
456
457#ifdef HAVE_NEW_YAMLCPP
458 const YAML::Node & wp_node_tmp = node["waypoints"];
459 const YAML::Node * wp_node = wp_node_tmp ? &wp_node_tmp : NULL;
460#else
461 const YAML::Node * wp_node = node.FindValue("waypoints");
462#endif
463
464 if (wp_node != NULL)
465 {
466 for (std::size_t i = 0; i < wp_node->size(); ++i)
467 {
468 // Parse waypoint entries on YAML
469 geometry_msgs::msg::Pose wp;
470
471 try
472 {
473 auto wpnodei = (*wp_node)[i];
474 wp.position.x = wpnodei["position"]["x"].as<double>();
475 wp.position.y = wpnodei["position"]["y"].as<double>();
476 wp.position.z = wpnodei["position"]["z"].as<double>();
477 wp.orientation.x = wpnodei["orientation"]["x"].as<double>();
478 wp.orientation.y = wpnodei["orientation"]["y"].as<double>();
479 wp.orientation.z = wpnodei["orientation"]["z"].as<double>();
480 wp.orientation.w = wpnodei["orientation"]["w"].as<double>();
481
482 if (wpnodei["name"].IsDefined())
483 {
484 this->waypointsNames_.push_back(wpnodei["name"].as<std::string>());
485 }
486
487 this->waypoints_.push_back(wp);
488 }
489 catch (...)
490 {
491 RCLCPP_ERROR(getLogger(), "parsing waypoint file, syntax error in point %ld", i);
492 }
493 }
494 RCLCPP_INFO_STREAM(getLogger(), "Parsed " << this->waypoints_.size() << " waypoints.");
495 }
496 else
497 {
498 RCLCPP_WARN_STREAM(getLogger(), "Couldn't find any waypoints in the provided yaml file.");
499 }
500 }
501 catch (const YAML::ParserException & ex)
502 {
503 RCLCPP_ERROR_STREAM(
504 getLogger(), "Error loading the Waypoints YAML file. Incorrect syntax: " << ex.what());
505 }
506}
507
509{
510 RCLCPP_INFO_STREAM(getLogger(), "[CpWaypointNavigator] Loading file:" << filepath);
511 this->waypoints_.clear();
512 std::ifstream ifs(filepath.c_str(), std::ifstream::in);
513 if (ifs.good() == false)
514 {
515 throw std::string("Waypoints file not found");
516 }
517
518 try
519 {
520#ifdef HAVE_NEW_YAMLCPP
521 YAML::Node node = YAML::Load(ifs);
522#else
523 YAML::Parser parser(ifs);
524 parser.GetNextDocument(node);
525#endif
526
527#ifdef HAVE_NEW_YAMLCPP
528 const YAML::Node & wp_node_tmp = node["waypoints"];
529 const YAML::Node * wp_node = wp_node_tmp ? &wp_node_tmp : NULL;
530#else
531 const YAML::Node * wp_node = node.FindValue("waypoints");
532#endif
533
534 if (wp_node != NULL)
535 {
536 for (std::size_t i = 0; i < wp_node->size(); ++i)
537 {
538 // Parse waypoint entries on YAML
539 geometry_msgs::msg::Pose wp;
540
541 try
542 {
543 wp.position.x = (*wp_node)[i]["x"].as<double>();
544 wp.position.y = (*wp_node)[i]["y"].as<double>();
545 auto name = (*wp_node)[i]["name"].as<std::string>();
546
547 this->waypoints_.push_back(wp);
548 this->waypointsNames_.push_back(name);
549 }
550 catch (...)
551 {
552 RCLCPP_ERROR(getLogger(), "parsing waypoint file, syntax error in point %ld", i);
553 }
554 }
555 RCLCPP_INFO_STREAM(getLogger(), "Parsed " << this->waypoints_.size() << " waypoints.");
556 }
557 else
558 {
559 RCLCPP_WARN_STREAM(getLogger(), "Couldn't find any waypoints in the provided yaml file.");
560 }
561 }
562 catch (const YAML::ParserException & ex)
563 {
564 RCLCPP_ERROR_STREAM(
565 getLogger(), "Error loading the Waypoints YAML file. Incorrect syntax: " << ex.what());
566 }
567}
568} // namespace cl_nav2z
void setGoalCheckerId(std::string goal_checker_id)
void setDefaultPlanners(bool commit=true)
geometry_msgs::msg::PoseStamped toPoseStampedMsg()
Definition cp_pose.hpp:63
const std::string & getReferenceFrame() const
Definition cp_pose.hpp:79
void setWaypoints(const std::vector< geometry_msgs::msg::Pose > &waypoints)
const std::vector< geometry_msgs::msg::Pose > & getWaypoints() const
void loadWayPointsFromFile2(std::string filepath)
std::vector< geometry_msgs::msg::Pose > waypoints_
geometry_msgs::msg::Pose getCurrentPose() const
void loadWayPointsFromFile(std::string filepath)
std::optional< std::string > getCurrentWaypointName() const
std::optional< geometry_msgs::msg::Pose > getNamedPose(std::string name) const
geometry_msgs::msg::Pose getPose(int index) const
const std::vector< std::string > & getWaypointNames() const
void loadWaypointsFromYamlParameter(std::string parameter_name, std::string yaml_file_package_name)
void insertWaypoint(int index, geometry_msgs::msg::Pose &newpose)
void onGoalReached(const components::CpNav2ActionInterface::WrappedResult &res)
smacc2::SmaccSignalConnection succeededNav2ZClientConnection_
smacc2::SmaccSignal< void()> onNavigationRequestSucceeded_
void onGoalAborted(const components::CpNav2ActionInterface::WrappedResult &)
smacc2::SmaccSignalConnection cancelledNav2ZClientConnection_
smacc2::SmaccSignalConnection abortedNav2ZClientConnection_
components::CpNav2ActionInterface * nav2ActionInterface_
smacc2::SmaccSignal< void()> onNavigationRequestCancelled_
void onNavigationResult(const components::CpNav2ActionInterface::WrappedResult &r)
void onGoalCancelled(const components::CpNav2ActionInterface::WrappedResult &)
std::optional< std::shared_future< std::shared_ptr< rclcpp_action::ClientGoalHandle< nav2_msgs::action::NavigateToPose > > > > sendNextGoal(std::optional< NavigateNextWaypointOptions > options=std::nullopt)
smacc2::SmaccSignal< void()> onNavigationRequestAborted_
smacc2::SmaccSignalConnection onNavigationSucceeded(void(T::*callback)(const WrappedResult &), T *object)
std::shared_future< typename GoalHandle::SharedPtr > sendGoal(Goal &goal)
smacc2::SmaccSignalConnection onNavigationAborted(void(T::*callback)(const WrappedResult &), T *object)
smacc2::SmaccSignalConnection onNavigationCancelled(void(T::*callback)(const WrappedResult &), T *object)
void setWorkingMode(WorkingMode workingMode)
void setStartPoint(const geometry_msgs::msg::PoseStamped &pose)
ISmaccStateMachine * getStateMachine()
ISmaccClient * owner_
Definition component.hpp:88
virtual std::string getName() const
rclcpp::Logger getLogger() const
void requiresComponent(TComponent *&requiredComponentStorage, ComponentRequirement requirementType=ComponentRequirement::SOFT)
rclcpp::Node::SharedPtr getNode()
virtual std::string getName()=0