ros之话题通信

一、话题通信理论模型
话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:

ROS Master (管理者)
Talker (发布者)
Listener (订阅者)
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。

整个流程由以下步骤实现:

0.Talker注册

Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。

1.Listener注册

Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。

2.ROS Master实现信息匹配

ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。

3.Listener向Talker发送请求

Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。

4.Talker确认请求

Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。

5.Listener与Talker件里连接

Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。

6.Talker向Listener发送消息

连接建立后,Talker 开始向 Listener 发布消息。

注意1:上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议

注意2: Talker 与 Listener 的启动无先后顺序要求

注意3: Talker 与 Listener 都可以有多个

注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker 与 Listern 照常通信。

二、简单的发布方框架

#include "ros/ros.h"
#include "std_msgs/String.h"
/*
    发布方实现
        1.包含头文件
            ros中的文本类型对应---->"std_msgs/string.h"
        2.初始化ros节点(创建二狗子对象)
        3.创建节点句柄
        4.创建发布者对象
        5.编写发布逻辑并发布数据
*/
int main(int argc, char *argv[])
{
    //2.初始化ros节点
    ros::init(argc,argv,"ergouzi");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    //5.编写发布逻辑并发布数据
    //5.1先创建被发布的消息
    std_msgs::String msg;
    //5.2编写循环,循环中发布数据
    while(ros::ok())
    {
        msg.data = "hello";
        pub.publish(msg);
    }
    return 0;
}

注意:通过ros topic echo + 话题可以查看发布的消息

三、要求以每秒10次发布文本并跟上编号

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/*
    发布方实现
        1.包含头文件
            ros中的文本类型对应---->"std_msgs/string.h"
        2.初始化ros节点(创建二狗子对象)
        3.创建节点句柄
        4.创建发布者对象
        5.编写发布逻辑并发布数据
*/
int main(int argc, char *argv[])
{
    //设置编码,解决中文乱码问题
    setlocale(LC_ALL,"");
    //2.初始化ros节点
    ros::init(argc,argv,"ergouzi");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    //5.编写发布逻辑并发布数据
    //5.1先创建被发布的消息
    std_msgs::String msg;
    //发布频率
    ros::Rate rate(10);//创建Rate对象
    //设置编号
    int count = 0;
    //为了防止先执行订阅方却还是不能接收前几条消息,因为这时发布方已经开始发送但是还未注册成功
    ros::Duration(3).sleep();
    //5.2编写循环,循环中发布数据
    while(ros::ok())
    {
        count++;
        //msg.data = "hello";
        //实现字符串拼接
        std::stringstream ss;//创建stringstream对象
        ss << "hello -->" << count;//实现拼接
        msg.data = ss.str(); //将拼接好的数据赋值给data并转换成字符串格式
        pub.publish(msg);
        //添加日志
        ROS_INFO("发布的数据是:%s",ss.str().c_str());//转换为c分格的字符串输出
        rate.sleep();//前面调用了Rate10HZ这里相当于睡0.1秒
    }
    return 0;
}

四、订阅方实现

#include "ros/ros.h"
#include "std_msgs/String.h"
/*
    订阅方实现
        1.包含头文件
            ros中的文本类型对应---->"std_msgs/string.h"
        2.初始化ros节点(创建翠花对象)
        3.创建节点句柄
        4.创建订阅者对象
        5.处理订阅到的数据
        6.spin()函数
*/
//回调函数处理订阅到的消息
void domsg(const std_msgs::String::ConstPtr &message)
{
    //通过message获取并操作订阅到的数据
    ROS_INFO("翠花订阅的数据:%s",message->data.c_str());
}
int main(int argc, char *argv[])
{
    //设置编码
    setlocale(LC_ALL,"");
    //2.初始化ros节点(创建翠花对象)
    ros::init(argc,argv,"cuihua");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建订阅者对象
    ros::Subscriber sub = nh.subscribe("fang",10,domsg);
    //5.处理订阅到的数据
    ros::spin();//循环读取接收的数据,并调用回调函数处理
    return 0;
}

五、ros中的解耦合
(1)用Python和C++写的话题通信程序两者之间可以正常数据通信。

六、话题通信自定义msg

在 ROS 通信协议中,数据载体是一个较为重要组成部分,
ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:
String、Int32、Int64、Char、Bool、Empty.... 但是,
这些数据一般只包含一个 data 字段,结构的单一意味着功能上
的局限性,当传输一些复杂的数据,比如: 激光雷达的信息... 
std_msgs 由于描述性较差而显得力不从心,这种场景下可以
使用自定义的消息类型.
msgs只是简单的文本文件,每行具有字段类型和字段名称,可以使用的字段类型有:

    int8, int16, int32, int64 (或者无符号类型: uint*)

    float32, float64

    string

    time, duration

    other msg files

    variable-length array[] and fixed-length array[C]

ROS中还有一种特殊类型:Header,标头包含时间戳和ROS中常用的坐标帧信息。会经常看到msg文件的第一行具有Header标头。

(1)在当前功能包下创建msg文件夹,里面创建Person.msg文件包含内容为自己需要的变量类型,譬如:string name、int32 age、float32 height
(2)配置当前功能包的package.xml文件,添加<build_depend>message_generation</build_depend> , <exec_depend>message_runtime</exec_depend>
(3)配置当前功能包的CMakeLists.txt有关message的相关信息

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs

## 配置 msg 源文件
add_message_files(
  FILES
  Person.msg
)

# 生成消息时依赖于 std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)

#执行时依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

(4)编译后在/home/zzh/demo03_ws/devel/include/plumbing_pub_sub下面生成一个Person.h文件,如果是Python相关的会在/home/zzh/demo03_ws/devel/lib/python3/dist-packages/plumbing_pub_sub/msg生成_Person.py文件

七、自定义msg的发布方实现

(1)配置.vscode下的c_cpp_properties.json加入自定义msg的路径
(2)消息发布方的实现

上一篇:mysql常用基础操作语法(十)~~子查询【命令行模式】


下一篇:关于LYNC 启用语音功能时,提示未能返回唯一结果