栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

ROS 通信机制

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ROS 通信机制

文章目录
  • ROS通信机制
    • 1. 话题通信
      • 1.1 基本话题通信
        • 1.1.1 C++实现
          • 发布方 demo01_pub.cpp
          • 订阅方 demo01_sub.cpp
        • 1.1.2 Python
          • 发布方 demo01_pub_p.py
          • 订阅方 demo01_sub_p.py
      • 1.2 自定义msg话题通信
        • 1.2.1 C++实现
          • 发布方 demo02_pub_person.cpp
          • 订阅方 demo02_sub_person.cpp
        • 1.2.2 Python实现
          • 发布方 demo02_pub_person_p.py
          • 订阅方 demo02_sub_person_p.py
    • 2. 服务通信
      • 2.1 服务通信实现
        • 2.1.1 C++实现
          • 服务端 demo01_server.cpp
          • 客户端 demo02_client.cpp
        • 2.1.2 Python实现
          • 服务端 demo01_server_p.py
          • 客户端 demo02_client_p.py
    • 3. 参数服务器
      • 3.1 增改
        • 3.1.1 C++实现 demo01_param_set.cpp
        • 3.1.2 Python实现
      • 3.2 查
        • 3.2.1 C++实现
        • 3.2.2 Python实现
      • 3.3 删
        • 3.3.1 C++实现
        • 3.3.2 Python实现

ROS通信机制 1. 话题通信

适用于不断更新的数据传输相关的应用场景

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

1.1 基本话题通信 1.1.1 C++实现 发布方 demo01_pub.cpp
#include"ros/ros.h"
#include"std_msgs/String.h"
#include


int main(int argc,char *argv[])
{
    // 解决中文乱码问题
    setlocale(LC_ALL,"");
    // 2.初始化ROS节点
    ros::init(argc,argv,"erGouZi");//ergouzi是节点名称,相当于是相亲对象,这里的ergouzi就是talker

    // 3.创建节点句柄
    ros::NodeHandle nh;

    // 4.创建发布者对象
    ros::Publisher pub = nh.advertise("fang",10);
    //缓存10条数据,话题名称是fang,队列长10,由于网络阻塞,没有发出去,则存储在这里,最大容量是10,超出阈值后先放进去的先销毁

    // 5.编写发布逻辑并发布数据
    
    // 5.1 创建被发布的消息
    std_msgs::String msg;

    // 5.2 要求以10HZ 的频率发布数据,并且文本后添加编号
    // 5.2.1 发布频率
    ros::Rate rate(1);//每秒发布10次
    // 5.2.2 设置编号
    int count = 0;

    // 订阅时,第一条数据丢失;原因: 发送第一条数据时, publisher 还未在 roscore 注册完毕
    ros::Duration(3).sleep();//延迟3秒

    // 5.3 编写循环,循环要发布的消息
    while(ros::ok())//如果节点还活着,循环成立
    {
        count++;
        //msg.data = "hello";
        //实现字符串的拼接
        std::stringstream ss;
        ss << "hello ---> " << count;
        msg.data = ss.str();
        pub.publish(msg);
        //添加日志:
        ROS_INFO("发布的数据是:%s",msg.data.c_str());
        rate.sleep();// 发布一次,睡0.1秒
        ros::spinOnce();//每次循环完都回头一次,spinOnce()只调用一次回调函数,之后会继续往下执行,官方建议,处理回调函数
    }
    return 0;
}
订阅方 demo01_sub.cpp
#include "ros/ros.h"
#include"std_msgs/String.h"


void doMsq(const std_msgs::String::ConstPtr &msg)//std_msgs String类型常量指针的引用,保证传入的实参是常量
{
    //通过msg获取并且操作订阅到的数据
    ROS_INFO("翠花订阅的数据:%s",msg->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,"cuiHua");//节点名称具有唯一性
    // 3.创建节点句柄
    ros::NodeHandle nh;
    // 4.创建订阅者对象
    ros::Subscriber sub = nh.subscribe("fang",10,doMsq);//fang是两者的共同话题,doMsq是回调函数
    // 5.处理订阅到的数据

    ros::spin();//spin意思是回头,main函数从上往下一次执行,回头调用doMsq函数,doMsq需要被频繁执行
    return 0;
}

