@目錄1.流程2.自定義請求和響應的數據2.1 std_msgs內置類型2.2 編寫.srv文件2.3 修改package.xml文件2.4 修改CMakeLists.txt文件2.4.1 修改find_package指令2.4.2 添加add_message_files指令2.4.3 添加gene ...
@
目錄1.流程
服務通信也是ROS中一種極其常用的通信模式,服務通信是基於請求響應模式的,是一種應答機制。也即: 一個節點A向另一個節點B發送請求,B接收處理請求並產生響應結果返回給A。在ROS中,實現服務通信只需要如下幾步:
- 確定客戶端發送的請求的數據類型和服務端響應的數據類型,需要自定義.srv文件,修改好CMakeLists.txt文件和package.xml文件並重編譯
- 編寫發佈方和訂閱方的cpp文件,修改好CMakeLists.txt文件並重編譯
- 分別啟動發佈方節點和訂閱方節點,必須先啟動服務端再啟動客戶端
2.自定義請求和響應的數據
2.1 std_msgs內置類型
- 內置類型與 C++ 和 Python 中的對應關係:
Primitive Type | C++ | Python |
---|---|---|
bool | uint8_t | bool |
int8 | int8_t | int |
uint8 | uint8_t | int |
int16 | int16_t | int |
uint16 | uint16_t | int |
int32 | uint32_t | int |
uint64 | uint64_t | long int |
float32 | float | float |
float64 | double | float |
string | std::string | str bytes |
time | ros::Time | rospy.Time |
duration | ros::Duration | rospy.Duration |
- 內置類型的數組與 C++ 和 Python 中的對應關係:
Primitive Type | C++ | Python |
---|---|---|
variable-length | std::vector | tuple |
fixed-length | boost::array<T, length>或std::vector | tuple |
2.2 編寫.srv文件
示例如下:
#文件名AddInt.srv
# 客戶端請求時發送的兩個數字
int32 num1
int32 num2
---
# 伺服器響應發送的數據
int32 sum
- 中間的三橫用以區分請求數據和響應數據
2.3 修改package.xml文件
- 查看是否存在如下編譯依賴
<build_depend>message_generation</build_depend>
- 查看是否存在如下執行依賴
<exec_depend>message_generation</exec_depend>
2.4 修改CMakeLists.txt文件
2.4.1 修改find_package指令
# 需要加入 message_generation,必須有 std_msgs
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
2.4.2 添加add_message_files指令
## 配置 srv 源文件
add_service_files(
FILES
AddInt.srv
)
2.4.3 添加generate_messages指令
generate_messages(
DEPENDENCIES
std_msgs
)
其中,add_service_files指令必須要在generate_messages指令的前面,然後在工作空間目錄編譯即可。
2.5 查看頭文件
經過以上幾步,在${workspace}/devel/include/${package}/
目錄下應該會出現頭文件,如圖:
- 如果沒有出現,是無法進行接下來的步驟的。這時,只需要把
${workspace}/
目錄下的build目錄和devel目錄全部刪除,然後重新編譯即可。
rm -rf build/
rm -rf devel/
catkin_make
3.編寫cpp文件
3.1 功能包目錄文件樹
3.2 修改CMakeLists.txt文件
3.2.1 添加add_executable指令
add_executable(server src/server.cpp)
add_executable(client src/client.cpp)
3.2.2 添加add_dependencies指令
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_dependencies(client ${PROJECT_NAME}_gencpp)
3.2.3 添加target_link_libraries指令
target_link_libraries(server
${catkin_LIBRARIES}
)
target_link_libraries(client
${catkin_LIBRARIES}
)
3.3 服務端cpp
示例如下:
/*
需求:
編寫兩個節點實現服務通信,客戶端節點需要提交兩個整數到伺服器
伺服器需要解析客戶端提交的數據,相加後,將結果響應回客戶端,
客戶端再解析
伺服器實現:
1.包含頭文件
2.初始化 ROS 節點
3.創建 ROS 句柄
4.創建 服務 對象
5.回調函數處理請求並產生響應
6.由於請求有多個,需要調用 ros::spin()
*/
// 1.包含頭文件
#include "ros/ros.h"
#include "serve/AddInt.h"
// bool 返回值由於標誌是否處理成功
bool reponse(serve::AddInt::Request& req,
serve::AddInt::Response& resp){
int num1 = req.num1;
int num2 = req.num2;
ROS_INFO("伺服器接收到的請求數據為:num1 = %d, num2 = %d",num1, num2);
//邏輯處理
if (num1 < 0 || num2 < 0)
{
ROS_ERROR("提交的數據異常:數據不可以為負數");
return false;
}
//如果沒有異常,那麼相加並將結果賦值給 resp
resp.sum = num1 + num2;
return true;
}
int main(int argc, char *argv[])
{
//設置編碼
setlocale(LC_ALL,"");
// 2.初始化 ROS 節點
ros::init(argc,argv,"server");
// 3.創建 ROS 句柄
ros::NodeHandle nh;
// 4.創建 服務 對象,回調函數的返回值必須是布爾類型
ros::ServiceServer server = nh.advertiseService("AddInt",reponse);
ROS_INFO("服務已經啟動....");
// 5.回調函數處理請求並產生響應
// 6.由於請求有多個,需要調用 ros::spin()
ros::spin();
return 0;
}
- 創建服務對象時的回調函數的返回值必須是布爾類型
3.4 客戶端cpp
示例如下:
/*
需求:
編寫兩個節點實現服務通信,客戶端節點需要提交兩個整數到伺服器
伺服器需要解析客戶端提交的數據,相加後,將結果響應回客戶端,
客戶端再解析
伺服器實現:
1.包含頭文件
2.初始化 ROS 節點
3.創建 ROS 句柄
4.創建 客戶端 對象
5.請求服務,接收響應
*/
// 1.包含頭文件
#include "ros/ros.h"
#include "serve/AddInt.h"
int main(int argc, char *argv[])
{
//設置編碼
setlocale(LC_ALL,"");
// 調用時動態傳值,如果通過 launch 的 args 傳參,需要傳遞的參數個數 +3
if (argc != 3)
// if (argc != 5)//launch 傳參(0-文件路徑 1傳入的參數 2傳入的參數 3節點名稱 4日誌路徑)
{
ROS_ERROR("請提交兩個整數");
return 1;
}
// 2.初始化 ROS 節點
ros::init(argc,argv,"client");
// 3.創建 ROS 句柄
ros::NodeHandle nh;
// 4.創建 客戶端 對象
ros::ServiceClient client = nh.serviceClient<serve::AddInt>("AddInt");
//等待服務啟動成功
//方式1
ros::service::waitForService("AddInt");
//方式2
// client.waitForExistence();
// 5.組織請求數據
serve::AddInt ai;
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
// 6.發送請求,返回 bool 值,標記是否成功
bool flag = client.call(ai);
// 7.處理響應
if (flag)
{
ROS_INFO("請求正常處理,響應結果:%d",ai.response.sum);
}
else
{
ROS_ERROR("請求處理失敗....");
return 1;
}
return 0;
}
4.效果
本文由博客一文多發平臺 OpenWrite 發佈!