예제 #1
0
    def test_send_goal_async_with_feedback_for_another_goal(self):
        ac = ActionClient(self.node, Fibonacci, 'fibonacci')
        try:
            self.assertTrue(ac.wait_for_server(timeout_sec=2.0))

            # Send a goal and then publish feedback
            first_goal_uuid = UUID(uuid=list(uuid.uuid4().bytes))
            future = ac.send_goal_async(
                Fibonacci.Goal(),
                feedback_callback=self.feedback_callback,
                goal_uuid=first_goal_uuid)
            rclpy.spin_until_future_complete(self.node, future, self.executor)

            # Send another goal, but without a feedback callback
            second_goal_uuid = UUID(uuid=list(uuid.uuid4().bytes))
            future = ac.send_goal_async(Fibonacci.Goal(),
                                        goal_uuid=second_goal_uuid)
            rclpy.spin_until_future_complete(self.node, future, self.executor)

            # Publish feedback for the second goal
            self.mock_action_server.publish_feedback(second_goal_uuid)
            self.timed_spin(1.0)
            self.assertEqual(self.feedback, None)
            # Publish feedback for the first goal (with callback)
            self.mock_action_server.publish_feedback(first_goal_uuid)
            self.timed_spin(1.0)
            self.assertNotEqual(self.feedback, None)
        finally:
            ac.destroy()
예제 #2
0
 def test_send_goal_async_no_server(self):
     ac = ActionClient(self.node, Fibonacci, 'not_fibonacci')
     try:
         future = ac.send_goal_async(Fibonacci.Goal())
         self.timed_spin(2.0)
         self.assertFalse(future.done())
     finally:
         ac.destroy()
예제 #3
0
class SendGoal(smach.State):  #Create state SendGoal
    def __init__(self):
        smach.State.__init__(self, 
                             outcomes=['success','failed'],
                             input_keys=['move_base_goals_in','from_goal','goal_order'],
                             output_keys=['from_goal'])
        self.goal_msgs = NavigateToPose.Goal()
        self.node_ = rclpy.create_node('nav2_client')
        self.node_.get_logger().info('Created node')
        self.action_client = ActionClient (self.node_,NavigateToPose,'navigate_to_pose')
    def execute(self, userdata):
        if self.action_client.wait_for_server(20)!=1:
            self.node_.get_logger().error('Action server not available after waiting.')
            self.destory_node()
            return 'failed'
        move_base_goal = userdata.move_base_goals_in[userdata.goal_order[userdata.from_goal]].split(';')
        userdata.from_goal+=1
        self.goal_msgs.pose.header.frame_id='map'
        self.goal_msgs.pose.header.stamp=self.node_.get_clock().now().to_msg()
        self.goal_msgs.pose.pose.position.x=float(move_base_goal[0])
        self.goal_msgs.pose.pose.position.y=float(move_base_goal[1])
        self.goal_msgs.pose.pose.position.z= 0.0

        self.goal_msgs.pose.pose.orientation.x = 0.0
        self.goal_msgs.pose.pose.orientation.y = 0.0
        self.goal_msgs.pose.pose.orientation.z = float(move_base_goal[2])
        self.goal_msgs.pose.pose.orientation.w = float(move_base_goal[3])
        goal_handle_future=self.action_client.send_goal_async(self.goal_msgs)
        rclpy.spin_until_future_complete(self.node_,goal_handle_future)
        goal_handle = goal_handle_future.result()
        if not goal_handle.accepted:
            self.node_.get_logger().info('Goal rejected :(')
            self.destory_node()
            return 'failed'
        get_result_future = goal_handle.get_result_async()
        rclpy.spin_until_future_complete(self.node_,get_result_future)
        result = get_result_future.result().result
        status = get_result_future.result().status
        if status == GoalStatus.STATUS_SUCCEEDED:
            self.node_.get_logger().info('Succeeded!')
        elif status == GoalStatus.STATUS_ABORTED:
            self.node_.get_logger().error('Goal was aborted')
            self.destory_node()
            return 'failed'
        elif status == GoalStatus.STATUS_CANCLED:
            self.node_.get_logger().error('Goal was canceled')
            self.destory_node()
            return 'failed'
        else: 
            self.node_.get_logger().error('Unknown result code')
            self.destory_node()
            return 'failed'
        if userdata.from_goal == len(userdata.goal_order):
            self.destory_node()
        return 'success'
    def destory_node(self):
        self.action_client.destroy()
        self.node_.destroy_node()
예제 #4
0
 def test_get_num_entities(self):
     ac = ActionClient(self.node, Fibonacci, 'fibonacci')
     num_entities = ac.get_num_entities()
     self.assertEqual(num_entities.num_subscriptions, 2)
     self.assertEqual(num_entities.num_guard_conditions, 0)
     self.assertEqual(num_entities.num_timers, 0)
     self.assertEqual(num_entities.num_clients, 3)
     self.assertEqual(num_entities.num_services, 0)
     ac.destroy()
예제 #5
0
 def test_different_type_raises(self):
     ac = ActionClient(self.node, Fibonacci, 'fibonacci')
     try:
         with self.assertRaises(TypeError):
             ac.send_goal('different goal type')
         with self.assertRaises(TypeError):
             ac.send_goal_async('different goal type')
     finally:
         ac.destroy()
예제 #6
0
 def test_wait_for_server_exists(self):
     ac = ActionClient(self.node, Fibonacci, 'fibonacci')
     try:
         start = time.monotonic()
         self.assertTrue(ac.wait_for_server(timeout_sec=2.0))
         end = time.monotonic()
         self.assertGreater(0.0, end - start - TIME_FUDGE)
         self.assertLess(0.0, end - start + TIME_FUDGE)
     finally:
         ac.destroy()
예제 #7
0
 def test_constructor_no_defaults(self):
     ac = ActionClient(
         self.node,
         Fibonacci,
         'fibonacci',
         goal_service_qos_profile=rclpy.qos.QoSProfile(depth=10),
         result_service_qos_profile=rclpy.qos.QoSProfile(depth=10),
         cancel_service_qos_profile=rclpy.qos.QoSProfile(depth=10),
         feedback_sub_qos_profile=rclpy.qos.QoSProfile(depth=10),
         status_sub_qos_profile=rclpy.qos.QoSProfile(depth=10))
     ac.destroy()
예제 #8
0
 def test_send_goal_async(self):
     ac = ActionClient(self.node, Fibonacci, 'fibonacci')
     try:
         self.assertTrue(ac.wait_for_server(timeout_sec=2.0))
         future = ac.send_goal_async(Fibonacci.Goal())
         rclpy.spin_until_future_complete(self.node, future, self.executor)
         self.assertTrue(future.done())
         goal_handle = future.result()
         self.assertTrue(goal_handle.accepted)
     finally:
         ac.destroy()
예제 #9
0
class HelloClient(Node):
    def __init__(self):
        super().__init__('hello_action_test_client')

        self.hello_client = ActionClient(self, Hello, 'hello_action')

    def goal_responseCB(self, future):  # dunno what the future handles...
        goal_handle = future.result()
        if not goal_handle.accepted:
            self.get_logger().info(
                'Goal rejected: {0.accepted}'.format(goal_handle))
            return

        self.get_logger().info(
            'Goal accepted: {0.accepted}'.format(goal_handle))

        self.get_result_future = goal_handle.get_result_async()
        self.get_result_future.add_done_callback(self.get_resultCB)

    def feedbackCB(self, feedback):
        # Gets feedback data by "goal_handle.publish_feedback" function from server
        self.get_logger().info('Received feedback:\n{0.status}'.format(
            feedback.feedback))

    def get_resultCB(self, future):
        result = future.result().result
        status = future.result(
        ).status  # probably "goal_handle.succeed()" from server?

        if status == GoalStatus.STATUS_SUCCEEDED:
            self.get_logger().info(
                'Goal succeeded!\n\t Result: {0.status}'.format(result))
        else:
            self.get_logger().info(
                'Goal failed with status: {}'.format(status))

    def send_goal(self):
        self.get_logger().info('Waiting for action server...')
        self.hello_client.wait_for_server()

        goal_msg = Hello.Goal()
        goal_msg.hello_goal = 'Hello World!'

        self.get_logger().info('Sending goal request...')
        self.send_goal_future = self.hello_client.send_goal_async(
            goal_msg, feedback_callback=self.feedbackCB)
        self.send_goal_future.add_done_callback(self.goal_responseCB)

    def destroy(self):
        self.hello_client.destroy()
        self.destroy_node()
