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

add a semantic command interface to "semantic_components" #1945

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f9081f9
add semantic component command interface
tpoignonec Dec 16, 2024
5585ef6
add led_rgb_device
tpoignonec Dec 16, 2024
7d08efd
add tests for LED device
tpoignonec Dec 16, 2024
b0b64cf
lint
tpoignonec Dec 16, 2024
39ed94c
fix misc typos and fix test errors
tpoignonec Dec 16, 2024
e3b99d3
refactor smr version of semantic command interface to match ros-contr…
Dec 16, 2024
2e2798f
add "validate_custom_names" test case
Dec 16, 2024
6c8b092
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Dec 18, 2024
121da64
minor fixes in comments
Dec 20, 2024
ab32971
Change "LEDRgbDevice" to "LedRgbDevice"
Dec 20, 2024
acfcb8c
add release notes
Dec 20, 2024
a71c699
make CI happy
Dec 20, 2024
803a5fb
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Dec 20, 2024
342af6a
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Dec 22, 2024
5318268
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Dec 22, 2024
050dbb6
Update controller_interface/include/semantic_components/led_rgb_devic…
tpoignonec Dec 24, 2024
2de80b4
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Dec 24, 2024
db6cb8d
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Dec 24, 2024
0d0bb54
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Dec 24, 2024
f8cb935
Update controller_interface/include/semantic_components/led_rgb_devic…
tpoignonec Dec 24, 2024
a2dfe9a
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Dec 24, 2024
ed05c4e
run pre-commit --all
tpoignonec Dec 24, 2024
246ac99
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Jan 2, 2025
89d4020
fix suggested changes for initialization
tpoignonec Jan 3, 2025
790b9d4
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Jan 4, 2025
34a7734
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 8, 2025
492fa9b
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Jan 11, 2025
86eb68b
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Jan 13, 2025
0d94c77
remove "limits" include
tpoignonec Jan 13, 2025
697bdb7
fix LedRgbDevice::set_values_from_message
Jan 13, 2025
29cb799
expand docstring for SemanticComponentCommandInterface::set_values
Jan 13, 2025
e4f8bdd
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 13, 2025
9b8a2fe
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 14, 2025
69277d8
make CI happy
Jan 16, 2025
25c5a91
Update controller_interface/include/semantic_components/led_rgb_devic…
tpoignonec Jan 16, 2025
f99afcf
remove virtual declaration (redundant with override keyword)
Jan 16, 2025
70ec8dd
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 16, 2025
7eaafde
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 22, 2025
609542e
declare "get_command_interface_names" as virtual
tpoignonec Jan 22, 2025
5df7b43
make "set_values_from_message" a pure virtual method
Jan 22, 2025
db374fe
remove constructor SemanticComponentCommandInterface(name, size)
Jan 22, 2025
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
19 changes: 19 additions & 0 deletions controller_interface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@ if(BUILD_TESTING)
ament_target_dependencies(test_pose_sensor
geometry_msgs
)

# Semantic component command interface tests

ament_add_gmock(test_semantic_component_command_interface
test/test_semantic_component_command_interface.cpp
)
target_link_libraries(test_semantic_component_command_interface
controller_interface
hardware_interface::hardware_interface
)

ament_add_gmock(test_led_rgb_device test/test_led_rgb_device.cpp)
target_link_libraries(test_led_rgb_device
controller_interface
hardware_interface::hardware_interface
)
ament_target_dependencies(test_led_rgb_device
std_msgs
)
endif()

install(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) 2024, Sherpa Mobile Robotics
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef SEMANTIC_COMPONENTS__LED_RGB_DEVICE_HPP_
#define SEMANTIC_COMPONENTS__LED_RGB_DEVICE_HPP_

#include <string>
#include <vector>

#include "semantic_components/semantic_component_command_interface.hpp"
#include "std_msgs/msg/color_rgba.hpp"

