ROS2 - Humble - Action in action with Python

Ever wanted to control a satellite without leaving your comfortable Linux terminal?

Today is your lucky day.

Welcome to this cosmic guide where we are going to build a fully automated ROS2 Action Server and Client in Python.

Grab your coffee, open your IDE, and let's launch this code into the stratosphere.

Shopping list

Let's code a bit

To keep our codebase clean and modular, we split our system into 4 distinct files:

  • Satellite.action
  • utils.py
  • node_server.py
  • node_client.py

Note that the pkg_interface package (folder) was generated with the following command:

ros2 pkg create --build-type ament_cmake pkg_interface --license MIT

That's a convenient way to generate all needed files such as CMakeLists.txt to use Action messages properly.

Satellite.action

Before writing a single line of Python, we need to define the contract between our Client and our Server.

In ROS2, this is done using a .action file.

By convention, the .action files start by an uppercase letter, so it explains why it start with an S.

Think of it as a blueprint divided into three distinct sections separated by triple dashes "---":

  • The Goal
  • The Result
  • The Feedback

utils.py

This is where the magic happens.

We use Python generics TypeVar to make our node runner completely agnostic.

It instantiates the node, attaches a MultiThreadedExecutor (crucial so our threads can breathe), and smoothly handles graceful shutdowns.

node_server.py

Our server listens on the topic_satellite_orbit action lane.

It accepts incoming requests, simulates the orbit step-by-step using a time-delta cadence, and actively checks if the client hit the panic button to abort the mission.

node_client.py

The client fires the goal request as soon as it initializes via our on_ready lambda hook.

It logs incoming live streaming feedback, and features a built-in security watchdog timer: If the mission takes longer than 10.0 seconds, it triggers an asynchronous cancellation order.

You can of course remove this timer to see the mission completed successfully.

Compiling and output

Type the following command from /workspaces/ros2_ws:

colcon build

Then:

source install/setup.bash

And for the server on first terminal:

ros2 run pkg_python node_server

And for the client on another terminal:

ros2 run pkg_python node_client

It will display the following output:

Server node has been created.
[INFO] [1779994391.811315253] [satellite_action_server]: === Satellite Action Server is ready ===
[INFO] [1779994394.456827274] [satellite_action_server]: Received goal request: 3 at 45.0°/s
[INFO] [1779994394.460967381] [satellite_action_server]: Starting orbital simulation...
[INFO] [1779994404.478852478] [satellite_action_server]: Received cancel request from client.
 
And for the client side:
 
Client node has been created.
[INFO] [1779994461.197069167] [satellite_action_client]: Waiting for the server...
[INFO] [1779994461.198452010] [satellite_action_client]: Sending the goal...
[INFO] [1779994461.213140418] [satellite_action_client]: Mission accepted, launching the satellite.
[INFO] [1779994461.214877531] [satellite_action_client]: [Feedback] Revolution: 0, Angle: 4.5°
[INFO] [1779994461.311845369] [satellite_action_client]: [Feedback] Revolution: 0, Angle: 9.0°
[INFO] [1779994461.410032284] [satellite_action_client]: [Feedback] Revolution: 0, Angle: 13.5°
[INFO] [1779994461.510891165] [satellite_action_client]: [Feedback] Revolution: 0, Angle: 18.0°
[INFO] [1779994461.611239112] [satellite_action_client]: [Feedback] Revolution: 0, Angle: 22.5°
...
[INFO] [1779994470.464951503] [satellite_action_client]: [Feedback] Revolution: 1, Angle: 58.5°
[INFO] [1779994470.565180567] [satellite_action_client]: [Feedback] Revolution: 1, Angle: 63.0°
[INFO] [1779994470.665577047] [satellite_action_client]: [Feedback] Revolution: 1, Angle: 67.5°
[INFO] [1779994470.766363310] [satellite_action_client]: [Feedback] Revolution: 1, Angle: 72.0°
[INFO] [1779994470.866544625] [satellite_action_client]: [Feedback] Revolution: 1, Angle: 76.5°
[INFO] [1779994471.406473665] [satellite_action_client]: [Feedback] Revolution: 1, Angle: 81.0°
[WARN] [1779994471.409185671] [satellite_action_client]: Time limit reached!Sending the cancel order...
[WARN] [1779994471.520832780] [satellite_action_client]: Mission stopped! (Status: 5). Time spent: 10.30s
 

Conclusion

That is how you build a rock-solid, production-ready ROS2 setup without breaking a sweat.

By utilizing a wrapper function with advanced Python typing TypeVar, you managed to dodge IDE autocompletion errors entirely.

Plus, using the MultiThreadedExecutor and asynchronous action handling gives your system true real-world concurrency.

Run your server, fire up your client, and watch those orbits spin perfectly.

Good job, you did it cool

 

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.