How to Reject an Action Goal

asked 2023-02-09 16:16:18 -0500

versavel gravatar image

I recently started to learn ROS and I have trouble Rejecting an Action Goal.

In a nutshell: when the goal_callback function in the Action Server returns GoalResponse.REJECT, instead of GoalResponse.ACCEPT, the Action Client hangs.

More details:

  • ROS2 Humble distro on ubuntu 20.04 in docker container on Raspberry Pi 4B.

  • Using the Action example in the ROS2 repository : code

  • commands to run the server and client nodes

    ros2 run examples_rclpy_minimal_action_server server
    ros2 run examples_rclpy_minimal_action_client client
    
  • both client and server nodes behave as expected: After the goal successfully completes, the client node is destroyed and I can rerun the client command in the same terminal window. The server node keeps running and successfully handles subsequent client goal requests.

  • when I modify the goal_callback function in the server.py file from

def goal_callback(self, goal_request):
    ...
    return GoalResponse.ACCEPT

to

def goal_callback(self, goal_request):
    ...
    return GoalResponse.REJECT

the action server continues to work as expected, and the action client prints 'Goal rejected :(' in the terminal window as expected, BUT the action client node is not destroyed.

  ros2 node list

shows that the client node as alive. Also the terminal window used to run the client does not return the prompt.

Questions:

  • What does the "return GoalResponse.REJECT" in the goal_callback function trigger ?
  • How do I modify the action client code to destroy the client node once the goal is rejected ?

Following are the action client and action server code:

from action_msgs.msg import GoalStatus
from example_interfaces.action import Fibonacci

import rclpy from rclpy.action import
ActionClient from rclpy.node import Node


class MinimalActionClient(Node):

def __init__(self):
    super().__init__('minimal_action_client')
    self._action_client = ActionClient(self, Fibonacci, 'fibonacci')

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

    self.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.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.get_logger().info('Goal succeeded! Result: {0}'.format(result.sequence))
    else:
        self.get_logger().info('Goal failed with status: {0}'.format(status))

    # Shutdown after receiving a result
    rclpy.shutdown()

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

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

    self.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 main(args=None):
    rclpy.init(args=args)

    action_client = MinimalActionClient()

    action_client.send_goal()

    rclpy.spin(action_client)


if __name__ == '__main__':
    main()

.

import time

from example_interfaces.action import Fibonacci

import rclpy from rclpy.action import ActionServer, CancelResponse, GoalResponse 
from rclpy.callback_groups import ReentrantCallbackGroup 
from rclpy.executors import MultiThreadedExecutor 
from rclpy.node import Node


class MinimalActionServer(Node):

def __init__(self):
    super().__init__('minimal_action_server')

    self._action_server = ActionServer(
        self,
        Fibonacci,
        'fibonacci',
        execute_callback=self.execute_callback,
        callback_group=ReentrantCallbackGroup(),
        goal_callback=self.goal_callback,
        cancel_callback=self.cancel_callback)

def destroy(self):
    self._action_server.destroy()
    super().destroy_node()

def goal_callback(self, goal_request):
    """Accept or reject a client request to begin an ...
(more)
edit retag flag offensive close merge delete

Comments

I have a similar problem but with RCLCPP, following this question

Mrmara gravatar image Mrmara  ( 2023-05-10 11:44:01 -0500 )edit