• ROS2学习(1)—核心概念


    注:本文根据古月居老师视频整理而成

    1. ROS2为何会替代ROS1?

    1. 要在资源有限的嵌入式系统中运行——>资源有限
    2. 要在有干扰的地方保证通信的可靠性——>易受到干扰
    3. 要做成产品走向市场——>成本昂贵,无法走向市场

    2. ROS2的命令行工具——>基本与ROS1相同

    1.ros2 topic

    1. ros2 topic list:显示所有话题
    2. ros2 topic info <话题名称>:显示节点信息,包含消息类型、发布者数量、订阅数量
    3. ros2 topic echo <话题名称>:打印某个话题中的消息数据——>相比于之前更加方便
    4. ros2 topic pub -rate 10 <话题> <消息> :向某个话题循环发送10次消息——>跟ROS1相同

    2. ros2 service call # 发送服务请求

    3.动作指令

    1. ros2 action

    4.录制

    1. ros2 record <话题>
    2. ros2 play <录制文件>

    3. 工作空间

    概念:实现机器人某项功能的所使用的文件夹

    1. 工作空间内会存在四个子空间
      1. src:源码空间,源代码之类
      2. install:安装空间,编译得到的可执行文件和脚本
      3. build:编译的中间文件
      4. log:日志空间

    创建工作空间:自动初始化

    $ mkdir -p ~/dev_ws/src    #-p代表递归文件夹
    $ cd ~/dev_ws/src
    $ git clone https://gitee.com/guyuehome/ros2_21_tutorials.git #下载课程所用源码
    
    • 1
    • 2
    • 3

    自动安装各种依赖

    #初次使用ROS2需要安装的依赖
    $ sudo apt install -y python3-pip
    $ sudo pip3 install rosdepc
    $ sudo rosdepc init
    $ rosdepc update
    
    
    $ cd ..  #返回主目录,cd ~/dev_ws
    $ rosdepc install -i --from-path src --rosdistro humble -y   #查看src空间中所需要的源码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编译工作空间

    $ sudo apt install python3-colcon-ros   #初次安装,colcon
    
    #返回主目录进行编译
    $ cd ~/dev_ws/
    $ colcon build
    
    • 1
    • 2
    • 3
    • 4
    • 5

    设置环境变量

    $ source install/local_setup.sh # 仅在当前终端生效
    $ echo " source ~/dev_ws/install/local_setup.sh" >> ~/.bashrc # 所有终端均生效
    
    • 1
    • 2

    总结

    创建工作空间
    安装依赖
    编译工作空间
    设置环境
    mkdir
    rosdepc
    colcon
    source

    4. 功能包

    创建功能包

    $ ros2 pkg create --build-type  
    
    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • build-type:创建类型
      • C/C++:ament_cmake
      • python: ament_python

    编译功能包:可以在功能包编写完成代码之后,再进行编译

    $ cd ~/dev_ws/src
    $ colcon build   # 编译工作空间所有功能包
    $ source install/local_setup.bash
    
    • 1
    • 2
    • 3

    5. 节点

    职责

    1. 节点在机器人系统中的职责就是执行某些具体的任务
    2. 节点都是一个独立运行的可执行文件
    3. 节点可以在不同设备上,称之为分布式
    4. 每个节点都要有唯一的命名

    节点流程:打开节点阀门->创建节点->实现节点->关闭节点阀门

    编程接口初始化
    创建节点并初始化
    实现节点功能
    销毁节点并关闭接口

    注意

    1. 当修改功能包中代码后,需要重新编译运行——>(1)colcon build (2)source install/local_setup.bash

    6.话题:节点间传输数据的桥梁

    1. 发布者和订阅者要统一数据的描述格式
    2. 消息是ROS中的一种接口定义方式

    发布者代码解析

    编程接口初始化
    创建节点并初始化
    创建发布者对象
    创建并填充话题消息
    发布话题消息
    销毁节点并关闭接口
    #!/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接口
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    订阅者代码解析

    编程接口初始化
    创建节点对象并初始化
    创建订阅者对象
    回调函数并处理数据
    销毁节点并关闭接口
    #!/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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    话题命令行常用操作

    $ ros2 topic list                # 查看话题列表
    $ ros2 topic info    # 查看话题信息
    $ ros2 topic hz      # 查看话题发布频率
    $ ros2 topic bw      # 查看话题传输带宽
    $ ros2 topic echo    # 查看话题内消息数据
    $ ros2 topic pub      # 发布话题消息
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7.服务:你问我答的模式

    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接口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    服务器代码解析

    编译接口代码
    创建节点对象
    创建服务器对象
    创建回调函数进行服务
    向客户端反馈结果
    销毁节点并关闭接口
    #!/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接口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    服务命令行操作

    $ ros2 service list                  # 查看服务列表
    $ ros2 service type    # 查看服务数据类型
    $ ros2 service call      # 发送服务请求
    
    • 1
    • 2
    • 3

    8.总结

    工作空间和功能包

    1. 工作空间和功能包创建过程

      创建工作空间
      安装依赖
      编译
      设置环境变量
      创建功能包
    2. 共同部分:当源码或其他地方发生变化时,需要重新编译和设置环境变量

      • 编译:clocon build
        • 工作空间是在主目录下编译
        • 功能包是在src空间下编译
      • 设置环境变量:source install/local_setup.bash
    3. 不同部分:

      • 创建:
        • 工作空间:mkdir ——>创建文件夹即可
        • 功能包(src文件夹下):ros2 pkg --build-type
          • C/C++:ros2 pkg --build-type ament_cmake
          • Python:ros2 pkg --build-type ament_python
      • 安装依赖:工作空间特有
        • rosdepc install -i --from-path src --rosdistro humble -y

    节点、话题、服务

    1. 总体流程:

      编译接口初始化
      创建并初始化节点
      实现功能
      销毁节点并关闭接口
      • 实现功能部分:
        • 话题:发布、订阅
        • 服务:客户端、服务器
    2. 话题:发布——服务:客户端

      创建发布者
      创建并填充话题消息/请求数据
      发布话题消息
      创建客户端
      发送请求数据
      等待服务器响应
      • 共同部分:创建

        • 创建发布者/客户端:一行代码,略

        • 创建并填充话题消息/请求数据

          1. 创建话题消息/请求数据对象

          2. 向对象中填充数据

      • 不同部分:

        • 发布/发送
          • 发布者:直接发送(一行代码),略
          • 客户端:
            1. 选择发送方式,比如上文中的异步发送
            2. 发送
        • 等待服务器响应
          • 因为服务是实时的情况,所以需要不断的问答,等待服务器响应
    3. 话题:订阅——服务:服务端

      创建订阅者
      创建回调函数并处理数据/进行服务
      结束
      创建服务端
      向客户端反馈结果
      • 共同部分:创建回调函数并处理数据/进行服务
        • 回调函数:对接受到的数据进行处理
      • 不同部分:
        • 服务端需要将处理后的数据反馈回客户端
  • 相关阅读:
    Java毕业设计之spring+springmvc实现的小型云盘网盘管理系统-课设大作业
    【笔记】SVG动画初见
    stm32f4dma串口收数卡死
    如何完美解决前端数字计算精度丢失与数字格式化问题?
    k8s集群证书过期解决
    Git常用命令
    Linux-unbuntu修改apt源
    Zabbix 5.0 监控教程(四)
    从字符串中提取数字并重新编号
    机器视觉在自动驾驶汽车中的应用与挑战
  • 原文地址:https://blog.csdn.net/cxkyxx/article/details/126036449