An action client lets your program ask another part to do a task and get updates while it works. Feedback helps you see progress and stay informed.
Action client with feedback in ROS
import rclpy from rclpy.node import Node from rclpy.action import ActionClient from example_interfaces.action import Fibonacci class FibonacciActionClient(Node): def __init__(self): super().__init__('fibonacci_action_client') self._action_client = ActionClient(self, Fibonacci, 'fibonacci') def send_goal(self, order): goal_msg = Fibonacci.Goal() goal_msg.order = order self._action_client.wait_for_server() 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: print('Goal rejected :(') return print('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_msg): feedback = feedback_msg.feedback print(f'Received feedback: {feedback.partial_sequence}') def get_result_callback(self, future): result = future.result().result print(f'Result: {result.sequence}')
The feedback_callback runs when the server sends progress updates.
You must wait for the action server before sending a goal.
self._action_client = ActionClient(self, Fibonacci, 'fibonacci')self._send_goal_future = self._action_client.send_goal_async(goal_msg, feedback_callback=self.feedback_callback)
def feedback_callback(self, feedback_msg): print(f'Received feedback: {feedback_msg.feedback.partial_sequence}')
This program creates a ROS 2 action client that sends a Fibonacci goal of order 5. It prints feedback as the sequence builds and shows the final result.
import rclpy from rclpy.node import Node from rclpy.action import ActionClient from example_interfaces.action import Fibonacci class FibonacciActionClient(Node): def __init__(self): super().__init__('fibonacci_action_client') self._action_client = ActionClient(self, Fibonacci, 'fibonacci') def send_goal(self, order): goal_msg = Fibonacci.Goal() goal_msg.order = order self._action_client.wait_for_server() 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.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_msg): feedback = feedback_msg.feedback self.get_logger().info(f'Received feedback: {feedback.partial_sequence}') def get_result_callback(self, future): result = future.result().result self.get_logger().info(f'Result: {result.sequence}') rclpy.shutdown() def main(args=None): rclpy.init(args=args) action_client = FibonacciActionClient() action_client.send_goal(5) rclpy.spin(action_client) if __name__ == '__main__': main()
Feedback helps you track progress without waiting for the whole task to finish.
Always check if the goal was accepted before waiting for the result.
Use rclpy.shutdown() to cleanly stop the node after the result.
An action client sends goals and can get feedback while the task runs.
Feedback callbacks let you see progress updates in real time.
Waiting for the server and checking goal acceptance are important steps.
Practice
Solution
Step 1: Understand the role of an action client
An action client sends goals to an action server and can receive feedback during execution.Step 2: Identify feedback purpose
Feedback callbacks provide real-time progress updates while the task runs.Final Answer:
To send goals to an action server and receive progress updates -> Option BQuick Check:
Action client = send goals + get feedback [OK]
- Thinking action clients only get final results
- Confusing action clients with service clients
- Ignoring feedback callbacks
Solution
Step 1: Recall ROS Python action client syntax
The standard way is to use SimpleActionClient with server name and action type.Step 2: Feedback callback is set separately
Feedback callbacks are registered when sending goals, not in constructor.Final Answer:
client = actionlib.SimpleActionClient('move', MoveAction) -> Option CQuick Check:
SimpleActionClient + feedback_cb separate [OK]
- Passing feedback_cb in constructor (wrong)
- Using ActionClient instead of SimpleActionClient
- Missing action type argument
def feedback_cb(feedback):
print(f"Progress: {feedback.progress}%")
client = actionlib.SimpleActionClient('task', TaskAction)
client.wait_for_server()
goal = TaskGoal()
client.send_goal(goal, feedback_cb=feedback_cb)Solution
Step 1: Understand feedback callback behavior
The feedback_cb prints the progress attribute from feedback with formatting.Step 2: Substitute progress value
If feedback.progress is 50, the print outputs "Progress: 50%" exactly.Final Answer:
Progress: 50% -> Option AQuick Check:
Formatted string with feedback.progress = output [OK]
- Printing variable name instead of value
- Assuming feedback_cb is not called
- Ignoring string formatting
client = actionlib.SimpleActionClient('task', TaskAction)
client.send_goal(TaskGoal(), feedback_cb=feedback_cb)
client.wait_for_server()Solution
Step 1: Check action client usage order
wait_for_server() must be called before sending a goal to ensure server is ready.Step 2: Identify error in code order
Here, send_goal() is called before wait_for_server(), which is incorrect.Final Answer:
wait_for_server() should be called before send_goal() -> Option AQuick Check:
wait_for_server() before send_goal() [OK]
- Calling send_goal before wait_for_server
- Misunderstanding feedback_cb usage
- Assuming feedback_cb is constructor argument
Solution
Step 1: Understand feedback handling for multiple goals
Each goal's feedback should be handled separately to avoid mixing progress updates.Step 2: Use closures or separate callbacks
Creating a unique feedback callback per goal that captures its ID ensures correct feedback association.Final Answer:
Create a new feedback callback function for each goal that captures its goal ID -> Option DQuick Check:
Separate feedback callbacks per goal prevent mix-up [OK]
- Using one callback for all goals causing mixed feedback
- Sending all goals simultaneously without control
- Ignoring feedback defeats purpose of action client
Ready to prove it?
Pass the test and this pattern moves unknown
