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

[Feature] Export chainable state interfaces from chainable controllers #1021

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
f16bb01
Add the first implementation to export the controller state interface…
saikishor Apr 21, 2023
494315d
rename the methods and variable from using state to estimated
saikishor Apr 21, 2023
3e375ea
update controller interface chainable controller tests for the newly …
saikishor Apr 21, 2023
2970213
Added some helper methods in the Test classes to set the estimated in…
saikishor Apr 23, 2023
e261d15
Added an IMU hardware test component
saikishor Apr 26, 2023
9723f28
Added IMU sensor hardware to the differential drive URDF description
saikishor Apr 26, 2023
6777056
Added new estimated interface data testing to chainable controller te…
saikishor Apr 27, 2023
483dbfa
Add optional imu sensor to the testing of chainable controller
saikishor May 1, 2023
6fd8dce
rename the method to have more generic meaning
saikishor May 1, 2023
df72851
Added sensor_fusion_controller to the class TestControllerChainingWit…
saikishor May 11, 2023
2782146
update the test_chained_controllers test with new sensor_fusion_contr…
saikishor May 11, 2023
889265b
Update auto_switch_to_chained_mode with more elaborate tests incl. se…
saikishor May 11, 2023
1d182f3
Update the activation_error_handling tests with all newly added contr…
saikishor May 11, 2023
2d103f7
update tests of the activation and deactivation error handling with n…
saikishor May 11, 2023
ad75253
Added new deactivation_switching_error_handling to test the controlle…
saikishor May 11, 2023
f0bc18c
Added toggle_references_from_subscribers method to the controller int…
saikishor May 11, 2023
006681b
Added some utility methods in the controller_manager
saikishor May 11, 2023
9c1218d
import exported interfaces without conditioning
saikishor May 11, 2023
61b337f
Added a new method to be able to toggle the controller references bet…
saikishor May 11, 2023
4a90814
activate and deactivate the controller when switching the references …
saikishor May 11, 2023
2fa39f5
in to_chained_mode enable references from interfaces and make all int…
saikishor May 11, 2023
47d3b3c
check all exported interfaces of following actuators when activating …
saikishor May 11, 2023
da4097b
Add the proper to_use_references_from_subscribers_ list generation wi…
saikishor May 11, 2023
295fcce
Check if all the exported interfaces being utilized by other precedin…
saikishor May 11, 2023
61b1a8a
Add estimated_interfaces field to the ControllerState msg and fill-in…
saikishor May 11, 2023
a016f2d
set the controllers references from interface or subscribers in manag…
saikishor May 12, 2023
e6c8138
update the activation_switching_error_handling tests in the controlle…
saikishor May 12, 2023
a032da1
check if the activate and deactivate list is empty after all the checks
saikishor May 14, 2023
db500d3
remove the controller from the use_references_from_subscribers_ list …
saikishor May 14, 2023
fc1cb5b
added some cpplint and clangformatting changes
saikishor May 23, 2023
415cef6
renamed estimated to exported state interface
saikishor Jul 29, 2023
409cd15
renamed to internal state for better semantic meaning
saikishor Aug 2, 2023
32bacd9
fix the chained_controllers adding_in_random_order test with new cont…
saikishor Aug 24, 2023
55a9e46
Fix the tests after rebasing and formatting changes
saikishor Mar 12, 2024
64bfaa1
improve controller manager service tests stability
saikishor Mar 14, 2024
1f96231
Apply the docstring code review suggestions
saikishor Mar 25, 2024
b9e85e9
Apply suggestions of using advanced gmock features
saikishor Mar 25, 2024
18e787d
Add using of IsEmpty and SizeIs from testing of gmock
saikishor Mar 25, 2024
067ca29
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor Mar 25, 2024
af45bcf
use SizeIs and IsEmpty in the test_chainable_controller_interface tests
saikishor Mar 26, 2024
88c99a7
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor Mar 30, 2024
5f42c26
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor Apr 8, 2024
f48d4e3
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor May 1, 2024
eb1603d
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor May 3, 2024
23291c5
Add review suggestions
saikishor May 5, 2024
8d27c67
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor May 5, 2024
254c568
renamed from internal state to the state by reverting
saikishor May 5, 2024
05282e9
remove the estimate variable from for loop
saikishor May 5, 2024
08da210
rename toggle_references_from_subscribers to set_using_references_fro…
saikishor May 5, 2024
56bb033
Remove set_using_references_from_subscribers from the controller inte…
saikishor May 6, 2024
d2f5487
remove to_use_references_from_subscribers_ and cleanup set_controller…
saikishor May 7, 2024
c78085d
update the logic of propagate deactivation of the controllers
saikishor May 7, 2024
bd9ca59
update logic in check_preceding_controllers_to_deactivate to work wit…
saikishor May 7, 2024
93bdaea
add controller chain interfaces usage controller list cache
saikishor May 8, 2024
46d5426
update the expected is_chained_mode in tests with the new changes
saikishor May 8, 2024
0a20f3e
use the new catched controller chain information to update the check …
saikishor May 8, 2024
da3487c
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor May 8, 2024
3ee2438
change robot localization controller a chained controller and some co…
saikishor May 9, 2024
63f2c87
Add position controller two to test the cases of closing loop with ot…
saikishor May 9, 2024
1057562
Add activating and deactivating in group with the new controller
saikishor May 12, 2024
b612a1b
Fix the logic of controllers exporting state interfaces not in chaine…
saikishor May 12, 2024
5e48aa9
Cleanup unused helper method
saikishor May 12, 2024
13a1fcd
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor May 12, 2024
96eced7
Add documentation and release note about exporting state interfaces f…
saikishor May 13, 2024
2969460
Apply suggestions from code review
saikishor May 13, 2024
b01722c
Add more documentation on different interfaces
saikishor May 13, 2024
e64558b
update the docs of set_chained_mode method
saikishor May 21, 2024
8e966a3
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor May 21, 2024
62263ea
Merge branch 'master' into export_readonly_chainable_interfaces
bmagyar Jun 2, 2024
fa2340c
Add default implementation to the on_export_state_interfaces and on_e…
saikishor Jun 14, 2024
20c5afc
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor Jun 14, 2024
bbca8ac
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor Jun 18, 2024
4f97b54
remove one of the redundant checks with the new approach
saikishor Jun 20, 2024
7d5efeb
Merge branch 'master' into export_readonly_chainable_interfaces
saikishor Jun 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef CONTROLLER_INTERFACE__CHAINABLE_CONTROLLER_INTERFACE_HPP_
#define CONTROLLER_INTERFACE__CHAINABLE_CONTROLLER_INTERFACE_HPP_

