话题通信实例1

写在前面的话


  • 文档没有任何商业因素,本着共享的精神进行分享,如有素材侵权,请给我留言;
  • 文档都是自己平时看书或工作中的笔记,观点错误的地方欢迎留言;

节点关系图:


我们尝试创建一个简单的话题通信的例子,节点创建一个Publisher 和 Topic,并向这个 Topic 发布字符串 “Hello World”;另一个节点创建一个 Listener,并订阅这个 Topic,打印出收到的消息;消息的类型,是基于系统提供的。
我们要创建一个如下的结构的计算图:

在这里插入图片描述


创建 base_communication 功能包


使用如下命令创建 base_communication 功能包:

$ cd ~/catkin_ws/src
$ catkin_create_pkg base_communication std_msgs rospy roscpp

创建 Publisher


创建 Publisher 的流程主要分为四步:

  1. 创始话节点;
  2. 向 Master 注册节点信息:包括要发布的话题名,消息类型,话题缓存大小;
  3. 按照一定的频率发布消息;
  4. 依次处理所有订阅者的回调函数;

源代码如下 base_communication\src\talker.cpp

#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"

int main(int argc, char **argv)
{
    ros::init(argc, argv, "talker"); // ROS 节点初始化

    ros::NodeHandle n;  // 创建节点句柄
    
    // 创建一个Publisher, 发布名称为 chatter的topic, 消息类型为 std_msgs::String
    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

    ros::Rate loop_rate(10);  // 设置循环频率
    int count = 0;
    while(ros::ok()) {
        // 初始化std_msg::String类型的消息
        std_msgs::String msg;
        std::stringstream ss;
        ss << "hello world " << count;
        msg.data = ss.str();

        chatter_pub.publish(msg); // 发布消息
        ROS_INFO("%s", msg.data.c_str());

        ros::spinOnce(); // 逐个处理订阅者的回调函数
        loop_rate.sleep(); // 按照循环频率延时
        count++;
    }

    return 0;
}

代码分析:

  • 头文件部分
    #include "ros/ros.h"
    #include "std_msgs/String.h"
    
    • ros/ros.h:包含了 ros 中要用到的绝大部分头文件;
    • std_msgs/String.h 是由 String.msg 的消息结构定义文件自动生成;我们也可以自定义消息结构,并生成所需要的头文件;

  • 初始化节点:ros::init(argc, argv, "talker"),这个函数用来初始化当前节点,参数含义如下:
    • argc, argv:命令行参数或者 launch 文件输入的参数,可以用来完成命名重映射等功能;
    • talker:定义了节点的名称,而且该名称必须是独一无二的;

  • 创建 Publisher 和 发布主题
    ros::NodeHandle n;
    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("cahtter", 1000);
    
    • ros::NodeHandle n:创建了一个节点句柄,用来管理节点资源;
    • ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); 表示在 Master 中注册了一个 Publisher,并设置了信息类型 std_msgs::String,设置 Topic 名字 ”chatter“,设置消息队列的大小 1000 个;Publisher 发布的消息会依次存储在消息队列中,如果实际的消息数量超过了消息队列的大小,ROS 会自动删除最早入队的消息;

  • 异常判断:ros::ok():主要用来监测异常,如果出现异常就返回 false;这里的异常主要包括:
    • 收到 SIGINT 信号(Ctrl + C);
    • 被另一个相同名称的节点踢掉线;
    • 节点调用了关闭函数 ros::shutdown()
    • 所有 ros:: NodeHandles 句柄被销毁;

  • 初始化消息
    std_msgs::String msg;
    std::stringstream ss;
    ss << "hellow world " << count;
    msg.data = ss.str();
    
    • 这里使用了最简单的消息类型 String,它只有一个成员,即 data;

  • 发布消息
    chatter_pub.publish(msg);
    ROS_INFO("%s", msg.data.c_str());
    
    • 发布封装完毕的消息 msg。消息发布之后,Master 会查阅订阅该话题节点,帮助两个节点建立联系,完成消息传递;
    • ROS_INFO() 类似 C/C++ 中的 printf/cout 函数,用来打印日志消息;这里显示要发送的消息;

  • 处理订阅者的回调函数:ros::spinOnce()
    • 该函数用来依次处理连接上此节点的所有订阅者的回调函数;

  • 延时函数:
    ros::Rate loop_rate(10);
    loop_rate.sleep();
    
    • ros::Rate loop_rate(10):设置了频率;
    • loop_rate.sleep():按设置的频率进行休眠;

创建 Listener


创建 Subsriber 的流程主要分为四步:

  1. 初始化 ROS 节点;
  2. 向 Master 注册节点信息:订阅话题,指明话题名、消息类型,话题缓存大小;
  3. 按照一定的频率循环接收消息;
  4. 在回调函数中依次处理消息;

