首页 关于
树枝想去撕裂天空 / 却只戳了几个微小的窟窿 / 它透出天外的光亮 / 人们把它叫做月亮和星星
目录

ROSLaunchParent

上一节的最后,我们看到 roslaunch 构建了一个 ROSLaunchParent 类型的对象。 下面这段是从官方文档中摘录的一段话,大体意思是说,ROSLaunch 通过客户端-服务器的模式提供远端进程运行服务。 运行在本地的roslaunch被称为"parent",负责本地进程的管理;运行在远端的被称为"child",负责管理远端的进程;"parent"通过"child"的代理可以在远端开启新的进程并提供服务。

ROSLaunch has a client/server architecture for running remote processes. When a user runs roslaunch, this creates a "parent" roslaunch process, which is responsible for managing local processes. This parent process will also start "child" processes on remote machines. The parent can then invoke methods on this child process to launch remote processes, and the child can invoke methods on the parent to provide feedback.

成员变量 数据类型 描述
config ROSLaunchConfig roslaunch的配置器对象
runner ROSLaunchRunner roslaunch的执行器对象
server ROSLaunchParentNode parent模式下的XML-RPC服务器
pm ProcessMonitor 进程监视器
process_listeners ProcessListener 进程listener配合ProcessMonitor使用。
remote_runner ROSRemoteRunner 远程进程执行器。

1. roslaunch.parent

文件parent.py主要实现了类 ROSLaunchParent,它代表了运行在本地的 'parent' 进程, 用来加载 launch 文件,分配 'child' 计算机(assigning machines),然后开启各个进程。右侧列举了类 ROSLaunchParent 的主要成员变量。这个类的构造函数 __init__ 我们就不展开了, 它主要在给这些成员变量赋初值。

上一节中,我们接触roslaunch的main的时候看到,先后调用了 ROSLaunchParent 对象的start和spin两个接口完成了roslaunch的运行。

下面左侧的代码是接口 start 的实现。该函数完成了三项工作:

  1. 通过接口 _start_infrastructure 建立基础设施,包括加载配置、构建进程监视器(pm)、构建XMLRPC服务器、开启远程服务。
  2. 开启 runner 用于建立新的进程,并运行 launch 文件中记录的节点。
  3. 添加远程进程的监听器,当远程机器跪掉的时候,可以及时的做出响应。
右侧的代码则分别是接口 _start_infrastructure 和 _stop_infrastructure 的实现。

        def start(self, auto_terminate=True):
            # load config, start XMLRPC servers and process monitor
            try:
                self._start_infrastructure()
            except:
                self._stop_infrastructure()
                raise

            # Initialize the actual runner. 
            self._init_runner()
            self.runner.launch()

            # inform process monitor process registration done
            if auto_terminate:
                self.pm.registrations_complete()
        
            if self.process_listeners:
                for l in self.process_listeners:
                    self.runner.pm.add_process_listener(l)
                    # Add listeners to server as well, 
                    # otherwise they won't be called when a
                    # node on a remote machine dies.
                    self.server.add_process_listener(l)
        def _start_infrastructure(self):
            if self.config is None:
                self._load_config()
            # Start the process monitor
            if self.pm is None:
                self._start_pm()
            # Startup the roslaunch runner and XMLRPC server.
            if self.server is None:
                self._start_server()
            # Startup the remote infrastructure.
            self._start_remote()
        def _stop_infrastructure(self):
            if self._shutting_down:
                return
            self._shutting_down = True
            if self.server:
                try:
                    self.server.shutdown("roslaunch parent complete")
                except:
                    pass
            if self.pm:
                self.pm.shutdown()
                self.pm.join()

在接口 _start_infrastructure 中先后调用了_load_config, _start_pm, _start_server, _start_remote 四个接口。 这四个接口都很简短。下面的代码是前三个接口的实现,它们分别通过 roslaunch.config, roslaunch.pmon 和 roslaunch.server 三个模块构建了ROSLaunchConfig、ProcessMonitor和ROSLaunchParentNode三个对象。 我们会在后文中依次介绍这三个模块。

        def _load_config(self):
            self.config = roslaunch.config.load_config_default(self.roslaunch_files, self.port, roslaunch_strs=self.roslaunch_strs, verbose=self.verbose)
            # 略去一些不太紧要的配置
        def _start_pm(self):
            self.pm = roslaunch.pmon.start_process_monitor()
        def _start_server(self):
            # 省略检查 self.config, self.pm 的相关语句
            self.server = roslaunch.server.ROSLaunchParentNode(self.config, self.pm)
            self.server.start()
            if not self.server.uri:
                raise RLException("server URI did not initialize")

_start_remote目前对我们来说没什么意义,因为我们目前关注的是运行在单机上的ROS。但还是列在了这里,它先检查如果尚未构建远程的runner,就 _init_remote 一个。然后触发远程runner开启一个子节点。

        def _start_remote(self):
            if self.remote_runner is None:
                self._init_remote()
            if self.remote_runner is not None:
                self.remote_runner.start_children()
        def _init_remote(self):
            # 省略检查 self.config, self.pm, self.server 相关的语句
            if not self.local_only and self.config.has_remote_nodes():
                import roslaunch.remote
                self.remote_runner = roslaunch.remote.ROSRemoteRunner(self.run_id, self.config, self.pm, self.server, sigint_timeout=self.sigint_timeout, sigterm_timeout=self.sigterm_timeout)
        def _init_runner(self):
            # 省略前置条件的检查, 构造函数的参数也一并省略了
            self.runner = roslaunch.launch.ROSLaunchRunner(...)

接口 _init_runner 构建了一个 ROSLaunchRunner 的对象,它用于加载并运行ROS的节点,也就是开启新的进程运行对应的应用程序。 紧接着调用runner的launch接口,用于分配宿主机器,开启rosmaster和core.xml中定义的节点。

成员变量 数据类型 描述
logger Logger python的日志模组logging构建出来的对象,用于输出日志的。
run_id str 运行roslaunch的唯一ID,UUID。
roslaunch_files [str] roslaunch需要加载的配置文件列表。
roslaunch_strs [str]
is_core bool 是否以roscore的形式运行roslaunch
is_rostest bool 是否以rostest的形式运行roslaunch
port int ROS Master的服务端口
local_only bool 是否只在本地提供服务
verbose bool 打印日志的时候是不是啰里啰嗦的
show_summary bool roslaunch成功运行结束之后是否输出摘要
num_workers int 如果以roscore的形式运行roslaunch,则指定了工作线程的数量
timeout float 如果以roscore的形式运行roslaunch,则指定了套接字的超时配置
sigint_timeout float 信号SIGINT的超时配置
sigterm_timeout float 信号SIGTERM的超时配置
master_logger_level str 声明了roscore的rosmaster.master的日志等级
force_screen bool
force_log bool
force_required bool
_shutting_down bool 关闭标志,防止尝试重复关闭



Copyright @ 高乙超. All Rights Reserved. 京ICP备16033081号-1