当前位置:   article > 正文

【ROS2】ROS2入门

ros2

ROS2入门

refer:古月 & 小鱼

系统架构

1.对比ROS1

  1. 相比ros1,ros2基于DDS实现去中心化,无需master节点,节点之间采用自发现机制进行通信。

  2. 相比API接口更具通用性,使用了新的编译器

  3. 支持更多的os

2.DDS通信

  1. DDS(Data Distribution Service,数据分发服务)是一种用于实时系统的开放式通信协议和架构。

  2. DDS提供了一种分布式数据架构,其中数据生产者和消费者之间通过数据交换实现通信。它的核心概念是数据发布者(Publishers)和数据订阅者(Subscribers),它们通过主题(Topics)进行连接。发布者发布数据到特定的主题,而订阅者订阅感兴趣的主题,并接收相应的数据。topic类似杂志,pub就是编辑,sub就是订阅的读者。

  3. 由于不同厂家的DDS软件接口协议不同,ROS2制定了一个标准接口:ROS Middleware,简称RMW。让厂家去适配。

3.ros2中的核心概念

节点和主题之间的关系可以描述为:

  • 发布者节点将消息发布到一个或多个主题上。

  • 订阅者节点通过订阅感兴趣的主题来接收消息。

通过节点和主题的结合使用,ROS 2系统可以实现复杂的通信和数据交换,从而构建各种类型的机器人应用程序或其他实时系统。

安装

  1. 古月居:https://book.guyuehome.com/ROS2/1.%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/1.3_ROS2%E5%AE%89%E8%A3%85%E6%96%B9%E6%B3%95/

  2. 鱼香ROS一键安装指令:wget http://fishros.com/install -O fishros && . fishros

4.linux常用命令操作

cd

  • 语法:cd <目录路径>

  • 功能:改变工作目录。若没有指定“目录路径”,则回到用户的主目录

pwd

  • 语法:pwd

  • 功能:此命令显示出当前工作目录的绝对路径

mkdir

  • 语法:mkdir [选项] <目录名称>

  • 功能:创建一个目录/文件夹

ls

  • 语法:ls [选项] [目录名称…]

  • 功能:列出目录/文件夹中的文件列表

gedit

  • 语法:gedit <文件名称>

  • 功能:打开gedit编辑器编辑文件,若没有此文件则会新建

mv

  • 语法:mv [选项] <源文件或目录> <目地文件或目录>

  • 功能:为文件或目录改名或将文件由一个目录移入另一个目录中

cp

  • 语法:cp [选项] <源文件名称或目录名称> <目的文件名称或目录名称>

  • 功能:把一个文件或目录拷贝到另一文件或目录中,或者把多个源文件复制到目标目录中

rm

  • 语法:rm [选项] <文件名称或目录名称…>

  • 功能:该命令的功能为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所有文件及子目录均删除。对于链接文件,只是删除了链接,原有文件均保持不变

sudo

  • 语法:sudo [选项] [指令]

  • 功能:以系统管理员权限来执行指令

5.ROS中常用命令

easoom@easoom-virtual-machine:~$ ros2 --help
usage: ros2 [-h] Call `ros2 <command> -h` for more detailed usage. ...
​
ros2 is an extensible command-line tool for ROS 2.
​
optional arguments:
  -h, --help            show this help message and exit
​
Commands:
  action     Various action related sub-commands
  bag        Various rosbag related sub-commands
  component  Various component related sub-commands
  daemon     Various daemon related sub-commands
  doctor     Check ROS setup and other potential issues
  interface  Show information about ROS interfaces
  launch     Run a launch file
  lifecycle  Various lifecycle related sub-commands
  multicast  Various multicast related sub-commands
  node       Various node related sub-commands
  param      Various param related sub-commands
  pkg        Various package related sub-commands
  run        Run a package specific executable
  security   Various security related sub-commands
  service    Various service related sub-commands
  topic      Various topic related sub-commands
  wtf        Use `wtf` as alias to `doctor`
​
  Call `ros2 <command> -h` for more detailed usage.
​

核心概念

1.工作空间

即工程文件,包含编写的代码、参数、脚本等文件。