예제 #10
0
class TestPipelineRun(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        rclpy.init()

    @classmethod
    def tearDownClass(cls):
        rclpy.shutdown()

    def setUp(self):
        self.node = rclpy.create_node('test_node')

        self.configure_client = self.node.create_client(
            ConfigurePipeline, '/triton/configure_pipeline')
        while not self.configure_client.wait_for_service(timeout_sec=1.0):
            self.node.get_logger().warn(
                'Configure pipeline service not available, waiting again...')

        self.run_client = ActionClient(self.node, RunPipeline,
                                       '/triton/run_pipeline')
        while not self.run_client.wait_for_server(timeout_sec=1.0):
            self.node.get_logger().warn(
                'Run pipeline service not available, waiting again...')

    def tearDown(self):
        self.configure_client.destroy()
        self.run_client.destroy()
        self.node.destroy_node()

    def test_run_no_configure(self, pipeline_manager, proc_info, proc_output):
        goal_msg = RunPipeline.Goal()
        goal_msg.input = 0
        future = self.run_client.send_goal_async(goal_msg)

        future_count = 0
        result = None
        while rclpy.ok():
            rclpy.spin_once(self.node)
            if future.done():
                future_count += 1
                if future_count == 1:
                    future = future.result().get_result_async()
                if future_count == 2:
                    result = future.result().result
                    break
        if result.output == 1:
            proc_output.assertWaitFor(
                expected_output='Pipeline is not configured')
        else:
            self.fail('Should abort due to unconfigured pipeline')
예제 #11
0
def main(args=None):
    rclpy.init(args=args)

    node = rclpy.create_node('minimal_action_client')

    action_client = ActionClient(node, Fibonacci, 'fibonacci')

    node.get_logger().info('Waiting for action server...')

    action_client.wait_for_server()

    goal_msg = Fibonacci.Goal()
    goal_msg.order = 10

    node.get_logger().info('Sending goal request...')

    send_goal_future = action_client.send_goal_async(
        goal_msg,
        feedback_callback=lambda feedback: feedback_cb(node.get_logger(),
                                                       feedback))

    rclpy.spin_until_future_complete(node, send_goal_future)

    goal_handle = send_goal_future.result()

    if not goal_handle.accepted:
        node.get_logger().info('Goal rejected :(')
        action_client.destroy()
        node.destroy_node()
        rclpy.shutdown()
        return

    node.get_logger().info('Goal accepted :)')

    get_result_future = goal_handle.get_result_async()

    rclpy.spin_until_future_complete(node, get_result_future)

    result = get_result_future.result().result
    status = get_result_future.result().status
    if status == GoalStatus.STATUS_SUCCEEDED:
        node.get_logger().info('Goal succeeded! Result: {0}'.format(
            result.sequence))
    else:
        node.get_logger().info(
            'Goal failed with status code: {0}'.format(status))

    action_client.destroy()
    node.destroy_node()
    rclpy.shutdown()
예제 #12
0
    def test_get_result_async(self):
        ac = ActionClient(self.node, Fibonacci, 'fibonacci')
        try:
            self.assertTrue(ac.wait_for_server(timeout_sec=2.0))

            # Send a goal
            goal_future = ac.send_goal_async(Fibonacci.Goal())
            rclpy.spin_until_future_complete(self.node, goal_future, self.executor)
            self.assertTrue(goal_future.done())
            goal_handle = goal_future.result()

            # Get the goal result
            result_future = goal_handle.get_result_async()
            rclpy.spin_until_future_complete(self.node, result_future, self.executor)
            self.assertTrue(result_future.done())
        finally:
            ac.destroy()
예제 #13
0
    def test_send_goal_async_with_feedback_after_goal(self):
        ac = ActionClient(self.node, Fibonacci, 'fibonacci')
        try:
            self.assertTrue(ac.wait_for_server(timeout_sec=2.0))

            # Send a goal and then publish feedback
            goal_uuid = UUID(uuid=list(uuid.uuid4().bytes))
            future = ac.send_goal_async(
                Fibonacci.Goal(),
                feedback_callback=self.feedback_callback,
                goal_uuid=goal_uuid)
            rclpy.spin_until_future_complete(self.node, future, self.executor)

            # Publish feedback after goal has been accepted
            self.mock_action_server.publish_feedback(goal_uuid)
            self.timed_spin(1.0)
            self.assertNotEqual(self.feedback, None)
        finally:
            ac.destroy()
예제 #14
0
    def test_send_cancel_async(self):
        ac = ActionClient(self.node, Fibonacci, 'fibonacci')
        try:
            self.assertTrue(ac.wait_for_server(timeout_sec=2.0))

            # Send a goal
            goal_future = ac.send_goal_async(Fibonacci.Goal())
            rclpy.spin_until_future_complete(self.node, goal_future, self.executor)
            self.assertTrue(goal_future.done())
            goal_handle = goal_future.result()

            # Cancel the goal
            cancel_future = goal_handle.cancel_goal_async()
            rclpy.spin_until_future_complete(self.node, cancel_future, self.executor)
            self.assertTrue(cancel_future.done())
            self.assertEqual(
                cancel_future.result().goals_canceling[0].goal_id,
                goal_handle.goal_id)
        finally:
            ac.destroy()
예제 #15
0
 def test_send_goal_multiple(self):
     ac = ActionClient(self.node,
                       Fibonacci,
                       'fibonacci',
                       callback_group=ReentrantCallbackGroup())
     executor = MultiThreadedExecutor(context=self.context)
     try:
         self.assertTrue(ac.wait_for_server(timeout_sec=2.0))
         future_0 = ac.send_goal_async(Fibonacci.Goal())
         future_1 = ac.send_goal_async(Fibonacci.Goal())
         future_2 = ac.send_goal_async(Fibonacci.Goal())
         rclpy.spin_until_future_complete(self.node, future_0, executor)
         rclpy.spin_until_future_complete(self.node, future_1, executor)
         rclpy.spin_until_future_complete(self.node, future_2, executor)
         self.assertTrue(future_0.done())
         self.assertTrue(future_1.done())
         self.assertTrue(future_2.done())
         self.assertTrue(future_0.result().accepted)
         self.assertTrue(future_1.result().accepted)
         self.assertTrue(future_2.result().accepted)
     finally:
         ac.destroy()
예제 #16
0
class NavTester(Node):
    def __init__(self,
                 initial_pose: Pose,
                 goal_pose: Pose,
                 namespace: str = ''):
        super().__init__(node_name='nav2_tester', namespace=namespace)
        self.initial_pose_pub = self.create_publisher(
            PoseWithCovarianceStamped, 'initialpose', 10)
        self.goal_pub = self.create_publisher(PoseStamped, 'goal_pose', 10)

        pose_qos = QoSProfile(
            durability=QoSDurabilityPolicy.
            RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
            reliability=QoSReliabilityPolicy.
            RMW_QOS_POLICY_RELIABILITY_RELIABLE,
            history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
            depth=1)

        self.model_pose_sub = self.create_subscription(
            PoseWithCovarianceStamped, 'amcl_pose', self.poseCallback,
            pose_qos)
        self.initial_pose_received = False
        self.initial_pose = initial_pose
        self.goal_pose = goal_pose
        self.action_client = ActionClient(self, NavigateToPose,
                                          'navigate_to_pose')

    def info_msg(self, msg: str):
        self.get_logger().info('\033[1;37;44m' + msg + '\033[0m')

    def warn_msg(self, msg: str):
        self.get_logger().warn('\033[1;37;43m' + msg + '\033[0m')

    def error_msg(self, msg: str):
        self.get_logger().error('\033[1;37;41m' + msg + '\033[0m')

    def setInitialPose(self):
        msg = PoseWithCovarianceStamped()
        msg.pose.pose = self.initial_pose
        msg.header.frame_id = 'map'
        self.info_msg('Publishing Initial Pose')
        self.initial_pose_pub.publish(msg)
        self.currentPose = self.initial_pose

    def getStampedPoseMsg(self, pose: Pose):
        msg = PoseStamped()
        msg.header.frame_id = 'map'
        msg.pose = pose
        return msg

    def publishGoalPose(self, goal_pose: Optional[Pose] = None):
        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        self.goal_pub.publish(self.getStampedPoseMsg(self.goal_pose))

    def runNavigateAction(self, goal_pose: Optional[Pose] = None):
        # Sends a `NavToPose` action request and waits for completion
        self.info_msg("Waiting for 'NavigateToPose' action server")
        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg(
                "'NavigateToPose' action server not available, waiting...")

        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        goal_msg = NavigateToPose.Goal()
        goal_msg.pose = self.getStampedPoseMsg(self.goal_pose)

        self.info_msg('Sending goal request...')
        send_goal_future = self.action_client.send_goal_async(goal_msg)

        rclpy.spin_until_future_complete(self, send_goal_future)
        goal_handle = send_goal_future.result()

        if not goal_handle.accepted:
            self.error_msg('Goal rejected')
            return False

        self.info_msg('Goal accepted')
        get_result_future = goal_handle.get_result_async()

        self.info_msg("Waiting for 'NavigateToPose' action to complete")
        rclpy.spin_until_future_complete(self, get_result_future)
        status = get_result_future.result().status
        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg('Goal failed with status code: {0}'.format(status))
            return False

        self.info_msg('Goal succeeded!')
        return True

    def poseCallback(self, msg):
        self.info_msg('Received amcl_pose')
        self.current_pose = msg.pose.pose
        self.initial_pose_received = True

    def reachesGoal(self, timeout, distance):
        goalReached = False
        start_time = time.time()

        while not goalReached:
            rclpy.spin_once(self, timeout_sec=1)
            if self.distanceFromGoal() < distance:
                goalReached = True
                self.info_msg('*** GOAL REACHED ***')
                return True
            elif timeout is not None:
                if (time.time() - start_time) > timeout:
                    self.error_msg('Robot timed out reaching its goal!')
                    return False

    def distanceFromGoal(self):
        d_x = self.current_pose.position.x - self.goal_pose.position.x
        d_y = self.current_pose.position.y - self.goal_pose.position.y
        distance = math.sqrt(d_x * d_x + d_y * d_y)
        self.info_msg('Distance from goal is: ' + str(distance))
        return distance

    def wait_for_node_active(self, node_name: str):
        # Waits for the node within the tester namespace to become active
        self.info_msg('Waiting for ' + node_name + ' to become active')
        node_service = node_name + '/get_state'
        state_client = self.create_client(GetState, node_service)
        while not state_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(node_service + ' service not available, waiting...')
        req = GetState.Request()  # empty request
        state = 'UNKNOWN'
        while (state != 'active'):
            self.info_msg('Getting ' + node_name + ' state...')
            future = state_client.call_async(req)
            rclpy.spin_until_future_complete(self, future)
            if future.result() is not None:
                state = future.result().current_state.label
                self.info_msg('Result of get_state: %s' % state)
            else:
                self.error_msg('Exception while calling service: %r' %
                               future.exception())
            time.sleep(5)

    def shutdown(self):
        self.info_msg('Shutting down')
        self.action_client.destroy()

        transition_service = 'lifecycle_manager_navigation/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down navigation lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down navigation lifecycle manager complete.')
        except Exception as e:
            self.error_msg('Service call failed %r' % (e, ))
        transition_service = 'lifecycle_manager_localization/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down localization lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down localization lifecycle manager complete')
        except Exception as e:
            self.error_msg('Service call failed %r' % (e, ))
예제 #17
0
class WaypointFollowerTest(Node):
    def __init__(self):
        super().__init__(node_name='nav2_waypoint_tester', namespace='')
        self.waypoints = None
        self.action_client = ActionClient(self, FollowWaypoints,
                                          'follow_waypoints')
        self.initial_pose_pub = self.create_publisher(
            PoseWithCovarianceStamped, 'initialpose', 10)
        self.initial_pose_received = False
        self.goal_handle = None

        pose_qos = QoSProfile(
            durability=QoSDurabilityPolicy.
            RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
            reliability=QoSReliabilityPolicy.
            RMW_QOS_POLICY_RELIABILITY_RELIABLE,
            history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
            depth=1)

        self.model_pose_sub = self.create_subscription(
            PoseWithCovarianceStamped, 'amcl_pose', self.poseCallback,
            pose_qos)

    def setInitialPose(self, pose):
        self.init_pose = PoseWithCovarianceStamped()
        self.init_pose.pose.pose.position.x = pose[0]
        self.init_pose.pose.pose.position.y = pose[1]
        self.init_pose.header.frame_id = 'map'
        self.publishInitialPose()
        time.sleep(5)

    def poseCallback(self, msg):
        self.info_msg('Received amcl_pose')
        self.initial_pose_received = True

    def setWaypoints(self, waypoints):
        self.waypoints = []
        for wp in waypoints:
            msg = PoseStamped()
            msg.header.frame_id = 'map'
            msg.pose.position.x = wp[0]
            msg.pose.position.y = wp[1]
            msg.pose.orientation.w = 1.0
            self.waypoints.append(msg)

    def run(self, block):
        if not self.waypoints:
            rclpy.error_msg('Did not set valid waypoints before running test!')
            return False

        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg(
                "'follow_waypoints' action server not available, waiting...")

        action_request = FollowWaypoints.Goal()
        action_request.poses = self.waypoints

        self.info_msg('Sending goal request...')
        send_goal_future = self.action_client.send_goal_async(action_request)
        try:
            rclpy.spin_until_future_complete(self, send_goal_future)
            self.goal_handle = send_goal_future.result()
        except Exception as e:  # noqa: B902
            self.error_msg('Service call failed %r' % (e, ))

        if not self.goal_handle.accepted:
            self.error_msg('Goal rejected')
            return False

        self.info_msg('Goal accepted')
        if not block:
            return True

        get_result_future = self.goal_handle.get_result_async()

        self.info_msg("Waiting for 'follow_waypoints' action to complete")
        try:
            rclpy.spin_until_future_complete(self, get_result_future)
            status = get_result_future.result().status
            result = get_result_future.result().result
        except Exception as e:  # noqa: B902
            self.error_msg('Service call failed %r' % (e, ))

        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg('Goal failed with status code: {0}'.format(status))
            return False
        if len(result.missed_waypoints) > 0:
            self.info_msg('Goal failed to process all waypoints,'
                          ' missed {0} wps.'.format(
                              len(result.missed_waypoints)))
            return False

        self.info_msg('Goal succeeded!')
        return True

    def publishInitialPose(self):
        self.initial_pose_pub.publish(self.init_pose)

    def shutdown(self):
        self.info_msg('Shutting down')

        self.action_client.destroy()
        self.info_msg('Destroyed follow_waypoints action client')

        transition_service = 'lifecycle_manager_navigation/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            rclpy.spin_until_future_complete(self, future)
            future.result()
        except Exception as e:  # noqa: B902
            self.error_msg('%s service call failed %r' % (
                transition_service,
                e,
            ))

        self.info_msg('{} finished'.format(transition_service))

        transition_service = 'lifecycle_manager_localization/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            rclpy.spin_until_future_complete(self, future)
            future.result()
        except Exception as e:  # noqa: B902
            self.error_msg('%s service call failed %r' % (
                transition_service,
                e,
            ))

        self.info_msg('{} finished'.format(transition_service))

    def cancel_goal(self):
        cancel_future = self.goal_handle.cancel_goal_async()
        rclpy.spin_until_future_complete(self, cancel_future)

    def info_msg(self, msg: str):
        self.get_logger().info(msg)

    def warn_msg(self, msg: str):
        self.get_logger().warn(msg)

    def error_msg(self, msg: str):
        self.get_logger().error(msg)
