ROS 2 実践コース|#03 talker と listener を壊して調べる

この回では、ROS 2 のいちばん基本になる「ノード」と「トピック」を、サンプルの talker(送る側)と listener(受ける側)で調べます。コードはまだ書きません。起動の順番や台数、送るデータの型などをわざと変えて、何が起きるかを見ていきます。

どの実験も、コマンドを打つ前に「どうなるか」の予想をノートに書いてください。予想と結果が食い違ったところが、この回でいちばん覚えておいてほしいところです。

何を予想すればよいかは、次の表のとおりです。各実験の結果の画面と説明は、「結果を見る」をクリックすると開きます。予想を書き、自分で実行してから開いてください。

実験予想すること
1. talker と listener を動かすros2 node list と ros2 topic list に、何がいくつ出るか
2. listener を先に起動するtalker がいないうちに listener を起動すると、エラーになるか、何も出ないか。あとから talker を起動したら受け取れるか
3. listener を 3 つ、talker を 2 つにする3 つの listener のどれに届くか。ros2 topic info /chatter の送る側・受ける側の数はいくつか
4. 同じトピックに違う型で送る文字列が流れている /chatter に整数を送ると、listener はどうなるか。エラーは出るか
5. ドメインを変えるlistener だけ ROS_DOMAIN_ID を 7 にすると、talker の文字列は届くか
6. 送る側を止めるtalker を止めると、listener と ros2 topic hz の画面はどうなるか

予想は外れてかまいません。点になるのは、予想と結果が違ったときに「なぜ違ったか」を考えて書くところです。分からないときも空欄にせず、「分からない」と書いて、手がかりにしたことを 1 つ書きます。予想・結果・違った理由は、レポートの第 1 回の表に書きます。

授業で受ける人は、この回の記録を実習レポート(Word)の「第 1 回」のシートに書きます。スクリーンショットは、シートの中の「画面:」の欄(実験や記録の表のすぐ下)に貼ります。ひとりで学ぶ人も、予想と結果を同じ形でノートに残しておくと、あとで見返せます。

この回で撮っておく画面

動画は撮りません。そのかわり、下の画面を撮って残します。授業で受ける人は、実習レポートの「第 1 回」の、それぞれの「画面:」の欄に貼ります。ひとりで学ぶ人も、同じ画面を残しておくと、あとで値を見直せます。最初の 1 枚は、実習を始める前に端末で TZ=Asia/Tokyo date と打ち、日付と時刻が出た画面です。授業の日に実習したことの記録になります。講義用の環境の時計は世界標準時(UTC)なので、TZ=Asia/Tokyo を付けて日本時間で出します。

  • TZ=Asia/Tokyo date の画面
  • talker と listener が動いている端末と、ros2 node list・ros2 topic list
  • listener 3 つ・talker 2 つのときの ros2 topic info /chatter
  • Int32 を送っている端末と listener の端末(どちらにもエラーが出ていないこと)
  • ドメイン 0 の端末と 7 の端末で出した ros2 node list・ros2 topic info /chatter
  • talker を止めたあとの ros2 topic hz /chatter

ノードとトピック

ノードは、1 つの仕事をするプログラムです。トピックは、ノードどうしがデータをやりとりするための、名前の付いた通り道です。送る側(Publisher)はトピックに向けてデータを出し、受ける側(Subscriber)は同じ名前のトピックからデータを受け取ります。

ノード(丸)とトピック(四角)。この回の実験 3 で、この形を実際に作ります
ノード(丸)とトピック(四角)。この回の実験 3 で、この形を実際に作ります

送る側のコードには「誰に送るか」が書かれていません。書かれているのはトピックの名前と型だけです。これが本当かどうかを、実験で確かめます。

準備:端末を開いて環境を確かめる

#00 の手順で start.bat を実行し、ブラウザでデスクトップを開きます。デスクトップの Terminator をダブルクリックして端末を開き、次の 2 つを実行します。

echo $ROS_DISTRO $RMW_IMPLEMENTATION
ros2 pkg executables sakigake_samples
humble rmw_cyclonedds_cpp と表示され、sakigake_samples の実行ファイルが 6 つ並べば準備はできています
humble rmw_cyclonedds_cpp と表示され、sakigake_samples の実行ファイルが 6 つ並べば準備はできています

毎回はじめに、日付と時刻を撮る

準備ができたら、実験を始める前に、同じ端末で次のコマンドを打ちます。

TZ=Asia/Tokyo date

