ROS学习(5)——话题消息与服务

节点之间的消息通信分为几种形式:

  • 话题(topic):单向消息发送/接收方式
  • 服务(service):双向消息请求/响应方式
  • 动作(action):双向消息目标(goal)/结果(result)/反馈(feedback)方式
  • 参数服务器(参数共享模式)

种类 区别
话题 异步 单向 连续单向的发送/接收数据的情况
服务 同步 双向        ` 需要对请求给出即时相应的情况
动作 异步 双向   

请求与响应之间需要太长的时间,所以难以使用服务的情况,

或需要中途返回值的情况

节点通信的过程

一、运行主节点

节点之间的消息通信当中,管理连接信息的主节点是使用 ROS 必须首先运行的必需元
素。ROS 主节点使用 roscore 命令来运行,并使用 XMLRPC 运行服务器。

主节点为了节点与节点的连接,会注册节点的名称、话题、消息类型、 URI 地址和端口,
并在有请求时将此信息通知给其他节点。

 二、运行发布者节点

发布者节点使用 rosrun 或 roslaunch 命令来运行。发布者节点向主节点注册发布者节
点名称、话题名称、消息类型、URI 地址和端口。主节点和节点使用 XMLRPC 进行通
信。

  三、运行订阅者节点

扫描二维码关注公众号,回复: 15529682 查看本文章

订阅者节点使用 rosrun 或 roslaunch 命令来运行。订阅者节点在运行时向主节点注册其订阅者节点名称、话题名称、消息类型、 URI 地址和端口。主节点和节点使用 XMLRPC
进行通信。

四、通知发布者信息

主节点向订阅者节点发送此订阅者希望访问的发布者的名称、话题名称、消息类型、
URI 地址和端口等信息。主节点和节点使用 XMLRPC 进行通信。

 五、订阅者节点的连接请求

订阅者节点根据从主节点接收的发布者信息,向发布者节点请求直接连接。在这种情况下,要发送的信息包括订阅者节点名称、话题名称和消息类型。发布者节点和订阅
者节点使用 XMLRPC 进行通信。

六、发布者节点的连接响应

发布者节点将 TCP 服务器的 URI 地址和端口作为连接响应发送给订阅者节点。发布者
节点和订阅者节点使用 XMLRPC 进行通信。

 七、TCPROS 连接

订阅者节点使用 TCPROS 创建一个与发布者节点对应的客户端,并直接与发布者节点
连接。节点间通信使用一种称为 TCPROS 的 TCP/IP 方式。

  八、发送消息

发布者节点向订阅者节点发送消息。节点间通信使用一种称为 TCPROS 的 TCP/IP
方式。

下面是实现者(Publisher)发布话题和接收者(Subcriber)接收话题的例子

一、发布者

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

int main(int argc, char *argv[])
{
    // 1.设置编码
    setlocale(LC_ALL,"");

    //2.初始化 ROS 节点:命名(唯一)
    // 参数 1 和参数 2 后期为节点传值会使用
    // 参数 3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
    ros::init(argc, argv, "publisher");

    //3.实例化 ROS 句柄
    ros::NodeHandle nh;  //该类封装了 ROS 中的一些常用功能

    //4.实例化 发布者 对象
    //泛型: 发布的消息类型
    //参数 1: 要发布到的话题
    //参数 2: 队列中最大保存的消息数,超出此阈值时,先进的先销毁(时间早的先销毁)
    ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);

    //5.组织被发布的数据,并编写逻辑发布数据
    //数据(动态组织)
    std_msgs::String msg;
    std::string msg_front = "Hello,你好啊!";  // 消息前缀
    int count = 0;  //消息计数器

    //逻辑(一秒 10 次)
    ros::Rate r(1);

    //节点不死;
    while(ros::ok())
    {
        //使用 stringstream 拼接字符串与编号
        std::stringstream ss;
        ss<<msg_front<<count;
        msg.data = ss.str();
        //发布消息
        pub.publish(msg);
        //加入调试,打印发送的消息
        ROS_INFO("发送的消息:%s",msg.data.c_str());

        //根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;
        r.sleep();
        count++;//循环结束前,让 count 自增
        ros::spinOnce();
    }
    return 0;
}

二、接收者

// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h"

void doMsg(const std_msgs::String::ConstPtr& msg_p){
    ROS_INFO("我订阅:%s",msg_p->data.c_str());
    // ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"subscriber");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;
    //4.实例化 订阅者 对象
    ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
    //5.处理订阅的消息(回调函数)

    //6.设置循环调用回调函数

    ros::spin(); //循环读取接收的数据,并调用回调函数处理
    return 0;
}

三、CMakeLists文件编写(不会的小伙伴可以参考ROS学习(3)——CMakeLists文件的编写

cmake_minimum_required(VERSION 3.0.2)
project(hello)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
)

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES hello
#  CATKIN_DEPENDS roscpp rospy std_msgs
#  DEPENDS system_lib
)

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
add_executable(publish_man src/publish_man.cpp)
add_executable(subscrib_man src/subscrib_man.cpp)

## Specify libraries to link a library or executable target against
target_link_libraries(publish_man
  ${catkin_LIBRARIES}
)
target_link_libraries(subscrib_man
  ${catkin_LIBRARIES}
)

四、编写launch文件(不会的小伙伴可以参考ROS学习(4)——launch文件的编写

<launch>
    <node pkg="hello" type="publish_man" name="pub" output="screen" />
    <node pkg = "hello" type = "subscrib_man" name = "sub" output = "screen" />
</launch>

 五、在终端运行launch文件:

source ./devel/setup.bash
roslaunch <功能包名> <launch文件名.launch>

得到运行结果如下:

 实现了发布消息和接收消息的过程。

需要注意的几点:

1.main函数自动补齐的格式是

int main(int argc, char const *argv[]){}

我们需要把里面的const给删掉。

2.第一条数据丢失

原因是发送第一条数据时,publisher还没有在roscore注册完毕。

解决方法是,注册后,加入休眠 ros::Duration(3.0).sleep(); 延迟第一条数据的发送。

3.出现错误ros/ros.h No such file or directory

检查CMakeLists里面是否有重复的部分,将其删除。

猜你喜欢

转载自blog.csdn.net/bulletstart/article/details/130795717
今日推荐