If you only need to specify the order, then launch will already respect the order you specify nodes in. But if you need to have node1 wait for until nodes 2, 3, and 4 are present and running then read on.
I'm not sure exactly what criteria you have for "when a node starts", so I'm just going to use the built-in events. If you require more specific start-up conditions, you may have to write your own event, event handler, and event emitter.
Here's my python launch script to have node1 started only after nodes 2, 3, and 4 have been started. In this example "started" means that launch has processed that action, and the ProcessStarted
event has been emitted.
from rclpy.node import Node
from launch import LaunchContext, LaunchDescription, LaunchService
from launch.actions import RegisterEventHandler
from launch.events.process import ProcessStarted
from launch.event_handlers.on_process_start import OnProcessStart
from launch_ros.actions import Node
def generate_launch_description():
node1 = Node(package='demo_nodes_cpp',
executable='talker',
name='node1',
exec_name='node1')
node2 = Node(package='demo_nodes_cpp',
executable='listener',
name='node2',
exec_name='node2')
node3 = Node(package='demo_nodes_cpp',
executable='listener',
name='node3',
exec_name='node3')
node4 = Node(package='demo_nodes_cpp',
executable='listener',
name='node4',
exec_name='node4')
already_started_nodes = set()
def start_next_node(event: ProcessStarted, context: LaunchContext):
print(f'node {event.process_name} started.')
already_started_nodes.update([event.process_name])
if len(already_started_nodes) == 3:
print(f'all required nodes are up, time to start node1')
return node1
return LaunchDescription([
RegisterEventHandler(event_handler=OnProcessStart(target_action=node2,
on_start=start_next_node)),
RegisterEventHandler(event_handler=OnProcessStart(target_action=node3,
on_start=start_next_node)),
RegisterEventHandler(event_handler=OnProcessStart(target_action=node4,
on_start=start_next_node)),
node2,
node3,
node4,
])
if __name__ == '__main__':
ls = LaunchService()
ls.include_launch_description(generate_launch_description())
ls.run()
The three RegisterEventHandler
actions will call start_next_node
after the specific node has been started. Once three nodes have already been started, then we'll start node1. We could do something to look at which node actually was started, So if we needed node3 to wait until node2 to start, and node 4 to wait for node3 to start, we could do that kind of logic there.
Example output:
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [node2-1]: process started with pid [5499]
[INFO] [node3-2]: process started with pid [5500]
[INFO] [node4-3]: process started with pid [5501]
node node2-1 started.
node node3-2 started.
node node4-3 started.
all required nodes are up, time to start node1
[INFO] [node1-4]: process started with pid [5502]
[node1-4] [INFO] [1679095729.989045466] [node1]: Publishing: 'Hello World: 1'
[node4-3] [INFO] [1679095729.990325048] [node4]: I heard: [Hello World: 1]
[node3-2] [INFO] [1679095729.990340845] [node3]: I heard: [Hello World: 1]
[node2-1] [INFO] [1679095729.990431971] [node2]: I heard: [Hello World: 1]
[node1-4] [INFO] [1679095730.988866943] [node1]: Publishing: 'Hello World: 2'
[node3-2] [INFO] [1679095730.989625772] [node3]: I heard: [Hello World: 2]
[node4-3] [INFO] [1679095730.989626316] [node4]: I heard: [Hello World: 2]
[node2-1] [INFO] [1679095730.989626457] [node2]: I heard: [Hello World: 2]