Lifecycle Node Demo
Last updated
Last updated
This tutorial is based on ROS2 Humbler. The behavior may vary in a different distro.
In this tutorial, we will demonstrate how to trigger a state transition in a lifecycle node. We'll start with a brief introduction to the lifecycle node's states and transitions. Next, we'll clarify a few potentially confusing concepts. Lastly, we'll guide you through a practical example and demonstrate the state transition via the command line and the code.
According to , a lifecycle node has the following states and state transitions:
Lifecycle nodes in ROS2 have two state types: primary states and transition states. Transitioning between primary states requires invoking a specific function. For instance, to move a node from inactive to active, we use the activate
function. This transition can be initiated either from the command line or directly in the code. During the execution of the transition function, the node enters the corresponding transition states. For example, if the activate
function takes 10 seconds to run and we query the node's state during this window, it will return activating
.
Before moving on to our demo, it's important to clarify a few key points.
This section is personal interpretation.
There are two plausible explanations. Firstly, like the start
state, destroyed
is a concept state. This means although we understand the node is destroyed, querying it for its current state is not feasible as the node no longer exists. This explains why the destroyed
state is not a primary state.
Secondly, the destruction of a node involves memory deallocation by an owner. However, not all nodes are under the supervision of a node manager or similar system, making it challenging to determine the recipient of the destroy command in advance.
The demo code will illustrate the transition from the unconfigured
state to the finalized
state. We will manually trigger most transitions via the command line, except for the transition from inactive to active, which happens automatically after a pre-defined delay. Components such as timer, subscriber, and publisher are created in the configuring
state and become functional in the inactive
state. The node will then automatically move to the active
state, where it can receive messages and become ready for operation.
The ready
state is a user-define concept in our demo code. As suggested in the design document, complex software-level initialization logic can be implemented as a user-defined state machine that runs when the node is in the active
state. In our demonstration, the user-defined state machine is trivial: the node transitions from active to ready upon receiving a message from the ready_signal
topic.
Let's first launch the system:
It gives the following results:
There aren't many outputs at this step. We can check the state of the node and it should be in the unconfigured
state:
To transit from the unconfigured
state to the inactive
state, we can invoke the configure
method using the following command:
We will see the following outputs in the terminal where we launched the system:
We can see the on_configured
method is called and the node executes the publish
method at a fixed rate. This shows that the timer is activated when the node is in the inactive state. Check the node state again by running the command below
It confirms that the node is in the inactive
state.
If we publish a message to the read_signal
topic, we can see that the node is able to subscirbe to the topic when it's in the inactive
state. To publish a message, use the following command:
The output confirms the node can receive the message:
We add a small code in the on_configure
method to make the transit from inactive to active automatic:
Once the node reaches the inactive
state, no further commands are required. Depending on the delay specified in the code, simply wait a few seconds and you should observe the following outputs:
If you haven't published a message to the ready_signal
topic, the ready flag of the node remains false. To set the node to ready, you can manually publish a message from the command line:
And you should see the following messages:
©2023 - 2024 all rights reserved
The is implemented in the rclcpp/rclcpp_lifecycle package. However, if you check the code, the State
class is simply a wrapper of the id and label pair. The class does not contain any clue about the mapping between the id and the actual state name described in the design document. The mapping is defined in the message of the . Therefore, the most robust way to check the current state of the lifecycle node in the code is to include lifecycle_msgs/msg/state.hpp
and examine the id
field of the state object.
The design doc seems to suggest that transitioning out of the finalized
state is possible by invoking the destroy
method. Yet, when we set the node to the finalized
state and attempt to invoke the destroy
method, the following results are observed:
When the node is in the finalized
state, it appears that there are no valid transitions available. Indeed, examining the implementation of transition methods in within the package reveals no reference to the destroy
method. This raises the question: what exactly is happening in this scenario?
Note a few details. We use sleep_for
to delay the invocation of the activate
function and this code runs in a separate thread. The detach
is required because we want to separate the execution from the thread object; otherwise, when the execution exits the on_configure
method, the thread object will terminate the program (see ).
The code is available for download at this .