namespace semantic_components
{
class LedRgbDevice : public SemanticComponentCommandInterface<std_msgs::msg::ColorRGBA>
{
public:
/**
* Constructor for a LED RGB device with interface names set based on device name.
* The constructor sets the command interface names to "<name>/r", "<name>/g", "<name>/b".
*/
explicit LedRgbDevice(const std::string & name)
: SemanticComponentCommandInterface(
name, {{name + "/" + "r"}, {name + "/" + "g"}, {name + "/" + "b"}})
{
}

/**
* Constructor for a LED RGB device with custom interface names.
* The constructor takes the three command interface names for the red, green, and blue channels.
*/
explicit LedRgbDevice(
const std::string & interface_r, const std::string & interface_g,
const std::string & interface_b)
: SemanticComponentCommandInterface("", 3)
{
interface_names_.emplace_back(interface_r);
interface_names_.emplace_back(interface_g);
interface_names_.emplace_back(interface_b);
}
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved

/// Set LED states from ColorRGBA message

/**
* Set the values of the LED RGB device from a ColorRGBA message.
*
* \details Sets the values of the red, green, and blue channels from the message.
* If any of the values are out of the range [0, 1], the function fails and returns false.
*
* \param[in] message ColorRGBA message
*
* \return true if all values were set, false otherwise
*/
bool set_values_from_message(const std_msgs::msg::ColorRGBA & message) override
{
if (
message.r < 0 || message.r > 1 || message.g < 0 || message.g > 1 || message.b < 0 ||
message.b > 1)
{
return false;
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved
}
bool all_set = true;
all_set &= command_interfaces_[0].get().set_value(message.r);
all_set &= command_interfaces_[1].get().set_value(message.g);
all_set &= command_interfaces_[2].get().set_value(message.b);
return all_set;
}
};

} // namespace semantic_components

#endif // SEMANTIC_COMPONENTS__LED_RGB_DEVICE_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright (c) 2024, Sherpa Mobile Robotics
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef SEMANTIC_COMPONENTS__SEMANTIC_COMPONENT_COMMAND_INTERFACE_HPP_
#define SEMANTIC_COMPONENTS__SEMANTIC_COMPONENT_COMMAND_INTERFACE_HPP_

#include <string>
#include <vector>

#include "controller_interface/helpers.hpp"
#include "hardware_interface/loaned_command_interface.hpp"

namespace semantic_components
{
template <typename MessageInputType>
class SemanticComponentCommandInterface
{
public:
SemanticComponentCommandInterface(
const std::string & name, const std::vector<std::string> & interface_names)
: name_(name), interface_names_(interface_names)
{
assert(interface_names.size() > 0);
command_interfaces_.reserve(interface_names.size());
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved
}

explicit SemanticComponentCommandInterface(const std::string & name, size_t size = 0)
: name_(name)
{
interface_names_.reserve(size);
command_interfaces_.reserve(size);
}
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved

virtual ~SemanticComponentCommandInterface() = default;

/// Assign loaned command interfaces from the hardware.
/**
* Assign loaned command interfaces on the controller start.
*
* \param[in] command_interfaces vector of command interfaces provided by the controller.
*/
bool assign_loaned_command_interfaces(
std::vector<hardware_interface::LoanedCommandInterface> & command_interfaces)
{
return controller_interface::get_ordered_interfaces(
command_interfaces, interface_names_, "", command_interfaces_);
}

/// Release loaned command interfaces from the hardware.
void release_interfaces() { command_interfaces_.clear(); }

/// Definition of command interface names for the component.
/**
* The function should be used in "command_interface_configuration()" of a controller to provide
* standardized command interface names semantic component.
*
* \default Default implementation defined command interfaces as "name/NR" where NR is number
* from 0 to size of values;
* \return list of strings with command interface names for the semantic component.
*/
virtual std::vector<std::string> get_command_interface_names()
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved
{
if (interface_names_.empty())
{
for (auto i = 0u; i < interface_names_.capacity(); ++i)
{
interface_names_.emplace_back(name_ + "/" + std::to_string(i + 1));
}
}
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved
return interface_names_;
}

/// Return all values.
/**
* \return true if it gets all the values, else false (i.e., invalid size or if the method
* ``hardware_interface::LoanedCommandInterface::set_value`` fails).
*/
bool set_values(const std::vector<double> & values)
{
// check we have sufficient memory
if (values.size() != command_interfaces_.size())
{
return false;
}
// set values
bool all_set = true;
for (auto i = 0u; i < values.size(); ++i)
{
all_set &= command_interfaces_[i].get().set_value(values[i]);
}
return all_set;
}

/// Set values from MessageInputType
/**
* \return false by default
*/
virtual bool set_values_from_message(const MessageInputType & /* message */) { return false; }
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved

protected:
std::string name_;
std::vector<std::string> interface_names_;
std::vector<std::reference_wrapper<hardware_interface::LoanedCommandInterface>>
command_interfaces_;
};

} // namespace semantic_components

