Skip to content
  • Home
  • About the Blog
  • About the Author
  • Sitemap

Abdur Rosyid's Blog

Just a few notes on mechanical engineering and robotics

Creating ROS2 Action in Python

July 9, 2021 by Abdur Rosyid

Below are the steps to create a ROS2 action using Python. Notice that we are going to have two packages involved: 1) a package called “my_package” which contains the action file, and 2) a package called “action_nodes_python” which contains the action server and client nodes. Separating the package which contains the action file(s) like this, in general, is a good practice.

In the official example, the action server is wrapped in a package and the action client is wrapped in another package. This is also possible. We will do similarly in this tutorial, i.e., the action server and the action client are wrapped in separate packages, namely “action_server_python” and “action_client_python”.

Step 1: Creating an action file. For ROS2 Foxy, the official documentation only provides the creation of an action file in C++. No Python way is provided. Therefore, here we also use C++ to create an action file.

1.1. Create a folder called “action” (if you still don’t have it) inside a package folder called “my_package”. And then make the action file (.action) called “ActionFile.action”. For example, if the action is to compute Fibonacci numbers, you may call it “Fibonacci.action” which looks like below:

#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback
int32[] partial_sequence

The lines of three dashes above separate between the request variable(s), the result variable(s), and the feedback variable(s).

1.2. Add the following in package.xml of “my_package” package.

rosidl_default_generators
action_msgs
rosidl_interface_packages

1.3. Add the following in CMakeLists.txt of “my_package” package, before ament_package().

1
2
3
find_package(rosidl_default_generators REQUIRED)
 
rosidl_generate_interfaces(${PROJECT_NAME} "action/Fibonacci.action")

1.4. Build the package by using “colcon build” command.

Step 2: Write the “action_server” node in Python, wrapped in the action_server package. See an example package here: https://github.com/ros2/examples/tree/foxy/rclpy/actions/minimal_action_server

2.1. Make a Python package called “action_server_python”.

2.2. Check package.xml. Modify if necessary, particularly regarding the dependency packages.

2.3. Modify setup.py. Add the following:

    entry_points={
        'console_scripts': [
            'server = ' + package_name + '.my_action_server:main',
        ],
    },

2.4. Write the action_server node in a file namely “my_action_server.py”, in the package subfolder inside the action_server package folder.

 import time
 
 import rclpy
 from rclpy.action import ActionServer
 from rclpy.node import Node
 
 from my_package.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):
        self.get_logger().info('Executing goal...')

        feedback_msg = Fibonacci.Feedback()
        feedback_msg.partial_sequence = [0, 1]

        for i in range(1, goal_handle.request.order):
            feedback_msg.partial_sequence.append(
                feedback_msg.partial_sequence[i] + feedback_msg.partial_sequence[i-1])
            self.get_logger().info('Feedback: {0}'.format(feedback_msg.partial_sequence))
            goal_handle.publish_feedback(feedback_msg)
            time.sleep(1)

        goal_handle.succeed()

        result = Fibonacci.Result()
        result.sequence = feedback_msg.partial_sequence
        return result

2.5. Build the package by using “colcon build” command.

Step 3: Write the “action_client” node in Python, wrapped in the action_client package. See an example package here: https://github.com/ros2/examples/tree/foxy/rclpy/actions/minimal_action_client

3.1. Make a Python package called “action_client_python”.

3.2. Modify package.xml. Modify if necessary, particularly regarding the dependency packages.

3.3. Modify setup.py. Add the following:

    entry_points={
        'console_scripts': [
            'client = ' + package_name + '.my_action_client:main',
        ],
    },

3.4. Write the action_client node in a file namely “my_action_client.py”, in the package subfolder inside the action_client package folder.

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

from my_package.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 get_result_callback(self, future):
        result = future.result().result
        self.get_logger().info('Result: {0}'.format(result.sequence))
        rclpy.shutdown()

    def feedback_callback(self, feedback_msg):
        feedback = feedback_msg.feedback
        self.get_logger().info('Received feedback: {0}'.format(feedback.partial_sequence))


def main(args=None):
    rclpy.init(args=args)

    action_client = FibonacciActionClient()

    action_client.send_goal(10)

    rclpy.spin(action_client)


if __name__ == '__main__':
    main()

3.5. Build the package by using “colcon build” command.

Example of action_server and action_client nodes in Python can be found here: https://docs.ros.org/en/foxy/Tutorials/Actions/Writing-a-Py-Action-Server-Client.html

Running the action

Type the following in the terminal to run the “action_server” node:

ros2 run action_server_python my_action_server.py

Type the following in the terminal to run the “action_client” node:

ros2 run action_client_python my_action_client.py

To show the stream of the action feedback:

ros2 topic echo /action_name/feedback

Official documentation:

  • https://docs.ros.org/en/foxy/Tutorials/Actions/Creating-an-Action.html
  • https://docs.ros.org/en/foxy/Tutorials/Actions/Writing-a-Py-Action-Server-Client.html

Post navigation

Previous Post:

Creating ROS2 Action in C++

Next Post:

ROS1 vs ROS2: What Are Differences?

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Categories

  • STEM 101
  • Robotics
  • Kinematics
  • Dynamics
  • Control
  • Robot Operating System (ROS)
  • Robot Operating System (ROS2)
  • Software Development
  • Mechanics of Materials
  • Finite Element Analysis
  • Fluid Mechanics
  • Thermodynamics

Recent Posts

  • Pull Request on Github
  • Basics of Git and Github
  • Conda vs Docker
  • A Conda Cheat Sheet
  • Installing NVIDIA GPU Driver on Ubuntu

Archives

  • June 2025
  • July 2021
  • June 2021
  • March 2021
  • September 2020
  • April 2020
  • January 2015
  • April 2014
  • March 2014
  • March 2012
  • February 2012
  • June 2011
  • March 2008
© 2026 Abdur Rosyid's Blog | WordPress Theme by Superbthemes