7.ROS编程学习:自定义服务数据c++调用

目录

一、准备工作——配置vscode

二、服务端创建

1.创建文件demo01_server.cpp

2.服务端的CMakeList.txt配置

3.测试服务端

三、客户端实现

1.创建domo01_client.cpp

2.客户端的CMakeList.txt配置

3.测试客户端

4.优化客户端


学习参考:赵虚左的ROS课程与古月的ROS机器人开发实践书籍。

一、准备工作——配置vscode

为了vscode在调用自定义服务数据时不报假错同时能够补全代码,需要对vscode进行配置。

c_cpp_properties.json(注意逗号)

            "includePath": [
                --snip--
                "/home/rosmelodic/catkin_ws/devel/include/**"
            ],

也就是刚刚创建自定义服务数据,catkin_make后中间文件的目录。

7.ROS编程学习:自定义服务数据c++调用_第1张图片

二、服务端创建

1.创建文件demo01_server.cpp

在src中创建demo01_server.cpp文件。

7.ROS编程学习:自定义服务数据c++调用_第2张图片

#include "ros/ros.h"
#include "server_client/AddInts.h"

bool huidiao(server_client::AddInts::Request &request,
                            server_client::AddInts::Response &response){

    int num11 = request.num1;
    int num22 = request.num2;
    ROS_INFO("收到的请求数据为:num11 =%d,num22=%d",num11,num22);
    int sum12 = num11 + num22;
    response.sum = sum12;
    ROS_INFO("求和结果:sum = %d",sum12);
    //ros::spin();//这是一次尝试
    return true;
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"jiafa_server");
    ros::NodeHandle n;
    ros::ServiceServer service = n.advertiseService("add_ints",huidiao);
    ROS_INFO("准备好加入两个整数");
    ros::spin();
    return 0;
}
#include "ros/ros.h"
#include "server_client/AddInts.h"

头文件:调用roscpp和刚刚创建的自定义服务数据类型。调用形式为功能包名/通过编译生成的自定义头文件名

bool huidiao(server_client::AddInts::Request &request,
                            server_client::AddInts::Response &response){

    int num11 = request.num1;
    int num22 = request.num2;
    ROS_INFO("收到的请求数据为:num11 =%d,num22=%d",num11,num22);
    int sum12 = num11 + num22;
    response.sum = sum12;
    ROS_INFO("求和结果:sum = %d",sum12);

    ros::spin();
    return true;
}

首先先声明了函数输入参数的数据类型,其中数据类型为刚刚创建的自定义服务数据类型,需要注意的是,自定义服务数据类型创建时通过“---”的符号将上下两部分分为了请求和应答数据类型,如下图。其中调用方法为

server_client::AddInts::Request(Response)——功能包名::创建的自定义数据类型文件名::Request(Response)——请求(应答)

7.ROS编程学习:自定义服务数据c++调用_第3张图片

    int num11 = request.num1;
    int num22 = request.num2;

声明变量同时接收数据。

ROS_INFO("收到的请求数据为:num11 =%d,num22=%d",num11,num22);

在控制台输出字符串,同时注意中文乱码问题,为了解决此问题,主函数中加入:

setlocale(LC_ALL,"");

 计算完,再赋值给response.sum应答。

    int sum12 = num11 + num22;
    response.sum = sum12;

 控制台输出结果

ROS_INFO("求和结果:sum = %d",sum12);

因为服务的回调函数需要返回的时布尔类型,故运行成功应该返回true,不再是之前话题通信中返回的void(空)。这是一次尝试,在回调函数中加入回头函数,发现这是不行的,主函数运行一次,直接跳出去了,不再循环。

//ros::spin();//这是一次尝试    
return true;

进入主函数部分:

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"jiafa_server");
    ros::NodeHandle n;
    ros::ServiceServer service = n.advertiseService("add_ints",huidiao);
    ROS_INFO("准备好加入两个整数");
    ros::spin();
    return 0;
}

 防止在控制台出现中文乱码。

setlocale(LC_ALL,"");

 常规的初始化节点和创建节点句柄

    ros::init(argc,argv,"jiafa_server");
    ros::NodeHandle n;

 服务端创建,其中发现区别:

泛型函数的没声明,不意味着不能声明,不加就跟随着回调函数数据类型了。(函数帮助通过ctrl+shift+空格,调出)

 加上的效果。

ros::ServiceServer service = n.advertiseService("add_ints",huidiao);

没有缓存区了,之前的话题通信都是有缓存区的。

    ros::ServiceServer service = n.advertiseService("add_ints",huidiao);

2.服务端的CMakeList.txt配置

add_executable(demo_server src/demo01_server.cpp)

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(demo_server ${PROJECT_NAME}_gencpp)

target_link_libraries(demo_server
  ${catkin_LIBRARIES}
)

加上注释掉的部分是因为CMakeList.txt中出现了两处 add_dependencies,配置不对会报错:

(2条消息) ROS报错:CMakeList.txt配置错误——add_dependencies位置错误(难发现)_机械专业的计算机小白的博客-CSDN博客https://blog.csdn.net/wzfafabga/article/details/127385406其中这是为了在编译cpp文件之前,先编译自定义服务数据类型,生成中间文件。

add_dependencies(demo_server ${PROJECT_NAME}_gencpp)