date は、いまの日付と時刻を表示するコマンドです。Wed Sep 30 11:59:34 JST 2026 のように、曜日・月・日・時刻・時間帯・年の順に並びます。最後から 2 つめの JST は日本時間という意味です。

この画面を撮って、レポートのその回の最初の「画面:」の欄に貼ります。第 1 回だけでなく、毎回の最初にやります。

なぜ撮るのか

実習レポートは、Google Classroom で配られる Word のファイルです。毎回の授業で同じファイルに書き足していき、コースの最後に Google Classroom でまとめて 1 回だけ出します。出した日時だけでは、いつ実験したのかが分かりません。そこで、各回の最初に日付と時刻の画面を残しておき、授業の日に自分の PC で実験した記録にします。この画面のあとに撮る実験の画面も、同じ日に同じ環境で撮ったものとして読まれます。

なぜ TZ=Asia/Tokyo を付けるのか

講義用の環境の中の時計は、日本時間ではなく世界標準時(UTC)で動いています。UTC は日本時間より 9 時間遅れています。date だけを打つと、次のように出ます。

$ date
Wed Sep 30 02:59:16 UTC 2026
$ TZ=Asia/Tokyo date
Wed Sep 30 11:59:34 JST 2026

日本時間の朝 9 時より前に打つと、UTC では前の日の日付になってしまいます。コマンドの前に TZ=Asia/Tokyo を付けると、そのコマンドだけ時間帯を日本にして表示します。TZ は時間帯(タイムゾーン)を決める環境変数です(環境変数は #02 の「14. 環境変数と source」)。こう書いたときは、このコマンドだけに効き、端末の設定は変わりません。

画面の撮り方

講義用のデスクトップはブラウザの中に映っているので、Windows の機能で撮ります。

  1. Windows キー + Shift + S を押す
  2. 画面の上に出たメニューで「四角形」を選び、端末の窓をドラッグで囲む
  3. 囲んだ画像がクリップボードに入るので、Word のレポートの「画面:」の欄をクリックして Ctrl+V で貼る

撮った画面の文字が読めることを確かめてください。小さくて読めないときは、端末の窓を大きくしてから撮り直します。このあとの実験の画面も、同じやり方で撮ります。

この回では端末をいくつも使います。Terminator のアイコンをダブルクリックするたびに、新しい端末が 1 つ開きます。動いているノードを止めるときは、その端末をクリックしてから Ctrl+C を押します。端末の操作に自信がない人は、#02 を見ながら進めてください。

実験 1 talker と listener を動かす

端末を 2 つ開き、それぞれで次を実行します。

# 端末 1
ros2 run sakigake_samples talker
# 端末 2
ros2 run sakigake_samples listener
左が talker、右が listener。talker が送った文字列を、listener が受け取って表示しています
左が talker、右が listener。talker が送った文字列を、listener が受け取って表示しています

2 つを動かしたまま、3 つめの端末で、いま動いているノードとトピックの一覧を出します。打つ前に、予想を書いてください。

手がかりは、はじめの「ノードとトピック」の説明です。ノードは 1 つの仕事をするプログラムで、いま起動したプログラムは 2 つです。トピックは、ノードどうしがデータをやりとりする通り道です。talker から listener へ文字列が届いているので、どこかに通り道があるはずです。ノードとトピックがそれぞれいくつ出るか、どんな名前だと思うかを書きます。名前が分からなければ、数と、そう考えた理由だけでかまいません。

ros2 node list
ros2 topic list
結果を見る(予想を書き、自分で実行してから開く)
ノードは /listener と /talker の 2 つ、トピックには /chatter があります
ノードは /listener と /talker の 2 つ、トピックには /chatter があります

/parameter_events と /rosout は、どのノードも自動で使うトピックです。いまは気にしなくてかまいません。

つながり方を図で見るには rqt_graph を使います。

rqt_graph
rqt_graph。丸がノード、四角がトピックです。talker → /chatter → listener とつながっています
rqt_graph。丸がノード、四角がトピックです。talker → /chatter → listener とつながっています

rqt_graph が開いたら、左上のメニューを「Nodes/Topics (active)」に変えて、その左にある更新ボタンを押します。rqt_graph は更新ボタンを押した時点のつながりを描くので、ノードを増やしたり止めたりしたあとは、もう一度押してください。

実験 2 起動の順番を逆にする

いったん両方を Ctrl+C で止めます。今度は端末 2 で listener を先に起動し、少し待ってから端末 1 で talker を起動します。コマンドは実験 1 と同じです。先に予想を書いてから試してください。

結果を見る(予想を書き、自分で実行してから開く)
listener だけを起動したところ。何も表示されませんが、エラーも出ません
listener だけを起動したところ。何も表示されませんが、エラーも出ません
あとから talker を起動すると、listener がすぐに受け取り始めます
あとから talker を起動すると、listener がすぐに受け取り始めます

listener は talker がどこにいるかを知りません。ROS 2 のノードは、起動すると「/chatter を送りたい」「/chatter を受けたい」と周りに知らせ合い、名前と型が合う相手を自動で見つけます。これを発見(ディスカバリ)と呼びます。どちらを先に起動しても、あとから来た方が相手を見つけてつながります。

実験 3 受ける側と送る側を増やす

まず、実験 2 の talker と listener を Ctrl+C で止めます。止めずに残すと、このあとの数が合わなくなります。

端末を 5 つ開き、listener を 3 つ、talker を 2 つ動かします。下の 5 行を、1 つの端末に 1 行ずつ打ちます。同じ名前のノードがあると rqt_graph で見分けにくいので、2 つめからは __node:= でノードの名前を変えます。

ros2 run sakigake_samples listener
ros2 run sakigake_samples listener --ros-args -r __node:=listener2
ros2 run sakigake_samples listener --ros-args -r __node:=listener3
ros2 run sakigake_samples talker
ros2 run sakigake_samples talker --ros-args -r __node:=talker2

--ros-args から先は、ノードそのものへの指示ではなく、ROS 2 への指示です。-r は「置きかえる」という意味で、-r A:=B と書くと「A のかわりに B を使う」になります。ここでは、ノードの名前を表す __node を listener2 に置きかえています。:= は ROS 2 での「置きかえ」の記号で、Python の =(代入)とは別のものです。

別の端末で、/chatter につながっている数を確かめます。

ros2 topic info /chatter
結果を見る(予想を書き、自分で実行してから開く)
左に talker が 2 つ、右に listener が 3 つ。3 つの listener がどれも受け取っています
左に talker が 2 つ、右に listener が 3 つ。3 つの listener がどれも受け取っています
Publisher count: 2、Subscription count: 3。/chatter に送る側が 2 つ、受ける側が 3 つつながっています
Publisher count: 2、Subscription count: 3。/chatter に送る側が 2 つ、受ける側が 3 つつながっています
rqt_graph で見ると、1 つのトピックに 5 つのノードがつながっています
rqt_graph で見ると、1 つのトピックに 5 つのノードがつながっています

talker のコードは、受ける側が 0 でも 3 でも同じです。1 対 1 の電話というより、掲示板に貼り出すしくみに近いと考えてください。送る側も受ける側も、相手の数や名前を知らなくてかまいません。

実験 4 同じ名前のトピックに、違う型で送る

まず、実験 3 で開いた端末を片づけます。talker 1 つと listener 1 つだけを残して、あとは Ctrl+C で止めてください(Ctrl+C は #02 の「10. 動いているプログラムを止める」)。止めずに残しておくと、このあとの画面と数が合わなくなります。

talker と listener を 1 つずつ動かしたまま、別の端末から /chatter に整数(std_msgs/msg/Int32)を送ります。talker が送っているのは文字列(std_msgs/msg/String)です。

ros2 topic pub --rate 1 /chatter std_msgs/msg/Int32 "{data: 42}"

"{data: 42}" は、送る中身の書き方です。Int32 というメッセージは data という名前の入れ物を 1 つだけ持っていて、そこに 42 を入れています。どんな名前の入れ物があるかは、ros2 interface show std_msgs/msg/Int32 で見られます。外側の " は、中身に空白が入っていても 1 つのまとまりとして渡すために付けています。

もう 1 つ端末を開き、/chatter に流れている型を確かめます。

ros2 topic info /chatter
結果を見る(予想を書き、自分で実行してから開く)
下の端末から Int32 型の 42 を 1 秒ごとに送っています。listener は talker の文字列だけを表示し続け、どの端末にもエラーは出ません
下の端末から Int32 型の 42 を 1 秒ごとに送っています。listener は talker の文字列だけを表示し続け、どの端末にもエラーは出ません
Type に std_msgs/msg/Int32 と std_msgs/msg/String の 2 つが並んでいます
Type に std_msgs/msg/Int32 と std_msgs/msg/String の 2 つが並んでいます

トピックは、名前が同じでも型(メッセージの形)が違えばつながりません。しかも、食い違っていても誰もエラーで知らせてくれません。トピック名と型は、送る側と受ける側が交わす「契約」です。契約が合っているかどうかは、ros2 topic info で自分で確かめる必要があります。

実験 5 ドメインを変える

まず、実験 4 で動かしているもの(talker・listener・ros2 topic pub)を、すべて Ctrl+C で止めます。残っていると、ドメイン 0 の一覧にほかのノードが混ざります。

talker は普通に起動し、listener だけ ROS_DOMAIN_ID を 7 にして起動します。

# 端末 1
ros2 run sakigake_samples talker
# 端末 2
ROS_DOMAIN_ID=7 ros2 run sakigake_samples listener

端末をさらに 2 つ開き、片方はそのまま、もう片方はドメインを 7 にしてから、ノードの一覧とトピックの情報を出します。

# 端末 3(ドメイン 0 のまま)
ros2 node list
ros2 topic info /chatter
# 端末 4(ドメイン 7 にする)
export ROS_DOMAIN_ID=7
ros2 node list
ros2 topic info /chatter
結果を見る(予想を書き、自分で実行してから開く)
右の listener だけ ROS_DOMAIN_ID を 7 にしました。talker は送り続けていますが、listener には何も届きません
右の listener だけ ROS_DOMAIN_ID を 7 にしました。talker は送り続けていますが、listener には何も届きません
左(ドメイン 0)には /talker だけ、右(ドメイン 7)には /listener だけが見えます
左(ドメイン 0)には /talker だけ、右(ドメイン 7)には /listener だけが見えます

ROS_DOMAIN_ID は、発見の範囲を分ける番号です。同じ PC の中でも、番号が違うノードどうしはお互いが見えません。同じネットワークに何台ものロボットがあるときに、ほかの台と混ざらないように分けるために使います。この講義の環境の既定値は 0 です。

実験 5 が終わったら、端末 4 は閉じてください。export で変えた ROS_DOMAIN_ID はその端末に残るので、そのまま次の実験に使うと、ほかのノードが見えなくなります。

実験 6 送る側を止める

実験 5 の端末 2 の listener(ドメイン 7)を Ctrl+C で止め、同じ端末で ros2 run sakigake_samples listener を打って、ドメイン 0 で起動し直します。端末 1 の talker はそのまま使います。

talker と listener が動いている状態で、別の端末で /chatter に届いている周期を測ります。

ros2 topic hz /chatter
talker が動いている間は、下の端末に average rate(1 秒あたりに届いた回数)が表示されます
talker が動いている間は、下の端末に average rate(1 秒あたりに届いた回数)が表示されます

次に、talker の端末で Ctrl+C を押して止めます。ros2 topic hz も一度 Ctrl+C で止め、もう一度実行します。

結果を見る(予想を書き、自分で実行してから開く)
talker を止めてから測り直したところ。何も表示されず、listener にもエラーは出ません
talker を止めてから測り直したところ。何も表示されず、listener にもエラーは出ません

送る側がいなくなっても、受ける側はエラーを出さずに待ち続けます。「届かなくなった」ことは、受ける側が自分で見張らないと気づけません。

これがロボットの速度指令だったらどうなるでしょうか。指令を送るノードが止まっても、ロボットは最後に受け取った速度のまま動き続けるかもしれません。第 3 週(#08)では、この問題をロボット側の仕組みで防いでいることを確かめます。

本質の問い

授業の最後に、次の 2 つに答えてください。

  • #00 で AI に聞いて保存しておいた「ノードとトピックとは何か」の答えのうち、実験で確かめられたことと、確かめられなかったことを 1 つずつ挙げる。
  • 実験 6 の結果から、ロボットに速度指令を送るシステムで何が危ないかを書く。
考え方の例(答えを書いてから開く)

2 つめの問いの例:速度指令を送るノードが止まったり通信が切れたりしても、ロボット側のノードはエラーにならず、最後の指令で動き続けるおそれがあります。受ける側で「一定時間指令が届かなければ止まる」ようにしておく必要があります。どのノードにその役目を持たせるかが、第 3 週(#08)のテーマです。

まとめ

実験わかったこと
1システムは、ノードとトピックでできたグラフとして見られる
2起動の順番は関係ない。ノードは相手を知らず、名前と型で自動的に見つけ合う
3つながりは多対多。送る側は受ける側の数を知らない
4型が違うとつながらない。エラーは出ないので、自分で調べる
5ドメインが違うと、同じ PC の中でも見えない
6送る側が止まっても、受ける側は気づかない

次の回では、自分でトピック名・型・周期を決めた「設計シート」を書き、AI にノードを作らせて、この回で使ったコマンドで設計どおりかを確かめます。

トップへ戻る