1. 程式人生 > 實用技巧 >ROS入門筆記(十二):動作程式設計 (C++)

ROS入門筆記(十二):動作程式設計 (C++)

ROS入門筆記(十二):動作程式設計 (C++)

目錄

01 導讀

C++程式碼必須通過編譯生成可執行檔案;

python程式碼是可執行檔案,不需要編譯;

  • 開發的功能包都放在catkin_ws這樣一個工作空間裡;
  • 新建的功能包取名為action_example;

工作模式的結構示意圖如下:

什麼是動作(action)

  • 一種問答通訊機制;
  • 帶有連續反饋;
  • 可以在任務過程中止執行;
  • 基於ROS的訊息機制實現。

通訊雙方在ROS Action Protocal下進行交流通訊是通過介面來實現,如下圖:

Action的介面

  • goal:釋出任務目標;
  • cancel:請求取消任務;
  • status:通知客戶端當前的狀態;
  • feedback:週期反饋任務執行的監控資料;
  • result:向客戶端傳送任務的執行結果,只發布一次。

我們可以看到,客戶端會向伺服器傳送目標指令和取消動作指令,而伺服器則可以給客戶端傳送實時的狀態資訊,結果資訊,反饋資訊等等,從而完成了service沒法做到的部分.

02 功能包的建立

在catkin_ws/src/目錄下新建功能包action_example,並在建立時顯式的指明依賴roscpp和actionlib actionlib_msgs,依賴actionlib actionlib_msgs將作為基本資料型別用於定義我們的服務型別。開啟命令列終端,輸入命令:

$ cd ~/catkin_ws/src

#建立功能包topic_example時,顯式的指明依賴roscpp和std_msgs,
#依賴會被預設寫到功能包的CMakeLists.txt和package.xml中
$ catkin_create_pkg action_example roscpp actionlib actionlib_msgs

03 在功能包中建立action(動作)

Action的工作原理是client-server模式,也是一個雙向的通訊模式。通訊雙方在ROS Action Protocol下通過訊息進行資料的交流通訊。client和server為使用者提供一個簡單的API來請求目標(在客戶端)或通過函式呼叫和回撥來執行目標(在伺服器端)。

3.1 自定義action

利用動作庫進行請求響應,動作的內容格式應包含三個部分,目標、反饋、結果。

在功能包action_example目錄下新建action目錄,然後在action_example/action/目錄中建立DoDishes.action檔案

# Define the goal  定義目標資訊
uint32 dishwasher_id  # Specify which dishwasher we want to use
---
# Define the result  定義結果資訊
uint32 total_dishes_cleaned
---
# Define a feedback message 定義週期反饋的資訊
float32 percent_complete

3.2 在package.xml中新增功能包依賴

action檔案被轉換成為C++,Python和其他語言的原始碼:

檢視package.xml, 確保它包含以下語句:

*部分ROS版本中的exec_depend需要改成run_depend

<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>

3.3 在CMakeLists.txt新增編譯選項

##1 Find catkin macros and libraries
##1 if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
##1 is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  actionlib_msgs 
  actionlib
)


##2 Generate actions in the 'action' folder
add_action_files(
  FILES
  DoDishes.action
)



##3 Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  actionlib_msgs   
)

04 功能包的原始碼編寫

在新建的功能包action_example/src目錄下新建兩個檔案action_server.cpp和action_client.cpp,並將下面的程式碼分別填入。

4.1 編寫action_server.cpp

如何實現一個動作伺服器:

  • 初始化ROS節點;
  • 建立動作伺服器例項;
  • 啟動伺服器,等待動作請求;
  • 在回撥函式中完成動作服務功能的處理,並反饋進度資訊;
  • 動作完成,傳送結束資訊。

在action_example/src包中建立action_server.cpp檔案:

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "action_example/DoDishesAction.h"

typedef actionlib::SimpleActionServer<action_example::DoDishesAction> Server;

// 收到action的goal後呼叫該回調函式
void execute(const action_example::DoDishesGoalConstPtr& goal, Server* as)
{
    ros::Rate r(1);
    action_example::DoDishesFeedback feedback;

    ROS_INFO("Dishwasher %d is working.", goal->dishwasher_id);

    // 假設洗盤子的進度,並且按照1hz的頻率釋出進度feedback
    for(int i=1; i<=10; i++)
    {
        feedback.percent_complete = i * 10;
        as->publishFeedback(feedback);
        r.sleep();
    }

    // 當action完成後,向客戶端返回結果
    ROS_INFO("Dishwasher %d finish working.", goal->dishwasher_id);
    as->setSucceeded();
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "do_dishes_server");
    ros::NodeHandle n;

    // 定義一個伺服器
    Server server(n, "do_dishes", boost::bind(&execute, _1, &server), false);
    
    // 伺服器開始執行
    server.start();

    ros::spin();

    return 0;
}

4.2 編寫action_client.cpp

如何實現一個動作客戶端:

  • 初始化ROS節點;
  • 建立動作客戶端例項;
  • 連線動作服務端;
  • 傳送動作目標;
  • 根據不同型別的服務端反饋處理回撥函式。