1.1.2 Python 发布方 demo01_pub_p.py
#! /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) # che是话题名称,类型是String,消息队列大小为10
    # 4.编写发布逻辑并发布数据
    # 4.1 创建数据类型
    msg = String()

    # 4.2 指定发布频率
    rate = rospy.Rate(1)

    # 4.3 设置计数器
    count = 0

    # 休眠3s,完成在master下面的注册
    rospy.sleep(3)

    # 4.4 循环发布数据
    while not rospy.is_shutdown(): # 判断当前节点是否已经关闭,没有关闭则发布数据
        count += 1 # 如果要将count追加到hello后面,则需要将其变为字符串
        msg.data = "hello" + str(count)
        # 4.5 发布数据
        pub.publish(msg)
        # 4.6 添加日志输出
        rospy.loginfo("发布的数据:%s",msg.data)
        rate.sleep()
订阅方 demo01_sub_p.py
#! /usr/bin/env python

import  rospy
from std_msgs.msg import String
"""
    订阅实现流程:
        1.导包
        2.初始化ROS节点
        3.创建订阅者对象
        4.回调函数处理数据
        5.spin()
"""

def doMsg(msg): #将订阅到的数据传进来
    rospy.loginfo("订阅的数据:%s",msg.data) #data是数据

if __name__ == "__main__":

    # 2.初始化ROS节点
    rospy.init_node("huaHua")
    # 3.创建订阅者对象
    sub = rospy.Subscriber("che",String,doMsg,queue_size = 10)
    # sub = rospy.Subscriber("fang",String,doMsg,queue_size = 10) 跨语言通信
    # 4.回调函数处理数据
    # 5.spin()
    rospy.spin()
1.2 自定义msg话题通信 1.2.1 C++实现 发布方 demo02_pub_person.cpp
#include"ros/ros.h"
#include"plumbing_pub_sub/Person.h"


int main(int argc, char  *argv[])
{
    // 解决中文乱码问题
    setlocale(LC_ALL,"");

    // 添加日志
    ROS_INFO("这是消息发布方");

    // 2.初始化ROS节点
    ros::init(argc, argv,"banZhuRen");

    // 3.创建节点句柄
    ros::NodeHandle nh;

    // 4.创建发布者对象
    ros::Publisher pub = nh.advertise("liaoTian",10); // 消息类型是功能包plumbing_pub_sub下面的Person
    
    // 5.编写发布逻辑,发布数据
    // 5.1 创建被发布的数据
    plumbing_pub_sub::Person person;
    person.name = "张三";
    person.age = 1;
    person.height = 1.73;

    // 5.2 设置发布频率
    ros::Rate rate(1);

    // 5.3 循环发布数据
    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;
}
订阅方 demo02_sub_person.cpp
#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[])
{
    // 中文乱码问题
    setlocale(LC_ALL,"");
    
    //添加日志
    ROS_INFO("订阅方实现");

    // 2.初始化ROS节点
    ros::init(argc,argv,"jiaZhang");

    // 3.创建节点句柄
    ros::NodeHandle nh;

    // 4.创建订阅者对象
    ros::Subscriber sub = nh.subscribe("liaoTian",10,doPerson);

    // 5.处理订阅的数据
    // 6.spin()
    ros::spin();
    return 0;
}
1.2.2 Python实现 发布方 demo02_pub_person_p.py
#! /usr/bin/env python
"""
    发布方:发布人的消息
        1.导包
        2.初始化ROS节点
        3.创建发布者对象
        4.组织发布逻辑并发布数据

"""
import rospy
from plumbing_pub_sub.msg import Person

if __name__ == "__main__":

    # 2.初始化ROS节点
    rospy.init_node("daMa")
    # 3.创建发布者对象
    pub = rospy.Publisher("jiaoSheTou",Person,queue_size=10)
    # 4.组织发布逻辑并发布数据
    # 4.1 创建Person
    p = Person()
    p.name = "奥特曼"
    p.age = 8
    p.height = 1.85
    # 4.2 创建Rate对象
    rate = rospy.Rate(1)
    # 4.3 循环发布数据
    while not rospy.is_shutdown():
        pub.publish(p)
        rospy.loginfo("发布的消息:%s,%d,%.2f",p.name,p.age,p.height)
        rate.sleep()
