ROS2之第2讲--话题(topic)程序的编写

前言

  在ROS2之第1讲–环境的配置以及HelloWorld程序的建立中我们主要介绍了ros2环境的配置,工作空间的创建,以及helloworld程序的编写;通过第一节你会了解到通过ros2如何编写一个简单的程序;接下来我们将介绍ros2中的话题通信机制,如何编写一个简单的发布话题的程序和订阅话题的程序。

节点的介绍

  节点是ROS2通信的基本单位,每个节点都对应某一单一的功能模块。例如,雷达驱动节点可能负责发布雷达消息,摄像头驱动节点可能负责发布图像消息。一个完整的机器人系统可能由许多协同工作的节点组成,而ROS2中的每个可执行文件(C++程序或Python程序)就是一个节点。

话题通信

   以下通过一张图来介绍话题通信,节点A实现的功能是从摄像头获取图像,节点B的功能是向用户展示节点A获取的图像,我们通过话题进行节点A和节点B之间数据的传递;
在这里插入图片描述
   话题通信是ROS 2中的一种主要通信方式,它基于发布/订阅模型。在这种模型中,节点之间以松耦合的方式交换消息,非常适用于高频率的数据流,如传感器数据、状态更新等。

特点: 话题通信是异步的,发布者和订阅者之间是独立的。发布者可以根据自己的节奏发布数据,而订阅者也可以根据自己的需求接收并处理这些消息。此外,一个话题可以有多个发布者和多个订阅者,支持复杂的多对多通信模式。
优势: 当其中一个节点出现问题时,并不会影响其他的节点,这种设计可以使得系统在各个部分可以更加容易的独立开发和替换,提高系统的可维护性。

   下面这张图是官网上发布者和订阅者之间的关系;
在这里插入图片描述

创建python节点

  发布者和订阅者的节点可以在以前建立的功能包里面直接添加节点,也可以创建新的功能包,在新的功能包里面创建节点;创建功能包的方法详细可以看 ROS2之第一讲。我直接就在创建好的功能包里添加两个节点;

  • 添加发布者节点和订阅者节点的代码如下:
cd ws_helloworld/src/learning_pkg_py/learning_pkg_py   # cd到元文件夹下
touch topic_helloworld_pub.py topic_helloworld_sub.py   # 创建发布者和订阅者文件
  • 在learning_pkg_py文件夹下找到 setup.py 文件并打开,并在以下图中的位置添加两个节点
    在这里插入图片描述
发布者(publisher)python源码

  发布节点源码如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy
from rclpy.node import Node
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接口
    
if __name__ == '__main__':
    main()
订阅者(subscriber)python源码

  订阅节点源码如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy
from rclpy.node import Node
from std_msgs.msg import 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 hear: "%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接口
    
if __name__ == '__main__':
    main()
运行结果展示

   打开两个终端分别运行发布节点和接收节点。

  • 运行发布节点 topic_helloworld_pub.py 的脚本如下:
cd ws_helloworld/   # cd到元文件夹下
colcon build        # 编译文件
ros2 run learning_pkg_py topic_helloworld_pub      # 运行发布节点

运行结果如下:
在这里插入图片描述

ros2 run learning_pkg_py topic_helloworld_sub      # 运行接收节点

运行结果如下:
在这里插入图片描述

创建C++节点
  • 直接在创建好的功能包 learning_pkg_cpp 里面添加发布者节点和订阅者节点的代码如下:
cd ws_helloworld/src/learning_pkg_cpp/src   // cd到元文件夹下
touch topic_helloworld_pub_cpp.cpp topic_helloworld_sub_cpp.cpp   // 创建发布者和订阅者文件

创建完成后如下图所示:
在这里插入图片描述

  • CMakeLists.txt 对应的修改
    在这里插入图片描述从上图可以看见,在 CMakeLists.txt文件中田间对应节点;之后在进行编译
发布者(publisher)C++源码

  发布者节点源码如下:

#include <chrono>
#include <functional>
#include <memory>
#include <string>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;


class PublisherNode : public rclcpp::Node
{
    public:
        PublisherNode(): Node("topic_helloworld_pub_cpp")    //ROS2节点父类初始化
        {
          //创建发布者对象(消息类型、话题名、队列长度)
          publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);
          //创建一个定时器, 定时执行回调函数
          timer_ = this->create_wall_timer(500ms, std::bind(&PublisherNode::timer_callback, this ));
        }

    private:
        //创建定时器周期执行的回调函数
        void timer_callback()
        {
            // 创建一个string类型的消息对象
            auto msg = std_msgs::msg::String();
            // 填充消息对象中的消息数据
            msg.data = "hello world!";
            // 发布话题消息
            publisher_->publish(msg);
            // 输出日至信息,提示已经完成话题发布
            RCLCPP_INFO(this->get_logger(), "Publishering: '%s", msg.data.c_str());
        }
        rclcpp::TimerBase::SharedPtr timer_;                               //定时器指针
        rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;    //发布者指针

};


//ROS2节点主函数入口

int main(int argc, char * argv[])
{
    //ROS2 C++ 接口初始化
    rclcpp::init(argc, argv);

    //创建ROS2节点对象并进行初始化
    rclcpp::spin(std::make_shared<PublisherNode>());
    //关闭ROS2 C++接口
    rclcpp::shutdown();

    return 0;

}

订阅者(subscriber)C++源码

  订阅者节点源码如下:


#include <memory>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using std::placeholders::_1;

class SubscriberNode : public rclcpp::Node
{
    public:
        SubscriberNode(): Node("topic_helloworld_sub_cpp")
        {
            // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
            subscription_ = this->create_subscription<std_msgs::msg::String>(
                "chatter",10, std::bind(&SubscriberNode::topic_callback,this, _1));

        }
    private:
        void topic_callback(const std_msgs::msg::String::SharedPtr msg) const
        {
            RCLCPP_INFO(this->get_logger(), "I hear: %s", msg->data.c_str());
        }
        rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;   //订阅者指针
};


//ROS2节点主入口main函数
int main(int argc, char * argv[])
{
    //ROS2 C++接口初始化
    rclcpp::init(argc, argv);
    //创建ROS2节点对象并进行初始化
    rclcpp::spin(std::make_shared<SubscriberNode>());
    //关闭ROS2 C++ 接口
    rclcpp::shutdown();

    return 0;
    
}
运行结果展示

   打开两个终端分别运行发布节点和接收节点。

  • 运行发布节点 topic_helloworld_pub_cpp.cpp的脚本如下:
cd ws_helloworld/   # cd到元文件夹下
colcon build        # 编译文件
ros2 run learning_pkg_cpp topic_helloworld_pub_cpp      # 运行发布节点

运行结果如下:
在这里插入图片描述

  • 运行接收节点 topic_helloworld_sub_cpp.cpp 的脚本如下:
ros2 run learning_pkg_cpp topic_helloworld_sub_cpp       # 运行发布节点

运行结果如下:
在这里插入图片描述

总结

   以上为ros2话题通信的源码,以上程序传输的是字符串消息,应用的ros2中自带的消息结构,在现实的项目中难免会碰到自定义消息的时候,所以接下来我们将会讲一下,如何应用自定义消息进行话题的传递。
   以上讲解如果帮到您请点赞加收藏哦,如有错误欢迎指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xp_fangfei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值