Skip to content

在类中使用参数(Python)

目标: 使用 Python 创建并运行一个带有 ROS 参数的类。

教程级别: 初级

预计时间: 20 分钟

在编写自己的节点时,有时需要添加可以通过 launch 文件设置的参数。

本教程将介绍如何在 Python 类中定义这些参数,以及如何通过 launch 文件来设置它们。

在之前的教程中,你学习了如何创建工作空间和功能包,也了解了参数在 ROS 2 系统中的作用。

打开一个新终端并 source ROS 2 安装环境,以便使用 ros2 命令。

按照之前的说明创建一个名为 ros2_ws 的新工作空间。

注意,功能包应在 src 目录中创建,而非工作空间根目录。进入 ros2_ws/src,创建一个新功能包:

Terminal window
ros2 pkg create --build-type ament_python --license Apache-2.0 python_parameters --dependencies rclpy

终端将返回一条消息,确认功能包 python_parameters 及其所有必要的文件和文件夹已创建。

--dependencies 参数会自动向 package.xml 添加必要的依赖行。

由于在创建功能包时使用了 --dependencies 选项,因此无需手动向 package.xml 添加依赖。不过,仍需照例填写描述、维护者姓名和邮箱以及许可证信息。

<description>Python parameter tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache-2.0</license>

在 ros2_ws/src/python_parameters/python_parameters 目录中,创建一个名为 python_parameters_node.py 的新文件,并粘贴以下代码:

import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node
class MinimalParam(Node):
def __init__(self):
super().__init__('minimal_param_node')
self.declare_parameter('my_parameter', 'world')
self.timer = self.create_timer(1, self.timer_callback)
def timer_callback(self):
my_param = self.get_parameter('my_parameter').get_parameter_value().string_value
self.get_logger().info('Hello %s!' % my_param)
my_new_param = rclpy.parameter.Parameter(
'my_parameter',
rclpy.Parameter.Type.STRING,
'world'
)
all_new_parameters = [my_new_param]
self.set_parameters(all_new_parameters)
def main():
try:
with rclpy.init():
node = MinimalParam()
rclpy.spin(node)
except (KeyboardInterrupt, ExternalShutdownException):
pass
if __name__ == '__main__':
main()

顶部的 import 语句用于导入功能包依赖。

接下来的代码创建了类和构造函数。构造函数中的 self.declare_parameter('my_parameter', 'world') 声明了一个名为 my_parameter 的参数,默认值为 world。参数类型由默认值推断,因此在本例中会被设为字符串类型。随后,timer 以 1 秒为周期初始化,使得 timer_callback 函数每秒执行一次。

class MinimalParam(Node):
def __init__(self):
super().__init__('minimal_param_node')
self.declare_parameter('my_parameter', 'world')
self.timer = self.create_timer(1, self.timer_callback)

timer_callback 函数的第一行从节点获取参数 my_parameter 并存入 my_param。随后通过 get_logger 打印日志信息。接着 set_parameters 函数将参数 my_parameter 重置为默认字符串值 world,这样即使用户在外部修改了参数,它也会被恢复为原始值。

def timer_callback(self):
my_param = self.get_parameter('my_parameter').get_parameter_value().string_value
self.get_logger().info('Hello %s!' % my_param)
my_new_param = rclpy.parameter.Parameter(
'my_parameter',
rclpy.Parameter.Type.STRING,
'world'
)
all_new_parameters = [my_new_param]
self.set_parameters(all_new_parameters)

timer_callback 之后是 main 函数。这里先初始化 ROS 2,构造 MinimalParam 类的实例,然后通过 rclpy.spin 开始处理来自节点的数据。

def main():
try:
with rclpy.init():
node = MinimalParam()
rclpy.spin(node)
except (KeyboardInterrupt, ExternalShutdownException):
pass
if __name__ == '__main__':
main()
2.1.1(可选)添加 ParameterDescriptor
Section titled “2.1.1(可选)添加 ParameterDescriptor”

你可以选择为参数设置描述符。描述符允许你为参数指定文本描述和约束条件,例如设为只读、限定取值范围等。为此,__init__ 代码需要修改为:

# ...
class MinimalParam(Node):
def __init__(self):
super().__init__('minimal_param_node')
from rcl_interfaces.msg import ParameterDescriptor
my_parameter_descriptor = ParameterDescriptor(description='This parameter is mine!')
self.declare_parameter('my_parameter', 'world', my_parameter_descriptor)
self.timer = self.create_timer(1, self.timer_callback)

由于导入了 rcl_interfaces,需要在 package.xml 中添加对应依赖,以避免将来出现依赖问题:

# ...
<depend>rclpy</depend>
<depend>rcl_interfaces</depend>

其余代码保持不变。运行节点后,你可以运行 ros2 param describe /minimal_param_node my_parameter 来查看类型和描述。

打开 setup.py 文件。同样,将 maintainer、maintainer_email、description 和 license 字段与 package.xml 保持一致:

maintainer='YourName',
maintainer_email='you@email.com',
description='Python parameter tutorial',
license='Apache-2.0',