예제 #18
0
class NavTester(Node):
    def __init__(self,
                 initial_pose: Pose,
                 goal_pose: Pose,
                 namespace: str = ''):
        super().__init__(node_name='nav2_tester', namespace=namespace)
        self.initial_pose_pub = self.create_publisher(
            PoseWithCovarianceStamped, 'initialpose', 10)
        self.goal_pub = self.create_publisher(PoseStamped, 'goal_pose', 10)

        pose_qos = QoSProfile(
            durability=QoSDurabilityPolicy.
            RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
            reliability=QoSReliabilityPolicy.
            RMW_QOS_POLICY_RELIABILITY_RELIABLE,
            history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
            depth=1)

        self.model_pose_sub = self.create_subscription(
            PoseWithCovarianceStamped, 'amcl_pose', self.poseCallback,
            pose_qos)
        self.initial_pose_received = False
        self.initial_pose = initial_pose
        self.goal_pose = goal_pose
        self.action_client = ActionClient(self, NavigateToPose,
                                          'navigate_to_pose')

    def info_msg(self, msg: str):
        self.get_logger().info('\033[1;37;44m' + msg + '\033[0m')

    def warn_msg(self, msg: str):
        self.get_logger().warn('\033[1;37;43m' + msg + '\033[0m')

    def error_msg(self, msg: str):
        self.get_logger().error('\033[1;37;41m' + msg + '\033[0m')

    def setInitialPose(self):
        msg = PoseWithCovarianceStamped()
        msg.pose.pose = self.initial_pose
        msg.header.frame_id = 'map'
        self.info_msg('Publishing Initial Pose')
        self.initial_pose_pub.publish(msg)
        self.currentPose = self.initial_pose

    def getStampedPoseMsg(self, pose: Pose):
        msg = PoseStamped()
        msg.header.frame_id = 'map'
        msg.pose = pose
        return msg

    def publishGoalPose(self, goal_pose: Optional[Pose] = None):
        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        self.goal_pub.publish(self.getStampedPoseMsg(self.goal_pose))

    def runNavigateAction(self, goal_pose: Optional[Pose] = None):
        # Sends a `NavToPose` action request and waits for completion
        self.info_msg("Waiting for 'NavigateToPose' action server")
        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg(
                "'NavigateToPose' action server not available, waiting...")

        if (os.getenv('GROOT_MONITORING') == 'True'):
            if self.grootMonitoringGetStatus():
                self.error_msg('Behavior Tree must not be running already!')
                self.error_msg('Are you running multiple goals/bts..?')
                return False
            self.info_msg('This Error above MUST Fail and is o.k.!')

        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        goal_msg = NavigateToPose.Goal()
        goal_msg.pose = self.getStampedPoseMsg(self.goal_pose)

        self.info_msg('Sending goal request...')
        send_goal_future = self.action_client.send_goal_async(goal_msg)

        rclpy.spin_until_future_complete(self, send_goal_future)
        goal_handle = send_goal_future.result()

        if not goal_handle.accepted:
            self.error_msg('Goal rejected')
            return False

        self.info_msg('Goal accepted')
        get_result_future = goal_handle.get_result_async()

        future_return = True
        if (os.getenv('GROOT_MONITORING') == 'True'):
            try:
                if not self.grootMonitoringReloadTree():
                    self.error_msg(
                        'Failed GROOT_BT - Reload Tree from ZMQ Server')
                    future_return = False
                if not self.grootMonitoringGetStatus():
                    self.error_msg(
                        'Failed GROOT_BT - Get Status from ZMQ Publisher')
                    future_return = False
            except Exception as e:  # noqa: B902
                self.error_msg('Failed GROOT_BT - ZMQ Tests: ' + e.__doc__ +
                               e.message)
                future_return = False

        self.info_msg("Waiting for 'NavigateToPose' action to complete")
        rclpy.spin_until_future_complete(self, get_result_future)
        status = get_result_future.result().status
        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg('Goal failed with status code: {0}'.format(status))
            return False

        if not future_return:
            return False

        self.info_msg('Goal succeeded!')
        return True

    def grootMonitoringReloadTree(self):
        # ZeroMQ Context
        context = zmq.Context()

        sock = context.socket(zmq.REQ)
        port = 1667  # default server port for groot monitoring
        # # Set a Timeout so we do not spin till infinity
        sock.setsockopt(zmq.RCVTIMEO, 1000)
        # sock.setsockopt(zmq.LINGER, 0)

        sock.connect('tcp://localhost:' + str(port))
        self.info_msg('ZMQ Server Port: ' + str(port))

        # this should fail
        try:
            sock.recv()
            self.error_msg(
                'ZMQ Reload Tree Test 1/3 - This should have failed!')
            # Only works when ZMQ server receives a request first
            sock.close()
            return False
        except zmq.error.ZMQError:
            self.info_msg('ZMQ Reload Tree Test 1/3: Check')
        try:
            # request tree from server
            sock.send_string('')
            # receive tree from server as flat_buffer
            sock.recv()
            self.info_msg('ZMQ Reload Tree Test 2/3: Check')
        except zmq.error.Again:
            self.info_msg('ZMQ Reload Tree Test 2/3 - Failed to load tree')
            sock.close()
            return False

        # this should fail
        try:
            sock.recv()
            self.error_msg(
                'ZMQ Reload Tree Test 3/3 - This should have failed!')
            # Tree should only be loadable ONCE after ZMQ server received a request
            sock.close()
            return False
        except zmq.error.ZMQError:
            self.info_msg('ZMQ Reload Tree Test 3/3: Check')

        return True

    def grootMonitoringGetStatus(self):
        # ZeroMQ Context
        context = zmq.Context()
        # Define the socket using the 'Context'
        sock = context.socket(zmq.SUB)
        # Set a Timeout so we do not spin till infinity
        sock.setsockopt(zmq.RCVTIMEO, 2000)
        # sock.setsockopt(zmq.LINGER, 0)

        # Define subscription and messages with prefix to accept.
        sock.setsockopt_string(zmq.SUBSCRIBE, '')
        port = 1666  # default publishing port for groot monitoring
        sock.connect('tcp://127.0.0.1:' + str(port))

        for request in range(3):
            try:
                sock.recv()
            except zmq.error.Again:
                self.error_msg('ZMQ - Did not receive any status')
                sock.close()
                return False
        self.info_msg('ZMQ - Did receive status')
        return True

    def poseCallback(self, msg):
        self.info_msg('Received amcl_pose')
        self.current_pose = msg.pose.pose
        self.initial_pose_received = True

    def reachesGoal(self, timeout, distance):
        goalReached = False
        start_time = time.time()

        while not goalReached:
            rclpy.spin_once(self, timeout_sec=1)
            if self.distanceFromGoal() < distance:
                goalReached = True
                self.info_msg('*** GOAL REACHED ***')
                return True
            elif timeout is not None:
                if (time.time() - start_time) > timeout:
                    self.error_msg('Robot timed out reaching its goal!')
                    return False

    def distanceFromGoal(self):
        d_x = self.current_pose.position.x - self.goal_pose.position.x
        d_y = self.current_pose.position.y - self.goal_pose.position.y
        distance = math.sqrt(d_x * d_x + d_y * d_y)
        self.info_msg('Distance from goal is: ' + str(distance))
        return distance

    def wait_for_node_active(self, node_name: str):
        # Waits for the node within the tester namespace to become active
        self.info_msg('Waiting for ' + node_name + ' to become active')
        node_service = node_name + '/get_state'
        state_client = self.create_client(GetState, node_service)
        while not state_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(node_service + ' service not available, waiting...')
        req = GetState.Request()  # empty request
        state = 'UNKNOWN'
        while (state != 'active'):
            self.info_msg('Getting ' + node_name + ' state...')
            future = state_client.call_async(req)
            rclpy.spin_until_future_complete(self, future)
            if future.result() is not None:
                state = future.result().current_state.label
                self.info_msg('Result of get_state: %s' % state)
            else:
                self.error_msg('Exception while calling service: %r' %
                               future.exception())
            time.sleep(5)

    def shutdown(self):
        self.info_msg('Shutting down')
        self.action_client.destroy()

        transition_service = 'lifecycle_manager_navigation/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down navigation lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down navigation lifecycle manager complete.')
        except Exception as e:  # noqa: B902
            self.error_msg('Service call failed %r' % (e, ))
        transition_service = 'lifecycle_manager_localization/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down localization lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down localization lifecycle manager complete')
        except Exception as e:  # noqa: B902
            self.error_msg('Service call failed %r' % (e, ))

    def wait_for_initial_pose(self):
        self.initial_pose_received = False
        while not self.initial_pose_received:
            self.info_msg('Setting initial pose')
            self.setInitialPose()
            self.info_msg('Waiting for amcl_pose to be received')
            rclpy.spin_once(self, timeout_sec=1)
