ROS通信机制

 机器人是一种高度复杂的系统性实现,在机器人上可能集成各种传感器(雷达、摄像头、GPS…)以及运动控制实现,为了解耦合,在ROS中每一个功能点都是一个单独的进程,每一个进程都是独立运行的。更确切的讲,ROS是进程(也称为Nodes)的分布式框架。
    ROS 中的基本通信机制主要有如下三种实现策略:

  1. 话题通信(发布订阅模式)
  2. 服务通信(请求响应模式)
  3. 参数服务器(参数共享模式)

一、话题通信

1、概念

以发布订阅的方式实现不同节点之间数据交互的通信模式

2、话题通信理论模型

话题通信流程

对于话题通信的流可以理解为,起初tallker在master处注册自身信息以及自身的rpc地址,接着当listener在master处注册自身信息时,master比对已注册的tallker信息,对于匹配复合的tallker信息,将该tallker的rpc地址发送给listener,listener根据rpc地址链接tallker地址,tallker并把自身tcp地址传给listener,listener根据tcp地址连接tallker,连接成功后tallker即可与listener发送信息。

3、话题通信基本操作(C++)

在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:

  • 发布方
  • 接收方
  • 数据(此处为普通文本)

流程:

  • 编写发布方实现;
  • 编写订阅方实现;
  • 编辑配置文件;
  • 编译并执行。

(1)编写发布方实现

1、首先打开vs,找到自己的工作目录,新建功能包,命名为plumbing_pub_sub,回车后输入依赖roscpp rospy std_msgs回车

2、这样工作空间就创建完成了,找到创建好的文件下的src目录,新建文件demo01_pub.cpp回车

3、编写c++文件

#include "ros/ros.h"
#include "std_msgs/String.h"
/*
    发布实现方式:
    1、包含头文件;
    2、初始化RSO节点;
    3、创建节点句柄;
    4、创建发布者对象;
    5、编写发布逻辑并发布数据。
*/

int main(int argc, char *argv[])
{
    // 2、初始化RSO节点;
    ros::init(argc,argv,"erGouZi");
    // 3、创建节点句柄;
    ros::NodeHandle nh;
    // 4、创建发布者对象;
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    // 5、编写发布逻辑并发布数据。
    //先创建被发布的消息
    std_msgs::String msg;
    //编写循环,循环中发布数据
    while (ros::ok())
    {
        msg.data = "hello";
        pub.publish(msg);
    }
    
    return 0;
}

ctrl+shift+b编译运行查看是否报错

4、修改CMakeList.txt文件

ctrl+shift+b编译运行查看是否报错

5、打开三个终端

第一个终端输入roscore,第二个终端输入

cd demo02_ws/
source ./devel/setup.bash
rosrun plumbing_pub_sub demo01_pub

第三个终端输入

rostopic echo fang

6、发送数据添加编号

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "sstream"
/*
    发布实现方式:
    1、包含头文件;
    2、初始化RSO节点;
    3、创建节点句柄;
    4、创建发布者对象;
    5、编写发布逻辑并发布数据。
*/

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2、初始化RSO节点;
    ros::init(argc,argv,"erGouZi");
    // 3、创建节点句柄;
    ros::NodeHandle nh;
    // 4、创建发布者对象;
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    // 5、编写发布逻辑并发布数据。
    //要求以10hz的频率发布数据,并且文本后添加编号
    //先创建被发布的消息
    std_msgs::String msg;
    //发布频率
    ros::Rate rate(10);
    //设置编号
    int count = 0;
    //编写循环,循环中发布数据
    while (ros::ok())
    {
        count++;
        //实现字符串拼接数字
        std::stringstream ss;
        ss << "hello ---> " << count;
        // msg.data = "hello";
        msg.data = ss.str();
        pub.publish(msg);
        //添加日在
        ROS_INFO("发布的数据是:%s",ss.str().c_str());
        rate.sleep();
    }
    
    return 0;
}

(2)编写订阅方实现

开头步骤与前面一致,创建c++文件demo02_sub.cpp,输入代码

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "sstream"
/*
    发布订阅方式:
    1、包含头文件;
    2、初始化RSO节点;
    3、创建节点句柄;
    4、创建订阅者对象;
    5、处理订阅到的数据。
    6、spin函数。
*/