订阅方 demo02_sub_person_p.py
#! /usr/bin/env python
"""
    订阅方:订阅人的消息
        1.导包
        2.初始化ROS节点
        3.创建订阅者对象
        4.处理订阅的数据
        5.spin()

"""

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__":
    # 2.初始化ROS节点
    rospy.init_node("daYe")
    # 3.创建订阅者对象
    sub = rospy.Subscriber("jiaoSheTou",Person,doPerson,queue_size=10)
    # 4.处理订阅的数据
    # 5.spin()
    rospy.spin()

2. 服务通信

适用于对时时性有要求、具有一定逻辑处理的应用场景。基于请求响应模式的,是一种应答机制。一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。

ROS Master 负责保管 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client ,帮助 Server 与 Client 建立连接,连接建立后,Client 发送请求信息,Server 返回响应信息。

和话题通信相比,服务通信必须是服务端先启动,然后客户端启动。就是说客户端发起请求时,服务端已经启动。

srv包括请求+响应

srv目录下的AddInts里面进行设置时除了32后面其他地方不能加空格

2.1 服务通信实现 2.1.1 C++实现 服务端 demo01_server.cpp
#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"


bool doNums(plumbing_server_client::AddInts::Request &request,
            plumbing_server_client::AddInts::Response &response){
    //1.处理请求
    int num1 = request.num1;
    int num2 = request.num2;
    ROS_INFO("收到的请求数据:num1 = %d,num2 = %d",num1,num2);
    //2.组织响应
    int sum = num1 + num2;
    response.sum = sum;
    ROS_INFO("求和的结果:sum = %d",sum);

    return true;
}
int main(int argc, char *argv[])
{
    //解决乱码问题
    setlocale(LC_ALL,"");

    // 2.初始化ROS节点
    ros::init(argc,argv,"heiShui");//节点名称

    // 3.创建节点句柄
    ros::NodeHandle nh;

    // 4.创建一个服务对象
    ros::ServiceServer server = nh.advertiseService("addInts",doNums);//addInts是话题名称,用于连接二者
    ROS_INFO("服务器端启动");

    // 5.处理请求并产生响应
    // 6.spin()
    ros::spin();
    
    return 0;
}
客户端 demo02_client.cpp
#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"


//  char* argv[]:是一个数组,每个元素存储的一个指针,就是穿进去的参数的地址。
int main(int argc, char  *argv[])//字符串数组,数组中存放的是数据的地址,读取时是读取数组里存储的地址对应的数据
{
    //解决乱码问题
    setlocale(LC_ALL,"");

    //优化实现,获取命令中参数,实现动态 // 有1个参数argc为2,函数本身1+参数1=argc2
    if(argc != 3){
        ROS_INFO("提交的参数个数不对。");
        return true;
    }

    // 2.初始化ROS节点
    ros::init(argc,argv,"daBao");

    // 3.创建节点句柄
    ros::NodeHandle nh;

    // 4.创建一个客户端对象
    ros::ServiceClient client = nh.serviceClient("addInts");//话题是addInts

    // 5.提交请求并产生响应
    plumbing_server_client::AddInts ai;
    // 5.1 组织请求
    ai.request.num1 = atoi(argv[1]);//argv[1]表示的是12,atoi将字符串变成整型
    ai.request.num2 = atoi(argv[2]);//argv[2]表示的是34
    // 5.2 处理响应
    //判断服务器状态的函数
    //函数1
    //client.waitForExistence();//客户端等待,实现挂起功能
    //函数2
    ros::service::waitForService("addInts");
    bool flag = client.call(ai);// true表示正常处理,否则为失败
    if(flag)
    {
        ROS_INFO("响应成功");
        //获取结果
        ROS_INFO("响应结果:%d",ai.response.sum);
    }
    else{
        ROS_INFO("处理失败");
    }

    return 0;
}
2.1.2 Python实现 服务端 demo01_server_p.py
#! /usr/bin/env python # 指定编译器
"""
    服务端:解析客户端请求,产生响应。

        1.导包
        2.初始化ROS节点
        3.创建服务端对象
        4.处理逻辑
        5.spin()
"""

