Note: This tutorial assumes that you have completed the previous tutorials: building a ROS package.
(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Understanding the Example Solution

Description: This tutorial walks through the code of the servicesim example solution detailing the ROS interfaces used and the state machine of the competition.

Tutorial Level: BEGINNER

The Code

The code is available on the servicesim bitbucket repository. Here is the full content of the file, we will go through it line by line below.

   1 #! /usr/bin/env python
   2 
   3 # Copyright (C) 2018 Open Source Robotics Foundation, Inc.
   4 #
   5 # Licensed under the Apache License, Version 2.0 (the "License");
   6 # you may not use this file except in compliance with the License.
   7 # You may obtain a copy of the License at
   8 #
   9 #     http://www.apache.org/licenses/LICENSE-2.0
  10 #
  11 # Unless required by applicable law or agreed to in writing, software
  12 # distributed under the License is distributed on an "AS IS" BASIS,
  13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 # See the License for the specific language governing permissions and
  15 # limitations under the License.
  16 
  17 import copy
  18 import random
  19 from enum import Enum
  20 
  21 import actionlib
  22 
  23 from actionlib_msgs.msg import GoalStatus
  24 
  25 from geometry_msgs.msg import Point, Pose, PoseWithCovarianceStamped, Quaternion
  26 
  27 from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
  28 
  29 import rospy
  30 
  31 from sensor_msgs.msg import Image
  32 
  33 from servicesim_competition.msg import ActorNames
  34 from servicesim_competition.srv import DropOffGuest, DropOffGuestRequest, NewTask
  35 from servicesim_competition.srv import PickUpGuest, PickUpGuestRequest, RoomInfo, RoomInfoRequest
  36 
  37 from std_srvs.srv import Empty, EmptyRequest
  38 
  39 
  40 class CompetitionState(Enum):
  41     BeginTask = 0
  42     Pickup = 1
  43     DropOff = 2
  44     ReturnToStart = 3
  45 
  46 
  47 class ExampleNode(object):
  48     def __init__(self):
  49         self.robot_name = 'servicebot'
  50         self.guest_name = ''
  51         self.action_timeout = 10
  52         self.actors_in_range = []
  53 
  54         rospy.init_node('example_solution')
  55         rospy.loginfo('node created')
  56         # create service proxies
  57         rospy.loginfo('wait for new task srv')
  58         rospy.wait_for_service('/servicesim/new_task')
  59         self.new_task_srv = rospy.ServiceProxy('/servicesim/new_task', NewTask)
  60         rospy.loginfo('wait for pickup guest srv')
  61         rospy.wait_for_service('/servicesim/pickup_guest')
  62         self.pickup_guest_srv = rospy.ServiceProxy('/servicesim/pickup_guest', PickUpGuest)
  63         rospy.loginfo('wait for drop off srv')
  64         rospy.wait_for_service('/servicesim/dropoff_guest')
  65         self.dropoff_guest_srv = rospy.ServiceProxy('/servicesim/dropoff_guest', DropOffGuest)
  66         rospy.loginfo('wait for room info srv')
  67         rospy.wait_for_service('/servicesim/room_info')
  68         self.room_info_srv = rospy.ServiceProxy('/servicesim/room_info', RoomInfo)
  69         # create a service proxy to clear the navigation costmaps
  70         rospy.loginfo('wait for clear_costmaps')
  71         rospy.wait_for_service('/servicebot/move_base/clear_costmaps')
  72         self.clear_costmaps_srv = rospy.ServiceProxy(
  73             '/servicebot/move_base/clear_costmaps', Empty)
  74         # create subscribers
  75         rospy.loginfo('create subs')
  76         rospy.Subscriber('/servicebot/rfid', ActorNames, self.rfid_callback)
  77         rospy.Subscriber('/servicebot/camera_front/image_raw', Image, self.image_callback)
  78         rospy.loginfo('done creating subs')
  79 
  80         # create publisher
  81         # create a publisher to initialize the localization with the starting pose
  82         # provided by the competition
  83         self.initial_pose_pub = rospy.Publisher(
  84             '/servicebot/initialpose', PoseWithCovarianceStamped, latch=True)
  85         # create action client
  86         self.move_base = actionlib.SimpleActionClient('/servicebot/move_base', MoveBaseAction)
  87         self.move_base.wait_for_server(rospy.Duration(self.action_timeout))
  88         rospy.loginfo('done waiting for move base action server')
  89 
  90     def rfid_callback(self, msg):
  91         # this function will be called periodically with the list of RFID tags
  92         # around the robot
  93         if msg.actor_names != self.actors_in_range:
  94             rospy.loginfo(msg.actor_names)
  95         self.actors_in_range = msg.actor_names
  96         return True
  97 
  98     def image_callback(self, msg):
  99         #  placeholder for competitor to add image processing
 100         return True
 101 
 102     def request_new_task(self):
 103         # Calls the service to start the competition and receive a new task
 104         resp = self.new_task_srv()
 105         rospy.loginfo(resp)
 106         return [
 107             resp.pick_up_location, resp.drop_off_location, resp.guest_name, resp.robot_start_pose]
 108 
 109     def request_follow(self):
 110         # Calls the service to request a specific guest to follow the robot
 111         req = PickUpGuestRequest()
 112         req.robot_name = self.robot_name
 113         req.guest_name = self.guest_name
 114         resp = self.pickup_guest_srv(req)
 115         rospy.loginfo(resp)
 116         return [resp.success]
 117 
 118     def request_unfollow(self):
 119         # Calls the service to request a specific guest to stop followingthe robot
 120         req = DropOffGuestRequest()
 121         req.guest_name = self.guest_name
 122         resp = self.dropoff_guest_srv(req)
 123         rospy.loginfo(resp)
 124         return [resp.success]
 125 
 126     # returns a target pose from the room name
 127     def pose_from_room_name(self, room_name):
 128         # Calls the service to get pose infromations based on a room name
 129         req = RoomInfoRequest()
 130         req.name = room_name
 131         resp = self.room_info_srv(req)
 132 
 133         # To be replaced by competitors
 134         # here we just return the center of the provided area
 135         mid_pose = Pose(
 136             Point(
 137                 (resp.min.x + resp.max.x) / 2., (resp.min.y + resp.max.y) / 2., 0),
 138             Quaternion(0, 0, 1.0, 0.0))
 139         return mid_pose
 140 
 141     def construct_goal_from_pose(self, pose):
 142         # Takes a 6D pose and returns a navigation goal
 143         posecopy = copy.deepcopy(pose)
 144         goal = MoveBaseGoal()
 145         goal.target_pose.pose = posecopy
 146         # As we need the guest to be at the target location and not the robot,
 147         # we offset every goal of 1 meter
 148         goal.target_pose.pose.position.x -= 1
 149         goal.target_pose.header.frame_id = 'map'
 150         goal.target_pose.header.stamp = rospy.Time.now()
 151 
 152         # add randomness (square of 10cm) around the goal
 153         dx = random.randrange(0, 20, 1) - 10
 154         dy = random.randrange(0, 20, 1) - 10
 155         goal.target_pose.pose.position.x += dx / 100.
 156         goal.target_pose.pose.position.y += dy / 100.
 157         return goal
 158 
 159     def example_solution(self):
 160         # This handles the competition logic
 161         # A state maching that gives goals to the robot and calls the competition services
 162         # to complete the checkpoints
 163         state = CompetitionState.BeginTask
 164 
 165         rate = rospy.Rate(1)
 166 
 167         while not rospy.is_shutdown():
 168             if state == CompetitionState.BeginTask:
 169                 rospy.loginfo('In BeginTask state')
 170                 # Request a new task
 171                 [pick_up_room, drop_off_room, self.guest_name, self.start_pose] = \
 172                     self.request_new_task()
 173                 self.start_pose.position.z = 0
 174                 # Initialize the localization with the provided starting position
 175                 init_pose_msg = PoseWithCovarianceStamped()
 176                 init_pose_msg.header.frame_id = 'map'
 177                 init_pose_msg.pose.pose = self.start_pose
 178                 self.initial_pose_pub.publish(init_pose_msg)
 179                 rospy.loginfo('published initial pose')
 180                 rospy.sleep(0.5)
 181 
 182                 # Switch to the Pickup state
 183                 state = CompetitionState.Pickup
 184 
 185             elif state == CompetitionState.Pickup:
 186                 # Go to pick up point
 187                 rospy.loginfo('In Pickup state')
 188                 # Cancel previous navigation goals
 189                 self.move_base.cancel_all_goals()
 190                 # Clears the obstacle maps
 191                 rospy.loginfo('clearing costmaps')
 192                 self.clear_costmaps_srv(EmptyRequest())
 193                 rospy.sleep(0.5)
 194                 pickup_goal = self.construct_goal_from_pose(self.pose_from_room_name(pick_up_room))
 195                 # Ask the robot to go to the pickup location by sending it a navigation goal
 196                 self.move_base.send_goal(pickup_goal)
 197                 rospy.loginfo('sent goal')
 198 
 199                 self.move_base.wait_for_result(rospy.Duration(self.action_timeout))
 200                 rospy.loginfo('action timed out in state: %s' % self.move_base.get_state())
 201                 if self.move_base.get_state() == GoalStatus.SUCCEEDED:
 202                     # If the robot succeeded to reach the pickup point
 203                     # check that the guest is in range
 204                     if self.guest_name in self.actors_in_range:
 205                         rospy.loginfo('requesting unfollow')
 206                         # if the guest is in range, ask the guest to follow the robot
 207                         if self.request_follow():
 208                             # the guest is following the robot
 209                             # Switch to the DropOff state
 210                             state = CompetitionState.DropOff
 211                     else:
 212                         # pickup point reached but the guest is not in range
 213                         # add custom room exploration logic here
 214                         rospy.logwarn('robot reached pickup point but guest is not in range!')
 215                 else:
 216                     rospy.loginfo('action timed out in state: %s' % self.move_base.get_state())
 217 
 218             elif state == CompetitionState.DropOff:
 219                 # Go to drop off point
 220                 rospy.loginfo('In DropOff state')
 221                 # Cancel previous navigation goals
 222                 self.move_base.cancel_all_goals()
 223                 # Clears the obstacle maps
 224                 rospy.loginfo('clearing costmaps')
 225                 self.clear_costmaps_srv(EmptyRequest())
 226                 rospy.sleep(0.5)
 227                 # Ask the robot to go to the drop off location by sending it a navigation goal
 228                 dropoff_goal = self.construct_goal_from_pose(
 229                     self.pose_from_room_name(drop_off_room))
 230                 self.move_base.send_goal(dropoff_goal)
 231                 self.move_base.wait_for_result(rospy.Duration(self.action_timeout))
 232                 # If the robot succeeded to reach the dropoff point
 233                 if self.move_base.get_state() == GoalStatus.SUCCEEDED:
 234                     # ask the guest to stop following the robot
 235                     self.request_unfollow()
 236                     state = CompetitionState.ReturnToStart
 237                 else:
 238                     rospy.loginfo('action timed out in state: %s' % self.move_base.get_state())
 239 
 240             elif state == CompetitionState.ReturnToStart:
 241                 # Go to drop off point
 242                 rospy.loginfo('In ReturnToStart state')
 243                 # Cancel previous navigation goals
 244                 self.move_base.cancel_all_goals()
 245                 # Clears the obstacle maps
 246                 rospy.loginfo('clearing costmaps')
 247                 self.clear_costmaps_srv(EmptyRequest())
 248                 rospy.sleep(0.5)
 249                 # Ask the robot to return to the starting position
 250                 startgoal = self.construct_goal_from_pose(self.start_pose)
 251                 self.move_base.send_goal(startgoal)
 252                 self.move_base.wait_for_result(rospy.Duration(self.action_timeout))
 253                 if self.move_base.get_state() == GoalStatus.SUCCEEDED:
 254                     # If the robot succeeded to reach the starting point
 255                     # the competition is over
 256                     rospy.loginfo('Hooray!')
 257                     rospy.signal_shutdown('Competition completed!')
 258                 else:
 259                     rospy.loginfo('action timed out in state: %s' % self.move_base.get_state())
 260 
 261             rate.sleep()
 262 
 263 
 264 if __name__ == '__main__':
 265     node = ExampleNode()
 266     try:
 267         node.example_solution()
 268     except rospy.ROSInterruptException:
 269         pass

The Code Explained

Now, let's break the code down.

   1 #! /usr/bin/env python

Every Python ROS Node will have this declaration at the top. The first line makes sure your script is executed as a Python script.

Importing the python modules

  17 import copy
  18 import random

We will need these python builtin modules to perform random number generation in our script

  19 from enum import Enum

This class will allow us to declare an enumeration of the states of our state machine

  21 import actionlib
  22 
  23 from actionlib_msgs.msg import GoalStatus
  24 
  25 from geometry_msgs.msg import Point, Pose, PoseWithCovarianceStamped, Quaternion
  26 
  27 from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal

  37 from std_srvs.srv import Empty, EmptyRequest

Import the ROS message and service types that we will use for our application

  29 import rospy

Import rospy is necessary to create a python ROS Node

  33 from servicesim_competition.msg import ActorNames
  34 from servicesim_competition.srv import DropOffGuest, DropOffGuestRequest, NewTask
  35 from servicesim_competition.srv import PickUpGuest, PickUpGuestRequest, RoomInfo, RoomInfoRequest

Import the servicesim_competition service and message type to be able to interact with the competition logic

Defining the comeptition's state machine states

  40 class CompetitionState(Enum):
  41     BeginTask = 0
  42     Pickup = 1
  43     DropOff = 2
  44     ReturnToStart = 3

Here we declare the states we will use in our state machine. These states match the Checkpoints of the competition.

The competition example class

  47 class ExampleNode(object):

Define the class where we will implement our ROS Node

Initialisation of the class

  48     def __init__(self):

In this function we will create all the variables and ROS interfaces we need to complete the competition

  49         self.robot_name = 'servicebot'
  50         self.guest_name = ''
  51         self.action_timeout = 10
  52         self.actors_in_range = []

We initialize the robot_name to its value in the competition. action_timeout will be used to decide when to resend a goal to the navigation algorithm and p[lan a new path towards that goal. Here we set it to 10 seconds.

  54         rospy.init_node('example_solution')

The next line, rospy.init_node(NAME, ...), is very important as it tells rospy the name of your node -- until rospy has this information, it cannot start communicating with the ROS Master. In this case, your node will take on the name "example_solution". NOTE: the name must be a base name, i.e. it cannot contain any slashes "/"

  58         rospy.wait_for_service('/servicesim/new_task')
  59         self.new_task_srv = rospy.ServiceProxy('/servicesim/new_task', NewTask)
  60         rospy.loginfo('wait for pickup guest srv')
  61         rospy.wait_for_service('/servicesim/pickup_guest')
  62         self.pickup_guest_srv = rospy.ServiceProxy('/servicesim/pickup_guest', PickUpGuest)
  63         rospy.loginfo('wait for drop off srv')
  64         rospy.wait_for_service('/servicesim/dropoff_guest')
  65         self.dropoff_guest_srv = rospy.ServiceProxy('/servicesim/dropoff_guest', DropOffGuest)
  66         rospy.loginfo('wait for room info srv')
  67         rospy.wait_for_service('/servicesim/room_info')
  68         self.room_info_srv = rospy.ServiceProxy('/servicesim/room_info', RoomInfo)

Here we make sure that all the service for the competition are available and we create Service Proxies to be able to use them later in the competition

  70         rospy.loginfo('wait for clear_costmaps')
  71         rospy.wait_for_service('/servicebot/move_base/clear_costmaps')
  72         self.clear_costmaps_srv = rospy.ServiceProxy(
  73             '/servicebot/move_base/clear_costmaps', Empty)

This service will allow us to remove clear the obstacle map built by the robot. This will be helpful when the robot is stuck and cannot find a path to its destination

  76         rospy.Subscriber('/servicebot/rfid', ActorNames, self.rfid_callback)
  77         rospy.Subscriber('/servicebot/camera_front/image_raw', Image, self.image_callback)

We subscribe to the sensor data from the robot we want to use. Note that the last argument provided is a function name. That callback function will be used everytime a message is received on that topic

  81         # create a publisher to initialize the localization with the starting pose
  82         # provided by the competition
  83         self.initial_pose_pub = rospy.Publisher(
  84             '/servicebot/initialpose', PoseWithCovarianceStamped, latch=True)
  85         # create action client
  86         self.move_base = actionlib.SimpleActionClient('/servicebot/move_base', MoveBaseAction)
  87         self.move_base.wait_for_server(rospy.Duration(self.action_timeout))

Create the interfaces that will allow us to interact with the localization and the navigation algorithms of the robot

Methods of the class

  90     def rfid_callback(self, msg):
  91         # this function will be called periodically with the list of RFID tags
  92         # around the robot
  93         if msg.actor_names != self.actors_in_range:
  94             rospy.loginfo(msg.actor_names)
  95         self.actors_in_range = msg.actor_names
  96         return True

This function will be used to process the RFID sensor data. This will receive messages of type ActorNames that is a list of all the actor names in range of the robot's sensor Everytime we receive the list we store it in the actor_in_range variable to be able to reuse it later. This callback also prints the list to the console if the list has changed.

  98     def image_callback(self, msg):
  99         #  placeholder for competitor to add image processing

This function will be used to process the robot's camera images. This is currently empty and should be where users can add their own image processing logic for the competition.

 102     def request_new_task(self):
 103         # Calls the service to start the competition and receive a new task
 104         resp = self.new_task_srv()
 105         rospy.loginfo(resp)
 106         return [
 107             resp.pick_up_location, resp.drop_off_location, resp.guest_name, resp.robot_start_pose]
 108 
 109     def request_follow(self):
 110         # Calls the service to request a specific guest to follow the robot
 111         req = PickUpGuestRequest()
 112         req.robot_name = self.robot_name
 113         req.guest_name = self.guest_name
 114         resp = self.pickup_guest_srv(req)
 115         rospy.loginfo(resp)
 116         return [resp.success]
 117 
 118     def request_unfollow(self):
 119         # Calls the service to request a specific guest to stop followingthe robot
 120         req = DropOffGuestRequest()
 121         req.guest_name = self.guest_name
 122         resp = self.dropoff_guest_srv(req)
 123         rospy.loginfo(resp)
 124         return [resp.success]

These functions are used to call the competition services allowing to validate checkpoints.

 127     def pose_from_room_name(self, room_name):
 128         # Calls the service to get pose infromations based on a room name
 129         req = RoomInfoRequest()
 130         req.name = room_name
 131         resp = self.room_info_srv(req)
 132 
 133         # To be replaced by competitors
 134         # here we just return the center of the provided area
 135         mid_pose = Pose(
 136             Point(
 137                 (resp.min.x + resp.max.x) / 2., (resp.min.y + resp.max.y) / 2., 0),
 138             Quaternion(0, 0, 1.0, 0.0))
 139         return mid_pose

As the task given by the competition are providing us room names, we need to convert these to cartesian locations in the world for the robot to navigate, This function requests the room information by calling the room_info service and than creates a Pose pointing to the center of the provided area. This is a place where competitors can add their own logic based on their knowledge of the simulation environment layout (for example to make sure this pose is not occupied by a known static obstacle).

 141     def construct_goal_from_pose(self, pose):
 142         # Takes a 6D pose and returns a navigation goal
 143         posecopy = copy.deepcopy(pose)
 144         goal = MoveBaseGoal()
 145         goal.target_pose.pose = posecopy
 146         # As we need the guest to be at the target location and not the robot,
 147         # we offset every goal of 1 meter
 148         goal.target_pose.pose.position.x -= 1
 149         goal.target_pose.header.frame_id = 'map'
 150         goal.target_pose.header.stamp = rospy.Time.now()
 151 
 152         # add randomness (square of 10cm) around the goal
 153         dx = random.randrange(0, 20, 1) - 10
 154         dy = random.randrange(0, 20, 1) - 10
 155         goal.target_pose.pose.position.x += dx / 100.
 156         goal.target_pose.pose.position.y += dy / 100.
 157         return goal

This function will create a navigation goal from a Pose. Competitors can add their own logic on how to modify a the provided pose to make a compelling navigation goal. Currently this function offsets the pose of 1 meter, this is to make sure that the guess following the robot will reach the destination area. It also adds some randomness (up to 10 centimeter in every direction), this is to make sur the planner will try to find a new path to the destination and should prevent the robot from getting stuck.

Main function

 159     def example_solution(self):

Now that we have all the functions we need to perform the competition, it is time to go through our main function containing all the competition logic. This function will be a state machine going through all states of the competition. It will loop every second and update the states if necessary. The competition starts in the state BeginTask.

BeginTask state

 168             if state == CompetitionState.BeginTask:
 169                 rospy.loginfo('In BeginTask state')
 170                 # Request a new task
 171                 [pick_up_room, drop_off_room, self.guest_name, self.start_pose] = \
 172                     self.request_new_task()
 173                 self.start_pose.position.z = 0
 174                 # Initialize the localization with the provided starting position
 175                 init_pose_msg = PoseWithCovarianceStamped()
 176                 init_pose_msg.header.frame_id = 'map'
 177                 init_pose_msg.pose.pose = self.start_pose
 178                 self.initial_pose_pub.publish(init_pose_msg)
 179                 rospy.loginfo('published initial pose')
 180                 rospy.sleep(0.5)
 181 
 182                 # Switch to the Pickup state
 183                 state = CompetitionState.Pickup

In the BeginTask state we start the competition by requesting a new task self.request_new_task(). Then we initialize the localisation of the robot with the starting position provided by the task Finally we trigger the transition to go to the Pickup state.

PickUp state

 185             elif state == CompetitionState.Pickup:

In the Pickup state we will ask to robot to go to the PickUp location.

 188                 # Cancel previous navigation goals
 189                 self.move_base.cancel_all_goals()
 190                 # Clears the obstacle maps
 191                 rospy.loginfo('clearing costmaps')
 192                 self.clear_costmaps_srv(EmptyRequest())
 193                 rospy.sleep(0.5)

We first make sure that the robot is not moving by cancelling all the goals and clear the obstacle map.

 194                 pickup_goal = self.construct_goal_from_pose(self.pose_from_room_name(pick_up_room))
 195                 # Ask the robot to go to the pickup location by sending it a navigation goal
 196                 self.move_base.send_goal(pickup_goal)
 197                 rospy.loginfo('sent goal')
 198 
 199                 self.move_base.wait_for_result(rospy.Duration(self.action_timeout))

Then we construct a navigation goal and send it to the navigation algorithm. We wait up to 10 seconds to let the robot perform the motion.

 201                 if self.move_base.get_state() == GoalStatus.SUCCEEDED:

Once the action is over, or 10 seconds have passed, we check if the robot reached the destination

 202                     # If the robot succeeded to reach the pickup point
 203                     # check that the guest is in range
 204                     if self.guest_name in self.actors_in_range:

If it succeeds we make sure that the guest is in range of the RFID sensor of the robot.

 206                         # if the guest is in range, ask the guest to follow the robot
 207                         if self.request_follow():
 208                             # the guest is following the robot
 209                             # Switch to the DropOff state
 210                             state = CompetitionState.DropOff

If that's the case we request the guest to follow the robot and switch to the DropOff state.

 211                     else:
 212                         # pickup point reached but the guest is not in range
 213                         # add custom room exploration logic here
 214                         rospy.logwarn('robot reached pickup point but guest is not in range!')

If the guest is not in range of the RFID sensor, the competitors should add their own logic to explore the surrounding and identify the guest.

DropOff state

Like before we start by cancelling all goals and clear the obstacle maps.

 228                 dropoff_goal = self.construct_goal_from_pose(
 229                     self.pose_from_room_name(drop_off_room))
 230                 self.move_base.send_goal(dropoff_goal)
 231                 self.move_base.wait_for_result(rospy.Duration(self.action_timeout))

We construct a navigation goal for the drop off location and send it to the navigation algorithm

 233                 if self.move_base.get_state() == GoalStatus.SUCCEEDED:
 234                     # ask the guest to stop following the robot
 235                     self.request_unfollow()
 236                     state = CompetitionState.ReturnToStart
 237                 else:
 238                     rospy.loginfo('action timed out in state: %s' % self.move_base.get_state())

If the robot succeeds to reach the goal we notify the guest that the destination has been reached by calling self.request_unfollow. Otherwise we try againg with a slightly different goal. Here competitors can add their own logic to pick a new goal to make sure the guest reaches the drop off area. If we successfully dropped off the guest to destination we switch to the ReturnToStart state

ReturnToStart state

 240             elif state == CompetitionState.ReturnToStart:
 241                 # Go to drop off point
 242                 rospy.loginfo('In ReturnToStart state')
 243                 # Cancel previous navigation goals
 244                 self.move_base.cancel_all_goals()
 245                 # Clears the obstacle maps
 246                 rospy.loginfo('clearing costmaps')
 247                 self.clear_costmaps_srv(EmptyRequest())
 248                 rospy.sleep(0.5)
 249                 # Ask the robot to return to the starting position
 250                 startgoal = self.construct_goal_from_pose(self.start_pose)
 251                 self.move_base.send_goal(startgoal)
 252                 self.move_base.wait_for_result(rospy.Duration(self.action_timeout))
 253                 if self.move_base.get_state() == GoalStatus.SUCCEEDED:
 254                     # If the robot succeeded to reach the starting point
 255                     # the competition is over
 256                     rospy.loginfo('Hooray!')
 257                     rospy.signal_shutdown('Competition completed!')
 258                 else:
 259                     rospy.loginfo('action timed out in state: %s' % self.move_base.get_state())

Similarly to the previous state, we clear the maps and ask the robot to navigate to its' starting position. If the robot succeeds the node exits by calling rospy.signal_shutdown(), otherwise we try again with a slightly different goal. Competitors can add their own logic to ensure the robot reaches the starting position.

Wiki: servicesim/Tutorials/UnderstandingTheExampleSolution (last edited 2018-03-01 17:54:40 by Marguedas)