很多人认为使用ROS在launch文件中使用launch_ros.action.node.Node启动节点只有附加namespace、name、arguments、parameters以及remappings等设置项供节点启动时使用。
在这里,第一要说明的是在ROS中使用launch文件启动节点,不只有使用launch_ros.action.node.Node这一种方式,这个问题在国内讲解ROS使用的相关资料都未有提及,这个问题留在下一次再详细说明。第二要说明的是即使是使用launch_ros.action.Node启动节点,设置项也不只有namespace、name、arguments、parameters、remappings,国内在讲解Node使用的时候也是忽略的,本文主要针对这个问题进行讲解。
从一个launch文件引出
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='turtlesim',
namespace='turtlesim1',
executable='turtlesim_node',
name='sim'
),
Node(
package='turtlesim',
namespace='turtlesim2',
executable='turtlesim_node',
name='sim'
),
Node(
package='turtlesim',
executable='mimic',
name='mimic',
remappings=[
('/input/pose', '/turtlesim1/turtle1/pose'),
('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
]
)
])
这是ROS官网中的一个简单实例,说明了在launch文件中使用Node启动节点,在这个launch文件中使用了package、namespace、executable、name、remappings设置项。
很多人忽略了import中存在的问题,所以首先看一下这里的import问题。
那这里的Node又存在于哪里呢?
首先看一下launch_ros这个package的结构
可以看到在launch_ros这个package中有node.py这个module。注意,这里的node肯定不是前面所提的Node,因为Python区分大小写。
打开node.py这个module,由于这个文件代码很长,只截取需要用到的2个部分,一个是import部分,另外一个的Node类的代码部分。
import部分:
代码部分:
import过程中的疑问
明确了import对象的具体来源之后,这里还存在一个问题,很明显这里在导入时越过node.py这个module,而直接从package中import了类,这是不合理的,那为什么又可以这样import的呢?
打开launch_ros中action文件夹的__init__.py文件
打开之后,可以看到里面的代码。
从这里可以看出,在这里定义了action这个package的公共接口,正因为这里定义了这个__all__公共接口,所以直接通过launch_ros找到Node这个类,但是对于node.py其他函数、类由于没有定义在__all__这个公共接口中,所以就不能越过node.py这个module,import时必须先指示from于node这个module,然后才能import。
题外话:其实在导入tensorflow相关的类和函数时,也会遇到这样的问题,在指示过程中,越过了module,这是因为在__init__.py定义了__all__公共接口,使得可以越过module,但是开发者通常并没有搞清楚__all__公共接口,在使用过程中只时临摹别人的使用方式,如果不知道这一个问题,其实会造成对被导入部分到底是module还是类或函数的混乱,无法准确定位层次关系。
Node中到底存在哪些设置项
要明确采用launch_ros.action.node.Node这种方式,到底采用哪些设置项,就必须回到Node这个类的代码中。
从这里可以看出在Node这个类中定义了executable、package、namespace、name、remmapings等变量,这也就恰好说明了为什么在launch文件中为什么可以对这些设置项进行赋值。
但是这就是所有Node可以赋值的设置项吗?不是!
在这里还可以有这样一句代码:
这说明Node类继承于ExecuteProcess,那ExecuteProcess又在哪里?
下面再看一下node.py的导入部分,有这样一条代码:
这说明ExecuteProcess是在launch.action中的,这已经是另外一个package了,继续找到launch这个package中的action文件夹。
可以看到launch这个package的结构:
打开__init__.py这个文件:
通过这个文件,可以知道ExecuteProcess在execute_process.py这个module中,跟前面一样,因为在__init__.py这个文件中的__all__这个公共接口中添加了ExecuteProcess,所以可以直接import。
打开execute_process.py
从这里看到ExecuteProcess类中有一些成员,由于Node类继承于ExecuteProcess类,所以这些成员依然在Node类中,当然也就可以在Node类使用过程中作为设置项。
此外,ExecuteProcess类又继承自ExecuteLocal类,ExecuteLocal类在execute_local.py中,下面再看一下ExecuteLocal类的代码:
ExecuteLocal类中又有一些成员,由于Node类继承于ExecuteProcess类,ExecuteProcess类又继承于ExecuteLocal类,所以这些成员依然在Node类中,当然也就可以在Node类使用过程中作为设置项。
再看一个launch文件的例子
这个例子是ROS中turtlesim中的一个launch文件,可以看到这里黄色框中的设置项来自于node.py中的Node类的定义,而红色框中的output设置项则是来自execute_local.py中ExecuteLocal类,Node类继承于ExecuteProcess类,ExecuteProcess类又继承于ExecuteLocal类,因此这里使用output设置项没有问题。
在launch文件中通过Node类实现节点启动是launch文件的主要功能之一,无论是ROS官网还是国内相关的资料都没有给出完整的Node类使用中的完整的设置项,之所以没有给出,就是由于太多继承关系,所以可以设置的项目非常多。在这里说明一下,主要设置项有以下几个:pkg、executable、namespace、name、arguments、parameters、remappings以及output,其中pkg和executable是必选项,否则找不到节点,其他的设置项可以根据实际情况使用。