3.测试服务端

 启动ROS Master

roscore

 source一下,打开刚刚的cpp文件,其中指向的名字是CMakeList.txt文件中的add_executable设置的。

source ./devel/setup.bash 
rosrun server_client demo_server 

 此时服务端是挂起的。

rosmelodic@rosmelodic-virtual-machine:~/catkin_ws$ source ./devel/setup.bash 
rosmelodic@rosmelodic-virtual-machine:~/catkin_ws$ rosrun server_client demo_server 
[ INFO] [1666080588.394932943]: 准备好加入两个整数

为了查看服务端是否功能正常:

source一下,

source ./devel/setup.bash 
rosservice call add_ints  "num1: 0
num2: 0"

 其中 add_ints是cpp主函数中创建的服务名

ros::ServiceServer service = n.advertiseService("add_ints",huidiao);

后面的都是Tab代码补齐的。

rosmelodic@rosmelodic-virtual-machine:~/catkin_ws$ rosservice call add_ints  "num1: 1
num2: 5" 
sum: 6

 修改并运行,发现应答的sum得到了结果。这些参数都是对应着创建的自定义服务数据类型的srv文件:

int32 num1
int32 num2
---
int32 sum

同时观察启动服务端的控制台:

rosmelodic@rosmelodic-virtual-machine:~/catkin_ws$ rosrun server_client demo_server 
[ INFO] [1666081116.775811954]: 准备好加入两个整数
[ INFO] [1666081146.878643685]: 收到的请求数据为:num11 =1,num22=5
[ INFO] [1666081146.878690322]: 求和结果:sum = 6

发现程序中的ROS_INFO输出的字符串全部输出了出来。到此服务端运行成功。

三、客户端实现

1.创建domo01_client.cpp

 domo01_client.cpp

#include "ros/ros.h"
#include "server_client/AddInts.h"

int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"jiafa_client");
    ros::NodeHandle n;
    ros::ServiceClient client = n.serviceClient("add_ints");
    server_client::AddInts xiaoxi ;
    xiaoxi.request.num1 = 20;
    xiaoxi.request.num2 = 30;
    bool tiaojian = client.call(xiaoxi);
    if(tiaojian)
    {
        ROS_INFO("应答结果接收成功");
        ROS_INFO("结果为:%d",xiaoxi.response.sum);
    }
    else
    {
        ROS_INFO("接收结果失败");
    }
    return 0;
}

只对与服务端不同地方进行讨论:

    ros::ServiceClient client = n.serviceClient("add_ints");

客户端的创建:

话题名必须和服务端话题名一致。

其中回调函数不在有,泛型数据类型为刚刚创建的自定义服务数据类型,注意这是不同于服务端的泛型数据类型的声明,其中服务端的泛型声明是请求和应答通过逗号隔开:

    ros::ServiceServer service = n.advertiseService("add_ints",huidiao);

具体函数提示可以通过ctrl+shift+空格显示。

 

 声明变量并赋值。

    server_client::AddInts xiaoxi ;
    xiaoxi.request.num1 = 20;
    xiaoxi.request.num2 = 30;

 通过client.call函数发布请求,返回一个布尔值来确定应答是否成功。

    bool tiaojian = client.call(xiaoxi);
    if(tiaojian)
    {
        ROS_INFO("应答结果接收成功");
        ROS_INFO("结果为:%d",xiaoxi.response.sum);
    }
    else
    {
        ROS_INFO("接收结果失败");
    }

2.客户端的CMakeList.txt配置

add_executable(demo_client src/demo01_client.cpp)
add_dependencies(demo_client ${PROJECT_NAME}_gencpp)
target_link_libraries(demo_client
  ${catkin_LIBRARIES}
)

再次提醒add_dependencies出现了两次,注意是后面add_executable的add_dependencies!

3.测试客户端

服务通信与话题通信不同。话题通信中的发布者和订阅者可以以不同的先后顺序启动,当然,为了发布者发布的信息订阅者全能接收到,故先运行订阅者。服务通信必须先运行服务端再运行客户端。

启动ROS Master

roscore

启动服务端

source ./devel/setup.bash 
rosrun server_client demo_server

启动客户端

source ./devel/setup.bash 
rosrun server_client demo_client 

运行结果如图

4.优化客户端

 主函数中加入,其中argc对应的是主函数的参数,argc代表着传入参数的个数。

    if (argc != 3)
    // if (argc != 5)//launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径)
    {
        ROS_ERROR("请提交两个整数");
        return 1;
    }

主函数中调整,其中atoi()函数作用是字符串转化为整形,可以按住ctrl点击函数名查看。其中古月的是atoll()是字符串转化为long long int型。其中argv[]对应主函数的参数。argv[]列表存放着传入参数的值,argv[1]和argv[2]是传入的第二个和第三个参数的值,由于传入的是字符串,故需要转化为int类型才能做数值计算。

/* Convert a string to an integer.  */
    // xiaoxi.request.num1 = 20;
    // xiaoxi.request.num2 = 30;
    xiaoxi.request.num1 = atoi(argv[1]);
    xiaoxi.request.num2 = atoi(argv[2]);

再次运行服务端和客户端

rosrun server_client demo_server 
rosrun server_client demo_client 10 20

结果:

你可能感兴趣的:(ROS编程,ros)