1. 程式人生 > >ROS系統學習4---最小服務系統的製作

ROS系統學習4---最小服務系統的製作

上一篇文章我們介紹了ros最小話題系統的製作,本篇將進一步介紹ROS的另一種節點間的互動形式---服務

首先,什麼是服務?

它是節點間的另外一種互動方式(這是句廢話。。。),它提供了一種有應答的通訊方式。

其次,為什麼要有它?

這個問題比較深奧,我們得先回頭去看看“話題”的特點。

話題是單向的,按照一定頻率傳送的一種通訊方式。在這種方式下,發出訊息的一方是不知道訊息有沒有人收到的。另一方面,如果只想傳送一次比較重要的訊息,也是辦不到的。

因此,我們可以做出這樣的類比,話題有點像是一種被封裝過了的UDP傳輸機制,而服務則像是一種被封裝過的TCP機制。注意這裡我說的是“像”,這是因為它們的底層其實並沒有想象的這麼簡單,以後本人研究過後會專門做這部分的介紹。

下面,我們將製作一個最小的服務系統,來看看服務該怎麼用起來。

這個系統是這樣的,我們製作一個伺服器,該伺服器接收三個整數,然後輸出三個整數的和(也是一個整數)。

客戶端通過請求服務,將要做加法的三個數給伺服器,然後伺服器返回結果給它。

首先強調一下,本篇文章的工作空間和包沿用第二篇文章的。

在製作的過程中,我們需要定義一個service檔案,該檔案告訴ROS我們這個伺服器的輸入輸出是什麼東西。具體的,在包“printHelloRosPK”中,新建一個叫做“srv”的資料夾,然後在裡面建立一個檔案,名字隨便取,本文取的“add.srv”。因為我們要輸入3個整數,輸出它們的和,因此檔案的內容如下:

int32 A
int32 B
int32 C
---
int32 sum

前三行是我們要輸入的三個整數,第四行是分割,第五行是輸出。

另外,由於我們在第二篇文章建立包的時候沒有包含服務需要的依賴項,因此需要加進去,方法是開啟包下面的“package.xml”,然後將下面一句話新增進去。

  <build_depend>message_generation</build_depend>

然後開啟CMakeLists.txt,找到

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)

新增 message_generation,變成

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
)

搜尋"srv"關鍵字,看到如下的程式碼

## Generate services in the 'srv' folder
# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

把“#”去掉,然後修改檔名稱,修改後如下:

# Generate services in the 'srv' folder
 add_service_files(
   FILES
   add.srv
 )

找到

# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )

把註釋去掉

 generate_messages(
   DEPENDENCIES
   std_msgs
 )

這樣準備工作就做完了,我們可以檢查ROS是否能夠識該服務描述。輸入:

rossrv show add

成功的話輸出如下:

接著編寫伺服器和客戶端:

service.cpp

#include "ros/ros.h"  
#include "printHelloRosPK/add.h"  
 
//服務主功能函式,輸入為client給的資料,輸出處理結果 
bool add(printHelloRosPK::add::Request  &req,  
         printHelloRosPK::add::Response &res)
{  
  res.sum = req.A + req.B+ req.C;  
  ROS_INFO("request: x=%ld, y=%ld, z=%ld", (long int)req.A, (long int)req.B, (long int)req.C);  
  ROS_INFO("sending back response: [%ld]", (long int)res.sum);  
  return true;  
}  
  
int main(int argc, char **argv)  
{  
  ros::init(argc, argv, "service");  
  ros::NodeHandle n;  
  
  ros::ServiceServer service = n.advertiseService("add", add);//建立service,並在ROS內釋出出來 
  ROS_INFO("Ready to add three ints.");  
  ros::spin();  
  
  return 0;  
}  

client.cpp

#include "ros/ros.h"  
#include "printHelloRosPK/add.h"//由編譯系統自動根據我們先前建立的srv檔案生成的對應該srv檔案的標頭檔案
#include <cstdlib>  
  
int main(int argc, char **argv)  
{  
  ros::init(argc, argv, "add_three_ints_client");  
  
  ros::NodeHandle n;  
  //為add_three_ints_service建立一個client。ros::ServiceClient物件待會用來呼叫service。
  ros::ServiceClient client = n.serviceClient<printHelloRosPK::add>("add");
  printHelloRosPK::add srv; //例項化一個由ROS編譯系統自動生成的service類,並給其request成員賦值 
  srv.request.A = 1;  
  srv.request.B = 2;  
  srv.request.C = 3;  

  //呼叫service。由於service的呼叫是模態過程(呼叫的時候佔用程序阻止其他程式碼的執行),一旦呼叫完成,將返回呼叫結果。
  //如果service呼叫成功,call()函式將返回true,srv.response裡面的值將是合法的值。
  //如果呼叫失敗,call()函式將返回false,srv.response裡面的值將是非法的
  if (client.call(srv))  
  {  
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);  
    return 0;
  }  
  else  
  {  
    ROS_ERROR("Failed to call service add_three_ints");  
    return 1;  
  }  
}  

在CMakeLists.txt末尾新增檔案位置及依賴關係:

add_executable(service /home/weixin/HelloRos/src/printHelloRosPK/src/service.cpp)#定義了這個工程會生成一個檔名為"topicSend"的可執行檔案
target_link_libraries(service ${catkin_LIBRARIES})#指定在連結目標檔案的時候需要連結的外部庫
add_dependencies(service beginner_tutorials_generate_messages_cpp)#為可執行檔案新增對生成的訊息檔案的依賴

add_executable(client /home/weixin/HelloRos/src/printHelloRosPK/src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client beginner_tutorials_generate_messages_cpp)

接著編譯下就可以了:

catkin_make