#!/usr/bin/env python3 import rclpy from rclpy.node import Node from tf2_ros import StaticTransformBroadcaster from geometry_msgs.msg import TransformStamped import numpy as np import open3d as o3d from scipy.spatial.transform import Rotation as R class StaticTransformPublisher(Node): def __init__(self): super().__init__('static_transform_from_pointclouds') # Static TF broadcaster self.br = StaticTransformBroadcaster(self) # Pointcloud files self.velodyne_file = "/root/velodyne.pcd" self.livox_file = "/root/livox.pcd" # Frames self.livox_frame = "frame_default" self.velodyne_frame = "velodyne" # Compute transform once self.T = self.compute_transform() self.get_logger().info(f"Computed initial transform:\n{self.T}") # Prepare translation and rotation T_copy = np.array(self.T, copy=True) trans = T_copy[:3, 3] rot_quat = R.from_matrix(T_copy[:3, :3]).as_quat() # [x, y, z, w] # Create static TransformStamped t = TransformStamped() t.header.stamp.sec = 0 t.header.stamp.nanosec = 0 t.header.frame_id = self.velodyne_frame t.child_frame_id = self.livox_frame t.transform.translation.x = trans[0] t.transform.translation.y = trans[1] t.transform.translation.z = trans[2] t.transform.rotation.x = rot_quat[0] t.transform.rotation.y = rot_quat[1] t.transform.rotation.z = rot_quat[2] t.transform.rotation.w = rot_quat[3] # Publish once self.br.sendTransform(t) self.get_logger().info("Published static transform.") def compute_transform(self): # Load point clouds pcd_vel = o3d.io.read_point_cloud(self.velodyne_file) pcd_liv = o3d.io.read_point_cloud(self.livox_file) # Downsample voxel_size = 0.05 pcd_vel_ds = pcd_vel.voxel_down_sample(voxel_size) pcd_liv_ds = pcd_liv.voxel_down_sample(voxel_size) # Estimate normals pcd_vel_ds.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamKNN(knn=20)) pcd_liv_ds.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamKNN(knn=20)) # ICP registration threshold = 0.5 reg_result = o3d.pipelines.registration.registration_icp( pcd_liv_ds, pcd_vel_ds, threshold, np.eye(4), o3d.pipelines.registration.TransformationEstimationPointToPoint() ) return reg_result.transformation def main(args=None): rclpy.init(args=args) node = StaticTransformPublisher() try: rclpy.spin(node) except KeyboardInterrupt: pass finally: node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()