void doMsg(const std_msgs::String::ConstPtr &msg){
    //通过msg获取并操作订阅到的数据
    ROS_INFO("翠花订阅的数据:%s",msg->data.c_str());
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2、初始化RSO节点;
    ros::init(argc,argv,"cuiHua");
    // 3、创建节点句柄;
    ros::NodeHandle nh;
    // 4、创建订阅在对象;
    ros::Subscriber sub = nh.subscribe("fang",10,doMsg);
    // 5、处理订阅到的数据。

    ros::spin();
    
    return 0;
}

5、打开三个终端

第一个终端输入roscore,第二个终端输入

cd demo02_ws/
source ./devel/setup.bash
rosrun plumbing_pub_sub demo01_pub

第三个终端输入

cd demo02_ws/
source ./devel/setup.bash
rosrun plumbing_pub_sub demo02_sub

(3)计算图

4、话题通信基本操作(python)

流程:

  1. 编写发布方实现;
  2. 编写订阅方实现;
  3. 为python文件添加可执行权限;
  4. 编辑配置文件;
  5. 编译并执行。

(1)编写发布方实现

1、新建plumbing_pub_sub文件夹

2、新建demo01_pub_p.py文件

3、编写代码

#! /usr/bin/env python

import rospy
from std_msgs.msg import String #发布的消息的类型

"""
使用python实现消息的发布
    1、导包
    2、初始化ros节点
    3、创建发布者对象
    4、编写发布逻辑并发布数据
"""

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    rospy.init_node("sanDai") #传入节点名称
    #3.实例化 发布者 对象
    pub = rospy.Publisher("che",String,queue_size=10)
    #4.组织被发布的数据,并编写逻辑发布数据
    #创建数据
    msg = String()  

    # 使用循环发布数据
    while not rospy.is_shutdown():
        msg.data = "hello"
        # 发布数据
        pub.publish(msg)

4、给plumbing_pub_sub文件夹权限,打开该文件夹终端输入命令

chmod +x *.py

5、修改CMakeList.txt文件

6、编译ctrl+shift+b

打开三个终端

一个

roscore

另一个

source ./devel/setup.bash

最后一个

rostopic echo che

7、完善后的代码

#! /usr/bin/env python

import rospy
from std_msgs.msg import String #发布的消息的类型

"""
使用python实现消息的发布
    1、导包
    2、初始化ros节点
    3、创建发布者对象
    4、编写发布逻辑并发布数据
"""

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    rospy.init_node("sanDai") #传入节点名称
    #3.实例化 发布者 对象
    pub = rospy.Publisher("che",String,queue_size=10)
    #4.组织被发布的数据,并编写逻辑发布数据
    #创建数据
    msg = String()  
    #制定发布频率
    rate = rospy.Rate(1)
    #设置计数器
    count = 0
    # 使用循环发布数据
    while not rospy.is_shutdown():
        count += 1
        msg.data = "hello" + str(count)
        # 发布数据
        pub.publish(msg)
        rospy.loginfo("发布的数据是:%s",msg.data)
        rate.sleep()

(2)编写订阅方实现

步骤与上述相同

新建文件demo02_sub_p.py,输入代码

#! /usr/bin/env python
import rospy
from std_msgs.msg import String

def doMsg(msg):
    rospy.loginfo("我订阅的数据:%s",msg.data)

if __name__ == "__main__":
    
    rospy.init_node("huaHua")

    sub = rospy.Subscriber("che",String,doMsg,queue_size=10)
    
    rospy.spin()

修改文件

编译运行

(3)计算图

补充

解耦合,对于不同编译语言程序可以互通。

5、话题通信自定义msg

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

1.定义msg文件

功能包下新建 msg 目录,添加文件 Person.msg,输入内容

string name
uint32 age
float32 height

2.编译package.xml文件

新增两条代码

<build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

2、编译CMakeList.txt

3、编译

ctrl+shift+b编译后

6、话题通信自定义msg(c++)

流程:

  1. 编写发布方实现;
  2. 编写订阅方实现;
  3. 编辑配置文件;
  4. 编译并执行。

0、vscode配置

为了方便代码提示以及避免误抛异常,需要先配置 vscode,将前面生成的 head 文件路径配置进 c_cpp_properties.json 的 includepath属性:

