我的ROS学习之路——服务通信

本知识点将用一个实例(服务端处理客户端提交的两个数字求和,然后返回处理结果)来理解 服务通信的基本使用,大致分为三块:

1.自定义消息*.srv的编写()

2.服务端编写

3.客户端编写

一.首先创建一个工作空间和软件包

创建工作空间


mkdir -p ~/catkin_ws_srv_self/src
 
cd catkin_ws_srv_self
 
catkin_make

创建软件包

cd src
 
catkin_create_pkg server_client std_msg rospy roscpp

二.自定义消息*.srv的编写——srv编写其实与前面说的话题通信步骤一样,只不过改成了srv自己的东西。

(PS:这里可以完全参考ros wki上面的教程,消息创建部分,照搬就可以

2.1编写srv文件

方式一:在终端中创建msg

cd ~/catkin_ws_srv_self/src/server_client
 
mkdir msg
 
gedit sum_ints.srv


在打开的文本,然后输入如下自己定义的信息(像结构体一样编写就行):

int32 num1
int32 num2
---
int32 sum

方式二:在vscode中创建srv

在终端中输入:

cd ~/catkin_ws_srv_self

code .

这样就打开了vscode编辑器,在~/catkin_ws_srv_self/src/plumbing_server_client/目录下,创建一个srv文件夹,在srv文件下,创建一个sum_ints.srv文件,在输入自己定义的信息:

int32 num1
int32 num2
---
int32 sum

最终效果如下:

以上两种方式实现效果一样,但是显然vscode的交互界面舒服。

2.2编辑配置文件(简记:231——package.xml改动两项,CMakeLists.txt改动三项,放开注释一项)

2.2.1首先在package.xml中添加编译依赖与执行依赖

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


2.2.2然后在CMakeLists.txt编辑srv相关配置

# 不要直接复制这一大段,只需将message_generation加在括号闭合前即可
find_package(catkin REQUIRED COMPONENTS
   roscpp
   rospy
   std_msgs
   message_generation
)
#执行时依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)
#配置srv的源文件
add_service_files(
  FILES
  num_sum.srv//这里放你自己定义的消息文件名
)
# 生成消息时依赖于 std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)

好了,至此已经自定义消息配置完了。但是还没完,编译完了之后,还需要继续配置环境。

2.3编译

编译之后,会产生一些中间文件(后续调用相关 msg 时,是从这些中间文件调用的),但我只关注头文件在创建的工作空间的开发空间下 include的文件夹里 即(~/catkin_ws_example/devel/inlcude下)。

因为这是自己定义的消息,所以现在需要做的是,把这个头文件包含进本工作空间的路径里面(在c_cpp_properties.json 里的 includepath属性引入,这样可以避免的是 在用vscode开发时,引入 自定义消息头文件,不会出现误报错误和代码无法补齐的情况)

PS:如何快速的得到这个头文件的路径呢?——这个路径可以鼠标放在include上,然后右击鼠标打开终端,输入pwd可得

引入的框架大致如下:

{
    "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
}

在本例中,引入图为:

最后面 ~/include/**的意思是,引入inlcude文件夹下的所有头文件。

至此,自定义消息部分,已经配置完成。

总结一下,做了哪些事?编写:

自定义消息

然后配置package.xml,CMakeLists.txt

引入头文件

三.服务端编写

3.1在~/catkin_ws_srv_self/src/server_client/src文件夹下创建一个*.cpp文件(本例为 server.cpp),然后放如下代码:

#include<ros/ros.h>
#include "plumbing_server_client/sum_ints.h"

/*
服务端实现:解析客户端提交的数据,并运算在产生结果
    1.包含头文件
   2. 初始化ros节点
    3.创建句柄
    4.创建句柄对象(服务对象)
    5.处理请求并产生响应
    spin()
*/

//5.处理请求并产生响应
bool add(plumbing_server_client::sum_ints::Request &request,plumbing_server_client::sum_ints::Response &response)
{
    /*操作主要有两步*/

    //1.处理请求
    
    int num1 = request.num1;/*问题:这里为什么用的是.   而不是->呢? ——因为函数形参列表不是以指针方式引用的,拿到的是引用,而不是指针,进一步的描述,这里的request和response不是指针,没有用ConstPtr来接收,而是对象。*/
    int num2 = request.num2;
    ROS_INFO("收到的请求数据:num2 = %d,num2 = %d",num1,num2);
                                                    //ROS_INFO(" 收到的数据: num1= %d,num2=%d",request.num1,request.num2);//以上三条消息,可以融合成一条打印,只是多了中间变量接收一下而已

    

    //2.处理响应
    int  sum = num1+num2;
    response.sum = sum;
                                                       //response.sum = request.num1+request.num2;//以上两条信息,也可以融合成一条打印,只是多了一个中间变量接收而已
    ROS_INFO("求和结果:%d",sum);
                                                      // ROS_INFO("求和结果:%d",response.sum);//说明返回response.sum和sum中间变量都可以
    return true;
}

int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");//日志输出,如果用中文的话,不要忘了这一句。

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

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

    //4.创建句柄对象(服务对象)
    ros::ServiceServer server = nh.advertiseService("add_two_ints",add) ;/*第二个参数就是回调函数,处理请求的,只要是关于有回调函数的就不用写模板函数,他会自己推导然后填写的
回调函数返回的是 布尔类型的,因为这个处理结果请求,可能性有两个,成功或失败*/

    ROS_INFO("服务器端启动了!");
    ros::spin();
     return 0;
}

