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:
Registers Executables: It adds the necessary entry points to your package's
setup.pyfile, turning your Python classes into runnable ROS 2 nodes.Generates Launch Files: It creates or updates a launch file in the workspace's
launch/directory, making your node immediately available to run withgenesys runorgenesys 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
MyTalkerclass into a full-fledged ROS 2 node.The string
"my_talker_node"becomes the executable name.It automatically injects a pre-configured
rclpylogger, available asself.logger.
@timer(period_sec=1.0)This decorator creates a ROS 2 timer that calls the
publish_messagemethod 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_messagemethod and publishes it to the/chattertopic. 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
/chattertopic.It registers the
message_callbackmethod as the callback function.When a message arrives, Genesys automatically passes it as the
msgargument 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
@subscriberis obviously a subscription callback.Productivity: You save significant time by not having to manually edit
setup.pyandCMakeLists.txtor 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?
