python-3.x - 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()
解决方案
如该线程的答案评论部分所述,使用subprocess.PIPE作为stdout时会出现问题。
因此,我用来解决问题的两种可能的解决方案是:
如果您不关心打印输出和其他东西 -> 使用 devnull 作为输出:
FNULL = open(os.devnull, 'w') process = subprocess.Popen('your command', stdout=FNULL, stderr=subprocess.STDOUT, shell=True, preexec_fn=os.setsid)
如果您确实需要打印输出和其他东西 -> 创建一个日志文件并将其用作输出:
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)
推荐阅读
- python - chrome 88 无法重新定义 webdriver
- sql - 我的 cs50 pset 7 10.sql 嵌套查询哪里出错了?
- c# - 在 DbContext 的 OnConfiguration 中使用 connectionString
- javascript - 事件的自定义视图
- html - 如何使用 css 在链接旁边添加文件格式图标
- c# - AppCenter:使用 API 为 iOS 构建配置上传证书文件
- c# - 将多个字符串和 ObservableCollection 传递并绑定到 RefreshView / CollectionView?
- azure-blob-storage - 如何测试 Azure 存储生命周期?
- php - 如何将文件上传到两个不同的文件夹
- autodesk-forge - 如何获取 Autodesk Forge SFV2 格式的模型视图元数据?