|
34 | 34 |
|
35 | 35 | // To get M_PI, especially on Windows. |
36 | 36 | #include <math.h> |
| 37 | +#include <memory> |
37 | 38 |
|
38 | 39 | #include <tf2_eigen/tf2_eigen.h> |
39 | 40 | #include <gtest/gtest.h> |
| 41 | +#include <rclcpp/clock.hpp> |
| 42 | +#include <tf2_ros/transform_listener.h> |
| 43 | +#include <tf2_ros/buffer.h> |
40 | 44 | #include <tf2/convert.h> |
| 45 | +#include <geometry_msgs/msg/vector3_stamped.hpp> |
| 46 | +#include <geometry_msgs/msg/quaternion_stamped.hpp> |
41 | 47 |
|
42 | 48 | TEST(TfEigen, ConvertVector3dStamped) |
43 | 49 | { |
@@ -133,7 +139,181 @@ TEST(TfEigen, ConvertTransform) |
133 | 139 | EXPECT_TRUE(tm.isApprox(Iback.matrix())); |
134 | 140 | } |
135 | 141 |
|
136 | | -int main(int argc, char **argv){ |
| 142 | +struct EigenBufferTransform : public ::testing::Test |
| 143 | +{ |
| 144 | + static void SetUpTestSuite() |
| 145 | + { |
| 146 | + rclcpp::Clock::SharedPtr clock = std::make_shared<rclcpp::Clock>(RCL_SYSTEM_TIME); |
| 147 | + tf_buffer = std::make_unique<tf2_ros::Buffer>(clock); |
| 148 | + tf_buffer->setUsingDedicatedThread(true); |
| 149 | + |
| 150 | + // populate buffer |
| 151 | + geometry_msgs::msg::TransformStamped t; |
| 152 | + t.transform.translation.x = 10; |
| 153 | + t.transform.translation.y = 20; |
| 154 | + t.transform.translation.z = 30; |
| 155 | + t.transform.rotation.w = 0; |
| 156 | + t.transform.rotation.x = 1; |
| 157 | + t.transform.rotation.y = 0; |
| 158 | + t.transform.rotation.z = 0; |
| 159 | + t.header.stamp = tf2_ros::toMsg(tf2::timeFromSec(2)); |
| 160 | + t.header.frame_id = "A"; |
| 161 | + t.child_frame_id = "B"; |
| 162 | + tf_buffer->setTransform(t, "test"); |
| 163 | + } |
| 164 | + |
| 165 | + |
| 166 | + template<int mode> |
| 167 | + void testEigenTransform(); |
| 168 | + |
| 169 | + ::testing::AssertionResult doTestEigenQuaternion( |
| 170 | + const Eigen::Quaterniond & parameter, |
| 171 | + const Eigen::Quaterniond & expected); |
| 172 | + |
| 173 | + static std::unique_ptr<tf2_ros::Buffer> tf_buffer; |
| 174 | + static constexpr double EPS = 1e-3; |
| 175 | +}; |
| 176 | + |
| 177 | +std::unique_ptr<tf2_ros::Buffer> EigenBufferTransform::tf_buffer; |
| 178 | + |
| 179 | +template<int mode> |
| 180 | +void EigenBufferTransform::testEigenTransform() |
| 181 | +{ |
| 182 | + using T = Eigen::Transform<double, 3, mode>; |
| 183 | + using stampedT = tf2::Stamped<T>; |
| 184 | + |
| 185 | + geometry_msgs::msg::PoseStamped v1; |
| 186 | + v1.pose.position.x = 1; |
| 187 | + v1.pose.position.y = 2; |
| 188 | + v1.pose.position.z = 3; |
| 189 | + v1.pose.orientation.w = 0; |
| 190 | + v1.pose.orientation.x = 1; |
| 191 | + v1.pose.orientation.y = 0; |
| 192 | + v1.pose.orientation.z = 0; |
| 193 | + v1.header.stamp = tf2_ros::toMsg(tf2::timeFromSec(2)); |
| 194 | + v1.header.frame_id = "A"; |
| 195 | + |
| 196 | + stampedT i1; |
| 197 | + tf2::convert(v1, i1); |
| 198 | + |
| 199 | + // simple api |
| 200 | + const stampedT i_simple = tf_buffer->transform(i1, "B", tf2::durationFromSec(2.0)); |
| 201 | + EXPECT_NEAR(i_simple.translation().x(), -9, EPS); |
| 202 | + EXPECT_NEAR(i_simple.translation().y(), 18, EPS); |
| 203 | + EXPECT_NEAR(i_simple.translation().z(), 27, EPS); |
| 204 | + const auto q1 = Eigen::Quaterniond(i_simple.linear()); |
| 205 | + EXPECT_NEAR(q1.x(), 0.0, EPS); |
| 206 | + EXPECT_NEAR(q1.y(), 0.0, EPS); |
| 207 | + EXPECT_NEAR(q1.z(), 0.0, EPS); |
| 208 | + EXPECT_NEAR(q1.w(), 1.0, EPS); |
| 209 | + |
| 210 | + geometry_msgs::msg::PoseStamped v_simple; |
| 211 | + tf2::convert(i_simple, v_simple); |
| 212 | + EXPECT_NEAR(v_simple.pose.position.x, -9, EPS); |
| 213 | + EXPECT_NEAR(v_simple.pose.position.y, 18, EPS); |
| 214 | + EXPECT_NEAR(v_simple.pose.position.z, 27, EPS); |
| 215 | + EXPECT_NEAR(v_simple.pose.orientation.x, 0.0, EPS); |
| 216 | + EXPECT_NEAR(v_simple.pose.orientation.y, 0.0, EPS); |
| 217 | + EXPECT_NEAR(v_simple.pose.orientation.z, 0.0, EPS); |
| 218 | + EXPECT_NEAR(v_simple.pose.orientation.w, 1.0, EPS); |
| 219 | + |
| 220 | + |
| 221 | + // advanced api |
| 222 | + const stampedT v_advanced = tf_buffer->transform( |
| 223 | + i1, "B", tf2::timeFromSec(2.0), |
| 224 | + "A", tf2::durationFromSec(3.0)); |
| 225 | + EXPECT_NEAR(v_advanced.translation().x(), -9, EPS); |
| 226 | + EXPECT_NEAR(v_advanced.translation().y(), 18, EPS); |
| 227 | + EXPECT_NEAR(v_advanced.translation().z(), 27, EPS); |
| 228 | + const auto q2 = Eigen::Quaterniond(v_advanced.linear()); |
| 229 | + EXPECT_NEAR(q2.x(), 0.0, EPS); |
| 230 | + EXPECT_NEAR(q2.y(), 0.0, EPS); |
| 231 | + EXPECT_NEAR(q2.z(), 0.0, EPS); |
| 232 | + EXPECT_NEAR(q2.w(), 1.0, EPS); |
| 233 | +} |
| 234 | + |
| 235 | +TEST_F(EigenBufferTransform, Affine3d) |
| 236 | +{ |
| 237 | + testEigenTransform<Eigen::Affine>(); |
| 238 | +} |
| 239 | + |
| 240 | +TEST_F(EigenBufferTransform, Isometry3d) |
| 241 | +{ |
| 242 | + testEigenTransform<Eigen::Isometry>(); |
| 243 | +} |
| 244 | + |
| 245 | +TEST_F(EigenBufferTransform, Vector) |
| 246 | +{ |
| 247 | + const tf2::Stamped<Eigen::Vector3d> v1 {{1, 2, 3}, tf2::timeFromSec(2), "A"}; |
| 248 | + |
| 249 | + // simple api |
| 250 | + const tf2::Stamped<Eigen::Vector3d> v_simple = |
| 251 | + tf_buffer->transform(v1, "B", tf2::durationFromSec(2.0)); |
| 252 | + EXPECT_NEAR(v_simple.x(), -9, EPS); |
| 253 | + EXPECT_NEAR(v_simple.y(), 18, EPS); |
| 254 | + EXPECT_NEAR(v_simple.z(), 27, EPS); |
| 255 | + |
| 256 | + // advanced api |
| 257 | + const tf2::Stamped<Eigen::Vector3d> v_advanced = tf_buffer->transform( |
| 258 | + v1, "B", tf2::timeFromSec(2.0), |
| 259 | + "A", tf2::durationFromSec(3.0)); |
| 260 | + EXPECT_NEAR(v_advanced.x(), -9, EPS); |
| 261 | + EXPECT_NEAR(v_advanced.y(), 18, EPS); |
| 262 | + EXPECT_NEAR(v_advanced.z(), 27, EPS); |
| 263 | +} |
| 264 | + |
| 265 | +// helper method for Quaternion tests |
| 266 | +::testing::AssertionResult EigenBufferTransform::doTestEigenQuaternion( |
| 267 | + const Eigen::Quaterniond & parameter, const Eigen::Quaterniond & expected) |
| 268 | +{ |
| 269 | + const tf2::Stamped<Eigen::Quaterniond> q1 {parameter, tf2::timeFromSec(2), "A"}; |
| 270 | + // avoid linking error |
| 271 | + const double eps = EPS; |
| 272 | + |
| 273 | + // simple api |
| 274 | + const tf2::Stamped<Eigen::Quaterniond> q_simple = |
| 275 | + tf_buffer->transform(q1, "B", tf2::durationFromSec(2.0)); |
| 276 | + // compare rotation matrices, as the quaternions can be ambigous |
| 277 | + EXPECT_TRUE(q_simple.toRotationMatrix().isApprox(expected.toRotationMatrix(), eps)); |
| 278 | + |
| 279 | + // advanced api |
| 280 | + const tf2::Stamped<Eigen::Quaterniond> q_advanced = tf_buffer->transform( |
| 281 | + q1, "B", tf2::timeFromSec(2.0), |
| 282 | + "A", tf2::durationFromSec(3.0)); |
| 283 | + EXPECT_TRUE(q_advanced.toRotationMatrix().isApprox(expected.toRotationMatrix(), eps)); |
| 284 | + return ::testing::AssertionSuccess(); |
| 285 | +} |
| 286 | + |
| 287 | +TEST_F(EigenBufferTransform, QuaternionRotY) |
| 288 | +{ |
| 289 | + // rotated by -90° around y |
| 290 | + // 0, 0, -1 |
| 291 | + // 0, 1, 0, |
| 292 | + // 1, 0, 0 |
| 293 | + const Eigen::Quaterniond param{Eigen::AngleAxisd(-1 * M_PI_2, Eigen::Vector3d::UnitY())}; |
| 294 | + const Eigen::Quaterniond expected{0, M_SQRT1_2, 0, -1 * M_SQRT1_2}; |
| 295 | + EXPECT_TRUE(doTestEigenQuaternion(param, expected)); |
| 296 | +} |
| 297 | + |
| 298 | +TEST_F(EigenBufferTransform, QuaternionRotX) |
| 299 | +{ |
| 300 | + // rotated by 90° around y |
| 301 | + const Eigen::Quaterniond param{Eigen::AngleAxisd(M_PI_2, Eigen::Vector3d::UnitX())}; |
| 302 | + const Eigen::Quaterniond expected{Eigen::AngleAxisd(-1 * M_PI_2, Eigen::Vector3d::UnitX())}; |
| 303 | + EXPECT_TRUE(doTestEigenQuaternion(param, expected)); |
| 304 | +} |
| 305 | + |
| 306 | +TEST_F(EigenBufferTransform, QuaternionRotZ) |
| 307 | +{ |
| 308 | + // rotated by 180° around z |
| 309 | + const Eigen::Quaterniond param{Eigen::AngleAxisd(M_PI, Eigen::Vector3d::UnitZ())}; |
| 310 | + const Eigen::Quaterniond expected{Eigen::AngleAxisd(M_PI, Eigen::Vector3d::UnitY())}; |
| 311 | + EXPECT_TRUE(doTestEigenQuaternion(param, expected)); |
| 312 | +} |
| 313 | + |
| 314 | + |
| 315 | +int main(int argc, char ** argv) |
| 316 | +{ |
137 | 317 | testing::InitGoogleTest(&argc, argv); |
138 | 318 |
|
139 | 319 | return RUN_ALL_TESTS(); |
|
0 commit comments