スポンサー広告
※ご注意:古い情報が含まれる場合があります

本サイトの記事には、過去の技術情報や設定手順が含まれています。現在の環境では動作しない可能性もありますので、ご利用の際はご注意ください。

ROS2 Jazzy:仮想オドメトリーにTFを追加してRViz2で座標系を可視化する(TF入門・URDFなし)

はじめに

本記事は、ROS2を「使う側」のエンジニア視点で、座標系・オドメトリ・座標変換(TF/TF2)を整理しながら、手を動かして理解を固めるシリーズの続きです。
ROS2はトピックやノードを動かすだけでも一定の成果は出ますが、実務では「座標が読めること(=フレームが説明できること)」が重要になります。
本記事では、見た目(URDF/ロボットモデル)よりも、座標系の関係と変換がどう動いているかに焦点を当てます。

ここまでの流れは次の通りです。

      • /cmd_vel を入力
      • 仮想的に自己位置(x, y, yaw)を計算
      • /odom を publish
      • ターミナルに表示される数値(ログ)だけで挙動を追う

この2回目の動作では数字だけの確認ですが、座標系の計算(ワールド座標と機体座標の関係)は十分に読み取れます。

そして今回は、その続きとして、TF(座標変換)を追加し、RViz2で座標系を可視化します。
環境はこれまで主に Humble で試していましたが、今回は ROS2 Jazzy を前提にします。
(WSLでの Jazzy 構築は別記事にしています。)
なお、Humbleで作ってテストしたノードは、Jazzyでも同様にビルド・実行できます(今回のノードは単純な計算ノードのため)。

WSL2 + Ubuntu 24.04 + ROS2 Jazzy 環境構築手順 ― Windows上で再現可能なROS2開発基盤を整える ―
1. 本記事の目的本記事では、Windows環境上のWSL2にUbuntu 24.04を導入し、ROS2 Jazzy(フル構成・GUI含む)をインストールする手順を整理します。目的は単なるインストールではありません。 再現可能なROS2開発...

今回のゴール

今回のゴールは「ロボットの箱モデルを表示すること」ではありません。
以下を最小構成で達成します。

  • teleop_twist_keyboard/cmd_vel を入力する
  • 仮想オドメトリーが /odom を publish する
  • TF(odombase_link)を publish する
  • RViz2で 座標系(TF)とオドメトリー(矢印)が連動して動くことを確認する

URDF(RobotModel)は扱いません。座標系の理解に集中します。
以下のように確認できます。


なぜTFが必要か

前回のノードでは /odom を publish していました。
しかし、RViz2で「ロボットの向き」や「フレーム間の関係」を理解するためには、TF(座標変換)が必要になります。

今回追加するのは、以下の関係です。

odom  →  base_link
  • odom:ワールド基準(オドメトリ基準)の座標系
  • base_link:ロボット本体(機体)の基準座標系

TFは「フレーム間の位置・姿勢(姿勢=向き)関係」を流す仕組みです。
/odom が「位置と姿勢」を持っていても、RVizが座標系として扱うためには TF が必要になります。


PythonノードへのTF追加

既存の virtual_odometry_node.py に TF broadcast を追加します。
(本記事では差分が分かる形で要点のみ記載します。)

ソース一式は以下で公開しています。
https://github.com/ISYNishida/ros2_virtual_odometry/tree/main

ノードビルド方法は以下の2回目を参照してください。
https://independence-sys.net/main/?p=7373

ここからvirtual_odometry_node.py への TF broadcast 追加方法です。

1) import を追加

from geometry_msgs.msg import TransformStamped
from tf2_ros import TransformBroadcaster

2) Broadcaster を初期化(__init__ に追加)

self.tf_broadcaster = TransformBroadcaster(self)

3) update_odometry() の最後で TF を送信

tf = TransformStamped()
tf.header.stamp = self.get_clock().now().to_msg()
tf.header.frame_id = 'odom'
tf.child_frame_id = 'base_link'

tf.transform.translation.x = self.x
tf.transform.translation.y = self.y
tf.transform.translation.z = 0.0

tf.transform.rotation = yaw_to_quaternion(self.theta)

self.tf_broadcaster.sendTransform(tf)

これで、仮想オドメトリーの計算結果(x, y, yaw)を、TFとしても配信できます。


補足:座標変換を自分で書く必要はあるのか?(tf2の実務的な位置づけ)

今回は学習目的として、オドメトリー計算(積分)を自前で書き、座標系の意味を確認しています。
一方で実務では、座標変換そのもの(フレーム間変換)は tf2 を利用して扱うのが一般的です。

重要なのは三角関数やクォータニオンを暗記することではなく、次の点です。

  • どのフレーム(frame_id)からどのフレーム(child_frame_id)へ変換しているか
  • そのTFツリーが破綻していないか(親が一意、循環しない)
  • RViz/ナビゲーションが期待するフレーム構成になっているか

「tf2をうまく使うこと」が現場エンジニアの実務です。
その前提として、今回のように TFの意味と見え方を理解しておくことが重要だと考えています。


実行手順

ターミナルを3つ使うと分かりやすいです。

ターミナル①:仮想オドメトリーノード起動

ros2 run virtual_odometry virtual_odometry_node

ターミナル②:teleop起動(/cmd_vel入力)

ros2 run teleop_twist_keyboard teleop_twist_keyboard

ターミナル③:RViz2起動

rviz2

RViz2での可視化

1) Fixed Frame を設定

RViz2を起動したら、左上の Global Options で以下を設定します。

  • Fixed Frame:odom

ここがズレると表示が破綻します。まずは odom 固定で進めます。

2) 表示を追加(Add)

次のDisplayを追加します。

  • TF(フレーム関係の可視化)
  • Axes(ロボットの向きの可視化)
    • Reference Frame:base_link
    • (任意)Length:0.3 など
  • Odometry(位置と向きの可視化)
    • Topic:/odom
    • Shape:Arrow
  • Grid(任意:ワールド平面の基準)


何が見えるか(「単に動く」ではなく、TFの表示として読む)

teleopでキー操作すると、Odometry(矢印)とAxes(base_linkの座標軸)が連動して動きます。
ここでは「動いた」で終わらず、TF表示としてどう読むかを整理します。

1) Axes(base_link)は「ロボット座標そのもの」

Axes を base_link に設定すると、
ロボットが今どちらを向いているかが視覚的に分かります。
回転(angular.z)を入れると、この軸がその場で回転します。

2) Odometry(矢印)は「位置 + 姿勢」

Odometry表示は、/odom に含まれる pose(位置・姿勢)を矢印として表現します。
前進(linear.x)で矢印が移動し、回転で向きが変わります。

3) TF表示は「フレームの関係」を示す

TFの表示で odombase_link がつながっていることを確認します。
この関係(ツリー)が崩れると、RVizやナビゲーション系は正しく動作しません。
今回の最小構成では、まず odom → base_link が成立していればOKです。

ここまで確認できれば、座標系・オドメトリ・変換の基礎としては十分に前進しています。


まとめ

今回は、前回作成した仮想オドメトリーノードにTFを追加し、RViz2で座標系を可視化しました。

  • /cmd_vel 入力に対し、仮想オドメトリが /odom を publish
  • 同時に TF(odom → base_link を publish
  • RViz2で TF / Axes / Odometry を追加し、座標系として挙動を読めるようにする

実務では、座標変換そのものは tf2 が担います。
一方で、フレームの設計(どのフレームを作り、どう繋ぐか)と、TFを読めることが重要になります。
本記事は、そのための「基礎の基礎」をRViz2で確認するステップです。
今回はここまでです。またいろいろと書きたいと思います。