Self Driving and ROS 2 - Learn by Doing! Odometry & Control: 通信 (セクション8-2/13)
講義79から83では、ROS2サービスの実装方法をPythonとC++で学び、ノード間のリクエスト・レスポンス通信プロトコルを紹介します。
サービスサーバーとクライアントの例として、2つの整数を加算するサービスを作成し、リクエストメッセージとレスポンスメッセージの定義と実装を行います。
実装したサービスサーバーとクライアントを使い、リクエストを送信してレスポンスを受け取る実践的な演習を行い、サービスの動作を確認します。
「自己運転とROS 2 - 実践的な学習!オドメトリー&制御」コースのセクション8、特にレクチャー79から83では、ROS2サービスの実装について学びます。このセクションの主要な概念とステップを分かりやすく説明します。
レクチャー79: ROS2サービスの紹介
TFライブラリとサービス: このレクチャーでは、ROS2サービスを紹介します。サービスは、トピックで使用されるパブリッシャー・サブスクライバーモデルとは異なり、ノード間の要求応答メカニズムを提供します。
実例 - 顔認識: カメラストリームで人の顔を検出し、ロボットをその顔に従わせるノードの実装例が説明されます。顔認識機能はサービスとして実装され、他のノードから呼び出すことができます。
クライアント-サーバー通信: クライアントはサービスサーバーに要求メッセージを送り、サーバーがそれを処理して応答メッセージを返します。この方法により、機能を一元化し、メンテナンスや更新が容易になります。
レクチャー80: Pythonでのシンプルなサービスサーバーの実装
新しいパッケージの作成: サービスインターフェースを定義するために、新しいパッケージ`bumperbot_msgs`を作成します。このパッケージには、サービスの要求と応答構造を定義する`AddTwoInts.srv`ファイルが含まれます。
サービスサーバーのコード: サービスサーバーはPythonで実装されます。以下はコードの簡略版です:
import rclpy
from rclpy.node import Node
from bumperbot_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.serviceCallback)
self.get_logger().info('Service add_two_ints Ready')
def serviceCallback(self, req, res):
self.get_logger().info('New Request Received: a=%d, b=%d' % (req.a, req.b))
res.sum = req.a + req.b
self.get_logger().info('Returning sum: %d' % res.sum)
return res
def main():
rclpy.init()
simple_service_server = SimpleServiceServer()
rclpy.spin(simple_service_server)
simple_service_server.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
このスクリプトは、`simple_service_server`という名前のサービスサーバーノードを作成し、`AddTwoInts`サービス要求をリッスンして処理し、結果を返します。
レクチャー81: C++でのシンプルなサービスサーバーの実装
C++サービスサーバー: 同じサービスサーバー機能がC++で実装されます。以下はC++コードのスニペットです:
#include <rclcpp/rclcpp.hpp>
#include "bumperbot_msgs/srv/add_two_ints.hpp"
class SimpleServiceServer : public rclcpp::Node {
public:
SimpleServiceServer() : Node("simple_service_server") {
service_ = create_service<bumperbot_msgs::srv::AddTwoInts>("add_two_ints", std::bind(&SimpleServiceServer::serviceCallback, this, std::placeholders::_1, std::placeholders::_2));
RCLCPP_INFO(this->get_logger(), "Service add_two_ints Ready");
}
private:
void serviceCallback(const std::shared_ptr<bumperbot_msgs::srv::AddTwoInts::Request> request,
const std::shared_ptr<bumperbot_msgs::srv::AddTwoInts::Response> response) {
RCLCPP_INFO(this->get_logger(), "New Request Received: a=%d, b=%d", request->a, request->b);
response->sum = request->a + request->b;
RCLCPP_INFO(this->get_logger(), "Returning sum: %d", response->sum);
}
rclcpp::Service<bumperbot_msgs::srv::AddTwoInts>::SharedPtr service_;
};
int main(int argc, char* argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<SimpleServiceServer>());
rclcpp::shutdown();
return 0;
}
このC++コードは、Pythonと同じ機能を提供し、サービスを処理する方法を示しています。
レクチャー82: Pythonでのシンプルなサービスクライアントの実装
サービスクライアントのコード: クライアントノードがサービスサーバーに要求を送り、応答を処理します。以下はPythonコードです:
import sys
import rclpy
from rclpy.node import Node
from bumperbot_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.responseCallback)
def responseCallback(self, future):
self.get_logger().info('Service Response: %d' % future.result().sum)
def main():
rclpy.init()
if len(sys.argv) != 3:
print('Usage: simple_service_client A B')
return -1
simple_service_client = SimpleServiceClient(int(sys.argv[1]), int(sys.argv[2]))
rclpy.spin(simple_service_client)
simple_service_client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
このスクリプトは、`add_two_ints`サービスに2つの整数を送信し、応答をログに記録するクライアントノードを作成します。
レクチャー83: C++でのシンプルなサービスクライアントの実装
C++サービスクライアント: クライアント機能もC++で実装されます。以下はスニペットです:
#include <rclcpp/rclcpp.hpp>
#include "bumperbot_msgs/srv/add_two_ints.hpp"
class SimpleServiceClient : public rclcpp::Node {
public:
SimpleServiceClient(int a, int b) : Node("simple_service_client") {
client_ = create_client<bumperbot_msgs::srv::AddTwoInts>("add_two_ints");
auto request = std::make_shared<bumperbot_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);
result.wait();
if (result.valid()) {
RCLCPP_INFO(this->get_logger(), "Service Response: %d", result.get()->sum);
} else {
RCLCPP_ERROR(this->get_logger(), "Service call failed");
}
}
private:
rclcpp::Client<bumperbot_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;
}
このC++コードは、サービスサーバーに要求を送り、応答を処理するクライアントノードを示しています。
結論
これらのレクチャーでは、PythonとC++の両方でROS2サービスとクライアントの実装について包括的に理解することができます。サービスを使用して機能を一元化することで、コードのメンテナンスや更新がより容易になります。簡単な算術演算から顔認識のような複雑な機能まで、ROS2サービスはROS2システムにおけるノード間通信のための強力なメカニズムを提供します。