基於ROS 服務通訊模式詳解
ROS 服務通訊模式
摘自《ROS機器人開發實踐》
服務(services)是節點之間通訊的另一種方式。服務允許節點發送請求(request) 並獲得一個響應(response)
AddTwoInts.h檔案是根據AddTwoInts.srv檔案生成的
還會自動生成
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回撥函式,輸入引數req,輸出引數res bool add(learning_communication::AddTwoInts::Request &req,learning_communication::AddTwoInts::Response &res) { // 將輸入引數中的請求資料相加,結果放到應答變數中 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節點初始化 ros::init(argc,argv,"add_two_ints_server"); // 建立節點控制代碼 ros::NodeHandle n; // 建立一個名為add_two_ints的server,註冊回撥函式add() ros::ServiceServer service = n.advertiseService("add_two_ints",add); // 迴圈等待回撥函式 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,"add_two_ints_client"); // 從終端命令列獲取兩個加數 if (argc != 3) { ROS_INFO("usage: add_two_ints_client X Y"); return 1; } // 建立節點控制代碼 ros::NodeHandle n; // 建立一個client,請求add_two_int service // service訊息型別是learning_communication::AddTwoInts ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints"); // 建立learning_communication::AddTwoInts型別的service訊息 learning_communication::AddTwoInts srv; srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]); // 釋出service請求,等待加法運算的應答結果 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="[email protected]">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:放置功能包中的配置檔案,由使用者建立,檔名可以不同。 2)include:放置功能包中需要用到的標頭檔案。 3)scripts:放置可以直接執行的Python指令碼。 4)src:放置需要編譯的C++程式碼。 5)launch:放置功能包中的所有啟動檔案。 6)msg:放置功能包自定義的訊息型別。 7)srv:放置功能包自定義的服務型別。 8)action:放置功能包自定義的動作指令。 9)CMakeLists.txt:編譯器編譯功能包的規則。
如何自定義服務資料
與話題訊息類似,ROS中的服務資料可以通過srv檔案進行語言無關的介面定義,一般放置在功能包根目錄下的srv資料夾中。該檔案包含請求與應答兩個資料域,資料域中的內容與話題訊息的資料型別相同,只是在請求與應答的描述之間,需要使用“—”三個橫線進行分割。
針對加法運算例程中的服務需求,建立一個定義服務資料型別的srv檔案learning_communication/srv/AddTwoInts.sint64 a
int64 b
int64 sumv檔案的內容較為簡單,在服務請求的資料域中定義了兩個int64型別的變數a和b,用來儲存兩個加數; 又在服務應答的資料域中定義了一個int64型別的變數sum,用來儲存“a+b”的結果。
完成服務資料型別的描述後,與話題訊息一樣,還需要在功能包的package.xml和CMakeLists.txt檔案中配置依賴與編譯規則,在編譯過程中將該描述檔案轉換成程式語言所能識別的程式碼。
開啟package.xml檔案,新增以下依賴配置
message_generation message_runtime
開啟CMakeLists.txt檔案,新增如下配置
find_package(catkin REQUIRED COMPONENTS geometry_msgs roscpp rospy std_msgs message_generation ) add_service_files( FILES AddTwoInts.srv )
message_generation包不僅可以針對話題訊息產生相應的程式碼,還可以根據服務訊息的型別描述檔案產生相關的程式碼。 功能包編譯成功後,在服務的Server節點和Client節點的程式碼實現中就可以直接呼叫這些定義好的服務訊息了。 接下來我們就編寫Server和Client節點的程式碼,完成兩數相加求和的服務過程。
程式碼解釋
Server節點實現過程
1. 標頭檔案
#include "learning-communication/AddTwoInts.h"
使用ROS中的服務,必須包含服務資料型別的標頭檔案,這裡使用的標頭檔案是learning_communication/AddTwoInts.h,該標頭檔案根據我們之前建立的服務資料型別的描述檔案AddTwoInts.srv自動生成。
2.主函ros::ServiceServer service = n.advertiseService(“add_two_ints”,add);部分相對簡單,先初始化節點,建立節點控制代碼,重點是要建立一個服務的Server,指定服務的名稱以及接收到服務資料後的回撥函式。然後開始 迴圈等待服務請求;一旦有服務請求,Server就跳入回撥函式進行處理。
3.回撥函式部分
bool add(learning_communication::AddTwoInts::Request &req,learning_communication::AddTwoInts::Response &res)
回撥函式是真正實現服務功能的部分,也是設計的重點。add()函式用於完成兩個變數相加的功能,其傳入引數便是我們在服務資料型別描述檔案中宣告的請求與應答的資料結構。
{ res.sum = req.a + req.b; ROS_INFO("request: x=%ld,(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; }
在完成加法運算後,求和結果會放到應答資料中,反饋到Client,回撥函式返回true。
服務中的Server實現流程如下:
·初始化ROS節點。
·建立Server例項。
·迴圈等待服務請求,進入回撥函式。
·在回撥函式中完成服務功能的處理並反饋應答資料。
Client節點的實現過程。
1.建立Client
ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts> ("add_two_ints");
首先需要建立一個add_two_ints的Client例項,指定服務型別為learning_communication:AddTwoInts。
2.釋出服務請求
learning_communication::AddTwoInts srv; srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]);
然後例項化一個服務資料型別的變數,該變數包含兩個成員:request和response。將節點執行時輸入的兩個引數作為需要相加的兩個整型數儲存到變數中。
if (client.call(srv))
接著進行服務呼叫。該呼叫過程會發生阻塞,呼叫成功後返回true,訪問srv.reponse即可獲取服務請求的結果。如果呼叫失敗會返回false,srv.reponse則不可使用。
服務中的Client實現流程如下:
·初始化ROS節點。
·建立一個Client例項。
·釋出服務請求資料。
·等待Server處理之後的應答結果。
編譯功能包
編輯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)
現在就可以使用catkin_make命令編譯功能包了。
執行Server和Client
執行編譯生成的Server和Client節點。
1.啟動roscore
在執行節點之前,首先需要確保ROS Master已經成功啟動:
roscore2.執行Server節點開啟終端,使用如下命令執行Server節點:roscore
2.執行Server節點開啟終端,使用如下命令執行
Server節點: rosrun learning_communication server
3.執行Client節點
開啟一個新的終端,執行Client節點,同時需要輸入加法運算的兩個加數值:
$ rosrun learning_communication client 3 5
Client啟動後釋出服務請求,併成功接收到反饋結果
Server接收到服務呼叫後完成加法求解,並將結果反饋給Client
wiki
rosservice list 輸出可用服務的資訊 rosservice call 呼叫帶引數的服務 rosservice type 輸出服務型別 rosservice find 依據型別尋找服務find services by service type rosservice uri 輸出服務的ROSRPC uri
以上這篇基於ROS 服務通訊模式詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。