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 auto pose = p->toPoseMsg();
223
224 // configuring goal
225 goal.pose.header.frame_id = p->getReferenceFrame();
226 goal.pose.pose = next;
227
228 cl_nav2z::CpPlannerSwitcher * plannerSwitcher;
229 this->requiresComponent(plannerSwitcher, ComponentRequirement::HARD);
230
231 plannerSwitcher->setDefaultPlanners(false);
232 if (options && options->controllerName_)
233 {
234 RCLCPP_WARN(
235 getLogger(), "[WaypointsNavigator] override controller: %s",
236 options->controllerName_->c_str());
237
238 plannerSwitcher->setDesiredController(*options->controllerName_);
239 }
240 else
241 {
242 RCLCPP_WARN(getLogger(), "[WaypointsNavigator] Configuring default planners");
243 }
244
245 cl_nav2z::CpGoalCheckerSwitcher * goalCheckerSwitcher;
246 this->requiresComponent(goalCheckerSwitcher, ComponentRequirement::SOFT);
247
248 if (options && options->goalCheckerName_)
249 {
250 RCLCPP_WARN(
251 getLogger(), "[WaypointsNavigator] override goal checker: %s",
252 options->goalCheckerName_->c_str());
253
254 goalCheckerSwitcher->setGoalCheckerId(*options->goalCheckerName_);
255 }
256 else
257 {
258 RCLCPP_WARN(getLogger(), "[WaypointsNavigator] Configuring default goal checker");
259 goalCheckerSwitcher->setGoalCheckerId("goal_checker");
260 }
261
262 plannerSwitcher->commitPublish();
263
264 RCLCPP_INFO(getLogger(), "[WaypointsNavigator] Getting odom tracker");
265
267 requiresComponent(odomTracker, ComponentRequirement::SOFT);
268
269 if (odomTracker != nullptr)
270 {
271 RCLCPP_INFO(getLogger(), "[WaypointsNavigator] Storing path in odom tracker");
272
273 auto pathname = this->owner_->getStateMachine()->getCurrentState()->getName() + " - " +
274 getName() + " - " + nextName;
275 odomTracker->pushPath(pathname);
276 odomTracker->setStartPoint(pose);
278 }
279
280 // Set up navigation result handling
281 if (!succeededNav2ZClientConnection_.connected())
282 {
289 }
290
291 return nav2ActionInterface_->sendGoal(goal);
292 }
293 else
294 {
295 RCLCPP_WARN(
296 getLogger(),
297 "[CpWaypointsNavigator] All waypoints were consumed. There is no more waypoints available.");
298 }
299
300 return std::nullopt;
301}
302
304{
305 // when it is the last waypoint post an finalization EOF event
306 if (currentWaypoint_ == (long)waypoints_.size() - 1)
307 {
308 RCLCPP_WARN(getLogger(), "[CpWaypointNavigator] Last waypoint reached, posting EOF event. ");
310 }
311}
312
315{
316 if (r.code == rclcpp_action::ResultCode::SUCCEEDED)
317 {
318 this->onGoalReached(r);
319 }
320 else if (r.code == rclcpp_action::ResultCode::ABORTED)
321 {
322 this->onGoalAborted(r);
323 }
324 else if (r.code == rclcpp_action::ResultCode::CANCELED)
325 {
326 this->onGoalCancelled(r);
327 }
328 else
329 {
330 this->onGoalAborted(r);
331 }
332}
333
334void CpWaypointNavigatorBase::insertWaypoint(int index, geometry_msgs::msg::Pose & newpose)
335{
336 if (index >= 0 && index <= (int)waypoints_.size())
337 {
338 waypoints_.insert(waypoints_.begin(), index, newpose);
339 }
340}
341
342void CpWaypointNavigatorBase::setWaypoints(const std::vector<geometry_msgs::msg::Pose> & waypoints)
343{
344 this->waypoints_ = waypoints;
345}
346
347void CpWaypointNavigatorBase::setWaypoints(const std::vector<Pose2D> & waypoints)
348{
349 waypoints_.clear();
350 waypointsNames_.clear();
351 int i = 0;
352 for (auto & p : waypoints)
353 {
354 geometry_msgs::msg::Pose pose;
355 pose.position.x = p.x_;
356 pose.position.y = p.y_;
357 pose.position.z = 0.0;
358 tf2::Quaternion q;
359 q.setRPY(0, 0, p.yaw_);
360 pose.orientation = tf2::toMsg(q);
361
362 waypoints_.push_back(pose);
363 waypointsNames_.push_back(std::to_string(i++));
364 }
365}
366
368{
369 if (index >= 0 && index < (int)waypoints_.size())
370 {
371 waypoints_.erase(waypoints_.begin() + index);
372 }
373}
374
375const std::vector<geometry_msgs::msg::Pose> & CpWaypointNavigatorBase::getWaypoints() const
376{
377 return waypoints_;
378}
379
380geometry_msgs::msg::Pose CpWaypointNavigatorBase::getPose(int index) const
381{
382 if (index >= 0 && index < (int)waypoints_.size())
383 {
384 return waypoints_[index];
385 }
386 else
387 {
388 throw std::out_of_range("Waypoint index out of range");
389 }
390}
391geometry_msgs::msg::Pose CpWaypointNavigatorBase::getCurrentPose() const
392{
393 if (currentWaypoint_ >= 0 && currentWaypoint_ < (int)waypoints_.size())
394 {
396 }
397 else
398 {
399 throw std::out_of_range("Waypoint index out of range");
400 }
401}
402
403std::optional<geometry_msgs::msg::Pose> CpWaypointNavigatorBase::getNamedPose(
404 std::string name) const
405{
406 if (this->waypointsNames_.size() > 0)
407 {
408 for (int i = 0; i < (int)this->waypointsNames_.size(); i++)
409 {
410 if (this->waypointsNames_[i] == name)
411 {
412 return this->waypoints_[i];
413 }
414 }
415 }
416
417 return std::nullopt;
418}
419
420const std::vector<std::string> & CpWaypointNavigatorBase::getWaypointNames() const
421{
422 return waypointsNames_;
423}
424
425std::optional<std::string> CpWaypointNavigatorBase::getCurrentWaypointName() const
426{
427 if (currentWaypoint_ >= 0 && currentWaypoint_ < (int)waypointsNames_.size())
428 {
430 }
431 return std::nullopt;
432}
433
435
436#define HAVE_NEW_YAMLCPP
438{
439 RCLCPP_INFO_STREAM(getLogger(), "[CpWaypointNavigatorBase] Loading file:" << filepath);
440 this->waypoints_.clear();
441 std::ifstream ifs(filepath.c_str(), std::ifstream::in);
442 if (ifs.good() == false)
443 {
444 throw std::string("Waypoints file not found");
445 }
446
447 try
448 {
449#ifdef HAVE_NEW_YAMLCPP
450 YAML::Node node = YAML::Load(ifs);
451#else
452 YAML::Parser parser(ifs);
453 parser.GetNextDocument(node);
454#endif
455
456#ifdef HAVE_NEW_YAMLCPP
457 const YAML::Node & wp_node_tmp = node["waypoints"];
458 const YAML::Node * wp_node = wp_node_tmp ? &wp_node_tmp : NULL;
459#else
460 const YAML::Node * wp_node = node.FindValue("waypoints");
461#endif
462
463 if (wp_node != NULL)
464 {
465 for (std::size_t i = 0; i < wp_node->size(); ++i)
466 {
467 // Parse waypoint entries on YAML
468 geometry_msgs::msg::Pose wp;
469
470 try
471 {
472 auto wpnodei = (*wp_node)[i];
473 wp.position.x = wpnodei["position"]["x"].as<double>();
474 wp.position.y = wpnodei["position"]["y"].as<double>();
475 wp.position.z = wpnodei["position"]["z"].as<double>();
476 wp.orientation.x = wpnodei["orientation"]["x"].as<double>();
477 wp.orientation.y = wpnodei["orientation"]["y"].as<double>();
478 wp.orientation.z = wpnodei["orientation"]["z"].as<double>();
479 wp.orientation.w = wpnodei["orientation"]["w"].as<double>();
480
481 if (wpnodei["name"].IsDefined())
482 {
483 this->waypointsNames_.push_back(wpnodei["name"].as<std::string>());
484 }
485
486 this->waypoints_.push_back(wp);
487 }
488 catch (...)
489 {
490 RCLCPP_ERROR(getLogger(), "parsing waypoint file, syntax error in point %ld", i);
491 }
492 }
493 RCLCPP_INFO_STREAM(getLogger(), "Parsed " << this->waypoints_.size() << " waypoints.");
494 }
495 else
496 {
497 RCLCPP_WARN_STREAM(getLogger(), "Couldn't find any waypoints in the provided yaml file.");
498 }
499 }
500 catch (const YAML::ParserException & ex)
501 {
502 RCLCPP_ERROR_STREAM(
503 getLogger(), "Error loading the Waypoints YAML file. Incorrect syntax: " << ex.what());
504 }
505}
506
508{
509 RCLCPP_INFO_STREAM(getLogger(), "[CpWaypointNavigator] Loading file:" << filepath);
510 this->waypoints_.clear();
511 std::ifstream ifs(filepath.c_str(), std::ifstream::in);
512 if (ifs.good() == false)
513 {
514 throw std::string("Waypoints file not found");
515 }
516
517 try
518 {
519#ifdef HAVE_NEW_YAMLCPP
520 YAML::Node node = YAML::Load(ifs);
521#else
522 YAML::Parser parser(ifs);
523 parser.GetNextDocument(node);
524#endif
525
526#ifdef HAVE_NEW_YAMLCPP
527 const YAML::Node & wp_node_tmp = node["waypoints"];
528 const YAML::Node * wp_node = wp_node_tmp ? &wp_node_tmp : NULL;
529#else
530 const YAML::Node * wp_node = node.FindValue("waypoints");
531#endif
532
533 if (wp_node != NULL)
534 {
535 for (std::size_t i = 0; i < wp_node->size(); ++i)
536 {
537 // Parse waypoint entries on YAML
538 geometry_msgs::msg::Pose wp;
539
540 try
541 {
542 wp.position.x = (*wp_node)[i]["x"].as<double>();
543 wp.position.y = (*wp_node)[i]["y"].as<double>();
544 auto name = (*wp_node)[i]["name"].as<std::string>();
545
546 this->waypoints_.push_back(wp);
547 this->waypointsNames_.push_back(name);
548 }
549 catch (...)
550 {
551 RCLCPP_ERROR(getLogger(), "parsing waypoint file, syntax error in point %ld", i);
552 }
553 }
554 RCLCPP_INFO_STREAM(getLogger(), "Parsed " << this->waypoints_.size() << " waypoints.");
555 }
556 else
557 {
558 RCLCPP_WARN_STREAM(getLogger(), "Couldn't find any waypoints in the provided yaml file.");
559 }
560 }
561 catch (const YAML::ParserException & ex)
562 {
563 RCLCPP_ERROR_STREAM(
564 getLogger(), "Error loading the Waypoints YAML file. Incorrect syntax: " << ex.what());
565 }
566}
567} // namespace cl_nav2z
void setGoalCheckerId(std::string goal_checker_id)
void setDefaultPlanners(bool commit=true)
geometry_msgs::msg::Pose toPoseMsg()
Definition cp_pose.hpp:57
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