- 1、服务通信原理
- 2、服务通信自定义srv
- 3、代码实现
- 1.C++实现
- 2.Python实现
- Server注册
Server启动后,会通过RPC协议在Master中注册自己的地址和提供的服务(话题) - Client注册
Client启动后,会在Master中注册自己所需要的服务(话题) - 匹配
Master根据注册表进行话题匹配,并将Server的TCP地址通过RPC协议发送给Client - Client发送请求
Client收到TCP地址后,通过TCP协议与Server建立连接,发送请求数据 - Server发送相应
Server根据请求,发送结果
注意:
在话题通信中,由于是采用订阅的方式,双方启动前后顺序无要求。但是,在服务通信中,由于采用请求响应的方式,所以要先启动Server,再启动Client
- 在服务通信中,srv文件分两部分:客户端和服务端,而在话题通信中,msg文件只有一部分,在功能包下创建srv目录,在其下面添加xxx.srv文件:
#客户端 int32 num1 int32 num2 --- #服务端 int32 sum
注意: 客户端和服务端之间用—分割 - 编辑配置文件
- package.xml
message_generation message_runtime - CmakeLists.txt
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 )
- 编译
编译后会生成相应的中间件,包括C++中的.h文件和Python中的.py文件
- package.xml
- vscode配置,在C++中,需要配置c_cpp_properies.json文件,主要是"includePath"路径的设置,如果不设置的话,将无法找到生成的.h文件,在敲代码的过程中,也没有相应的提示
{ "configurations": [ { "browse": { "databaseFilename": "", "limitSymbolsToIncludedHeaders": true }, "includePath": [ "/opt/ros/noetic/include //方式1 ros::service::waitForService("add_int"); //方式2 // client.waitForExistence(); //5.组织请求数据 server_client::add ai; ai.request.num1 = atoi(argv[1]); ai.request.num2 = atoi(argv[2]); //6.发送请求,返回bool值,成功为真 bool flag = client.call(ai); if (flag){ ROS_INFO("请求正常,结果为:%d, %s, %s, %s",ai.response.sum,argv[0],argv[1],argv[2]); } else{ ROS_INFO("请求失败!"); return 1; } return 0; } - 配置CMakeLists.txt
add_executable(AddInts_Server src/AddInts_Server.cpp) add_executable(AddInts_Client src/AddInts_Client.cpp) add_dependencies(AddInts_Server ${PROJECT_NAME}_gencpp) add_dependencies(AddInts_Client ${PROJECT_NAME}_gencpp) target_link_libraries(AddInts_Server ${catkin_LIBRARIES} ) target_link_libraries(AddInts_Client ${catkin_LIBRARIES} ) - 执行
- 启动服务
rosrun 包名 服务名
- 启动客户
rosrun 包名 客户名 参数1 参数2
- 启动服务
- vscode配置,在Python中,需要配置setting.json文件,方面参数补齐
{ "python.autoComplete.extraPaths": [ "/opt/ros/noetic/lib/python3/dist-packages", ] } - 服务端
#! /usr/bin/env python #1. 导包 import rospy #从功能包下的srv文件导入相应的数据类型 from server_client.srv import add, addRequest, addResponse #req参数可以随便取名,它是一个类,但是其元素必须为srv中的参数 def doReq(req): #num1 num2为srv中定义的数据类型 sum = req.num1 + req.num2 rospy.loginfo("结果:%d",sum) resp = addResponse(sum) #回调函数返回响应结果 return resp if __name__ == "__main__": #2.初始化ROS节点 注意节点名唯一 rospy.init_node("server_p") #3.创建服务对象 # add_int_p为话题名 add为数据类型 # doReq为回调函数,其数据类型为srv中的数据类型 server = rospy.Service("add_int_p",add,doReq) #4.回调函数处理请求并产生响应 #5.spin函数 rospy.spin() - 客户端
#! /usr/bin/env python #1.导包 import rospy from server_client.srv import * import sys if __name__ == "__main__": if len(sys.argv) != 3: rospy.logerr("请输入正确参数!") sys.exit #2.初始化ROS节点 rospy.init_node("client_p") #3.创建请求对象 # add_int_p为话题,与服务端的一样 # add为数据类型 client = rospy.ServiceProxy("add_int_p",add) rospy.wait_for_service("add_int_p") # client.wait_for_service() #4.发送请求,接受并处理相应 #方式1 #resp = client(3,4) #方式2 #resp = client(addRequest(1,2)) #方式3 req = addRequest() req.num1 = int(sys.argv[1]) req.num2 = int(sys.argv[2]) resp = client.call(req) rospy.loginfo("结果为:%d",resp.sum) - 添加可执行权限
在scripts文件夹下,执行:chmod +x *.py - 配置CMakeLists.txt文件
catkin_install_python(PROGRAMS scripts/AddInts_Server_p.py scripts/AddInts_Client_p.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) - 执行
- 服务端:rosrun 包名 服务
- 客户端:rosrun 包名 客户 参数1 参数2
注意: 服务和客户都是.py文件



