トピック通信は複数の ROS ノード間での一方向のデータ送信を実現します. この非同期通信メカニズムを使用すると, パブリッシャはサブスクライバがメッセージを受信したかどうかを正確に知ることができません. この講義では, ROS におけるもう 1 つの一般的な通信方法 - —Service を学びます、質問と回答と同様の同期コミュニケーション効果を達成できます。
http リクエスト、c/s モードと同様
通信模型
前回のコースでは、あるノードを介してカメラを駆動し、画像トピックを公開し、別のノードを介して画像トピックをサブスクライブし、その中の赤いオブジェクトの認識を実現しました。この時点で、オブジェクトの位置を取得できます。画像認識の頻度に応じて定期的に実行されます。
この位置情報は、ロボットの上位アプリケーションに継続的に送信して利用することができ、例えば、目標の動きに追従したり、目標位置の近くを移動したりすることができます。現時点では、それほど高い頻度でオブジェクトの位置をサブスクライブする必要はなく、その代わりに、このデータが必要なときにクエリリクエストを送信し、できるだけ早くターゲットの最新の位置を取得することを好みます。 。
このコミュニケーションモデルは、一方的な話題の伝達とは異なり、まるで「答えてください」のようにリクエストを送り、レスポンスを返す形となり、このコミュニケーションの仕組みがROSのサービスとなります。
クライアント/サーバーモデル
サービス実装メカニズムの観点から見ると、この形式の質疑応答はクライアント/サーバー モデルと呼ばれ、CS モデルと呼ばれます。クライアントが特定のデータを必要とする場合、特定のサービスに対する要求情報を送信し、サーバー クライアントはリクエストを受信すると、それを処理し、応答情報をフィードバックします。
この通信メカニズムは、私たちがよく閲覧するさまざまな Web ページなど、生活の中で非常に一般的でもあります。このとき、コンピューターのブラウザーはクライアントです。ドメイン名またはさまざまな操作を通じて Web サイトのサーバーにリクエストを送信し、サーバーはリクエストを受信後、表示されるページデータ。
同期通信
このプロセスは一般に、速いほど良いことが求められます。サーバーが長時間応答せず、ブラウザーが空回りし続けるとします。サーバーがダウンしているか、ネットワークが良好でない可能性があります。そのため、トピック通信と比較して、サービス通信、顧客 クライアントは、受信した応答情報を通じてサーバーのステータスを判断できます。これを 同期通信 とも呼びます。
1対多の通信
サービスインターフェース
トピック通信と同様に、サービス通信の中核はやはりデータを送信する必要があり、データは 2 つの部分になり、1 つは Apple の位置を要求するコマンドなどのリクエスト データ、もう 1 つはフィードバック データです。 Apple の座標位置をフィードバックするデータです トピックメッセージと同様に ROS 標準で定義する必要がありますトピックは .msg ファイルを使用して定義され、サービスは .srv ファイルを使用して定義されます定義方法は後ほど紹介します
例:
これで誰もが ROS サービス通信の基本を理解できたはずです。その後、コードを書き始めます。ROS の公式ルーチンでもある、サービスを介して加算ソルバーの機能を実装する比較的単純なルーチンから始めましょう。
2 つの加数の合計を計算する必要がある場合、クライアント ノードを介して 2 つの加数をリクエスト データにカプセル化してサービス「add_two_ints」に送信し、このサービスを提供するサーバー ノードがリクエスト データを受け取ります。を開始し、集計結果をレスポンスデータにカプセル化してクライアントにフィードバックすることで、クライアントは所望の結果を得ることができます。
プロセスのステップ
- 機能パッケージの作成
- カスタム サービス インターフェイス。
前のセクションの ros での通信インターフェイスの定義と、カスタム msg、srv、およびアクション ファイルの作成方法を参照してください。 - コードを書く
- setup.py ファイルを変更する
- 環境変数をコンパイルして構成する
- テストの実行
ros2 pkg create --build-type ament_python learning_server
関数パッケージが正常に作成されました
クライアントコードservice_adder_client.pyを作成します。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderClient(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
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.request = AddTwoInts.Request() # 创建服务请求的数据对象
def send_request(self): # 创建一个发送服务请求的函数
self.request.a = int(sys.argv[1])
self.request.b = int(sys.argv[2])
self.future = self.client.call_async(self.request) # 异步方式发送服务请求
def main(args=None):
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderClient("service_adder_client") # 创建ROS2节点对象并进行初始化
node.send_request() # 发送服务请求
while rclpy.ok(): # ROS2系统正常运行
rclpy.spin_once(node) # 循环执行一次节点
if node.future.done(): # 数据是否处理完成
try:
response = node.future.result() # 接收服务器端的反馈数据
except Exception as e:
node.get_logger().info(
'Service call failed %r' % (e,))
else:
node.get_logger().info( # 将收到的反馈信息打印输出
'Result of add_two_ints: for %d + %d = %d' %
(node.request.a, node.request.b, response.sum))
break
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
サーバーコードservice_adder_server.pyを作成します
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderServer(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.adder_callback) # 创建服务器对象(接口类型、服务名、服务器回调函数)
def adder_callback(self, request, response): # 创建回调函数,执行收到请求后对数据的处理
response.sum = request.a + request.b # 完成加法求和计算,将结果放到反馈的数据中
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) # 输出日志信息,提示已经完成加法求和计算
return response # 反馈应答信息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderServer("service_adder_server") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
setup.py ファイルを変更する
'service_adder_client = learning_server.service_adder_client:main',
'service_adder_server = learning_server.service_adder_server:main',
コンパイル
colcon build --packages-select learning_server
環境変数を設定する
source install/local_setup.bash
テストの実行
サーバーを起動します
ros2 run learning_server service_adder_server
起動後にコマンド ボックスが応答しない場合は、起動が成功したことを意味します。別の起動クライアントを開き、2 つのパラメータを渡します。
ros2 run learning_server service_adder_client 1 2
実行結果は次のとおりです。
クライアントはパラメータをサーバーに送信し、サーバーはパラメータを出力して結果をクライアントに返します。