예제 #19
0
class NavTester(Node):
    def __init__(self,
                 initial_pose: Pose,
                 goal_pose: Pose,
                 namespace: str = ""):
        super().__init__(node_name="nav2_tester", namespace=namespace)
        self.initial_pose_pub = self.create_publisher(
            PoseWithCovarianceStamped, "initialpose", 10)
        self.goal_pub = self.create_publisher(PoseStamped, "goal_pose", 10)

        pose_qos = QoSProfile(
            durability=QoSDurabilityPolicy.TRANSIENT_LOCAL,
            reliability=QoSReliabilityPolicy.RELIABLE,
            history=QoSHistoryPolicy.KEEP_LAST,
            depth=1,
        )

        self.model_pose_sub = self.create_subscription(
            PoseWithCovarianceStamped, "amcl_pose", self.poseCallback,
            pose_qos)
        self.initial_pose_received = False
        self.initial_pose = initial_pose
        self.goal_pose = goal_pose
        self.action_client = ActionClient(self, NavigateToPose,
                                          "navigate_to_pose")

    def info_msg(self, msg: str):
        self.get_logger().info("\033[1;37;44m" + msg + "\033[0m")

    def warn_msg(self, msg: str):
        self.get_logger().warn("\033[1;37;43m" + msg + "\033[0m")

    def error_msg(self, msg: str):
        self.get_logger().error("\033[1;37;41m" + msg + "\033[0m")

    def setInitialPose(self):
        msg = PoseWithCovarianceStamped()
        msg.pose.pose = self.initial_pose
        msg.header.frame_id = "map"
        self.info_msg("Publishing Initial Pose")
        self.initial_pose_pub.publish(msg)
        self.currentPose = self.initial_pose

    def getStampedPoseMsg(self, pose: Pose):
        msg = PoseStamped()
        msg.header.frame_id = "map"
        msg.pose = pose
        return msg

    def publishGoalPose(self, goal_pose: Optional[Pose] = None):
        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        self.goal_pub.publish(self.getStampedPoseMsg(self.goal_pose))

    def runNavigateAction(self, goal_pose: Optional[Pose] = None):
        # Sends a `NavToPose` action request and waits for completion
        self.info_msg("Waiting for 'NavigateToPose' action server")
        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg(
                "'NavigateToPose' action server not available, waiting...")

        if os.getenv("GROOT_MONITORING") == "True":
            if self.grootMonitoringGetStatus():
                self.error_msg("Behavior Tree must not be running already!")
                self.error_msg("Are you running multiple goals/bts..?")
                return False
            self.info_msg("This Error above MUST Fail and is o.k.!")

        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        goal_msg = NavigateToPose.Goal()
        goal_msg.pose = self.getStampedPoseMsg(self.goal_pose)

        self.info_msg("Sending goal request...")
        send_goal_future = self.action_client.send_goal_async(goal_msg)

        rclpy.spin_until_future_complete(self, send_goal_future)
        goal_handle = send_goal_future.result()

        if not goal_handle.accepted:
            self.error_msg("Goal rejected")
            return False

        self.info_msg("Goal accepted")
        get_result_future = goal_handle.get_result_async()

        future_return = True
        if os.getenv("GROOT_MONITORING") == "True":
            try:
                if not self.grootMonitoringReloadTree():
                    self.error_msg(
                        "Failed GROOT_BT - Reload Tree from ZMQ Server")
                    future_return = False
                if not self.grootMonitoringGetStatus():
                    self.error_msg(
                        "Failed GROOT_BT - Get Status from ZMQ Publisher")
                    future_return = False
            except Exception as e:  # noqa: B902
                self.error_msg(f"Failed GROOT_BT - ZMQ Tests: {e}")
                future_return = False

        self.info_msg("Waiting for 'NavigateToPose' action to complete")
        rclpy.spin_until_future_complete(self, get_result_future)
        status = get_result_future.result().status
        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg(f"Goal failed with status code: {status}")
            return False

        if not future_return:
            return False

        self.info_msg("Goal succeeded!")
        return True

    def grootMonitoringReloadTree(self):
        # ZeroMQ Context
        context = zmq.Context()

        sock = context.socket(zmq.REQ)
        port = 1667  # default server port for groot monitoring
        # # Set a Timeout so we do not spin till infinity
        sock.setsockopt(zmq.RCVTIMEO, 1000)
        # sock.setsockopt(zmq.LINGER, 0)

        sock.connect(f"tcp://localhost:{port}")
        self.info_msg(f"ZMQ Server Port:{port}")

        # this should fail
        try:
            sock.recv()
            self.error_msg(
                "ZMQ Reload Tree Test 1/3 - This should have failed!")
            # Only works when ZMQ server receives a request first
            sock.close()
            return False
        except zmq.error.ZMQError:
            self.info_msg("ZMQ Reload Tree Test 1/3: Check")
        try:
            # request tree from server
            sock.send_string("")
            # receive tree from server as flat_buffer
            sock.recv()
            self.info_msg("ZMQ Reload Tree Test 2/3: Check")
        except zmq.error.Again:
            self.info_msg("ZMQ Reload Tree Test 2/3 - Failed to load tree")
            sock.close()
            return False

        # this should fail
        try:
            sock.recv()
            self.error_msg(
                "ZMQ Reload Tree Test 3/3 - This should have failed!")
            # Tree should only be loadable ONCE after ZMQ server received a request
            sock.close()
            return False
        except zmq.error.ZMQError:
            self.info_msg("ZMQ Reload Tree Test 3/3: Check")

        return True

    def grootMonitoringGetStatus(self):
        # ZeroMQ Context
        context = zmq.Context()
        # Define the socket using the 'Context'
        sock = context.socket(zmq.SUB)
        # Set a Timeout so we do not spin till infinity
        sock.setsockopt(zmq.RCVTIMEO, 2000)
        # sock.setsockopt(zmq.LINGER, 0)

        # Define subscription and messages with prefix to accept.
        sock.setsockopt_string(zmq.SUBSCRIBE, "")
        port = 1666  # default publishing port for groot monitoring
        sock.connect(f"tcp://127.0.0.1:{port}")

        for request in range(3):
            try:
                sock.recv()
            except zmq.error.Again:
                self.error_msg("ZMQ - Did not receive any status")
                sock.close()
                return False
        self.info_msg("ZMQ - Did receive status")
        return True

    def poseCallback(self, msg):
        self.info_msg("Received amcl_pose")
        self.current_pose = msg.pose.pose
        self.initial_pose_received = True

    def reachesGoal(self, timeout, distance):
        goalReached = False
        start_time = time.time()

        while not goalReached:
            rclpy.spin_once(self, timeout_sec=1)
            if self.distanceFromGoal() < distance:
                goalReached = True
                self.info_msg("*** GOAL REACHED ***")
                return True
            elif timeout is not None:
                if (time.time() - start_time) > timeout:
                    self.error_msg("Robot timed out reaching its goal!")
                    return False

    def distanceFromGoal(self):
        d_x = self.current_pose.position.x - self.goal_pose.position.x
        d_y = self.current_pose.position.y - self.goal_pose.position.y
        distance = math.sqrt(d_x * d_x + d_y * d_y)
        self.info_msg(f"Distance from goal is: {distance}")
        return distance

    def wait_for_node_active(self, node_name: str):
        # Waits for the node within the tester namespace to become active
        self.info_msg(f"Waiting for {node_name} to become active")
        node_service = f"{node_name}/get_state"
        state_client = self.create_client(GetState, node_service)
        while not state_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(f"{node_service} service not available, waiting...")
        req = GetState.Request()  # empty request
        state = "UNKNOWN"
        while state != "active":
            self.info_msg(f"Getting {node_name} state...")
            future = state_client.call_async(req)
            rclpy.spin_until_future_complete(self, future)
            if future.result() is not None:
                state = future.result().current_state.label
                self.info_msg(f"Result of get_state: {state}")
            else:
                self.error_msg(
                    f"Exception while calling service: {future.exception()!r}")
            time.sleep(5)

    def shutdown(self):
        self.info_msg("Shutting down")
        self.action_client.destroy()

        transition_service = "lifecycle_manager_navigation/manage_nodes"
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(
                f"{transition_service} service not available, waiting...")

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg("Shutting down navigation lifecycle manager...")
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                "Shutting down navigation lifecycle manager complete.")
        except Exception as e:  # noqa: B902
            self.error_msg(f"Service call failed {e!r}")
        transition_service = "lifecycle_manager_localization/manage_nodes"
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(
                f"{transition_service} service not available, waiting...")

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg("Shutting down localization lifecycle manager...")
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                "Shutting down localization lifecycle manager complete")
        except Exception as e:  # noqa: B902
            self.error_msg(f"Service call failed {e!r}")

    def wait_for_initial_pose(self):
        self.initial_pose_received = False
        while not self.initial_pose_received:
            self.info_msg("Setting initial pose")
            self.setInitialPose()
            self.info_msg("Waiting for amcl_pose to be received")
            rclpy.spin_once(self, timeout_sec=1)
예제 #20
0
class MissionExperimentRunNode(Node):
    """
    Run a sequence of mission experiments.

    The control flow is buried in the callbacks. Each experiment does 3 things:
    1. sets parameters on auv_node (set_auv_node_params)
    2. sets parameters on filter_node (set_filter_node_params)
    3. starts a mission (send_mission_goal)
    4. repeat (start_next_run_or_experiment_or_done)
    """

    def __init__(self, experiments, repeat: bool = False):
        super().__init__('experiment_runner')

        assert len(experiments) > 0
        self._experiments = experiments

        # Subscribe to several messages for later processing
        self._co_sub = self.create_subscription(Control, '/control', self.co_cb, 10)
        self._fp_sub = self.create_subscription(FiducialPoseStamped, '/filtered_fp',
                                                self.fp_cb, 10)
        self._gt_sub = self.create_subscription(Odometry, '/ground_truth', self.gt_cb, 10)

        # Set parameter service clients
        self._set_param_auv_node_client = self.create_client(SetParameters,
                                                             '/auv_node/set_parameters')
        self._set_param_filter_node_client = self.create_client(SetParameters,
                                                                '/filter_node/set_parameters')

        # Mission action client
        self._mission_action_client = ActionClient(self, Mission, '/mission')

        # Futures manage the async state changes
        self._set_auv_node_params_future = None
        self._set_filter_node_params_future = None
        self._send_goal_future = None
        self._get_result_future = None

        # Active mission
        self._goal_handle = None

        # Repeat sequence of experiments forever, or until msg_processor returns True
        self._repeat = repeat

        # Keep track of the current experiment
        self._idx = 0
        self._count = 0

        # Start 1st experiment
        self.start_first_experiment()

    def start_first_experiment(self):
        self._idx = 0
        self.start_experiment()

    def start_experiment(self):
        """Start next experiment."""
        self.get_logger().info('starting experiment {}'.format(self._idx))
        self._experiments[self._idx].log_info(self.get_logger())
        self._count = 0
        self.start_run()

    def start_run(self):
        """Start next run."""
        self.get_logger().info(
            'starting run {} of {}'.format(self._count + 1, self._experiments[self._idx].count))

        # Step 1
        self.set_auv_node_params()

    def start_next_run_or_experiment_or_done(self):
        """Start next run, or next experiment, or we're done."""
        self._count += 1
        if self._count < self._experiments[self._idx].count:
            self.start_run()
        else:
            self._idx += 1
            self._count = 0
            if self._idx < len(self._experiments):
                self.start_experiment()
            elif self._repeat:
                self.get_logger().info('REPEAT')
                self.start_first_experiment()
            else:
                self.get_logger().info('DONE!')

    def co_cb(self, msg: Control):
        if 0 <= self._idx < len(self._experiments) and self._experiments[
                self._idx].co_msgs is not None:
            self._experiments[self._idx].co_msgs.append(msg)

    def fp_cb(self, msg: FiducialPoseStamped):
        if 0 <= self._idx < len(self._experiments) and self._experiments[
                self._idx].fp_msgs is not None:
            self._experiments[self._idx].fp_msgs.append(msg)

    def gt_cb(self, msg: Odometry):
        if 0 <= self._idx < len(self._experiments) and self._experiments[
                self._idx].gt_msgs is not None:
            self._experiments[self._idx].gt_msgs.append(msg)

    def set_auv_node_params(self):
        self.get_logger().debug('waiting for /auv_node/set_parameters server...')
        self._set_param_auv_node_client.wait_for_service()

        request = SetParameters.Request()
        for param in self._experiments[self._idx].auv_params:
            request.parameters.append(param)

        self.get_logger().debug('setting auv_node params...')
        self._set_auv_node_params_future = self._set_param_auv_node_client.call_async(request)
        self._set_auv_node_params_future.add_done_callback(self.set_auv_node_params_done_cb)

    def set_auv_node_params_done_cb(self, _):
        self.get_logger().debug('auv_node params set')

        # Step 2
        self.set_filter_node_params()

    def set_filter_node_params(self):
        if len(self._experiments[self._idx].filter_params) > 0:

            self.get_logger().debug('waiting for /filter_node/set_parameters server...')
            self._set_param_filter_node_client.wait_for_service()

            request = SetParameters.Request()
            for param in self._experiments[self._idx].filter_params:
                request.parameters.append(param)

            self.get_logger().debug('setting filter_node params...')
            self._set_filter_node_params_future = self._set_param_filter_node_client.call_async(
                request)
            self._set_filter_node_params_future.add_done_callback(
                self.set_filter_node_params_done_cb)

        else:

            # Step 3
            self.send_mission_goal()

    def set_filter_node_params_done_cb(self, _):
        self.get_logger().debug('filter_node params set')

        # Step 3
        self.send_mission_goal()

    def send_mission_goal(self):
        self.get_logger().debug('waiting for /mission server...')
        self._mission_action_client.wait_for_server()

        self.get_logger().debug('sending goal request...')
        self._send_goal_future = self._mission_action_client.send_goal_async(
            self._experiments[self._idx].get_goal_msg(),
            feedback_callback=self.feedback_cb)
        self._send_goal_future.add_done_callback(self.goal_response_cb)

    def goal_response_cb(self, future):
        goal_handle: ClientGoalHandle = future.result()
        if goal_handle.accepted:
            self.get_logger().info('goal accepted')

            # Save client goal handle, useful if we need to abort the mission
            self._goal_handle = goal_handle

            # Get notified when the mission is complete
            self._get_result_future = goal_handle.get_result_async()
            self._get_result_future.add_done_callback(self.get_result_cb)
        else:
            self.get_logger().warn('goal rejected, STOPPING')

    def feedback_cb(self, feedback):
        self.get_logger().debug(
            'feedback: {0} out of {1}'.format(feedback.feedback.targets_completed,
                                              feedback.feedback.targets_total))

    def get_result_cb(self, future):
        status = future.result().status
        if status == GoalStatus.STATUS_SUCCEEDED:
            result = future.result().result
            self.get_logger().info(
                'goal succeeded, completed {0} out of {1}'.format(result.targets_completed,
                                                                  result.targets_total))

            # Do any post-processing
            if self._experiments[self._idx].process_messages(self.get_logger()):
                self.get_logger().info('messages processor returned True, STOPPING')
            else:
                # Clear goal_handle
                self._goal_handle = None

                # Step 4
                self.start_next_run_or_experiment_or_done()
        else:
            self.get_logger().warn('goal failed with status {0}, STOPPING'.format(status))

    def stop_mission_and_destroy_client(self):
        if self._goal_handle is not None:
            print('stop mission')
            self._goal_handle.cancel_goal_async()
            self._goal_handle = None

        if self._mission_action_client is not None:
            print('destroy action client')
            # Avoids a crash in rclpy/action/client.py
            self._mission_action_client.destroy()
            self._mission_action_client = None
