文章结构
- 任务要求
- 话题模型
- 实现步骤
- 自定义srv
- 定义srv文件
- 编辑配置文件
- 编译
- 自定义srv调用
- vscode配置
- 编写服务端实现
- 编写客户端实现
- 执行
- 启动roscore
- 编译
- 启动客户端和服务端
- 编译
- 启动roscore
- 启动节点
任务要求
编写代码实现 ROS 中的服务请求与答复:
- 创建服务端,注册 Service
- 当服务端收到客户端 Service 请求(携带整型参数 a,b)后服务端返回 a,b 的和给客户端
- 客户端输出结果。
话题模型
ROS Master 负责保管 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client ,帮助 Server 与 Client 建立连接,连接建立后,Client 发送请求信息,Server 返回响应信息。
实现步骤
自定义srv
在服务通信中,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。因此,需要创建服务器与客户端通信的数据载体。
-
按照固定格式创建srv文件
-
编辑配置文件
-
编译生成中间文件
定义srv文件
进入工作空间,在src文件夹下创建新的功能包:
在新的功能包中创建名为srv的文件夹,并在该文件夹中创建srv文件,这里将其命名为addInts.srv:
服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用—分割,具体实现如下:
# 客户端请求时发送的两个数字
int32 a
int32 b
---
# 服务器响应发送的数据
int32 sum
注意,行末不要打分号
编辑配置文件
package.xml中添加编译依赖与执行依赖
<build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend>
CMakeLists.txt编辑 srv 相关配置:
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)
# 需要加入 message_generation,必须有 std_msgs
add_service_files(FILESaddInt.srv
)
由于在srv文件内使用的数据类型都是基于标准消息的,即依赖于standard_messages,故有了下面的修改:
generate_messages(DEPENDENCIESstd_msgs
)
前面的find_package是指创建的功能包所依赖的包,而catkin_package是指find_package所依赖的包:
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES service_demoCATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
编译
命令行中:
$ catkin_make
或者在vscode中:ctrl+shift+B:
自定义srv调用
编写服务通信,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。
vscode配置
找到devel/include的路径
在.vscode中的c_cpp_properties.json中修改
{"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
}
编写服务端实现
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器。服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,客户端再解析。
//1.头文件
#include "ros/ros.h"
#include "service_demo/addInt.h" //该头文件名字和srv文件一致//5.处理请求并产生响应
bool doNums(service_demo::addInt::Request &request,service_demo::addInt::Response &response) //请求对象和响应对象以参数的方式传入
{//1.处理请求int a = request.a;int b = request.b;ROS_INFO("收到的请求数据:a = %d, b = %d",a,b);//2.组织响应int sum = a+b;response.sum = sum;ROS_INFO("求和结果: sum = %d",sum);return true;
}
int main(int argc, char *argv[])
{setlocale(LC_ALL,"");//2.初始化ROS节点ros::init(argc,argv,"addInt_Server"); //节点名称需要保持唯一//3.创建ROS句柄ros::NodeHandle n;//4.创建服务对象//参数1为话题名称/主题名称,用于将服务端和客户端关联在一起//参数2是一个回调函数,用于处理请求,返回值为布尔类型ros::ServiceServer server = n.advertiseService("addInt",doNums); //6.多请求,需要调用ros::spin()ros::spin();return 0;
}
随后在CMakeLists进行编译设置修改:
编写客户端实现
实现参数的动态提交:
- 格式:rosrun xxxx xxxx 12 34
- 节点执行时,需要获取命令中的参数,并组织进request
// 1.包含头文件
// 1.包含头文件
#include "ros/ros.h"
#include "service_demo/addInt.h"int main(int argc, char *argv[])
{//如果输入的参数个数不对,会进行提示。argc为传入参数的个数。argv[]为参数的数组if(argc != 3){ROS_INFO("提交的参数个数不对");return 1;}setlocale(LC_ALL,"");// 2.初始化 ROS 节点ros::init(argc,argv,"addInt_Client");// 3.创建 ROS 句柄ros::NodeHandle n;// 4.创建 客户端 对象ros::ServiceClient client = n.serviceClient<service_demo::addInt>("addInt");// 5.组织请求数据service_demo::addInt ai;//5-1.组织请求//从argv数组中提取参数,将字符串类型转为整形ai.request.a = atoi(argv[1]);ai.request.b = atoi(argv[2]);//5-2.处理响应//调用判断服务器状态的函数,用于等待服务//函数1client.waitForExistence();//函数2// ros::service::waitForService("addInt");//客户端请求服务器bool flag = client.call(ai);if (flag){ROS_INFO("响应成功");//获取结果ROS_INFO("响应结果 = %d",ai.response.sum);}else{ROS_ERROR("请求处理失败....");return 1;}return 0;
}
随后在CMakeLists文件中修改配置:
执行
启动roscore
$ roscore
编译
命令行中:
$ catkin_make
或者在vscode中:ctrl+shift+B:
启动客户端和服务端
编译
$ catkin_make
启动roscore
$ roscore
启动节点
$ source ./devel/setup.bash
$ service_demo service_demo_server_c
再开一个终端
$ source ./devel/setup.bash
$ rosrun rosrun service_demo service_demo_client_c 10 10
效果如下: