[ROS 2] How to start and stop a node from a Python script?
Context: I am running a camera using ROS. To facilitate data collection I integrated a PySimpleGui graphical user interface inside a Node, so I can start streaming, recording or playing back data through a user-friendly interface.
Expected behaviour: Therefore, my goal is to execute a Node, when the user clicks on a button. For instance, if the following minimal example, I want to start a publisher Node when the user clicks on the button 'Talk'. And I want to start a subscriber Node when the user clicks on 'Listen'.
Platform : I run ROS2 humble on WSL2 - Ubuntu 22.04.1 LTS
Code: Here is GUI publisher
, which will publish a string corresponding to the button name to the topic \gui_command when the user clicks on a button.
import PySimpleGUI as sg
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
def generate_layout():
layout = [[sg.Button('Talk'), sg.Button('Listen'), sg.Button('Exit')]]
return layout
class MinimalGuiPublisher(Node):
def __init__(self):
super().__init__('gui')
# Initialization of the GUI
self.layout = generate_layout()
self.window = sg.Window('Camera recording', self.layout, location=(800, 400), finalize=True)
self.values, self.event = None, ''
# Initialization of the publisher
self.publisher_ = self.create_publisher(String, 'gui_command', 10)
timer_period = 0.2
self.timer = self.create_timer(timer_period, self.gui_callback)
def gui_callback(self):
msg = String()
# Check for any event
self.event, self.values = self.window.read()
print(self.event)
msg.data = str(self.event)
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
if self.event in ('Exit', None):
print('User clicked on Exit')
exit()
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalGuiServer()
rclpy.spin(minimal_publisher)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Here is the GUI subscriber
that listens to the command through subscription to the topic \gui_command.
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalGuiClient(Node):
def __init__(self):
super().__init__('gui_client')
self.subscription = self.create_subscription(
String,
'gui_command',
self.listener_callback,
10)
self.subscription # prevent unused variable warning
def listener_callback(self, msg):
recorder = None
self.get_logger().info('User clicked on: "%s"' % msg.data)
if msg.data in ('Exit', 'None'):
exit()
elif msg.data == 'Talk':
###### Here start the talker like it would with
###### ros2 run py_pubsub talker
pass
elif msg.data == 'Listen':
###### Here start the talker like it would with
###### ros2 run py_pubsub listener
pass
def main(args=None):
rclpy.init(args=args)
minimal_subscriber = MinimalGuiClient()
rclpy.spin(minimal_subscriber)
minimal_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Question : Is it possible to program the execution of a Node from another Node? If yes, could you please indicate which is the correct way to do so?
I tried to read the doc, and thought about using ros2launch
(edited). But it is not clear to me if and how a node or a launch can be triggered by user interaction. Your help would be very much appreciated !!