典型工作空间:

  • src,代码空间,未来编写的代码、脚本,都需要人为的放置到这里;

  • build,编译空间,保存编译过程中产生的中间文件;

  • install,安装空间,放置编译得到的可执行文件和脚本;

  • log,日志空间,编译和运行过程中,保存各种警告、错误、信息等日志。

总体来讲,这四个空间的文件夹,我们绝大部分操作都是在src中进行的,编译成功后,就会执行install里边的结果,build和log两个文件夹用的很少。

2.功能包

定义

将不同功能的代码整合打包即为功能包。

创建功能包
$ ros2 pkg create --build-type <build-type> <package_name>

ros2命令中:

  • pkg:表示功能包相关的功能;

  • create:表示创建功能包;

  • build-type:表示新创建的功能包是C++还是Python的,如果使用C++或者C,那这里就跟ament_cmake,如果使用Python,就跟ament_python;

  • package_name:新建功能包的名字。

创建CPP和python的功能包:

$ cd ~/dev_ws/src
$ ros2 pkg create --build-type ament_cmake learning_pkg_c               # C++
$ ros2 pkg create --build-type ament_python learning_pkg_python # Python

CPP的功能包中必然存在两个文件:package.xmlCMakerLists.txt

package.xml文件的主要包含功能包的版权描述,和各种依赖的声明。

CMakeLists.txt文件是编译规则,C++代码需要编译才能运行,所以必须要在该文件中设置如何编译,使用CMake语法。

pyhton的功能包中也会有这两个文件:package.xmlsetup.py

package.xml文件的主要内容和C++版本功能包一样,包含功能包的版权描述,和各种依赖的声明。

setup.py文件里边也包含一些版权信息,除此之外,还有“entry_points”配置的程序入口。

3.节点 node

节点是一个单独的进程,每个节点都是独立运行的可执行文件,可以分布在不同的硬件平台上,编程语言也可以不同,节点之间需要唯一命名。

节点编程
  • 编程接口初始化

  • 创建节点并初始化

  • 实现节点功能

  • 销毁节点并关闭接口

4.话题 topic

节点之间的一种通信方式

  1. 发布-(话题)-订阅

  2. 异步通信

  3. 可以多对多

  4. 消息接口.msg

publisher创建:
  • 编程接口初始化

  • 创建节点并初始化

  • 创建发布者对象

  • 创建并填充话题消息

  • 发布话题消息

  • 销毁节点并关闭接口

subscriber创建:
  • 编程接口初始化

  • 创建节点并初始化

  • 创建订阅者对象

  • 回调函数处理话题数据

  • 销毁节点并关闭接口

topic本身是没有存储功能的,发布者一旦发布,订阅者需要第一时间接受下来,但可以不第一时间处理。对于已经发布的消息,可以设置一定的历史消息深度,让订阅者能查询历史消息。

5.服务

通信机制

你问我答,同步通信。

类似服务器--客户端,即一对多通信,服务器只能有一个,客户端可以有多个。

服务接口

分为请求数据和反馈数据,格式为.srv。

创建服务器:
  • 编程接口初始化

  • 创建节点并初始化

  • 创建服务器端对象

  • 通过回调函数处进行服务

  • 向客户端反馈应答结果

  • 销毁节点并关闭接口

创建客户端:
  • 编程接口初始化

  • 创建节点并初始化

  • 创建客户端对象

  • 创建并发送请求数据

  • 等待服务器端应答数据

  • 销毁节点并关闭接口

服务命令行操作
$ ros2 service list                  # 查看服务列表
$ ros2 service type <service_name>   # 查看服务数据类型
$ ros2 service call <service_name> <service_type> <service_data>   # 发送服务请求

6.通信接口

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

特点
  1. 语言无关

    1. 比如这里看到的int32表示32位的整型数,int64表示64位的整型数,bool表示布尔值,还可以定义数组、结构体,这些定义在编译过程中,会自动生成对应到C++、Python等语言里的数据结构。

  • 话题通信接口的定义使用的是.msg文件,由于是单向传输,只需要描述传输的每一帧数据是什么就行,比如在这个定义里,会传输两个32位的整型数,x、y,我们可以用来传输二维坐标的数值。

  • 服务通信接口的定义使用的是.srv文件,包含请求和应答两部分定义,通过中间的“---”区分,比如之前我们学习的加法求和功能,请求数据是两个64位整型数a和b,应答是求和的结果sum。

  • 动作是另外一种通信机制,用来描述机器人的一个运动过程,使用.action文件定义,比如我们让小海龟转90度,一边转一边周期反馈当前的状态,此时接口的定义分成了三个部分,分别是动作的目标,比如是开始运动,运动的结果,最终旋转的90度是否完成,还有一个周期反馈,比如每隔1s反馈一下当前转到第10度、20度还是30度了,让我们知道运动的进度。

