0
$\begingroup$

Rosanswers logo

Whenever I launch a node from python using the roslaunch script api :

#Start roslaunch
launch = roslaunch.scriptapi.ROSLaunch()
launch.start()

# start required nodes
empty_srv_node = roslaunch.core.Node('rostful_node', 'emptyService.py', name='empty_service')
empty_srv_process = launch.launch(empty_srv_node)

I get an error on rospy.init_node() because master is not started :

Unable to register with master node [http://localhost:11311]: master may not be running yet. Will keep trying.

Question : How can I run roscore ( not only rosmaster ) from python if needed ? I would expect roslaunch to do it but it seems it doesnt.

Thank you.


Originally posted by asmodehn on ROS Answers with karma: 131 on 2015-08-11

Post score: 5

$\endgroup$

3 Answers 3

0
$\begingroup$

Rosanswers logo

I don't know the ros way to do it, and personally I use python subbprocess to run the roscore first:

import subprocess
roscore = subprocess.Popen('roscore')
time.sleep(1)  # wait a bit to be sure the roscore is really launched

then your code:

unittest.main()  # for instance in my case I need a roscore to run some node tests

Originally posted by lgeorge with karma: 123 on 2015-12-30

This answer was ACCEPTED on the original site

Post score: 3

$\endgroup$
0
$\begingroup$

Rosanswers logo

If you want to use the subprocess approach you should make sure that all processes are killed cleanly.

import subprocess
import shlex
import sys
import signal
import psutil

def kill_child_processes(parent_pid, sig=signal.SIGTERM):
    try:
        parent = psutil.Process(parent_pid)
        print(parent)
    except psutil.NoSuchProcess:
        print("parent process not existing")
        return
    children = parent.children(recursive=True)
    print(children)
    for process in children:
        print("try to kill child: " + str(process))
        process.send_signal(sig)

class Roscore(object):
    """
    roscore wrapped into a subprocess.
    Singleton implementation prevents from creating more than one instance.
    """
    __initialized = False
    def __init__(self):
        if Roscore.__initialized:
            raise Exception("You can't create more than 1 instance of Roscore.")
        Roscore.__initialized = True
    def run(self):
        try:
            self.roscore_process = subprocess.Popen(['roscore'])
            self.roscore_pid = self.roscore_process.pid  # pid of the roscore process (which has child processes)
        except OSError as e:
            sys.stderr.write('roscore could not be run')
            raise e
    def terminate(self):
        print("try to kill child pids of roscore pid: " + str(self.roscore_pid))
        kill_child_processes(self.roscore_pid)
        self.roscore_process.terminate()
        self.roscore_process.wait()  # important to prevent from zombie process
        Roscore.__initialized = False

Usage is like follows:

roscore = Roscore()
roscore.run()
roscore.terminate()

Originally posted by thinwybk with karma: 468 on 2018-05-28

This answer was NOT ACCEPTED on the original site

Post score: 3


Original comments

Comment by archiparchi on 2022-03-23:
Thank you @thinwybk ! I got to learn a lot from this piece of code. This seems like a very good practice. Here is my upvote :)

$\endgroup$
0
$\begingroup$

Rosanswers logo

I think this is the ROS way to do it:

import roslaunch
uuid = roslaunch.rlutil.get_or_generate_uuid(options_runid=None, options_wait_for_master=False)
roslaunch.configure_logging(uuid)
launch = roslaunch.parent.ROSLaunchParent(uuid, roslaunch_files=[], is_core=True)
launch.start()
#  --- your code ---
launch.shutdown()

You can specify your launch files as a list of string in the ROSLaunchParent constructor. Check the doc if needed.


Originally posted by qgallouedec with karma: 21 on 2020-12-09

This answer was NOT ACCEPTED on the original site

Post score: 2

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.