Skip to content

Commit c4dedbd

Browse files
committed
ros pose-tf transform functions
1 parent c89beaf commit c4dedbd

3 files changed

Lines changed: 147 additions & 1 deletion

File tree

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ set(CMAKE_CXX_STANDARD 20)
55
add_compile_options(-Wall -Wextra -Wpedantic)
66

77
find_package(ament_cmake REQUIRED)
8-
find_package(rclcpp)
8+
find_package(rclcpp REQUIRED)
99
find_package(ament_cmake_python REQUIRED)
1010
find_package(Eigen3 REQUIRED)
11+
find_package(tf2 REQUIRED)
12+
find_package(tf2_ros REQUIRED)
13+
find_package(tf2_geometry_msgs REQUIRED)
1114
find_package(geometry_msgs REQUIRED)
1215

1316
include_directories(include)
@@ -22,6 +25,9 @@ target_include_directories(${PROJECT_NAME} PUBLIC
2225
ament_target_dependencies(${PROJECT_NAME} PUBLIC
2326
rclcpp
2427
Eigen3
28+
tf2
29+
tf2_ros
30+
tf2_geometry_msgs
2531
geometry_msgs
2632
)
2733

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#ifndef VORTEX_UTILS__ROS_TRANSFORMS_HPP_
2+
#define VORTEX_UTILS__ROS_TRANSFORMS_HPP_
3+
4+
#include <geometry_msgs/msg/pose_array.hpp>
5+
#include <geometry_msgs/msg/pose_stamped.hpp>
6+
#include <geometry_msgs/msg/pose_with_covariance_stamped.hpp>
7+
8+
#include <tf2/exceptions.h>
9+
#include <tf2/time.h>
10+
#include <tf2_ros/buffer.h>
11+
12+
namespace vortex::utils::ros_transforms {
13+
14+
/**
15+
* @brief Transform a stamped pose into a target frame using TF.
16+
*
17+
* Applies a TF transform to a geometry_msgs::msg::PoseStamped message,
18+
* producing a pose expressed in the specified target frame.
19+
*
20+
* The input pose timestamp is used for TF lookup. If no valid transform
21+
* is available within the specified timeout, the transformation fails.
22+
*
23+
* @param tf_buffer TF buffer used for transform lookup
24+
* @param in Input PoseStamped message
25+
* @param target_frame Target frame ID
26+
* @param out Output PoseStamped message in the target frame
27+
* @param logger ROS logger used for warning output
28+
* @param timeout Maximum duration to wait for a valid transform
29+
* @return true if the transform succeeded, false otherwise
30+
*/
31+
inline bool transform_pose(tf2_ros::Buffer& tf_buffer,
32+
const geometry_msgs::msg::PoseStamped& in,
33+
const std::string& target_frame,
34+
geometry_msgs::msg::PoseStamped& out,
35+
rclcpp::Logger logger,
36+
tf2::Duration timeout = tf2::durationFromSec(0.00)) {
37+
try {
38+
tf_buffer.transform(in, out, target_frame, timeout);
39+
return true;
40+
} catch (const tf2::TransformException& ex) {
41+
RCLCPP_WARN(logger, "TF transform failed from '%s' to '%s': %s",
42+
in.header.frame_id.c_str(), target_frame.c_str(),
43+
ex.what());
44+
return false;
45+
}
46+
}
47+
48+
/**
49+
* @brief Transform a stamped pose with covariance into a target frame using TF.
50+
*
51+
* Applies a TF transform to a geometry_msgs::msg::PoseWithCovarianceStamped
52+
* message. Both the pose and its 6×6 covariance matrix are transformed
53+
* according to the rigid-body frame transform.
54+
*
55+
* The covariance is rotated into the target frame but is not otherwise
56+
* modified (no uncertainty inflation or filtering is applied).
57+
*
58+
* @param tf_buffer TF buffer used for transform lookup
59+
* @param in Input PoseWithCovarianceStamped message
60+
* @param target_frame Target frame ID
61+
* @param out Output PoseWithCovarianceStamped message in the target frame
62+
* @param logger ROS logger used for warning output
63+
* @param timeout Maximum duration to wait for a valid transform
64+
* @return true if the transform succeeded, false otherwise
65+
*/
66+
inline bool transform_pose(
67+
tf2_ros::Buffer& tf_buffer,
68+
const geometry_msgs::msg::PoseWithCovarianceStamped& in,
69+
const std::string& target_frame,
70+
geometry_msgs::msg::PoseWithCovarianceStamped& out,
71+
rclcpp::Logger logger,
72+
tf2::Duration timeout = tf2::durationFromSec(0.00)) {
73+
try {
74+
tf_buffer.transform(in, out, target_frame, timeout);
75+
return true;
76+
} catch (const tf2::TransformException& ex) {
77+
RCLCPP_WARN(logger, "TF transform failed from '%s' to '%s': %s",
78+
in.header.frame_id.c_str(), target_frame.c_str(),
79+
ex.what());
80+
return false;
81+
}
82+
}
83+
84+
/**
85+
* @brief Transform all poses in a PoseArray into a target frame using TF.
86+
*
87+
* Each pose in the input PoseArray is individually transformed using the
88+
* array header (frame ID and timestamp). The output PoseArray contains
89+
* only successfully transformed poses and is expressed in the target frame.
90+
*
91+
* TF does not natively support PoseArray, so each pose is internally
92+
* wrapped as a PoseStamped for transformation.
93+
*
94+
* @param tf_buffer TF buffer used for transform lookup
95+
* @param in Input PoseArray message
96+
* @param target_frame Target frame ID
97+
* @param out Output PoseArray message in the target frame
98+
* @param logger ROS logger used for warning output
99+
* @param timeout Maximum duration to wait for each pose transform
100+
* @return true if all poses were successfully transformed, false otherwise
101+
*/
102+
inline bool transform_pose(tf2_ros::Buffer& tf_buffer,
103+
const geometry_msgs::msg::PoseArray& in,
104+
const std::string& target_frame,
105+
geometry_msgs::msg::PoseArray& out,
106+
rclcpp::Logger logger,
107+
tf2::Duration timeout = tf2::durationFromSec(0.0)) {
108+
out.poses.clear();
109+
out.poses.reserve(in.poses.size());
110+
111+
out.header.stamp = in.header.stamp;
112+
out.header.frame_id = target_frame;
113+
114+
geometry_msgs::msg::PoseStamped in_ps, out_ps;
115+
in_ps.header = in.header;
116+
117+
for (const auto& pose : in.poses) {
118+
in_ps.pose = pose;
119+
120+
try {
121+
tf_buffer.transform(in_ps, out_ps, target_frame, timeout);
122+
out.poses.push_back(out_ps.pose);
123+
} catch (const tf2::TransformException& ex) {
124+
RCLCPP_WARN(logger,
125+
"TF transform failed for PoseArray element "
126+
"from '%s' to '%s': %s",
127+
in.header.frame_id.c_str(), target_frame.c_str(),
128+
ex.what());
129+
return false;
130+
}
131+
}
132+
return true;
133+
}
134+
135+
} // namespace vortex::utils::ros_transforms
136+
137+
#endif // VORTEX_UTILS__ROS_TRANSFORMS_HPP_

package.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
<depend>eigen</depend>
1515
<depend>python3-numpy</depend>
1616
<depend>python3-scipy</depend>
17+
<depend>tf2</depend>
18+
<depend>tf2_ros</depend>
19+
<depend>tf2_geometry_msgs</depend>
1720
<depend>geometry_msgs</depend>
1821

1922

0 commit comments

Comments
 (0)