在 entry_points 字段的 console_scripts 列表中添加以下行:

entry_points={
'console_scripts': [
'minimal_param_node = python_parameters.python_parameters_node:main',
],
},

不要忘记保存。

建议在工作空间(ros2_ws)的根目录下运行 rosdep,检查缺失的依赖:

Linux:

Terminal window
rosdep install -i --from-path src --rosdistro {DISTRO} -y

macOS:

rosdep 仅在 Linux 上运行,可直接跳到下一步。

Windows:

rosdep 仅在 Linux 上运行,可直接跳到下一步。

回到工作空间根目录 ros2_ws,构建你的新功能包:

Linux/macOS:

Terminal window
colcon build --packages-select python_parameters

Windows:

Terminal window
colcon build --merge-install --packages-select python_parameters

打开一个新终端,导航到 ros2_ws,并 source 环境配置文件:

Linux:

Terminal window
source install/setup.bash

macOS:

Terminal window
. install/setup.bash

Windows:

Terminal window
call install/setup.bat

现在运行节点。终端应该每秒输出 Hello world!:

Terminal window
$ ros2 run python_parameters minimal_param_node
[INFO] [parameter_node]: Hello world!

现在你可以看到参数的默认值,但你可能希望自己设置它。有以下四种方法。

本节将运用之前参数教程中的知识,将其应用到你刚创建的节点上。

确保节点正在运行:

Terminal window
ros2 run python_parameters minimal_param_node

打开另一个终端,再次从 ros2_ws 中 source 环境配置文件,然后输入以下命令:

Terminal window
ros2 param list

你将看到自定义参数 my_parameter。要更改它,只需在控制台中运行以下命令:

Terminal window
ros2 param set /minimal_param_node my_parameter earth

如果输出 Set parameter successful,说明操作成功。切换到另一个终端,你应该会看到输出变为 [INFO] [minimal_param_node]: Hello earth!

由于节点之后会将参数重置为 world,后续输出会显示 [INFO] [minimal_param_node]: Hello world!

你也可以在 launch 文件中设置参数,但首先需要添加一个 launch 目录。在 ros2_ws/src/python_parameters/ 目录中,创建一个名为 launch 的新目录。在其中创建一个名为 python_parameters_launch.py 的新文件:

from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='python_parameters',
executable='minimal_param_node',
name='custom_minimal_param_node',
output='screen',
emulate_tty=True,
parameters=[
{'my_parameter': 'earth'}
]
)
])

可以看到,我们在启动节点 minimal_param_node 时将 my_parameter 设置为 earth。通过添加以下两行,确保输出被打印到控制台。

Terminal window
output="screen",
emulate_tty=True,

现在打开 setup.py 文件。在文件顶部添加 import 语句,并在 data_files 参数中添加相关配置以包含所有 launch 文件:

import os
from glob import glob
# ...
setup(
# ...
data_files=[
# ...
(os.path.join('share', package_name, 'launch'), glob('launch/*')),
]
)

打开控制台并导航到工作空间根目录 ros2_ws,然后构建你的新功能包:

Linux/macOS:

Terminal window
colcon build --packages-select python_parameters

Windows:

Terminal window
colcon build --merge-install --packages-select python_parameters

然后在新终端中 source 环境配置文件:

Linux:

Terminal window
source install/setup.bash

macOS:

Terminal window
. install/setup.bash

Windows:

Terminal window
call install/setup.bat

现在使用刚才创建的 launch 文件运行节点:

Terminal window
$ ros2 launch python_parameters python_parameters_launch.py
[INFO] [custom_minimal_param_node]: Hello earth!

后续输出应该每秒显示 [INFO] [minimal_param_node]: Hello world!。

3.3 通过 launch 文件从 YAML 文件加载参数

Section titled “3.3 通过 launch 文件从 YAML 文件加载参数”

你可以在 launch 文件中加载单独的 YAML 文件,而不是逐个列出参数及其值。将参数放在 YAML 文件中可以更方便地组织它们,例如将它们分配到不同的命名空间。你可以在这里阅读更多相关信息。

注意:在 Python 节点中声明、获取和设置参数值时,应该使用点作为参数命名空间和名称之间的分隔符。

3.4 在节点启动时通过传递 YAML 文件作为参数更改

Section titled “3.4 在节点启动时通过传递 YAML 文件作为参数更改”

返回关于参数的教程,回顾如何使用 CLI 在节点启动时加载参数文件。

你创建了一个带有自定义参数的节点,该参数可以通过 launch 文件或命令行进行设置。你还向功能包配置文件添加了依赖、可执行文件和 launch 文件,从而能够构建和运行它们,并观察参数的实际效果。

现在你已经有了一些自己的功能包和 ROS 2 系统,下一篇教程将介绍如何在遇到问题时排查环境和系统中的故障。

  • 有关使用 YAML 文件加载参数的更多详细信息,请参阅管理大型项目教程中的相关章节。
  • 如果你想了解如何监控和响应参数更改,请查看监控参数变化(Python)教程。