Skip to content

Commit 8c6523a

Browse files
committed
Improve read() and write() algorithmic performance
1 parent a826a48 commit 8c6523a

2 files changed

Lines changed: 106 additions & 95 deletions

File tree

vesc_hardware/include/vesc_hardware/vesc_hardware.hpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
#define VESC_HARDWARE__VESC_HARDWARE_HPP_
3131

3232
#include <atomic>
33+
#include <functional>
3334
#include <memory>
3435
#include <string>
35-
#include <vector>
36+
#include <unordered_map>
3637

3738
#include "hardware_interface/handle.hpp"
3839
#include "hardware_interface/hardware_info.hpp"
@@ -154,34 +155,41 @@ class VescHardware : public hardware_interface::SystemInterface {
154155
double convertMechanicalRadToDeg(double mechanical_position_rad) const;
155156
double convertMechanicalRadSecToERPM(double mechanical_velocity_rad_s) const;
156157

157-
// Interface tracking structure
158-
struct InterfaceInfo
158+
// Interface definition initialization
159+
void populate_state_definitions();
160+
void populate_command_definitions();
161+
162+
// Interface data structures
163+
struct StateInterfaceData
164+
{
165+
bool requested; // Whether this interface was requested in URDF
166+
std::function<double()> get_value; // Functor to retrieve current state value
167+
};
168+
169+
struct CommandInterfaceData
159170
{
160-
std::string name; // Interface name (position or velocity)
161-
std::string type; // Interface type string
162171
bool requested; // Whether this interface was requested in URDF
172+
std::function<void(double)> set_command; // Functor to send command to hardware
163173
};
164174

165175
// Hardware parameters
166176
std::string device_;
167177
double gear_ratio_; // Gear ratio between motor and output (default: 1.0)
168178
int pole_pairs_; // Motor pole pairs (default: 1)
169179

170-
// VESC interface
171-
std::unique_ptr<vesc_driver::VescInterface> vesc_interface_;
172-
173-
// Interface availability tracking
174-
std::vector<InterfaceInfo> state_interfaces_;
175-
std::vector<InterfaceInfo> command_interfaces_;
176-
177180
// State storage (atomic for thread-safe access from callback)
178181
std::atomic<double> hw_state_position_;
179182
std::atomic<double> hw_state_velocity_;
180183

181-
// Command storage
182-
double hw_command_position_;
183-
double hw_command_velocity_;
184+
// Command storage (servo only - state mirrors command since VESC can't read it)
184185
double hw_command_servo_;
186+
187+
// Interface availability tracking
188+
std::unordered_map<std::string, StateInterfaceData> state_interfaces_;
189+
std::unordered_map<std::string, CommandInterfaceData> command_interfaces_;
190+
191+
// VESC interface
192+
std::unique_ptr<vesc_driver::VescInterface> vesc_interface_;
185193
};
186194

187195
} // namespace vesc_hardware

vesc_hardware/src/vesc_hardware.cpp

