ROS服务通信机制

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

srv文件

服务通信需要用到srv文件,在srv文件中定义了请求数据和响应数据的类型

1. 定义srv文件

具体实现:在功能包下新建srv目录,添加.srv文件。

.srv文件内容如下:

# 客户端请求时发送的数据
int8 num
---
# 服务端响应时发送的数据
int32 result

2.编辑配置文件

package.xml中添加编译依赖与执行依赖

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  <!-- 
  exce_depend 以前对应的是 run_depend 现在非法
  -->

CMakeLists.txt编辑 srv 相关配置

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs
add_service_files(
  FILES
  AddInts.srv
)
generate_messages(
  DEPENDENCIES
  std_msgs
)

3. 编译

编译后的中间文件查看:

C++ 需要调用的中间文件(…/工作空间/devel/include/包名/xxx.h)

4. vscode配置

配置c_cpp_properies.json文件,将中间文件的路径复制到"incluedePath"下

{
    
    
    "configurations": [
        {
    
    
            "browse": {
    
    
                "databaseFilename": "",
                "limitSymbolsToIncludedHeaders": true
            },
            "includePath": [
                "/opt/ros/noetic/include/**",
                "/usr/include/**",
                "/xxx/yyy工作空间/devel/include/**" //配置 head 文件的路径 
            ],
            "name": "ROS",
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

一、ROS基本数据类型的服务通信实现

1. ROS整形数据的服务通信实现

案例:客户端发送一个整形数据,服务端实现整形数据的2次方运算

服务端实现

#include "ros/ros.h"
#include "std_msgs/Int8.h"
#include "int_server_client/int_server_client.h"

bool doReq(int_server_client::int_server_client::Request& req,
            int_server_client::int_server_client::Response& res)
{
    res.result = pow(req.num,2);
    // ROS_INFO("接受到的数据为:%d",req.num);
    // ROS_INFO("处理后的数据为:%d",res.result);
    return true;
} 


int main(int argc, char *argv[])
{
    /* code */
    //解决乱码问题
    setlocale(LC_ALL,"");
    //初始化ros节点
    ros::init(argc,argv,"server");
    //句柄
    ros::NodeHandle nh;
    //创建server对象
    ros::ServiceServer server;
    server = nh.advertiseService("intServer",doReq);
    ros::spin();


    return 0;
}

客户端实现

#include "ros/ros.h"
#include "std_msgs/Int8.h"
#include "int_server_client/int_server_client.h"

int main(int argc, char *argv[])
{
    /* code */
    //解决乱码问题
    setlocale(LC_ALL,"");
    //初始化ros节点
    ros::init(argc,argv,"server");
    //句柄
    ros::NodeHandle nh;
    //创建client对象
    ros::ServiceClient client;
    client = nh.serviceClient<int_server_client::int_server_client>("intServer");
    //等待服务端启动
    client.waitForExistence();
    //组织数据
    int_server_client::int_server_client ai;
    ai.request.num = 10;

    //发送请求
    bool flag = client.call(ai);
    if (flag == 1)
    {
        ROS_INFO("处理后的数据为%d",ai.response.result);
    }
    else
    {
        ROS_INFO("请求失败!");
    }


    ros::spin();


    return 0;
}

launch文件

<launch>
    <node pkg="int_server_client" type="int_server" name="intServer" output="screen"/>
    <node pkg="int_server_client" type="int_client" name="intClint" output="screen"/>
</launch>

2.ROS整形数据的服务通信实现之采用动态传参的方式进行参数传输

服务端实现与上一节相同,不同的是客户端实现,客户端实现需要获取终端输入的参数,然后传递给服务端进行运算并响应。

客户端实现

#include "ros/ros.h"
#include "std_msgs/Int8.h"
#include "int_server_client/int_server_client.h"

int main(int argc, char *argv[])
{
    /* code */
    //  argc 是指命令行输入参数的个数(以空白符分隔),默认包含一个程序名
    if (argc != 2)
    {
        ROS_ERROR("请提交一个整数");
        return 1;
    }
    //ROS_INFO("%s",argv[1]);

    //解决乱码问题
    setlocale(LC_ALL,"");
    //初始化ros节点
    ros::init(argc,argv,"server");
    //句柄
    ros::NodeHandle nh;
    //创建client对象
    ros::ServiceClient client;
    client = nh.serviceClient<int_server_client::int_server_client>("intServer");
    //等待服务端启动
    client.waitForExistence();
    //组织数据
    int_server_client::int_server_client ai;
    
    //atoi()将字符串转换为整形
    ai.request.num = atoi(argv[1]);

    //发送请求
    bool flag = client.call(ai);
    if (flag == 1)
    {
        ROS_INFO("处理后的数据为%d",ai.response.result);
    }
    else
    {
        ROS_INFO("请求失败!");
    }


    ros::spin();


    return 0;
}

运行

与上一节不同,本节需要在终端中分别运行服务端和客户端,因为客户端需要获取终端中传入的参数

命令如下:

rosrun 功能包名 节点名 参数

注意:需要先启动roscore命令

3. ros字符串类型数据的服务通信实现

服务端实现

#include "ros/ros.h"
#include "string_server_client/string_srv.h"


bool doReq(string_server_client::string_srvRequest& req,
            string_server_client::string_srvResponse& res)
{
    ROS_INFO("名字:%s",req.name.c_str());
    ROS_INFO("年龄:%s",req.age.c_str());
    ROS_INFO("地址:%s",req.address.c_str());
    std::stringstream ss;
    ss<< req.name.c_str()<<"来自"<<req.address.c_str()<<"今年"<<req.age.c_str()<<"岁";    
    res.msg = ss.str();
}

int main(int argc, char *argv[])
{
    /* code */
    //乱码问题
    setlocale(LC_ALL,"");
    //初始化ros节点
    ros::init(argc,argv,"server");
    //句柄
    ros::NodeHandle nh;
    //创建服务端对象
    ros::ServiceServer server;
    server = nh.advertiseService("stringServerClient",doReq);
    ros::spin();

    return 0;
}

客户端实现

#include "ros/ros.h"
#include "string_server_client/string_srv.h"


int main(int argc, char *argv[])
{
    /* code */
    //解决乱码
    setlocale(LC_ALL,"");
    //判断参数数量是否正确
    if (argc!=4)
    {
        ROS_INFO("参数数量不正确,请重新输入");
        return 1;
    }
 
    //初始化ros节点
    ros::init(argc,argv,"server");
    //句柄
    ros::NodeHandle nh;
    //客户端对象
    ros::ServiceClient client;
    client = nh.serviceClient<string_server_client::string_srv>("stringServerClient");
    string_server_client::string_srv clientData;
    clientData.request.name = argv[1];
    clientData.request.age = argv[2];
    clientData.request.address = argv[3];
    //等待服务启动
    client.waitForExistence();
    bool flag = client.call(clientData);
    if (flag==1)
    {
        ROS_INFO("响应的数据为:");
        ROS_INFO("%s",clientData.response.msg.c_str());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_49216787/article/details/132678297