ros2中原生提供了标准接口:我们可以在ROS安装路径中的share文件夹中找到,涵盖众多标准定义

接口定义
服务通信的接口
回顾
  • package.xml文件的主要内容如下,包含功能包的版权描述,和各种依赖的声明。

  • CMakeLists.txt文件是编译规则,C++代码需要编译才能运行,所以必须要在该文件中设置如何编译,使用CMake语法。

  1. 定义,路径:learning_interface/srv/GetObjectPosition.srv

    定义中有两个部分,上边是获取目标位置的指令,get为true的话,就表示我们需要一次位置,服务端就会反馈这个x、y坐标了。

​
bool get      # 获取目标位置的指令
---
int32 x       # 目标的X坐标
int32 y       # 目标的Y坐标
  1. CMakeLists.txt中配置编译选项

    完成定义后,还需要在功能包的CMakeLists.txt中配置编译选项,让编译器在编译过程中,根据接口定义,自动生成不同语言的代码。

...
​
find_package(rosidl_default_generators REQUIRED)
​
rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/GetObjectPosition.srv"
)
​
..
  1. package.xml中添加功能依赖

    功能包的package.xml文件中也需要添加代码生成的功能依赖

 ...
​
 <build_depend>rosidl_default_generators</build_depend>
 <exec_depend>rosidl_default_runtime</exec_depend>
 <member_of_group>rosidl_interface_packages</member_of_group>
​
 ...
​
  1. 服务通信接口程序调用

# 客户端
···
from learning_interface.srv import GetObjectPosition    # 自定义的服务接口
​
class objectClient(Node):
    def __init__(self, name):
        super().__init__(name)                                                                  # ROS2节点父类初始化
        self.client = self.create_client(GetObjectPosition, 'get_target_position')
        while not self.client.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('service not available, waiting again...')
        self.request = GetObjectPosition.Request()
                    
    def send_request(self):
        self.request.get = True
        self.future = self.client.call_async(self.request)
···
​
​
#服务端
···
        self.srv = self.create_service(GetObjectPosition,   # 创建服务器对象(接口类型、服务名、服务器回调函数)
                                       'get_target_position',
                                       self.object_position_callback)   
···
话题服务接口
  1. 定义

    话题消息的内容是一个位置,我们使用x、y坐标值进行描述。

    文件路径:learning_interface/msg/ObjectPosition.msg

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

  2. CMakeLists.txt中配置编译选项

    ...
    ​
    find_package(rosidl_default_generators REQUIRED)
    ​
    rosidl_generate_interfaces(${PROJECT_NAME}
      "msg/ObjectPosition.msg"
    )
    ​
    ...
    ​
  3. 程序调用

    发布者
    ···
    from learning_interface.msg import ObjectPosition  # 自定义的目标位置消息
        def __init__(self, name):
            super().__init__(name)                                  # ROS2节点父类初始化
            self.sub = self.create_subscription(
                Image, 'image_raw', self.listener_callback, 10)     # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
            self.pub = self.create_publisher(
                ObjectPosition, "object_position", 10)              # 创建发布者对象(消息类型、话题名、队列长度)
            self.cv_bridge = CvBridge()                             # 创建一个图像转换对象,用于OpenCV图像与ROS的图像消息的互相转换
    ​
            self.objectX = 0
            self.objectY = 0 
    ···
    -----
    订阅者
    ···
    from learning_interface.msg import ObjectPosition  # 自定义的目标位置消息
    ​
        def __init__(self, name):
            super().__init__(name)                                                # ROS2节点父类初始化
            self.sub = self.create_subscription(\
                ObjectPosition, "/object_position", self.listener_callback, 10)   # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度
    ​
    ···
    接口命令行操作
