Skip to content

Commit 28fa9d7

Browse files
committed
Enhance interface configuration validation
1 parent 45ce50f commit 28fa9d7

2 files changed

Lines changed: 147 additions & 77 deletions

File tree

vesc_hardware/include/vesc_hardware/vesc_hardware.hpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include <functional>
3434
#include <memory>
3535
#include <string>
36+
#include <vector>
37+
#include <unordered_set>
3638
#include <unordered_map>
3739

3840
#include "hardware_interface/handle.hpp"
@@ -164,6 +166,17 @@ class VescHardware : public hardware_interface::SystemInterface {
164166
// Interface definition initialization
165167
void populate_state_definitions();
166168
void populate_command_definitions();
169+
std::unordered_set<std::string> get_state_interface_groups() const;
170+
hardware_interface::CallbackReturn validate_and_mark_requested_state_interfaces(
171+
const hardware_interface::ComponentInfo & joint);
172+
hardware_interface::CallbackReturn validate_and_mark_requested_command_interfaces(
173+
const hardware_interface::ComponentInfo & joint);
174+
175+
enum class ControlGroup
176+
{
177+
ROTOR = 0,
178+
SERVO = 1,
179+
};
167180

168181
// Interface data structures
169182
struct StateInterfaceData
@@ -175,6 +188,7 @@ class VescHardware : public hardware_interface::SystemInterface {
175188
struct CommandInterfaceData
176189
{
177190
bool requested; // Whether this interface was requested in URDF
191+
ControlGroup control_group; // Control group for this interface (ROTOR or SERVO)
178192
std::function<void(double)> set_command; // Functor to send command to hardware
179193
};
180194

@@ -218,8 +232,6 @@ class VescHardware : public hardware_interface::SystemInterface {
218232
std::unordered_map<std::string, StateInterfaceData> state_interfaces_;
219233
std::unordered_map<std::string, CommandInterfaceData> command_interfaces_;
220234

221-
std::unordered_set<std::string> state_interface_groups_;
222-
223235
// VESC interface
224236
std::unique_ptr<vesc_driver::VescInterface> vesc_interface_;
225237

vesc_hardware/src/vesc_hardware.cpp

Lines changed: 133 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -141,89 +141,50 @@ hardware_interface::CallbackReturn VescHardware::on_init(
141141
return hardware_interface::CallbackReturn::ERROR;
142142
}
143143

144-
const auto & joint = info_.joints[0];
145-
146-
// Initialize list of supported interfaces
144+
// Initialize list of supported interfaces
147145
populate_state_definitions();
148146
populate_command_definitions();
149147

150-
// Check which state interfaces are requested
151-
std::set<std::string> state_interfaces_requested;
152-
for (const auto & state_interface : joint.state_interfaces) {
153-
auto it = state_interface_groups_.find(state_interface.name);
154-
if (it == state_interface_groups_.end()) {
155-
RCLCPP_FATAL(get_logger(),
156-
"Unsupported state interface '%s' requested for joint '%s'",
157-
state_interface.name.c_str(), joint.name.c_str());
158-
return hardware_interface::CallbackReturn::ERROR;
159-
}
160-
if (state_interfaces_requested.count(state_interface.name) > 0) {
161-
RCLCPP_FATAL(
162-
get_logger(),
163-
"Duplicate state interface '%s' requested for joint '%s'",
164-
state_interface.name.c_str(), joint.name.c_str());
165-
return hardware_interface::CallbackReturn::ERROR;
166-
}
167-
state_interfaces_requested.insert(state_interface.name);
168-
// mark as requested all of the interfaces whose name matches the requested interface or is part of the requested group
169-
for (auto & [name, data] : state_interfaces_) {
170-
if (name == state_interface.name || name.rfind(state_interface.name + ".", 0) == 0) {
171-
data.requested = true;
148+
auto validate_component = [this](const hardware_interface::ComponentInfo & component) {
149+
// Validate and mark requested state interfaces
150+
if ((validate_and_mark_requested_state_interfaces(component) !=
151+
hardware_interface::CallbackReturn::SUCCESS) ||
152+
(validate_and_mark_requested_command_interfaces(component) !=
153+
hardware_interface::CallbackReturn::SUCCESS))
154+
{
155+
return hardware_interface::CallbackReturn::ERROR;
172156
}
173-
RCLCPP_INFO(get_logger(), "State interface '%s' requested",
174-
state_interface.name.c_str());
175-
}
176-
}
157+
return hardware_interface::CallbackReturn::SUCCESS;
158+
};
177159

178-
// Check which command interfaces are requested
179-
for (const auto & command_interface : joint.command_interfaces) {
180-
auto it = command_interfaces_.find(command_interface.name);
181-
if (it == command_interfaces_.end()) {
182-
RCLCPP_FATAL(
183-
get_logger(),
184-
"Unsupported command interface '%s' requested for joint '%s'",
185-
command_interface.name.c_str(), joint.name.c_str());
160+
// process joints
161+
for (const auto & component : info_.joints) {
162+
if (validate_component(component) != hardware_interface::CallbackReturn::SUCCESS) {
186163
return hardware_interface::CallbackReturn::ERROR;
187164
}
188-
if (it->second.requested) {
189-
RCLCPP_FATAL(
190-
get_logger(),
191-
"Duplicate command interface '%s' requested for joint '%s'",
192-
command_interface.name.c_str(), joint.name.c_str());
193-
return hardware_interface::CallbackReturn::ERROR;
194-
}
195-
it->second.requested = true;
196-
RCLCPP_INFO(get_logger(), "Command interface '%s' requested",
197-
command_interface.name.c_str());
198165
}
199166

200-
// Check that at least one interface is requested
201-
bool has_state_interface = false;
202-
for (const auto & [name, data] : state_interfaces_) {
203-
if (data.requested) {
204-
has_state_interface = true;
205-
break;
167+
// process sensors
168+
for (const auto & component : info_.sensors) {
169+
if (validate_component(component) != hardware_interface::CallbackReturn::SUCCESS) {
170+
return hardware_interface::CallbackReturn::ERROR;
206171
}
207172
}
208173

209-
bool has_command_interface = false;
174+
// check if more than one command interface from the same control group was requested
175+
std::unordered_set<VescHardware::ControlGroup> control_groups_seen;
210176
for (const auto & [name, data] : command_interfaces_) {
211177
if (data.requested) {
212-
has_command_interface = true;
213-
break;
178+
if (control_groups_seen.find(data.control_group) != control_groups_seen.end()) {
179+
RCLCPP_FATAL(get_logger(),
180+
"Multiple mutually exclusive command interfaces requested for control group %s",
181+
(data.control_group == ControlGroup::ROTOR ? "ROTOR" : "SERVO"));
182+
return hardware_interface::CallbackReturn::ERROR;
183+
}
184+
control_groups_seen.insert(data.control_group);
214185
}
215186
}
216187

217-
if (!has_state_interface) {
218-
RCLCPP_WARN(get_logger(), "No state interfaces requested for joint '%s'",
219-
joint.name.c_str());
220-
}
221-
222-
if (!has_command_interface) {
223-
RCLCPP_WARN(get_logger(), "No command interfaces requested for joint '%s'",
224-
joint.name.c_str());
225-
}
226-
227188
// Initialize state storage
228189
hw_state_position_ = 0.0;
229190
hw_state_velocity_ = 0.0;
@@ -531,52 +492,147 @@ void VescHardware::populate_state_definitions()
531492
false,
532493
[this]() {return hw_imu_magnetic_field_z_.load(std::memory_order_relaxed);}
533494
};
495+
}
496+
497+
std::unordered_set<std::string> VescHardware::get_state_interface_groups() const
498+
{
499+
// Create a set of state interface group names
500+
std::unordered_set<std::string> state_interface_groups;
534501

535-
// create a vector of state interface group names
536-
const auto get_group_name = [this](const auto & pair) {
502+
const auto get_group_name = [](const auto & pair) {
537503
const auto & name = pair.first;
538504
if (name.find('.') != std::string::npos) {
539-
// This is a grouped interface, extract the group name
540-
std::string group_name = name.substr(0, name.find('.'));
541-
return group_name;
505+
// This is a grouped interface, extract the group name
506+
return name.substr(0, name.find('.'));
542507
} else {
543-
// This is a single interface, add it directly
508+
// This is a single interface, add it directly
544509
return name;
545510
}
546511
};
547512

548-
state_interface_groups_.clear();
549513
std::for_each(state_interfaces_.begin(), state_interfaces_.end(),
550-
[this, &get_group_name](const auto & pair) {
551-
state_interface_groups_.insert(get_group_name(pair));
514+
[&state_interface_groups, &get_group_name](const auto & pair) {
515+
state_interface_groups.insert(get_group_name(pair));
552516
});
517+
518+
return state_interface_groups;
519+
}
520+
521+
hardware_interface::CallbackReturn VescHardware::validate_and_mark_requested_state_interfaces(
522+
const hardware_interface::ComponentInfo & joint)
523+
{
524+
// Get state interface groups for validation
525+
auto state_interface_groups = get_state_interface_groups();
526+
527+
// Check which state interfaces are requested
528+
for (const auto & state_interface : joint.state_interfaces) {
529+
// check that it's a valid interface name or group
530+
auto it = state_interface_groups.find(state_interface.name);
531+
if (it == state_interface_groups.end()) {
532+
RCLCPP_FATAL(get_logger(),
533+
"Unsupported state interface '%s' requested for joint '%s'",
534+
state_interface.name.c_str(), joint.name.c_str());
535+
return hardware_interface::CallbackReturn::ERROR;
536+
}
537+
// mark as requested all of the interfaces whose name matches the
538+
// requested interface or is part of the requested group. Check if it was previously
539+
// registered
540+
for (auto & [name, data] : state_interfaces_) {
541+
if (name == state_interface.name || name.rfind(state_interface.name + ".", 0) == 0) {
542+
// if it was already requested, this is a duplicate request
543+
if (data.requested) {
544+
RCLCPP_FATAL(get_logger(),
545+
"Duplicate state interface '%s' requested for joint '%s'",
546+
state_interface.name.c_str(), joint.name.c_str());
547+
return hardware_interface::CallbackReturn::ERROR;
548+
}
549+
data.requested = true;
550+
}
551+
RCLCPP_INFO(get_logger(), "State interface '%s' requested",
552+
state_interface.name.c_str());
553+
}
554+
}
555+
556+
// Check that at least one interface is requested
557+
bool has_state_interface = std::any_of(state_interfaces_.begin(), state_interfaces_.end(),
558+
[](const auto & pair) {return pair.second.requested;});
559+
560+
if (!has_state_interface) {
561+
RCLCPP_WARN(get_logger(), "No state interfaces requested for joint '%s'",
562+
joint.name.c_str());
563+
}
564+
565+
return hardware_interface::CallbackReturn::SUCCESS;
566+
}
567+
568+
hardware_interface::CallbackReturn VescHardware::validate_and_mark_requested_command_interfaces(
569+
const hardware_interface::ComponentInfo & joint)
570+
{
571+
// Check which command interfaces are requested
572+
for (const auto & command_interface : joint.command_interfaces) {
573+
// check that it's a valid interface name
574+
auto it = command_interfaces_.find(command_interface.name);
575+
if (it == command_interfaces_.end()) {
576+
RCLCPP_FATAL(
577+
get_logger(),
578+
"Unsupported command interface '%s' requested for joint '%s'",
579+
command_interface.name.c_str(), joint.name.c_str());
580+
return hardware_interface::CallbackReturn::ERROR;
581+
}
582+
// check that it was not previously requested
583+
if (it->second.requested) {
584+
RCLCPP_FATAL(
585+
get_logger(),
586+
"Duplicate command interface '%s' requested for joint '%s'",
587+
command_interface.name.c_str(), joint.name.c_str());
588+
return hardware_interface::CallbackReturn::ERROR;
589+
}
590+
it->second.requested = true;
591+
RCLCPP_INFO(get_logger(), "Command interface '%s' requested",
592+
command_interface.name.c_str());
593+
}
594+
595+
// Check that at least one interface is requested
596+
bool has_command_interface = std::any_of(command_interfaces_.begin(), command_interfaces_.end(),
597+
[](const auto & pair) {return pair.second.requested;});
598+
599+
if (!has_command_interface) {
600+
RCLCPP_WARN(get_logger(), "No command interfaces requested for joint '%s'",
601+
joint.name.c_str());
602+
}
603+
604+
return hardware_interface::CallbackReturn::SUCCESS;
553605
}
554606

555607
void VescHardware::populate_command_definitions()
556608
{
557609
command_interfaces_[hardware_interface::HW_IF_POSITION] = {
558610
false,
611+
ControlGroup::ROTOR,
559612
[this](double value) {
560613
double vesc_position = convertMechanicalRadToDeg(value);
561614
vesc_interface_->setPosition(vesc_position);
562615
}
563616
};
564617
command_interfaces_[hardware_interface::HW_IF_VELOCITY] = {
565618
false,
619+
ControlGroup::ROTOR,
566620
[this](double value) {
567621
double vesc_erpm = convertMechanicalRadSecToERPM(value);
568622
vesc_interface_->setSpeed(vesc_erpm);
569623
}
570624
};
571625
command_interfaces_[CUSTOM_HW_IF_SERVO] = {
572626
false,
627+
ControlGroup::SERVO,
573628
[this](double value) {
574629
hw_command_servo_ = value;
575630
vesc_interface_->setServo(hw_command_servo_);
576631
}
577632
};
578633
command_interfaces_[CUSTOM_HW_IF_DUTY_CYCLE] = {
579634
false,
635+
ControlGroup::ROTOR,
580636
[this](double value) {
581637
// Clamp duty cycle to [0, 1] range
582638
double clamped_duty_cycle = std::clamp(value, 0.0, 1.0);
@@ -585,12 +641,14 @@ void VescHardware::populate_command_definitions()
585641
};
586642
command_interfaces_[hardware_interface::HW_IF_CURRENT] = {
587643
false,
644+
ControlGroup::ROTOR,
588645
[this](double value) {
589646
vesc_interface_->setCurrent(value);
590647
}
591648
};
592649
command_interfaces_[CUSTOM_HW_IF_BRAKE] = {
593650
false,
651+
ControlGroup::ROTOR,
594652
[this](double value) {
595653
vesc_interface_->setBrake(value);
596654
}

0 commit comments

Comments
 (0)