ROS2 コア通信

06 ノード

06 ノード(Nodes)

6.1 ノードの概要

6.1.1 ノードとは

ノードは ROS 2 における最も基本的な計算単位です。1 つのノードは、ROS 2 API を使用して他のノードと通信するプロセスです。各ノードは通常、センサーデータの読み取り、データ処理、エクスキューターの制御など、特定の機能を担当します。

6.1.2 ノードの特性

特性注釈
軽量1 つの実行可能ファイルに複数のノードを含めることができる
分散ノードは異なるマシンで実行できる。
疎結合ノード間の通信はインターフェースを介し、直接依存しない
グループ化可能複数のノードが複雑な機能を実行
独立したライフサイクル各ノードを独立して開始および停止

6.1.3 ノードの命名規則

ノード名は一意でなければならない(同じ名前空間内で)

文字、数字、アンダースコアのみを含めることができる

大文字と小文字を区別する

説明的な名前の使用を推奨

名前の例:

ノード名評価
Camera_driver優秀。
path_planner優秀。
Node1推奨されない(説明的でない)
Oh, my-node.無効(ハイフン付き)

6.2 Hello World ノードのケース

6.2.1 python キットを作成

workspace を実際のワークスペースパスに置き換える

bash
cd workspace/src
ros2 pkg create pkg_helloworld_py --build-type ament_python --dependencies rclpy --node-name helloworld

6.2.2 コードの準備

bash
import rclpy  # ROS 2 Python client library
from rclpy.node import Node  # ROS 2 node class
import time

"""
Create a HelloWorld node. It prints the "hello world" log during initialization.
"""
class HelloWorldNode(Node):
  def __init__(self, name):
  super().__init__(name)  # initialize the ROS 2 node base class
  while rclpy.ok():  # Check whether ROS 2 is running normally
  self.get_logger().info("Hello World")  # ROS 2 log output
  time.sleep(0.5)  # Sleep to control the loop interval

def main(args=None):  # main entry function of the ROS 2 node
  rclpy.init(args=args)  # initialize the ROS 2 Python interface
  node = HelloWorldNode("helloworld")  # create and initialize the ROS 2 node object
  rclpy.spin(node)  # keep spinning until ROS 2 exits
  node.destroy_node()  # destroy the node object
  rclpy.shutdown()  # Shut down the ROS 2 Python client library

ビルドして実行:

bash
colcon build --packages-select pkg_helloworld_py
source install/setup.bash
ros2 run pkg_helloworld_py helloworld

6.3 次のステップ

学習後、次のことができます:

1.07 トピック通信 - トピック通信を深く学ぶ

2.08 サービス通信 - サービス通信を学ぶ

07 トピック通信

07 トピック通信(Topics)

7.1 トピック通信の概要

7.1.1 トピック通信とは

トピックは、パブリッシュ/サブスクライブ(Pub/Sub)モードを使用した ROS 2 ノード間の非同期通信のメカニズムです。発行者ノードはトピックにメッセージを発行し、サブスクライバーノードはトピックから情報を受け取り、いずれも相手を知る必要はありません。

7.1.2 トピック通信の特性

特性説明適用シーン
非同期通信送信者は購読者の応答を待たないセンサーデータストリーム
多対多複数の発行者と購読者データブロードキャスト
疎結合解耦モジュラー設計
ストリーム転送継続的データフロー継続的監視

7.1.3 トピック命名規則

ルール注釈
(グローバル名前空間)で開始するか相対名
小文字、数字、アンダースコアを使用
/ で名前空間レベルを区切る
予約名を避ける

名前の例:

トピック名評価
/cmd_vel標準、推奨
/camera/image_rawレベル明確。推奨。
/sensor/front_camera/image名前空間、推奨。
/MyTopic推奨されない(大文字)
add_vel相対名(ノード名前空間追加)

7.2 通信ケース

7.2.1 新しい機能パッケージ

