Feature Request: LaunchService wrapper for easy non-blocking launch and shutdown
KKSTB opened this issue · 5 comments
Feature request
Feature description
LaunchService
has puzzled many people as to how to properly launch a LaunchDescription
without blocking the main thread:
#210
https://answers.ros.org/question/321118/ros2-nodes-occasionally-dying-using-launchservice-in-a-subprocess/
#126
So I propose either adding a start()
function to LaunchService
, or a new wrapper class, that spawns a daemon process to run the async launch loop, like this:
import asyncio
import multiprocessing
from launch import LaunchDescription, LaunchService
class Ros2LaunchParent:
def start(self, launch_description: LaunchDescription):
self._stop_event = multiprocessing.Event()
self._process = multiprocessing.Process(target=self._run_process, args=(self._stop_event, launch_description), daemon=True)
self._process.start()
def shutdown(self):
self._stop_event.set()
self._process.join()
def _run_process(self, stop_event, launch_description):
loop = asyncio.get_event_loop()
launch_service = LaunchService()
launch_service.include_launch_description(launch_description)
launch_task = loop.create_task(launch_service.run_async())
loop.run_until_complete(loop.run_in_executor(None, stop_event.wait))
if not launch_task.done():
asyncio.ensure_future(launch_service.shutdown(), loop=loop)
loop.run_until_complete(launch_task)
Implementation considerations
Besides launching a LaunchDescription
, it would be better if there is another mode of launching an individual node and get its PID to operate the process, just like ROS1 does.
HI @KKSTB, I am trying to replicate what you described in this issue, but for me this code is still blocking. Can you help me out?
This is what I have so far:
def start_node_process(self, launch_description: LaunchDescription):
self._stop_event = multiprocessing.Event()
self._process = multiprocessing.Process(
target=self._run_process,
args=(self._stop_event, launch_description),
daemon=True
)
self._process.start()
def _run_process(self, stop_event, launch_description):
loop = asyncio.get_event_loop()
launch_service = LaunchService()
launch_service.include_launch_description(launch_description)
launch_task = loop.create_task(launch_service.run_async())
loop.run_until_complete(loop.run_in_executor(None, stop_event.wait))
if not launch_task.done():
asyncio.ensure_future(launch_service.shutdown(), loop=loop)
loop.run_until_complete(launch_task)
def start_ros_node(self, node_dict):
node = launch_ros.actions.Node(**node_dict)
self.start_node_process(LaunchDescription([node]))
However, when I call start_ros_node
it still blocks and never returns
Thanks in advance!
Hi @Rezenders. I used it in a large project to launch many other python launch files without problem. Maybe you can try the followings to see what's wrong:
- Just like what I do, use launch description that includes a python launch file that launches something e.g. a talker node
- Just like what you do, use launch description to launch a node, but launches a talker node