The Genesys way

The Genesys Way: Declarative ROS 2 with Decorators

"The Genesys Way" is a development philosophy centered on a single goal: letting you focus on your robot's logic, not on boilerplate and configuration.

Instead of manually writing verbose setup code for every node, publisher, and subscriber, Genesys provides a clean, declarative API using Python decorators. You simply "decorate" your classes and methods to describe their function, and the framework handles the rest.

How It Works: The Magic Behind the Scenes

When you run genesys build or a genesys make * command, the framework's auto-generation engine scans your Python files for Genesys decorators (@node, @publisher, etc.).

Based on what it finds, it automatically performs tasks you would otherwise do by hand:

  1. Registers Executables: It adds the necessary entry points to your package's setup.py file, turning your Python classes into runnable ROS 2 nodes.

  2. Generates Launch Files: It creates or updates a launch file in the workspace's launch/ directory, making your node immediately available to run with genesys run or genesys launch.

This means your node is ready to use the moment you finish writing its logic, with zero manual configuration.


Example 1: A Simple Publisher Node

Let's look at the standard publisher node generated by genesys make node. It's a perfect example of the Genesys Way in action.

from genesys.decorators import node, timer, publisher
from genesys.helpers import spin_node
from std_msgs.msg import String

@node("my_talker_node")
class MyTalker:
    def __init__(self):
        self.counter = 0
        # A `self.logger` instance is automatically injected by @node!
        self.logger.info("Talker node has been initialized.")

    @timer(period_sec=1.0)
    @publisher(topic="chatter", msg_type=String)
    def publish_message(self):
        """
        This method runs every second. The String it returns is
        automatically published to the 'chatter' topic.
        """
        msg = String()
        msg.data = f"Hello from Genesys! Message #{self.counter}"
        self.logger.info(f'Publishing: "{msg.data}"')
        self.counter += 1
        return msg

def main(args=None):
    # The spin_node helper initializes the class and keeps the node alive.
    spin_node(MyTalker, args)

if __name__ == '__main__':
    main()

Decorator Breakdown:

  • @node("my_talker_node")

    • This is the most important decorator. It transforms the MyTalker class into a full-fledged ROS 2 node.

    • The string "my_talker_node" becomes the executable name.

    • It automatically injects a pre-configured rclpy logger, available as self.logger.

  • @timer(period_sec=1.0)

    • This decorator creates a ROS 2 timer that calls the publish_message method once every second.

  • @publisher(topic="chatter", msg_type=String)

    • This powerful decorator creates a ROS 2 publisher.

    • It automatically takes the return value of the publish_message method and publishes it to the /chatter topic. You don't need to create a publisher object or call .publish() yourself.


Example 2: A Simple Subscriber Node

Creating a subscriber is just as easy. You define a method to handle incoming messages and decorate it appropriately.

from genesys.decorators import node, subscriber
from genesys.helpers import spin_node
from std_msgs.msg import String

@node("my_listener_node")
class MyListener:
    def __init__(self):
        self.logger.info("Listener node has been initialized.")

    @subscriber(topic="chatter", msg_type=String)
    def message_callback(self, msg):
        """
        This method is called whenever a message is received on the 'chatter' topic.
        The message is automatically passed as an argument.
        """
        self.logger.info(f'I heard: "{msg.data}"')

def main(args=None):
    spin_node(MyListener, args)

if __name__ == '__main__':
    main()

Decorator Breakdown:

  • @node("my_listener_node")

    • Just like before, this registers the class as a ROS 2 node and provides a logger.

  • @subscriber(topic="chatter", msg_type=String)

    • This decorator creates a ROS 2 subscription to the /chatter topic.

    • It registers the message_callback method as the callback function.

    • When a message arrives, Genesys automatically passes it as the msg argument to your method.

Summary: The Benefits

Adopting "The Genesys Way" offers several key advantages:

  • Readability: Your code clearly declares its purpose. A method decorated with @subscriber is obviously a subscription callback.

  • Productivity: You save significant time by not having to manually edit setup.py and CMakeLists.txt or create launch files for every new node.

  • Reduced Errors: Automation eliminates common mistakes like forgetting to add an entry point or sourcing a workspace.

  • Consistency: All nodes are created, registered, and launched in a standardized way, making projects easier to maintain.

Last updated

Was this helpful?