3.2配置CMakeLists.txt(需要修改三个地方,都是找到对应的地方,然后复制注释下来,修改需要改的部分即可) 

 第二句放的位置 有坑的说明,已经在发布的 话题通信之自定义消息 里已经说明了。 

OK~发布方已经编写好了,接下来就可以编译测试了(这个模块写好了,先测试没有问题在写其他模块)。

3.3编译测试

3.3.1

方式一(终端编译方式):

cd ~/catkin_ws_srv_self
 
catkin_make

方式二(vscode的快捷键编译方式):

在vscode中,shift+ctrl+B

3.3.2运行发布者节点(前提是已经运行了 roscore)

打开一个新的终端:

roscore

另开一个终端:

cd ~/catkin_ws_srv_self
 
soure devel/setup.bash

rosrun server_client server

​

到这一步之后,就运行成功了,服务端已经挂起了,但是在终端看不到什么反应,但是我们可以用内置指令,在终端上提交数字让服务端计算并把结果打印出来到终端来看结果,如:

另开一个终端:输入rosservice list 可以查看当前挂起的服务(发现/two_sum为我们所需的服务),然后输入rosservice  -h可以查看rosservice有什么功能(指令忘记了,可以走这一步),然后输入rosservice call /two_sum "num1:[自己随便输入数字,如1] num2:[自己随便输入数字,如2]"

(这一串指令这么长,怎么记得住?——小技巧:可以输入到rosservice call /two_sum 然后多按几次tab键盘,就能自动补齐后面的东西,PS:后面可以自己输入的数字,其实就是自己写的服务端所做的事情。)

结果如图:

验证成功~

总结一下干了哪些事?

编写*.cpp文件

配置CMakeLists.txt

 

四.客户端编写

以固定方式(即程序运行前,数字已经固定死)提交:

4.1在~/catkin_ws_srv_self/src/server_client/src文件夹下创建一个*.cpp文件(本例为 client.cpp),然后放如下代码:

#include<ros/ros.h>
#include "server_client/num_sum.h"

/*
客户端:提交两个整数,并处理响应的结果。
                1.包含头文件;
                2.初始化ros节点
                3.创建节点句柄
                4.创建一个客户端对象
                5.提交请求并处理响应
*/


