ROS2节点
版权信息
Copyright 2023 Herman Ye@Auromix. All rights reserved.
This course and all of its associated content, including but not limited to text,
images, videos, and any other materials, are protected by copyright law.
The author holds all rights to this course and its contents.
Any unauthorized use, reproduction, distribution, or modification of this course
or its contents is strictly prohibited and may result in legal action.
This includes, but is not limited to:
Copying or distributing course materials without express written permission.
Reposting, sharing, or distributing course content on any platform without proper attribution and permission.
Creating derivative works based on this course without permission.
Permissions and Inquiries
If you wish to use or reproduce any part of this course for purposes other than personal learning,
please contact the author to request permission.
The course content is provided for educational purposes, and the author makes no warranties or representations
regarding the accuracy, completeness, or suitability of the course content for any specific purpose.
The author shall not be held liable for any damages, losses,
or other consequences resulting from the use or misuse of this course.
Please be aware that this course may contain materials or images obtained from third-party sources.
The author and course creator diligently endeavor to ensure that these materials
are used in full compliance with copyright and fair use regulations.
If you have concerns about any specific content in this regard,
please contact the author for clarification or resolution.
By enrolling in this course, you agree to abide by the terms and conditions outlined in this copyright notice.
学习目标
- 熟悉ROS2的节点概念
- 了解ROS2节点相关的命令行工具操作
- 熟悉节点的代码编写和编程思想
难度级别
初级 | 中级 | 高级 |
---|---|---|
√ |
预计耗时
30 mins
学习前提
对象 | 类型 | 状态 |
---|---|---|
ROS2 Humble | 软件 | 已安装 |
Ubuntu22.04操作系统 | 软件 | 已确认 |
Shell的基本使用 | 知识 | 已了解 |
什么是节点(Node)?
ROS 中的每个节点(Node)通常负责单一的模块化目的,例如控制车轮电机或发布来自激光测距仪的传感器数据。每个节点都可以通过主题(Topic)、服务(Service)、操作(Action)或参数(Parameter)从其他节点发送和接收数据。
节点的特性
- 节点是独立的
每个节点都具备独立性,负责执行特定的模块化任务 - 节点负责执行机器人系统中的具体任务
不同的节点可以负责机器人系统中的不同任务,例如一个节点(A节点)可以处理视觉数据,另一个节点(B节点)可以负责运动 - 节点可以分布在不同的硬件载体上
节点之间的通信可以跨架构,例如PC是x86架构的,而机器人身上的主控是arm架构的,两个不同的硬件载体上的节点都可以发送和接收数据 - 不同节点可以用不同的编程语言来写
节点可以用Python和C++编写,并且功能基本一致,因此不需要考虑编程语言的区别
如何使用一个节点?
要使用一个ROS 2节点,可以使用 ros2 run
命令来启动位于相应功能包(Package)中的可执行文件(节点)。以下是使用此命令的步骤:
-
打开终端窗口。
-
使用以下命令格式启动节点:
ros2 run <package_name> <executable_name>
这里,
<package_name>
是功能包的名称,而<executable_name>
是要启动的节点的可执行文件名称。例如,如果要启动位于
turtlesim
功能包中的turtlesim_node
节点,可以执行以下命令:ros2 run turtlesim turtlesim_node
这将启动
turtlesim_node
节点,使用乌龟仿真(Turtlesim)功能包中的模拟乌龟控制器。可以根据功能包和节点的名称来自定义命令,以启动需要的节点。
如何查看节点相关的信息?
要查看节点相关的信息,可以使用以下命令:
-
列出当前正在运行的所有节点,使用
ros2 node list
命令:ros2 node list
这将显示当前正在运行的所有ROS 2节点的名称列表。
-
如果想要启动另一个节点并查看更新后的节点列表,可以执行以下步骤:
-
首先,启动一个新的节点,例如
turtle_teleop_key
,使用以下命令:ros2 run turtlesim turtle_teleop_key
-
然后,检查当前节点列表,使用
ros2 node list
命令:ros2 node list
这将列出包括新启动的
turtle_teleop_key
节点在内的所有节点名称。 -
-
若要查看有关特定节点的详细信息,可以使用
ros2 node info
命令,后跟节点名称,如下所示:ros2 node info <node_name>
其中
<node_name>
是要查看详细信息的节点的名称。这将显示有关节点的各种信息,包括节点的交互信息和自身的参数。
如何编写一个节点?
节点编写的基本思想
编写一个节点与一般编程相似,可以采用面向过程或面向对象的编程思想。对于复杂的机器人应用,通常使用面向对象的思想,以实现模块化和可维护性。
节点编写的基本流程
编写一个节点的基本流程包括以下步骤:
-
编程API初始化: 首先,需要初始化ROS的编程接口,比如
rclpy
,rclpy
是ROS Client Library for Python的缩写,与Node有关的内容在这个库中。 -
创建节点并初始化: 接下来,创建一个节点并进行初始化。节点是机器人应用中的基本单元,负责执行特定的任务或功能。
-
实现节点功能: 在节点中实现具体的功能。这包括处理传感器数据、执行动作、通信等任务,根据应用需求编写相应的代码。
-
销毁节点并关闭接口: 最后,在节点的工作完成后,需要销毁节点并关闭编程接口,释放资源并确保系统正常退出。
示例:编写一个简单的节点
0.前置工作
如果尚未创建功能包来保存代码,那么需要先创建一个功能包,功能包是实现ROS2中基本功能的一个单位,此处不作讨论,将在之后的课程中讲解。
# Go to your src
cd ~/ros2_workspace/src
# Create ros2_learning package
ros2 pkg create --build-type ament_python ros2_learning
1.编写代码
下面是一个使用面向过程编程思想,以Python语言编写一个发布"Hello World"日志信息的简单节点的示例代码:
# Go to package
cd ros2_learning/ros2_learning
# Create file
touch hello_world_demo.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Description: Python ROS2 demo(面向过程编程)
import rclpy # rclpy是ROS 2的Python客户端库,用于与ROS 2相关的操作。
from rclpy.node import Node # 引入Node类
import time # 引入time库
# 下面的部分是ROS2节点主入口main函数
def main(args=None): # 主函数
rclpy.init(args=args) # 初始化 rclpy ROS2的python编程接口
nodename = Node("hello_world_demo_procedural_programming") # 创建ROS2节点对象并进行初始化
while rclpy.ok(): # 当ROS就绪并正常运行
nodename.get_logger().info("Hello World!") # 发布日志信息
time.sleep(1) # 休眠1秒
nodename.destroy_node() # 销毁节点
rclpy.shutdown() # 关闭ROS2的Python接口
if __name__ == '__main__':
main()
这个示例代码演示了一个简单节点的编写过程,它初始化ROS2接口,创建节点对象,循环发布"Hello World"的日志信息,然后在系统退出时销毁节点和关闭接口。
更为推荐的做法是采用面向对象的编程方法。机器人自身天然适合作为对象来进行建模和控制。如果打算采用面向对象的方法来开发机器人控制软件,以下是一些示例代码,以帮助更好地理解和实施这一方法:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Description: 这是一个 Python ROS2 示例,演示了如何使用ROS 2中的面向对象编程。
import time
import rclpy # rclpy是ROS 2的Python客户端库,用于与ROS 2相关的操作。
from rclpy.node import Node # 引入Node类
class HelloWorldNode(Node):
def __init__(self, node_name):
super().__init__(node_name) # 调用父类 Node 的构造函数,使用提供的节点名称进行初始化。
while rclpy.ok(): # 当系统正常运行时
self.get_logger().info("Hello World!") # 记录日志输出 "Hello World!"
time.sleep(1) # 休眠1秒,然后再循环。
def main(args=None):
rclpy.init(args=args) # 初始化ROS 2
node = HelloWorldNode("hello_world_demo_object_oriented_programming") # 创建一个ROS 2节点对象并初始化。
rclpy.spin(node) # 进入ROS的循环,等待节点销毁信号。
node.destroy_node() # 销毁节点对象。
rclpy.shutdown() # 关闭ROS 2接口。
if __name__ == '__main__':
main()
2.为功能包添加依赖
在代码的编译或者运行过程中,通常需要引入一些外部依赖包。为了确保编译或运行顺利进行,我们需要在功能包目录下的package.xml文件中声明这些依赖项。下面是必须添加的内容:
<exec_depend>rclpy</exec_depend>
这些语句表明了功能包的依赖关系,以确保在功能包的代码执行时所需的依赖包已经安装。
此外,你还可以添加额外的信息,比如功能包的描述、维护者和许可证,如示例所示:
<description>ROS2 learning demos</description>
<maintainer email="hermanye233@icloud.com">Herman Ye</maintainer>
<license>Apache License 2.0</license>
3.添加入口点
为了使系统知道 Python 程序的入口点在哪里,你需要在setup.py文件中添加入口点。下面是必须添加的内容:
- 打开
setup.py
,添加入口点
在entry_points下的这个位置添加以下命令
"hello_world_demo_node = ros2_learning.hello_world_demo:main",
这行代码定义了一个入口点,它告诉系统如何运行功能包中的节点。具体示例形式如下:
entry_points={
"console_scripts": [
"hello_world_demo_node = ros2_learning.hello_world_demo:main",
],
},
)
此外,你还可以添加一些额外的信息,如维护者、维护者的电子邮件地址、功能包描述和许可证信息:
maintainer='Herman Ye',
maintainer_email='hermanye233@icloud.com',
description='ROS2 learning demos',
license='Apache License 2.0',
4. 检查 setup.cfg【可选】
setup.cfg
文件是用于配置 Python 包的元数据和其他信息的文件,尤其是用于定义 Python 包的 setuptools 配置。 ROS2 功能包也使用 setup.cfg
来配置包的元数据和依赖关系。
[develop]
script_dir=$base/lib/ros2_learning
:这个部分通常用于配置在开发模式下(使用python setup.py develop
)运行包时的一些参数。script_dir
参数指定了脚本的目录,通常这些脚本用于在开发期间启动和管理功能包。在这里,$base
是一个变量,它可能代表功能包的根目录,这个参数告诉 ROS 或 setuptools,在开发模式下,将脚本放在lib/ros2_learning
目录中。
[install]
install_scripts=$base/lib/ros2_learning
:这个部分通常用于配置在安装功能包时(使用python setup.py install
)将哪些文件复制到安装目录中。install_scripts
参数指定要安装的脚本文件的目录。在这里,与[develop]
部分类似,$base
可能代表功能包的根目录,该参数告诉 setuptools 或 ROS 在安装包时将脚本文件复制到lib/ros2_learning
目录中。
它在这个功能包里的作用是指定功能包的可执行文件所在的目录,这些可执行文件将被放置在lib
目录下。这些配置确保了在使用ros2 run
命令时,系统将会在lib
目录中找到这些可执行文件并执行它们。
以下是setup.cfg
文件的示例内容:
[develop]
script_dir=$base/lib/ros2_learning
[install]
install_scripts=$base/lib/ros2_learning
需要注意的是,setup.cfg
文件通常是自动生成的,无需手动编辑,除非有特殊需求。
5.编译工作空间
在最后,编译工作空间,相关命令在之后的课程里会讲解,此处不涉及。
# Go to your workspace
cd ~/ros2_workspace
# Build workspace
colcon build --symlink-install
Troubleshooting:
- 弃用警告
ROS2的工作空间编译使用了setuptools
,ROS2 Rolling 到 Python3.10 的过渡最近才发生,这可能是由此产生的问题之一,因此需要执行以下命令:
pip install setuptools==58.2.0
重新编译工作空间,该错误不再发生。
6.测试节点
# Set workspace env in current terminal
source ~/ros2_workspace/install/setup.bash
# Run hello world demo node
ros2 run ros2_learning hello_world_demo_node
ROS2的node相关命令行工具
ROS 2的节点相关命令行工具提供了轻松管理ROS 2节点的能力。比如说ros2 node info
和ros2 node list
。
① ros2 node info
ros2 node info
命令允许用户获取有关特定节点的详细信息,包括其订阅者(Subscribers)、发布者(Publishers)、服务服务器(Service Servers)、服务客户端(Service Clients)、动作服务器(Action Servers)和动作客户端(Action Clients)等信息。这是通过提供节点的名称作为参数来实现的。
例如,要检索有关名为"turtlesim"的节点的信息,可以执行以下命令:
ros2 node info /turtlesim
② ros2 node list
ros2 node list
命令用于查看当前运行的ROS 2节点的列表。这有助于用户了解系统中哪些节点处于活动状态,以及可以使用哪些节点进行通信或监控。执行此命令后,将列出所有活跃的ROS 2节点的名称。
ros2 node list
③ 对节点名进行重映射
有时可能会需要进行节点名称的重新映射,以便在ROS 2中为节点提供新的标识。这个过程允许在运行节点时,为其指定一个不同的名称,从而使其能够在整个系统中以新名称运行,同时保留原始节点的功能。这种功能对于实现多节点系统以及避免名称冲突非常有用,特别是需要启动多个相同的节点时,可以通过重映射命令创建多个不同名称的同类型对象。
要进行节点名称的重新映射,可以使用--ros-args
参数和--remap
选项,其语法如下:
ros2 run package_name executable_name --ros-args --remap __node:=new_node_name
这里的package_name
代表ROS 2软件包名称,executable_name
是要运行的节点的可执行文件名称,original_node_name
是原始节点名称,而new_node_name
则是希望为节点分配的新名称。
例如,如果同时要运行两个小乌龟的demo,想要将名为"turtlesim_node"的节点的名称从"/turtlesim"重映射为"/renamed_turtle",可以使用以下命令:
ros2 run turtlesim turtlesim_node --ros-args --remap __node:=renamed_turtle
可以看到通过--remap
成功修改了要启动的节点名,不再是/turtlesim
而是/renamed_turtle
思考:
如果不重映射名字,同时开两个小乌龟节点会发生什么?
如果此时进行速度控制,又会发生什么?
如果分别给两个小乌龟节点起不同的名字,此时再运行速度控制节点,又会发生什么?