bash
cd ~/workspaces/src
ros2 pkg create pkg_topic --build-type ament_python --dependencies rclpy --node-name publisher_demo

7.2.2 発行者の実装

bash
# Import the `rclpy` library
import rclpy
from rclpy.node import Node
# Import the `String` message type
from std_msgs.msg import String
# Create a `Topic_Pub` subclass of `Node` and pass the node name as an argument
class Topic_Pub(Node):
  def __init__(self,name):
  super().__init__(name)
  # Create a publisher with `create_publisher()`. The arguments are:
  # Topic data type, topic name, and message queue depth
  self.pub = self.create_publisher(String,"/topic_demo",1)
  # Create a timer that triggers the callback every 1 second. The arguments are:
  # Callback interval and callback function
  self.timer = self.create_timer(1,self.pub_msg)
  # Define the callback function
  def pub_msg(self):
  msg = String()  #Create a `String` message variable named `msg`
  msg.data = "Hi,I send a message." #Assign a value to `msg.data`
  self.pub.publish(msg) #Publish the topic data

# Main function
def main():
  rclpy.init() #Initialize
  pub_demo = Topic_Pub("publisher_node") #Create a `Topic_Pub` object and pass in the node name
  rclpy.spin(pub_demo)  #Call `rclpy.spin()` and pass the `Topic_Pub` object created above
  pub_demo.destroy_node()  #destroy the node object
  rclpy.shutdown()  #Shut down the ROS 2 Python client library

7.2.3 設定ファイルの編集

7.2.4 機能パッケージのコンパイル

bash
cd ~/workspace
colcon build --packages-select pkg_topic
source install/setup.bash

7.2.5 リリースノードの実行

bash
ros2 run pkg_topic publisher_demo
ros2 topic list
ros2 topic echo /topic_demo

"Hi, I send a message." と表示されます。

7.2.6 サブスクライバーの作成

publisher_demo.py ディレクトリに新しいファイル subscriber_demo.py を作成します。subscriber_demo.py ファイルに以下のコードを貼り付けます:

bash
#Import related libraries
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Topic_Sub(Node):
  def __init__(self,name):
  super().__init__(name)
  #Create a subscriber with `create_subscription()`. The arguments are the topic data type, topic name, callback function, and queue depth
  self.sub = self.create_subscription(String,"/topic_demo",self.sub_callback,1)
  #Callback function: print the received message
  def sub_callback(self,msg):
  self.get_logger().info(msg.data)



def main():
  rclpy.init() #initialize the ROS 2 Python interface
  sub_demo = Topic_Sub("subscriber_node") # Create and initialize the object
  rclpy.spin(sub_demo)
  sub_demo.destroy_node()  #destroy the node object
  rclpy.shutdown()  #Shut down the ROS 2 Python client library

7.2.7 設定ファイルの編集

7.2.8 機能パッケージのコンパイル

bash
cd ~/workspace
colcon build --packages-select pkg_topic
source install/setup.bash

7.2.9 ノードの実行

各ノードを実行するために 2 つのターミナルを開きます:

bash
ros2 run pkg_topic publisher_demo
ros2 run pkg_topic subscriber_demo

7.3 次のステップ

通信方法を学んだ後、次のことができます:

1.08 サービス通信 - サービス通信を学ぶ

2.09 アクション通信 - アクション通信を学ぶ

08 サービス通信

08 サービス通信(Services)

8.1 サービス通信の概要

8.1.1 サービス通信とは

サービス(Service)は、クライアント/サーバー(Client/Server)モデルを使用した ROS 2 におけるノード間の同期通信のメカニズムです。クライアントが要求を送信し、サービスが処理して応答を返します。

8.1.2 サービスとトピック

特徴サービストピック
通信モード同期(要求-応答)非同期(プル-サブスクリプション)
接続1 対 1。多対多。
適用シーン短い操作のクエリ継続的データフロー
ブロッククライアントブロッキング待機。ブロックなし。
戻り値応答を返さなければならない。応答なし

8.1.3 サービスタイプの定義

