pythonsubprocessrosrospy

How to keep ROS publisher publishing while executing a subprocess callback?


How can I keep the ROS Publisher publishing the messages while calling a sub-process:

import subprocess
import rospy

class Pub():
    def __init__(self):
        pass

    def updateState(self, msg):
     cmd = ['python3', planner_path, "--alias", search_options, "--plan-file", plan_path, domain_path, problem_path]

    subprocess.run(cmd, shell=False, stdout=subprocess.PIPE)
    self.plan_pub.publish(msg)

    def myPub(self):
        rospy.init_node('problem_formulator', anonymous=True)
        self.plan_pub = rospy.Publisher("plan", String, queue_size=10)
        rate = rospy.Rate(10) # 10hz
        rospy.Subscriber('model', String, updateState)
        rospy.sleep(1)
        rospy.spin()

if __name__ == "__main__":
    p_ = Pub()
    p_.myPub()

Solution

  • Since subprocess.call is a blocking call your subscription callback may take a long time.

    Run the command described by args. Wait for command to complete, then return the returncode attribute.

    ROS itself will not call the callback again while it is executed already. This means you are blocking this and potentially also other callbacks to be called in time.

    The most simple solution would be to replace subprocess.call by subprocess.Popen which

    Execute a child program in a new process

    nonblocking.

    But keep in mind that this potentially starts the process multiple times quite fast.

    Think about starting the process only conditionally if not already running. This can be achieved by checking the process to be finished in another thread. Simple but effective, use boolean flag. Here is a small prototype:

    def updateState(self, msg):
        #Start the process if not already running
        if not self._process_running:
            p = subprocess.Popen(...)
            self._process_running = True
            def wait_process():
                while p.poll() is None:
                    time.sleep(0.1)
                self._process_running = False
            threading.Thread(target=wait_process).start()
        #Other callback code
        self.plan_pub.publish(msg)