Lines changed: 83 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -92,82 +92,64 @@ hardware_interface::CallbackReturn VescHardware::on_init(
9292
const auto & joint = info_.joints[0];
9393

9494
// Initialize list of supported interfaces
95-
state_interfaces_ = {{hardware_interface::HW_IF_POSITION, "position", false},
96-
{hardware_interface::HW_IF_VELOCITY, "velocity", false},
97-
{"servo", "servo", false}};
98-
99-
command_interfaces_ = {
100-
{hardware_interface::HW_IF_POSITION, "position", false},
101-
{hardware_interface::HW_IF_VELOCITY, "velocity", false},
102-
{"servo", "servo", false}};
95+
populate_state_definitions();
96+
populate_command_definitions();
10397

10498
// Check which state interfaces are requested
10599
for (const auto & state_interface : joint.state_interfaces) {
106-
bool found = false;
107-
for (auto & supported_interface : state_interfaces_) {
108-
if (state_interface.name == supported_interface.name) {
109-
if (supported_interface.requested) {
110-
RCLCPP_FATAL(
111-
get_logger(),
112-
"Duplicate state interface '%s' requested for joint '%s'",
113-
state_interface.name.c_str(), joint.name.c_str());
114-
return hardware_interface::CallbackReturn::ERROR;
115-
}
116-
supported_interface.requested = true;
117-
found = true;
118-
RCLCPP_INFO(get_logger(), "State interface '%s' requested",
119-
state_interface.name.c_str());
120-
break;
121-
}
122-
}
123-
if (!found) {
100+
auto it = state_interfaces_.find(state_interface.name);
101+
if (it == state_interfaces_.end()) {
124102
RCLCPP_FATAL(get_logger(),
125103
"Unsupported state interface '%s' requested for joint '%s'",
126104
state_interface.name.c_str(), joint.name.c_str());
127105
return hardware_interface::CallbackReturn::ERROR;
128106
}
107+
if (it->second.requested) {
108+
RCLCPP_FATAL(
109+
get_logger(),
110+
"Duplicate state interface '%s' requested for joint '%s'",
111+
state_interface.name.c_str(), joint.name.c_str());
112+
return hardware_interface::CallbackReturn::ERROR;
113+
}
114+
it->second.requested = true;
115+
RCLCPP_INFO(get_logger(), "State interface '%s' requested",
116+
state_interface.name.c_str());
129117
}
130118

131119
// Check which command interfaces are requested
132120
for (const auto & command_interface : joint.command_interfaces) {
133-
bool found = false;
134-
for (auto & supported_interface : command_interfaces_) {
135-
if (command_interface.name == supported_interface.name) {
136-
if (supported_interface.requested) {
137-
RCLCPP_FATAL(
138-
get_logger(),
139-
"Duplicate command interface '%s' requested for joint '%s'",
140-
command_interface.name.c_str(), joint.name.c_str());
141-
return hardware_interface::CallbackReturn::ERROR;
142-
}
143-
supported_interface.requested = true;
144-
found = true;
145-
RCLCPP_INFO(get_logger(), "Command interface '%s' requested",
146-
command_interface.name.c_str());
147-
break;
148-
}
149-
}
150-
if (!found) {
121+
auto it = command_interfaces_.find(command_interface.name);
122+
if (it == command_interfaces_.end()) {
151123
RCLCPP_FATAL(
152124
get_logger(),
153125
"Unsupported command interface '%s' requested for joint '%s'",
154126
command_interface.name.c_str(), joint.name.c_str());
155127
return hardware_interface::CallbackReturn::ERROR;
156128
}
129+
if (it->second.requested) {
130+
RCLCPP_FATAL(
131+
get_logger(),
132+
"Duplicate command interface '%s' requested for joint '%s'",
133+
command_interface.name.c_str(), joint.name.c_str());
134+
return hardware_interface::CallbackReturn::ERROR;
135+
}
136+
it->second.requested = true;
137+
RCLCPP_INFO(get_logger(), "Command interface '%s' requested",
138+
command_interface.name.c_str());
157139
}
158140

159141
// Check that at least one interface is requested
160142
bool has_state_interface = false;
161-
for (const auto & iface : state_interfaces_) {
162-
if (iface.requested) {
143+
for (const auto & [name, data] : state_interfaces_) {
144+
if (data.requested) {
163145
has_state_interface = true;
164146
break;
165147
}
166148
}
167149

168150
bool has_command_interface = false;
169-
for (const auto & iface : command_interfaces_) {
170-
if (iface.requested) {
151+
for (const auto & [name, data] : command_interfaces_) {
152+
if (data.requested) {
171153
has_command_interface = true;
172154
break;
173155
}
@@ -186,8 +168,6 @@ hardware_interface::CallbackReturn VescHardware::on_init(
186168
// Initialize state and command storage
187169
hw_state_position_ = 0.0;
188170
hw_state_velocity_ = 0.0;
189-
hw_command_position_ = 0.0;
190-
hw_command_velocity_ = 0.0;
191171
hw_command_servo_ = 0.0;
192172

193173
return hardware_interface::CallbackReturn::SUCCESS;
@@ -200,15 +180,15 @@ VescHardware::on_configure(const rclcpp_lifecycle::State & /*previous_state*/)
200180

201181
// Log which interfaces are active
202182
RCLCPP_INFO(get_logger(), "Active state interfaces:");
203-
for (const auto & iface : state_interfaces_) {
204-
if (iface.requested) {
205-
RCLCPP_INFO(get_logger(), " - %s", iface.type.c_str());
183+
for (const auto & [name, data] : state_interfaces_) {
184+
if (data.requested) {
185+
RCLCPP_INFO(get_logger(), " - %s", name.c_str());
206186
}
207187
}
208188
RCLCPP_INFO(get_logger(), "Active command interfaces:");
209-
for (const auto & iface : command_interfaces_) {
210-
if (iface.requested) {
211-
RCLCPP_INFO(get_logger(), " - %s", iface.type.c_str());
189+
for (const auto & [name, data] : command_interfaces_) {
190+
if (data.requested) {
191+
RCLCPP_INFO(get_logger(), " - %s", name.c_str());
212192
}
213193
}
214194

@@ -289,14 +269,9 @@ VescHardware::read(
289269
// (These are updated asynchronously by vescPacketCallback)
290270
for (const auto &[name, descr] : joint_state_interfaces_) {
291271
std::string interface_type = name.substr(name.find_last_of("/") + 1);
292-
293-
if (interface_type == hardware_interface::HW_IF_POSITION) {
294-
set_state(name, hw_state_position_.load(std::memory_order_relaxed));
295-
} else if (interface_type == hardware_interface::HW_IF_VELOCITY) {
296-
set_state(name, hw_state_velocity_.load(std::memory_order_relaxed));
297-
} else if (interface_type == "servo") {
298-
// Servo state mirrors command because the VESC cannot read servo position
299-
set_state(name, hw_command_servo_);
272+
auto it = state_interfaces_.find(interface_type);
273+
if (it != state_interfaces_.end() && it->second.get_value) {
274+
set_state(name, it->second.get_value());
300275
}
301276
}
302277

@@ -317,22 +292,9 @@ VescHardware::write(
317292
for (const auto &[name, descr] : joint_command_interfaces_) {
318293
// Extract interface type from full name "motor_joint/velocity"
319294
std::string interface_type = name.substr(name.find_last_of("/") + 1);
320-
321-
if (interface_type == hardware_interface::HW_IF_POSITION) {
322-
hw_command_position_ = get_command(name);
323-
double vesc_position = convertMechanicalRadToDeg(hw_command_position_);
324-
vesc_interface_->setPosition(vesc_position);
325-
} else if (interface_type == hardware_interface::HW_IF_VELOCITY) {
326-
hw_command_velocity_ = get_command(name);
327-
double vesc_erpm = convertMechanicalRadSecToERPM(hw_command_velocity_);
328-
vesc_interface_->setSpeed(vesc_erpm);
329-
} else if (interface_type == "servo") {
330-
hw_command_servo_ = get_command(name);
331-
vesc_interface_->setServo(hw_command_servo_);
332-
} else {
333-
RCLCPP_WARN_THROTTLE(get_logger(), *get_clock(), 1000,
334-
"Unknown command interface type: %s",
335-
interface_type.c_str());
295+
auto it = command_interfaces_.find(interface_type);
296+
if (it != command_interfaces_.end() && it->second.set_command) {
297+
it->second.set_command(get_command(name));
336298
}
337299
}
338300

@@ -368,6 +330,47 @@ double VescHardware::convertMechanicalRadSecToERPM(
368330
return erpm;
369331
}
370332

333+
void VescHardware::populate_state_definitions()
334+
{
335+
state_interfaces_[hardware_interface::HW_IF_POSITION] = {
336+
false,
337+
[this]() {return hw_state_position_.load(std::memory_order_relaxed);}
338+
};
339+
state_interfaces_[hardware_interface::HW_IF_VELOCITY] = {
340+
false,
341+
[this]() {return hw_state_velocity_.load(std::memory_order_relaxed);}
342+
};
343+
state_interfaces_["servo"] = {
344+
false,
345+
[this]() {return hw_command_servo_;}
346+
};
347+
}
348+
349+
void VescHardware::populate_command_definitions()
350+
{
351+
command_interfaces_[hardware_interface::HW_IF_POSITION] = {
352+
false,
353+
[this](double value) {
354+
double vesc_position = convertMechanicalRadToDeg(value);
355+
vesc_interface_->setPosition(vesc_position);
356+
}
357+
};
358+
command_interfaces_[hardware_interface::HW_IF_VELOCITY] = {
359+
false,
360+
[this](double value) {
361+
double vesc_erpm = convertMechanicalRadSecToERPM(value);
362+
vesc_interface_->setSpeed(vesc_erpm);
363+
}
364+
};
365+
command_interfaces_["servo"] = {
366+
false,
367+
[this](double value) {
368+
hw_command_servo_ = value;
369+
vesc_interface_->setServo(hw_command_servo_);
370+
}
371+
};
372+
}
373+
371374
void VescHardware::processValuesPacket(
372375
const vesc_driver::VescPacketValues *values_packet)
373376
{

0 commit comments

Comments
 (0)