#endif // SEMANTIC_COMPONENTS__SEMANTIC_COMPONENT_COMMAND_INTERFACE_HPP_
105 changes: 105 additions & 0 deletions controller_interface/test/test_led_rgb_device.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) 2024, Sherpa Mobile Robotics
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "test_led_rgb_device.hpp"

void LedDeviceTest::SetUp()
{
full_cmd_interface_names_.reserve(size_);
for (const auto & interface_name : interface_names_)
{
full_cmd_interface_names_.emplace_back(device_name_ + '/' + interface_name);
}
}

void LedDeviceTest::TearDown() { led_device_.reset(nullptr); }

TEST_F(LedDeviceTest, validate_all)
{
// Create device
led_device_ = std::make_unique<TestableLedDevice>(device_name_);
EXPECT_EQ(led_device_->name_, device_name_);

// Validate reserved space for interface_names_ and command_interfaces_
// As command_interfaces_ are not defined yet, use capacity()
ASSERT_EQ(led_device_->interface_names_.size(), size_);
ASSERT_EQ(led_device_->command_interfaces_.capacity(), size_);

// Validate default interface_names_
EXPECT_TRUE(std::equal(
led_device_->interface_names_.cbegin(), led_device_->interface_names_.cend(),
full_cmd_interface_names_.cbegin(), full_cmd_interface_names_.cend()));

// Get interface names
std::vector<std::string> interface_names = led_device_->get_command_interface_names();

// Assign values to position
hardware_interface::CommandInterface led_r{device_name_, interface_names_[0], &led_values_[0]};
hardware_interface::CommandInterface led_g{device_name_, interface_names_[1], &led_values_[1]};
hardware_interface::CommandInterface led_b{device_name_, interface_names_[2], &led_values_[2]};

// Create command interface vector in jumbled order
std::vector<hardware_interface::LoanedCommandInterface> temp_command_interfaces;
temp_command_interfaces.reserve(3);
temp_command_interfaces.emplace_back(led_r);
temp_command_interfaces.emplace_back(led_g);
temp_command_interfaces.emplace_back(led_b);

// Assign interfaces
led_device_->assign_loaned_command_interfaces(temp_command_interfaces);
EXPECT_EQ(led_device_->command_interfaces_.size(), size_);

// Validate correct assignment
const std::vector<double> test_led_values_cmd = {0.1, 0.2, 0.3};
EXPECT_TRUE(led_device_->set_values(test_led_values_cmd));

EXPECT_EQ(led_values_[0], test_led_values_cmd[0]);
EXPECT_EQ(led_values_[1], test_led_values_cmd[1]);
EXPECT_EQ(led_values_[2], test_led_values_cmd[2]);

// Validate correct assignment from message
std_msgs::msg::ColorRGBA temp_message;
temp_message.r = static_cast<float>(test_led_values_cmd[0]);
temp_message.g = static_cast<float>(test_led_values_cmd[1]);
temp_message.b = static_cast<float>(test_led_values_cmd[2]);
EXPECT_TRUE(led_device_->set_values_from_message(temp_message));

double float_tolerance = 1e-6;
EXPECT_NEAR(led_values_[0], test_led_values_cmd[0], float_tolerance);
EXPECT_NEAR(led_values_[1], test_led_values_cmd[1], float_tolerance);
EXPECT_NEAR(led_values_[2], test_led_values_cmd[2], float_tolerance);

// Release command interfaces
led_device_->release_interfaces();
ASSERT_EQ(led_device_->command_interfaces_.size(), 0);
}

TEST_F(LedDeviceTest, validate_custom_names)
{
std::string interface_name_r = "led/custom_r";
std::string interface_name_g = "led/custom_g";
std::string interface_name_b = "led/custom_b";
// Create device
led_device_ =
std::make_unique<TestableLedDevice>(interface_name_r, interface_name_g, interface_name_b);
EXPECT_EQ(led_device_->name_, "");

EXPECT_EQ(led_device_->interface_names_.size(), size_);
EXPECT_EQ(led_device_->command_interfaces_.capacity(), size_);

// Validate custom interface_names_
EXPECT_EQ(led_device_->interface_names_[0], interface_name_r);
EXPECT_EQ(led_device_->interface_names_[1], interface_name_g);
EXPECT_EQ(led_device_->interface_names_[2], interface_name_b);
}
Loading
Loading