import rospy
from plumbing_server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
# import plumbing_server_client import *

# 回调函数
# 参数:封装了请求数据 返回值:响应数据
def doNum(request):
    # 1. 解析提交的两个整数
    num1 = request.num1
    num2 = request.num2

    # 2. 求和
    sum = num1 + num2

    # 3. 将结果封装进响应
    response = AddIntsResponse()
    response.sum = sum # 将sum赋值给响应对象的sum,即可完成封装
    rospy.loginfo("服务器解析的数据:num1 = %d, num2 = %d,响应的结果:sum = %d",num1,num2,sum)
    return response

if __name__ == "__main__":
    
    # 2.初始化ROS节点
    rospy.init_node("heiShui")

    # 3.创建服务端对象
    server = rospy.Service("addInts",AddInts,doNum)# Addints表示消息对应类型,addInts表示话题名称
    rospy.loginfo("服务器已经启动了")
    # 4.处理逻辑
    # 5.spin()
    rospy.spin()
  • 问题:如果先启动客户端,那么会请求异常
  • 需求:如果先启动,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求
  • 解决:在ROS中提供内置相关函数,可以让客户端启动后挂起,等待服务端启动
  • client.waitForExistence()
  • ros::service::waitForService(“话题名称”)
客户端 demo02_client_p.py
#! /usr/bin/env python # 指定编译器
"""
    客户端:组织并提交请求,处理服务端响应
        1.导包
        2.初始化ROS节点
        3.创建客户端对象
        4.组织请求数据,并且发送请求
        5.处理来自服务端的响应

    优化实现:
        可以在执行节点时,动态传入参数,使用sys
    
    
    问题:
        客户端先于服务端启动,那么会抛出异常
    需求:
        如果客户端先启动,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求。
    解决:
        在ROS中提供内置相关函数,可以让客户端启动后挂起,等待服务端启动
"""

import rospy
from plumbing_server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
import sys

if __name__ == "__main__":

    # 判断参数长度
    if len(sys.argv) != 3:
        rospy.loginfo("传入的参数个数不对")
        sys.exit(1)

    # 2.初始化ROS节点
    rospy.init_node("erHei")

    # 3.创建客户端对象
    client = rospy.ServiceProxy("addInts",AddInts)

    # 4.组织请求数据,并且发送请求,call函数就是发请求的
    # 解析传入的参数 argv[0]表示程序名
    num1 = int(sys.argv[1])
    num2 = int(sys.argv[2]) # 将字符串转化为整型

    # 在客户端之后,发送请求之前调用。等待服务器启动
    # client.wait_for_service()
    rospy.wait_for_service("addInts")

    response = client.call(num1,num2)

    # 5.处理来自服务端的响应
    rospy.loginfo("相应的数据:%d",response.sum)
  • 问题:客户端先于服务端启动,那么会抛出异常
  • 需求:如果客户端先启动,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求
  • 解决:在ROS中提供内置相关函数,可以让客户端启动后挂起,等待服务端启动
  • 法1:client.wait_for_service()
  • 法2:rospy.wait_for_service(“addInts”)
3. 参数服务器

实现不同节点之间的数据共享

参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,不同的节点也可以往其中存储数据。

  • rosparam list:可以列出参数服务器里的参数
  • rosparam get /type:得到类型参数的具体值
  • rosparam get /type_param
3.1 增改 3.1.1 C++实现 demo01_param_set.cpp
#include"ros/ros.h"


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

    // 创建ROS节点句柄
    ros::NodeHandle nh;

    // 参数增-------------------------
    // 方案1:nh
    nh.setParam("type","xiaoHuang");// 类型
    nh.setParam("radius",0.15);// 半径
    // 方案2:ros::param
    ros::param::set("type_param","xiaoBai");
    ros::param::set("radius_param",0.15);

    // 参数改-------------------------
    // 方案1:nh
    nh.setParam("radius",0.2);//参数的覆盖
    
    // 方案2:ros::param
    ros::param::set("radius_param",0.25);

    return 0;
}
3.1.2 Python实现 3.2 查 3.2.1 C++实现 3.2.2 Python实现 3.3 删 3.3.1 C++实现 3.3.2 Python实现
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1009014.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号