ROS通信机制
编译平台
本项目依赖于ubuntu18.04,ROS版本为ROS melodic ,运行于windows10上面的虚拟机
ros service
ros service是节点之间通讯的另一种方式。服务允许节点发送一个请求(request)并获得一个响应(response),本文使用c++完成了一个基础了ROS service项目的编写。
rosservice于前面文章中提到的ros topic一个显著的区别是,service是双向有应答机制的通讯方式,而topic只是完成了简单的接受消息和处理消息的任务。
创建工作空间
mkdir -p catkin_test/src
使用mkdir命令创建一个工作空间
然后我们cd进入catkin_test,使用catkin_make对工作空间进行编译,完成工作空间的注册和初始化
编译完成后会出现build和devel两个文件夹,当这两个文件夹产生,就代表这个工作空间创建成功。
创建一个名为test的功能包
cd src
catkin_create_pkg test roscpp rospy std_msgs
在正式的cpp文件编写之前,我们需要做一点准备。创建在两个节点之间沟通的“消息”。
使用ROS完成的这个功能,我们需要有两个节点,这俩个节点为别作为client和service,即一个作为客户端一个作为服务端。我们在srvz中规定了他们之间是如何通信的,而msg规定了srv中传递的消息是什么类型的。
msg和srv
首先我们需要明确一下msg和srv两个概念
msg:msg 文件是描述 ROS 消息字段的简单文本文件。它们用于生成不同语言消息的源代码。
SRV:SRV 文件描述服务。它由两部分组成:请求和响应。
这是ROS官网上的解释。
我们将msg文件放在msg文件夹下,将srv文件放在srv文件夹下。
在功能包下建立两个新建文件夹,msg与srv:
接下来分别在msg和srv下创建msg文件和srv文件
在test_1.msg中填入下列内容
int64 a
int64 b
int64 sum
在test_2.srv填入一下内容
int64 a
int64 b
---
int64 sum
在msg中规定了通信中主要涉及有三个数据,数据类型均为int64
在srvz中我们规定了int64 a与int64 b为请求数据,int6 sum为响应数据
client
在src下完成client.cpp和listner,cpp的创建
client.cpp
#include<ros/ros.h>
#include<std_msgs/String.h>
#include"test1/test_2.h"
int main(int argc,char **argv)
{
ros::init(argc,argv,"add_two_ints_client");
if(argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<test1::test_2>("add_two_ints");
test1::test_2 srv;
srv.request.a = atoll(argv[1]);
srv.request.b=atoll(argv[2]);
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}
return 0;
}
server,cpp
#include<ros/ros.h>
#include"std_msgs/String.h"
#include"test/test.h"
bool add(test1::test_2::Request &req,
test1::test_2::Response &res)
{
res.sum=req.a+req.b;
ROS_INFO("request: x=%ld,y=%ld",(long int)req.a,(long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}
int main(int argc,char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
cmakelist.txt修改
接下来最重要的就是cmakelist的修改
修改find_package添加
message_generation
添加msg和srv
添加msg和srv的依赖项
功能包依赖项
添加可执行文件
修改package.xml
添加下列内容
然后我们返回工作空间下编译
catkin_make
在编译的过程中可以看到生成了一个massage和一个服务
这个工程就算是构建成功了,接下来运行一下
在运行之前需要先启动roscore
打开一个新的终端
roscore
然后需要source一下
source ./devel/setup.bash
启动service
rosrun test1 service
此时会输出一个日志
[ INFO] [1705819135.358506874]: Ready to add two ints.
就是等待两个int类型的数字输入
接下来打开一个新的终端打开客户端输入这两个数字
~/catkin_test$ source ./devel/setup.bash
~/catkin_test$ rosrun test1 client 2 4
返回这样的输出就说明服务端接收到我们输入的数据并完成了处理(相加)并将结果返回回来
接下来详细梳理一下代码
server.cpp
#include<ros/ros.h>
#include"std_msgs/String.h"
#include"test/test.h"
bool add(test1::test_2::Request &req,
test1::test_2::Response &res)
{
res.sum=req.a+req.b;//求和
ROS_INFO("request: x=%ld,y=%ld",(long int)req.a,(long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);//输出日志
return true;
}
//bool类型函数,将srv里面的请求和应答读入节点,test1为功能包的名字,test_2为我们刚才创建好的srv文件
int main(int argc,char **argv)
{
ros::init(argc, argv, "add_two_ints_server"); //初始化节点,节点名add_two_ints_server
ros::NodeHandle n;//创建句柄
ros::ServiceServer service = n.advertiseService("add_two_ints", add); //创建一个服务
ROS_INFO("Ready to add two ints.");
ros::spin();//ROS消息回调处理函数
return 0;
}
client.cpp
#include<ros/ros.h>
#include<std_msgs/String.h>
#include"test1/test_2.h"
int main(int argc,char **argv)
{
ros::init(argc,argv,"add_two_ints_client"); //初始化节点
if(argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
} //错误处理,如果输入参数不对则报错
ros::NodeHandle n; //创建句柄
ros::ServiceClient client = n.serviceClient<test1::test_2>("add_two_ints"); //创建客户端
test1::test_2 srv;
srv.request.a = atoll(argv[1]);
srv.request.b=atoll(argv[2]); //将输入的参数读到srv里面
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}//如果服务端做出相应则打印相应结果
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}//else则报错
return 0;
}