ROS中roslaunch启动节点报process has died但rosrun运行正常
问题根因
两个核心问题导致roslaunch启动节点时触发进程退出,rosrun单独启动可正常运行——本质是rosrun启动时通常会等所有依赖节点、话题、参数全部就绪再运行自定义节点,而roslaunch默认并行启动所有节点,叠加代码本身的执行顺序缺陷:
- 参数获取顺序错误
代码中调用rospy.get_param("~map_file")的位置在rospy.init_node('position_tracker')之前。roslaunch中在<node>标签下配置的<param>属于节点私有命名空间参数,必须等节点初始化完成、私有命名空间注册到ROS master之后才能正常读取,提前调用会直接抛出ParamNotFound异常导致进程退出。使用rosrun启动时,一般是提前启动了其他节点加载了全局同名参数,或是手动传入了参数,不会触发该报错。 - odom话题频率获取逻辑无容错,和启动时序不匹配
代码仅写死1秒的rospy.sleep(1)等待odom话题发布,但roslaunch并行启动gazebo仿真、turtlebot3驱动节点时,1秒内/odom话题大概率还没开始输出消息,此时r.get_hz('/odom')会返回None,直接对None做下标取[0]会抛出TypeError: 'NoneType' object is not subscriptable导致进程退出。用rosrun启动时,通常是等仿真、驱动完全运行、/odom正常输出之后才启动自定义节点,此时get_hz可以正常拿到频率值,不会触发报错。
修复方案
1. 调整代码执行顺序,增加必要容错
修改核心代码段如下:
if __name__=="__main__": global odom_hz # 必须先初始化节点,再读取私有参数 rospy.init_node('position_tracker') map_file = rospy.get_param("~map_file") odom_topic_name = "odom" pub_point = rospy.Publisher('realpoints_marker', MarkerArray, queue_size=1) tf_sub=tf.TransformListener() # 主动等待odom话题出现,替代硬编码等待时长 rospy.loginfo("Waiting for odom topic...") rospy.wait_for_message("/odom", Odometry, timeout=10) r = rostopic.ROSTopicHz(-1) s=rospy.Subscriber('/' + odom_topic_name, Odometry, r.callback_hz, callback_args='/odom') # 循环等待频率统计结果,避免空值报错 odom_hz = None rate = rospy.Rate(10) while not rospy.is_shutdown() and odom_hz is None: hz_res = r.get_hz('/' + odom_topic_name) if hz_res is not None: odom_hz = int(hz_res[0]) break rospy.loginfo("Waiting for odom frequency stat...") rate.sleep() s.unregister() if odom_hz is None: rospy.logerr("Cannot get odom topic frequency, exit") exit(1) rospy.Subscriber("/odom",Odometry, callback) rospy.spin()
2. (可选)优化launch文件启动逻辑
调整节点启动顺序,给自定义节点加适当延迟,同时开启屏幕日志输出方便调试:
<launch> <!-- 优先启动仿真、驱动、键盘控制节点 --> <include file="$(find turtlebot3_gazebo)/launch/turtlebot3_world.launch" /> <include file="$(find turtlebot3_bringup)/launch/turtlebot3_model.launch"/> <node pkg="teleoperat1" type="key_to_twist.py" name="keys_to_twist" output="screen"/> <node pkg="teleoperat1" type="keyboard_drive.py" name="keyboard_driver" output="screen"/> <!-- 最后启动自定义定位节点,加3秒延迟等待所有依赖话题就绪 --> <node pkg="just_for_learning" type="pointstamp.py" name="position_tracker" output="screen" launch-prefix="bash -c 'sleep 3; $0 $@'"> <param name="map_file" value="$(find robot_laser_grid_mapping)/maps/map.yaml" /> </node> </launch>
调试节点时给对应
<node>标签加output="screen"属性,可以直接在终端看到节点的报错日志,不用翻ROS日志文件,排查效率高很多。
内容的提问来源于stack exchange,提问作者peyman movlani
相关产品推荐
相关产品推荐