#include <string>
#include <vector>

#include "controller_interface/controller_interface_base.hpp"
Expand Down Expand Up @@ -55,6 +56,9 @@ class ChainableControllerInterface : public ControllerInterfaceBase
CONTROLLER_INTERFACE_PUBLIC
bool is_chainable() const final;

CONTROLLER_INTERFACE_PUBLIC
std::vector<hardware_interface::StateInterface> export_state_interfaces() final;

CONTROLLER_INTERFACE_PUBLIC
std::vector<hardware_interface::CommandInterface> export_reference_interfaces() final;

Expand All @@ -65,16 +69,27 @@ class ChainableControllerInterface : public ControllerInterfaceBase
bool is_in_chained_mode() const final;

protected:
/// Virtual method that each chainable controller should implement to export its chainable
/// interfaces.
/// Virtual method that each chainable controller should implement to export its read-only
/// chainable interfaces.
/**
* Each chainable controller implements this methods where all its state(read only) interfaces are
* exported. The method has the same meaning as `export_state_interfaces` method from
* hardware_interface::SystemInterface or hardware_interface::ActuatorInterface.
*
* \returns list of StateInterfaces that other controller can use as their inputs.
*/
virtual std::vector<hardware_interface::StateInterface> on_export_state_interfaces();

/// Virtual method that each chainable controller should implement to export its read/write
/// chainable interfaces.
/**
* Each chainable controller implements this methods where all input (command) interfaces are
* exported. The method has the same meaning as `export_command_interface` method from
* hardware_interface::SystemInterface or hardware_interface::ActuatorInterface.
*
* \returns list of CommandInterfaces that other controller can use as their outputs.
*/
virtual std::vector<hardware_interface::CommandInterface> on_export_reference_interfaces() = 0;
virtual std::vector<hardware_interface::CommandInterface> on_export_reference_interfaces();

