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 を実際のワークスペースパスに置き換える
cd workspace/src
ros2 pkg create pkg_helloworld_py --build-type ament_python --dependencies rclpy --node-name helloworld6.2.2 コードの準備
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ビルドして実行:
colcon build --packages-select pkg_helloworld_py
source install/setup.bash
ros2 run pkg_helloworld_py helloworld6.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 新しい機能パッケージ
cd ~/workspaces/src
ros2 pkg create pkg_topic --build-type ament_python --dependencies rclpy --node-name publisher_demo7.2.2 発行者の実装
# 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 library7.2.3 設定ファイルの編集
7.2.4 機能パッケージのコンパイル
cd ~/workspace
colcon build --packages-select pkg_topic
source install/setup.bash7.2.5 リリースノードの実行
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 ファイルに以下のコードを貼り付けます:
#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 library7.2.7 設定ファイルの編集
7.2.8 機能パッケージのコンパイル
cd ~/workspace
colcon build --packages-select pkg_topic
source install/setup.bash7.2.9 ノードの実行
各ノードを実行するために 2 つのターミナルを開きます:
ros2 run pkg_topic publisher_demo
ros2 run pkg_topic subscriber_demo7.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 ディレクトリで
ros2 pkg create pkg_service --build-type ament_python --dependencies rclpy --node-name server_demo8.2.2 サービスエンドの作成
server_demo.py コードを次のように修正します:
# 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 はレスポンスの一部です。
次のコマンドで表示できます:
ros2 interface show example_interfaces/srv/AddTwoInts8.2.3 設定ファイルの編集
setup.py を開き、console_scripts のリストに追加
'server_demo = pkg_service.server_demo:main',8.2.4 機能パッケージのコンパイル
colcon build --packages-select pkg_service
source install/setup.bash
ros2 run pkg_service server_demo実行後、サービスが呼び出されないためフィードバックデータがなく、コマンドラインでサービスを呼び出すことができ、まず現在のサービスを尋ねてから別のターミナル入力:
ros2 service list/add_two_ints は次のコマンドで呼び出す必要があるサービスで、ターミナル入力:
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 1,b: 4}"8.2.5 クライアントの作成
server_demo.py の下に新しいファイルを作成
以下のコードを client_demo.py に書きます:
#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 設定ファイルの編集
'client_demo = pkg_service.client_demo:main'8.2.7 機能パッケージのコンパイル
cd ~/workspace
colcon build --packages-select pkg_service
source install/setup.bash
ros2 run pkg_service server_demo別のターミナルを開いて実行:
source install/setup.bash
ros2 run pkg_service client_demoクライアントが a = 10、b = 90 を提供し、サービスエンドで調整、結果は 100、両端終了。
8.3 次のステップ
1.09 アクション通信 - アクション通信を学ぶ(長いミッション)
- 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 新機能キット
ros2 pkg create --build-type ament_cmake pkg_interfacesint64 num
---
int64 sum
---
float64 progress<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>find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"action/Progress.action")cd ~/workspace
colcon build --packages-select pkg_interfacesros2 interface show pkg_interfaces/action/Progressros2 pkg create pkg_action --build-type ament_python --dependencies rclpy pkg_interfaces --node-name action_server_demo4. サービスエンド実現
4.1 サービスプロバイダーの作成
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 設定ファイルの編集
'action_server_demo = pkg_action.action_server_demo:main',4.3 機能パッケージのコンパイル
cd ~/workspace
colcon build --packages-select pkg_action
source install/setup.bash
ros2 run pkg_action action_server_demo別のターミナル入力:
ros2 action list
ros2 action send_goal /get_sum pkg_interfaces/action/Progress "{num: 10}"