SLAM+語音機器人DIY系列:(二)ROS入門——6.編寫簡單的service和client


摘要                                          

ROS機器人操作系統在機器人應用領域很流行,依托代碼開源和模塊間協作等特性,給機器人開發者帶來了很大的方便。我們的機器人“miiboo”中的大部分程序也采用ROS進行開發,所以本文就重點對ROS基礎知識進行詳細的講解,給不熟悉ROS的朋友起到一個拋磚引玉的作用。本章節主要內容:

1.ROS是什么

2.ROS系統整體架構

3.在ubuntu16.04中安裝ROS kinetic

4.如何編寫ROS的第一個程序hello_world

5.編寫簡單的消息發布器和訂閱器

6.編寫簡單的service和client

7.理解tf的原理

8.理解roslaunch在大型項目中的作用

9.熟練使用rviz

10.在實際機器人上運行ROS高級功能預覽



6.編寫簡單的service和client  

上一節介紹了兩個ros節點通過發布與訂閱消息的通信方式,現在就介紹ros節點間通信的另外一種方式服務。我們將學到:如何自定義服務類型、server端節點編寫、client端節點編寫等。我就以實現兩個整數求和為例,client端節點向server端節點發送ab的請求,server端節點返回響應sum=a+bclient端節點,通信網絡結構如圖20

20服務請求與響應ROS通信網絡結構圖

1)功能包的創建

catkin_ws/src/目錄下新建功能包service_example,並在創建時顯式的指明依賴roscppstd_msgs,依賴std_msgs將作為基本數據類型用於定義我們的服務類型。打開命令行終端,輸入命令:

cd ~/catkin_ws/src/

#創建功能包service_example時,顯式的指明依賴roscpp和std_msgs,
#依賴會被默認寫到功能包的CMakeLists.txt和package.xml中
catkin_create_pkg service_example roscpp std_msgs

2)在功能包中創建自定義服務類型

通過前面的學習,我們知道服務通信過程中服務的數據類型需要用戶自己定義,與消息不同,節點並不提供標准服務類型。服務類型的定義文件都是以*.srv為擴展名,並且被放在功能包的srv/文件夾下。

服務類型定義:

首先,在功能包service_example目錄下新建srv目錄,然后在service_example/srv/目錄中創建AddTwoInts.srv文件,文件內容如下:

int64 a
int64 b
---
int64 sum

服務類型編譯配置:

定義好我們的服務類型后,要想讓該服務類型能在c++python等代碼中被使用,必須要做相應的編譯與運行配置。編譯依賴message_generation,運行依賴message_runtime

打開功能包中的CMakeLists.txt文件,找到下面這段代碼:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)

message_generation添加進去,添加后的代碼如下:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
)

繼續,找到這段代碼:

# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

去掉這段代碼的#注釋,將自己的服務類型定義文件AddTwoInts.srv填入,修改好后的代碼如下:

add_service_files(
  FILES
 AddTwoInts.srv
)

繼續,找到這段代碼:

# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )

去掉這段代碼的#注釋,generate_messages的作用是自動創建我們自定義的消息類型*.msg與服務類型*.srv相對應的*.h,由於我們定義的服務類型使用了std_msgs中的int64基本類型,所以必須向generate_messages指明該依賴,修改好后的代碼如下:

generate_messages(
  DEPENDENCIES
 std_msgs
)

然后打開功能包中的package.xml文件,填入下面三句依賴:

<build_depend>message_generation</build_depend>
<build_export_depend>message_generation</build_export_depend>
<exec_depend>message_runtime</exec_depend>

檢查ROS是否識別新建的服務類型:

我們通過<功能包名/服務類型名>找到該服務,打開命令行終端,輸入命令:

source ~/catkin_ws/devel/setup.bash

rossrv show service_example/AddTwoInts

看到下面的輸出,如圖21,就說明新建服務類型能被ROS識別,新建服務類型成功了。

21新建服務類型能被ROS識別

3)功能包的源代碼編寫

功能包中需要編寫兩個獨立可執行的節點,一個節點用來作為client端發起請求,另一個節點用來作為server端響應請求,所以需要在新建的功能包service_example/src/目錄下新建兩個文件server_node.cppclient_node.cpp,並將下面的代碼分別填入。