예제 #21
0
class TestPipelineRun(unittest.TestCase):


    @classmethod
    def setUpClass(cls):
        rclpy.init()


    @classmethod
    def tearDownClass(cls):
        rclpy.shutdown()


    def setUp(self):
        self.node = rclpy.create_node('test_node')

        self.configure_client = self.node.create_client(
            ConfigurePipeline,
            '/triton/configure_pipeline'
        )
        while not self.configure_client.wait_for_service(timeout_sec=1.0):
            self.node.get_logger().warn('Configure pipeline service not available, waiting again...')

        self.run_client = ActionClient(
            self.node,
            RunPipeline,
            '/triton/run_pipeline'
        )
        while not self.run_client.wait_for_server(timeout_sec=1.0):
            self.node.get_logger().warn('Run pipeline service not available, waiting again...')


    def tearDown(self):
        self.configure_client.destroy()
        self.run_client.destroy()
        self.node.destroy_node()


    def test_run_comp_not_valid(self, pipeline_manager, proc_info, proc_output):
        req = ConfigurePipeline.Request()
        pipeline_type = PipelineType()
        test_type = 'example'
        pipeline_type.type = test_type
        req.pipeline_type = pipeline_type
        test_file_name = 'test_run_comp_not_valid'
        req.config_file_name = test_file_name
        future = self.configure_client.call_async(req)

        while rclpy.ok():
            rclpy.spin_once(self.node)
            if future.done():
                break

        goal_msg = RunPipeline.Goal()
        goal_msg.input = 0
        future = self.run_client.send_goal_async(goal_msg)

        future_count = 0
        result = None
        while rclpy.ok():
            rclpy.spin_once(self.node)
            if future.done():
                future_count += 1
                if future_count == 1:
                    future = future.result().get_result_async()
                if future_count == 2:
                    result = future.result().result
                    break
        if result.output == 1:
            component = 'example::DoesNotExist'
            proc_output.assertWaitFor(
                expected_output='Failed to find class with the requested plugin name \'{}\' in the loaded library'.format(component))
            proc_output.assertWaitFor(
                expected_output='The component {} was not loaded successfully'.format(component))
            proc_output.assertWaitFor(
                expected_output='Aborting the pipeline')
        else:
            self.fail('Should abort from component not able to load')
예제 #22
0
 def test_constructor_defaults(self):
     # Defaults
     ac = ActionClient(self.node, Fibonacci, 'fibonacci')
     ac.destroy()
class WaypointFollowerTest(Node):

    def __init__(self):
        super().__init__(node_name='nav2_waypoint_tester', namespace='')
        self.waypoints = None
        self.readyToMove = True
        self.currentPose = None
        self.lastWaypoint = None
        self.action_client = ActionClient(self, FollowWaypoints, 'FollowWaypoints')
        self.initial_pose_pub = self.create_publisher(PoseWithCovarianceStamped,
                                                      'initialpose', 10)

        self.costmapClient = self.create_client(GetCostmap, '/global_costmap/get_costmap')
        while not self.costmapClient.wait_for_service(timeout_sec=1.0):
            self.info_msg('service not available, waiting again...')
        self.initial_pose_received = False
        self.goal_handle = None

        pose_qos = QoSProfile(
          durability=QoSDurabilityPolicy.RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
          reliability=QoSReliabilityPolicy.RMW_QOS_POLICY_RELIABILITY_RELIABLE,
          history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
          depth=1)

        self.model_pose_sub = self.create_subscription(Odometry,
                                                       '/odom', self.poseCallback, pose_qos)

        # self.costmapSub = self.create_subscription(Costmap(), '/global_costmap/costmap_raw', self.costmapCallback, pose_qos)
        self.costmapSub = self.create_subscription(OccupancyGrid(), '/map', self.occupancyGridCallback, pose_qos)
        self.costmap = None

        self.get_logger().info('Running Waypoint Test')

    def occupancyGridCallback(self, msg):
        self.costmap = OccupancyGrid2d(msg)

    def moveToFrontiers(self):
        frontiers = getFrontier(self.currentPose, self.costmap, self.get_logger())

        if len(frontiers) == 0:
            self.info_msg('No More Frontiers')
            return

        location = None
        largestDist = 0
        for f in frontiers:
            dist = math.sqrt(((f[0] - self.currentPose.position.x)**2) + ((f[1] - self.currentPose.position.y)**2))
            if  dist > largestDist:
                largestDist = dist
                location = [f] 

        #worldFrontiers = [self.costmap.mapToWorld(f[0], f[1]) for f in frontiers]
        self.info_msg(f'World points {location}')
        self.setWaypoints(location)

        action_request = FollowWaypoints.Goal()
        action_request.poses = self.waypoints

        self.info_msg('Sending goal request...')
        send_goal_future = self.action_client.send_goal_async(action_request)
        try:
            rclpy.spin_until_future_complete(self, send_goal_future)
            self.goal_handle = send_goal_future.result()
        except Exception as e:
            self.error_msg('Service call failed %r' % (e,))

        if not self.goal_handle.accepted:
            self.error_msg('Goal rejected')
            return

        self.info_msg('Goal accepted')

        get_result_future = self.goal_handle.get_result_async()

        self.info_msg("Waiting for 'FollowWaypoints' action to complete")
        try:
            rclpy.spin_until_future_complete(self, get_result_future)
            status = get_result_future.result().status
            result = get_result_future.result().result
        except Exception as e:
            self.error_msg('Service call failed %r' % (e,))

        #self.currentPose = self.waypoints[len(self.waypoints) - 1].pose

        self.moveToFrontiers()

    def costmapCallback(self, msg):
        self.costmap = Costmap2d(msg)

        unknowns = 0
        for x in range(0, self.costmap.getSizeX()):
            for y in range(0, self.costmap.getSizeY()):
                if self.costmap.getCost(x, y) == 255:
                    unknowns = unknowns + 1
        self.get_logger().info(f'Unknowns {unknowns}')
        self.get_logger().info(f'Got Costmap {len(getFrontier(None, self.costmap, self.get_logger()))}')

    def dumpCostmap(self):
        costmapReq = GetCostmap.Request()
        self.get_logger().info('Requesting Costmap')
        costmap = self.costmapClient.call(costmapReq)
        self.get_logger().info(f'costmap resolution {costmap.specs.resolution}')

    def setInitialPose(self, pose):
        self.init_pose = PoseWithCovarianceStamped()
        self.init_pose.pose.pose.position.x = pose[0]
        self.init_pose.pose.pose.position.y = pose[1]
        self.init_pose.header.frame_id = 'map'
        self.currentPose = self.init_pose.pose.pose
        self.publishInitialPose()
        time.sleep(5)

    def poseCallback(self, msg):
        self.info_msg('Received amcl_pose')
        self.currentPose = msg.pose.pose
        self.initial_pose_received = True
        

    def setWaypoints(self, waypoints):
        self.waypoints = []
        for wp in waypoints:
            msg = PoseStamped()
            msg.header.frame_id = 'map'
            msg.pose.position.x = wp[0]
            msg.pose.position.y = wp[1]
            msg.pose.orientation.w = 1.0
            self.waypoints.append(msg)

    def run(self, block):
        if not self.waypoints:
            rclpy.error_msg('Did not set valid waypoints before running test!')
            return False

        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg("'FollowWaypoints' action server not available, waiting...")

        action_request = FollowWaypoints.Goal()
        action_request.poses = self.waypoints

        self.info_msg('Sending goal request...')
        send_goal_future = self.action_client.send_goal_async(action_request)
        try:
            rclpy.spin_until_future_complete(self, send_goal_future)
            self.goal_handle = send_goal_future.result()
        except Exception as e:
            self.error_msg('Service call failed %r' % (e,))

        if not self.goal_handle.accepted:
            self.error_msg('Goal rejected')
            return False

        self.info_msg('Goal accepted')
        if not block:
            return True

        get_result_future = self.goal_handle.get_result_async()

        self.info_msg("Waiting for 'FollowWaypoints' action to complete")
        try:
            rclpy.spin_until_future_complete(self, get_result_future)
            status = get_result_future.result().status
            result = get_result_future.result().result
        except Exception as e:
            self.error_msg('Service call failed %r' % (e,))

        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg('Goal failed with status code: {0}'.format(status))
            return False
        if len(result.missed_waypoints) > 0:
            self.info_msg('Goal failed to process all waypoints,'
                          ' missed {0} wps.'.format(len(result.missed_waypoints)))
            return False

        self.info_msg('Goal succeeded!')
        return True

    def publishInitialPose(self):
        self.initial_pose_pub.publish(self.init_pose)

    def shutdown(self):
        self.info_msg('Shutting down')

        self.action_client.destroy()
        self.info_msg('Destroyed FollowWaypoints action client')

        transition_service = 'lifecycle_manager_navigation/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes, transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service + ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            rclpy.spin_until_future_complete(self, future)
            future.result()
        except Exception as e:
            self.error_msg('%s service call failed %r' % (transition_service, e,))

        self.info_msg('{} finished'.format(transition_service))

        transition_service = 'lifecycle_manager_localization/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes, transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service + ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            rclpy.spin_until_future_complete(self, future)
            future.result()
        except Exception as e:
            self.error_msg('%s service call failed %r' % (transition_service, e,))

        self.info_msg('{} finished'.format(transition_service))

    def cancel_goal(self):
        cancel_future = self.goal_handle.cancel_goal_async()
        rclpy.spin_until_future_complete(self, cancel_future)

    def info_msg(self, msg: str):
        self.get_logger().info(msg)

    def warn_msg(self, msg: str):
        self.get_logger().warn(msg)

    def error_msg(self, msg: str):
        self.get_logger().error(msg)
