tf2
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 odomtopic that tells you how far the car is
- you have the odomframe that tells you the distance betweenmapandbase_link

There are two types of transformations:
- Static Transformations (ex: between laserandbase_link), 1-time thing, never changes because they are fixed to each other
- Dynamic Transformations (ex: between laserandmap). 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]
 
	self.tf_static_broadcaster.sendTransform(t)In the above example,
- header.frame_idis the source frame
- child_frame_idfield is the frame ID of the coordinate frame that is being transformed into (i.e.target frame)
Resources
- (Shared Penn) L03 Rigid Body Transformation
- (Penn) T03 Transformations in ROS 2
- https://docs.ros.org/en/rolling/Tutorials/Intermediate/Tf2/Tf2-Main.html
- https://docs.ros.org/en/rolling/Tutorials/Intermediate/Tf2/Writing-A-Tf2-Static-Broadcaster-Py.html
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_framesIf 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
self.tf_broadcaster.sendTransform(t)C++, code here
tf_broadcaster_->sendTransform(t);Lookup a transform (Listener)
In the Pure Pursuit code for F1TENTH, we do this.
Python, code here
try:
	t = self.tf_buffer.lookup_transform(to_frame_rel,from_frame_rel, rclpy.time.Time())
except TransformException as ex:
	self.get_logger().info(
		f'Could not transform {to_frame_rel} to {from_frame_rel}: {ex}')
	returnC++, 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());
  return;
}