基于ROS 服務(wù)通信模式詳解
ROS 服務(wù)通信模式
摘自《ROS機(jī)器人開(kāi)發(fā)實(shí)踐》
服務(wù)(services)是節(jié)點(diǎn)之間通訊的另一種方式。服務(wù)允許節(jié)點(diǎn)發(fā)送請(qǐng)求(request) 并獲得一個(gè)響應(yīng)(response)
AddTwoInts.h文件是根據(jù)AddTwoInts.srv文件生成的
還會(huì)自動(dòng)生成
AddTwoIntsRequest.h
AddTwoIntsResponse.h
AddTwoInts.h所在的目錄是
\catkin_ws\devel
AddTwoInts.srv
int64 a int64 b --- int64 sum
server.cpp
/** * AddTwoInts Server */ #include "ros/ros.h" #include "learning_communication/AddTwoInts.h" // service回調(diào)函數(shù),輸入?yún)?shù)req,輸出參數(shù)res bool add(learning_communication::AddTwoInts::Request &req, learning_communication::AddTwoInts::Response &res) { // 將輸入?yún)?shù)中的請(qǐng)求數(shù)據(jù)相加,結(jié)果放到應(yīng)答變量中 res.sum = req.a + req.b; ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b); ROS_INFO("sending back response: [%ld]", (long int)res.sum); return true; } int main(int argc, char **argv) { // ROS節(jié)點(diǎn)初始化 ros::init(argc, argv, "add_two_ints_server"); // 創(chuàng)建節(jié)點(diǎn)句柄 ros::NodeHandle n; // 創(chuàng)建一個(gè)名為add_two_ints的server,注冊(cè)回調(diào)函數(shù)add() ros::ServiceServer service = n.advertiseService("add_two_ints", add); // 循環(huán)等待回調(diào)函數(shù) ROS_INFO("Ready to add two ints."); ros::spin(); return 0; }
client.cpp
/** * AddTwoInts Client */ #include <cstdlib> #include "ros/ros.h" #include "learning_communication/AddTwoInts.h" int main(int argc, char **argv) { // ROS節(jié)點(diǎn)初始化 ros::init(argc, argv, "add_two_ints_client"); // 從終端命令行獲取兩個(gè)加數(shù) if (argc != 3) { ROS_INFO("usage: add_two_ints_client X Y"); return 1; } // 創(chuàng)建節(jié)點(diǎn)句柄 ros::NodeHandle n; // 創(chuàng)建一個(gè)client,請(qǐng)求add_two_int service // service消息類(lèi)型是learning_communication::AddTwoInts ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints"); // 創(chuàng)建learning_communication::AddTwoInts類(lèi)型的service消息 learning_communication::AddTwoInts srv; srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]); // 發(fā)布service請(qǐng)求,等待加法運(yùn)算的應(yīng)答結(jié)果 if (client.call(srv)) { ROS_INFO("Sum: %ld", (long int)srv.response.sum); } else { ROS_ERROR("Failed to call service add_two_ints"); return 1; } return 0; }
CMakeLists.txt
add_executable(server src/server.cpp) target_link_libraries(server ${catkin_LIBRARIES}) add_dependencies(server ${PROJECT_NAME}_gencpp) add_executable(client src/client.cpp) target_link_libraries(client ${catkin_LIBRARIES}) add_dependencies(client ${PROJECT_NAME}_gencpp)
package.xml
<?xml version="1.0"?> <package> <name>learning_communication</name> <version>0.0.0</version> <description>The learning_communication package</description> <maintainer email="hcx@todo.todo">hcx</maintainer> <license>TODO</license> <buildtool_depend>catkin</buildtool_depend> <build_depend>geometry_msgs</build_depend> <build_depend>roscpp</build_depend> <build_depend>rospy</build_depend> <build_depend>std_msgs</build_depend> <run_depend>geometry_msgs</run_depend> <run_depend>roscpp</run_depend> <run_depend>rospy</run_depend> <run_depend>std_msgs</run_depend> <build_depend>message_generation</build_depend> <run_depend>message_runtime</run_depend> <!-- The export tag contains other, unspecified, tags --> <export> <!-- Other tools can request additional information be placed here --> </export> </package>
文件夾的主要功能
1)config:放置功能包中的配置文件,由用戶創(chuàng)建,文件名可以不同。 2)include:放置功能包中需要用到的頭文件。 3)scripts:放置可以直接運(yùn)行的Python腳本。 4)src:放置需要編譯的C++代碼。 5)launch:放置功能包中的所有啟動(dòng)文件。 6)msg:放置功能包自定義的消息類(lèi)型。 7)srv:放置功能包自定義的服務(wù)類(lèi)型。 8)action:放置功能包自定義的動(dòng)作指令。 9)CMakeLists.txt:編譯器編譯功能包的規(guī)則。
如何自定義服務(wù)數(shù)據(jù)
與話題消息類(lèi)似,ROS中的服務(wù)數(shù)據(jù)可以通過(guò)srv文件進(jìn)行語(yǔ)言無(wú)關(guān)的接口定義,一般放置在功能包根目錄下的srv文件夾中。該文件包含請(qǐng)求與應(yīng)答兩個(gè)數(shù)據(jù)域,數(shù)據(jù)域中的內(nèi)容與話題消息的數(shù)據(jù)類(lèi)型相同,只是在請(qǐng)求與應(yīng)答的描述之間,需要使用“—”三個(gè)橫線進(jìn)行分割。
針對(duì)加法運(yùn)算例程中的服務(wù)需求,創(chuàng)建一個(gè)定義服務(wù)數(shù)據(jù)類(lèi)型的srv文件learning_communication/srv/AddTwoInts.sint64 a
int64 b
int64 sumv文件的內(nèi)容較為簡(jiǎn)單,在服務(wù)請(qǐng)求的數(shù)據(jù)域中定義了兩個(gè)int64類(lèi)型的變量a和b,用來(lái)存儲(chǔ)兩個(gè)加數(shù); 又在服務(wù)應(yīng)答的數(shù)據(jù)域中定義了一個(gè)int64類(lèi)型的變量sum,用來(lái)存儲(chǔ)“a+b”的結(jié)果。
完成服務(wù)數(shù)據(jù)類(lèi)型的描述后,與話題消息一樣,還需要在功能包的package.xml和CMakeLists.txt文件中配置依賴(lài)與編譯規(guī)則,在編譯過(guò)程中將該描述文件轉(zhuǎn)換成編程語(yǔ)言所能識(shí)別的代碼。
打開(kāi)package.xml文件,添加以下依賴(lài)配置
message_generation message_runtime
打開(kāi)CMakeLists.txt文件,添加如下配置
find_package(catkin REQUIRED COMPONENTS geometry_msgs roscpp rospy std_msgs message_generation ) add_service_files( FILES AddTwoInts.srv )
message_generation包不僅可以針對(duì)話題消息產(chǎn)生相應(yīng)的代碼,還可以根據(jù)服務(wù)消息的類(lèi)型描述文件產(chǎn)生相關(guān)的代碼。 功能包編譯成功后,在服務(wù)的Server節(jié)點(diǎn)和Client節(jié)點(diǎn)的代碼實(shí)現(xiàn)中就可以直接調(diào)用這些定義好的服務(wù)消息了。 接下來(lái)我們就編寫(xiě)Server和Client節(jié)點(diǎn)的代碼,完成兩數(shù)相加求和的服務(wù)過(guò)程。
代碼解釋
Server節(jié)點(diǎn)實(shí)現(xiàn)過(guò)程
1. 頭文件
#include "learning-communication/AddTwoInts.h"
使用ROS中的服務(wù),必須包含服務(wù)數(shù)據(jù)類(lèi)型的頭文件,這里使用的頭文件是learning_communication/AddTwoInts.h,該頭文件根據(jù)我們之前創(chuàng)建的服務(wù)數(shù)據(jù)類(lèi)型的描述文件AddTwoInts.srv自動(dòng)生成。
2.主函ros::ServiceServer service = n.advertiseService(“add_two_ints”, add);部分相對(duì)簡(jiǎn)單,先初始化節(jié)點(diǎn),創(chuàng)建節(jié)點(diǎn)句柄,重點(diǎn)是要?jiǎng)?chuàng)建一個(gè)服務(wù)的Server,指定服務(wù)的名稱(chēng)以及接收到服務(wù)數(shù)據(jù)后的回調(diào)函數(shù)。然后開(kāi)始 循環(huán)等待服務(wù)請(qǐng)求;一旦有服務(wù)請(qǐng)求,Server就跳入回調(diào)函數(shù)進(jìn)行處理。
3.回調(diào)函數(shù)部分
bool add(learning_communication::AddTwoInts::Request &req,learning_communication::AddTwoInts::Response &res)
回調(diào)函數(shù)是真正實(shí)現(xiàn)服務(wù)功能的部分,也是設(shè)計(jì)的重點(diǎn)。add()函數(shù)用于完成兩個(gè)變量相加的功能,其傳入?yún)?shù)便是我們?cè)诜?wù)數(shù)據(jù)類(lèi)型描述文件中聲明的請(qǐng)求與應(yīng)答的數(shù)據(jù)結(jié)構(gòu)。
{ res.sum = req.a + req.b; ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b); ROS_INFO("sending back response: [%ld]", (long int)res.sum); ROS_INFO("sending back response: [%ld]", (long int)res.sum); return true; }
在完成加法運(yùn)算后,求和結(jié)果會(huì)放到應(yīng)答數(shù)據(jù)中,反饋到Client,回調(diào)函數(shù)返回true。
服務(wù)中的Server實(shí)現(xiàn)流程如下:
·初始化ROS節(jié)點(diǎn)。
·創(chuàng)建Server實(shí)例。
·循環(huán)等待服務(wù)請(qǐng)求,進(jìn)入回調(diào)函數(shù)。
·在回調(diào)函數(shù)中完成服務(wù)功能的處理并反饋應(yīng)答數(shù)據(jù)。
Client節(jié)點(diǎn)的實(shí)現(xiàn)過(guò)程。
1.創(chuàng)建Client
ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts> ("add_two_ints");
首先需要?jiǎng)?chuàng)建一個(gè)add_two_ints的Client實(shí)例,指定服務(wù)類(lèi)型為learning_communication:AddTwoInts。
2.發(fā)布服務(wù)請(qǐng)求
learning_communication::AddTwoInts srv; srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]);
然后實(shí)例化一個(gè)服務(wù)數(shù)據(jù)類(lèi)型的變量,該變量包含兩個(gè)成員:request和response。將節(jié)點(diǎn)運(yùn)行時(shí)輸入的兩個(gè)參數(shù)作為需要相加的兩個(gè)整型數(shù)存儲(chǔ)到變量中。
if (client.call(srv))
接著進(jìn)行服務(wù)調(diào)用。該調(diào)用過(guò)程會(huì)發(fā)生阻塞,調(diào)用成功后返回true,訪問(wèn)srv.reponse即可獲取服務(wù)請(qǐng)求的結(jié)果。如果調(diào)用失敗會(huì)返回false,srv.reponse則不可使用。
服務(wù)中的Client實(shí)現(xiàn)流程如下:
·初始化ROS節(jié)點(diǎn)。
·創(chuàng)建一個(gè)Client實(shí)例。
·發(fā)布服務(wù)請(qǐng)求數(shù)據(jù)。
·等待Server處理之后的應(yīng)答結(jié)果。
編譯功能包
編輯CMakeLists.txt文件,加入如下編譯規(guī)則:
add_executable(server src/server.cpp) target_link_libraries(server ${catkin_LIBRARIES}) add_dependencies(server ${PROJECT_NAME}_gencpp) add_executable(client src/client.cpp) target_link_libraries(client ${catkin_LIBRARIES}) add_dependencies(client ${PROJECT_NAME}_gencpp)
現(xiàn)在就可以使用catkin_make命令編譯功能包了。
運(yùn)行Server和Client
運(yùn)行編譯生成的Server和Client節(jié)點(diǎn)。
1.啟動(dòng)roscore
在運(yùn)行節(jié)點(diǎn)之前,首先需要確保ROS Master已經(jīng)成功啟動(dòng):
roscore2.運(yùn)行Server節(jié)點(diǎn)打開(kāi)終端,使用如下命令運(yùn)行Server節(jié)點(diǎn):roscore
2.運(yùn)行Server節(jié)點(diǎn)打開(kāi)終端,使用如下命令運(yùn)行
Server節(jié)點(diǎn): rosrun learning_communication server
3.運(yùn)行Client節(jié)點(diǎn)
打開(kāi)一個(gè)新的終端,運(yùn)行Client節(jié)點(diǎn),同時(shí)需要輸入加法運(yùn)算的兩個(gè)加數(shù)值:
$ rosrun learning_communication client 3 5
Client啟動(dòng)后發(fā)布服務(wù)請(qǐng)求,并成功接收到反饋結(jié)果
Server接收到服務(wù)調(diào)用后完成加法求解,并將結(jié)果反饋給Client
wiki
rosservice list 輸出可用服務(wù)的信息 rosservice call 調(diào)用帶參數(shù)的服務(wù) rosservice type 輸出服務(wù)類(lèi)型 rosservice find 依據(jù)類(lèi)型尋找服務(wù)find services by service type rosservice uri 輸出服務(wù)的ROSRPC uri
以上這篇基于ROS 服務(wù)通信模式詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++面經(jīng)之什么是RAII面試問(wèn)題解析
這篇文章主要介紹了C++面經(jīng)之什么是RAII面試問(wèn)題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06C++實(shí)現(xiàn)LeetCode(166.分?jǐn)?shù)轉(zhuǎn)循環(huán)小數(shù))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(166.分?jǐn)?shù)轉(zhuǎn)循環(huán)小數(shù))2021-07-07C語(yǔ)言實(shí)現(xiàn) 數(shù)據(jù)類(lèi)型占多少字節(jié)指針占多少字節(jié)
這篇文章主要介紹了 C語(yǔ)言 數(shù)據(jù)類(lèi)型占多少字節(jié)指針占多少字節(jié)的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09C語(yǔ)言拓展實(shí)現(xiàn)Lua sleep函數(shù)
這篇文章主要介紹了C語(yǔ)言拓展實(shí)現(xiàn)Lua sleep函數(shù),本文使用C語(yǔ)言寫(xiě)出sleep函數(shù),編譯后在Lua中調(diào)用,需要的朋友可以參考下2015-04-04C語(yǔ)言進(jìn)階幾分鐘帶你理解大小端存儲(chǔ)模式
這篇文章主要為大家介紹了C語(yǔ)言進(jìn)階大小端模式的示例詳解,帶各位讀者朋友五分鐘腳踩大小端模式,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02讓?xiě)?yīng)用程序只運(yùn)行一個(gè)實(shí)例的實(shí)現(xiàn)方法
我們?cè)谑褂谩?60軟件管家》時(shí)發(fā)現(xiàn),在《360軟件管家》已經(jīng)運(yùn)行了的情況下,再次點(diǎn)擊《360軟件管家》的圖標(biāo),那么它不會(huì)再運(yùn)行另外一個(gè)《360軟件管家》,而是將已有的《360軟件管家》給激活,始終只能運(yùn)行一個(gè)《360軟件管家》的實(shí)例2013-05-05C++實(shí)現(xiàn)LeetCode(95.獨(dú)一無(wú)二的二叉搜索樹(shù)之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(95.獨(dú)一無(wú)二的二叉搜索樹(shù)之二),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C++?opencv圖像處理實(shí)現(xiàn)圖像腐蝕和膨脹示例
這篇文章主要為大家介紹了C++?opencv圖像處理實(shí)現(xiàn)圖像腐蝕和圖像膨脹示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05C++結(jié)構(gòu)體初始化的10種寫(xiě)法總結(jié)
這篇文章主要為大家詳細(xì)介紹了10種C++中結(jié)構(gòu)體初始化的寫(xiě)法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04