/// Virtual method that each chainable controller should implement to switch chained mode.
/**
Expand Down Expand Up @@ -114,7 +129,12 @@ class ChainableControllerInterface : public ControllerInterfaceBase
virtual return_type update_and_write_commands(
const rclcpp::Time & time, const rclcpp::Duration & period) = 0;

/// Storage of values for state interfaces
std::vector<std::string> exported_state_interface_names_;
std::vector<double> state_interfaces_values_;

/// Storage of values for reference interfaces
std::vector<std::string> exported_reference_interface_names_;
std::vector<double> reference_interfaces_;

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ class ControllerInterface : public controller_interface::ControllerInterfaceBase
CONTROLLER_INTERFACE_PUBLIC
bool is_chainable() const final;

/**
* A non-chainable controller doesn't export any state interfaces.
*
* \returns empty list.
*/
CONTROLLER_INTERFACE_PUBLIC
std::vector<hardware_interface::StateInterface> export_state_interfaces() final;

/**
* Controller has no reference interfaces.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,20 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
CONTROLLER_INTERFACE_PUBLIC
virtual std::vector<hardware_interface::CommandInterface> export_reference_interfaces() = 0;

/**
* Export interfaces for a chainable controller that can be used as state interface by other
* controllers.
*
* \returns list of state interfaces for preceding controllers.
*/
CONTROLLER_INTERFACE_PUBLIC
virtual std::vector<hardware_interface::StateInterface> export_state_interfaces() = 0;

/**
* Set chained mode of a chainable controller. This method triggers internal processes to switch
* a chainable controller to "chained" mode and vice-versa. Setting controller to "chained" mode
* usually involves disabling of subscribers and other external interfaces to avoid potential
* concurrency in input commands.
* usually involves the usage of the controller's reference interfaces by the other
* controllers
*
* \returns true if mode is switched successfully and false if not.
*/
Expand Down
65 changes: 50 additions & 15 deletions controller_interface/src/chainable_controller_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,35 @@ return_type ChainableControllerInterface::update(
return ret;
}

std::vector<hardware_interface::CommandInterface>
ChainableControllerInterface::export_reference_interfaces()
std::vector<hardware_interface::StateInterface>
ChainableControllerInterface::export_state_interfaces()
{
auto reference_interfaces = on_export_reference_interfaces();
auto state_interfaces = on_export_state_interfaces();

// check if the "reference_interfaces_" variable is resized to number of interfaces
if (reference_interfaces_.size() != reference_interfaces.size())
// check if the names of the controller state interfaces begin with the controller's name
for (const auto & interface : state_interfaces)
{
// TODO(destogl): Should here be "FATAL"? It is fatal in terms of controller but not for the
// framework
RCLCPP_FATAL(
get_node()->get_logger(),
"The internal storage for reference values 'reference_interfaces_' variable has size '%zu', "
"but it is expected to have the size '%zu' equal to the number of exported reference "
"interfaces. No reference interface will be exported. Please correct and recompile "
"the controller with name '%s' and try again.",
reference_interfaces_.size(), reference_interfaces.size(), get_node()->get_name());
reference_interfaces.clear();
if (interface.get_prefix_name() != get_node()->get_name())
{
RCLCPP_FATAL(
get_node()->get_logger(),
"The name of the interface '%s' does not begin with the controller's name. This is "
"mandatory for state interfaces. No state interface will be exported. Please "
"correct and recompile the controller with name '%s' and try again.",
interface.get_name().c_str(), get_node()->get_name());
state_interfaces.clear();
break;
}
}

return state_interfaces;
}