サービスタイプは .srv ファイルを使用して定義され、リクエストとレスポンスの両方を含みます:

Plain Text

Data type definition

File: example interfaces/srv/AddTwoInts.srv

Request part ( -- above)

int64 a int64 b

Response part ( -- below)

int64 sum

8.2 サービス通信の例

8.2.1 新しい機能パッケージ

~/workspace/src ディレクトリで

bash
ros2 pkg create pkg_service --build-type ament_python --dependencies rclpy --node-name server_demo

8.2.2 サービスエンドの作成

server_demo.py コードを次のように修正します:

bash
# Import related library files
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class Service_Server(Node):
  def __init__(self,name):
  super().__init__(name)
  # Create a service server with `create_service()`. The arguments are:
  # Service data type, service name, and service callback function
  self.srv = self.create_service(AddTwoInts, '/add_two_ints', self.Add2Ints_callback)
  # This service callback adds two integers and returns the result
  def Add2Ints_callback(self,request,response):
  response.sum = request.a + request.b
  print("response.sum = ",response.sum)
  return response
def main():
  rclpy.init()
  server_demo = Service_Server("publisher_node")
  rclpy.spin(server_demo)
  server_demo.destroy_node()  # destroy the node object
  rclpy.shutdown()  # Shut down the ROS 2 Python client library

サービスコールバック関数 Add2Ints_callback に焦点を当て、self に加えて運搬する必要があるパラメータは request と response で、request はサービス必要のパラメータ、response はサービスのフィードバックです。Request.a と request.b はリクエストの一部で、response.sum はレスポンスの一部です。

次のコマンドで表示できます:

bash
ros2 interface show example_interfaces/srv/AddTwoInts

8.2.3 設定ファイルの編集

setup.py を開き、console_scripts のリストに追加

bash
'server_demo = pkg_service.server_demo:main',

8.2.4 機能パッケージのコンパイル

bash
colcon build --packages-select pkg_service
source install/setup.bash
ros2 run pkg_service server_demo

実行後、サービスが呼び出されないためフィードバックデータがなく、コマンドラインでサービスを呼び出すことができ、まず現在のサービスを尋ねてから別のターミナル入力:

bash
ros2 service list

/add_two_ints は次のコマンドで呼び出す必要があるサービスで、ターミナル入力:

bash
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 1,b: 4}"

8.2.5 クライアントの作成

server_demo.py の下に新しいファイルを作成

以下のコードを client_demo.py に書きます:

bash
#Import related libraries
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class Service_Client(Node):
  def __init__(self,name):
  super().__init__(name)
  # Create a client with `create_client()`. The arguments are the service data type and service topic name
  self.client = self.create_client(AddTwoInts,'/add_two_ints')
  # Wait in a loop until the service server starts successfully
  while not self.client.wait_for_service(timeout_sec=1.0):
  print("service not available, waiting again...")
  # Create the service request data object
  self.request = AddTwoInts.Request()

  def send_request(self):
  self.request.a = 10
  self.request.b = 90
  #send the service request
  self.future = self.client.call_async(self.request)

def main():
  rclpy.init() # Initialize the node
  service_client = Service_Client("client_node") #Create the object
  service_client.send_request() #send the service request
  while rclpy.ok():
  rclpy.spin_once(service_client)
  #Check whether the data processing is complete
  if service_client.future.done():
  try:
  #Get the service response and print it
  response = service_client.future.result()
  print("service_client.request.a = ",service_client.request.a)
  print("service_client.request.b = ",service_client.request.b)
  print("Result = ",response.sum)
  except Exception as e:
  service_client.get_logger().info('Service call failed %r' % (e,))
  break
  service_client.destroy_node()
  rclpy.shutdown()

8.2.6 設定ファイルの編集

bash
'client_demo = pkg_service.client_demo:main'

8.2.7 機能パッケージのコンパイル

bash
cd ~/workspace
colcon build --packages-select pkg_service
source install/setup.bash
ros2 run pkg_service server_demo