예제 #24
0
class NavTester(Node):
    def __init__(self,
                 initial_pose: Pose,
                 goal_pose: Pose,
                 namespace: str = ""):
        super().__init__(node_name="nav2_tester", namespace=namespace)
        self.initial_pose_pub = self.create_publisher(
            PoseWithCovarianceStamped, "initialpose", 10)
        self.goal_pub = self.create_publisher(PoseStamped, "goal_pose", 10)

        pose_qos = QoSProfile(
            durability=QoSDurabilityPolicy.TRANSIENT_LOCAL,
            reliability=QoSReliabilityPolicy.RELIABLE,
            history=QoSHistoryPolicy.KEEP_LAST,
            depth=1,
        )

        self.model_pose_sub = self.create_subscription(
            PoseWithCovarianceStamped, "amcl_pose", self.poseCallback,
            pose_qos)
        self.initial_pose_received = False
        self.initial_pose = initial_pose
        self.goal_pose = goal_pose
        self.action_client = ActionClient(self, NavigateToPose,
                                          "navigate_to_pose")

    def info_msg(self, msg: str):
        self.get_logger().info("\033[1;37;44m" + msg + "\033[0m")

    def warn_msg(self, msg: str):
        self.get_logger().warn("\033[1;37;43m" + msg + "\033[0m")

    def error_msg(self, msg: str):
        self.get_logger().error("\033[1;37;41m" + msg + "\033[0m")

    def setInitialPose(self):
        msg = PoseWithCovarianceStamped()
        msg.pose.pose = self.initial_pose
        msg.header.frame_id = "map"
        self.info_msg("Publishing Initial Pose")
        self.initial_pose_pub.publish(msg)
        self.currentPose = self.initial_pose

    def getStampedPoseMsg(self, pose: Pose):
        msg = PoseStamped()
        msg.header.frame_id = "map"
        msg.pose = pose
        return msg

    def publishGoalPose(self, goal_pose: Optional[Pose] = None):
        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        self.goal_pub.publish(self.getStampedPoseMsg(self.goal_pose))

    def runNavigateAction(self, goal_pose: Optional[Pose] = None):
        # Sends a `NavToPose` action request and waits for completion
        self.info_msg("Waiting for 'NavigateToPose' action server")
        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg(
                "'NavigateToPose' action server not available, waiting...")

        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        goal_msg = NavigateToPose.Goal()
        goal_msg.pose = self.getStampedPoseMsg(self.goal_pose)

        self.info_msg("Sending goal request...")
        send_goal_future = self.action_client.send_goal_async(goal_msg)

        rclpy.spin_until_future_complete(self, send_goal_future)
        goal_handle = send_goal_future.result()

        if not goal_handle.accepted:
            self.error_msg("Goal rejected")
            return False

        self.info_msg("Goal accepted")
        get_result_future = goal_handle.get_result_async()

        future_return = True

        self.info_msg("Waiting for 'NavigateToPose' action to complete")
        rclpy.spin_until_future_complete(self, get_result_future)
        status = get_result_future.result().status
        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg(f"Goal failed with status code: {status}")
            return False

        if not future_return:
            return False

        self.info_msg("Goal succeeded!")
        return True

    def poseCallback(self, msg):
        self.info_msg("Received amcl_pose")
        self.current_pose = msg.pose.pose
        self.initial_pose_received = True

    def reachesGoal(self, timeout, distance):
        goalReached = False
        start_time = time.time()

        while not goalReached:
            rclpy.spin_once(self, timeout_sec=1)
            if self.distanceFromGoal() < distance:
                goalReached = True
                self.info_msg("*** GOAL REACHED ***")
                return True
            elif timeout is not None:
                if (time.time() - start_time) > timeout:
                    self.error_msg("Robot timed out reaching its goal!")
                    return False

    def distanceFromGoal(self):
        d_x = self.current_pose.position.x - self.goal_pose.position.x
        d_y = self.current_pose.position.y - self.goal_pose.position.y
        distance = math.sqrt(d_x * d_x + d_y * d_y)
        self.info_msg(f"Distance from goal is: {distance}")
        return distance

    def wait_for_node_active(self, node_name: str):
        # Waits for the node within the tester namespace to become active
        self.info_msg(f"Waiting for {node_name} to become active")
        node_service = f"{node_name}/get_state"
        state_client = self.create_client(GetState, node_service)
        while not state_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(f"{node_service} service not available, waiting...")
        req = GetState.Request()  # empty request
        state = "UNKNOWN"
        while state != "active":
            self.info_msg(f"Getting {node_name} state...")
            future = state_client.call_async(req)
            rclpy.spin_until_future_complete(self, future)
            if future.result() is not None:
                state = future.result().current_state.label
                self.info_msg(f"Result of get_state: {state}")
            else:
                self.error_msg(
                    f"Exception while calling service: {future.exception()!r}")
            time.sleep(5)

    def shutdown(self):
        self.info_msg("Shutting down")
        self.action_client.destroy()

        transition_service = "lifecycle_manager_navigation/manage_nodes"
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(
                f"{transition_service} service not available, waiting...")

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg("Shutting down navigation lifecycle manager...")
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                "Shutting down navigation lifecycle manager complete.")
        except Exception as e:  # noqa: B902
            self.error_msg(f"Service call failed {e!r}")
        transition_service = "lifecycle_manager_localization/manage_nodes"
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(
                f"{transition_service} service not available, waiting...")

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg("Shutting down localization lifecycle manager...")
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                "Shutting down localization lifecycle manager complete")
        except Exception as e:  # noqa: B902
            self.error_msg(f"Service call failed {e!r}")

    def wait_for_initial_pose(self):
        self.initial_pose_received = False
        while not self.initial_pose_received:
            self.info_msg("Setting initial pose")
            self.setInitialPose()
            self.info_msg("Waiting for amcl_pose to be received")
            rclpy.spin_once(self, timeout_sec=1)
class ActionClientWrapperThread(threading.Thread):        

    def __init__(self, node, exit_future):
        threading.Thread.__init__(self)
        self._node = node
        self._exit_future = exit_future

        self._action_client = ActionClient(node, Fibonacci, 'fibonacci')      

    def send_goal(self):
        self._node.get_logger().info('Waiting for action server...')

        self._action_client.wait_for_server()

        goal_msg = Fibonacci.Goal()
        goal_msg.order = 10

        self._node.get_logger().info('Sending goal request...')

        self._send_goal_future = self._action_client.send_goal_async(
            goal_msg, 
            feedback_callback=self.feedback_callback)

        self._send_goal_future.add_done_callback(self.goal_response_callback)

    def goal_response_callback(self, future):        
        goal_handle = future.result()
        if not goal_handle.accepted:
            self._node.get_logger().info('Goal rejected :(')
            return

        self._node.get_logger().info('Goal accepted :)')

        self._get_result_future = goal_handle.get_result_async()
        self._get_result_future.add_done_callback(self.get_result_callback)

    def feedback_callback(self, feedback):
        self._node.get_logger().info('Received feedback: {0}'.format(feedback.feedback.sequence))

    def get_result_callback(self, future):
        result = future.result().result
        status = future.result().status
        if status == GoalStatus.STATUS_SUCCEEDED:
            self._node.get_logger().info('Goal succeeded! Result: {0}'.format(result.sequence))
        else:
            self._node.get_logger().info('Goal failed with status: {0}'.format(status))

        # Shutdown after receiving a result
        # rclpy.shutdown()

        self._exit_future.set_result(None)    
    
    def destroy(self):
        self._action_client.destroy()
        
    def run(self):
        print("Client is entering...")
        self.send_goal()

        while not self._exit_future.done():
            time.sleep(1)
            print("Client is running...")
        
        print('Client is exiting...')
예제 #26
0
def main():
    # A flow of typical ROS program
    # 1. Initialization
    rclpy.init()
    # 2. Create one or more nodes
    node = rclpy.create_node('commander')
    fp_range = FloatingPointRange(from_value=0.2, to_value=2.0, step=0.1)
    pd = ParameterDescriptor(name='parameter in commander',
                             type=ParameterType.PARAMETER_DOUBLE,
                             description='set key-in period in seconds',
                             read_only=False,
                             floating_point_range=[fp_range])
    param = node.declare_parameter('key_in_period',
                                   value=0.2,
                                   descriptor=pd,
                                   ignore_override=False)
    # 3. Process node callbacks
    publisher = node.create_publisher(String, 'keys', 10)

    goal_handle = None

    def goal_response_callback(future):
        nonlocal goal_handle
        goal_handle = future.result()
        if not goal_handle.accepted:
            nonlocal timer
            node.get_logger().info('Goal rejected. oTL')
            goal_handle = None
            node.destroy_timer(timer)
            timer = node.create_timer(timer_period_sec=period,
                                      callback=unix_callback)
            return
        node.get_logger().info('Goal accepted!')
        get_result_future = goal_handle.get_result_async()
        get_result_future.add_done_callback(get_result_callback)

    def get_result_callback(future):
        nonlocal timer
        result = future.result().result.cmds
        status = future.result().status
        print(status)

        if status == GoalStatus.STATUS_SUCCEEDED:
            msg = String()
            msg.data = result[-1]
            publisher.publish(msg)
            node.get_logger().info('Goal succeeded! ')
        elif status == GoalStatus.STATUS_CANCELED:
            node.get_logger().info('Goal canceled')
        else:
            node.get_logger().info(
                'Goal failed with status code: {}'.format(status))
        node.destroy_timer(timer)
        timer = node.create_timer(timer_period_sec=period,
                                  callback=unix_callback)

    def feedback_callback(fb):
        node.get_logger().info('Received feedback: {}'.format(
            fb.feedback.partial_cmds))
        msg = String()
        msg.data = fb.feedback.partial_cmds[-1]
        publisher.publish(msg)

    def cancel_done(future):
        goals_canceling = future.result(
        ).goals_canceling  # [action_msgs.msg.GoalInfo,...]
        if len(goals_canceling) > 0:
            nonlocal timer
            node.get_logger().info('Goal canceled')
            node.destroy_timer(timer)
            timer = node.create_timer(timer_period_sec=period,
                                      callback=unix_callback)
        else:
            node.get_logger().info('Goal not canceled')

    def unix_callback2():
        node.get_logger().info('Key \'' + autopilot_cmds['cancel_goal'] +
                               '\' in to cancel')
        fd = sys.stdin.fileno()
        old = termios.tcgetattr(fd)
        tty.setcbreak(fd, tty.TCSANOW)
        timeout = 0
        read_ready = select.select([sys.stdin], [], [], timeout)[0]
        if read_ready == [sys.stdin]:
            if sys.stdin.read(1) == autopilot_cmds['cancel_goal']:
                # if goal_handle == None:
                #     node.get_logger().info('Goal unavailable to be canceled')
                #     return

                node.get_logger().info('Canceling goal...')

                future = goal_handle.cancel_goal_async()
                future.add_done_callback(cancel_done)

        termios.tcsetattr(fd, termios.TCSANOW, old)

    def unix_callback():
        node.get_logger().info('Please key in!')
        fd = sys.stdin.fileno()
        old = termios.tcgetattr(fd)
        tty.setcbreak(fd, tty.TCSANOW)
        timeout = 0
        read_ready = select.select([sys.stdin], [], [], timeout)[0]
        if read_ready == [sys.stdin]:
            msg = String()
            msg.data = sys.stdin.read(1)
            if msg.data == autopilot_cmds['request_goal']:
                nonlocal timer
                goal_msg = AutoPilot.Goal()
                goal_msg.goal_x = 0
                goal_msg.goal_y = 0
                node.get_logger().info('Sending goal request...')
                send_goal_future = action_client.send_goal_async(
                    goal_msg, feedback_callback=feedback_callback)
                send_goal_future.add_done_callback(goal_response_callback)
                node.destroy_timer(timer)
                timer = node.create_timer(timer_period_sec=period,
                                          callback=unix_callback2)
                return

            publisher.publish(msg)
        termios.tcsetattr(fd, termios.TCSANOW, old)

    def before_parameter(param_list: [rclpy.parameter.Parameter]):
        nonlocal timer
        p_list = [its for its in param_list if its.name == 'key_in_period']
        period = p_list[0].get_parameter_value().double_value
        node.destroy_timer(timer)
        timer = node.create_timer(timer_period_sec=period,
                                  callback=unix_callback)
        return SetParametersResult(successful=True)

    node.add_on_set_parameters_callback(before_parameter)

    def after_parameter(msg: ParameterEvent):
        if msg.node != '/commander':
            return
        param_msg_list = [
            its for its in msg.changed_parameters
            if its.name == 'key_in_period'
        ]
        name = param_msg_list[0].name
        value = param_msg_list[0].value.double_value
        node.get_logger().info('parameter \'{}\' = {} sec.'.format(
            name, value))

    node.create_subscription(ParameterEvent, 'parameter_events',
                             after_parameter, 10)

    action_client = ActionClient(node, AutoPilot, 'autopilot')
    node.get_logger().info('Waiting for action server...')
    action_client.wait_for_server()

    period = param.get_parameter_value().double_value
    timer = node.create_timer(timer_period_sec=period, callback=unix_callback)
    node.get_logger().info('Publishing keystrokes.')

    rclpy.spin(node)
    action_client.destroy()
    node.destroy_timer(timer)
    node.destroy_node()
    rclpy.shutdown()