首先,介紹server節點server_node.cpp,代碼內容如下:

 

 1 #include "ros/ros.h"
 2 #include "service_example/AddTwoInts.h"
 3 
 4 bool add_execute(service_example::AddTwoInts::Request &req,
 5 service_example::AddTwoInts::Response &res)
 6 {
 7   res.sum = req.a + req.b;
 8   ROS_INFO("recieve request: a=%ld,b=%ld",(long int)req.a,(long int)req.b);
 9   ROS_INFO("send response: sum=%ld",(long int)res.sum);
10   return true;
11 } 
12 
13 int main(int argc,char **argv)
14 {
15   ros::init(argc,argv,"server_node");
16   ros::NodeHandle nh;
17 
18   ros::ServiceServer service = nh.advertiseService("add_two_ints",add_execute);
19   ROS_INFO("service is ready!!!");
20   ros::spin();
21 
22   return 0;
23 }

server節點代碼進行解析。

#include “ros/ros.h”

#include “service_example/AddTwoInts.h”

包含頭文件ros/ros.h,這是ROS提供的C++客戶端庫,是必須包含的頭文件,就不多說了。service_example/AddTwoInts.h是由編譯系統自動根據我們的功能包和在功能包下創建的*.srv文件生成的對應的頭文件,包含這個頭文件,程序中就可以使用我們自定義服務的數據類型了。

 

bool add_execute(...)

這個函數實現兩個int64整數求和的服務,兩個int64值從request獲取,返回求和結果裝入response里,requestresponse的具體數據類型都在前面創建的*.srv文件中被定義,這個函數返回值為bool型。

 

ros::init(argc,argv,”server_node”);

ros::NodeHandle nh;

初始化ros節點並指明節點的名稱,聲明一個ros節點的句柄,,就不多說了。

 

ros::ServiceServer service = nh.advertiseService(“add_two_ints”,add_execute);

這一句是創建服務,並將服務加入到ROS網絡中,並且這個服務在ROS網絡中以名稱add_two_ints唯一標識,以便於其他節點通過服務名稱進行請求。

 

ros::spin();

這一句話讓程序進入自循環的掛起狀態,從而讓程序以最好的效率接收客戶端的請求並調用回調函數,就不多說了。

 

接着,介紹client節點client_node.cpp,代碼內容如下:

 

 1 #include "ros/ros.h"
 2 #include "service_example/AddTwoInts.h"
 3 
 4 #include <iostream>
 5 
 6 int main(int argc,char **argv)
 7 {
 8   ros::init(argc,argv,"client_node");
 9   ros::NodeHandle nh;
10 
11   ros::ServiceClient client =
12   nh.serviceClient<service_example::AddTwoInts>("add_two_ints");
13   service_example::AddTwoInts srv;
14   
15   while(ros::ok())
16   {
17     long int a_in,b_in;
18     std::cout<<"please input a and b:";
19     std::cin>>a_in>>b_in;
20 
21     srv.request.a = a_in;
22     srv.request.b = b_in;
23     if(client.call(srv))
24     {
25       ROS_INFO("sum=%ld",(long int)srv.response.sum);
26     }
27     else
28     {
29       ROS_INFO("failed to call service add_two_ints");
30     }
31   }
32   return 0;
33 }

client節點代碼進行解析。

之前解釋過的類似的代碼就不做過多的解釋了,這里重點解釋一下前面沒遇到過的代碼。

ros::ServiceClient client =

 nh.serviceClient<service_example::AddTwoInts>("add_two_ints");

這一句創建client對象,用來向ROS網絡中名稱叫add_two_intsservice發起請求。

 

service_example::AddTwoInts srv;

定義了一個service_example::AddTwoInts服務類型的對象,該對象中的成員正是我們在*.srv文件中定義的absum,我們將待請求的數據填充到數據成員ab,請求成功后返回結果會被自動填充到數據成員sum中。

 

if(client.call(srv)){...}

這一句便是通過client的方法call來向service發起請求,請求傳入的參數srv在上面已經介紹過了。

4)功能包的編譯配置及編譯

創建功能包service_example時,顯式的指明依賴roscppstd_msgs,依賴會被默認寫到功能包的CMakeLists.txtpackage.xml中,並且在功能包中創建*.srv服務類型時已經對服務的編譯與運行做了相關配置,所以只需要在CMakeLists.txt文件的末尾行加入以下幾句用於聲明可執行文件就可以了:

add_executable(server_node src/server_node.cpp)
target_link_libraries(server_node ${catkin_LIBRARIES})
add_dependencies(server_node service_example_gencpp)

add_executable(client_node src/client_node.cpp)
target_link_libraries(client_node ${catkin_LIBRARIES})
add_dependencies(client_node service_example_gencpp)