別のターミナルを開いて実行:

bash
source install/setup.bash
ros2 run pkg_service client_demo

クライアントが a = 10、b = 90 を提供し、サービスエンドで調整、結果は 100、両端終了。

8.3 次のステップ

1.09 アクション通信 - アクション通信を学ぶ(長いミッション)

  1. 10 TF2 座標変換 - カスタムサービスタイプを作成

09 アクション通信

09 アクション通信(Actions)

9.1 アクション通信の概要

9.1.1 動作通信とは

アクションは、長い割り当てを処理するために ROS 2 で使用される通信メカニズムです。サービスに似て、アクションはクライアント-サーバーモードですが、以下をサポートします:

  • 任務遂行中のリアルタイムフィードバック

  • クライアントはアクティブな割り当てをキャンセルできます。

  • 数秒から数分かかる操作を処理するのに適しています。

9.1.2 アクションとサービス

特徴サービスアクション
申請の長さ短い操作(ms-s)長いミッション(秒-分)
フィードバックリアルタイムフィードバックなし継続的にフィードバックを送信
キャンセルサポートなししかしキャンセル。
ブロッククライアントブロック無効化
適用シーンクエリ、シンプル操作ナビゲーション、キャプチャ

9.2 アクション通信ケース

アクションクライアントは 1 つの整数データ N を送信し、アクションサービスは要求されたデータを受け取り、1 と N の間のすべての整数を加算し、最終結果をアクションクライアントに返し、加算するたびに現在の計算進行状況を計算し、アクションクライアントにフィードバックします。

9.2.1 新機能キット

bash
ros2 pkg create --build-type ament_cmake pkg_interfaces
bash
int64 num
---
int64 sum
---
float64 progress
bash
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<depend>action_msgs</depend>
<member_of_group>rosidl_interface_packages</member_of_group>
bash
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "action/Progress.action")
bash
cd ~/workspace
colcon build --packages-select pkg_interfaces
bash
ros2 interface show pkg_interfaces/action/Progress
bash
ros2 pkg create pkg_action --build-type ament_python --dependencies rclpy pkg_interfaces --node-name action_server_demo

4. サービスエンド実現

4.1 サービスプロバイダーの作成

bash
import time
import rclpy
from rclpy.action import ActionServer
from rclpy.node import Node

from pkg_interfaces.action import Progress

class Action_Server(Node):
  def __init__(self):
  super().__init__('progress_action_server')
  # Create the action server
  self._action_server = ActionServer(
  self,
  Progress,
  'get_sum',
  self.execute_callback)
  self.get_logger().info('The action server has started!')

  def execute_callback(self, goal_handle):
  self.get_logger().info('Starting task execution...')

  # Generate continuous feedback.
  feedback_msg = Progress.Feedback()

  total = 0
  for i in range(1, goal_handle.request.num + 1):
  total += i
  feedback_msg.progress = i / goal_handle.request.num
  self.get_logger().info('Continuous feedback: %.2f' % feedback_msg.progress)
  goal_handle.publish_feedback(feedback_msg)
  time.sleep(1)

  # Generate the final response.
  goal_handle.succeed()
  result = Progress.Result()
  result.sum = total
  self.get_logger().info('Task completed!')

  return result

def main(args=None):

  rclpy.init(args=args)
  Progress_action_server = Action_Server()
  rclpy.spin(Progress_action_server)
  Progress_action_server.destroy_node()
  rclpy.shutdown()

4.2 設定ファイルの編集

bash
'action_server_demo = pkg_action.action_server_demo:main',

4.3 機能パッケージのコンパイル

bash
cd ~/workspace
colcon build --packages-select pkg_action
source install/setup.bash
ros2 run pkg_action action_server_demo

別のターミナル入力:

bash
ros2 action list
ros2 action send_goal /get_sum pkg_interfaces/action/Progress "{num: 10}"

5. クライアント実現

5.1 クライアントの作成