20 from typing
import Any, Union
22 from action_msgs.msg
import GoalStatus
24 from geographic_msgs.msg
import GeoPose
26 from lifecycle_msgs.srv
import GetState
27 from nav2_msgs.action
import (AssistedTeleop, BackUp,
28 ComputeAndTrackRoute, ComputePathThroughPoses, ComputePathToPose,
29 ComputeRoute, DockRobot, DriveOnHeading, FollowGPSWaypoints,
30 FollowObject, FollowPath, FollowWaypoints, NavigateThroughPoses,
31 NavigateToPose, SmoothPath, Spin, UndockRobot)
32 from nav2_msgs.srv
import (ClearCostmapAroundPose, ClearCostmapAroundRobot,
33 ClearCostmapExceptRegion, ClearEntireCostmap, GetCostmap, LoadMap,
34 ManageLifecycleNodes, Toggle)
37 from rclpy.action
import ActionClient
38 from rclpy.client
import Client
39 from rclpy.duration
import Duration
as rclpyDuration
40 from rclpy.node
import Node
41 from rclpy.qos
import QoSDurabilityPolicy, QoSHistoryPolicy, QoSProfile, QoSReliabilityPolicy
58 NAVIGATE_THROUGH_POSES = 2
61 FOLLOW_GPS_WAYPOINTS = 5
68 COMPUTE_AND_TRACK_ROUTE = 12
74 def __init__(self, node_name: str =
'basic_navigator', namespace: str =
''):
75 super().__init__(node_name=node_name, namespace=namespace)
96 amcl_pose_qos = QoSProfile(
97 durability=QoSDurabilityPolicy.TRANSIENT_LOCAL,
98 reliability=QoSReliabilityPolicy.RELIABLE,
99 history=QoSHistoryPolicy.KEEP_LAST,
105 self, NavigateThroughPoses,
'navigate_through_poses')
106 self.
nav_to_pose_clientnav_to_pose_client = ActionClient(self, NavigateToPose,
'navigate_to_pose')
108 self, FollowWaypoints,
'follow_waypoints'
111 self, FollowGPSWaypoints,
'follow_gps_waypoints'
113 self.
follow_path_clientfollow_path_client = ActionClient(self, FollowPath,
'follow_path')
115 self, ComputePathToPose,
'compute_path_to_pose'
118 self, ComputePathThroughPoses,
'compute_path_through_poses'
120 self.
smoother_clientsmoother_client = ActionClient(self, SmoothPath,
'smooth_path')
121 self.
compute_route_clientcompute_route_client = ActionClient(self, ComputeRoute,
'compute_route')
124 ComputeAndTrackRoute,
125 'compute_and_track_route',
127 self.
spin_clientspin_client = ActionClient(self, Spin,
'spin')
129 self.
backup_clientbackup_client = ActionClient(self, BackUp,
'backup')
131 self, DriveOnHeading,
'drive_on_heading'
134 self, AssistedTeleop,
'assisted_teleop'
136 self.
docking_clientdocking_client = ActionClient(self, DockRobot,
'dock_robot')
137 self.
undocking_clientundocking_client = ActionClient(self, UndockRobot,
'undock_robot')
138 self.
following_clientfollowing_client = ActionClient(self, FollowObject,
'follow_object')
141 PoseWithCovarianceStamped,
147 PoseWithCovarianceStamped,
'initialpose', 10
150 self.create_client(LoadMap,
'map_server/load_map')
153 'global_costmap/clear_entirely_global_costmap',
157 'local_costmap/clear_entirely_local_costmap',
160 ClearCostmapExceptRegion,
161 'local_costmap/clear_costmap_except_region',
164 ClearCostmapAroundRobot,
165 'local_costmap/clear_costmap_around_robot',
168 ClearCostmapAroundPose,
169 'local_costmap/clear_costmap_around_pose',
172 ClearCostmapAroundPose,
173 'global_costmap/clear_costmap_around_pose',
177 'global_costmap/get_costmap',
181 'local_costmap/get_costmap',
185 'collision_monitor/toggle',
188 def destroyNode(self):
191 def destroy_node(self):
208 super().destroy_node()
211 """Set the initial pose to the localization system."""
217 """Send a `NavThroughPoses` action request."""
219 self.
debugdebug(
"Waiting for 'NavigateThroughPoses' action server")
221 self.
infoinfo(
"'NavigateThroughPoses' action server not available, waiting...")
223 goal_msg = NavigateThroughPoses.Goal()
224 goal_msg.poses = poses
225 goal_msg.behavior_tree = behavior_tree
227 self.
infoinfo(f
'Navigating with {len(poses.goals)} goals....')
231 rclpy.spin_until_future_complete(self, send_goal_future)
232 self.
goal_handlegoal_handle = send_goal_future.result()
235 msg = f
'NavigateThroughPoses request with {len(poses.goals)} was rejected!'
236 self.
setTaskErrorsetTaskError(NavigateThroughPoses.Result.UNKNOWN, msg)
241 return RunningTask.NAVIGATE_THROUGH_POSES
243 def goToPose(self, pose: PoseStamped, behavior_tree: str =
''):
244 """Send a `NavToPose` action request."""
246 self.
debugdebug(
"Waiting for 'NavigateToPose' action server")
248 self.
infoinfo(
"'NavigateToPose' action server not available, waiting...")
250 goal_msg = NavigateToPose.Goal()
252 goal_msg.behavior_tree = behavior_tree
255 'Navigating to goal: '
256 + str(pose.pose.position.x)
258 + str(pose.pose.position.y)
264 rclpy.spin_until_future_complete(self, send_goal_future)
265 self.
goal_handlegoal_handle = send_goal_future.result()
269 'NavigateToPose goal to '
270 + str(pose.pose.position.x)
272 + str(pose.pose.position.y)
275 self.
setTaskErrorsetTaskError(NavigateToPose.Result.UNKNOWN, msg)
280 return RunningTask.NAVIGATE_TO_POSE
283 """Send a `FollowWaypoints` action request."""
285 self.
debugdebug(
"Waiting for 'FollowWaypoints' action server")
287 self.
infoinfo(
"'FollowWaypoints' action server not available, waiting...")
289 goal_msg = FollowWaypoints.Goal()
290 goal_msg.poses = poses
292 self.
infoinfo(f
'Following {len(goal_msg.poses)} goals....')
296 rclpy.spin_until_future_complete(self, send_goal_future)
297 self.
goal_handlegoal_handle = send_goal_future.result()
300 msg = f
'Following {len(poses)} waypoints request was rejected!'
301 self.
setTaskErrorsetTaskError(FollowWaypoints.Result.UNKNOWN, msg)
306 return RunningTask.FOLLOW_WAYPOINTS
309 """Send a `FollowGPSWaypoints` action request."""
311 self.
debugdebug(
"Waiting for 'FollowWaypoints' action server")
313 self.
infoinfo(
"'FollowWaypoints' action server not available, waiting...")
315 goal_msg = FollowGPSWaypoints.Goal()
316 goal_msg.gps_poses = gps_poses
318 self.
infoinfo(f
'Following {len(goal_msg.gps_poses)} gps goals....')
322 rclpy.spin_until_future_complete(self, send_goal_future)
323 self.
goal_handlegoal_handle = send_goal_future.result()
326 msg = f
'Following {len(gps_poses)} gps waypoints request was rejected!'
327 self.
setTaskErrorsetTaskError(FollowGPSWaypoints.Result.UNKNOWN, msg)
332 return RunningTask.FOLLOW_GPS_WAYPOINTS
335 self, spin_dist: float = 1.57, time_allowance: int = 10,
336 disable_collision_checks: bool =
False):
338 self.
debugdebug(
"Waiting for 'Spin' action server")
339 while not self.
spin_clientspin_client.wait_for_server(timeout_sec=1.0):
340 self.
infoinfo(
"'Spin' action server not available, waiting...")
341 goal_msg = Spin.Goal()
342 goal_msg.target_yaw = spin_dist
343 goal_msg.time_allowance = Duration(sec=time_allowance)
344 goal_msg.disable_collision_checks = disable_collision_checks
346 self.
infoinfo(f
'Spinning to angle {goal_msg.target_yaw}....')
347 send_goal_future = self.
spin_clientspin_client.send_goal_async(
350 rclpy.spin_until_future_complete(self, send_goal_future)
351 self.
goal_handlegoal_handle = send_goal_future.result()
354 msg =
'Spin request was rejected!'
360 return RunningTask.SPIN
363 self, backup_dist: float = 0.15, backup_speed: float = 0.025,
364 time_allowance: int = 10,
365 disable_collision_checks: bool =
False):
367 self.
debugdebug(
"Waiting for 'Backup' action server")
368 while not self.
backup_clientbackup_client.wait_for_server(timeout_sec=1.0):
369 self.
infoinfo(
"'Backup' action server not available, waiting...")
370 goal_msg = BackUp.Goal()
371 goal_msg.target = Point(x=float(backup_dist))
372 goal_msg.speed = backup_speed
373 goal_msg.time_allowance = Duration(sec=time_allowance)
374 goal_msg.disable_collision_checks = disable_collision_checks
376 self.
infoinfo(f
'Backing up {goal_msg.target.x} m at {goal_msg.speed} m/s....')
377 send_goal_future = self.
backup_clientbackup_client.send_goal_async(
380 rclpy.spin_until_future_complete(self, send_goal_future)
381 self.
goal_handlegoal_handle = send_goal_future.result()
384 msg =
'Backup request was rejected!'
385 self.
setTaskErrorsetTaskError(BackUp.Result.UNKNOWN, msg)
390 return RunningTask.BACKUP
393 self, dist: float = 0.15, speed: float = 0.025,
394 time_allowance: int = 10,
395 disable_collision_checks: bool =
False):
397 self.
debugdebug(
"Waiting for 'DriveOnHeading' action server")
399 self.
infoinfo(
"'DriveOnHeading' action server not available, waiting...")
400 goal_msg = DriveOnHeading.Goal()
401 goal_msg.target = Point(x=float(dist))
402 goal_msg.speed = speed
403 goal_msg.time_allowance = Duration(sec=time_allowance)
404 goal_msg.disable_collision_checks = disable_collision_checks
406 self.
infoinfo(f
'Drive {goal_msg.target.x} m on heading at {goal_msg.speed} m/s....')
410 rclpy.spin_until_future_complete(self, send_goal_future)
411 self.
goal_handlegoal_handle = send_goal_future.result()
414 msg =
'Drive On Heading request was rejected!'
415 self.
setTaskErrorsetTaskError(DriveOnHeading.Result.UNKNOWN, msg)
420 return RunningTask.DRIVE_ON_HEADING
422 def assistedTeleop(self, time_allowance: int = 30):
425 self.
debugdebug(
"Wanting for 'assisted_teleop' action server")
428 self.
infoinfo(
"'assisted_teleop' action server not available, waiting...")
429 goal_msg = AssistedTeleop.Goal()
430 goal_msg.time_allowance = Duration(sec=time_allowance)
432 self.
infoinfo(
"Running 'assisted_teleop'....")
436 rclpy.spin_until_future_complete(self, send_goal_future)
437 self.
goal_handlegoal_handle = send_goal_future.result()
440 msg =
'Assisted Teleop request was rejected!'
441 self.
setTaskErrorsetTaskError(AssistedTeleop.Result.UNKNOWN, msg)
446 return RunningTask.ASSISTED_TELEOP
448 def followPath(self, path: Path, controller_id: str =
'',
449 goal_checker_id: str =
'', progress_checker_id: str =
'',
450 path_handler_id: str =
''):
452 """Send a `FollowPath` action request."""
453 self.
debugdebug(
"Waiting for 'FollowPath' action server")
455 self.
infoinfo(
"'FollowPath' action server not available, waiting...")
457 goal_msg = FollowPath.Goal()
459 goal_msg.controller_id = controller_id
460 goal_msg.goal_checker_id = goal_checker_id
461 goal_msg.progress_checker_id = progress_checker_id
462 goal_msg.path_handler_id = path_handler_id
464 self.
infoinfo(
'Executing path...')
468 rclpy.spin_until_future_complete(self, send_goal_future)
469 self.
goal_handlegoal_handle = send_goal_future.result()
472 msg =
'FollowPath goal was rejected!'
473 self.
setTaskErrorsetTaskError(FollowPath.Result.UNKNOWN, msg)
478 return RunningTask.FOLLOW_PATH
480 def dockRobotByPose(self, dock_pose: PoseStamped,
481 dock_type: str =
'', nav_to_dock: bool =
True):
483 """Send a `DockRobot` action request."""
484 self.
infoinfo(
"Waiting for 'DockRobot' action server")
485 while not self.
docking_clientdocking_client.wait_for_server(timeout_sec=1.0):
486 self.
infoinfo(
'"DockRobot" action server not available, waiting...')
488 goal_msg = DockRobot.Goal()
489 goal_msg.use_dock_id =
False
490 goal_msg.dock_pose = dock_pose
491 goal_msg.dock_type = dock_type
492 goal_msg.navigate_to_staging_pose = nav_to_dock
494 self.
infoinfo(
'Docking at pose: ' + str(dock_pose) +
'...')
495 send_goal_future = self.
docking_clientdocking_client.send_goal_async(
497 rclpy.spin_until_future_complete(self, send_goal_future)
498 self.
goal_handlegoal_handle = send_goal_future.result()
501 msg =
'DockRobot request was rejected!'
502 self.
setTaskErrorsetTaskError(DockRobot.Result.UNKNOWN, msg)
507 return RunningTask.DOCK_ROBOT
510 """Send a `DockRobot` action request."""
512 self.
infoinfo(
"Waiting for 'DockRobot' action server")
513 while not self.
docking_clientdocking_client.wait_for_server(timeout_sec=1.0):
514 self.
infoinfo(
'"DockRobot" action server not available, waiting...')
516 goal_msg = DockRobot.Goal()
517 goal_msg.use_dock_id =
True
518 goal_msg.dock_id = dock_id
519 goal_msg.navigate_to_staging_pose = nav_to_dock
521 self.
infoinfo(
'Docking at dock ID: ' + str(dock_id) +
'...')
522 send_goal_future = self.
docking_clientdocking_client.send_goal_async(
524 rclpy.spin_until_future_complete(self, send_goal_future)
525 self.
goal_handlegoal_handle = send_goal_future.result()
528 msg =
'DockRobot request was rejected!'
529 self.
setTaskErrorsetTaskError(DockRobot.Result.UNKNOWN, msg)
534 return RunningTask.DOCK_ROBOT
537 """Send a `UndockRobot` action request."""
539 self.
infoinfo(
"Waiting for 'UndockRobot' action server")
540 while not self.
undocking_clientundocking_client.wait_for_server(timeout_sec=1.0):
541 self.
infoinfo(
'"UndockRobot" action server not available, waiting...')
543 goal_msg = UndockRobot.Goal()
544 goal_msg.dock_type = dock_type
546 self.
infoinfo(
'Undocking from dock of type: ' + str(dock_type) +
'...')
549 rclpy.spin_until_future_complete(self, send_goal_future)
550 self.
goal_handlegoal_handle = send_goal_future.result()
553 msg =
'UndockRobot request was rejected!'
554 self.
setTaskErrorsetTaskError(UndockRobot.Result.UNKNOWN, msg)
559 return RunningTask.UNDOCK_ROBOT
562 """Send a `FollowObject` action request."""
564 self.
infoinfo(
"Waiting for 'FollowObject' action server")
565 while not self.
following_clientfollowing_client.wait_for_server(timeout_sec=1.0):
566 self.
infoinfo(
'"FollowObject" action server not available, waiting...')
568 goal_msg = FollowObject.Goal()
569 goal_msg.pose_topic = topic
570 goal_msg.max_duration = Duration(sec=max_duration)
572 self.
infoinfo(
'Following object on topic: ' + str(topic) +
'...')
575 rclpy.spin_until_future_complete(self, send_goal_future)
576 self.
goal_handlegoal_handle = send_goal_future.result()
579 msg =
'FollowObject request was rejected!'
580 self.
setTaskErrorsetTaskError(FollowObject.Result.UNKNOWN, msg)
585 return RunningTask.FOLLOW_OBJECT
588 """Send a `FollowObject` action request."""
590 self.
infoinfo(
"Waiting for 'FollowObject' action server")
591 while not self.
following_clientfollowing_client.wait_for_server(timeout_sec=1.0):
592 self.
infoinfo(
'"FollowObject" action server not available, waiting...')
594 goal_msg = FollowObject.Goal()
595 goal_msg.tracked_frame = frame
596 goal_msg.max_duration = Duration(sec=max_duration)
598 self.
infoinfo(
'Following object in frame: ' + str(frame) +
'...')
601 rclpy.spin_until_future_complete(self, send_goal_future)
602 self.
goal_handlegoal_handle = send_goal_future.result()
605 msg =
'FollowObject request was rejected!'
606 self.
setTaskErrorsetTaskError(FollowObject.Result.UNKNOWN, msg)
611 return RunningTask.FOLLOW_OBJECT
614 """Cancel pending task request of any type."""
615 self.
infoinfo(
'Canceling current task.')
618 future = self.
goal_handlegoal_handle.cancel_goal_async()
619 rclpy.spin_until_future_complete(self, future)
621 self.
errorerror(
'Cancel task failed, goal handle is None')
622 self.
setTaskErrorsetTaskError(0,
'Cancel task failed, goal handle is None')
627 rclpy.spin_until_future_complete(self, future)
629 self.
errorerror(
'Cancel route task failed, goal handle is None')
630 self.
setTaskErrorsetTaskError(0,
'Cancel route task failed, goal handle is None')
636 """Check if the task request of any type is complete yet."""
639 self.
errorerror(
'Task is None, cannot check for completion')
643 if task != RunningTask.COMPUTE_AND_TRACK_ROUTE:
647 if not result_future:
652 rclpy.spin_until_future_complete(self, result_future, timeout_sec=0.10)
653 result_response = result_future.result()
656 self.
statusstatus = result_response.status
657 if self.
statusstatus != GoalStatus.STATUS_SUCCEEDED:
658 result = result_response.result
659 if result
is not None:
660 self.
setTaskErrorsetTaskError(result.error_code, result.error_msg)
662 'Task with failed with'
663 f
' status code:{self.status}'
664 f
' error code:{result.error_code}'
665 f
' error msg:{result.error_msg}')
669 self.
debugdebug(
'Task failed with no result received')
675 self.
debugdebug(
'Task succeeded!')
679 """Get the pending action feedback message."""
680 if task != RunningTask.COMPUTE_AND_TRACK_ROUTE:
687 """Get the pending action result message."""
688 if self.
statusstatus == GoalStatus.STATUS_SUCCEEDED:
689 return TaskResult.SUCCEEDED
690 elif self.
statusstatus == GoalStatus.STATUS_ABORTED:
691 return TaskResult.FAILED
692 elif self.
statusstatus == GoalStatus.STATUS_CANCELED:
693 return TaskResult.CANCELED
695 return TaskResult.UNKNOWN
697 def clearPreviousState(self):
702 def setTaskError(self, error_code: int, error_msg: str):
706 def getTaskError(self):
710 localizer: str =
'amcl'):
711 """Block until the full navigation system is up and running."""
712 if localizer !=
'robot_localization':
714 if localizer ==
'amcl':
717 self.
infoinfo(
'Nav2 is ready for use!')
721 self, start: PoseStamped, goal: PoseStamped,
722 planner_id: str =
'', use_start: bool =
False
725 Send a `ComputePathToPose` action request.
727 Internal implementation to get the full result, not just the path.
729 self.
debugdebug(
"Waiting for 'ComputePathToPose' action server")
731 self.
infoinfo(
"'ComputePathToPose' action server not available, waiting...")
733 goal_msg = ComputePathToPose.Goal()
734 goal_msg.start = start
736 goal_msg.planner_id = planner_id
737 goal_msg.use_start = use_start
739 self.
infoinfo(
'Getting path...')
741 rclpy.spin_until_future_complete(self, send_goal_future)
742 self.
goal_handlegoal_handle = send_goal_future.result()
745 self.
errorerror(
'Get path was rejected!')
746 self.
statusstatus = GoalStatus.STATUS_UNKNOWN
747 result = ComputePathToPose.Result()
748 result.error_code = ComputePathToPose.Result.UNKNOWN
749 result.error_msg =
'Get path was rejected'
753 rclpy.spin_until_future_complete(self, self.
result_futureresult_future)
759 self, start: PoseStamped, goal: PoseStamped,
760 planner_id: str =
'', use_start: bool =
False):
761 """Send a `ComputePathToPose` action request."""
763 rtn = self.
_getPathImpl_getPathImpl(start, goal, planner_id, use_start)
765 if self.
statusstatus == GoalStatus.STATUS_SUCCEEDED:
768 self.
setTaskErrorsetTaskError(rtn.error_code, rtn.error_msg)
769 self.
warnwarn(
'Getting path failed with'
770 f
' status code:{self.status}'
771 f
' error code:{rtn.error_code}'
772 f
' error msg:{rtn.error_msg}')
775 def _getPathThroughPosesImpl(
776 self, start: PoseStamped, goals: list[PoseStamped],
777 planner_id: str =
'', use_start: bool =
False
780 Send a `ComputePathThroughPoses` action request.
782 Internal implementation to get the full result, not just the path.
784 self.
debugdebug(
"Waiting for 'ComputePathThroughPoses' action server")
789 "'ComputePathThroughPoses' action server not available, waiting..."
792 goal_msg = ComputePathThroughPoses.Goal()
793 goal_msg.start = start
794 goal_msg.goals.header.frame_id =
'map'
795 goal_msg.goals.header.stamp = self.get_clock().now().to_msg()
796 goal_msg.goals.goals = goals
797 goal_msg.planner_id = planner_id
798 goal_msg.use_start = use_start
800 self.
infoinfo(
'Getting path...')
804 rclpy.spin_until_future_complete(self, send_goal_future)
805 self.
goal_handlegoal_handle = send_goal_future.result()
808 self.
errorerror(
'Get path was rejected!')
809 result = ComputePathThroughPoses.Result()
810 result.error_code = ComputePathThroughPoses.Result.UNKNOWN
811 result.error_msg =
'Get path was rejected!'
815 rclpy.spin_until_future_complete(self, self.
result_futureresult_future)
821 self, start: PoseStamped, goals: list[PoseStamped],
822 planner_id: str =
'', use_start: bool =
False):
823 """Send a `ComputePathThroughPoses` action request."""
827 if self.
statusstatus == GoalStatus.STATUS_SUCCEEDED:
830 self.
setTaskErrorsetTaskError(rtn.error_code, rtn.error_msg)
831 self.
warnwarn(
'Getting path failed with'
832 f
' status code:{self.status}'
833 f
' error code:{rtn.error_code}'
834 f
' error msg:{rtn.error_msg}')
838 self, start: Union[int, PoseStamped],
839 goal: Union[int, PoseStamped], use_start: bool =
False
842 Send a `ComputeRoute` action request.
844 Internal implementation to get the full result, not just the sparse route and dense path.
846 self.
debugdebug(
"Waiting for 'ComputeRoute' action server")
848 self.
infoinfo(
"'ComputeRoute' action server not available, waiting...")
850 goal_msg = ComputeRoute.Goal()
851 goal_msg.use_start = use_start
854 if isinstance(start, int)
and isinstance(goal, int):
855 goal_msg.start_id = start
856 goal_msg.goal_id = goal
857 goal_msg.use_poses =
False
858 elif isinstance(start, PoseStamped)
and isinstance(goal, PoseStamped):
859 goal_msg.start = start
861 goal_msg.use_poses =
True
863 self.
errorerror(
'Invalid start and goal types. Must be PoseStamped for pose or int for ID')
864 result = ComputeRoute.Result()
865 result.error_code = ComputeRoute.Result.UNKNOWN
866 result.error_msg =
'Request type fields were invalid!'
869 self.
infoinfo(
'Getting route...')
871 rclpy.spin_until_future_complete(self, send_goal_future)
872 self.
goal_handlegoal_handle = send_goal_future.result()
875 self.
errorerror(
'Get route was rejected!')
876 result = ComputeRoute.Result()
877 result.error_code = ComputeRoute.Result.UNKNOWN
878 result.error_msg =
'Get route was rejected!'
882 rclpy.spin_until_future_complete(self, self.
result_futureresult_future)
888 self, start: Union[int, PoseStamped],
889 goal: Union[int, PoseStamped],
890 use_start: bool =
False):
891 """Send a `ComputeRoute` action request."""
893 rtn = self.
_getRouteImpl_getRouteImpl(start, goal, use_start=
False)
895 if self.
statusstatus != GoalStatus.STATUS_SUCCEEDED:
896 self.
setTaskErrorsetTaskError(rtn.error_code, rtn.error_msg)
898 'Getting route failed with'
899 f
' status code:{self.status}'
900 f
' error code:{rtn.error_code}'
901 f
' error msg:{rtn.error_msg}')
904 return [rtn.path, rtn.route]
907 self, start: Union[int, PoseStamped],
908 goal: Union[int, PoseStamped], use_start: bool =
False
910 """Send a `ComputeAndTrackRoute` action request."""
912 self.
debugdebug(
"Waiting for 'ComputeAndTrackRoute' action server")
914 self.
infoinfo(
"'ComputeAndTrackRoute' action server not available, waiting...")
916 goal_msg = ComputeAndTrackRoute.Goal()
917 goal_msg.use_start = use_start
920 if isinstance(start, int)
and isinstance(goal, int):
921 goal_msg.start_id = start
922 goal_msg.goal_id = goal
923 goal_msg.use_poses =
False
924 elif isinstance(start, PoseStamped)
and isinstance(goal, PoseStamped):
925 goal_msg.start = start
927 goal_msg.use_poses =
True
929 self.
setTaskErrorsetTaskError(ComputeAndTrackRoute.Result.UNKNOWN,
930 'Request type fields were invalid!')
931 self.
errorerror(
'Invalid start and goal types. Must be PoseStamped for pose or int for ID')
934 self.
infoinfo(
'Computing and tracking route...')
937 rclpy.spin_until_future_complete(self, send_goal_future)
941 msg =
'Compute and track route was rejected!'
942 self.
setTaskErrorsetTaskError(ComputeAndTrackRoute.Result.UNKNOWN, msg)
947 return RunningTask.COMPUTE_AND_TRACK_ROUTE
950 self, path: Path, smoother_id: str =
'',
951 max_duration: float = 2.0, check_for_collision: bool =
False
954 Send a `SmoothPath` action request.
956 Internal implementation to get the full result, not just the path.
958 self.
debugdebug(
"Waiting for 'SmoothPath' action server")
959 while not self.
smoother_clientsmoother_client.wait_for_server(timeout_sec=1.0):
960 self.
infoinfo(
"'SmoothPath' action server not available, waiting...")
962 goal_msg = SmoothPath.Goal()
964 goal_msg.max_smoothing_duration = rclpyDuration(seconds=max_duration).to_msg()
965 goal_msg.smoother_id = smoother_id
966 goal_msg.check_for_collisions = check_for_collision
968 self.
infoinfo(
'Smoothing path...')
969 send_goal_future = self.
smoother_clientsmoother_client.send_goal_async(goal_msg)
970 rclpy.spin_until_future_complete(self, send_goal_future)
971 self.
goal_handlegoal_handle = send_goal_future.result()
974 self.
errorerror(
'Smooth path was rejected!')
975 result = SmoothPath.Result()
976 result.error_code = SmoothPath.Result.UNKNOWN
977 result.error_msg =
'Smooth path was rejected'
981 rclpy.spin_until_future_complete(self, self.
result_futureresult_future)
987 self, path: Path, smoother_id: str =
'',
988 max_duration: float = 2.0, check_for_collision: bool =
False):
989 """Send a `SmoothPath` action request."""
991 rtn = self.
_smoothPathImpl_smoothPathImpl(path, smoother_id, max_duration, check_for_collision)
993 if self.
statusstatus == GoalStatus.STATUS_SUCCEEDED:
996 self.
setTaskErrorsetTaskError(rtn.error_code, rtn.error_msg)
997 self.
warnwarn(
'Getting path failed with'
998 f
' status code:{self.status}'
999 f
' error code:{rtn.error_code}'
1000 f
' error msg:{rtn.error_msg}')
1004 """Change the current static map in the map server."""
1005 while not self.
change_maps_srvchange_maps_srv.wait_for_service(timeout_sec=1.0):
1006 self.
infoinfo(
'change map service not available, waiting...')
1007 req = LoadMap.Request()
1008 req.map_url = map_filepath
1010 rclpy.spin_until_future_complete(self, future)
1012 future_result = future.result()
1013 if future_result
is None:
1014 self.
errorerror(
'Change map request failed!')
1017 result = future_result.result
1018 if result != LoadMap.Response.RESULT_SUCCESS:
1019 if result == LoadMap.Response.RESULT_MAP_DOES_NOT_EXIST:
1020 reason =
'Map does not exist'
1021 elif result == LoadMap.Response.RESULT_INVALID_MAP_DATA:
1022 reason =
'Invalid map data'
1023 elif result == LoadMap.Response.RESULT_INVALID_MAP_METADATA:
1024 reason =
'Invalid map metadata'
1025 elif result == LoadMap.Response.RESULT_UNDEFINED_FAILURE:
1026 reason =
'Undefined failure'
1030 self.
errorerror(f
'Change map request failed:{reason}!')
1033 self.
infoinfo(
'Change map request was successful!')
1037 """Clear all costmaps."""
1043 """Clear local costmap."""
1045 self.
infoinfo(
'Clear local costmaps service not available, waiting...')
1046 req = ClearEntireCostmap.Request()
1048 rclpy.spin_until_future_complete(self, future)
1050 result = future.result()
1052 self.
errorerror(
'Clear local costmap request failed!')
1057 """Clear global costmap."""
1059 self.
infoinfo(
'Clear global costmaps service not available, waiting...')
1060 req = ClearEntireCostmap.Request()
1062 rclpy.spin_until_future_complete(self, future)
1064 result = future.result()
1066 self.
errorerror(
'Clear global costmap request failed!')
1071 """Clear the costmap except for a specified region."""
1073 self.
infoinfo(
'ClearCostmapExceptRegion service not available, waiting...')
1074 req = ClearCostmapExceptRegion.Request()
1075 req.reset_distance = reset_distance
1077 rclpy.spin_until_future_complete(self, future)
1079 result = future.result()
1081 self.
errorerror(
'Clear costmap except region request failed!')
1086 """Clear the costmap around the robot."""
1088 self.
infoinfo(
'ClearCostmapAroundRobot service not available, waiting...')
1089 req = ClearCostmapAroundRobot.Request()
1090 req.reset_distance = reset_distance
1092 rclpy.spin_until_future_complete(self, future)
1094 result = future.result()
1096 self.
errorerror(
'Clear costmap around robot request failed!')
1101 """Clear the costmap around a given pose."""
1103 self.
infoinfo(
'ClearLocalCostmapAroundPose service not available, waiting...')
1104 req = ClearCostmapAroundPose.Request()
1106 req.reset_distance = reset_distance
1108 rclpy.spin_until_future_complete(self, future)
1110 result = future.result()
1112 self.
errorerror(
'Clear local costmap around pose request failed!')
1117 """Clear the global costmap around a given pose."""
1119 self.
infoinfo(
'ClearGlobalCostmapAroundPose service not available, waiting...')
1120 req = ClearCostmapAroundPose.Request()
1122 req.reset_distance = reset_distance
1124 rclpy.spin_until_future_complete(self, future)
1126 result = future.result()
1128 self.
errorerror(
'Clear global costmap around pose request failed!')
1133 """Get the global costmap."""
1135 self.
infoinfo(
'Get global costmaps service not available, waiting...')
1136 req = GetCostmap.Request()
1138 rclpy.spin_until_future_complete(self, future)
1140 result = future.result()
1142 self.
errorerror(
'Get global costmap request failed!')
1148 """Get the local costmap."""
1150 self.
infoinfo(
'Get local costmaps service not available, waiting...')
1151 req = GetCostmap.Request()
1153 rclpy.spin_until_future_complete(self, future)
1155 result = future.result()
1158 self.
errorerror(
'Get local costmap request failed!')
1164 """Toggle the collision monitor."""
1166 self.
infoinfo(
'Toggle collision monitor service not available, waiting...')
1167 req = Toggle.Request()
1171 rclpy.spin_until_future_complete(self, future)
1172 result = future.result()
1174 self.
errorerror(
'Toggle collision monitor request failed!')
1179 """Startup nav2 lifecycle system."""
1180 self.
infoinfo(
'Starting up lifecycle nodes based on lifecycle_manager.')
1181 for srv_name, srv_type
in self.get_service_names_and_types():
1182 if srv_type[0] ==
'nav2_msgs/srv/ManageLifecycleNodes':
1183 self.
infoinfo(f
'Starting up {srv_name}')
1184 mgr_client: Client[ManageLifecycleNodes.Request, ManageLifecycleNodes.Response] = \
1185 self.create_client(ManageLifecycleNodes, srv_name)
1186 while not mgr_client.wait_for_service(timeout_sec=1.0):
1187 self.
infoinfo(f
'{srv_name} service not available, waiting...')
1188 req = ManageLifecycleNodes.Request()
1189 req.command = ManageLifecycleNodes.Request.STARTUP
1190 future = mgr_client.call_async(req)
1195 rclpy.spin_until_future_complete(self, future, timeout_sec=0.10)
1200 self.
infoinfo(
'Nav2 is ready for use!')
1204 """Shutdown nav2 lifecycle system."""
1205 self.
infoinfo(
'Shutting down lifecycle nodes based on lifecycle_manager.')
1206 for srv_name, srv_type
in self.get_service_names_and_types():
1207 if srv_type[0] ==
'nav2_msgs/srv/ManageLifecycleNodes':
1208 self.
infoinfo(f
'Shutting down {srv_name}')
1209 mgr_client: Client[ManageLifecycleNodes.Request, ManageLifecycleNodes.Response] = \
1210 self.create_client(ManageLifecycleNodes, srv_name)
1211 while not mgr_client.wait_for_service(timeout_sec=1.0):
1212 self.
infoinfo(f
'{srv_name} service not available, waiting...')
1213 req = ManageLifecycleNodes.Request()
1214 req.command = ManageLifecycleNodes.Request.SHUTDOWN
1215 future = mgr_client.call_async(req)
1216 rclpy.spin_until_future_complete(self, future)
1220 def _waitForNodeToActivate(self, node_name: str):
1222 self.
debugdebug(f
'Waiting for {node_name} to become active..')
1223 node_service = f
'{node_name}/get_state'
1224 state_client: Client[GetState.Request, GetState.Response] = \
1225 self.create_client(GetState, node_service)
1226 while not state_client.wait_for_service(timeout_sec=1.0):
1227 self.
infoinfo(f
'{node_service} service not available, waiting...')
1229 req = GetState.Request()
1231 while state !=
'active':
1232 self.
debugdebug(f
'Getting {node_name} state...')
1233 future = state_client.call_async(req)
1234 rclpy.spin_until_future_complete(self, future)
1236 result = future.result()
1237 if result
is not None:
1238 state = result.current_state.label
1239 self.
debugdebug(f
'Result of get_state: {state}')
1243 def _waitForInitialPose(self):
1245 self.
infoinfo(
'Setting initial pose')
1247 self.
infoinfo(
'Waiting for amcl_pose to be received')
1248 rclpy.spin_once(self, timeout_sec=1.0)
1251 def _amclPoseCallback(self, msg: PoseWithCovarianceStamped):
1252 self.
debugdebug(
'Received amcl pose')
1256 def _feedbackCallback(self, msg: Any):
1257 self.
debugdebug(
'Received action feedback message')
1258 self.
feedbackfeedback = msg.feedback
1261 def _routeFeedbackCallback(
1262 self, msg: ComputeAndTrackRoute.Impl.FeedbackMessage):
1263 self.
debugdebug(
'Received route action feedback message')
1267 def _setInitialPose(self):
1268 msg = PoseWithCovarianceStamped()
1270 msg.header.frame_id = self.
initial_poseinitial_pose.header.frame_id
1271 msg.header.stamp = self.
initial_poseinitial_pose.header.stamp
1272 self.
infoinfo(
'Publishing Initial Pose')
1276 def info(self, msg: str):
1277 self.get_logger().info(msg)
1280 def warn(self, msg: str):
1281 self.get_logger().warning(msg)
1284 def error(self, msg: str):
1285 self.get_logger().error(msg)
1288 def debug(self, msg: str):
1289 self.get_logger().debug(msg)
def clearAllCostmaps(self)
def _getPathThroughPosesImpl(self, PoseStamped start, list[PoseStamped] goals, str planner_id='', bool use_start=False)
def goThroughPoses(self, Goals poses, str behavior_tree='')
def clearLocalCostmapAroundPose(self, PoseStamped pose, float reset_distance)
compute_path_to_pose_client
def getRoute(self, Union[int, PoseStamped] start, Union[int, PoseStamped] goal, bool use_start=False)
def _waitForNodeToActivate(self, str node_name)
def getPath(self, PoseStamped start, PoseStamped goal, str planner_id='', bool use_start=False)
def waitUntilNav2Active(self, str navigator='bt_navigator', str localizer='amcl')
def isTaskComplete(self, RunningTask task=RunningTask.NONE)
def _getPathImpl(self, PoseStamped start, PoseStamped goal, str planner_id='', bool use_start=False)
def _feedbackCallback(self, Any msg)
def followWaypoints(self, list[PoseStamped] poses)
def followObjectByFrame(self, str frame, int max_duration=0)
toggle_collision_monitor_srv
def dockRobotByID(self, str dock_id, bool nav_to_dock=True)
def toggleCollisionMonitor(self, bool enable)
def clearCostmapAroundRobot(self, float reset_distance)
def clearPreviousState(self)
def getLocalCostmap(self)
def setInitialPose(self, PoseStamped initial_pose)
def clearGlobalCostmapAroundPose(self, PoseStamped pose, float reset_distance)
def getPathThroughPoses(self, PoseStamped start, list[PoseStamped] goals, str planner_id='', bool use_start=False)
def _routeFeedbackCallback(self, ComputeAndTrackRoute.Impl.FeedbackMessage msg)
clear_costmap_except_region_srv
def lifecycleStartup(self)
follow_gps_waypoints_client
clear_costmap_around_robot_srv
def changeMap(self, str map_filepath)
def _waitForInitialPose(self)
def _amclPoseCallback(self, PoseWithCovarianceStamped msg)
def clearLocalCostmap(self)
def clearGlobalCostmap(self)
def getAndTrackRoute(self, Union[int, PoseStamped] start, Union[int, PoseStamped] goal, bool use_start=False)
def undockRobot(self, str dock_type='')
clear_local_costmap_around_pose_srv
def lifecycleShutdown(self)
def getFeedback(self, RunningTask task=RunningTask.NONE)
def getGlobalCostmap(self)
def clearCostmapExceptRegion(self, float reset_distance)
def followObjectByTopic(self, str topic, int max_duration=0)
def _getRouteImpl(self, Union[int, PoseStamped] start, Union[int, PoseStamped] goal, bool use_start=False)
compute_path_through_poses_client
def _smoothPathImpl(self, Path path, str smoother_id='', float max_duration=2.0, bool check_for_collision=False)
def setTaskError(self, int error_code, str error_msg)
compute_and_track_route_client
def _setInitialPose(self)
def goToPose(self, PoseStamped pose, str behavior_tree='')
clear_global_costmap_around_pose_srv
def smoothPath(self, Path path, str smoother_id='', float max_duration=2.0, bool check_for_collision=False)
def followGpsWaypoints(self, list[GeoPose] gps_poses)