【ROS】ROS2中的概念和名词解释

news2025/1/10 17:53:35

1、工作空间 workspace

ROS以固定的目录结构创建项目工程,项目根目录称为工作空间

1.1 典型工作空间结构

src:	 代码空间;
build:	 编译空间,保存编译过程中产生的中间文件;
install:安装空间,放置编译得到的可执行文件和脚本;
log:	 日志空间,编译和运行过程中,保存各种警告、错误、信息等日志。

1.2 创建工作空间

就是创建两个目录
例如本人的工作空间:

mkdir -p ~/ros/src

典型的工作空间结构中只需创建src目录即可,其它三个目录会在编译过程中自动生成

下载古月居老师ROS2入门课程21讲的demo

cd ~/ros/src
git clone https://gitee.com/guyuehome/ros2_21_tutorials.git

1.3 安装依赖

所谓的依赖就是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系统版本来更改

附上不同Ubuntu版本下的ROS版本代号
在这里插入图片描述

1.4 安装编译工具colcon

ROS2中使用colcon来编译ROS项目

sudo apt install python3-colcon-ros

1.5 编译

编译时,需要在工作空间根目录下执行编译命令

cd ~/ros
colcon build

编译完成后,自动生存build、install、log三个目录

~/ros$ ls
build  install  log  src

1.6 配置环境变量

ROS2是以目录结构管理项目,因此ROS2系统需要知道我们自己创建工作空间的目录所在的位置;
有两种方法:
1)手动执行命令,仅在当前终端生效,需要每次打开终端时都要执行一遍,注意要在工作空间的根目录下执行

~$ cd ~/ros
~/ros$ source install/local_setup.sh

2)自动执行命令,将执行命令保存到系统脚本中,在系统启动时,自动执行,并且所有终端有效

~$ echo " source ~/dev_ws/install/local_setup.sh" >> ~/.bashrc

2、功能包package

ROS注重、强调功能复用,将各个功能的源码放入各自的目录来管理,这样可以达到代码复用的效果

2.1 创建功能包

创建功能包的命令格式如下:

$ ros2 pkg create --build-type <build-type> <package_name>

参数说明:

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

2.2 编译功能包

在工作空间的根木下执行编译操作:

$ cd ~/ros/src
$ colcon build

2.3 更新环境变量

新创建的功能包还没有被ROS2系统识别,需要手动更新下环境变量

$ source install/local_setup.bash

2.4 包结构

自动创建的包结构如下,其中

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

3、 节点node

ROS由多个可执行程序来协作完成整体功能,可以将一个个单独的程序视为一个个节点,由这些节点协作完成整体功能

3.1 运行、查看节点

在终端1中启动一个小海龟的节点

~$ ros2 run turtlesim turtlesim_node 

在终端2中启动控制小海龟的按键节点:

~$ ros2 run turtlesim turtle_teleop_key

在终端3中查看节点列表

~$ ros2 node list 
/teleop_turtle
/turtlesim

在终端3中查看节点连接图

rqt_graph

在这里插入图片描述

3.2 节点编程

下面是复制古月居老师用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接口

4、话题topic

以话题订阅、发布的形式来实现节点间单向传输数据

4.1 运行节点、查看话题

在终端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
……

4.2 话题编程

4.2.1 话题发布者

#!/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接口

4.2.2 话题订阅者

#!/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接口

5、服务service

以服务端、客户端之间一问一答的形式来双向传输数据

5.1 运行节点、查看服务

在终端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中使用键盘控制小海龟运动,可见画笔颜色已修改
在这里插入图片描述

5.2 服务编程

5.2.1 客户端

#!/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接口

5.2.2 服务端

#!/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接口

6、通信接口interface

用于定义传输数据的结构形式
ROS有三种常用的通信机制,分别是话题、服务、动作,通过每一种通信种定义的接口,各种节点才能有机的联系到一起。

6.1 运行节点、查看通信接口

在终端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

6.2 自定义通信接口

6.2.1 话题通信接口

话题的通信接口文件一般在功能包目录的msg子目录中,内容示例如下

int32 x      # 表示目标的X坐标
int32 y      # 表示目标的Y坐标

6.2.2 服务通信接口

服务的通信接口文件一般在功能包目录的srv子目录中,内容示例如下

bool get      # 获取目标位置的指令
---
int32 x       # 目标的X坐标
int32 y       # 目标的Y坐标

通信接口文件中有两个部分,上边是发送的指令,下边是反馈的信息

6.2.3 动作通信接口

动作的通信接口文件一般在功能包目录的action子目录中,内容示例如下

bool enable     # 定义动作的目标,表示动作开始的指令
---
bool finish     # 定义动作的结果,表示是否成功执行
---
int32 state     # 定义动作的反馈,表示当前执行到的位置

动作通信接口文包含三个部分:

第一块是动作的目标,enable为true时,表示开始运动;
第二块是动作的执行结果,finish为true,表示动作执行完成;
第三块是动作的周期反馈,表示当前机器人旋转到的角度。

7、参数paramter

参数在ROS系统中表示:全局变量,可以在多个节点中共享数据