在action_example/src包中建立action_client.cpp檔案,並在其中貼上以下內容:

#include <actionlib/client/simple_action_client.h>
#include "action_example/DoDishesAction.h"

typedef actionlib::SimpleActionClient<action_example::DoDishesAction> Client;

// 當action完成後會呼叫該回調函式一次
void doneCb(const actionlib::SimpleClientGoalState& state,
        const action_example::DoDishesResultConstPtr& result)
{
    ROS_INFO("Yay! The dishes are now clean");
    ros::shutdown();
}

// 當action啟用後會呼叫該回調函式一次
void activeCb()
{
    ROS_INFO("Goal just went active");
}

// 收到feedback後呼叫該回調函式
void feedbackCb(const action_example::DoDishesFeedbackConstPtr& feedback)
{
    ROS_INFO(" percent_complete : %f ", feedback->percent_complete);
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "do_dishes_client");

    // 定義一個客戶端
    Client client("do_dishes", true);

    // 等待伺服器端
    ROS_INFO("Waiting for action server to start.");
    client.waitForServer();
    ROS_INFO("Action server started, sending goal.");

    // 建立一個action的goal
    action_example::DoDishesGoal goal;
    goal.dishwasher_id = 1;

    // 傳送action的goal給伺服器端,並且設定回撥函式
    client.sendGoal(goal,  &doneCb, &activeCb, &feedbackCb);

    ros::spin();

    return 0;
}

05 功能包的編譯配置(編譯節點)

說明:

  • C++程式碼必須通過編譯生成可執行檔案;
  • python程式碼是可執行檔案,不需要編譯;

如何編譯程式碼

  • 設定需要編譯的程式碼和生成的可執行檔案;
  • 設定連結庫;
  • 設定依賴。

建立功能包action_example時,顯式的指明依賴roscpp和std_msgs,依賴會被預設寫到功能包的CMakeLists.txt和package.xml中。

CMakeLists.txt 檔案末尾加入幾條語句:

add_executable(action_client src/action_client.cpp)
target_link_libraries( action_client ${catkin_LIBRARIES})
add_dependencies(action_client ${${PROJECT_NAME}_EXPORTED_TARGETS})

add_executable(action_server src/action_server.cpp)
target_link_libraries( action_server ${catkin_LIBRARIES})
add_dependencies(action_server ${${PROJECT_NAME}_EXPORTED_TARGETS})

這會生成兩個可執行檔案, action_clientaction_server, 預設儲存到 devel space 目錄下,具體是在~/catkin_ws/devel/lib/<package name> 中.

06 功能包的編譯

$ cd ~/catkin_ws
$ catkin_make -DCATKIN_WHITELIST_PACKAGES="action_example"    
$ source ~/catkin_ws/devel/setup.bash   # 重新整理環境

07 測試action_server和action_client

7.1 執行action_server

第一步,開啟一個命令列終端:

$ roscore

第二步,開啟第二個命令列終端:

# 用rosrun <package_name> <node_name>啟動功能包中的釋出節點。
$ source ~/catkin_ws/devel/setup.bash    # 啟用catkin_ws工作空間(必須有,必不可少) 
$ rosrun action_example action_server     

你將看到如下的輸出資訊:

[ INFO] [1588752334.514526874]: Dishwasher 1 is working.
[ INFO] [1588752344.515033939]: Dishwasher 1 finish working.  # Server節點啟動後的日誌資訊

7.2 執行action_client

開啟第三個命令列客戶端:

$ source ~/catkin_ws/devel/setup.bash     # 啟用catkin_ws工作空間(必須有,必不可少) 
$ rosrun action_example action_client        

你將會看到如下的輸出資訊:

[ INFO] [1588752334.233231877]: Waiting for action server to start.
[ INFO] [1588752334.513889608]: Action server started, sending goal.
[ INFO] [1588752334.514780017]: Goal just went active
[ INFO] [1588752334.515056866]:  percent_complete : 10.000000 
[ INFO] [1588752335.516336080]:  percent_complete : 20.000000 
[ INFO] [1588752336.516271562]:  percent_complete : 30.000000 
[ INFO] [1588752337.516315111]:  percent_complete : 40.000000 
[ INFO] [1588752338.515751638]:  percent_complete : 50.000000 
[ INFO] [1588752339.515473734]:  percent_complete : 60.000000 
[ INFO] [1588752340.516373053]:  percent_complete : 70.000000 
[ INFO] [1588752341.515448200]:  percent_complete : 80.000000 
[ INFO] [1588752342.515654876]:  percent_complete : 90.000000 
[ INFO] [1588752343.515571413]:  percent_complete : 100.000000 
[ INFO] [1588752344.516076162]: Yay! The dishes are now clean     

現在,你已經成功地運行了你的第一個action_server和action_client程式。

微信公眾號:喵哥解說
公眾號介紹:主要研究機器學習、計算機視覺、深度學習、ROS等相關內容,分享學習過程中的學習筆記和心得!期待您的關注,歡迎一起學習交流進步!同時還有1200G的Python視訊和書籍資料等你領取!!!