ROS action 勉強メモ 

actionの概要
=
ROSの基本通信message,serviceがあるが、制御用通信としてFBを見ながら次のアクションを決めるシーン、FBの待ち時間中に他の処理ができるようにしたいシーンにはactionが2つよりも柔軟的に対応できる。

actionlibのpkgでaction機能を実現している。actionは何が違う？簡単に言うとリクエストに対して継続的なFB機能がある。例：ロボットをある方向に100m移動を指令出した場合、目的地までの距離を100,90,80,,,,0までFBを継続的にみることができる、移動終了後にaction終了報告もしてもらえる、途中で止めることもできるような通信。

messsage、serviseとの違いイメージ
![ROS通信.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/5d1c45c3-f1c7-5c22-0065-5b97945501a6.png)

実際の通信protcol
=
![20200907115536323.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/c072c8fd-d9be-a17f-e3a4-04b4e37e010e.png)

goal:目標リクエスト（ユーザー定義通信内容）
cancel:キャンセルする場合のキャンセルリクエスト
status:現在の状態の返し
result:結果の返し(１回だけ)（ユーザー定義通信内容）
feedback:指定周期で指定データーの返し（ユーザー定義通信内容）

actionの定義
=
.msg,.srvと同じようにpkgフォルダの下にactionフォルダを作り、定義したい通信内容を＊＊＊.actionファイルを作成する。

![Screenshot from 2023-07-22 21-25-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/f1e8f7a3-3a87-a8fc-1941-54ff61c06b34.png)

"---"で３部分に区切りしたが、上から、goal,result,feedbackになっている。

定義したファイルをビルトできるようにCmakeLists.txtファイルにビルト設定を追加する

~~~~~~~~~~~~~~~~~~~~~~~~~~
find_package(catkin REQUIRED COMPONENTS
  genmsg
  actionlib_msgs
  actionlib
  actionlib_msgs
)

add_action_files(
  FILES
  DoDishes.action
)

generate_messages(
  DEPENDENCIES
  std_msgs
  actionlib_msgs # action用追加
)

catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs message_runtime actionlib_msgs)
~~~~~~~~~~~~~~~~~~~~~~~~~~
package.xml にビルト関連を追加する
~~~~~~~~~~~~~~~~~~~~~~~~~~
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<run_depend>actionlib</run_depend>
<run_depend>actionlib_msgs</run_depend>
~~~~~~~~~~~~~~~~~~~~~~~~~~

これでビルト(catkin_make)してみるとdevelの下に関連するmsgファイルができたことがわかる。見ている通り、actionは複数のmsg機能で構成されたapiのイメージ。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/ab396206-8852-b43c-1787-4a60c45a9789.png)

actionのclientを作ってみる
=
~~~~~~~~~~~~~~~~
#include <actionlib/client/simple_action_client.h>
#include "learning_communication/DoDishesAction.h"

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

// actions終了時に１回だけCallされる関数
void doneCb(const actionlib::SimpleClientGoalState& state,
        const learning_communication::DoDishesResultConstPtr& result)
{
    ROS_INFO("Yay! The dishes are now clean");
    ros::shutdown();
}

// actions終了時に１回だけCallされる関数
void activeCb()
{
    ROS_INFO("Goal just went active");
}

// feedback来る度にCallされる関数
void feedbackCb(const learning_communication::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を定義する
    Client client("do_dishes", true);

    // sever待ち
    ROS_INFO("Waiting for action server to start.");
    client.waitForServer();
    ROS_INFO("Action server started, sending goal.");

    // actionのgoalをセットする
    learning_communication::DoDishesGoal goal;
    goal.dishwasher_id = 1;

    // actionのgoalを送り、callbackを設定する
    client.sendGoal(goal,  &doneCb, &activeCb, &feedbackCb);

    ros::spin();

    return 0;
}
~~~~~~~~~~~~~~~~


actionのseverを作ってみる
=
~~~~~~~~~~~~~~~~
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "learning_communication/DoDishesAction.h"

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

// actionのgoalを受信したらCallする関数
void execute(const learning_communication::DoDishesGoalConstPtr& goal, Server* as)
{
    ros::Rate r(1);
    learning_communication::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 server(n, "do_dishes", boost::bind(&execute, _1, &server), false);
    
    // serverを開始する
    server.start();

    ros::spin();

    return 0;
}
~~~~~~~~~~~~~~~~

actionを動かしてみる
=
cmake_listsファイルに下記cppビルト指定をする
~~~~~~~~~~~~~~~~
add_executable(DoDishes_client src/DoDishes_client.cpp)
target_link_libraries(DoDishes_client ${catkin_LIBRARIES})
add_dependencies(DoDishes_client ${PROJECT_NAME}_gencpp)

add_executable(DoDishes_server src/DoDishes_server.cpp)
target_link_libraries(DoDishes_server ${catkin_LIBRARIES})
add_dependencies(DoDishes_server ${PROJECT_NAME}_gencpp)
~~~~~~~~~~~~~~~~
これでビルト(catkin_make)完了すれば、使えるようになる。

ターミナル１
~~~~~~~~~~~~~~~~
roscore
~~~~~~~~~~~~~~~~

ターミナル2
~~~~~~~~~~~~~~~~
rosrun learning_communication DoDishes_client
~~~~~~~~~~~~~~~~

ターミナル3
~~~~~~~~~~~~~~~~
rosrun learning_communication DoDishes_server
~~~~~~~~~~~~~~~~

結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/af2e9a7f-4510-19f6-6d61-67b72a80ee58.png)

https://qiita.com/ryuka0610/items/f18897d5b0995746228f

https://qiita.com/ryuka0610/items/51af41555988db15514a