예제 #27
0
def send_goal(action_name, action_type, goal_values, feedback_callback):
    goal_handle = None
    node = None
    action_client = None
    try:
        try:
            # TODO(jacobperron): This logic should come from a rosidl related package
            parts = action_type.split('/')
            if len(parts) == 1:
                raise ValueError()
            if len(parts) == 2:
                parts = [parts[0], 'action', parts[1]]
            package_name = parts[0]
            action_type = parts[-1]
            if not all(parts):
                raise ValueError()
        except ValueError:
            raise RuntimeError('The passed action type is invalid')

        module = importlib.import_module('.'.join(parts[:-1]))
        action_module = getattr(module, action_type)
        goal_dict = yaml.safe_load(goal_values)

        rclpy.init()

        node_name = NODE_NAME_PREFIX + '_send_goal_{}_{}'.format(
            package_name, action_type)
        node = rclpy.create_node(node_name)

        action_client = ActionClient(node, action_module, action_name)

        goal = action_module.Goal()

        try:
            set_message_fields(goal, goal_dict)
        except Exception as ex:
            return 'Failed to populate message fields: {!r}'.format(ex)

        print('Waiting for an action server to become available...')
        action_client.wait_for_server()

        print('Sending goal:\n     {}'.format(message_to_yaml(goal, None)))
        goal_future = action_client.send_goal_async(goal, feedback_callback)
        rclpy.spin_until_future_complete(node, goal_future)

        goal_handle = goal_future.result()

        if goal_handle is None:
            raise RuntimeError('Exception while sending goal: {!r}'.format(
                goal_future.exception()))

        if not goal_handle.accepted:
            print('Goal was rejected.')
            # no need to potentially cancel the goal anymore
            goal_handle = None
            return

        print('Goal accepted with ID: {}\n'.format(
            bytes(goal_handle.goal_id.uuid).hex()))

        result_future = goal_handle.get_result_async()
        rclpy.spin_until_future_complete(node, result_future)

        result = result_future.result()

        if result is None:
            raise RuntimeError('Exception while getting result: {!r}'.format(
                result_future.exception()))

        # no need to potentially cancel the goal anymore
        goal_handle = None

        print('Result:\n    {}'.format(message_to_yaml(result.result, None)))
        print('Goal finished with status: {}'.format(
            _goal_status_to_string(result.status)))
    finally:
        # Cancel the goal if it's still active
        if (goal_handle is not None
                and (GoalStatus.STATUS_ACCEPTED == goal_handle.status
                     or GoalStatus.STATUS_EXECUTING == goal_handle.status)):
            print('Canceling goal...')
            cancel_future = goal_handle.cancel_goal_async()
            rclpy.spin_until_future_complete(node, cancel_future)

            cancel_response = cancel_future.result()

            if cancel_response is None:
                raise RuntimeError(
                    'Exception while canceling goal: {!r}'.format(
                        cancel_future.exception()))

            if len(cancel_response.goals_canceling) == 0:
                raise RuntimeError('Failed to cancel goal')
            if len(cancel_response.goals_canceling) > 1:
                raise RuntimeError('More than one goal canceled')
            if cancel_response.goals_canceling[
                    0].goal_id != goal_handle.goal_id:
                raise RuntimeError('Canceled goal with incorrect goal ID')
            print('Goal canceled.')

        if action_client is not None:
            action_client.destroy()
        if node is not None:
            node.destroy_node()
        if rclpy.ok():
            rclpy.shutdown()
class NavTester(Node):
    def __init__(self,
                 initial_pose: Pose,
                 goal_pose: Pose,
                 namespace: str = ''):
        super().__init__(node_name='nav2_tester', namespace=namespace)
        self.initial_pose_pub = self.create_publisher(
            PoseWithCovarianceStamped, 'initialpose', 10)

        pose_qos = QoSProfile(durability=QoSDurabilityPolicy.TRANSIENT_LOCAL,
                              reliability=QoSReliabilityPolicy.RELIABLE,
                              history=QoSHistoryPolicy.KEEP_LAST,
                              depth=1)

        self.model_pose_sub = self.create_subscription(
            PoseWithCovarianceStamped, 'amcl_pose', self.poseCallback,
            pose_qos)
        self.initial_pose_received = False
        self.initial_pose = initial_pose
        self.goal_pose = goal_pose
        self.action_client = ActionClient(self, NavigateThroughPoses,
                                          'navigate_through_poses')

    def info_msg(self, msg: str):
        self.get_logger().info('\033[1;37;44m' + msg + '\033[0m')

    def warn_msg(self, msg: str):
        self.get_logger().warn('\033[1;37;43m' + msg + '\033[0m')

    def error_msg(self, msg: str):
        self.get_logger().error('\033[1;37;41m' + msg + '\033[0m')

    def setInitialPose(self):
        msg = PoseWithCovarianceStamped()
        msg.pose.pose = self.initial_pose
        msg.header.frame_id = 'map'
        self.info_msg('Publishing Initial Pose')
        self.initial_pose_pub.publish(msg)
        self.currentPose = self.initial_pose

    def getStampedPoseMsg(self, pose: Pose):
        msg = PoseStamped()
        msg.header.frame_id = 'map'
        msg.pose = pose
        return msg

    def runNavigateAction(self, goal_pose: Optional[Pose] = None):
        # Sends a `NavToPose` action request and waits for completion
        self.info_msg("Waiting for 'NavigateThroughPoses' action server")
        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg(
                "'NavigateThroughPoses' action server not available, waiting..."
            )

        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        goal_msg = NavigateThroughPoses.Goal()
        goal_msg.poses = [self.getStampedPoseMsg(self.goal_pose)]

        self.info_msg('Sending goal request...')
        send_goal_future = self.action_client.send_goal_async(goal_msg)

        rclpy.spin_until_future_complete(self, send_goal_future)
        goal_handle = send_goal_future.result()

        if not goal_handle.accepted:
            self.error_msg('Goal rejected')
            return False

        self.info_msg('Goal accepted')
        get_result_future = goal_handle.get_result_async()

        self.info_msg("Waiting for 'NavigateToPose' action to complete")
        rclpy.spin_until_future_complete(self, get_result_future)
        status = get_result_future.result().status
        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg(f'Goal failed with status code: {status}')
            return False

        self.info_msg('Goal succeeded!')
        return True

    def poseCallback(self, msg):
        self.info_msg('Received amcl_pose')
        self.current_pose = msg.pose.pose
        self.initial_pose_received = True

    def wait_for_node_active(self, node_name: str):
        # Waits for the node within the tester namespace to become active
        self.info_msg(f'Waiting for {node_name} to become active')
        node_service = f'{node_name}/get_state'
        state_client = self.create_client(GetState, node_service)
        while not state_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(f'{node_service} service not available, waiting...')
        req = GetState.Request()  # empty request
        state = 'UNKNOWN'
        while (state != 'active'):
            self.info_msg(f'Getting {node_name} state...')
            future = state_client.call_async(req)
            rclpy.spin_until_future_complete(self, future)
            if future.result() is not None:
                state = future.result().current_state.label
                self.info_msg(f'Result of get_state: {state}')
            else:
                self.error_msg(
                    f'Exception while calling service: {future.exception()!r}')
            time.sleep(5)

    def shutdown(self):
        self.info_msg('Shutting down')
        self.action_client.destroy()

        transition_service = 'lifecycle_manager_navigation/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(
                f'{transition_service} service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down navigation lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down navigation lifecycle manager complete.')
        except Exception as e:  # noqa: B902
            self.error_msg(f'Service call failed {e!r}')
        transition_service = 'lifecycle_manager_localization/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(
                f'{transition_service} service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down localization lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down localization lifecycle manager complete')
        except Exception as e:  # noqa: B902
            self.error_msg(f'Service call failed {e!r}')

    def wait_for_initial_pose(self):
        self.initial_pose_received = False
        while not self.initial_pose_received:
            self.info_msg('Setting initial pose')
            self.setInitialPose()
            self.info_msg('Waiting for amcl_pose to be received')
            rclpy.spin_once(self, timeout_sec=1)
