{"id":3913,"date":"2025-08-24T09:34:32","date_gmt":"2025-08-24T09:34:32","guid":{"rendered":"https:\/\/learnbydoing.dev\/?p=3913"},"modified":"2026-01-10T21:33:57","modified_gmt":"2026-01-10T21:33:57","slug":"how-to-build-and-test-a-ros-2-action-client-in-python","status":"publish","type":"post","link":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/","title":{"rendered":"How to Build and Test a ROS 2 Action Client in Python"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"3913\" class=\"elementor elementor-3913\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e28ab35 e-flex e-con-boxed e-con e-parent\" data-id=\"e28ab35\" data-element_type=\"container\" data-e-type=\"container\" id=\"content\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-62ab6e3 e-con-full e-flex e-con e-child\" data-id=\"62ab6e3\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-62ab203 elementor-align-center elementor-widget elementor-widget-post-info\" data-id=\"62ab203\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"post-info.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<ul class=\"elementor-inline-items elementor-icon-list-items elementor-post-info\">\n\t\t\t\t\t\t\t\t<li class=\"elementor-icon-list-item elementor-repeater-item-2c98363 elementor-inline-item\" itemprop=\"about\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-terms\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-post-info__terms-list\">\n\t\t\t\t<span class=\"elementor-post-info__terms-list-item\">ROS 2<\/span>, <span class=\"elementor-post-info__terms-list-item\">Tutoriales<\/span>\t\t\t\t<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t\t\t\t<\/li>\n\t\t\t\t<\/ul>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-0650e10 e-con-full e-flex e-con e-child\" data-id=\"0650e10\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ac19582 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"ac19582\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"75\" height=\"75\" viewbox=\"0 0 75 75\" fill=\"none\"><path d=\"M74.9999 75H13.1889V73.0002H71.5859L0.460938 1.87521L1.87515 0.460999L73.0001 71.586V13.1889H74.9999V75Z\" fill=\"white\"><\/path><\/svg>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-47aa245d e-flex e-con-boxed e-con e-parent\" data-id=\"47aa245d\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-206a001 elementor-widget elementor-widget-image\" data-id=\"206a001\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"1920\" height=\"1080\" src=\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp\" class=\"attachment-full size-full wp-image-3918\" alt=\"\" srcset=\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp 1920w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2-300x169.webp 300w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2-1024x576.webp 1024w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2-768x432.webp 768w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2-1536x864.webp 1536w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2-18x10.webp 18w\" sizes=\"(max-width: 1920px) 100vw, 1920px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4bc31ca3 elementor-widget elementor-widget-text-editor\" data-id=\"4bc31ca3\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"457\" data-end=\"665\">In the previous article, we implemented a <a href=\"https:\/\/learnbydoing.dev\/es\/how-to-build-and-test-a-ros-2-action-server-in-python\/\"><strong data-start=\"499\" data-end=\"522\">ROS 2 Action Server<\/strong><\/a> in Python that calculates the <strong data-start=\"553\" data-end=\"575\">Fibonacci sequence<\/strong>. We also saw how to interact with it using the <strong data-start=\"623\" data-end=\"636\">ROS 2 CLI<\/strong> (<code data-start=\"638\" data-end=\"661\">ros2 action send_goal<\/code>).<br \/><br \/><\/p><p data-start=\"667\" data-end=\"964\">Now, we\u2019ll take a step further and implement a <strong data-start=\"714\" data-end=\"737\">ROS 2 Action Client<\/strong> in Python.<br data-start=\"748\" data-end=\"751\" \/>This client node will connect directly to our <strong data-start=\"797\" data-end=\"824\">Fibonacci Action Server<\/strong>, send a <strong data-start=\"833\" data-end=\"849\">goal request<\/strong>, handle <strong data-start=\"858\" data-end=\"870\">feedback<\/strong> messages in real time, and finally display the <strong data-start=\"918\" data-end=\"928\">result<\/strong> when the computation is complete.<br \/><br \/><\/p><p data-start=\"966\" data-end=\"1097\">If you are following along in C++, you can skip ahead to the next article, where we will replicate the same action client in C++.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-12a4ca0 elementor-widget elementor-widget-text-editor\" data-id=\"12a4ca0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h2 data-start=\"1104\" data-end=\"1148\">\ud83d\udcd6 A Quick Recap: What are ROS 2 Actions?<\/h2><p data-start=\"1150\" data-end=\"1237\">As we\u2019ve already seen, <strong data-start=\"1173\" data-end=\"1190\">ROS 2 Actions<\/strong> are designed for <strong data-start=\"1208\" data-end=\"1230\">long-running tasks<\/strong> where:<\/p><ul data-start=\"1238\" data-end=\"1489\"><li data-start=\"1238\" data-end=\"1327\"><p data-start=\"1240\" data-end=\"1327\">A <strong data-start=\"1242\" data-end=\"1250\">goal<\/strong> is sent by the client (e.g., &#8220;compute Fibonacci sequence up to order 10&#8221;).<\/p><\/li><li data-start=\"1328\" data-end=\"1425\"><p data-start=\"1330\" data-end=\"1425\">The <strong data-start=\"1334\" data-end=\"1344\">server<\/strong> works on that request, sending periodic <strong data-start=\"1385\" data-end=\"1397\">feedback<\/strong> (e.g., partial sequence).<\/p><\/li><li data-start=\"1426\" data-end=\"1489\"><p data-start=\"1428\" data-end=\"1489\">When complete, the <strong data-start=\"1447\" data-end=\"1457\">server<\/strong> returns a <strong data-start=\"1468\" data-end=\"1478\">result<\/strong> message.<\/p><\/li><\/ul><p data-start=\"1491\" data-end=\"1631\">This makes actions perfect for robotics tasks like navigation, arm manipulation, or\u2014in our simplified case\u2014computing the Fibonacci sequence.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a0aa20b elementor-widget elementor-widget-image\" data-id=\"a0aa20b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"800\" height=\"450\" src=\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Progetto-senza-titolo-4-1024x576.gif\" class=\"attachment-large size-large wp-image-3758\" alt=\"\" srcset=\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Progetto-senza-titolo-4-1024x576.gif 1024w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Progetto-senza-titolo-4-300x169.gif 300w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Progetto-senza-titolo-4-768x432.gif 768w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Progetto-senza-titolo-4-1536x864.gif 1536w, https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Progetto-senza-titolo-4-18x10.gif 18w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d5889a3 elementor-widget elementor-widget-text-editor\" data-id=\"d5889a3\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h3 data-start=\"2586\" data-end=\"2639\"><strong><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/16.0.1\/svg\/1f6e0.svg\" alt=\"\ud83d\udee0\" \/> Hands-On:\u00a0Creating the Action Client Node<\/strong><\/h3><p data-start=\"1827\" data-end=\"1934\">We\u2019ll place our client inside the same package as before, <strong data-start=\"1885\" data-end=\"1913\"><code data-start=\"1887\" data-end=\"1911\">arduinobot_py_examples<\/code><\/strong>, under a new script:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9dfe0a8 elementor-widget elementor-widget-code-highlight\" data-id=\"9dfe0a8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-bash\">\n\t\t\t\t<code readonly=\"true\" class=\"language-bash\">\n\t\t\t\t\t<xmp>arduinobot_py_examples\/arduinobot_py_examples\/simple_action_client.py\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c85344d elementor-widget elementor-widget-text-editor\" data-id=\"c85344d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Here\u2019s the complete code for the client:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-bf34d83 elementor-widget elementor-widget-code-highlight\" data-id=\"bf34d83\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>import rclpy\r\nfrom rclpy.node import Node\r\nfrom rclpy.action import ActionClient\r\nfrom arduinobot_msgs.action import Fibonacci\r\n\r\n\r\nclass SimpleActionClient(Node):\r\n\r\n    def __init__(self):\r\n        super().__init__(\"simple_action_client\")\r\n\r\n        # Create action client to connect to the 'fibonacci' action server\r\n        self.action_client = ActionClient(self, Fibonacci, \"fibonacci\")\r\n\r\n        # Wait until the server becomes available\r\n        self.action_client.wait_for_server()\r\n\r\n        # Create a goal message\r\n        goal = Fibonacci.Goal()\r\n        goal.order = 10\r\n\r\n        # Send the goal asynchronously, register callbacks\r\n        self.future = self.action_client.send_goal_async(\r\n            goal, feedback_callback=self.feedbackCallback\r\n        )\r\n        self.future.add_done_callback(self.responseCallback)\r\n\r\n    def responseCallback(self, future):\r\n        goal_handle = future.result()\r\n        if not goal_handle.accepted:\r\n            self.get_logger().info(\"Goal rejected :(\")\r\n            return\r\n\r\n        self.get_logger().info(\"Goal accepted :)\")\r\n\r\n        # Wait for the result asynchronously\r\n        result_future = goal_handle.get_result_async()\r\n        result_future.add_done_callback(self.resultCallback)\r\n\r\n    def feedbackCallback(self, feedback_msg):\r\n        self.get_logger().info(\r\n            f\"Received feedback: {feedback_msg.feedback.partial_sequence}\"\r\n        )\r\n\r\n    def resultCallback(self, future):\r\n        result = future.result().result\r\n        self.get_logger().info(f\"Result: {result.sequence}\")\r\n        rclpy.shutdown()\r\n\r\n\r\ndef main():\r\n    rclpy.init()\r\n    action_client = SimpleActionClient()\r\n    rclpy.spin(action_client)\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f9b3b54 elementor-widget elementor-widget-text-editor\" data-id=\"f9b3b54\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h3 data-start=\"125\" data-end=\"161\"><strong>\ud83d\udd0e\u00a0Let\u2019s break down the code\u00a0<\/strong><\/h3><p data-start=\"163\" data-end=\"324\">Let\u2019s go line by line and understand what\u2019s happening.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-912b47f elementor-widget elementor-widget-text-editor\" data-id=\"912b47f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Imports<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a4aede0 elementor-widget elementor-widget-code-highlight\" data-id=\"a4aede0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>import rclpy\r\nfrom rclpy.node import Node\r\nfrom rclpy.action import ActionClient\r\nfrom arduinobot_msgs.action import Fibonacci\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-48c1c08 elementor-widget elementor-widget-text-editor\" data-id=\"48c1c08\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul data-start=\"4010\" data-end=\"4231\"><li data-start=\"4010\" data-end=\"4051\"><p data-start=\"4012\" data-end=\"4051\"><code data-start=\"4012\" data-end=\"4019\">rclpy<\/code>: ROS 2 Python client library.<\/p><\/li><li data-start=\"4052\" data-end=\"4094\"><p data-start=\"4054\" data-end=\"4094\"><code data-start=\"4054\" data-end=\"4060\">Node<\/code>: Base class for any ROS 2 node.<\/p><\/li><li data-start=\"4095\" data-end=\"4169\"><p data-start=\"4097\" data-end=\"4169\"><code data-start=\"4097\" data-end=\"4111\">ActionClient<\/code>: Class to create and manage clients for action servers.<\/p><\/li><li data-start=\"4170\" data-end=\"4231\"><p data-start=\"4172\" data-end=\"4231\"><code data-start=\"4172\" data-end=\"4183\">Fibonacci<\/code>: Our custom action definition (<code data-start=\"4215\" data-end=\"4224\">.action<\/code> file).<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b295898 elementor-widget elementor-widget-text-editor\" data-id=\"b295898\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Class Setup<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-dd26523 elementor-widget elementor-widget-code-highlight\" data-id=\"dd26523\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>class SimpleActionClient(Node):\r\n    def __init__(self):\r\n        super().__init__(\"simple_action_client\")\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-75b58e4 elementor-widget elementor-widget-text-editor\" data-id=\"75b58e4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul><li data-start=\"4378\" data-end=\"4436\"><p data-start=\"4380\" data-end=\"4436\">Defines a <strong data-start=\"4390\" data-end=\"4402\">new node<\/strong> named <code data-start=\"4409\" data-end=\"4433\">\"simple_action_client\"<\/code>.<\/p><\/li><li data-start=\"4437\" data-end=\"4527\"><p data-start=\"4439\" data-end=\"4527\">All ROS 2 interactions (logging, publishers, subscribers, actions) happen inside a node.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fe6ee56 elementor-widget elementor-widget-text-editor\" data-id=\"fe6ee56\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Creating the Action Client<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-628397c elementor-widget elementor-widget-code-highlight\" data-id=\"628397c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>self.action_client = ActionClient(self, Fibonacci, \"fibonacci\")\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-93b48b2 elementor-widget elementor-widget-text-editor\" data-id=\"93b48b2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul><li data-start=\"4648\" data-end=\"4713\"><p data-start=\"4650\" data-end=\"4713\">Connects this client to an action server named <code data-start=\"4697\" data-end=\"4710\">\"fibonacci\"<\/code>.<\/p><\/li><li data-start=\"4714\" data-end=\"4756\"><p data-start=\"4716\" data-end=\"4756\">Uses the <code data-start=\"4725\" data-end=\"4736\">Fibonacci<\/code> action interface.<\/p><\/li><li data-start=\"4757\" data-end=\"4830\"><p data-start=\"4759\" data-end=\"4830\">If the server isn\u2019t running, this will wait until it becomes available.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1e13dec elementor-widget elementor-widget-text-editor\" data-id=\"1e13dec\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Sending a Goal<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5fec98e elementor-widget elementor-widget-code-highlight\" data-id=\"5fec98e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>goal = Fibonacci.Goal()\r\ngoal.order = 10\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9d99ff9 elementor-widget elementor-widget-text-editor\" data-id=\"9d99ff9\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Creates a <strong data-start=\"4927\" data-end=\"4943\">goal message<\/strong> asking for Fibonacci sequence of order <code data-start=\"4983\" data-end=\"4987\">10<\/code>.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d767750 elementor-widget elementor-widget-code-highlight\" data-id=\"d767750\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>self.future = self.action_client.send_goal_async(\r\n    goal, feedback_callback=self.feedbackCallback\r\n)\r\nself.future.add_done_callback(self.responseCallback)\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f1d4fc7 elementor-widget elementor-widget-text-editor\" data-id=\"f1d4fc7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul><li data-start=\"5160\" data-end=\"5200\"><p data-start=\"5162\" data-end=\"5200\"><code data-start=\"5162\" data-end=\"5179\">send_goal_async<\/code> sends the request.<\/p><\/li><li data-start=\"5201\" data-end=\"5295\"><p data-start=\"5203\" data-end=\"5295\">We also pass a <strong data-start=\"5218\" data-end=\"5239\">feedback callback<\/strong>, executed whenever the server sends progress updates.<\/p><\/li><li data-start=\"5296\" data-end=\"5374\"><p data-start=\"5298\" data-end=\"5374\">The returned <code data-start=\"5311\" data-end=\"5319\">future<\/code> will tell us if the goal was <strong data-start=\"5349\" data-end=\"5373\">accepted or rejected<\/strong>.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-be4dabe elementor-widget elementor-widget-text-editor\" data-id=\"be4dabe\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Handling Goal Response<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-22cf0ea elementor-widget elementor-widget-code-highlight\" data-id=\"22cf0ea\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>def responseCallback(self, future):\r\n    goal_handle = future.result()\r\n    if not goal_handle.accepted:\r\n        self.get_logger().info(\"Goal rejected :(\")\r\n        return\r\n    self.get_logger().info(\"Goal accepted :)\")\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-87b0967 elementor-widget elementor-widget-text-editor\" data-id=\"87b0967\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul><li data-start=\"5643\" data-end=\"5703\"><p data-start=\"5645\" data-end=\"5703\">The server may reject the request (e.g., invalid order).<\/p><\/li><li data-start=\"5704\" data-end=\"5784\"><p data-start=\"5706\" data-end=\"5784\">Otherwise, we get a <code data-start=\"5726\" data-end=\"5739\">goal_handle<\/code>, which lets us request the <strong data-start=\"5767\" data-end=\"5783\">final result<\/strong>.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ea0f59e elementor-widget elementor-widget-text-editor\" data-id=\"ea0f59e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Feedback Handling<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1404264 elementor-widget elementor-widget-code-highlight\" data-id=\"1404264\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>def feedbackCallback(self, feedback_msg):\r\n    self.get_logger().info(\r\n        f\"Received feedback: {feedback_msg.feedback.partial_sequence}\"\r\n    )\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fb7ce2d elementor-widget elementor-widget-text-editor\" data-id=\"fb7ce2d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul><li data-start=\"5979\" data-end=\"6025\"><p data-start=\"5981\" data-end=\"6025\">Triggered multiple times during execution.<\/p><\/li><li data-start=\"6026\" data-end=\"6082\"><p data-start=\"6028\" data-end=\"6082\">Prints the <strong data-start=\"6039\" data-end=\"6069\">partial Fibonacci sequence<\/strong> as it grows.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0a461ab elementor-widget elementor-widget-text-editor\" data-id=\"0a461ab\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Receiving the Result<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-476d173 elementor-widget elementor-widget-code-highlight\" data-id=\"476d173\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>def resultCallback(self, future):\r\n    result = future.result().result\r\n    self.get_logger().info(f\"Result: {result.sequence}\")\r\n    rclpy.shutdown()\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-04dda64 elementor-widget elementor-widget-text-editor\" data-id=\"04dda64\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul><li data-start=\"6281\" data-end=\"6332\"><p data-start=\"6283\" data-end=\"6332\">Once the server completes, this callback fires.<\/p><\/li><li data-start=\"6333\" data-end=\"6367\"><p data-start=\"6335\" data-end=\"6367\">Prints the <strong data-start=\"6346\" data-end=\"6364\">final sequence<\/strong>.<\/p><\/li><li data-start=\"6368\" data-end=\"6407\"><p data-start=\"6370\" data-end=\"6407\">Shuts down the node since we\u2019re done.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-299bf9e elementor-widget elementor-widget-text-editor\" data-id=\"299bf9e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong>Main Function<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fab3bdd elementor-widget elementor-widget-code-highlight\" data-id=\"fab3bdd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-python\">\n\t\t\t\t<code readonly=\"true\" class=\"language-python\">\n\t\t\t\t\t<xmp>def main():\r\n    rclpy.init()\r\n    action_client = SimpleActionClient()\r\n    rclpy.spin(action_client)\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7a365ec elementor-widget elementor-widget-text-editor\" data-id=\"7a365ec\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ul><li data-start=\"6551\" data-end=\"6573\"><p data-start=\"6553\" data-end=\"6573\">Initializes ROS 2.<\/p><\/li><li data-start=\"6574\" data-end=\"6609\"><p data-start=\"6576\" data-end=\"6609\">Creates the action client node.<\/p><\/li><li data-start=\"6610\" data-end=\"6646\"><p data-start=\"6612\" data-end=\"6646\">Spins until the client shuts down.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e401c61 elementor-widget elementor-widget-text-editor\" data-id=\"e401c61\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h3 data-start=\"6653\" data-end=\"6678\"><strong>\ud83d\udee0\ufe0f Running the Client<\/strong><\/h3><p data-start=\"6680\" data-end=\"6703\">Rebuild your workspace:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-28fef0d elementor-widget elementor-widget-code-highlight\" data-id=\"28fef0d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-bash\">\n\t\t\t\t<code readonly=\"true\" class=\"language-bash\">\n\t\t\t\t\t<xmp>cd ~\/arduinobot_ws\r\ncolcon build\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-948da42 elementor-widget elementor-widget-text-editor\" data-id=\"948da42\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Source it:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-55781f7 elementor-widget elementor-widget-code-highlight\" data-id=\"55781f7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-bash\">\n\t\t\t\t<code readonly=\"true\" class=\"language-bash\">\n\t\t\t\t\t<xmp>. install\/setup.bash\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-567dd68 elementor-widget elementor-widget-text-editor\" data-id=\"567dd68\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/16.0.1\/svg\/1f5a5.svg\" alt=\"\ud83d\udda5\ufe0f\" \/> Terminal 1: Run the action server (from previous article):<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c0b6b30 elementor-widget elementor-widget-code-highlight\" data-id=\"c0b6b30\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-bash\">\n\t\t\t\t<code readonly=\"true\" class=\"language-bash\">\n\t\t\t\t\t<xmp>ros2 run arduinobot_py_examples simple_action_server\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-03fa186 elementor-widget elementor-widget-text-editor\" data-id=\"03fa186\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h5><strong><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/16.0.1\/svg\/1f5a5.svg\" alt=\"\ud83d\udda5\ufe0f\" \/> Terminal 2: Run the client:<\/strong><\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-62b8618 elementor-widget elementor-widget-code-highlight\" data-id=\"62b8618\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-tomorrow copy-to-clipboard\">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-bash\">\n\t\t\t\t<code readonly=\"true\" class=\"language-bash\">\n\t\t\t\t\t<xmp>ros2 run arduinobot_py_examples simple_action_client\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9b426fb elementor-widget elementor-widget-text-editor\" data-id=\"9b426fb\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"7357\" data-end=\"7411\">We now have a working <strong data-start=\"7379\" data-end=\"7403\">Python Action Client<\/strong> that:<\/p><ul data-start=\"7412\" data-end=\"7539\"><li data-start=\"7412\" data-end=\"7456\"><p data-start=\"7414\" data-end=\"7456\">Connects to the Fibonacci Action Server.<\/p><\/li><li data-start=\"7457\" data-end=\"7482\"><p data-start=\"7459\" data-end=\"7482\">Sends a goal request.<\/p><\/li><li data-start=\"7483\" data-end=\"7510\"><p data-start=\"7485\" data-end=\"7510\">Receives live feedback.<\/p><\/li><li data-start=\"7511\" data-end=\"7539\"><p data-start=\"7513\" data-end=\"7539\">Prints the final result.<\/p><\/li><\/ul><p data-start=\"7541\" data-end=\"7665\">This is a foundation for more complex robotics tasks where you need long-running interactions between clients and servers.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-c5d20e7 e-con-full e-flex e-con e-parent\" data-id=\"c5d20e7\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0741169 elementor-bg-transform elementor-bg-transform-move-left elementor-cta--layout-image-left elementor-cta--mobile-layout-image-above elementor-cta--skin-classic elementor-animated-content elementor-widget elementor-widget-call-to-action\" data-id=\"0741169\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"call-to-action.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-cta\">\n\t\t\t\t\t<div class=\"elementor-cta__bg-wrapper\">\n\t\t\t\t<div class=\"elementor-cta__bg elementor-bg\" style=\"background-image: url(https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/06\/manipulation-ros2.webp);\" role=\"img\" aria-label=\"manipulation ros2\"><\/div>\n\t\t\t\t<div class=\"elementor-cta__bg-overlay\"><\/div>\n\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"elementor-cta__content\">\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<h2 class=\"elementor-cta__title elementor-cta__content-item elementor-content-item\">\n\t\t\t\t\t\tWant to learn more?\t\t\t\t\t<\/h2>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<div class=\"elementor-cta__description elementor-cta__content-item elementor-content-item\">\n\t\t\t\t\t\tExplore all the ROS 2 actions in the \"Robotics and ROS 2 - Learn by Doing! Manipulators\" course\t\t\t\t\t<\/div>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<div class=\"elementor-cta__button-wrapper elementor-cta__content-item elementor-content-item\">\n\t\t\t\t\t<a class=\"elementor-cta__button elementor-button elementor-size-\" href=\"\" target=\"_blank\">\n\t\t\t\t\t\tEnroll Now\t\t\t\t\t<\/a>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"elementor-ribbon elementor-ribbon-right\">\n\t\t\t\t<div class=\"elementor-ribbon-inner\">\n\t\t\t\t\tDISCOUNT\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-180426a elementor-widget elementor-widget-spacer\" data-id=\"180426a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"spacer.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-spacer\">\n\t\t\t<div class=\"elementor-spacer-inner\"><\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>In the previous article, we implemented a ROS 2 Action Server in Python that calculates the Fibonacci sequence. We also saw how to interact with it using the ROS 2 CLI (ros2 action send_goal). Now, we\u2019ll take a step further and implement a ROS 2 Action Client in Python.This client node will connect directly to [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":3918,"comment_status":"closed","ping_status":"open","sticky":false,"template":"elementor_header_footer","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[45,43],"tags":[286,292,248,83,87,179,66,120,290,107,72,246,76,288,64],"class_list":["post-3913","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ros-2","category-tutorials","tag-action","tag-action-client","tag-client","tag-cpp","tag-guide","tag-lbd","tag-learn-by-doing","tag-manipulators","tag-plus","tag-ros-2","tag-ros2","tag-server","tag-step-by-step","tag-test","tag-tutorial"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>How to Build and Test a ROS 2 Action Client in Python - Learn by Doing!<\/title>\n<meta name=\"description\" content=\"Explore what Actions are, why they\u2019re different from Topics and Services, and how to build and test a ROS 2 Action Client in Python\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/learnbydoing.dev\/es\/how-to-build-and-test-a-ros-2-action-client-in-python\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Build and Test a ROS 2 Action Client in Python\" \/>\n<meta property=\"og:description\" content=\"Learn by Doing!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/learnbydoing.dev\/es\/how-to-build-and-test-a-ros-2-action-client-in-python\/\" \/>\n<meta property=\"og:site_name\" content=\"Learn by Doing!\" \/>\n<meta property=\"article:published_time\" content=\"2025-08-24T09:34:32+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-10T21:33:57+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"How to Build and Test a ROS 2 Action Client in Python\",\"datePublished\":\"2025-08-24T09:34:32+00:00\",\"dateModified\":\"2026-01-10T21:33:57+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/\"},\"wordCount\":509,\"publisher\":{\"@id\":\"https:\/\/learnbydoing.dev\/es\/#organization\"},\"image\":{\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp\",\"keywords\":[\"Action\",\"action client\",\"client\",\"cpp\",\"Guide\",\"lbd\",\"learn by doing\",\"manipulators\",\"plus\",\"ROS 2\",\"ROS2\",\"server\",\"step by step\",\"Test\",\"tutorial\"],\"articleSection\":[\"ROS 2\",\"Tutorials\"],\"inLanguage\":\"es\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/\",\"url\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/\",\"name\":\"How to Build and Test a ROS 2 Action Client in Python - Learn by Doing!\",\"isPartOf\":{\"@id\":\"https:\/\/learnbydoing.dev\/es\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp\",\"datePublished\":\"2025-08-24T09:34:32+00:00\",\"dateModified\":\"2026-01-10T21:33:57+00:00\",\"description\":\"Explore what Actions are, why they\u2019re different from Topics and Services, and how to build and test a ROS 2 Action Client in Python\",\"breadcrumb\":{\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage\",\"url\":\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp\",\"contentUrl\":\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp\",\"width\":1920,\"height\":1080},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/learnbydoing.dev\/es\/learn-by-doing-es\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Build and Test a ROS 2 Action Client in Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/learnbydoing.dev\/es\/#website\",\"url\":\"https:\/\/learnbydoing.dev\/es\/\",\"name\":\"Learn by Doing!\",\"description\":\"Learn Robotics the fun way\",\"publisher\":{\"@id\":\"https:\/\/learnbydoing.dev\/es\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/learnbydoing.dev\/es\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/learnbydoing.dev\/es\/#organization\",\"name\":\"Learn by Doing!\",\"url\":\"https:\/\/learnbydoing.dev\/es\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/learnbydoing.dev\/es\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/06\/cropped-cropped-cropped-Progetto-senza-titolo-6-1.png\",\"contentUrl\":\"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/06\/cropped-cropped-cropped-Progetto-senza-titolo-6-1.png\",\"width\":512,\"height\":512,\"caption\":\"Learn by Doing!\"},\"image\":{\"@id\":\"https:\/\/learnbydoing.dev\/es\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/antonio-brandi-512166bb\/\"]},{\"@type\":\"Person\",\"@id\":\"\",\"url\":\"https:\/\/learnbydoing.dev\/es\/author\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How to Build and Test a ROS 2 Action Client in Python - Learn by Doing!","description":"Explore what Actions are, why they\u2019re different from Topics and Services, and how to build and test a ROS 2 Action Client in Python","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/learnbydoing.dev\/es\/how-to-build-and-test-a-ros-2-action-client-in-python\/","og_locale":"es_ES","og_type":"article","og_title":"How to Build and Test a ROS 2 Action Client in Python","og_description":"Learn by Doing!","og_url":"https:\/\/learnbydoing.dev\/es\/how-to-build-and-test-a-ros-2-action-client-in-python\/","og_site_name":"Learn by Doing!","article_published_time":"2025-08-24T09:34:32+00:00","article_modified_time":"2026-01-10T21:33:57+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp","type":"image\/webp"}],"twitter_card":"summary_large_image","twitter_misc":{"Escrito por":"","Tiempo de lectura":"4 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#article","isPartOf":{"@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/"},"author":{"name":"","@id":""},"headline":"How to Build and Test a ROS 2 Action Client in Python","datePublished":"2025-08-24T09:34:32+00:00","dateModified":"2026-01-10T21:33:57+00:00","mainEntityOfPage":{"@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/"},"wordCount":509,"publisher":{"@id":"https:\/\/learnbydoing.dev\/es\/#organization"},"image":{"@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage"},"thumbnailUrl":"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp","keywords":["Action","action client","client","cpp","Guide","lbd","learn by doing","manipulators","plus","ROS 2","ROS2","server","step by step","Test","tutorial"],"articleSection":["ROS 2","Tutorials"],"inLanguage":"es"},{"@type":"WebPage","@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/","url":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/","name":"How to Build and Test a ROS 2 Action Client in Python - Learn by Doing!","isPartOf":{"@id":"https:\/\/learnbydoing.dev\/es\/#website"},"primaryImageOfPage":{"@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage"},"image":{"@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage"},"thumbnailUrl":"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp","datePublished":"2025-08-24T09:34:32+00:00","dateModified":"2026-01-10T21:33:57+00:00","description":"Explore what Actions are, why they\u2019re different from Topics and Services, and how to build and test a ROS 2 Action Client in Python","breadcrumb":{"@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#primaryimage","url":"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp","contentUrl":"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/08\/Action-Server-2.webp","width":1920,"height":1080},{"@type":"BreadcrumbList","@id":"https:\/\/learnbydoing.dev\/how-to-build-and-test-a-ros-2-action-client-in-python\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/learnbydoing.dev\/es\/learn-by-doing-es\/"},{"@type":"ListItem","position":2,"name":"How to Build and Test a ROS 2 Action Client in Python"}]},{"@type":"WebSite","@id":"https:\/\/learnbydoing.dev\/es\/#website","url":"https:\/\/learnbydoing.dev\/es\/","name":"Learn by Doing!","description":"Learn Robotics the fun way","publisher":{"@id":"https:\/\/learnbydoing.dev\/es\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/learnbydoing.dev\/es\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/learnbydoing.dev\/es\/#organization","name":"Learn by Doing!","url":"https:\/\/learnbydoing.dev\/es\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/learnbydoing.dev\/es\/#\/schema\/logo\/image\/","url":"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/06\/cropped-cropped-cropped-Progetto-senza-titolo-6-1.png","contentUrl":"https:\/\/learnbydoing.dev\/wp-content\/uploads\/2025\/06\/cropped-cropped-cropped-Progetto-senza-titolo-6-1.png","width":512,"height":512,"caption":"Learn by Doing!"},"image":{"@id":"https:\/\/learnbydoing.dev\/es\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/in\/antonio-brandi-512166bb\/"]},{"@type":"Person","@id":"","url":"https:\/\/learnbydoing.dev\/es\/author\/"}]}},"_links":{"self":[{"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/posts\/3913","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/comments?post=3913"}],"version-history":[{"count":7,"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/posts\/3913\/revisions"}],"predecessor-version":[{"id":5447,"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/posts\/3913\/revisions\/5447"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/media\/3918"}],"wp:attachment":[{"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/media?parent=3913"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/categories?post=3913"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/learnbydoing.dev\/es\/wp-json\/wp\/v2\/tags?post=3913"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}