$ ros2 interface list                    # 查看系统接口列表
$ ros2 interface show <interface_name>   # 查看某个接口的详细定义
$ ros2 interface package <package_name>  # 查看某个功能包中的接口定义
​

7.动作

也是一种通信机制,顾名思义,对机器人的一段完整行为进行描述和管理。

接口定义
  1. 定义格式

bool enable     # 定义动作的目标,表示动作开始的指令
---
bool finish     # 定义动作的结果,表示是否成功执行
---
int32 state     # 定义动作的反馈,表示当前执行到的位置
​
  1. CMakeLists.txt中配置编译选项

...
​
find_package(rosidl_default_generators REQUIRED)
​
rosidl_generate_interfaces(${PROJECT_NAME}
  "action/MoveCircle.action"
)
​
...

服务器-客户端模型
  • 服务器(机器人),客户端(控制器)

  • 一对多通信,机器人只能有一个,控制器可以有多个。

  • 同步通信

  • “动作”是应用层通信机制,由话题和服务搭建而成

大致逻辑

# 客户端(python)
'''
注意三个回调函数
1、 目标发送后等待服务器反馈的回调函数
2、 接收服务器发送的反馈数据的回调函数
3、 异步询问是否完成动作的回调函数
​
​
一、定义action接口
二、初始化action对象
三、send_goal();发送目标函数
    1. 发送目标函数
    2. 设置 反馈接收回调函数
        a. 反馈数据处理
    3. 设置 接收目标函数回调函数
        a. if 拒绝目标 return
        b. if 接受目标,异步获取执行结果反馈,并设置对应回调函数
            (1) 动作完成回调函数
'''

8.参数

也是一种数据传输方式,类似全局变量。

参数是ROS机器人系统中的全局字典,可以运行多个节点中共享数据。

可动态监控

在ROS2中,参数的特性非常丰富,比如某一个节点共享了一个参数,其他节点都可以访问,如果某一个节点对参数进行了修改,其他节点也有办法立刻知道,从而获取最新的数值。这在参数的高级编程中,大家都可能会用到。

常用命令
$ ros2 param list   # 查询系统中的参数
​
$ ros2 param describe turtlesim background_b   # 查看某个参数的描述信息
$ ros2 param get turtlesim background_b        # 查询某个参数的值
$ ros2 param set turtlesim background_b 10     # 修改某个参数的值
​
$ ros2 param dump turtlesim >> turtlesim.yaml  # 将某个节点的参数保存到参数文件中
$ ros2 param load turtlesim turtlesim.yaml     # 一次性加载某一个文件中的所有参数
​
    
程序实现

9.分布式通信

将任务拆分,分配给多个计算平台。基于分布式通信实现多计算平台的任务分配。

基于同一局域网下,对多个ros设备建立连接,当作一个整体使用。

可以设置不同的域,对设备进行分组,只有相同ID才能通信。

$ export ROS_DOMAIN_ID=<your_domain_id>

10.DDS

DDS的全称是Data Distribution Service,也就是数据分发服务,以数据为中心。

类似广播的模式,将数据放在一个总的,含有众多并行通路的bus上,但挂载在bus上的设备只接收自己关心的数据。

质量服务策略,QoS

QoS是一种网络传输策略,应用程序指定所需要的网络传输质量行为,QoS服务实现这种行为要求,尽可能地满足客户对通信质量的需求,可以理解为数据提供者和接收者之间的合约

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

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

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

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

 ···
    from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy # ROS2 QoS类
​
    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)         # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
···

11.Launch

ROS系统中多节点启动与配置的一种脚本。

ROS2中的Launch文件就是基于Python描述的。

exp:

from launch import LaunchDescription           # launch文件的描述类
from launch_ros.actions import Node            # 节点启动的描述类
​
def generate_launch_description():             # 自动生成launch文件的函数
    return LaunchDescription([                 # 返回launch文件的描述信息
        Node(                                  # 配置一个节点的启动
            package='learning_topic',          # 节点所在的功能包
            executable='topic_helloworld_pub', # 节点的可执行文件
        ),
        Node(                                  # 配置一个节点的启动
            package='learning_topic',          # 节点所在的功能包
            executable='topic_helloworld_sub', # 节点的可执行文件名
        ),
    ])
​

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/870428
推荐阅读
  

闽ICP备14008679号