見出し画像

Robotics and ROS 2 - Learn by Doing! Manipulators: サービスサーバー (セクション6-2/10)

  • ROS 2サービスの基本: サービスサーバーとクライアントの概念と、リクエスト・レスポンス通信プロトコルを学習。

  • PythonおよびC++での実装: 2つの整数の合計を計算するサービスサーバーとクライアントをPythonとC++で開発。

  • 静的および動的変換: `tf`および`tf_static`トピックを使用してロボットの静的および動的変換を処理する方法を理解。

Udemyの「Robotics and ROS 2 - Learn by Doing! Manipulators」コースのセクション6のレッスン57から62では、ROS 2サービスの仕組みとPythonおよびC++での実装について詳しく学びます。これらのレッスンは、ROS 2の基礎理解をさらに深め、ロボットアプリケーション内でのサービスベースの通信に実践的なアプローチを提供します。以下は、これらのレッスンの詳細な解説です。

レッスン57: ROS 2サービスの理解

まず、ROS 2サービスの概念を探求します。これは、ノード間の別の通信プロトコルを提供し、トピックで使用されるパブリッシャー・サブスクライバーモデルとは異なり、リクエスト・レスポンスプロトコルを使用します。例えば、顔認識サービスを複数のノードで使用することで、冗長なコードを排除できます。

重要な概念:

  • サービスサーバー: サービスを提供するノード。

  • サービスクライアント: サービスをリクエストするノード。

  • リクエストとレスポンス: サービスが使用する通信プロトコル。

実践例:

顔認識サービスを実装し、画像内の顔を検出し、その位置に基づいてロボットを移動させるノードを追加します。この設定により、サービスの効率性が強調され、コードの再利用と中央集権的な最適化が可能になります。

レッスン58: Pythonでのサービスサーバーの実装

このレッスンでは、2つの整数の合計を計算するシンプルなサービスサーバーをPythonで開発します。ステップには以下が含まれます:

  • サービスの作成: `rclpy`を使用してサービスを定義および初期化。

  • サービスコールバック: リクエストを処理し、レスポンスを送信するための関数。

コードのハイライト:

import rclpy
from rclpy.node import Node
from arduinobot_msgs.srv import AddTwoInts

class SimpleServiceServer(Node):
    def __init__(self):
        super().__init__('simple_service_server')
        self.service = self.create_service(AddTwoInts, 'add_two_ints', self.service_callback)
        self.get_logger().info('Service add_two_ints Ready')

    def service_callback(self, request, response):
        self.get_logger().info(f'New Request Received: a={request.a}, b={request.b}')
        response.sum = request.a + request.b
        self.get_logger().info(f'Returning sum: {response.sum}')
        return response

def main():
    rclpy.init()
    node = SimpleServiceServer()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

レッスン59: C++でのサービスサーバーの実装

次に、同じサービスサーバーの機能をC++で実装します。これには以下が含まれます:

  • サービスの初期化: `rclcpp`を使用してサービスを作成。

  • サービスコールバック: リクエストを処理し、レスポンスを送信。

コードのハイライト:

#include <rclcpp/rclcpp.hpp>
#include "arduinobot_msgs/srv/add_two_ints.hpp"

class SimpleServiceServer : public rclcpp::Node {
public:
    SimpleServiceServer() : Node("simple_service_server") {
        service = create_service<arduinobot_msgs::srv::AddTwoInts>("add_two_ints",
                    std::bind(&SimpleServiceServer::handle_service, this, _1, _2));
        RCLCPP_INFO(this->get_logger(), "Service add_two_ints Ready");
    }
private:
    void handle_service(const std::shared_ptr<arduinobot_msgs::srv::AddTwoInts::Request> request,
                        std::shared_ptr<arduinobot_msgs::srv::AddTwoInts::Response> response) {
        RCLCPP_INFO(this->get_logger(), "New Request Received: a=%ld, b=%ld", request->a, request->b);
        response->sum = request->a + request->b;
        RCLCPP_INFO(this->get_logger(), "Returning sum: %ld", response->sum);
    }
    rclcpp::Service<arduinobot_msgs::srv::AddTwoInts>::SharedPtr service;
};

