Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(controllers): add TF listener and broadcaster tests #172

Merged
merged 8 commits into from
Dec 5, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Release Versions:
- fix(controllers): move predicate publishing rate parameter to BaseControllerInterface (#168)
- feat(controllers): add TF listener in BaseControllerInterface (#169)
- feat(controllers): add TF broadcaster in BaseControllerInterface (#170)
- test(controllers): add TF listener and broadcaster tests (#172)

## 5.0.2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ class BaseControllerInterface : public controller_interface::ControllerInterface
[[nodiscard]] geometry_msgs::msg::TransformStamped lookup_ros_transform(
const std::string& frame, const std::string& reference_frame, const tf2::TimePoint& time_point,
const tf2::Duration& duration);

/**
* @brief Helper function to send a vector of transforms through a transform broadcaster
* @tparam T The type of the broadcaster (tf2_ros::TransformBroadcaster or tf2_ros::StaticTransformBroadcaster)
Expand Down
74 changes: 73 additions & 1 deletion source/modulo_controllers/test/test_controller_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ class FriendControllerInterface : public ControllerInterface {
public:
using ControllerInterface::add_input;
using ControllerInterface::add_output;
using ControllerInterface::add_static_tf_broadcaster;
using ControllerInterface::add_tf_broadcaster;
using ControllerInterface::add_tf_listener;
using ControllerInterface::lookup_transform;
using ControllerInterface::read_input;
using ControllerInterface::send_static_transform;
using ControllerInterface::send_static_transforms;
using ControllerInterface::send_transform;
using ControllerInterface::send_transforms;
using ControllerInterface::write_output;

private:
Expand Down Expand Up @@ -211,7 +219,71 @@ TYPED_TEST_P(ControllerInterfaceTest, OutputTest) {
}
}

REGISTER_TYPED_TEST_CASE_P(ControllerInterfaceTest, ConfigureErrorTest, InputTest, OutputTest);
TYPED_TEST_P(ControllerInterfaceTest, TF) {
EXPECT_THROW(this->interface_->add_tf_broadcaster(), modulo_core::exceptions::CoreException);
EXPECT_THROW(this->interface_->add_static_tf_broadcaster(), modulo_core::exceptions::CoreException);
EXPECT_THROW(this->interface_->add_tf_listener(), modulo_core::exceptions::CoreException);
auto send_early_tf = state_representation::CartesianPose::Random("controller_test", "world");
EXPECT_THROW(this->interface_->send_transform(send_early_tf), modulo_core::exceptions::CoreException);
this->init();
this->interface_->add_tf_broadcaster();
this->interface_->add_static_tf_broadcaster();
this->interface_->add_tf_listener();
auto send_tf = state_representation::CartesianPose::Random("controller_test", "world");
EXPECT_NO_THROW(this->interface_->send_transform(send_tf));
sleep(1);
state_representation::CartesianPose lookup_tf;
EXPECT_NO_THROW(lookup_tf = this->interface_->lookup_transform("controller_test", "world"));
auto identity = send_tf * lookup_tf.inverse();
EXPECT_FLOAT_EQ(identity.data().norm(), 1.);
EXPECT_FLOAT_EQ(abs(identity.get_orientation().w()), 1.);

sleep(1);
EXPECT_THROW(
lookup_tf = this->interface_->lookup_transform("controller_test", "world", 0.1),
modulo_core::exceptions::LookupTransformException);

auto send_static_tf = state_representation::CartesianPose::Random("controller_static_test", "world");
EXPECT_NO_THROW(this->interface_->send_static_transform(send_static_tf));
EXPECT_THROW(
auto throw_tf = this->interface_->lookup_transform("dummy", "world"),
modulo_core::exceptions::LookupTransformException);

EXPECT_NO_THROW(lookup_tf = this->interface_->lookup_transform("controller_static_test", "world"));
identity = send_static_tf * lookup_tf.inverse();
EXPECT_FLOAT_EQ(identity.data().norm(), 1.);
EXPECT_FLOAT_EQ(abs(identity.get_orientation().w()), 1.);

std::vector<state_representation::CartesianPose> send_tfs;
send_tfs.reserve(3);
for (std::size_t idx = 0; idx < 3; ++idx) {
send_tfs.emplace_back(
state_representation::CartesianPose::Random("controller_test_" + std::to_string(idx), "world"));
}
EXPECT_NO_THROW(this->interface_->send_transforms(send_tfs));
for (const auto& tf : send_tfs) {
lookup_tf = this->interface_->lookup_transform(tf.get_name(), tf.get_reference_frame());
identity = tf * lookup_tf.inverse();
EXPECT_FLOAT_EQ(identity.data().norm(), 1.);
EXPECT_FLOAT_EQ(abs(identity.get_orientation().w()), 1.);
}

std::vector<state_representation::CartesianPose> send_static_tfs;
send_static_tfs.reserve(3);
for (std::size_t idx = 0; idx < 3; ++idx) {
send_static_tfs.emplace_back(
state_representation::CartesianPose::Random("controller_test_static_" + std::to_string(idx), "world"));
}
EXPECT_NO_THROW(this->interface_->send_static_transforms(send_static_tfs));
for (const auto& tf : send_static_tfs) {
lookup_tf = this->interface_->lookup_transform(tf.get_name(), tf.get_reference_frame());
identity = tf * lookup_tf.inverse();
EXPECT_FLOAT_EQ(identity.data().norm(), 1.);
EXPECT_FLOAT_EQ(abs(identity.get_orientation().w()), 1.);
}
}

REGISTER_TYPED_TEST_CASE_P(ControllerInterfaceTest, ConfigureErrorTest, InputTest, OutputTest, TF);

typedef ::testing::Types<BoolT, DoubleT, DoubleVecT, IntT, StringT, CartesianStateT, JointStateT, ImageT> SignalTypes;
INSTANTIATE_TYPED_TEST_CASE_P(TestPrefix, ControllerInterfaceTest, SignalTypes);
Loading