ROS2中文网:http://dev.ros2.fishros.com/
ROS2官方文档:http://dev.ros2.fishros.com/doc/
ROS2教程:http://dev.ros2.fishros.com/doc/Tutorials.html
API接口:https://docs.ros.org/en/rolling/API-Docs.html
ROS2 Support on NVIDIA Jetson:https://nvidia-ai-iot.github.io/ros2_jetson/
官方源码:
https://github.com/ros2
https://github.com/ros2/examples
ROS以固定的目录结构创建项目工程,项目根目录称为工作空间
src: 代码空间;
build: 编译空间,保存编译过程中产生的中间文件;
install:安装空间,放置编译得到的可执行文件和脚本;
log: 日志空间,编译和运行过程中,保存各种警告、错误、信息等日志。
就是创建两个目录
例如本人的工作空间:
mkdir -p ~/ros/src
典型的工作空间结构中只需创建src目录即可,其它三个目录会在编译过程中自动生成
下载古月居老师ROS2入门课程21讲的demo
cd ~/ros/src
git clone https://gitee.com/guyuehome/ros2_21_tutorials.git
所谓的依赖就是ROS中已经完成的、可复用的功能包,可以使用工具rosdep来自动安装依赖,受网络影响,国内的用户可以使用rosdepc来安装,这里以rosdepc为例:
1)rosdepc是python3编写的,先要安装python3-pip
sudo apt install python3-pip
2)安装rosdepc
sudo pip3 install rosdepc
3)初始化、更新
sudo rosdepc init
rosdepc update
4)安装依赖
安装依赖时,一定要在工作空间的根目录下执行rosdepc命令
cd ~/ros/
rosdepc install -i --from-path src --rosdistro humble -y
为src目录下的功能包安装依赖;
注意:参数humble是指ROS2在Ubuntu22.04系统下的版本代号,需要根据自己Ubuntu系统版本来更改
ROS2中使用colcon来编译ROS项目
sudo apt install python3-colcon-ros
编译时,需要在工作空间根目录下执行编译命令
cd ~/ros
colcon build
编译完成后,自动生存build、install、log三个目录
~/ros$ ls
build install log src
ROS2是以目录结构管理项目,因此ROS2系统需要知道我们自己创建工作空间的目录所在的位置;
有两种方法:
1)手动执行命令,仅在当前终端生效,需要每次打开终端时都要执行一遍,注意要在工作空间的根目录下执行
~$ cd ~/ros
~/ros$ source install/local_setup.sh
2)自动执行命令,将执行命令保存到系统脚本中,在系统启动时,自动执行,并且所有终端有效
~$ echo " source ~/dev_ws/install/local_setup.sh" >> ~/.bashrc
ROS注重、强调功能复用,将各个功能的源码放入各自的目录来管理,这样可以达到代码复用的效果
创建功能包的命令格式如下:
$ ros2 pkg create --build-type
参数说明:
pkg: ros2子命令,执行与功能包相关的操作;
create:ros2 pkg 的子命令,用来创建功能包;
build-type:表示新创建的功能包是C++还是Python的,可选参数:ament_cmake(表示创建C++或C包)、ament_python(表示创建python包)
package_name:新建功能包的名字。
例如:在终端中分别创建C++和Python版本的功能包:
注意执行创建功能包的命令时,需要在工作空间的src目录或其子目录中下执行:
$ cd ~/ros/src
$ ros2 pkg create --build-type ament_cmake learning_pkg_c
$ ros2 pkg create --build-type ament_python learning_pkg_python
在工作空间的根木下执行编译操作:
$ cd ~/ros/src
$ colcon build
新创建的功能包还没有被ROS2系统识别,需要手动更新下环境变量
$ source install/local_setup.bash
自动创建的包结构如下,其中
package.xml:ROS2功能包依赖描述文档
CMakeLists.txt:C++或C项目中使用cmake来编译时的工程描述文档
setup.py:Python项目入口文档
~/ros/src$ tree
.
├── learning_pkg_c
│ ├── CMakeLists.txt
│ ├── include
│ │ └── learning_pkg_c
│ ├── package.xml
│ └── src
├── learning_pkg_python
│ ├── learning_pkg_python
│ │ └── __init__.py
│ ├── package.xml
│ ├── resource
│ │ └── learning_pkg_python
│ ├── setup.cfg
│ ├── setup.py
│ └── test
│ ├── test_copyright.py
│ ├── test_flake8.py
│ └── test_pep257.py
ROS由多个可执行程序来协作完成整体功能,可以将一个个单独的程序视为一个个节点,由这些节点协作完成整体功能
在终端1中启动一个小海龟的节点
~$ ros2 run turtlesim turtlesim_node
在终端2中启动控制小海龟的按键节点:
~$ ros2 run turtlesim turtle_teleop_key
在终端3中查看节点列表
~$ ros2 node list
/teleop_turtle
/turtlesim
在终端3中查看节点连接图
rqt_graph
下面是复制古月居老师用python写的demo
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-发布“Hello World”日志信息, 使用面向对象的实现方式
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
import time
"""
创建一个HelloWorld节点, 初始化时输出“hello world”日志
"""
class HelloWorldNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
while rclpy.ok(): # ROS2系统是否正常运行
self.get_logger().info("Hello World") # ROS2日志输出
time.sleep(0.5) # 休眠控制循环时间
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = HelloWorldNode("node_helloworld_class") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
以话题订阅、发布的形式来实现节点间单向传输数据
在终端1中启动一个小海龟的节点
~$ ros2 run turtlesim turtlesim_node
在终端2中启动控制小海龟的按键节点:
~$ ros2 run turtlesim turtle_teleop_key
在终端3中查看话题列表
~$ ros2 topic list
/parameter_events
/rosout
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose
在终端3中查看指定话题信息
~$ ros2 topic info /turtle1/pose
Type: turtlesim/msg/Pose
Publisher count: 1
Subscription count: 0
在终端3中查看指定话题内容
~$ ros2 topic echo /turtle1/pose
x: 7.758955478668213
y: 2.675198793411255
theta: -0.3631852865219116
linear_velocity: 0.0
angular_velocity: 0.0
……
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-发布“Hello World”话题
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # 字符串消息类型
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.pub = self.create_publisher(String, "chatter", 10) # 创建发布者对象(消息类型、话题名、队列长度)
self.timer = self.create_timer(0.5, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
def timer_callback(self): # 创建定时器周期执行的回调函数
msg = String() # 创建一个String类型的消息对象
msg.data = 'Hello World' # 填充消息对象中的消息数据
self.pub.publish(msg) # 发布话题消息
self.get_logger().info('Publishing: "%s"' % msg.data) # 输出日志信息,提示已经完成话题发布
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = PublisherNode("topic_helloworld_pub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-订阅“Hello World”话题消息
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # ROS2标准定义的String消息
"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.sub = self.create_subscription(\
String, "chatter", self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
def listener_callback(self, msg): # 创建回调函数,执行收到话题消息后对数据的处理
self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = SubscriberNode("topic_helloworld_sub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
以服务端、客户端之间一问一答的形式来双向传输数据
在终端1中启动一个小海龟的节点
~$ ros2 run turtlesim turtlesim_node
在终端2中启动控制小海龟的按键节点:
~$ ros2 run turtlesim turtle_teleop_key
在终端3中查看服务列表
~$ ros2 service list
/clear
/kill
……
/turtle1/set_pen
……
使用rqt图形化工具调用(call)/turtle1/set_pen服务来改变小海龟画笔的颜色和宽度
在终端3中输入命令
rqt
选择插件Service Caller
选择服务“/turtle1/set_pen” ,并设置画笔的颜色rgb和宽度width的值,然后点击Call
在终端2中使用键盘控制小海龟运动,可见画笔颜色已修改
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-发送两个加数,请求加法器计算
"""
import sys
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderClient(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.client = self.create_client(AddTwoInts, 'add_two_ints') # 创建服务客户端对象(服务接口类型,服务名)
while not self.client.wait_for_service(timeout_sec=1.0): # 循环等待服务器端成功启动
self.get_logger().info('service not available, waiting again...')
self.request = AddTwoInts.Request() # 创建服务请求的数据对象
def send_request(self): # 创建一个发送服务请求的函数
self.request.a = int(sys.argv[1])
self.request.b = int(sys.argv[2])
self.future = self.client.call_async(self.request) # 异步方式发送服务请求
def main(args=None):
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderClient("service_adder_client") # 创建ROS2节点对象并进行初始化
node.send_request() # 发送服务请求
while rclpy.ok(): # ROS2系统正常运行
rclpy.spin_once(node) # 循环执行一次节点
if node.future.done(): # 数据是否处理完成
try:
response = node.future.result() # 接收服务器端的反馈数据
except Exception as e:
node.get_logger().info(
'Service call failed %r' % (e,))
else:
node.get_logger().info( # 将收到的反馈信息打印输出
'Result of add_two_ints: for %d + %d = %d' %
(node.request.a, node.request.b, response.sum))
break
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-提供加法器的服务器处理功能
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderServer(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.adder_callback) # 创建服务器对象(接口类型、服务名、服务器回调函数)
def adder_callback(self, request, response): # 创建回调函数,执行收到请求后对数据的处理
response.sum = request.a + request.b # 完成加法求和计算,将结果放到反馈的数据中
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) # 输出日志信息,提示已经完成加法求和计算
return response # 反馈应答信息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderServer("service_adder_server") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
用于定义传输数据的结构形式
ROS有三种常用的通信机制,分别是话题、服务、动作,通过每一种通信种定义的接口,各种节点才能有机的联系到一起。
在终端1中启动一个小海龟的节点
~$ ros2 run turtlesim turtlesim_node
在终端2中启动控制小海龟的按键节点:
~$ ros2 run turtlesim turtle_teleop_key
在终端3中查看通信列表,一共有三类:Message、Services、Actions,分别对应话题、服务、动作三种通信机制
~$ ros2 interface list
Messages:
turtlesim/msg/Color
……
Services:
turtlesim/srv/SetPen
……
Actions:
turtlesim/action/RotateAbsolute
……
在终端3中查看指定通信接口的定义
1)话题通信接口
~$ ros2 interface show turtlesim/msg/Pose
float32 x
float32 y
2)服务通信接口
~$ ros2 interface show turtlesim/srv/TeleportAbsolute
float32 x
float32 y
float32 theta
---
3)动作通信接口
~$ ros2 interface show turtlesim/action/RotateAbsolute
float32 theta
---
float32 delta
---
float32 remaining
话题的通信接口文件一般在功能包目录的msg子目录中,内容示例如下
int32 x # 表示目标的X坐标
int32 y # 表示目标的Y坐标
服务的通信接口文件一般在功能包目录的srv子目录中,内容示例如下
bool get # 获取目标位置的指令
---
int32 x # 目标的X坐标
int32 y # 目标的Y坐标
通信接口文件中有两个部分,上边是发送的指令,下边是反馈的信息
动作的通信接口文件一般在功能包目录的action子目录中,内容示例如下
bool enable # 定义动作的目标,表示动作开始的指令
---
bool finish # 定义动作的结果,表示是否成功执行
---
int32 state # 定义动作的反馈,表示当前执行到的位置
动作通信接口文包含三个部分:
第一块是动作的目标,enable为true时,表示开始运动;
第二块是动作的执行结果,finish为true,表示动作执行完成;
第三块是动作的周期反馈,表示当前机器人旋转到的角度。
参数在ROS系统中表示:全局变量,可以在多个节点中共享数据
在终端1中启动一个小海龟的节点
~$ ros2 run turtlesim turtlesim_node
在终端2中启动控制小海龟的按键节点:
~$ ros2 run turtlesim turtle_teleop_key
在终端3中查看参数列表
~$ ros2 param list
/turtlesim:
background_b
background_g
background_r
……
在终端3中查看、设置参数的值,以修改小海龟的背景颜色值为例
查看
~$ ros2 param get /turtlesim background_b
Integer value is: 255
设置
~$ ros2 param set /turtlesim background_b 125
Set parameter successful
再次查看
~$ ros2 param get /turtlesim background_b
Integer value is: 125
将某个节点的参数保存到参数文件中
~$ ros2 param dump turtlesim >> turtlesim.yaml
一次性加载某一个文件中的所有参数
~$ ros2 param load turtlesim turtlesim.yaml
参考参数文件的内容
~$ cat turtlesim.yaml
/turtlesim:
ros__parameters:
background_b: 125
background_g: 86
background_r: 69
qos_overrides:
/parameter_events:
publisher:
depth: 1000
durability: volatile
history: keep_last
reliability: reliable
use_sim_time: false
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2参数示例-创建、读取、修改参数
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
class ParameterNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.timer = self.create_timer(2, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
self.declare_parameter('robot_name', 'mbot') # 创建一个参数,并设置参数的默认值
def timer_callback(self): # 创建定时器周期执行的回调函数
robot_name_param = self.get_parameter('robot_name').get_parameter_value().string_value # 从ROS2系统中读取参数的值
self.get_logger().info('Hello %s!' % robot_name_param) # 输出日志信息,打印读取到的参数值
new_name_param = rclpy.parameter.Parameter('robot_name', # 重新将参数值设置为指定值
rclpy.Parameter.Type.STRING, 'mbot')
all_new_parameters = [new_name_param]
self.set_parameters(all_new_parameters) # 将重新创建的参数列表发送给ROS2系统
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = ParameterNode("param_declare") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
模拟执行一个完整动作,有话题和服务协作完成,例如:开始执行动作、动作是否响应、传输执行动作时的过程数据、动作完成反馈等。
在终端1中启动一个小海龟的节点
~$ ros2 run turtlesim turtlesim_node
在终端2中启动控制小海龟的按键节点:
~$ ros2 run turtlesim turtle_teleop_key
在终端3中查看动作列表
~$ ros2 action list
/turtle1/rotate_absolute
在终端3中查看动作详细信息
~$ ros2 action info /turtle1/rotate_absolute
Action: /turtle1/rotate_absolute
Action clients: 1
/teleop_turtle
Action servers: 1
/turtlesim
在终端3中执行一个动作:发送执行命令 --> 反馈1 --> …… --> 反馈n --> 执行结果
~$ ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: -4.57}" --feedback
Sending goal:
theta: -4.57
Feedback:
remaining: -3.016444206237793
Feedback:
remaining: -3.0004444122314453
……
Result:
delta: 3.007999897003174
Goal finished with status: SUCCEEDED
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2动作示例-负责执行圆周运动动作的服务端
"""
import time
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from rclpy.action import ActionServer # ROS2 动作服务器类
from learning_interface.action import MoveCircle # 自定义的圆周运动接口
class MoveCircleActionServer(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self._action_server = ActionServer( # 创建动作服务器(接口类型、动作名、回调函数)
self,
MoveCircle,
'move_circle',
self.execute_callback)
def execute_callback(self, goal_handle): # 执行收到动作目标之后的处理函数
self.get_logger().info('Moving circle...')
feedback_msg = MoveCircle.Feedback() # 创建一个动作反馈信息的消息
for i in range(0, 360, 30): # 从0到360度,执行圆周运动,并周期反馈信息
feedback_msg.state = i # 创建反馈信息,表示当前执行到的角度
self.get_logger().info('Publishing feedback: %d' % feedback_msg.state)
goal_handle.publish_feedback(feedback_msg) # 发布反馈信息
time.sleep(0.5)
goal_handle.succeed() # 动作执行成功
result = MoveCircle.Result() # 创建结果消息
result.finish = True
return result # 反馈最终动作执行的结果
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = MoveCircleActionServer("action_move_server") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2动作示例-请求执行圆周运动动作的客户端
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from rclpy.action import ActionClient # ROS2 动作客户端类
from learning_interface.action import MoveCircle # 自定义的圆周运动接口
class MoveCircleActionClient(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self._action_client = ActionClient( # 创建动作客户端(接口类型、动作名)
self, MoveCircle, 'move_circle')
def send_goal(self, enable): # 创建一个发送动作目标的函数
goal_msg = MoveCircle.Goal() # 创建一个动作目标的消息
goal_msg.enable = enable # 设置动作目标为使能,希望机器人开始运动
self._action_client.wait_for_server() # 等待动作的服务器端启动
self._send_goal_future = self._action_client.send_goal_async( # 异步方式发送动作的目标
goal_msg, # 动作目标
feedback_callback=self.feedback_callback) # 处理周期反馈消息的回调函数
self._send_goal_future.add_done_callback(self.goal_response_callback) # 设置一个服务器收到目标之后反馈时的回调函数
def goal_response_callback(self, future): # 创建一个服务器收到目标之后反馈时的回调函数
goal_handle = future.result() # 接收动作的结果
if not goal_handle.accepted: # 如果动作被拒绝执行
self.get_logger().info('Goal rejected :(')
return
self.get_logger().info('Goal accepted :)') # 动作被顺利执行
self._get_result_future = goal_handle.get_result_async() # 异步获取动作最终执行的结果反馈
self._get_result_future.add_done_callback(self.get_result_callback) # 设置一个收到最终结果的回调函数
def get_result_callback(self, future): # 创建一个收到最终结果的回调函数
result = future.result().result # 读取动作执行的结果
self.get_logger().info('Result: {%d}' % result.finish) # 日志输出执行结果
def feedback_callback(self, feedback_msg): # 创建处理周期反馈消息的回调函数
feedback = feedback_msg.feedback # 读取反馈的数据
self.get_logger().info('Received feedback: {%d}' % feedback.state)
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = MoveCircleActionClient("action_move_client") # 创建ROS2节点对象并进行初始化
node.send_goal(True) # 发送动作目标
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
将ROS的节点放到多个平台运行,通过分布式通信来实现
略……
本人只有一台电脑,懒的搭建测试环境……
ROS2提供了一个DOMAIN的机制,就类似分组一样,处于同一个DOMAIN中的计算机才能通信;
在系统中设置环境变量ROS_DOMAIN_ID,即可将两者分配到一个小组中:
$ export ROS_DOMAIN_ID=
DDS的全称是Data Distribution Service(数据分发服务),ROS2底层通信机制,可以比喻为机器人的神经网络。
DDS是2004年由对象管理组织OMG发布和维护,是一套专门为实时系统设计的数据分发/订阅标准,最早应用于美国海军, 解决舰船复杂网络环境中大量软件升级的兼容性问题,现在已经成为强制标准。
DDS强调以数据为中心,可以提供丰富的服务质量策略,以保障数据进行实时、高效、灵活地分发,可满足各种分布式实时通信应用需求。
1)DEADLINE策略,表示通信数据必须要在每次截止时间内完成一次通信;
2)HISTORY策略,表示针对历史数据的一个缓存大小;
3)RELIABILITY策略,表示数据通信的模式,配置成BEST_EFFORT,就是尽力传输模式,网络情况不好的时候,也要保证数据流畅,此时可能会导致数据丢失,配置成RELIABLE,就是可信赖模式,可以在通信中尽量保证图像的完整性,我们可以根据应用功能场景选择合适的通信模式;
4)DURABILITY策略,可以配置针对晚加入的节点,也保证有一定的历史数据发送过去,可以让新节点快速适应系统。
在终端1中启动一个小海龟的节点
~$ ros2 run turtlesim turtlesim_node
在终端2中启动控制小海龟的按键节点:
~$ ros2 run turtlesim turtle_teleop_key
在终端3中查看话题策略
~$ ros2 topic info /turtle1/pose --verbose
Type: turtlesim/msg/Pose
Publisher count: 1
Node name: turtlesim
Node namespace: /
Topic type: turtlesim/msg/Pose
Endpoint type: PUBLISHER
GID: 01.0f.76.64.91.0f.22.79.01.00.00.00.00.00.1c.03.00.00.00.00.00.00.00.00
QoS profile:
Reliability: RELIABLE
History (Depth): UNKNOWN
Durability: VOLATILE
Lifespan: Infinite
Deadline: Infinite
Liveliness: AUTOMATIC
Liveliness lease duration: Infinite
Subscription count: 0
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2 QoS示例-发布“Hello World”话题
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # 字符串消息类型
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy # ROS2 QoS类
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
qos_profile = QoSProfile( # 创建一个QoS原则
# reliability=QoSReliabilityPolicy.BEST_EFFORT,
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=1
)
self.pub = self.create_publisher(String, "chatter", qos_profile) # 创建发布者对象(消息类型、话题名、QoS原则)
self.timer = self.create_timer(0.5, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
def timer_callback(self): # 创建定时器周期执行的回调函数
msg = String() # 创建一个String类型的消息对象
msg.data = 'Hello World' # 填充消息对象中的消息数据
self.pub.publish(msg) # 发布话题消息
self.get_logger().info('Publishing: "%s"' % msg.data)# 输出日志信息,提示已经完成话题发布
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = PublisherNode("qos_helloworld_pub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2 QoS示例-订阅“Hello World”话题消息
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # ROS2标准定义的String消息
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy # ROS2 QoS类
"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
qos_profile = QoSProfile( # 创建一个QoS原则
# reliability=QoSReliabilityPolicy.BEST_EFFORT,
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=1
)
self.sub = self.create_subscription(\
String, "chatter", self.listener_callback, qos_profile) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、QoS原则)
def listener_callback(self, msg): # 创建回调函数,执行收到话题消息后对数据的处理
self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = SubscriberNode("qos_helloworld_sub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口