std::vector<hardware_interface::CommandInterface>
ChainableControllerInterface::export_reference_interfaces()
{
auto reference_interfaces = on_export_reference_interfaces();

// check if the names of the reference interfaces begin with the controller's name
for (const auto & interface : reference_interfaces)
{
Expand Down Expand Up @@ -113,4 +122,30 @@ bool ChainableControllerInterface::is_in_chained_mode() const { return in_chaine

bool ChainableControllerInterface::on_set_chained_mode(bool /*chained_mode*/) { return true; }

std::vector<hardware_interface::StateInterface>
ChainableControllerInterface::on_export_state_interfaces()
{
state_interfaces_values_.resize(exported_state_interface_names_.size(), 0.0);
std::vector<hardware_interface::StateInterface> state_interfaces;
for (size_t i = 0; i < exported_state_interface_names_.size(); ++i)
{
state_interfaces.emplace_back(hardware_interface::StateInterface(
get_node()->get_name(), exported_state_interface_names_[i], &state_interfaces_values_[i]));
}
return state_interfaces;
}

std::vector<hardware_interface::CommandInterface>
ChainableControllerInterface::on_export_reference_interfaces()
{
reference_interfaces_.resize(exported_reference_interface_names_.size(), 0.0);
std::vector<hardware_interface::CommandInterface> reference_interfaces;
for (size_t i = 0; i < exported_reference_interface_names_.size(); ++i)
{
reference_interfaces.emplace_back(hardware_interface::CommandInterface(
get_node()->get_name(), exported_reference_interface_names_[i], &reference_interfaces_[i]));
}
return reference_interfaces;
}

} // namespace controller_interface
5 changes: 5 additions & 0 deletions controller_interface/src/controller_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ ControllerInterface::ControllerInterface() : ControllerInterfaceBase() {}

bool ControllerInterface::is_chainable() const { return false; }

std::vector<hardware_interface::StateInterface> ControllerInterface::export_state_interfaces()
{
return {};
}

std::vector<hardware_interface::CommandInterface> ControllerInterface::export_reference_interfaces()
{
return {};
Expand Down
47 changes: 34 additions & 13 deletions controller_interface/test/test_chainable_controller_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

#include <gmock/gmock.h>

using ::testing::IsEmpty;
using ::testing::SizeIs;

TEST_F(ChainableControllerInterfaceTest, default_returns)
{
TestableChainableControllerInterface controller;
Expand All @@ -31,7 +34,7 @@ TEST_F(ChainableControllerInterfaceTest, default_returns)
EXPECT_FALSE(controller.is_in_chained_mode());
}

TEST_F(ChainableControllerInterfaceTest, export_reference_interfaces)
TEST_F(ChainableControllerInterfaceTest, export_state_interfaces)
{
TestableChainableControllerInterface controller;

Expand All @@ -42,16 +45,16 @@ TEST_F(ChainableControllerInterfaceTest, export_reference_interfaces)
controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());

auto reference_interfaces = controller.export_reference_interfaces();
auto exported_state_interfaces = controller.export_state_interfaces();

ASSERT_EQ(reference_interfaces.size(), 1u);
EXPECT_EQ(reference_interfaces[0].get_prefix_name(), TEST_CONTROLLER_NAME);
EXPECT_EQ(reference_interfaces[0].get_interface_name(), "test_itf");
ASSERT_THAT(exported_state_interfaces, SizeIs(1));
EXPECT_EQ(exported_state_interfaces[0].get_prefix_name(), TEST_CONTROLLER_NAME);
EXPECT_EQ(exported_state_interfaces[0].get_interface_name(), "test_state");

EXPECT_EQ(reference_interfaces[0].get_value(), INTERFACE_VALUE);
EXPECT_EQ(exported_state_interfaces[0].get_value(), EXPORTED_STATE_INTERFACE_VALUE);
}

TEST_F(ChainableControllerInterfaceTest, reference_interfaces_storage_not_correct_size)
TEST_F(ChainableControllerInterfaceTest, export_reference_interfaces)
{
TestableChainableControllerInterface controller;

Expand All @@ -62,13 +65,16 @@ TEST_F(ChainableControllerInterfaceTest, reference_interfaces_storage_not_correc
controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());

// expect empty return because storage is not resized
controller.reference_interfaces_.clear();
auto reference_interfaces = controller.export_reference_interfaces();
ASSERT_TRUE(reference_interfaces.empty());

ASSERT_THAT(reference_interfaces, SizeIs(1));
EXPECT_EQ(reference_interfaces[0].get_prefix_name(), TEST_CONTROLLER_NAME);
EXPECT_EQ(reference_interfaces[0].get_interface_name(), "test_itf");

EXPECT_EQ(reference_interfaces[0].get_value(), INTERFACE_VALUE);
}

TEST_F(ChainableControllerInterfaceTest, reference_interfaces_prefix_is_not_node_name)
TEST_F(ChainableControllerInterfaceTest, interfaces_prefix_is_not_node_name)
{
TestableChainableControllerInterface controller;

Expand All @@ -83,7 +89,10 @@ TEST_F(ChainableControllerInterfaceTest, reference_interfaces_prefix_is_not_node

// expect empty return because interface prefix is not equal to the node name
auto reference_interfaces = controller.export_reference_interfaces();
ASSERT_TRUE(reference_interfaces.empty());
ASSERT_THAT(reference_interfaces, IsEmpty());
// expect empty return because interface prefix is not equal to the node name
auto exported_state_interfaces = controller.export_state_interfaces();
ASSERT_THAT(exported_state_interfaces, IsEmpty());
}

TEST_F(ChainableControllerInterfaceTest, setting_chained_mode)
Expand All @@ -98,12 +107,15 @@ TEST_F(ChainableControllerInterfaceTest, setting_chained_mode)
ASSERT_NO_THROW(controller.get_node());

auto reference_interfaces = controller.export_reference_interfaces();
ASSERT_EQ(reference_interfaces.size(), 1u);
ASSERT_THAT(reference_interfaces, SizeIs(1));
auto exported_state_interfaces = controller.export_state_interfaces();
ASSERT_THAT(exported_state_interfaces, SizeIs(1));

EXPECT_FALSE(controller.is_in_chained_mode());

// Fail setting chained mode
EXPECT_EQ(reference_interfaces[0].get_value(), INTERFACE_VALUE);
EXPECT_EQ(exported_state_interfaces[0].get_value(), EXPORTED_STATE_INTERFACE_VALUE);

EXPECT_FALSE(controller.set_chained_mode(true));
EXPECT_FALSE(controller.is_in_chained_mode());
Expand All @@ -116,6 +128,7 @@ TEST_F(ChainableControllerInterfaceTest, setting_chained_mode)

EXPECT_TRUE(controller.set_chained_mode(true));
EXPECT_TRUE(controller.is_in_chained_mode());
EXPECT_EQ(exported_state_interfaces[0].get_value(), EXPORTED_STATE_INTERFACE_VALUE_IN_CHAINMODE);

controller.configure();
EXPECT_TRUE(controller.set_chained_mode(false));
Expand Down Expand Up @@ -147,27 +160,31 @@ TEST_F(ChainableControllerInterfaceTest, test_update_logic)
controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());

EXPECT_FALSE(controller.set_chained_mode(false));
EXPECT_FALSE(controller.is_in_chained_mode());

// call update and update it from subscriber because not in chained mode
ASSERT_EQ(
controller.update(rclcpp::Time(0), rclcpp::Duration::from_seconds(0.01)),
controller_interface::return_type::OK);
ASSERT_EQ(controller.reference_interfaces_[0], INTERFACE_VALUE_INITIAL_REF - 1);
ASSERT_EQ(controller.state_interfaces_values_[0], EXPORTED_STATE_INTERFACE_VALUE + 1);

// Provoke error in update from subscribers - return ERROR and update_and_write_commands not exec.
controller.set_new_reference_interface_value(INTERFACE_VALUE_SUBSCRIBER_ERROR);
ASSERT_EQ(
controller.update(rclcpp::Time(0), rclcpp::Duration::from_seconds(0.01)),
controller_interface::return_type::ERROR);
ASSERT_EQ(controller.reference_interfaces_[0], INTERFACE_VALUE_INITIAL_REF - 1);
ASSERT_EQ(controller.state_interfaces_values_[0], EXPORTED_STATE_INTERFACE_VALUE + 1);

// Provoke error from update - return ERROR, but reference interface is updated and not reduced
controller.set_new_reference_interface_value(INTERFACE_VALUE_UPDATE_ERROR);
ASSERT_EQ(
controller.update(rclcpp::Time(0), rclcpp::Duration::from_seconds(0.01)),
controller_interface::return_type::ERROR);
ASSERT_EQ(controller.reference_interfaces_[0], INTERFACE_VALUE_UPDATE_ERROR);
ASSERT_EQ(controller.state_interfaces_values_[0], EXPORTED_STATE_INTERFACE_VALUE + 1);

controller.reference_interfaces_[0] = 0.0;

Expand All @@ -181,6 +198,8 @@ TEST_F(ChainableControllerInterfaceTest, test_update_logic)
controller.update(rclcpp::Time(0), rclcpp::Duration::from_seconds(0.01)),
controller_interface::return_type::OK);
ASSERT_EQ(controller.reference_interfaces_[0], -1.0);
ASSERT_EQ(
controller.state_interfaces_values_[0], EXPORTED_STATE_INTERFACE_VALUE_IN_CHAINMODE + 1);

// Provoke error from update - return ERROR, but reference interface is updated directly
controller.set_new_reference_interface_value(INTERFACE_VALUE_SUBSCRIBER_ERROR);
Expand All @@ -189,4 +208,6 @@ TEST_F(ChainableControllerInterfaceTest, test_update_logic)
controller.update(rclcpp::Time(0), rclcpp::Duration::from_seconds(0.01)),
controller_interface::return_type::ERROR);
ASSERT_EQ(controller.reference_interfaces_[0], INTERFACE_VALUE_UPDATE_ERROR);
ASSERT_EQ(
controller.state_interfaces_values_[0], EXPORTED_STATE_INTERFACE_VALUE_IN_CHAINMODE + 1);
}
Loading
Loading