int main(int argc, char  *argv[])
{

    setlocale(LC_ALL,"");
    //2.初始化ros节点
    ros::init(argc,argv,"client");

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

    //4.创建一个客户端对象
    ros::ServiceClient client = nh.serviceClient<server_client::num_sum>("two_sum");//serviceClient有三个重载,意思是可以 形参输入有三种样式,这里使用第二个重载,有范型的那一个,范型可以就是自己创建的那个服务消息srv(具体解释可看视频第67个4min10s)
   
   //5.提交请求并处理响应(主逻辑实现)
    server_client::num_sum srv;

   //5.1 组织请求
        srv.request.num1 = 1;
        srv.request.num2 = 2;
   //5.2 处理响应
   bool flag = client.call(srv); //返回值是一个布尔类型的,提交完之后,是有返回结果的,所以可以用一个bool值接收一下。如果是true就正常处理,false就是处理失败了。并且响应的结果(比如这里的sum),也封装进了这个srv对象(在这个srv对象中,有request还有response这两个属性)。
    if(flag)
    {
        ROS_INFO("响应成功!");
        //获取结果
        ROS_INFO("处理结果 = %d" ,srv.response);
    }
   else
   {
       ROS_INFO("响应失败...");
   }
   
    ros::spin();
    return 0;
}

4. 2配置CMakeLists.txt(需要修改三个地方,都是找到对应的地方,然后复制注释下来,修改需要改的部分即可,改的方法与发布方一样,因此这里只放了最终效果图即可,用红笔标出改了哪些部分)

 shift+ctrl+B编译(或者用终端方式的编译,上面3.3.1写过了)通过,好了。可以联合调试了。

总结一下干了哪些事?

编写*.cpp文件

配置CMakeLists.xtxt

五.联合调试

5.1 编译(不会编译的见3.1)

5.2运行roscore

5.3运行服务端节点

新开一个终端:

cd ~/catkin_ws_srv_self
 
soure devel/setup.bash
 
rosrun server_client server

5.4运行客户端节点

新开一个终端:

cd ~/catkin_ws_srv_self
 
soure devel/setup.bash
 
rosrun server_client client

最后效果如图所示:

 OK~完成,结束~

 

--------------------------------------------------------分割线--------------------------------------------------------

这里值得一提的是(主要体会思想动态提交的思想,可以从终端输入数据,然后从mian函数入口进到程序里面):上面第四部分,写了一个以固定方式(即程序运行前,数字已经固定死)提交,这里优化客户端的编写,以动态的方式提交(就是灵活的填写两个数字,都可以实现求解)

可以在client.cpp文件中,放如下代码:

//优化客户端,实现参数的动态提交
//1.终端输入格式:rosrun xxxx(包名) xxxx(节点名)  1 2
//2.节点执行时,需要获取命令中的参数,然后数据放入requerst中
#include<ros/ros.h>
#include "server_client/num_sum.h"

int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");
    //优化实现,获取命令中的参数
    /*此时argc 应该等于3(节点文件名,两个参数,所以是三个),*argv指针数组应该也是3,并且argv[0]为客户端文件名,argv[1]为终端传进来的第一个数,如1,,argv[2]为传进来的第二个数,如2*/
    if(argc != 3)
    {
        ROS_INFO("输入个数不对...");
        return 1;
    }
    
    ros::init(argc,argv,"client");
    ros::NodeHandle nh;
    ros::ServiceClient client = nh.serviceClient<server_client::num_sum>("two_sum");

    server_client::num_sum srv;
    srv.request.num1 = atoi(argv[1]);
    srv.request.num2 = atoi(argv[2]);

    bool flag = client.call(srv);
    if(flag)
    {
        ROS_INFO("求和结果为:%d",srv.response.sum);
    }
    else
    {
        ROS_INFO("响应失败...");
        return 1;
    }
    //return 0;

    ros::spin();//经过测试,程序运行到这里,会回调(因为终端就停在那了,并没有结束,继续给输入命令,需要按ctrl+c才能结束)。然后就不经过 return 0了。  ( return 0为正常停止,return 1为非正常停止的意思。)
    return 0;
}

然后就可以开始调试了。

1. 编译(不会编译的见3.1)

2。运行roscore

3运行服务端节点

新开一个终端:

cd ~/catkin_ws_srv_self
 
soure devel/setup.bash
 
rosrun server_client server

4。运行客户端节点

新开一个终端:

cd ~/catkin_ws_srv_self
 
soure devel/setup.bash
 
rosrun server_client client

最后效果如图所示(如图演示了 两组动态提交数据,分别是 1和2 ,5和10):

OK~完成,结束~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值