Rigid-Body Transformations


This is the implementation from ROS of Rigid-Body Transformations. I think that while I don’t have 100% theoretical background about transformations, I should be familiar with the practical side of things.

This is an aha moment: The way that a car can locate itself is by publishing to the tf2 tree.

  • you have the odom topic that tells you how far the car is
  • you have the odom frame that tells you the distance between map and base_link

There are two types of transformations:

  1. Static Transformations (ex: between laser and base_link), 1-time thing, never changes because they are fixed to each other
  2. Dynamic Transformations (ex: between laser and map). As the car moves, the values change
def make_transforms(self, transformation):
	t = TransformStamped()
	t.header.stamp = self.get_clock().now().to_msg()
	t.header.frame_id = 'world'
	t.child_frame_id = transformation[1]
	t.transform.translation.x = float(transformation[2])
	t.transform.translation.y = float(transformation[3])
	t.transform.translation.z = float(transformation[4])
	quat = quaternion_from_euler(
		float(transformation[5]), float(transformation[6]), float(transformation[7]))
	t.transform.rotation.x = quat[0]
	t.transform.rotation.y = quat[1]
	t.transform.rotation.z = quat[2]
	t.transform.rotation.w = quat[3]

In the above example,

  • header.frame_id is the source frame
  • child_frame_id field is the frame ID of the coordinate frame that is being transformed into (i.e.target frame)


Publish a static transform from terminal

ros2 run tf2_ros static_transform_publisher 0 0 0 0 0 0 base_link laser

Visualizing the TF Tree

To see the current transforms being published (on Humble and up)

ros2 run tf2_tools view_frames

If you are on ROS Foxy, run

ros2 run tf2_tools view_frames.py

This sames it to frames.pdf, and you can open it with a PDF viewer.

Also see rqt_graph to generate the computation graph with all the ROS2 nodes.

Static Transformations

I’ve added these before in a launch file, by running something like:

Node(package='tf2_ros', executable='static_transform_publisher',
            arguments=['-0.012', '0', '0.144', '0', '0', '0', 'base_footprint', 'laser'])

Dynamic Transformations

Publish a Transform (Broadcaster)

I have never done this. The particle filter code automatically does this.

Python, code here


C++, code here


Lookup a transform (Listener)

In the Pure Pursuit code for F1TENTH, we do this.

Python, code here

	t = self.tf_buffer.lookup_transform(to_frame_rel,from_frame_rel, rclpy.time.Time())
except TransformException as ex:
		f'Could not transform {to_frame_rel} to {from_frame_rel}: {ex}')

C++, code here

try {
  t = tf_buffer_->lookupTransform(toFrameRel, fromFrameRel, tf2::TimePointZero);
} catch (const tf2::TransformException & ex) {
	RCLCPP_INFO(this->get_logger(), "Could not transform %s to %s: %s", toFrameRel.c_str(), fromFrameRel.c_str(), ex.what());