30代エンジニアの備忘録のカバー画像

30代エンジニアの備忘録

組み込み開発・信号処理・プログラミングの技術メモと短編小説

🗺️ 【ROS2入門⑨】TF2座標変換フレームワーク - ロボットの空間関係を理解する

シリーズ この記事は「ROS2入門シリーズ」の一部です。 シリーズ目次を見る

RViz2 やトピック通信に慣れた状態から、TF2 で複数座標系の位置・姿勢を扱う手順を示します。例えば LiDAR の点群を base_link 基準にそろえるとき、毎回三角関数で計算せず lookup_transform に任せられます。正本は Introducing tf2 です。

2026年5月時点、Ubuntu 22.04 + ROS 2 Humble 想定です。


座標フレーム(TF フレーム)とは?

ロボットには base_link、camera_link、lidar_link など複数の座標フレーム(基準点)が存在します。TF2 tutorial の図解も併読してください。

ロボットの座標フレーム構造(world → base_link → camera/lidar)
ロボットの座標フレーム構造(world → base_link → camera/lidar)

各フレーム間の変換(位置 + 姿勢)を TF で受け渡しし、ツリーが途切れない範囲でフレーム間の変換を引き出せます。


TFフレームツリー構造
TFフレームツリー構造

TF2 関連パッケージのインストール手順

sudo apt install ros-humble-tf2-ros \
                 ros-humble-tf2-tools \
                 ros-humble-tf-transformations
pip3 install transforms3d

静的変換(Static Transform)を配信する方法

変化しない変換(カメラがロボットに固定されているなど)は静的変換で配信します。Static transform publisher も参照。

# コマンドで静的変換を配信
# ros2 run tf2_ros static_transform_publisher x y z yaw pitch roll parent child
ros2 run tf2_ros static_transform_publisher \
  0.1 0.0 0.2 0.0 0.0 0.0 \
  base_link camera_frame
# Pythonで静的変換を配信
from geometry_msgs.msg import TransformStamped
from tf2_ros import StaticTransformBroadcaster
import math

class CameraFramePublisher(Node):
    def __init__(self):
        super().__init__('camera_frame_publisher')
        broadcaster = StaticTransformBroadcaster(self)

        tf = TransformStamped()
        tf.header.stamp = self.get_clock().now().to_msg()
        tf.header.frame_id = 'base_link'
        tf.child_frame_id = 'camera_frame'
        # カメラは前方 10cm、上方 20cmの位置
        tf.transform.translation.x = 0.1
        tf.transform.translation.y = 0.0
        tf.transform.translation.z = 0.2
        # 回転なし(クォータニオン)
        tf.transform.rotation.w = 1.0

        broadcaster.sendTransform(tf)

動的変換(Transform)を配信する方法

ロボットが動く場合、動的変換で位置を更新し続けます。

from tf2_ros import TransformBroadcaster

class RobotPosePublisher(Node):
    def __init__(self):
        super().__init__('robot_pose_publisher')
        self.broadcaster = TransformBroadcaster(self)
        self.timer = self.create_timer(0.1, self.broadcast_pose)
        self.x = 0.0

    def broadcast_pose(self):
        tf = TransformStamped()
        tf.header.stamp = self.get_clock().now().to_msg()
        tf.header.frame_id = 'world'
        tf.child_frame_id = 'base_link'

        # ロボットが移動していると仮定
        self.x += 0.01
        tf.transform.translation.x = self.x
        tf.transform.translation.y = 0.0
        tf.transform.translation.z = 0.0
        tf.transform.rotation.w = 1.0

        self.broadcaster.sendTransform(tf)

lookup_transform で座標変換を取得する方法

import rclpy
from tf2_ros import Buffer, TransformListener
from tf2_ros import LookupException, ConnectivityException

class CoordTransformNode(Node):
    def __init__(self):
        super().__init__('coord_transform')
        self.tf_buffer = Buffer()
        self.tf_listener = TransformListener(self.tf_buffer, self)
        self.timer = self.create_timer(1.0, self.lookup_transform)

    def lookup_transform(self):
        try:
            # lookup_transform(ターゲット座標系, ソース座標系, 時刻)
            tf = self.tf_buffer.lookup_transform(
                'base_link',
                'camera_frame',
                rclpy.time.Time())
            x = tf.transform.translation.x
            y = tf.transform.translation.y
            self.get_logger().info(f'Camera pos in base_link: x={x:.2f}, y={y:.2f}')
        except Exception as e:
            # 代表的には Lookup / Connectivity / Extrapolation(時刻まわり)など
            self.get_logger().warn(str(e))
座標変換の概念図
座標変換の概念図

TF ツリーを可視化・確認する方法

# TFツリーをPDFで出力
ros2 run tf2_tools view_frames
# frames.pdf が生成される

# フレーム間の変換をリアルタイム表示
ros2 run tf2_ros tf2_echo base_link camera_frame
# At time 0.0
# Translation: [0.100, 0.000, 0.200]
# Rotation: [0.000, 0.000, 0.000, 1.000]

TF2 でつまずきやすい点

  • static_transform_publisher の 引数の並び(位置・オイラー角・クォータニオン等)は版で差が出るので、--help と公式メモをセットで見る
  • lookup_transform が落ちる典型は フレーム名の typo、親子がまだブロードキャストされていない、時刻が未来/古すぎて外挿できないなど
  • view_frames はスナップショット用途。TF を出しているノードが止まると PDF 上のツリーも欠ける

TF2 座標変換のまとめ

  • TF2 は フレームを束ねたツリーとして理解し、どの辺が自分のブロードキャストか押さえると追いやすい
  • 静的変換は StaticTransformBroadcaster で、固定の親子関係を流し続ける用途向き
  • 動的変換は TransformBroadcaster で、位置姿勢が更新されるたびに書き換える
  • Buffer と TransformListener を組み合わせて、lookup_transform で目的のペアを引くのが典型的な読み側パターン
  • view_frames や tf2_echo で、いまツリーがどう見えているかを確認する
  • 次回(シリーズ⑩)では Gazebo 側のシミュレーション入り口を扱う想定です