基本的な ROS 2 メッセージの操作
この例では、ロボティクス アプリケーションで一般に使用される ROS 2 メッセージの作成、検査、取り込みを MATLAB で行うためのさまざまな方法を調べます。
ROS "メッセージ" は、ROS 2 でデータを交換するための主要なコンテナーです。パブリッシャーとサブスクライバーは、指定された "トピック" のメッセージを使用してノード間でデータを伝達することでデータを交換します。メッセージ送受信の詳細については、ROS 2 のパブリッシャーとサブスクライバーとのデータ交換を参照してください。
それぞれのメッセージに、そのデータ構造を識別するための "メッセージ タイプ" があります。たとえば、レーザー スキャナーからのセンサー データは、一般に sensor_msgs/LaserScan
タイプのメッセージで送信されます。各メッセージ タイプは、メッセージに含まれているデータ要素を識別します。それぞれのメッセージ タイプの名前は、パッケージ名の後にスラッシュ (/) とタイプ名を組み合わせた名前になります。
MATLAB® では、ロボティクス アプリケーションで一般に使用されるさまざまな ROS 2 メッセージ タイプをサポートしています。この例では、ROS 2 メッセージの作成、検査、取り込みを MATLAB で行うためのいくつかの方法を調べます。
前提条件:ROS 2 入門、ROS 2 ネットワークへの接続
メッセージ タイプの特定
exampleHelperROS2CreateSampleNetwork
を使用して、ROS 2 ネットワークに 3 つのノードを取り込み、特定のトピックに関するサンプルのパブリッシャーとサブスクライバーを設定します。
exampleHelperROS2CreateSampleNetwork
ros2 topic list -t
を使用して、使用可能なトピックとそれらに関連付けられているメッセージ タイプを調べます。
ros2 topic list -t
Topic MessageType _____________________ _________________________________ {'/parameter_events'} {'rcl_interfaces/ParameterEvent'} {'/pose' } {'geometry_msgs/Twist' } {'/rosout' } {'rcl_interfaces/Log' } {'/scan' } {'sensor_msgs/LaserScan' }
トピックのメッセージ タイプの詳細を調べるには、ros2message
を使用して同じタイプの空のメッセージを作成します。ros2message
では、メッセージ タイプのタブ補完がサポートされています。メッセージ タイプの完全な名前をすばやく入力するには、目的の名前の最初の数文字を入力して "Tab" キーを押します。
scanData = ros2message("sensor_msgs/LaserScan")
scanData = struct with fields:
MessageType: 'sensor_msgs/LaserScan'
header: [1×1 struct]
angle_min: 0
angle_max: 0
angle_increment: 0
time_increment: 0
scan_time: 0
range_min: 0
range_max: 0
ranges: 0
intensities: 0
作成されたメッセージ scanData
には、レーザー スキャナーから一般に受信するデータに関連する多数のフィールドが含まれています。たとえば、range_min
プロパティには最小検出距離、range_max
プロパティには最大検出距離が格納されます。
ここで、作成されたメッセージは削除できます。
clear scanData
トピックとサービスについての使用可能なすべてのメッセージ タイプの完全なリストを確認するには、ros2 msg list
を使用します。
メッセージ構造体の確認とメッセージ データの取得
ROS 2 メッセージは構造体として表され、フィールドにメッセージ データが格納されます。MATLAB には、メッセージの内容を特定して確認できる便利な方法が用意されています。
ros2 msg show
を使用して、メッセージ タイプの定義を確認します。
ros2 msg show geometry_msgs/Twist
# This expresses velocity in free space broken into its linear and angular parts. Vector3 linear Vector3 angular
/pose
トピックをサブスクライブすると、送信されるメッセージを受信して調べることができます。
controlNode = ros2node("/base_station"); poseSub = ros2subscriber(controlNode,"/pose","geometry_msgs/Twist")
poseSub = ros2subscriber with properties: TopicName: '/pose' LatestMessage: [] MessageType: 'geometry_msgs/Twist' NewMessageFcn: [] History: 'keeplast' Depth: 10 Reliability: 'reliable' Durability: 'volatile'
receive
を使用して、サブスクライバーからデータを取得します。新しいメッセージを受信すると、そのメッセージが関数から返されて posedata
変数に格納されます。メッセージの受信のタイムアウトを 10 秒と指定します。
poseData = receive(poseSub,10)
poseData = struct with fields:
MessageType: 'geometry_msgs/Twist'
linear: [1×1 struct]
angular: [1×1 struct]
メッセージのタイプは geometry_msgs/Twist
です。メッセージには、このほかに linear
と angular
の 2 つのフィールドがあります。それらのメッセージ フィールドに直接アクセスして、それらの値を確認できます。
poseData.linear
ans = struct with fields:
MessageType: 'geometry_msgs/Vector3'
x: 0.0206
y: -0.0468
z: -0.0223
poseData.angular
ans = struct with fields:
MessageType: 'geometry_msgs/Vector3'
x: -0.0454
y: -0.0403
z: 0.0323
それらの各メッセージ フィールドの値自体が実際にはメッセージであることがわかります。geometry_msgs/Twist
は、geometry_msgs/Vector3
の 2 つのメッセージで構成される複合メッセージです。
それらの入れ子になったメッセージのデータ アクセスも、他のメッセージのデータにアクセスする場合とまったく同じです。次のコマンドを使用して linear
メッセージの x
成分にアクセスします。
xPose = poseData.linear.x
xPose = 0.0206
メッセージ データの設定
メッセージ プロパティの値を設定することもできます。タイプが geometry_msgs/Twist
のメッセージを作成します。
twist = ros2message("geometry_msgs/Twist")
twist = struct with fields:
MessageType: 'geometry_msgs/Twist'
linear: [1×1 struct]
angular: [1×1 struct]
このメッセージの数値プロパティは、既定では 0
に初期化されます。このメッセージの任意のプロパティを変更できます。linear.y
フィールドを 5 に設定します。
twist.linear.y = 5;
メッセージ データを表示して、変更が反映されたことを確認できます。
twist.linear
ans = struct with fields:
MessageType: 'geometry_msgs/Vector3'
x: 0
y: 5
z: 0
メッセージにデータが取り込まれたら、それをパブリッシャーおよびサブスクライバーで使用できます。
メッセージのコピー
ROS 2 メッセージは構造体です。それらをそのままコピーして新しいメッセージを作成できます。コピーと元のメッセージは、それぞれ独自のデータをもちます。
温度データを伝達する新しい空のメッセージを作成し、変更用にそのコピーを作成します。
tempMsgBlank = ros2message("sensor_msgs/Temperature");
tempMsgCopy = tempMsgBlank
tempMsgCopy = struct with fields:
MessageType: 'sensor_msgs/Temperature'
header: [1×1 struct]
temperature: 0
variance: 0
tempMsg の temperature
プロパティを変更し、tempMsgBlank
の内容は変わらないことを確認します。
tempMsgCopy.temperature = 100
tempMsgCopy = struct with fields:
MessageType: 'sensor_msgs/Temperature'
header: [1×1 struct]
temperature: 100
variance: 0
tempMsgBlank
tempMsgBlank = struct with fields:
MessageType: 'sensor_msgs/Temperature'
header: [1×1 struct]
temperature: 0
variance: 0
空白のメッセージ構造体を用意し、データがあるときにだけ特定のフィールドを設定してメッセージを送信すると便利な場合があります。
thermometerNode = ros2node("/thermometer"); tempPub = ros2publisher(thermometerNode,"/temperature","sensor_msgs/Temperature"); tempMsgs(10) = tempMsgBlank; % Pre-allocate message structure array for iMeasure = 1:10 % Copy blank message fields tempMsgs(iMeasure) = tempMsgBlank; % Record sample temperature tempMsgs(iMeasure).temperature = 20+randn*3; % Only calculate the variation once sufficient data observed if iMeasure >= 5 tempMsgs(iMeasure).variance = var([tempMsgs(1:iMeasure).temperature]); end % Pass the data to subscribers send(tempPub,tempMsgs(iMeasure)) end errorbar([tempMsgs.temperature],[tempMsgs.variance])
メッセージの保存と読み込み
後で使用するためにメッセージを保存して内容を格納できます。
サブスクライバーから新しいメッセージを取得します。
poseData = receive(poseSub,10)
poseData = struct with fields:
MessageType: 'geometry_msgs/Twist'
linear: [1×1 struct]
angular: [1×1 struct]
関数save
を使用して、姿勢データを MAT ファイルに保存します。
save("poseFile.mat","poseData")
ファイルをワークスペースに再度読み込む前に、poseData
変数をクリアします。
clear poseData
これで、関数load
を呼び出してメッセージ データを読み込むことができます。これにより、上記の poseData
が messageData
構造体に読み込まれます。poseData
は struct のデータ フィールドです。
messageData = load("poseFile.mat")
messageData = struct with fields:
poseData: [1×1 struct]
messageData.poseData
を調べてメッセージの内容を確認します。
messageData.poseData
ans = struct with fields:
MessageType: 'geometry_msgs/Twist'
linear: [1×1 struct]
angular: [1×1 struct]
ここで、MAT ファイルは削除できます。
delete("poseFile.mat")
ROS 2 ネットワークからの切断
サンプル ノード、パブリッシャー、およびサブスクライバーを ROS 2 ネットワークから削除します。
exampleHelperROS2ShutDownSampleNetwork