int main(int argc, char * argv[]) {
    rclcpp::init(argc, argv);
    auto node = std::make_shared<SimpleServiceServer>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

レッスン60: 静的および動的変換

このレッスンでは、ROS 2が`tf`および`tf_static`トピックを使用して静的および動的変換をどのように処理するかを探ります。静的変換は一度だけ公開され、動的変換は一定の間隔で公開されます。

レッスン61: Pythonでのサービスクライアントの実装

次に、サービスサーバーと対話するPythonクライアントを実装します。このクライアントはリクエストを送信し、非同期にレスポンスを処理します。

コードのハイライト:

import sys
import rclpy
from rclpy.node import Node
from arduinobot_msgs.srv import AddTwoInts

class SimpleServiceClient(Node):
    def __init__(self, a, b):
        super().__init__('simple_service_client')
        self.client = self.create_client(AddTwoInts, 'add_two_ints')
        while not self.client.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('service not available, waiting again...')
        self.req = AddTwoInts.Request()
        self.req.a = a
        self.req.b = b
        self.future = self.client.call_async(self.req)
        self.future.add_done_callback(self.response_callback)

    def response_callback(self, future):
        self.get_logger().info(f'Service Response: {future.result().sum}')

def main():
    rclpy.init()
    if len(sys.argv) != 3:
        print("Usage: simple_service_client A B")
        return
    a = int(sys.argv[1])
    b = int(sys.argv[2])
    node = SimpleServiceClient(a, b)
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

レッスン62: C++でのサービスクライアントの実装

最後に、C++でサービスクライアントを実装します。Pythonクライアントと同様の手順に従い、両方の言語実装を理解します。

コードのハイライト:

#include <rclcpp/rclcpp.hpp>
#include "arduinobot_msgs/srv/add_two_ints.hpp"
#include <chrono>

class SimpleServiceClient : public rclcpp::Node {
public:
    SimpleServiceClient(int a, int b) : Node("simple_service_client") {
        client = create_client<arduinobot_msgs::srv::AddTwoInts>("add_two_ints");
        auto request = std::make_shared<arduinobot_msgs::srv::AddTwoInts::Request>();
        request->a = a;
        request->b = b;
        while (!client->wait_for_service(1s)) {
            if (!rclcpp::ok()) {
                RCLCPP_ERROR(this->get_logger(), "Interrupted while waiting for the service. Exiting.");
                return;
            }
            RCLCPP_INFO(this->get_logger(), "service not available, waiting again...");
        }
        auto result = client->async_send_request(request, std::bind(&SimpleServiceClient::handle_response, this, _1));
    }
private:
    void handle_response(rclcpp::Client<arduinobot_msgs::srv::AddTwoInts>::SharedFuture future) {
        if (future.valid()) {
            RCLCPP_INFO(this->get_logger(), "Service Response: %ld", future.get()->sum);
        } else {
            RCLCPP_ERROR(this->get_logger(), "Service Failure");
        }
    }
    rclcpp::Client<arduinobot_msgs::srv::AddTwoInts>::SharedPtr client;
};

int main(int argc, char * argv[]) {
    rclcpp::init(argc, argv);
    if (argc != 3) {
        RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Usage: simple_service_client A B");
        return 1;
    }
    auto node = std::make_shared<SimpleServiceClient>(

atoi(argv[1]), atoi(argv[2]));
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

結論

セクション6のこれらのレッスンは、ROS 2サービスの包括的な理解を提供し、PythonおよびC++での実装を実演しました。これらの概念をマスターすることで、サービスベースの通信を活用した、より効率的で保守しやすいロボットアプリケーションを構築できます。今後もROS 2とロボティクスのエキサイティングな世界を探求していきましょう!

「超本当にドラゴン」へ

この記事が気に入ったらサポートをしてみませんか?