一、在setup.py文件中添加配置
1-data_files列表
对于通过python构建的ros2功能包,ros2自带编译器会把data_files这一python列表的元素所对应的文件复制到工作空间install文件夹下。所以,添加launch文件夹并加入launch.py文件时,需要将这一launch文件对应路径添加到data_files列表。先介绍一下data_files列表。
from setuptools import setup
package_name = 'launch_test'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='ros2_mobile',
maintainer_email='ros2_mobile@todo.todo',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
],
},
)
以上是原始的setup.py文件,注意到data_files列表是setup元组中的一个元素。
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
]
data_files列表的每一个元素都是一个元组。元组的第一个元素是源文件被编译器复制后的目标位置,第二个元素是一个列表,该列表的各个元素即为所要复制的源文件的位置。注意,前者的相对位置是install目录下对应功能包位置;后者的相对位置是src目录下的对应功能包位置。
如,package.xml的绝对路径为
your_workspace/src/launch_test
最终它会被复制到如下的目录中
your_worksapce/install/launch_test/share/launch_test
(其中,your_worksapce是工作空间名称,launch_test是功能包名称)
2-将launch文件添加到data_files列表里
按照date_files元组的规范,将launch文件的依赖添加进去
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
('share/'+package_name+'/launch',['launch' + '/testlaunch.launch.py'])
]
假设我们在launch文件夹里的launch.py文件名称为testlaunch.launch.py,那么应该按照如上的方式添加。注意,尽管我们只添加了一个元素,但必须把它放在一个列表里,构成一个单元素的列表。因为上层元组的第二个元素被规定为列表。
data_files里的源文件位置和目标位置必须为相对位置。
launch/testlaunch.launch.py
即为相对位置,如果加一个/
/launch/testlaunch.launch.py
就是绝对位置了。
3-使用通配符
如果有多个launch文件,显然一个一个写会很麻烦。
引入python模块glob实现通配,会大大简化我们的操作
from glob import glob
glob.glob()函数输入参数为文件地址的通配形式,如
launch/*.launch.py
返回值是一个列表,把所有匹配元素的地址一一列出
所以,data_files列表的描述还可以写为
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
('share/'+package_name+'/launch',glob('launch/*.launch.py'))
]
4-更加优雅地写出文件的路径
文件路径的字符串总会写错?借用os.path.join函数解决这个!
引入python的os模块实现自动配置路径
import os
os.path.join的输入参数为'share' package_name 和 ''launch'时,会自动返回对应路径!
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share',package_name,'launch'),glob(os.path.join('launch','*.launch.py')))
]
注意,虽然是用os.path.join自动生成的路径,但首位是不能带着/,否则就会变成绝对路径!
对于os.path.join函数,首个参数以'/'开头,则会返回绝对路径
二、launch文件的基本框架
from launch import LaunchDescription
def generate_launch_description():
return LaunchDescription([
# add your actions here...
])
以上是python文件的基本框架。主干是generate_launch_description函数,返回值类型是LaunchDescription类。而这个类的构造函数之参数为一个列表。所有,关键在于,把所有需要启动的节点信息添加到这个列表中。
三、直接启动Node
列表的各个元素分别描述各个节点的启动信息。各个节点的描述信息被封装为Node类。
用这一行代码将Node类引入
from launch_ros.actions import Node
Node类的构造函数有许多参数,可以配置节点的启动信息。其中最关键的是package_name和executable,分别是启动节点的功能包和节点名称。注意,功能包应当在ros2的环境变量里或者在launch文件所在功能包的package.xml文件里添加了相关依赖!
以下是启动turtlesim的一个示例:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
turtlesim_node=Node(
package='turtlesim',
executable='turtlesim_node',
name='sim'
)
return LaunchDescription([
turtlesim_node
])
Node类的常用参数如下:
package
(必需): 要启动节点的包名。executable
(必需): 要启动的节点可执行文件的名称。name
(可选): 节点的名称。如果未指定,将自动生成一个名称。namespace
(可选): 节点的命名空间。parameters
(可选): 节点的参数,可以是字典或字典列表。如果是列表,则表示多个参数文件。remappings
(可选): 节点的重映射规则,用于更改节点的输入和输出主题等。这是一个字典,将原始名称映射到新名称。output
(可选): 节点的输出选项,可以是'screen'
、'log'
或'both'
。默认是'screen'
,即在屏幕上输出节点的标准输出和错误输出。arguments
(可选): 传递给节点可执行文件的附加参数列表
其中,argument参数是通过命令行传递的,就好像我们在命令行运行某个可执行文件所附带的参数:
./a.out [arguments]
四、launch文件指令输入——ExecuteProcess
ExecuteProcess
是 ROS 2 Launch 中用于执行外部进程的动作。它通常用于启动系统中的其他程序或者执行一些外部命令。这个动作的构造函数通常是这样的:
launch.actions.ExecuteProcess(
cmd: List[str],
*,
name: Optional[str] = None,
output: str = 'log',
shell: bool = False,
emulate_tty: bool = False,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
respawn: bool = False,
respawn_delay: float = 1.0,
sigkill_on_shutdown: bool = True
)
cmd
(必需): 要执行的命令及其参数,以列表形式提供。name
(可选): 执行进程的名称。output
(可选): 进程的输出选项,可以是'log'
、'screen'
或'both'
。默认是'log'
,即将输出记录到日志文件中。shell
(可选): 是否使用 shell 运行命令。默认为False
。emulate_tty
(可选): 是否模拟终端 TTY。默认为False
。cwd
(可选): 执行进程的当前工作目录。env
(可选): 执行进程时使用的环境变量字典。respawn
(可选): 是否在进程退出后重新启动。默认为False
。respawn_delay
(可选): 重新启动进程的延迟时间(以秒为单位)。默认为1.0
。sigkill_on_shutdown
(可选): 是否在关闭 Launch 文件时发送 SIGKILL 信号以终止进程。默认为True
。
需要引用下面这个模块才行:
from launch.actions import ExecuteProcess
下面是一个例子:
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import ExecuteProcess
def generate_launch_description():
turtlesim_start=ExecuteProcess(
cmd=['ros2', 'run', 'turtlesim', 'turtlesim_node']
)
return LaunchDescription([
turtlesim_start
])
但是,launch启动文件无法从终端获取用户输入。
五、调用类方法添加节点
LaunchDescription有类方法add_action,可以把Node对象和ExecuteProcess对象添加进入已经创建的LaunchDescription类对象实例中。
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import ExecuteProcess
def generate_launch_description():
ld = LaunchDescription()
turtlesim_node = Node(
package='turtlesim',
executable='turtlesim_node',
name='sim'
)
ld.add_action(turtlesim_node)
return ld