目标:使用 Python 创建并运行一个带有 ROS 参数的类。
教程级别:初学者
时间:20 分钟
目录
背景
先决条件
任务
1. 创建一个包
2. 编写 Python 节点
3. 建立并运行
摘要
下一步
背景
在创建您自己的节点时,有时需要添加可以从启动文件设置的参数。
本教程将向您展示如何在 Python 类中创建这些参数,以及如何在启动文件中设置它们。
先决条件
在之前的教程中,您学习了如何创建工作空间和创建软件包。您还了解了参数及其在 ROS 2 系统中的功能。
任务
1. 创建一个包
打开一个新的终端并且初始化您的 ROS 2 安装,这样 ros2
命令就会生效。
按照这些指示创建一个名为 ros2_ws
的新工作区。
请记住,包应该在 src
目录中创建,而不是在工作区的根目录中。导航到 ros2_ws/src
并创建一个新包:
cxy@ubuntu2404-cxy:~/ros2_ws/src$ ros2 pkg create --build-type ament_python --license Apache-2.0 python_parameters --dependencies rclpy
going to create a new package
package name: python_parameters
destination directory: /home/cxy/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['cxy <cxy@todo.todo>']
licenses: ['Apache-2.0']
build type: ament_python
dependencies: ['rclpy']
creating folder ./python_parameters
creating ./python_parameters/package.xml
creating source folder
creating folder ./python_parameters/python_parameters
creating ./python_parameters/setup.py
creating ./python_parameters/setup.cfg
creating folder ./python_parameters/resource
creating ./python_parameters/resource/python_parameters
creating ./python_parameters/python_parameters/__init__.py
creating folder ./python_parameters/test
creating ./python_parameters/test/test_copyright.py
creating ./python_parameters/test/test_flake8.py
creating ./python_parameters/test/test_pep257.py
您的终端将返回一条消息,确认您的包 python_parameters
及其所有必要的文件和文件夹已创建。
` --dependencies
`参数将自动向` package.xml
`和` CMakeLists.txt
`添加必要的依赖行。
1.1 更新 package.xml
因为在创建包时您使用了 --dependencies
选项,所以您无需手动添加依赖项到 package.xml
或 CMakeLists.txt
。
一如既往,不过,请确保将描述、维护者电子邮件和姓名以及许可信息添加到 package.xml
。
<description>Python parameter tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache-2.0</license>
2. 编写 Python 节点
在 ros2_ws/src/python_parameters/python_parameters
目录中,创建一个名为 python_parameters_node.py
的新文件,并将以下代码粘贴其中:
cxy@ubuntu2404-cxy:~/ros2_ws/src/python_parameters/python_parameters$ gedit python_parameters_node.py
# 导入rclpy模块,这是ROS2中Python客户端库的一部分
import rclpy
# 导入rclpy.node模块,这是ROS2中用于创建节点的模块
import rclpy.node
# 定义一个MinimalParam类,它继承自rclpy.node.Node类
class MinimalParam(rclpy.node.Node):
# 类的初始化函数
def __init__(self):
# 调用父类的初始化函数,并设置节点名称为'minimal_param_node'
super().__init__('minimal_param_node')
# 声明一个参数'my_parameter',并设置默认值为'world'
self.declare_parameter('my_parameter', 'world')
# 创建一个定时器,每1秒调用一次timer_callback函数
self.timer = self.create_timer(1, self.timer_callback)
# 定时器回调函数
def timer_callback(self):
# 获取'my_parameter'参数的值
my_param = self.get_parameter('my_parameter').get_parameter_value().string_value
# 使用节点的日志功能打印信息
self.get_logger().info('Hello %s!' % my_param)
# 创建一个新的Parameter对象,用于更新'my_parameter'参数的值
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)
# main函数定义
def main():
# 初始化rclpy库
rclpy.init()
# 创建MinimalParam类的实例
node = MinimalParam()
# 使节点保持运行,直到被外部事件(如Ctrl+C)中断
rclpy.spin(node)
# Python脚本的入口点,当直接运行此脚本时执行main函数
if __name__ == '__main__':
main()
2.1 检查代码
顶部的 import
语句用于导入包依赖项。
下一段代码创建了类和构造函数。构造函数的第 self.declare_parameter('my_parameter', 'world')
行创建了一个名为 my_parameter
的参数,其默认值为 world
。参数类型是根据默认值推断出来的,所以在这种情况下它会被设置为字符串类型。接下来, timer
被初始化为 1,这导致 timer_callback
函数每秒执行一次。
class MinimalParam(rclpy.node.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():
rclpy.init()
node = MinimalParam()
rclpy.spin(node)
if __name__ == '__main__':
main()
2.1.1(可选)添加参数描述符
您可以选择为参数设置一个描述符。描述符允许您指定参数及其约束条件的文本描述,例如使其只读,指定范围等。为此, __init__
代码必须更改为:
# ...
class MinimalParam(rclpy.node.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)
代码的其余部分保持不变。一旦运行了节点,你就可以运行 ros2 param describe /minimal_param_node my_parameter
来查看类型和描述。
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',
],
},
别忘了保存。
# 导入setuptools模块,用于配置、打包和安装Python包
from setuptools import find_packages, setup
# 设置包名为'python_parameters'
package_name = 'python_parameters'
# 调用setup函数来配置Python包
setup(
# 包名
name=package_name,
# 版本号
version='0.0.0',
# 自动查找并包含所有find_packages()找到的包,排除名为'test'的包
packages=find_packages(exclude=['test']),
# 包含的数据文件
data_files=[
# 安装时需要复制的文件或文件夹列表
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
# 安装依赖项,这里只需要'setuptools'
install_requires=['setuptools'],
# 设置为zip安全,即可以安全地从zip压缩包运行
zip_safe=True,
# 维护者的名称
maintainer='cxy',
# 维护者的邮箱地址
maintainer_email='cxy@126.com',
# 包的简短描述
description='Python parameter tutorial',
# 许可证类型
license='Apache-2.0',
# 测试依赖项
tests_require=['pytest'],
# 入口点,定义了哪些脚本会被安装到系统路径中,以便直接在命令行中运行
entry_points={
'console_scripts': [
# minimal_param_node是一个可执行命令,指向python_parameters.python_parameters_node模块中的main函数
'minimal_param_node = python_parameters.python_parameters_node:main',
],
},
)
3. 构建并运行
在工作区的根目录运行 rosdep
( ros2_ws
)以检查构建前缺失的依赖项是一个好习惯:
rosdep install -i --from-path src --rosdistro jazzy -y
返回到您的工作区根目录, ros2_ws
,然后构建您的新包:
cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select python_parameters
Starting >>> python_parameters
Finished <<< python_parameters [2.56s]
Summary: 1 package finished [2.82s]
打开一个新的终端,导航到 ros2_ws
,并且导入设置文件:
source install/setup.bash
现在运行节点:终端应该每秒返回以下消息:
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 run python_parameters minimal_param_node
[INFO] [1720325261.370823709] [minimal_param_node]: Hello world!
[INFO] [1720325262.346870169] [minimal_param_node]: Hello world!
[INFO] [1720325263.346835815] [minimal_param_node]: Hello world!
现在您可以看到参数的默认值,但您希望能够自己设置它。有两种方法可以做到这一点。
3.1 通过控制台进行更改
这部分将使用你从教程中获得的关于参数的知识,并将其应用到你刚刚创建的节点上。
确保节点正在运行:
ros2 run python_parameters minimal_param_node
打开另一个终端,再次从 ros2_ws
内部获取设置文件,并输入以下行:
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 param list
/minimal_param_node:
my_parameter
start_type_description_service
use_sim_time
在那里,您将看到自定义参数 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 通过启动文件进行更改
您也可以在启动文件中设置参数,但首先您需要添加一个启动目录。在 ros2_ws/src/python_parameters/
目录内,创建一个名为 launch
的新目录。在那里,创建一个名为 python_parameters_launch.py
的新文件。
# 从launch模块导入LaunchDescription类
from launch import LaunchDescription
# 从launch_ros模块的actions子模块导入Node类
from launch_ros.actions import Node
# 定义一个函数generate_launch_description,这个函数将会返回一个LaunchDescription对象
def generate_launch_description():
# 返回一个LaunchDescription对象,它包含了一个Node列表
return LaunchDescription([
# 创建一个Node对象,用于启动一个ROS节点
Node(
# 指定节点所属的包名
package='python_parameters',
# 指定节点可执行文件的名称
executable='minimal_param_node',
# 为节点指定一个名称
name='custom_minimal_param_node',
# 设置节点的输出选项,这里是输出到屏幕
output='screen',
# 设置emulate_tty为True,以便在屏幕上模拟tty输出,使得输出更易读
emulate_tty=True,
# 设置节点的参数,这里定义了一个名为'my_parameter'的参数,其值为'earth'
parameters=[
{'my_parameter': 'earth'}
]
)
])
在这里您可以看到,当我们启动节点 parameter_node
时,我们将 my_parameter
设置为 earth
。通过添加下面两行代码,我们确保输出打印在控制台上。
output="screen",
emulate_tty=True,
现在打开 setup.py
文件。将 import
语句添加到文件顶部,并将另一个新语句添加到 data_files
参数中,以包含所有启动文件:
import os
from glob import glob
# ...
setup(
# ...
data_files=[
# ...
(os.path.join('share', package_name), glob('launch/*launch.[pxy][yma]*')),
]
)
# 从setuptools模块导入find_packages和setup函数
from setuptools import find_packages, setup
# 导入os模块,用于操作系统功能,如路径操作
import os
# 从glob模块导入glob函数,用于文件路径的模式匹配
from glob import glob
# 定义包名为'python_parameters'
package_name = 'python_parameters'
# 调用setup函数来配置包的设置
setup(
# 设置包名
name=package_name,
# 设置包版本
version='0.0.0',
# 使用find_packages函数查找并包含所有子包,排除名为'test'的子包
packages=find_packages(exclude=['test']),
# 设置数据文件,这些文件将会被安装到系统中
data_files=[
# 安装ament索引资源到'share/ament_index/resource_index/packages'
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
# 安装package.xml到'share/' + package_name
('share/' + package_name, ['package.xml']),
# 安装launch文件夹下所有的启动文件到'share/' + package_name
(os.path.join('share', package_name), glob('launch/*launch.[pxy][yma]*')),
],
# 设置包的依赖项,这里只需要'setuptools'
install_requires=['setuptools'],
# 设置zip_safe为True,表示可以安全地将此包作为zip文件运行
zip_safe=True,
# 设置维护者信息
maintainer='cxy',
maintainer_email='cxy@126.com',
# 设置包描述
description='Python parameter tutorial',
# 设置许可证类型
license='Apache-2.0',
# 设置测试需要的依赖项
tests_require=['pytest'],
# 设置入口点,定义如何启动包中的程序
entry_points={
'console_scripts': [
# 当运行'minimal_param_node'命令时,会调用python_parameters_node模块中的main函数
'minimal_param_node = python_parameters.python_parameters_node:main',
],
},
)
打开控制台并导航到您的工作区的根目录, ros2_ws
,然后构建您的新包:
cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select python_parameters
Starting >>> python_parameters
Finished <<< python_parameters [2.51s]
Summary: 1 package finished [2.75s]
然后在新终端中导入设置文件:
source install/setup.bash
现在使用我们刚刚创建的启动文件运行节点:
cxy@ubuntu2404-cxy:~/ros2_ws$ source install/setup.bash
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 launch python_parameters python_parameters_launch.py
[INFO] [launch]: All log files can be found below /home/cxy/.ros/log/2024-07-07-12-15-25-235509-ubuntu2404-cxy-21329
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [minimal_param_node-1]: process started with pid [21332]
[minimal_param_node-1] [INFO] [1720325726.687588853] [custom_minimal_param_node]: Hello earth!
[minimal_param_node-1] [INFO] [1720325727.671121468] [custom_minimal_param_node]: Hello world!
进一步的输出应该每秒显示 [INFO] [minimal_param_node]: Hello world!
。
摘要
您创建了一个带有自定义参数的节点,该参数可以从启动文件或命令行中设置。您将依赖项、可执行文件和启动文件添加到包配置文件中,以便您可以构建并运行它们,并看到参数在实际中的应用。
下一步
现在您已经拥有了一些包和自己的 ROS 2 系统,下一个教程 https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Getting-Started-With-Ros2doctor.html 将向您展示如何在遇到问题时检查环境和系统中的问题。