7.1 运行节点、查看参数

在终端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

在这里插入图片描述

7.2 保存、加载参数

将某个节点的参数保存到参数文件中

~$ 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

7.3 参数编程

#!/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接口

8、动作action

模拟执行一个完整动作,有话题和服务协作完成,例如:开始执行动作、动作是否响应、传输执行动作时的过程数据、动作完成反馈等。

8.1 运行节点、测试动作

在终端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

8.2 编程

执行动作时的通信流程图:
在这里插入图片描述

8.2.1 服务端代码

#!/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接口

8.2.2 客户端代码

#!/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接口

9、分布式通信Distributed Communication

将ROS的节点放到多个平台运行,通过分布式通信来实现

9.1 演示

略……
本人只有一台电脑,懒的搭建测试环境……

9.2 分布式网络分组

ROS2提供了一个DOMAIN的机制,就类似分组一样,处于同一个DOMAIN中的计算机才能通信;
在系统中设置环境变量ROS_DOMAIN_ID,即可将两者分配到一个小组中:

$ export ROS_DOMAIN_ID=<your_domain_id>

10、DDS(Data Distribution Service)

10.1 DDS简介

DDS的全称是Data Distribution Service(数据分发服务),ROS2底层通信机制,可以比喻为机器人的神经网络。

DDS是2004年由对象管理组织OMG发布和维护,是一套专门为实时系统设计的数据分发/订阅标准,最早应用于美国海军, 解决舰船复杂网络环境中大量软件升级的兼容性问题,现在已经成为强制标准。

DDS强调以数据为中心,可以提供丰富的服务质量策略,以保障数据进行实时、高效、灵活地分发,可满足各种分布式实时通信应用需求。
在这里插入图片描述

10.2 DDS质量服务策略:QoS

1)DEADLINE策略,表示通信数据必须要在每次截止时间内完成一次通信;

2)HISTORY策略,表示针对历史数据的一个缓存大小;

3)RELIABILITY策略,表示数据通信的模式,配置成BEST_EFFORT,就是尽力传输模式,网络情况不好的时候,也要保证数据流畅,此时可能会导致数据丢失,配置成RELIABLE,就是可信赖模式,可以在通信中尽量保证图像的完整性,我们可以根据应用功能场景选择合适的通信模式;

4)DURABILITY策略,可以配置针对晚加入的节点,也保证有一定的历史数据发送过去,可以让新节点快速适应系统。

10.3 运行节点、查看策略

在终端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

10.4 编程

10.4.1 发布者代码

#!/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接口

10.4.2 订阅者代码

#!/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接口

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/590823.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一种在不改变源码的情况下测试看门狗复位的方法

什么是“看门狗”&#xff1f; 看门狗定时器&#xff08;WDT&#xff0c;Watch Dog Timer&#xff09;是单片机的一个组成部分&#xff0c;它实际上是一个计数器&#xff0c;一般给看门狗一个数字&#xff0c;程序开始运行后看门狗开始倒计数。如果程序运行正常&#xff0c;过…

git使用X篇_2_Git全套教程IDEA版(git、GitHub、Gitee码云、搭建公司内部GitLab、与IDEA集成等内容)

本文是根据以下视频及网上总结进行更新后的介绍git使用的博文。包含了git、GitHub、Gitee码云、搭建公司内部GitLab、与IDEA集成等内容。 笔记来源&#xff1a;【尚硅谷】5h打通Git全套教程IDEA版&#xff08;涵盖GitHub\Gitee码云\GitLab&#xff09; 文章目录 初识 Git0、内容…

vue-echarts图表的应用(总结)

vue项目中echarts图表的应用(总结) 一 . 安装echarts包 npm i echarts 二 . 放置两个图表的div&#xff0c;并给定高宽 <div class"chart"><!-- 图表 --><div ref"social" style" width: 100%; height:100% " /> </div&g…

Python入门(十五)函数(三)

函数&#xff08;三&#xff09; 1.返回值1.1 返回简单值1.2 让实参变成可选的1.3 返回字典1.4 结合使用函数和while循环 作者&#xff1a;Xiou 1.返回值 函数并非总是直接显示输出&#xff0c;它还可以处理一些数据&#xff0c;并返回一个或一组值。函数返回的值称为返回值。…

【2023】Redis主从复制模式集群

资源有限&#xff0c;本文使用Docker部署目录 &#x1f3b6;主从模式介绍&#x1f3b6; 搭建主从模式集群&#x1f3b6; 使用命令搭建主从集群&#x1f3b6; 通过配置文件搭建主从模式集群 &#x1f3b6;配置读写分离&#x1f3b6; 用心跳机制提高主从复制的可靠性&#x1f3b6…

[golang 微服务] 3. ProtoBuf认识与使用

一.protobuf简介 前言 在移动互联网时代&#xff0c; 手机流量、 电量是最为有限的资源&#xff0c;而移动端的即时通讯应用无疑必须得直面这两点。解决流量过大的基本方法就是 使用高度压缩的通信协议&#xff0c;而数据压缩后流量减小带来的自然结果也就是省电&#xff1a;因…

