Robotics and ROS 2 - Learn by Doing! Manipulators: アクションクライアント (セクション7-2/10)
ROS 2でのアクションクライアント作成方法をPythonとC++で解説。
Pythonでは`SimpleActionClient`クラスを使用し、ゴール送信とフィードバック処理を行う。
C++では同様に`SimpleActionClient`クラスを使用し、タイマーを使ってサーバーへのゴール送信を管理。
ROS 2を使ったロボティクスの習得において、「Robotics and ROS 2 - Learn by Doing! Manipulators」のレッスン76と77は、アクションクライアントの作成に関する詳細を説明しています。これらのレッスンは、アクションサーバーと対話して複雑なタスクを実行するためにロボットをプログラムする方法を理解するための重要なものです。ここでは、これらのレッスンからの主要なポイントと実装について探ります。
レッスン76: Pythonでアクションクライアントを作成する
目的: Fibonacciアクションインターフェースを使用して、アクションサーバーと通信するPythonアクションクライアントを作成する方法を学ぶ。
主なステップ:
環境の設定:
必要なライブラリとモジュールをインポート。
`Node`を継承した`SimpleActionClient`クラスを定義。
アクションクライアントの初期化:
特定の名前でROS 2ノードを初期化。
`Fibonacci`アクションインターフェースを使用して`ActionClient`のインスタンスを作成。
ゴールの送信とフィードバックの処理:
`wait_for_server()`を使用してアクションサーバーが利用可能であることを確認。
ゴールを定義し、非同期で`send_goal_async()`を使用して送信。
ゴールの応答、フィードバック、および結果を処理するためのコールバック関数を実装:
`responseCallback`: ゴールが受け入れられたか拒否されたかを確認。
`feedbackCallback`: サーバーから受信したフィードバックメッセージをログに記録。
`resultCallback`: アクションの最終結果をログに記録。
アクションクライアントの実行:
ROS 2環境を初期化し、ノードをスピンして実行を維持。
実装スニペット:
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from arduinobot_msgs.action import Fibonacci
class SimpleActionClient(Node):
def __init__(self):
super().__init__("simple_action_client")
self.action_client = ActionClient(self, Fibonacci, "fibonacci")
self.goal = Fibonacci.Goal()
self.goal.order = 10
self.action_client.wait_for_server()
self.future = self.action_client.send_goal_async(self.goal, feedback_callback=self.feedbackCallback)
self.future.add_done_callback(self.responseCallback)
def responseCallback(self, future):
goal_handle = future.result()
if not goal_handle.accepted:
self.get_logger().info('Goal rejected :(')
return
self.get_logger().info('Goal accepted :)')
self.future = goal_handle.get_result_async()
self.future.add_done_callback(self.resultCallback)
def resultCallback(self, future):
result = future.result().result
self.get_logger().info('Result: {0}'.format(result.sequence))
rclpy.shutdown()
def feedbackCallback(self, feedback_msg):
feedback = feedback_msg.feedback
self.get_logger().info('Received feedback: {0}'.format(feedback.partial_sequence))
def main(args=None):
rclpy.init(args=args)
action_client = SimpleActionClient()
rclpy.spin(action_client)
if __name__ == '__main__':
main()
レッスン77: C++でアクションクライアントを作成する
目的: Python実装を反映し、Fibonacciアクションインターフェースを使用して同様のタスクを実行するC++アクションクライアントを作成する。
主なステップ:
環境の設定:
必要なヘッダをインクルードし、`Node`を継承した`SimpleActionClient`クラスを定義。
アクションクライアントの初期化:
ROS 2ノードを初期化し、Fibonacciインターフェースを使用して`rclcpp_action::Client`のインスタンスを作成。
ゴールの送信とフィードバックの処理:
`wait_for_action_server()`を使用してサーバーが利用可能であることを確認。
ゴールを定義し、`async_send_goal()`を使用して送信。
ゴールの応答、フィードバック、および結果を処理するためのコールバック関数を実装:
`goalCallback`: ゴールが受け入れられたか拒否されたかを確認。
`feedbackCallback`: サーバーから受信したフィードバックメッセージをログに記録。
`resultCallback`: アクションの最終結果をログに記録。
アクションクライアントの実行:
ROS 2ノードを初期化し、スピンして実行を維持。
実装スニペット:
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_action/rclcpp_action.hpp>
#include <rclcpp_components/register_node_macro.hpp>
#include "arduinobot_msgs/action/fibonacci.hpp"
using namespace std::chrono_literals;
namespace arduinobot_cpp_examples {
class SimpleActionClient : public rclcpp::Node {
public:
explicit SimpleActionClient(const rclcpp::NodeOptions& options)
: Node("simple_action_client", options) {
client_ = rclcpp_action::create_client<arduinobot_msgs::action::Fibonacci>(this, "fibonacci");
timer_ = create_wall_timer(1s, std::bind(&SimpleActionClient::timerCallback, this));
}
private:
rclcpp_action::Client<arduinobot_msgs::action::Fibonacci>::SharedPtr client_;
rclcpp::TimerBase::SharedPtr timer_;
void timerCallback() {
timer_->cancel();
if (!client_->wait_for_action_server()) {
RCLCPP_ERROR(get_logger(), "Action server not available after waiting");
rclcpp::shutdown();
}
auto goal_msg = arduinobot_msgs::action::Fibonacci::Goal();
goal_msg.order = 10;
RCLCPP_INFO(get_logger(), "Sending goal");
auto send_goal_options = rclcpp_action::Client<arduinobot_msgs::action::Fibonacci>::SendGoalOptions();
send_goal_options.goal_response_callback = std::bind(&SimpleActionClient::goalCallback, this, std::placeholders::_1);
send_goal_options.feedback_callback = std::bind(&SimpleActionClient::feedbackCallback, this, std::placeholders::_1, std::placeholders::_2);
send_goal_options.result_callback = std::bind(&SimpleActionClient::resultCallback, this, std::placeholders::_1);
client_->async_send_goal(goal_msg, send_goal_options);
}
void goalCallback(const rclcpp_action::ClientGoalHandle<arduinobot_msgs::action::Fibonacci>::SharedPtr& goal_handle) {
if (!goal_handle) {
RCLCPP_ERROR(get_logger(), "Goal was rejected by server");
} else {
RCLCPP_INFO(get_logger(), "Goal accepted by server, waiting for result");
}
}
void feedbackCallback(rclcpp_action::ClientGoalHandle<arduinobot_msgs::action::Fibonacci>::SharedPtr,
const std::shared_ptr<const arduinobot_msgs::action::Fibonacci::Feedback> feedback) {
std::stringstream ss;
ss << "Next number in sequence received: ";
for (auto number : feedback->partial_sequence) {
ss << number << " ";
}
RCLCPP_INFO(get_logger(), ss.str().c_str());
}
void resultCallback(const rclcpp_action::ClientGoalHandle<arduinobot_msgs::action::Fibonacci>::WrappedResult& result) {
switch (result.code) {
case rclcpp_action::ResultCode::SUCCEEDED:
break;
case rclcpp_action::ResultCode::ABORTED:
RCLCPP_ERROR(get_logger(), "Goal was aborted");
return;
case rclcpp_action::ResultCode::CANCELED:
RCLCPP_ERROR(get_logger(), "Goal was canceled");
return;
default:
RCLCPP_ERROR(get_logger(), "Unknown result code");
return;
}
std::stringstream ss;
ss << "Result received: ";
for (auto number : result.result->sequence) {
ss << number << " ";
}
RCLCPP_INFO(get_logger(), ss.str().c_str());
rclcpp::shutdown();
}
};
} // namespace
arduinobot_cpp_examples
RCLCPP_COMPONENTS_REGISTER_NODE(arduinobot_cpp_examples::SimpleActionClient)
結論
「Robotics and ROS 2 - Learn by Doing! Manipulators」のレッスン76と77は、PythonとC++の両方でアクションクライアントを作成するための包括的なガイドを提供します。これらのレッスンを通じて、アクションサーバーと通信してFibonacci数列を計算するなどの複雑なタスクを実行するアクションクライアントを設定、初期化、管理するための基本的なスキルを習得できます。これらのスキルは、ROS 2を使用して高度なロボットアプリケーションを開発する際に不可欠です。