找到工作空间下的devel文件夹,右键集成终端打开,输入pwd,获得路径,复制,打开.vscode文件夹下的c_cpp_properties.json文件,添加该路径

1、编写发布方实现

#include "ros/ros.h"
#include "plumbing_pub_sub/person.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"banZhuRen");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<plumbing_pub_sub::person>("liaoTian",10);
    plumbing_pub_sub::person person;
    person.name = "张三";
    person.age = 1;
    person.height = 1.73;
    ros::Rate rate(1);
    while(ros::ok()){
        person.age =+1;
        pub.publish(person);

        rate.sleep();
        ros::spinOnce();
    }
    return 0;
}

修改配置文件

ctrl+shift+b编译一下是否通过。

完善代码

#include "ros/ros.h"
#include "plumbing_pub_sub/person.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ROS_INFO("这是消息的发布方");
    ros::init(argc,argv,"banZhuRen");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<plumbing_pub_sub::person>("liaoTian",10);
    plumbing_pub_sub::person person;
    person.name = "张三";
    person.age = 1;
    person.height = 1.73;
    ros::Rate rate(1);
    while(ros::ok()){
        person.age =+1;
        pub.publish(person);
        ROS_INFO("发布的消息:%s,%d,%2f",person.name.c_str(),person.age,person.height);
        rate.sleep();
        ros::spinOnce();
    }
    return 0;
}

2、编写订阅方实现

#include "ros/ros.h"
#include "plumbing_pub_sub/person.h"

void doPerson(const plumbing_pub_sub::person::ConstPtr& person){
    ROS_INFO("订阅的人的信息:%s,%d,%2f",person->name.c_str(),person->age,person->height);

}

int main(int argc, char *argv[])
{
    /* code */
    setlocale(LC_ALL,"");
    ROS_INFO("订阅方实现");
    ros::init(argc,argv,"jiaZhang");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("liaoTian",10,doPerson);
    ros::spin();

    return 0;
}

3、计算图

7、话题通信自定义msg(python)

流程:

  1. 编写发布方实现;
  2. 编写订阅方实现;
  3. 为python文件添加可执行权限;
  4. 编辑配置文件;
  5. 编译并执行。

0、vscode配置

找到工作目录下的devel文件夹,接着找到lib文件夹,对于lib文件夹下的python3文件夹打开集成终端,输入pwd,复制路径,打开.vscode文件夹下的settings.jsan,添加路径

1、编写发布方实现

#! /usr/bin/env python

import rospy
from plumbing_pub_sub.msg import person

if __name__ == "__main__":
    rospy.init_node("daMa")
    pub =rospy.Publisher("jiaoSheTou",person,queue_size=10)
    p = person()
    p.name = "奥特曼"
    p.age = 8
    p.height = 1.85
    rate = rospy.Rate(1)
    while not rospy.is_shutdown():
        pub.publish(p)
        rospy.loginfo("发布的消息:%s,%d,%.2f",p.name,p.age,p.height)
        rate.sleep()
    
    pass

2、编写订阅方实现

#! /usr/bin/env python

import rospy
from plumbing_pub_sub.msg import person

def doperson(p):
    rospy.loginfo("小伙子的数据:%s,%d,%.2f",p.name,p.age,p.height)

if __name__ == "__main__":
    rospy.init_node("daYe")
    sub = rospy.Subscriber("jiaoSheTou",person,doperson)
    rospy.spin()
    

3、计算图

二、服务通信

1、概念

服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。比如如下场景:

机器人巡逻过程中,控制系统分析传感器数据发现可疑物体或人... 此时需要拍摄照片并留存。

在上述场景中,就使用到了服务通信。

  • 一个节点需要向相机节点发送拍照请求,相机节点处理请求,并返回处理结果

与上述应用类似的,服务通信更适用于对时时性有要求、具有一定逻辑处理的应用场景。


概念

以请求响应的方式实现不同节点之间数据交互的通信模式。

作用

用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。

参考资料

038话题通信_理论模型_Chapter2-ROS通信机制_哔哩哔哩_bilibili

【ROS】—— ROS通信机制——话题通信(二)_vscode中编写ros2的udp通信-CSDN博客

ROS的通讯机制_ros通信机制-CSDN博客

Introduction · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值