這里面的add_executable用於聲明可執行文件。target_link_libraries用於聲明可執行文件創建時需要鏈接的庫。add_dependencies用於聲明可執行文件的依賴項,由於我們自定義了*.srvservice_example_gencpp的作用是讓編譯系統自動根據我們的功能包和在功能包下創建的*.srv文件生成的對應的頭文件和庫文件,service_example_gencpp這個名稱是由功能包名稱service_example加上_gencpp后綴而來的,后綴很好理解:生成c++文件就是_gencpp

生成python文件就是_genpy

接下來,就可以用下面的命令對功能包進行編譯了:

cd ~/catkin_ws/
catkin_make -DCATKIN_WHITELIST_PACKAGES="service_example"

5)功能包的啟動運行

首先,需要用roscore命令來啟動ROS節點管理器,ROS節點管理器是所有節點運行的基礎。

打開命令行終端,輸入命令:

roscore

然后,用source devel/setup.bash激活catkin_ws工作空間,用rosrun <package_name> <node_name>啟動功能包中的server節點。

再打開一個命令行終端,分別輸入命令:

cd ~/catkin_ws/
source devel/setup.bash
rosrun service_example server_node 

看到有輸出servive is ready!!!”,就說明server節點已經正常啟動並開始等待client節點向自己發起請求了,如圖22

(圖22server節點已經正常啟動

最后,用source devel/setup.bash激活catkin_ws工作空間,用rosrun <package_name> <node_name>啟動功能包中的client節點。

再打開一個命令行終端,分別輸入命令:

cd ~/catkin_ws/
source devel/setup.bash
rosrun service_example client_node 

看到有輸出提示信息please input a and b:”后,鍵盤鍵入兩個整數,以空格分割,輸入完畢后回車。如果看到輸出信息“sum=xxx”,就說明client節點向server端發起的請求得到了響應,打印出來的sum就是響應結果,這樣就完成了一次服務請求的通信過程,如圖23

(圖23client節點已經正常啟動

到這里,我們編寫的serverclient就大功告成了,為了加深對整個程序工作流程的理解,我再把serverclientROS通信網絡結構圖拿出來,加深一下理解。

24服務請求與響應ROS通信網絡結構圖

后記                

------SLAM+語音機器人DIY系列【目錄】快速導覽------

第1章:Linux基礎

1.Linux簡介

2.安裝Linux發行版ubuntu系統

3.Linux命令行基礎操作

第2章:ROS入門

1.ROS是什么

2.ROS系統整體架構

3.在ubuntu16.04中安裝ROS kinetic

4.如何編寫ROS的第一個程序hello_world

5.編寫簡單的消息發布器和訂閱器

6.編寫簡單的service和client

7.理解tf的原理

8.理解roslaunch在大型項目中的作用

9.熟練使用rviz

10.在實際機器人上運行ROS高級功能預覽

第3章:感知與大腦

1.ydlidar-x4激光雷達

2.帶自校准九軸數據融合IMU慣性傳感器

3.輪式里程計與運動控制

4.音響麥克風與攝像頭

5.機器人大腦嵌入式主板性能對比

6.做一個能走路和對話的機器人

第4章:差分底盤設計

1.stm32主控硬件設計

2.stm32主控軟件設計

3.底盤通信協議

4.底盤ROS驅動開發

5.底盤PID控制參數整定

6.底盤里程計標

第5章:樹莓派3開發環境搭建

1.安裝系統ubuntu_mate_16.04

2.安裝ros-kinetic

3.裝機后一些實用軟件安裝和系統設置

4.PC端與robot端ROS網絡通信

5.Android手機端與robot端ROS網絡通信

6.樹莓派USB與tty串口號綁定

7.開機自啟動ROS節點

第6章:SLAM建圖與自主避障導航

1.在機器人上使用傳感器

2.google-cartographer機器人SLAM建圖

3.ros-navigation機器人自主避障導航

4.多目標點導航及任務調度

5.機器人巡航與現場監控

第7章:語音交互與自然語言處理

1.語音交互相關技術

2.機器人語音交互實現

3.自然語言處理雲計算引擎

第8章:高階拓展

1.miiboo機器人安卓手機APP開發

2.centos7下部署Django(nginx+uwsgi+django+python3)

----------------文章將持續更新,敬請關注-----------------

 

如果大家對博文的相關類容感興趣,或有什么技術疑問,歡迎加入下面的《SLAM+語音機器人DIY》QQ技術交流群,一起討論學習^_^

關於我們:

視頻教程:

 

https://www.bilibili.com/video/av61448040


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM