ROS 2 実践コース|#09 座標と時間を調べる(QoS・TF・タイムスタンプ)

前の回の最後に、ロボットに正方形を歩かせると、戻ってきた位置がずれることを確かめました。ロボットが「どこに何があるか」を扱うには、データに 2 つの情報が欠かせません。どの座標系で測ったかと、いつ測ったかです。

この回は、その 2 つを /scan(LiDAR の距離データ)で調べます。その前に、データが届くかどうかを決める約束ごと、QoS を試します。

  1. QoS の組み合わせで、届くか届かないかが変わることを確かめる(実験 1、ロボットなし)
  2. 実物の /scan の QoS を読む(実験 2)
  3. LiDAR の座標系をたどり、RViz で点を表示する(実験 3・4・5)
  4. データに付いた時刻と、届くまでの遅れを測る(実験 6)

この回は、ロボットを机の上に置いたまま動かしません。

どの実験も、コマンドを打つ前に「どうなるか」の予想をノートに書いてください。何を予想すればよいかは、次の表のとおりです。各実験の結果の画面と説明は、「結果を見る」をクリックすると開きます。予想を書き、自分で実行してから開いてください。

実験予想すること
1. QoS の組み合わせ送る側と受ける側の Reliability の組み合わせ 4 通り(A〜D)のそれぞれで、届くか届かないか
2. 実物の /scan の QoSLiDAR の /scan は、reliable と best_effort のどちらで送られているか
3. LiDAR の座標系LiDAR は、ロボットの体の基準(base_link)から見て、前・左・上のどちらへ何 cm くらいのところにあるか
5. LiDAR の 0° の向きロボットの正面に置いた箱の点が、RViz で正面(赤い矢印の先)に出るか
6. 時刻と遅れros2 topic delay /scan の平均は、#07 で測った往復の時間(20 ms ほど)より大きいか小さいか

予想は外れてかまいません。点になるのは、予想と結果が違ったときに「なぜ違ったか」を考えて書くところです。分からないときも空欄にせず、「分からない」と書いて、手がかりにしたことを 1 つ書きます。実験 4 は RViz で表示するための準備なので、予想はいりません。

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

この回で撮っておく画面

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

  • TZ=Asia/Tokyo date と、つないだ直後の robot-status
  • QoS の組み合わせで届かなかったときの、受ける側の端末
  • ros2 topic info -v /scan(Reliability・History・Durability)
  • ros2 run tf2_ros tf2_echo base_link laser_frame
  • ros2 run tf2_tools view_frames で作った TF の木
  • 箱を正面に置いたときの RViz
  • ros2 topic delay /scan の平均・最小・最大

この回で使うもの

もの使うところ
講義用の環境(#00 で準備したもの)すべての実験
ロボット(机の上に置いたまま動かさない)実験 2〜6
段ボール箱など、LiDAR に映る箱 1 つ実験 5

実験 1 QoS:名前も型も合っているのに届かない

QoS(Quality of Service)は、トピックをどう届けるかの約束ごとです。いちばんよく使うのが Reliability(信頼性)で、次の 2 種類があります。

Reliability届け方
reliable届かなかったら送り直す。確実に届くが、送り直す分だけ遅れることがある
best_effort送りっぱなし。届かなかったものは捨てる

送る側と受ける側は、それぞれ自分の QoS を決めます。組み合わせによって届くかどうかを、ロボットを使わずに PC の中だけで試します。この実験は、次の順に進めます。

  1. 4 通りの組み合わせについて、届くか届かないかを予想して表に書く
  2. A と B を試す(送る側 1 つに、受ける側 2 つを同時につなぐ)
  3. C と D を試す(送る側を reliable に替える)
  4. 結果を表に書き、予想と違ったところの理由を考える

予想する

次の表の「予想」の欄を、自分のノートかレポートに書きます。手がかりは、上の表の「届け方」です。reliable の受ける側は「届かなかったら送り直してほしい」と求めます。送る側がそれに応えられるかどうかを、1 行ずつ考えてください。

番号送る側受ける側予想(届く? 届かない?)結果
Abest_effortreliable
Bbest_effortbest_effort
Creliablebest_effort
Dreliablereliable

A と B を試す

端末を 3 つ開き、上から順に実行します。--qos-reliability で、そのコマンドの Reliability を指定します。

# 端末 1(送る側:best_effort で 1 秒に 2 回送る)
ros2 topic pub --rate 2 --qos-reliability best_effort /qos_test std_msgs/msg/String "{data: 'hello'}"
# 端末 2(受ける側:reliable)
ros2 topic echo --qos-reliability reliable /qos_test
# 端末 3(受ける側:best_effort)
ros2 topic echo --qos-reliability best_effort /qos_test

端末 2 と端末 3 に何が出るかを見て、表の A(端末 2)と B(端末 3)の「結果」を書きます。何も出ないときは、黄色の警告が出ていないかも見てください。

結果を見る(予想を書き、自分で実行してから開く)
上:best_effort で送り続けている。中:reliable で受ける端末には何も届かず、黄色の警告が出た。下:best_effort で受ける端末には hello が届いている
上:best_effort で送り続けている。中:reliable で受ける端末には何も届かず、黄色の警告が出た。下:best_effort で受ける端末には hello が届いている

端末 2 の警告は「新しい送り手を見つけたが、QoS が合わない。この送り手からは何も受け取らない。合わなかったのは RELIABILITY」という意味です。トピック名 /qos_test も型 std_msgs/msg/String も合っているのに、1 つも届きません。

確かめたら、3 つの端末をすべて Ctrl+C で止めます。止めずに次へ進むと、best_effort の送り手が残ったままになり、C と D の結果が読めなくなります。

C と D を試す

今度は送る側を reliable にして、best_effort で受ける端末(C)と、reliable で受ける端末(D)を同時につなぎます。

# 端末 1(送る側:reliable)
ros2 topic pub --rate 2 --qos-reliability reliable /qos_test std_msgs/msg/String "{data: 'hello'}"
# 端末 2(受ける側:best_effort)
ros2 topic echo --qos-reliability best_effort /qos_test
# 端末 3(受ける側:reliable)
ros2 topic echo --qos-reliability reliable /qos_test

端末 2 が C、端末 3 が D の結果です。表の「結果」を書いたら、3 つの端末をすべて Ctrl+C で止めます。

結果を見る(予想を書き、自分で実行してから開く)
送る側が reliable、受ける側が best_effort なら届く
送る側が reliable、受ける側が best_effort なら届く
番号結果理由
A届かない受ける側が「送り直してほしい」と求めているのに、送る側は送り直さない
B届くどちらも送りっぱなしでよいと決めている
C届く送る側は送り直せるが、受ける側はそれを求めていない。求める以上のことをしてくれる相手とは組める
D届く受ける側が求める「送り直し」を、送る側が約束している

受ける側が求めることを、送る側が約束していれば届きます。反対に、受ける側が送る側より多くを求めると届きません。#03 で「名前と型が合わないと届かない」ことを確かめました。QoS も、同じようにトピックの約束ごとの一部です。

実験 2 実物の /scan の QoS を読む

ロボットにつなぎます(手順は #07 の準備と同じです)。つないだら、robot-status でブリッジが動作中かを確かめ、その画面を回の頭の 1 枚として撮ります。ここでつながっていないと、このあとの実験で何も出ません。

robot-connect
robot-status

robot-connect は、IP を付けないと前回つないだ IP につなぎます。OLED の 2 行目の IP が前回と変わっていたら(ホットスポットを立て直したときなど)、robot-connect 192.168.137.134 のように IP を付けて打ちます。

PC から /scan の QoS を読みます。-v は「くわしく表示する」という指定で、これを付けると送り手・受け手ごとの QoS が出ます。予想(reliable か best_effort か)を書いてから実行してください。手がかりは、#04 の設計シートの説明で「センサーの値のように新しいものが次々に来るものは best effort にする」と書いたことです。

ros2 topic info -v /scan

出てくる QoS の行は、次のように読みます。

行意味
Reliability実験 1 で試した届け方。RELIABLE か BEST_EFFORT
History (Depth)まだ渡していないデータを何件までためておくか。KEEP_LAST (10) なら、新しいほうから 10 件だけ残し、古いものは捨てる
Durabilityあとから来た受け手に、前に送ったデータを渡すかどうか。VOLATILE は渡さない(受け手がつないだあとのデータだけが届く)
結果を見る(予想を書き、自分で実行してから開く)
PC から見た /scan。送っているのは zenoh_bridge_pc で、Reliability は RELIABLE
PC から見た /scan。送っているのは zenoh_bridge_pc で、Reliability は RELIABLE

PC から見える送り手はブリッジです。ロボットの中で本当に /scan を送っているノードの QoS は、ロボットの中で調べると、次のようになっていました(2026 年 9 月に測定)。

$ ros2 topic info -v /scan
Type: sensor_msgs/msg/LaserScan

Publisher count: 1

Node name: MS200
Node namespace: /
Topic type: sensor_msgs/msg/LaserScan
Endpoint type: PUBLISHER
QoS profile:
  Reliability: RELIABLE
  History (Depth): KEEP_LAST (10)
  Durability: VOLATILE

LiDAR のノード MS200 も RELIABLE で送っています。

考える:センサーのデータを best_effort で受けるのはなぜか

ROS 2 では、センサーのデータは best_effort で送ったり受けたりすることがよくあります。次の回(#10)と第 5 週で使うサンプル scan_monitor も、/scan を best_effort で受けています。この LiDAR は RELIABLE で送っているのに、なぜ受ける側は best_effort にしておくのでしょうか。実験 1 の結果を使って考えてください。

考え方の例(自分の考えを書いてから開く)
  • LiDAR は 1 秒に 10 回、新しい距離を測り直す。0.1 秒前のデータを送り直してもらっても、そのころにはもう新しいデータが届いている。センサーのデータは、古いものを確実に受け取るより、新しいものを早く受け取るほうが役に立つ。
  • 実験 1 の C のとおり、best_effort で受けるノードは、送り手が reliable でも best_effort でも受け取れる。LiDAR を別の機種に替えて送り手の QoS が変わっても、そのまま動く。
  • 反対に reliable で受けるノードを書くと、best_effort で送る LiDAR からは何も届かない(実験 1 の A)。

実験 3 LiDAR の座標系をたどる

ロボットには、体の中心、LiDAR、カメラ、脚の関節など、たくさんの「基準の場所」があります。それぞれの場所に付けた座標系と、座標系どうしの位置関係をまとめて TF と呼びます。ROS 2 では、TF が /tf(脚のように動くもの)と /tf_static(LiDAR の取り付け位置のように動かないもの)の 2 つのトピックで流れています。

この実験は、次の順に進めます。

  1. view_frames で、座標系のつながりを PDF の図にする
  2. PDF を開き、base_link と laser_frame の親子関係を読む
  3. tf2_echo で、LiDAR が体の基準からどこにあるかを数字で見る

座標系のつながりを図にする

作業用のフォルダを作って移動し、view_frames を実行します。5 秒間 TF を集めて、つながりを PDF に書き出します。view_frames は、コマンドを打った場所(いまいるフォルダ)にファイルを作るので、先に専用のフォルダへ移動しておきます。mkdir -p の -p は「すでにあってもエラーにしない」、&& は「左が成功したら、続けて右を実行する」という意味です(#04 の「4. ビルドして動かす」と同じ)。最後の ls で、PDF ができたかを確かめます。

mkdir -p ~/tf_check && cd ~/tf_check
ros2 run tf2_tools view_frames
ls
5 秒待つと frames_(日付).pdf ができる。最後の行はとても長いので、この画面では 100 文字で切って表示している
5 秒待つと frames_(日付).pdf ができる。最後の行はとても長いので、この画面では 100 文字で切って表示している

PDF は、ファイル マネージャーでダブルクリックするか、次のコマンドで開きます。atril は、この環境に入っている PDF を見るためのソフトです。* は「ここは何文字でもよい」という意味で、日付の部分を打たずに済みます。

atril frames_*.pdf
PDF が開いたところ。上から下へ、座標系が枝分かれしてつながっている
PDF が開いたところ。上から下へ、座標系が枝分かれしてつながっている
PDF の上の部分。base_footprint の下に base_link があり、その下に laser_frame がつながっている(赤枠は説明のために付けた)
PDF の上の部分。base_footprint の下に base_link があり、その下に laser_frame がつながっている(赤枠は説明のために付けた)

矢印は「親 → 子」の向きです。base_link(ロボットの体の基準)の子として、laser_frame、camera_link、imu_link、4 本の脚の付け根(lf_hip_link など)がつながっています。

laser_link という似た名前もあります。どれが /scan の座標系かは、データ自身に書いてあります(実験 6 で確かめます)。

atril を端末から開いている間、その端末は次のコマンドを受け付けません。PDF を読み終えたら、atril の窓を閉じてから次へ進みます。

LiDAR は体の中心からどこにあるか

2 つの座標系の位置関係を数字で見ます。打つ前に、机の上のロボットを見て、LiDAR が体のまん中から前・左・上のどちらへ何 cm くらいのところにあるかを予想してください。

tf2_echo は、1 つめの座標系(base_link)から見た 2 つめの座標系(laser_frame)の位置と向きを、くり返し表示し続けます。表示の中の Translation が位置で、3 つの数は x(前)、y(左)、z(上)の順、単位は m です。Rotation が向きです。数字が何回か出たら、Ctrl+C で止めます。

ros2 run tf2_ros tf2_echo base_link laser_frame
結果を見る(予想を書き、自分で実行してから開く)
base_link から見た laser_frame の位置(Translation)と向き(Rotation)
base_link から見た laser_frame の位置(Translation)と向き(Rotation)

[0.000, 0.000, 0.180] なので、LiDAR は体の基準からまっすぐ上に 18 cm の位置にあります。Rotation が回っていない値なので、LiDAR の向きは体と同じとして登録されています。

実験 4 RViz で /scan の点を表示する

RViz を起動します。

rviz2

rviz2 を打った端末は、RViz を閉じるまでほかのコマンドを受け付けません。RViz は実験 5 が終わるまで開いたままにし、実験 6 のコマンドは新しい端末で打ちます。

左の Displays パネルで、次の順に設定します。

  1. Global Options の Fixed Frame を base_link にする(表示の基準にする座標系)。
  2. 左下の Add を押し、By display type の中から RobotModel を選んで OK。Description Topic を /robot_description にする。
  3. 同じように Add から TF を追加する。
  4. Add から LaserScan を追加し、Topic を /scan にする。Topic の左の三角を開き、Reliability Policy を実験 2 で読んだ QoS に合わせる。
LaserScan の Status が Ok になり、ロボットの周りの壁や物が色の付いた点で表示された。中央の小さな矢印の集まりが TF で、赤が x(前)、緑が y(左)、青が z(上)
LaserScan の Status が Ok になり、ロボットの周りの壁や物が色の付いた点で表示された。中央の小さな矢印の集まりが TF で、赤が x(前)、緑が y(左)、青が z(上)

点の色は、光がどれだけ強く返ってきたか(intensity)を表しています。距離ではありません。

LaserScan の Status がエラーになるとき
  • Fixed Frame が base_link になっているか。つづりを 1 文字でも間違えると、点を置く場所が決められない。
  • robot-status でブリッジが動作中か。
  • Reliability Policy を変えた直後は、表示されるまで数秒かかることがある。

RViz の設定は、File → Save Config As で保存しておくと、次の回に rviz2 -d ファイル名 で同じ表示から始められます。

実験 5 LiDAR の 0° はロボットの正面か

ロボットの正面、30 cm ほど先に箱を置き(LiDAR はロボットの背中の上にあるので、LiDAR より背の高い箱を使います。低い箱は点に出ません)、RViz のどこに箱の点が出るかを見ます。

TF の赤い矢印(base_link の x)の向きが、ロボットの正面です。箱の点が赤い矢印の先に出れば、LiDAR の 0° とロボットの正面は一致しています。

箱を置く前に、点が赤い矢印の先に出るかを予想してください。手がかりは、実験 3 の tf2_echo です。Rotation は「LiDAR の向きは体と同じ」として登録されていました。ただし、これは登録した値です。実物の LiDAR が本当にその向きで付いているかは、この実験で初めて分かります。

ずれていたときは、赤い矢印から左回りか右回りか、およそ何度かを読みます。時計の文字盤を上から見るつもりで、赤い矢印を 12 時とし、1 時間分(30°)を目安にするとおおよその角度を読めます。

記録すること結果
箱の点は、赤い矢印に対してどちらに出たか(先・左・右・後ろ)
ずれていたら、およそ何度か

この結果は第 5 週で使います。障害物をよける判断は「前にあるか」で決めるので、前がどちらかを間違えると、よけるつもりでぶつかりに行きます。

実験 6 データに付いている時刻と遅れ

/scan のメッセージには、中身の距離データとは別に、header(見出し)が付いています。RViz は開いたままで、新しい端末を開き、header だけを表示します。

ros2 topic echo /scan --field header

--field は、「メッセージ全部ではなく、この部分だけを表示して」という指定です。/scan は距離データが何百個も並ぶので、そのまま表示すると画面が流れてしまいます。中に入っているものの名前は、ros2 interface show sensor_msgs/msg/LaserScan で確かめられます。

何回分か表示されたら Ctrl+C で止め、同じ端末で PC の今の時刻を、header の stamp と同じ形で表示します。+%s.%N は、「1970 年 1 月 1 日からの秒数(%s)と、その秒の中の 10 億分の 1 秒の数(%N)を、点でつないで出して」という書式の指定です。

date +%s.%N

表示された header の中の frame_id が、実験 3 で tf2_echo に使った座標系と同じかを確かめてください。

結果を見る(予想を書き、自分で実行してから開く)
1 回分の header は stamp(時刻)と frame_id(座標系)。Ctrl+C のあとで date +%s.%N を実行し、PC の今の時刻を同じ形で表示した
1 回分の header は stamp(時刻)と frame_id(座標系)。Ctrl+C のあとで date +%s.%N を実行し、PC の今の時刻を同じ形で表示した
  • frame_id: laser_frame が、実験 3 で「データ自身に書いてある」と言った座標系です。
  • stamp が測った時刻です。sec は 1970 年 1 月 1 日からの秒数、nanosec はその秒の中の 10 億分の 1 秒の単位です。
  • 続けて届いた stamp の差を見ると、0.956 → 1.156 → 1.356 秒のように約 0.2 秒ずつ進んでいます。PC に届く /scan は 5 Hz でした(#07)。

最後の date との差は、Ctrl+C を押してコマンドを打つまでの時間も含むので、遅れの測定には使えません。遅れは次のコマンドで測ります。ros2 topic delay は、「PC が受け取った時刻 − ロボットが付けた stamp」を、届くたびに計算して平均します。何行か表示されたら Ctrl+C で止め、最後の行の平均(average delay)・最小(min)・最大(max)を書きます。

打つ前に、平均が #07 で測った往復の時間(latency_probe の平均、20 ms ほど)より大きいか小さいかを予想してください。手がかりは、stamp が「測った時刻」だということです。測ってから PC に届くまでに、どんな段階を通るかを考えます。

ros2 topic delay /scan
結果を見る(予想を書き、自分で実行してから開く)
PC で受け取った時刻から stamp を引いた値。平均 0.214 秒、最小 0.131 秒、最大 0.518 秒
PC で受け取った時刻から stamp を引いた値。平均 0.214 秒、最小 0.131 秒、最大 0.518 秒

ロボットの中で同じコマンドを実行した結果はこうでした(2026 年 9 月に測定)。

$ ros2 topic delay /scan
average delay: 0.100
	min: 0.099s max: 0.102s std dev: 0.00124s window: 9
average delay: 0.100
	min: 0.099s max: 0.102s std dev: 0.00122s window: 19
(中略)
average delay: 0.100
	min: 0.098s max: 0.102s std dev: 0.00129s window: 69

ロボットの中で受け取っても、約 0.1 秒の遅れがあります。

測ったもの値
往復にかかる時間(#07 の latency_probe)平均 18.1 ms
ros2 topic delay /scan(ロボットの中)平均 0.100 秒
ros2 topic delay /scan(PC)平均 0.214 秒

本質の問い

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

  • /scan の 1 つの点について「その点はロボットのどこにあるか」を答えるには、どんな情報が必要か。3 つ挙げ、それぞれ今日どのコマンドで見たかも書く。
  • PC で測った ros2 topic delay の平均(0.2 秒ほど)は、#07 の往復時間(20 ms ほど)よりずっと大きい。どこで差が生まれていそうか。考えられることを 2 つ以上書く。

1 つめの手がかりは、実験 3(座標系)と実験 6(header)です。2 つめは、ロボットの中で測った delay(約 0.1 秒)と PC で測った delay の差がどこから来たかを考えます。

考え方の例(答えを書いてから開く)
/scan の 1 点の場所を決める 3 つの情報
/scan の 1 点の場所を決める 3 つの情報

1 つめ:① その点の距離と角度(ranges の値と、何番目の点か)、② laser_frame と base_link の関係(TF。この LiDAR は 18 cm 上)、③ いつ測ったか(stamp)。ロボットが歩いていると、同じ距離と角度でも、測った時刻によって部屋の中での場所が変わります。

  • ロボットの中ですでに約 0.1 秒ある。LiDAR が 1 回転するのにかかる時間(scan_time、この LiDAR では約 0.1 秒)と近いので、1 回転を測り始めた時刻を stamp にして、測り終えてから送っていると考えると合う。
  • 残りの 0.1 秒ほどは、ブリッジと Wi-Fi の区間。ロボット側のブリッジは /scan を 10 Hz から 5 Hz に間引いていて、どの回を通すかでも遅れが変わりそう。
  • PC とロボットの時計がずれていると、その分だけ delay が増えたり減ったりする。往復時間は PC の時計だけで測るので、時計のずれの影響を受けない。

つまずきやすい点

症状見るところ
ros2 topic echo に何も出ず、黄色の警告だけが出る警告の最後の「Last incompatible policy」。QoS の組み合わせ(実験 1 の A)
view_frames の PDF に base_link しか出ないrobot-status でブリッジが動作中か。つないでから数秒待ってから実行する
tf2_echo が「frame does not exist」と出し続ける座標系の名前のつづり。最初の数回はまだ TF が集まっていないだけのこともある
RViz に点が出ないFixed Frame のつづりと、LaserScan の Topic。Status の行を開くとエラーの理由が書いてある
ros2 topic delay がマイナスや数秒になるPC とロボットの時計がずれている。授業では自分で直そうとせず、先生に伝える。ひとりで学ぶ人は、PC とロボットの両方で date を実行して時刻を比べる

まとめ

確かめたことわかったこと
QoS の組み合わせ(実験 1)名前と型が合っていても、受ける側が送る側より多くを求めると届かない
実物の /scan(実験 2)この LiDAR は RELIABLE で送っている。受ける側を best_effort にしておけば、送り手の QoS によらず受け取れる
座標系(実験 3・4・5)/scan は laser_frame で測っていて、laser_frame は base_link の 18 cm 上。0° が正面かは実物で確かめる
時刻(実験 6)データには測った時刻が付いている。受け取るまでの遅れは、ロボットの中の分とその先の分に分けて考える

次の回は、今日表示した /scan を rosbag で記録し、ロボットにつながずに再生しながらノードを作ります。

トップへ戻る