An action server lets your robot do tasks that take time and can give updates while working. It helps your robot respond to commands and tell you progress or if it finished.
Action server implementation in ROS
import rclpy from rclpy.action import ActionServer from rclpy.node import Node from example_interfaces.action import Fibonacci class FibonacciActionServer(Node): def __init__(self): super().__init__('fibonacci_action_server') self._action_server = ActionServer( self, Fibonacci, 'fibonacci', self.execute_callback) def execute_callback(self, goal_handle): feedback_msg = Fibonacci.Feedback() feedback_msg.sequence = [0, 1] for i in range(1, goal_handle.request.order): feedback_msg.sequence.append( feedback_msg.sequence[i] + feedback_msg.sequence[i - 1]) goal_handle.publish_feedback(feedback_msg) self.get_logger().info(f'Feedback: {feedback_msg.sequence}') goal_handle.succeed() result = Fibonacci.Result() result.sequence = feedback_msg.sequence return result
The ActionServer class creates the server that listens for goals.
The execute_callback runs when a goal is received and sends feedback and result.
self._action_server = ActionServer(self, Fibonacci, 'fibonacci', self.execute_callback)def execute_callback(self, goal_handle): # process goal and send feedback goal_handle.publish_feedback(feedback_msg) # finish and return result goal_handle.succeed() return result
This program creates a ROS 2 action server that calculates Fibonacci numbers. It sends feedback as it calculates and returns the full sequence when done.
import rclpy from rclpy.action import ActionServer from rclpy.node import Node from example_interfaces.action import Fibonacci class FibonacciActionServer(Node): def __init__(self): super().__init__('fibonacci_action_server') self._action_server = ActionServer( self, Fibonacci, 'fibonacci', self.execute_callback) def execute_callback(self, goal_handle): feedback_msg = Fibonacci.Feedback() feedback_msg.sequence = [0, 1] for i in range(1, goal_handle.request.order): feedback_msg.sequence.append( feedback_msg.sequence[i] + feedback_msg.sequence[i - 1]) goal_handle.publish_feedback(feedback_msg) self.get_logger().info(f'Feedback: {feedback_msg.sequence}') goal_handle.succeed() result = Fibonacci.Result() result.sequence = feedback_msg.sequence return result def main(args=None): rclpy.init(args=args) action_server = FibonacciActionServer() rclpy.spin(action_server) rclpy.shutdown() if __name__ == '__main__': main()
Remember to match the action type with your .action file definition.
Use feedback to keep clients informed during long tasks.
Always call succeed() or abort() on the goal handle to finish the action.
An action server handles long tasks and sends progress updates.
Use execute_callback to process goals and send feedback.
Finish by returning a result and marking the goal succeeded or aborted.
Practice
Solution
Step 1: Understand the purpose of an action server
An action server is designed to manage tasks that take time and can provide updates while working.Step 2: Compare options with the role
Only To handle long-running tasks and provide feedback during execution correctly describes handling long tasks and sending feedback, unlike others which describe unrelated roles.Final Answer:
To handle long-running tasks and provide feedback during execution -> Option AQuick Check:
Action server = long tasks + feedback [OK]
- Confusing action server with simple message publishers
- Thinking action server only receives goals without processing
- Mixing action server with parameter server functionality
Solution
Step 1: Recall the standard signature for execute callback
In ROS Python action servers, the execute callback is a method with 'self' and 'goal' parameters.Step 2: Match options to this signature
Only def execute_callback(self, goal): correctly uses 'def execute_callback(self, goal):' matching the expected pattern.Final Answer:
def execute_callback(self, goal): -> Option DQuick Check:
Method signature = self first, then goal [OK]
- Omitting 'self' parameter in method definition
- Swapping parameter order incorrectly
- Using camelCase instead of snake_case for method name
for i in range(3):
feedback.percent_complete = (i + 1) * 33
self._action_server.publish_feedback(feedback)
rospy.sleep(1)
result.success = True
self._action_server.set_succeeded(result)What will the client receive?
Solution
Step 1: Analyze the loop sending feedback
The loop runs 3 times, each time publishing feedback with percent_complete increasing by 33.Step 2: Check the result setting after loop
After feedback, the result.success is set to True and the goal is marked succeeded.Final Answer:
Three feedback updates at 33%, 66%, and 99%, then success result -> Option BQuick Check:
Loop feedback + success result = Three feedback updates at 33%, 66%, and 99%, then success result [OK]
- Assuming feedback is sent only once after loop
- Confusing success with failure result
- Ignoring the sleep delay between feedbacks
def execute_callback(self, goal):
if goal.order < 0:
self._action_server.set_aborted()
result = Result()
result.output = goal.order * 2
self._action_server.set_succeeded(result)Solution
Step 1: Check set_aborted() usage
set_aborted() requires a result argument to inform the client about failure details.Step 2: Verify other parts
Result() usage is typical; goal.order is assumed valid; set_succeeded() after set_aborted() is not called sequentially here.Final Answer:
set_aborted() is called without a result argument -> Option AQuick Check:
set_aborted needs result argument [OK]
- Calling set_aborted() without result causes runtime error
- Assuming set_aborted() can be called without parameters
- Ignoring the need to inform client on abort
Solution
Step 1: Understand cancellation handling in action servers
Cancellation requests are detected by checking is_preempt_requested() during execution.Step 2: Proper response to cancellation
When cancellation is requested, call set_preempted() to notify client and stop processing.Final Answer:
Check self._action_server.is_preempt_requested() inside the loop, then call self._action_server.set_preempted() and return -> Option CQuick Check:
Cancellation check + set_preempted = Check self._action_server.is_preempt_requested() inside the loop, then call self._action_server.set_preempted() and return [OK]
- Ignoring cancellation requests during execution
- Calling set_aborted() instead of set_preempted() on cancel
- Delaying cancellation handling until after task finishes
Ready to prove it?
Pass the test and this pattern moves unknown