예제 #29
0
class NavTester(Node):
    def __init__(self,
                 test_type: TestType,
                 initial_pose: Pose,
                 goal_pose: Pose,
                 namespace: str = ''):
        super().__init__(node_name='nav2_tester', namespace=namespace)
        self.initial_pose_pub = self.create_publisher(
            PoseWithCovarianceStamped, 'initialpose', 10)
        self.goal_pub = self.create_publisher(PoseStamped, 'goal_pose', 10)

        transient_local_qos = QoSProfile(
            durability=QoSDurabilityPolicy.
            RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
            reliability=QoSReliabilityPolicy.
            RMW_QOS_POLICY_RELIABILITY_RELIABLE,
            history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
            depth=1)

        volatile_qos = QoSProfile(
            durability=QoSDurabilityPolicy.RMW_QOS_POLICY_DURABILITY_VOLATILE,
            reliability=QoSReliabilityPolicy.
            RMW_QOS_POLICY_RELIABILITY_RELIABLE,
            history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
            depth=1)

        self.model_pose_sub = self.create_subscription(
            PoseWithCovarianceStamped, 'amcl_pose', self.poseCallback,
            transient_local_qos)

        self.test_type = test_type
        self.filter_test_result = True
        if self.test_type == TestType.KEEPOUT:
            self.plan_sub = self.create_subscription(Path, 'plan',
                                                     self.planCallback,
                                                     volatile_qos)
        elif self.test_type == TestType.SPEED:
            self.speed_it = 0
            # Expected chain of speed limits
            self.limits = [50.0, 0.0]
            # Permissive array: all received speed limits must match to "limits" from above
            self.limit_passed = [False, False]
            self.plan_sub = self.create_subscription(SpeedLimit, 'speed_limit',
                                                     self.speedLimitCallback,
                                                     volatile_qos)

        self.mask_received = False
        self.mask_sub = self.create_subscription(OccupancyGrid, 'filter_mask',
                                                 self.maskCallback,
                                                 transient_local_qos)

        self.initial_pose_received = False
        self.initial_pose = initial_pose
        self.goal_pose = goal_pose
        self.action_client = ActionClient(self, NavigateToPose,
                                          'navigate_to_pose')

    def info_msg(self, msg: str):
        self.get_logger().info('\033[1;37;44m' + msg + '\033[0m')

    def warn_msg(self, msg: str):
        self.get_logger().warn('\033[1;37;43m' + msg + '\033[0m')

    def error_msg(self, msg: str):
        self.get_logger().error('\033[1;37;41m' + msg + '\033[0m')

    def setInitialPose(self):
        msg = PoseWithCovarianceStamped()
        msg.pose.pose = self.initial_pose
        msg.header.frame_id = 'map'
        self.info_msg('Publishing Initial Pose')
        self.initial_pose_pub.publish(msg)
        self.currentPose = self.initial_pose

    def getStampedPoseMsg(self, pose: Pose):
        msg = PoseStamped()
        msg.header.frame_id = 'map'
        msg.pose = pose
        return msg

    def publishGoalPose(self, goal_pose: Optional[Pose] = None):
        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        self.goal_pub.publish(self.getStampedPoseMsg(self.goal_pose))

    def runNavigateAction(self, goal_pose: Optional[Pose] = None):
        # Sends a `NavToPose` action request and waits for completion
        self.info_msg("Waiting for 'NavigateToPose' action server")
        while not self.action_client.wait_for_server(timeout_sec=1.0):
            self.info_msg(
                "'NavigateToPose' action server not available, waiting...")

        self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose
        goal_msg = NavigateToPose.Goal()
        goal_msg.pose = self.getStampedPoseMsg(self.goal_pose)

        self.info_msg('Sending goal request...')
        send_goal_future = self.action_client.send_goal_async(goal_msg)

        rclpy.spin_until_future_complete(self, send_goal_future)
        goal_handle = send_goal_future.result()

        if not goal_handle.accepted:
            self.error_msg('Goal rejected')
            return False

        self.info_msg('Goal accepted')
        get_result_future = goal_handle.get_result_async()

        self.info_msg("Waiting for 'NavigateToPose' action to complete")
        rclpy.spin_until_future_complete(self, get_result_future)
        status = get_result_future.result().status
        if status != GoalStatus.STATUS_SUCCEEDED:
            self.info_msg('Goal failed with status code: {0}'.format(status))
            return False

        self.info_msg('Goal succeeded!')
        return True

    def isInKeepout(self, x, y):
        mx, my = self.filter_mask.worldToMap(x, y)
        if mx == -1 and my == -1:  # Out of mask's area
            return False
        if self.filter_mask.getValue(mx, my) == 100:  # Occupied
            return True
        return False

    # Checks that (x, y) position does not belong to a keepout zone.
    def checkKeepout(self, x, y):
        if not self.mask_received:
            self.warn_msg('Filter mask was not received')
        elif self.isInKeepout(x, y):
            self.filter_test_result = False
            self.error_msg('Pose (' + str(x) + ', ' + str(y) +
                           ') belongs to keepout zone')
            return False
        return True

    # Checks that currently received speed_limit is equal to the it-th item
    # of expected speed "limits" array.
    # If so, sets it-th item of permissive array "limit_passed" to be true.
    # Otherwise it will be remained to be false.
    # Also verifies that speed limit messages received no more than N-times
    # (where N - is the length of "limits" array),
    # otherwise sets overall "filter_test_result" to be false.
    def checkSpeed(self, it, speed_limit):
        if it >= len(self.limits):
            self.error_msg('Got excess speed limit')
            self.filter_test_result = False
            return
        if speed_limit == self.limits[it]:
            self.limit_passed[it] = True
        else:
            self.error_msg('Incorrect speed limit received: ' +
                           str(speed_limit) + ', but should be: ' +
                           str(self.limits[it]))

    def poseCallback(self, msg):
        self.info_msg('Received amcl_pose')
        self.current_pose = msg.pose.pose
        self.initial_pose_received = True
        if self.test_type == TestType.KEEPOUT:
            if not self.checkKeepout(msg.pose.pose.position.x,
                                     msg.pose.pose.position.y):
                self.error_msg('Robot goes into keepout zone')

    def planCallback(self, msg):
        self.info_msg('Received plan')
        for pose in msg.poses:
            if not self.checkKeepout(pose.pose.position.x,
                                     pose.pose.position.y):
                self.error_msg('Path plan intersects with keepout zone')
                return

    def speedLimitCallback(self, msg):
        self.info_msg('Received speed limit: ' + str(msg.speed_limit))
        self.checkSpeed(self.speed_it, msg.speed_limit)
        self.speed_it += 1

    def maskCallback(self, msg):
        self.info_msg('Received filter mask')
        self.filter_mask = FilterMask(msg)
        self.mask_received = True

    def wait_for_filter_mask(self, timeout):
        start_time = time.time()

        while not self.mask_received:
            self.info_msg('Waiting for filter mask to be received ...')
            rclpy.spin_once(self, timeout_sec=1)
            if (time.time() - start_time) > timeout:
                self.error_msg('Time out to waiting filter mask')
                return False
        return True

    def reachesGoal(self, timeout, distance):
        goalReached = False
        start_time = time.time()

        while not goalReached:
            rclpy.spin_once(self, timeout_sec=1)
            if self.distanceFromGoal() < distance:
                goalReached = True
                self.info_msg('*** GOAL REACHED ***')
                return True
            elif timeout is not None:
                if (time.time() - start_time) > timeout:
                    self.error_msg('Robot timed out reaching its goal!')
                    return False

    def distanceFromGoal(self):
        d_x = self.current_pose.position.x - self.goal_pose.position.x
        d_y = self.current_pose.position.y - self.goal_pose.position.y
        distance = math.sqrt(d_x * d_x + d_y * d_y)
        self.info_msg('Distance from goal is: ' + str(distance))
        return distance

    def wait_for_node_active(self, node_name: str):
        # Waits for the node within the tester namespace to become active
        self.info_msg('Waiting for ' + node_name + ' to become active')
        node_service = node_name + '/get_state'
        state_client = self.create_client(GetState, node_service)
        while not state_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(node_service + ' service not available, waiting...')
        req = GetState.Request()  # empty request
        state = 'UNKNOWN'
        while (state != 'active'):
            self.info_msg('Getting ' + node_name + ' state...')
            future = state_client.call_async(req)
            rclpy.spin_until_future_complete(self, future)
            if future.result() is not None:
                state = future.result().current_state.label
                self.info_msg('Result of get_state: %s' % state)
            else:
                self.error_msg('Exception while calling service: %r' %
                               future.exception())
            time.sleep(5)

    def shutdown(self):
        self.info_msg('Shutting down')
        self.action_client.destroy()

        transition_service = 'lifecycle_manager_navigation/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down navigation lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down navigation lifecycle manager complete.')
        except Exception as e:
            self.error_msg('Service call failed %r' % (e, ))
        transition_service = 'lifecycle_manager_localization/manage_nodes'
        mgr_client = self.create_client(ManageLifecycleNodes,
                                        transition_service)
        while not mgr_client.wait_for_service(timeout_sec=1.0):
            self.info_msg(transition_service +
                          ' service not available, waiting...')

        req = ManageLifecycleNodes.Request()
        req.command = ManageLifecycleNodes.Request().SHUTDOWN
        future = mgr_client.call_async(req)
        try:
            self.info_msg('Shutting down localization lifecycle manager...')
            rclpy.spin_until_future_complete(self, future)
            future.result()
            self.info_msg(
                'Shutting down localization lifecycle manager complete')
        except Exception as e:
            self.error_msg('Service call failed %r' % (e, ))

    def wait_for_initial_pose(self):
        self.initial_pose_received = False
        while not self.initial_pose_received:
            self.info_msg('Setting initial pose')
            self.setInitialPose()
            self.info_msg('Waiting for amcl_pose to be received')
            rclpy.spin_once(self, timeout_sec=1)
def send_goal(action_name, action_type, goal_values, feedback_callback):
    goal_handle = None
    node = None
    action_client = None
    try:
        try:
            action_module = get_action(action_type)
        except (AttributeError, ModuleNotFoundError, ValueError):
            raise RuntimeError('The passed action type is invalid')

        goal_dict = yaml.safe_load(goal_values)

        rclpy.init()

        node_name = f"{NODE_NAME_PREFIX}_send_goal_{action_type.replace('/', '_')}"
        node = rclpy.create_node(node_name)

        action_client = ActionClient(node, action_module, action_name)

        goal = action_module.Goal()

        try:
            set_message_fields(goal, goal_dict)
        except Exception as ex:
            return 'Failed to populate message fields: {!r}'.format(ex)

        print('Waiting for an action server to become available...')
        action_client.wait_for_server()

        print('Sending goal:\n     {}'.format(message_to_yaml(goal)))
        goal_future = action_client.send_goal_async(goal, feedback_callback)
        rclpy.spin_until_future_complete(node, goal_future)

        goal_handle = goal_future.result()

        if goal_handle is None:
            raise RuntimeError(
                'Exception while sending goal: {!r}'.format(goal_future.exception()))

        if not goal_handle.accepted:
            print('Goal was rejected.')
            # no need to potentially cancel the goal anymore
            goal_handle = None
            return

        print('Goal accepted with ID: {}\n'.format(bytes(goal_handle.goal_id.uuid).hex()))

        result_future = goal_handle.get_result_async()
        rclpy.spin_until_future_complete(node, result_future)

        result = result_future.result()

        if result is None:
            raise RuntimeError(
                'Exception while getting result: {!r}'.format(result_future.exception()))

        # no need to potentially cancel the goal anymore
        goal_handle = None

        print('Result:\n    {}'.format(message_to_yaml(result.result)))
        print('Goal finished with status: {}'.format(_goal_status_to_string(result.status)))
    finally:
        # Cancel the goal if it's still active
        if (goal_handle is not None and
            (GoalStatus.STATUS_ACCEPTED == goal_handle.status or
             GoalStatus.STATUS_EXECUTING == goal_handle.status)):
            print('Canceling goal...')
            cancel_future = goal_handle.cancel_goal_async()
            rclpy.spin_until_future_complete(node, cancel_future)

            cancel_response = cancel_future.result()

            if cancel_response is None:
                raise RuntimeError(
                    'Exception while canceling goal: {!r}'.format(cancel_future.exception()))

            if len(cancel_response.goals_canceling) == 0:
                raise RuntimeError('Failed to cancel goal')
            if len(cancel_response.goals_canceling) > 1:
                raise RuntimeError('More than one goal canceled')
            if cancel_response.goals_canceling[0].goal_id != goal_handle.goal_id:
                raise RuntimeError('Canceled goal with incorrect goal ID')
            print('Goal canceled.')

        if action_client is not None:
            action_client.destroy()
        if node is not None:
            node.destroy_node()
        if rclpy.ok():
            rclpy.shutdown()