#Verilog HDL# Verilog设计中的竞争问题和解决办法

经过前面文章的学习&#xff0c;我们知道&#xff1a;不管是Verilog设计语言&#xff0c;还是Sytemverilog验证语言&#xff0c;标准都定义了语言调度机制&#xff0c;来规范各家编译器和仿真器的开发。今天&#xff0c;我们着重看一下Verilog 硬件设计语言中竞争问题&#xff…

算法拾遗三十一马拉车算法

算法拾遗三十一马拉车算法 回文是什么回文暴力求法 Manacher算法回文直径和回文半径最右回文边界最右回文右边界的中心C位置Manacher求解过程Manacher 题 回文是什么 一个字符串正过来念和反过来念一样&#xff0c;总的来说就是有一个对称轴可能在字符上也可能在范围上面 回文…

算法刷题总结 (十一) 二叉树

算法总结11 二叉树 一、二叉树的概念1.1、什么是二叉树&#xff1f;1.2、二叉树的常见类型1.2.1、无数值&#xff08;1&#xff09;、满二叉树&#xff08;2&#xff09;、完全二叉树 1.2.2、有数值&#xff08;3&#xff09;、二叉搜索树&#xff08;4&#xff09;、平衡二叉搜…

设置服务器ssh远程连接时超时关闭的时间

我们通过ssh远程连接服务器时&#xff0c;如果一段时间客户端没有使用&#xff0c;就会与服务器断开连接。这个断开的时间我们是可以自己的设置的。 以linux centos系统为例&#xff0c; 具体设置方法如下&#xff1a; 1、通过下面的命令编译sshd配置文件 vim /etc/ssh/sshd_…

SylixOS vutex

vutex 概念 SylixOS 引入了与 Linux futex 类似的用户快速锁 vutex&#xff08;vitual user mutex&#xff09;&#xff08;SylixOS 习惯称为“等待变量锁”&#xff09;&#xff1b;vutex 包括两个操作&#xff1a;pend 和 post&#xff0c;pend 操作用于等待期望值得到满足&…

DFS专题

题单地址&#xff1a;【237题】算法基础精选题单_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网 老子的全排列呢 dfs回溯 int n 8; int idx; int record[10]; bool vis[10];void dfs(int num) {if(numn){for(int i1;i<n;i) cout<<record[i]<…

【ONE·C++ || C++11(一)】

总言 主要介绍C11中的一些功能语法。 文章目录 总言0、思维导图1、基本情况简介2、统一的列表初始化2.1、{}的使用2.2、initializer_list2.2.1、基础介绍2.2.2、在各容器中实现说明 3、声明3.1、auto3.2、nullptr3.3、decltype 4、范围for5、智能指针6、STL中一些变化6.1、C11…

一、版本控制

1、什么是版本控制 1.1、版本控制的概念 版本控制&#xff08;Revision control&#xff09;是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史&#xff0c;方便查看更改历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术。 1.2、版本控制的作用…

泛型方法、Function类的函数化编程与调用

0、引言 在项目开发的过程中&#xff0c;常常需要将一些高频复用的方法封装成工具类&#xff0c;例如最近学到的Redis缓存中&#xff0c;解决缓存穿透、解决缓存击穿的方法&#xff08;例如解决缓存穿透的问题的方法queryWithPassThrough&#xff09;&#xff0c;传入一个Long型…

谷粒商城:Oss endpoint can‘t be empty.问题

商品API &#xff0c;文件上传管理的时候 出现这个问题 解决两个方向 1.springBoot、alibabaCloud、springCloud、aliyunOSS 之间的版本问题&#xff0c;我的是下面的版本可以运行了。 // springBoot版本 2.7.7 <groupId>org.springframework.boot</groupId> &l…

中关村论坛 | 金融业从增量到存量博弈背后两大原因 更重要的是……

在数字经济浪潮下&#xff0c;中国金融业正在经历数字化转型的深刻变革。为研判金融科技行业发展趋势和前景&#xff0c;探索金融创新与监管安全的边界&#xff0c;“2023中关村论坛金融科技论坛”于5月29日召开。 中电金信常务副总经理冯明刚与中国银行软件中心副总经理康钧伟…

链表:虚拟头节点你会用吗?

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 前言&#xff1a;什么是链表 什么是链表&#xff0c;链表是一种通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据域一个是指…

提高用户忠诚度的 4 种客户保留策略

什么是客户保留&#xff1f;简而言之&#xff0c;客户保留是指企业用来鼓励现有客户群重复购买和持续忠诚度的策略和战术。根据最近的研究&#xff0c;多达68%的客户在觉得公司不重视他们的业务时会转向竞争对手。 这就是为什么客户保留对各行各业的企业都如此重要的原因。与获…

《程序员面试金典(第6版)》面试题 16.25. LRU 缓存(自定义双向链表,list库函数,哈希映射)

题目描述 设计和构建一个“最近最少使用”缓存&#xff0c;该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值)&#xff0c;并在初始化时指定最大容量。当缓存被填满时&#xff0c;它应该删除最近最少使用的项目。 题目传送门&#xff1a;…