源码如下 base_communication\src\listener_1.cppbase_communication\src\listener_2.cpp,这里设置了两个节点,它们都订阅了上面设置的主题;

#include "ros/ros.h"
#include "std_msgs/String.h"


// 接收到订阅方的消息后,会进入消息回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
    // 将接收到的消息打印出来
    ROS_INFO("I heard: [%s], msg->data.c_str()");
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "listener_1");  // 初始化ROS节点,另一个是 listener_2;

    ros::NodeHandle n; // 创建节点
    // 创建一个 Subscriber,订阅名为 chatter 的话题,注册回调函数 chatterCallback
    ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

    ros::spin(); // 循环接收主题消息

    return 0;
}

代码解析:

  • 设置回调函数
    void chatterCallback(const std_msgs::String::ConstPtr& msg)
    {
        // 将接收到的消息打印出来
        ROS_INFO("I heard: [%s], msg->data.c_str()");
    }
    
    • 回调函数是订阅节点接收消息的基础机制,当有消息到达时会自动以消息指针作为参数,再调用回调函数,完成对消息内容的处理;
    • 此回调函数完成对消息的打印操作;

  • 订阅话题:
    ros::NodeHandle n; // 创建节点
    // 创建一个 Subscriber,订阅名为 chatter 的话题,注册回调函数 chatterCallback
    ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
    
    • 订阅节点订阅话题,指明话题名,消息队列大小,和回调函数;该操作会完成订阅节点在 Master 中的注册操作。Master 会关注系统中是否存在发布该话题的节点,如果存在会帮助两个节点建立,完成数据传输。

  • 接收消息:ros::spin():该代码会是节点进入循环状态,当有消息到达时,会尽快调用回调函数完成处理。ros::spin()ros::ok() 返回 false 时退出。

修改 CMakeFile.txt 文件并编译


如果是用 C++ 编写的代码,则需要对其进行编译;如果是用 Python 编写的则不需要对其进行编译;
我们需要修改 base_communication/CMakeList.txt 中的部分配置。CMakeList.txt 中每一部分的设置都有详细的描述,我们只需要找到相关配置项,去掉注释并稍作修改。
相关的配置设置主要包含四个方面:

  • include_directories:设置头文件的相对路径。
    • 全局路径默认是功能包的所在目录,此处需要添加的是功能包下的 include 文件;
    • ROS catkin 编译器默认包含的其他文件路径,比如 ROS 默认安装路径、Linux 系统路径等;
  • add_executable:用于设置需要编译的代码和生成可执行文件:
    • 第一个参数表示期望生成的可执行文件的名称;
    • 第二个参数表示参与编译的源码,如果存在多个源码,依次列出,空格作为分隔;
  • target_link_libraries:用于设置链接库;
    • 很多功能需要使用到系统或第三方库函数,通过该选项可以配置执行文件链接的库文件;
    • 第一个参数表示可执行文件的名称;
    • 第二个参数表示需要链接的库,此处编译的 Publisher 和 Subscriber 没有使用其他库,添加默认链接库即可;
  • add_dependencies:设置依赖项。
    • 我们定义的是语言无关的消息类型,消息类型会在编译过程中产生相应语言的代码,如果编译的可执行代码文件依赖这些动态生成的代码,则需要使用 add_pendencies 添加 ${PROJECT_NAME}_generate_message_cpp 配置,即该功能包动态产生消息代码;
    • 该编译规则也可以添加其他需要依赖的功能包;
include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp)

add_executable(listener_1 src/listener.cpp)
target_link_libraries(listener_1 ${catkin_LIBRARIES})

add_executable(listener_2 src/listener.cpp)
target_link_libraries(listener_2 ${catkin_LIBRARIES})

使用如下命令会执行编译操作:

$ cd ~/catkin_ws
$ catkin_make

以上编译会产生三个可执行文件:talker、listener_1, listener_2;放置在目录 ~/catkin_ws/devel/lib/<packagename>;


运行创建者和发布者


  • 设置环境变量
    $ cd ~/catkin_ws
    $ source ./devel/setup.bash
    // 或者
    $ echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
    $ source ~/.bashrc
    
  • 启动 roscore:在运行节点之前要确保 ROS Master 已经成功启动;
    $ roscore
    
  • 启动 Publisher:$ rosrun base_communication talker; 结果如下:

在这里插入图片描述

  • 启动 Subscribe_1:$ rosrun base_communication listener_1;结果如下:

在这里插入图片描述

  • 启动 Subscribe_2:$ rosrun base_communication listener_2;结果如下:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值