在类中使用参数(Python)
目标: 使用 Python 创建并运行一个带有 ROS 参数的类。
教程级别: 初级
预计时间: 20 分钟
在编写自己的节点时,有时需要添加可以通过 launch 文件设置的参数。
本教程将介绍如何在 Python 类中定义这些参数,以及如何通过 launch 文件来设置它们。
在之前的教程中,你学习了如何创建工作空间和功能包,也了解了参数在 ROS 2 系统中的作用。
1 创建功能包
Section titled “1 创建功能包”打开一个新终端并 source ROS 2 安装环境,以便使用 ros2 命令。
按照之前的说明创建一个名为 ros2_ws 的新工作空间。
注意,功能包应在 src 目录中创建,而非工作空间根目录。进入 ros2_ws/src,创建一个新功能包:
ros2 pkg create --build-type ament_python --license Apache-2.0 python_parameters --dependencies rclpy终端将返回一条消息,确认功能包 python_parameters 及其所有必要的文件和文件夹已创建。
--dependencies 参数会自动向 package.xml 添加必要的依赖行。
1.1 更新 package.xml
Section titled “1.1 更新 package.xml”由于在创建功能包时使用了 --dependencies 选项,因此无需手动向 package.xml 添加依赖。不过,仍需照例填写描述、维护者姓名和邮箱以及许可证信息。
<description>Python parameter tutorial</description><maintainer email="you@email.com">Your Name</maintainer><license>Apache-2.0</license>2 编写 Python 节点
Section titled “2 编写 Python 节点”在 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()2.1 代码解析
Section titled “2.1 代码解析”顶部的 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 来查看类型和描述。
2.2 添加入口点
Section titled “2.2 添加入口点”打开 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', ], },不要忘记保存。
3 构建与运行
Section titled “3 构建与运行”建议在工作空间(ros2_ws)的根目录下运行 rosdep,检查缺失的依赖:
Linux:
rosdep install -i --from-path src --rosdistro {DISTRO} -ymacOS:
rosdep 仅在 Linux 上运行,可直接跳到下一步。
Windows:
rosdep 仅在 Linux 上运行,可直接跳到下一步。
回到工作空间根目录 ros2_ws,构建你的新功能包:
Linux/macOS:
colcon build --packages-select python_parametersWindows:
colcon build --merge-install --packages-select python_parameters打开一个新终端,导航到 ros2_ws,并 source 环境配置文件:
Linux:
source install/setup.bashmacOS:
. install/setup.bashWindows:
call install/setup.bat现在运行节点。终端应该每秒输出 Hello world!:
$ ros2 run python_parameters minimal_param_node [INFO] [parameter_node]: Hello world!现在你可以看到参数的默认值,但你可能希望自己设置它。有以下四种方法。
3.1 通过控制台更改
Section titled “3.1 通过控制台更改”本节将运用之前参数教程中的知识,将其应用到你刚创建的节点上。
确保节点正在运行:
ros2 run python_parameters minimal_param_node打开另一个终端,再次从 ros2_ws 中 source 环境配置文件,然后输入以下命令:
ros2 param list你将看到自定义参数 my_parameter。要更改它,只需在控制台中运行以下命令:
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!
3.2 通过 launch 文件更改
Section titled “3.2 通过 launch 文件更改”你也可以在 launch 文件中设置参数,但首先需要添加一个 launch 目录。在 ros2_ws/src/python_parameters/ 目录中,创建一个名为 launch 的新目录。在其中创建一个名为 python_parameters_launch.py 的新文件:
from launch import LaunchDescriptionfrom 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。通过添加以下两行,确保输出被打印到控制台。
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:
colcon build --packages-select python_parametersWindows:
colcon build --merge-install --packages-select python_parameters然后在新终端中 source 环境配置文件:
Linux:
source install/setup.bashmacOS:
. install/setup.bashWindows:
call install/setup.bat现在使用刚才创建的 launch 文件运行节点:
$ 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)教程。