参考内容
Understanding servicesSEO靠我
服务通讯是ROS2的一种基于请求响应式的通讯方式,与之前的话题通讯不同的是服务通讯不是连续的数据流式的通讯,而是需要特定的触发才能收到回复的模式。
服务通讯可以是单个服务端和和单个客户端直接的通讯,一对SEO靠我一的模式
服务通讯也可以是单个服务端和多个客户端通讯,一对多的模式
事实上,服务通讯和自定义的话题通讯的操作过程很类似,即将所需要利用到服务模块利用ament_camkeSEO靠我工具编译成.c和.py的ROS2功能包,然后新建的功能包依赖这个服务功能包完成通讯。
创建自定义消息功能包srv_demo,采用amentSEO靠我_cmake方式:ros2 pkg create srv_demo --build-type ament_cmake
在功能包内创建srv文件夹,其中存放AddInt.srv文件,需要利用到---符号作SEO靠我为分割,文件在---上面的内容作为请求体的参数,在---下面的内容作为响应体的参数,文件内容如下:
int32 num1 int32 num2 --- iSEO靠我nt32 sum 此时的功能包内部的目录结构如下:tree -a pldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/code/srv_SEO靠我demo$ tree -a . ├── CMakeLists.txt ├── include │ └── srv_demo SEO靠我 ├── package.xml ├── src └── srv└── AddInt.srv 配置packages.xml添加依赖项,由于AddInSEO靠我t.srv没有依赖任何的东西因此不需要添加其他的depend: <?xml version="1.0"?> <?xml-model href="http://downlSEO靠我oad.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> <pSEO靠我ackage format="3"><name>msg_demo</name><version>0.0.0</version><description>TODO: Package descriptioSEO靠我n</description><maintainer email="pldz@R7000.com">pldz</maintainer><license>TODO: License declaratioSEO靠我n</license><buildtool_depend>ament_cmake</buildtool_depend><!-- 构建自定义功能包的的必须依赖项 --><buildtool_dependSEO靠我>rosidl_default_generators</buildtool_depend><exec_depend>rosidl_default_runtime</exec_depend><membeSEO靠我r_of_group>rosidl_interface_packages</member_of_group><test_depend>ament_lint_auto</test_depend><tesSEO靠我t_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export> SEO靠我 </package> 配置CMakeLists.txt文件,添加构建自定义AddInt.srv的依赖: cmake_minimum_required(VERSIONSEO靠我 3.8) project(srv_demo)if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")SEO靠我add_compile_options(-Wall -Wextra -Wpedantic) endif()# find dependencies find_packagSEO靠我e(ament_cmake REQUIRED) # uncomment the following section in order to fill in # furtSEO靠我her dependencies manually. # find_package(<dependency> REQUIRED)# 1. 构建自定义功能包必须的依赖 fSEO靠我ind_package(rosidl_default_generators REQUIRED)# 2. 配置自定义的srv的位置 rosidl_generate_interfaces(SEO靠我${PROJECT_NAME}"srv/AddInt.srv" )if(BUILD_TESTING)find_package(ament_lint_auto REQUIRED)# thSEO靠我e following line skips the linter which checks for copyrights# comment the line when a copyright andSEO靠我 license is added to all source filesset(ament_cmake_copyright_FOUND TRUE)# the following line skipsSEO靠我 cpplint (only works in a git repo)# comment the line when this package is in a git repo and when# aSEO靠我 copyright and license is added to all source filesset(ament_cmake_cpplint_FOUND TRUE)ament_lint_autSEO靠我o_find_test_dependencies() endif()ament_package()构建功能包:colcon build --packages-select srv_deSEO靠我mo
查看自定义的消息:激活环境:. install/setup.bash ,查看自定义消息ros2 interface show srv_demo/srv/AddInt
pldz@pldz-pc:~/sSEO靠我hare/ROS2_DEMO/3_Chapter/code$ colcon build --packages-select srv_demo Starting >>> srv_demoSEO靠我 Finished <<< srv_demo [12.2s] Summary: 1 package finished [12.5s] pldz@pldz-pc:~/shSEO靠我are/ROS2_DEMO/3_Chapter/code$ . install/setup.bash pldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/SEO靠我code$ ros2 interface show srv_demo/srv/AddInt int32 num1 int32 num2 --- SEO靠我 int32 sum 查看instll下面的文件:其中.c的class文件在install/<功能包名>/include/<功能包名>/<功能包名>/msg/**,.py在inSEO靠我stall/<功能包名>/local/lib/python3.10/dist-packages/<功能包名>/msg/**,后续Vscode可以通过配置settings.json添加提示这里直接使用3.2.2创建的srv_demo服务功能包进行ROS2 C/C++的服务通讯的实现
参考内容:
Writing a simple service and clienSEO靠我t (C++)
2.3.3_服务通信_C++实现_01框架搭建
ROS2探索(三)service
创建功能包,其中功能包名称为cpp_srv_seSEO靠我rver,节点名为cppSrvServerNode:ros2 pkg create cpp_srv_server --build-type ament_cmake --node-name cppSrvSEO靠我ServerNode
配置Vscode环境,在工作空间创建.vscode文件夹,并加入settings.json文件,添加ROS2的include环境和当前工作空间的install文件夹下的srv_deSEO靠我mo功能包的include路径:
{"C_Cpp.default.includePath": ["/opt/ros/humble/include/**","./install/srv_demo/inclSEO靠我ude/**"], } 编写服务端节点文件,主要包括导入包,创建服务节点,实现回调函数: // 1. 调用自定义的服务文件 // 1SEO靠我.1 rclcpp和srv_demo两个功能包的头文件 // 如果有vscode的下划线提示,说明是settings.json没有配置好 #include "rclcpSEO靠我p/rclcpp.hpp" #include "srv_demo/srv/add_int.hpp"// 1.2. 调用功能包下的自定义的服务,其中自定义的服务名已经变成了一个类名 SEO靠我 using srv_demo::srv::AddInt;// std_bind的占位符 using std::placeholders::_1; usingSEO靠我 std::placeholders::_2;// 2.定义节点类; class CppSrvServer: public rclcpp::Node{public:// 2.1 构造函SEO靠我数,其中节点名直接赋予cppSrvServerNodeCppSrvServer():Node("cppSrvServerNode"){// 2.2 创建服务端server_ = this->creatSEO靠我e_service<AddInt>("mySrvName",std::bind(&CppSrvServer::addIntFunc, this, _1, _2));RCLCPP_INFO(this->SEO靠我get_logger(),"Server is starting ...");}private:// 3. 服务端的回调函数实现,其中参数的写法区分主要在于类型,参数数量包括请求体req和响应体rspSEO靠我void addIntFunc(const AddInt::Request::SharedPtr req, const AddInt::Response::SharedPtr rsp){rsp->suSEO靠我m = req->num1 + req->num2;RCLCPP_INFO(this->get_logger(),"request body :(%d,%d), response is :%d", rSEO靠我eq->num1, req->num2, rsp->sum);}// 4 服务的声明rclcpp::Service<AddInt>::SharedPtr server_; };int SEO靠我main(int argc, char const *argv[]) {rclcpp::init(argc,argv);auto server_ = std::make_shared<SEO靠我CppSrvServer>();rclcpp::spin(server_);rclcpp::shutdown();return 0; }编译: colcon SEO靠我build --packages-select cpp_srv_server
激活环境:. install/setup.bash
运行:ros2 run cpp_srv_server cppSrvServSEO靠我erNode
pldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/code$ colcon build --packages-select cpp_srv_server SEO靠我 Starting >>> cpp_srv_server Finished <<< cpp_srv_server [20.6s] Summary: 1 package fiSEO靠我nished [21.0s] pldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/code$ . install/setup.bash pSEO靠我ldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/code$ ros2 run cpp_srv_server cppSrvServerNode [INFOSEO靠我] [1683125184.734934447] [cppSrvServerNode]: Server is starting ... ^C[INFO] [1683125186.240SEO靠我946812] [rclcpp]: signal_handler(signum=2)创建功能包,功能包名为cpp_srv_client,节点SEO靠我名称为cppSrvClientNode,依赖rclcpp和srv_demo:ros2 pkg create cpp_srv_client --build-type ament_cmake --nodeSEO靠我-name cppSrvClientNode --dependencies rclcpp srv_demo
编写客户端节点,注意在此过程中需要和 服务端的连接进行判断,以及发送的请求是否能够收到返回值的用SEO靠我法的操作:
// 1. 调用自定义的服务文件 // 1.1 rclcpp和srv_demo两个功能包的头文件 #include "rclcpp/rclcpp.hpp" SEO靠我 #include "srv_demo/srv/add_int.hpp"// 1.2. 调用功能包下的自定义的服务,其中自定义的服务名已经变成了一个类名 using srvSEO靠我_demo::srv::AddInt;// 时间函数用于持续访问服务端 using namespace std::chrono_literals;// 2.定义节点类; SEO靠我 class CppSrvClient: public rclcpp::Node{public:CppSrvClient():Node("cppSrvClientNode"){// 2.1 创建客户端SEO靠我,并绑定服务通讯名称为mySrvNameclient_ = this->create_client<AddInt>("mySrvName");RCLCPP_INFO(this->get_logger(SEO靠我),"Client is starting ...");}// 2.2 等待与服务的连接bool connect_server(){while (!client_->wait_for_service(SEO靠我1s)){if (!rclcpp::ok()){RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"Interrupted while waiting for the SEO靠我service. Exiting.");return false;}RCLCPP_INFO(this->get_logger(),"service not available, waiting agaSEO靠我in...");}return true;}// 2.3 客户端发送请求;rclcpp::Client<srv_demo::srv::AddInt>::FutureAndRequestId send_SEO靠我request(int32_t num1, int32_t num2){auto request = std::make_shared<AddInt::Request>();request->num1SEO靠我 = num1;request->num2 = num2;// 2.3.1 发送请求auto response = client_->async_send_request(request);returSEO靠我n response;}private:// 2.4 服务通讯客户端的声明rclcpp::Client<AddInt>::SharedPtr client_; };int main(iSEO靠我nt argc, char ** argv) {// 3. 初始化ROS2客户端rclcpp::init(argc,argv);// 3.1 创建对象指针并调用其功能;auto cppSEO靠我SrvClientNode = std::make_shared<CppSrvClient>();// 3.2 连接客户端bool flag = cppSrvClientNode->connect_sSEO靠我erver();if (!flag){RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"Connect failed! ");return 0;}// 3.3 发送请SEO靠我求并等待响应auto response = cppSrvClientNode->send_request(100,200);// 3.4 节点处理响应if (rclcpp::spin_until_fuSEO靠我ture_complete(cppSrvClientNode,response) == rclcpp::FutureReturnCode::SUCCESS){RCLCPP_INFO(cppSrvCliSEO靠我entNode->get_logger(),"The response is :%d!", response.get()->sum);} else {RCLCPP_INFO(cppSrvClientNSEO靠我ode->get_logger(),"Request error");}rclcpp::shutdown();return 0; }由SEO靠我于在创建包的过程中已经指明了所有依赖项,不需要进行额外的配置,直接编译运行即可:colcon build --packages-select cpp_srv_client
激活环境并运行节点
参考内容
Writing a simple service and client (Python)
2.3.4_服务通信_Python实现_01框架搭建
创建功能包,包名为python_srv_server,节点名为pythonSrvServerNode:ros2 pkg create python_srv_sSEO靠我erver --build-type ament_python --node-name pythonSrvServerNode
配置Vscode,主要添加功能包的提示环境,编辑settings.jsonSEO靠我:
{"C_Cpp.default.includePath": ["/opt/ros/humble/include/**","./install/srv_demo/include/**"],"pythoSEO靠我n.analysis.include": ["/opt/ros/humble/local/lib/python3.10/dist-packages/**","./install/srv_demo/loSEO靠我cal/lib/python3.10/dist-packages/**"] } 编写Python服务端节点文件: import rclpy SEO靠我 from rclpy.node import Node# 1. 导入包,其中的类名等于服务文件名称 from srv_demo.srv import AddInt# 2. 定义SEO靠我服务端节点 class PythonSrvServer(Node):def __init__(self):super().__init__(pythonSrvServerNode)# SEO靠我2.1 创建服务端self.srver_ = self.create_service(AddInt, mySrvName, self.addIntFunc)self.get_logger().infoSEO靠我("Server is starting ...")# 2.2 服务端的处理回调函数def addIntFunc(self, request:AddInt, response:AddInt):respSEO靠我onse.sum = request.num1 + request.num2self.get_logger().info(The request is :{} {},Response is :{}.fSEO靠我ormat(request.num1, request.num2, response.sum))return responsedef main():rclpy.init()pythonSrvServeSEO靠我rNode = PythonSrvServer()rclpy.spin(pythonSrvServerNode)rclpy.shutdown()if __name__ == __main__:mainSEO靠我()编SEO靠我译:colcon build --packages-select python_srv_server
激活环境:. install/setup.bash
运行节点:ros2 run python_srv_SEO靠我server pythonSrvServerNode
pldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/code$ colcon build --packages-selSEO靠我ect python_srv_server Starting >>> python_srv_server --- stderr: python_srv_server SEO靠我 /usr/lib/python3/dist-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarningSEO靠我: setup.py install is deprecated. Use build and pip and other standards-based tools.warnings.warn( SEO靠我 --- Finished <<< python_srv_server [2.18s]Summary: 1 package finished [2.71s]1 packagSEO靠我e had stderr output: python_srv_server pldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/code$ . instSEO靠我all/setup.bash pldz@pldz-pc:~/share/ROS2_DEMO/3_Chapter/code$ ros2 run python_srv_server pytSEO靠我honSrvServerNode [INFO] [1683157866.794236460] [pythonSrvServerNode]: Server is starting ...SEO靠我创建Python功能包时,包名为python_srv_client,节点名为pythonSrvClientNode,直接指定依赖项rclpSEO靠我y和srv_demo:ros2 pkg create python_srv_client --build-type ament_python --node-name pythonSrvClientNoSEO靠我de --dependencies rclpy srv_demo
编写节点:主要是统一节点的订阅话题,异步接收返回的结果
import sys import rclpy fSEO靠我rom rclpy.node import Node # 1. 导入包 from srv_demo.srv import AddInt# 2.定义客户端节点 SEO靠我 class pythonSrvClient(Node):def __init__(self):# 2.1 继承node节点,节点名为pythonSrvClientNodesuper().__inSEO靠我it__(pythonSrvClientNode)# 2.2 创建客户端self.client_ = self.create_client(AddInt, mySrvName)# 2.3 等待连接seSEO靠我lf.wait_for_connect()# 3. 实现等待函数def wait_for_connect(self):while not self.client_.wait_for_service(tSEO靠我imeout_sec=1.0):self.get_logger().info(Waiting for connect ...)# 4. 实现发送请求函数def send_request(self, nSEO靠我um1, num2):request = AddInt.Request()request.num1 = num1request.num2 = num2self.future = self.clientSEO靠我_.call_async(request)def main():rclpy.init()# 5.创建客户端节点pythonSrvClientNode = pythonSrvClient()pythonSEO靠我SrvClientNode.send_request(200,300)# 6. 等待响应rclpy.spin_until_future_complete(pythonSrvClientNode,pytSEO靠我honSrvClientNode.future)try:response = pythonSrvClientNode.future.result()except Exception as e:pythSEO靠我onSrvClientNode.get_logger().info(Request error {}.format(e))else:pythonSrvClientNode.get_logger().iSEO靠我nfo(Response is {}.format(response.sum))rclpy.shutdown()if __name__ == __main__:main()由于指定了依赖项,直接编译即可: colcon build --packages-select python_srv_client
激活环境: . install/setup.bSEO靠我ash
运行所有节点:
网站备案号:浙ICP备17034767号-2