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程序。


免責聲明!

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



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