首页 > 解决方案 > subprocess.popen-process 在与 SMACH 一起使用时停止运行

问题描述

我只是想在 SMACH 中从 python 启动一个 rosbag-command。我发现这样做的一种方法是使用子流程。我的目标是一旦 rosbag 启动,状态机就会转换到状态 T2(并保持在那里)。

但是,当在 SMACH 状态中使用 subprocess.popen 启动 rosbag 然后使用rostopic echo 'topic'时,rosbag 似乎首先正确发布数据,然后突然停止发布数据,只有当我使用 Ctrl+C 结束 SMACH 时,rosbag 才会继续在停止之前发布更多数据。

对此是否有任何合理的解释(我是否可能错过了一个参数,或者只是不可能保持节点以这种方式运行)?或者是否有更好的方法来启动 rosbag 并让其在后台运行?

(顺便说一句,其他一些命令,如一些 roslaunch-commands 在通过 subprocess.popen 启动后似乎停止工作!)

我的代码如下所示:

#!/usr/bin/env python3

import os
import signal
import subprocess
import smach
import smach_ros
import rospy
import time
from gnss_navigation.srv import *

class t1(smach.State):
    def __init__(self, outcomes=['successful', 'failed', 'preempted']):
        smach.State.__init__(self, outcomes)

    def execute(self, userdata):
        if self.preempt_requested():
            self.service_preempt()
            return 'preempted'
        try:
            process1 = subprocess.Popen('rosbag play /home/faps/bags/2020-05-07-11-18-18.bag', stdout=subprocess.PIPE,
                                        shell=True, preexec_fn=os.setsid)
        except Exception:
            return 'failed'
        return 'successful'

class t2(smach.State):
    def __init__(self, outcomes=['successful', 'failed', 'preempted']):
        smach.State.__init__(self, outcomes)

    def execute(self, userdata):
        #time.sleep(2)
        if self.preempt_requested():
            self.service_preempt()
            return 'preempted'
        return 'successful'


if __name__=="__main__":
    rospy.init_node('test_state_machine')

    sm_1 = smach.StateMachine(outcomes=['success', 'error', 'preempted'])
    with sm_1:
        smach.StateMachine.add('T1', t1(), transitions={'successful': 'T2', 'failed': 'error'})
        smach.StateMachine.add('T2', t2(), transitions={'successful': 'T2', 'failed': 'error', 'preempted':'preempted'})

    # Execute SMACH plan
    outcome = sm_1.execute()
    print('exit-outcome:' + outcome)
    # Wait for ctrl-c to stop the application
    rospy.spin()

标签: python-3.xmultiprocessingsubprocesspopenstate-machine

解决方案


如该线程的答案评论部分所述,使用subprocess.PIPE作为stdout时会出现问题。

因此,我用来解决问题的两种可能的解决方案是:

  1. 如果您不关心打印输出和其他东西 -> 使用 devnull 作为输出:

    FNULL = open(os.devnull, 'w')
    process = subprocess.Popen('your command', stdout=FNULL, stderr=subprocess.STDOUT,
                                         shell=True, preexec_fn=os.setsid)
    
  2. 如果您确实需要打印输出和其他东西 -> 创建一个日志文件并将其用作输出:

    log_file = open('path_to_log/log.txt', 'w')
    process = subprocess.Popen('your command', stdout=log_file, stderr=subprocess.STDOUT,
                                        shell=True, preexec_fn=os.setsid)
    

推荐阅读