actionの概要
ROSの基本通信message,serviceがあるが、制御用通信としてFBを見ながら次のアクションを決めるシーン、FBの待ち時間中に他の処理ができるようにしたいシーンにはactionが2つよりも柔軟的に対応できる。
actionlibのpkgでaction機能を実現している。actionは何が違う?簡単に言うとリクエストに対して継続的なFB機能がある。例:ロボットをある方向に100m移動を指令出した場合、目的地までの距離を100,90,80,,,,0までFBを継続的にみることができる、移動終了後にaction終了報告もしてもらえる、途中で止めることもできるような通信。
実際の通信protcol
goal:目標リクエスト(ユーザー定義通信内容)
cancel:キャンセルする場合のキャンセルリクエスト
status:現在の状態の返し
result:結果の返し(1回だけ)(ユーザー定義通信内容)
feedback:指定周期で指定データーの返し(ユーザー定義通信内容)
actionの定義
.msg,.srvと同じようにpkgフォルダの下にactionフォルダを作り、定義したい通信内容を***.actionファイルを作成する。
"---"で3部分に区切りしたが、上から、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のイメージ。
actionのclientを作ってみる
#include <actionlib/client/simple_action_client.h>
#include "learning_communication/DoDishesAction.h"
typedef actionlib::SimpleActionClient<learning_communication::DoDishesAction> Client;
// actions終了時に1回だけCallされる関数
void doneCb(const actionlib::SimpleClientGoalState& state,
const learning_communication::DoDishesResultConstPtr& result)
{
ROS_INFO("Yay! The dishes are now clean");
ros::shutdown();
}
// actions終了時に1回だけ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)完了すれば、使えるようになる。
ターミナル1
roscore
ターミナル2
rosrun learning_communication DoDishes_client
ターミナル3
rosrun learning_communication DoDishes_server