ROS通信机制

        机器人系统大多数都是多功能的集合体,而每一个功能都可以定义为一个单独的进程,每一个进程都是独立运行的。采用进程的分布式框架,可以在提高不同功能的协同性的同时分散计算压力。而不同进程之间就是靠通信机制做数据交换的。ROS中的基本通信机制有三种实现策略:

  • 话题通信(发布-订阅模式)
  • 服务通信(请求-响应模式)
  • 参数服务器(参数共享模式)

一、话题通信

1.概念及应用场景

        以激光雷达信息的采集处理为例,在ROS中需要有一个节点实时发布雷达采集到的数据,而导航模块中就需要有一个节点订阅并解析该雷达数据。话题通信主要是用于连续高频、少逻辑处理的数据传输场景,属于多对多的异步通信。

2.整体架构

        话题通信模型主要是由三个角色组成,分别是管理者、发布者、订阅者。不走底层逻辑,浅层理解的话,就是①发布者和订阅者要先向管理者注册自己的信息,让发布者和订阅者能够互相找到对方;②发布者开通某个话题,并在话题上发布信息(不用在乎谁是订阅者,发就是了);③订阅者打开这个对应的这个话题,就能收到信息了(不用在乎是谁发的,收就是了)。

3.主要API(C++)

创建发布者/订阅者对象

// 发布者对象
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);
// 订阅者对象
ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);

        该函数创建一个名为 发布者对象,用于向 ROS 网络中的 chatter 话题(Topic) 发送 std_msgs::String 类型 的消息。

  • ros::Publisher
    ROS 中的一个类,代表 “发布者”—— 用于向 ROS 话题发送消息的对象。

  • pub
    自定义的变量名,代表这个发布者对象(可以任意命名,比如 chatter_pub)。

  • nh
    一个 ros::NodeHandle 类型的对象(通常称为 “节点句柄”),是 ROS 节点与系统交互的接口,用于创建发布者、订阅者等。

  • nh.advertise(...)
    调用节点句柄的 advertise 方法,用于 “宣告”(注册)一个发布者。其作用是告诉 ROS 系统:“我要向某个个话题题发送某种类型的消息”。

  • std_msgs::String
    指定发布的消息类型 —— 这里是 ROS 标准消息息库中的 String 类型(字符串消息)。ROS 中有很多预定义的消息类型(如整数、点坐标等),也可以自定义。

  • "chatter"
    指定要发布的 话题名称(Topic Name)。ROS 中,消息通过 “话题” 进行传递,订阅者(Subscriber)需要订阅相同的话题才能收到消息(类似广播的频道名)。

  • 10
    消息队列长度(Queue Size)。当发布消息的速度超过接收速度时,ROS 会缓存最多 10 条消息,超出的会被丢弃,用于防止内存溢出。

 创建要发送的消息变量

std_msgs::String msg;
msg.data = "Hello!!!";

        话题中要用到的消息类型是std_msgs::String,故定义该类型的变量,并填充内容。如果要发送的消息内容过于复杂,也可以封装成msg文件,定义要发送的消息中的变量类型和变量名称:

string name
uint16 age
float64 height

发布方发布消息API

pub.publish(msg);

二、服务通信

1、概念及应用场景

        服务通信属于异步通信,是基于请求-响应模式的一种应答机制。一个节点A向另外一个节点B发送请求,B接收处理请求并产生响应结果返回给A。实际应用中,比如机器人前进过程中,控制节点向激光雷达节点请求告知距离,激光雷达节点收到并处理请求,将距离信息返回控制节点。服务通信更适用于对实时性有要求、具有一定逻辑处理的应用场景。

2、整体架构

        服务通信主要涉及到三个角色:管理者、服务端、客户端。管理者负责保管服务端和客户端注册的信息,并匹配话题相同的服务端与客户端,帮助服务端与客户端建立联系。客户端发送请求信号,服务端返回相应信息。

3、主要API(C++)

srv文件

        与话题通信中的msg文件类似,在使用服务通信的时候需要定义一个srv文件,来装载客户端发送请求的数据类型和变量名+服务端响应时发送的数据类型和变量名,请求和响应两部分之间一定要用‘---’隔开

# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum

  创建服务端对象

//创建服务端对象
ros::ServiceServer server = nh.advertiseService("AddInts",doReq);
//创建客户端对象
ros::ServiceClient client = nh.serviceClient<包名::srv文件名>("AddInts");

        创建一个服务服务器,用于响应 ROS 网络中名为 AddInts 的服务请求,并指定由 doReq 函数处理具体的请求逻辑。 

  • ros::ServiceServer
    ROS 中的类,代表 “服务服务器” 对象,用于接收客户端的服务请求并返回响应。

  • server
    自定义的变量名,指代这个服务服务器对象(可任意命名,如 add_server)。

  • nh
    ros::NodeHandle 类型的节点句柄(同之前的发布者逻辑),是与 ROS 系统交互的接口。

  • nh.advertiseService(...)
    节点句柄的方法,用于 “宣告” 一个服务服务器,告诉 ROS 系统:“我提供名为 AddInts 的服务,由 doReq 函数处理请求”。

  • "AddInts"
    服务的名称(类似话题话题名),客户端需要通过这个名称找到对应的服务服务器。

  • doReq
    回调函数(处理函数)的名称,当有客户端发送服务请求时,ROS 会自动调用这个函数来处理请求并生成响应。

回调函数

        回调函数就是在服务端收到请求时会自动处理的函数,形参就是在当前工具包->刚才定义的srv文件名->req/resp,分别对应刚才srv文件中的请求和响应两部分。

// bool 返回值由于标志是否处理成功
bool doReq(包名::srv文件名::Request& req,
          包名::srv文件名::Response& resp){
    int num1 = req.num1;
    int num2 = req.num2;

    ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2);

    //逻辑处理
    if (num1 < 0 || num2 < 0)
    {
        ROS_ERROR("提交的数据异常:数据不可以为负数");
        return false;
    }

    //如果没有异常,那么相加并将结果赋值给 resp
    resp.sum = num1 + num2;
    return true;
}

三、参数服务器

1、概念及应用场景

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

2、整体架构

        参数服务器主要由三个部分组成:管理者、参数设置者、参数调用者。管理者作为一个公共容器保存参数,参数设置者可以向容器中设置参数,参数调用者可以获取参数。

3、主要API(C++)

通过节点修改参数

ros::NodeHandle nh;
nh.setParam("nh_int",10); //整型
nh.setParam("nh_double",3.14); //浮点型
nh.setParam("nh_bool",true); //bool
nh.setParam("nh_string","hello NodeHandle"); //字符串
nh.setParam("nh_vector",stus); // vector
nh.setParam("nh_map",friends); // map

//修改演示(相同的键,不同的值)
nh.setParam("nh_int",10000);

全局参数

ros::param::set("param_int",20);
ros::param::set("param_double",3.14);
ros::param::set("param_string","Hello Param");
ros::param::set("param_bool",false);
ros::param::set("param_vector",stus);
ros::param::set("param_map",friends);

//修改演示(相同的键,不同的值)
ros::param::set("param_int",20000);

四.CMakeList.txt中配置函数

1.add_executable编译源代码为可执行文件

add_executable(要生成的可执行文件的名称   src/源代码文件.cpp)

2.add_dependencie指定编译依赖

add_dependencies(必须和add_executable中定义的可执行文件名称完全一致  ${PROJECT_NAME}_gencpp),第二个参数的作用是告诉CMake,编译可执行文件之前,必须先完成消息生成的任务。

3.target_link_libraries链接ROS库

可执行文件名称也还是必须和add_executable中定义的可执行文件名称完全一

//编译源代码为可执行文件
add_executable(AddInts_Server src/AddInts_Server.cpp)
add_executable(AddInts_Client src/AddInts_Client.cpp)

//指定编译依赖
add_dependencies(AddInts_Server ${PROJECT_NAME}_gencpp)
add_dependencies(AddInts_Client ${PROJECT_NAME}_gencpp)

//链接ROS库
target_link_libraries(AddInts_Server
  ${catkin_LIBRARIES}
)
target_link_libraries(AddInts_Client
  ${catkin_LIBRARIES}
)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值