Skip to content

Commit f8ed9ee

Browse files
committed
Improve read() and write() algorithmic performance
1 parent 68546fe commit f8ed9ee

2 files changed

Lines changed: 111 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: 88 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
#include "rclcpp/rclcpp.hpp"
4343
#include "vesc_driver/vesc_packet.hpp"
4444

45+
namespace
46+
{
47+
constexpr char CUSTOM_HW_IF_SERVO[] = "servo";
48+
} // namespace
49+
4550
namespace vesc_hardware
4651
{
4752
hardware_interface::CallbackReturn VescHardware::on_init(
@@ -92,82 +97,64 @@ hardware_interface::CallbackReturn VescHardware::on_init(
9297
const auto & joint = info_.joints[0];
9398

9499
// 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}};
100+
populate_state_definitions();
101+
populate_command_definitions();
103102

104103
// Check which state interfaces are requested
105104
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) {
105+
auto it = state_interfaces_.find(state_interface.name);
106+
if (it == state_interfaces_.end()) {
124107
RCLCPP_FATAL(get_logger(),
125108
"Unsupported state interface '%s' requested for joint '%s'",
126109
state_interface.name.c_str(), joint.name.c_str());
127110
return hardware_interface::CallbackReturn::ERROR;
128111
}
112+
if (it->second.requested) {
113+
RCLCPP_FATAL(
114+
get_logger(),
115+
"Duplicate state interface '%s' requested for joint '%s'",
116+
state_interface.name.c_str(), joint.name.c_str());
117+
return hardware_interface::CallbackReturn::ERROR;
118+
}
119+
it->second.requested = true;
120+
RCLCPP_INFO(get_logger(), "State interface '%s' requested",
121+
state_interface.name.c_str());
129122
}
130123

131124
// Check which command interfaces are requested
132125
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) {
126+
auto it = command_interfaces_.find(command_interface.name);
127+
if (it == command_interfaces_.end()) {
151128
RCLCPP_FATAL(
152129
get_logger(),
153130
"Unsupported command interface '%s' requested for joint '%s'",
154131
command_interface.name.c_str(), joint.name.c_str());
155132
return hardware_interface::CallbackReturn::ERROR;
156133
}
134+
if (it->second.requested) {
135+
RCLCPP_FATAL(
136+
get_logger(),
137+
"Duplicate command interface '%s' requested for joint '%s'",
138+
command_interface.name.c_str(), joint.name.c_str());
139+
return hardware_interface::CallbackReturn::ERROR;
140+
}
141+
it->second.requested = true;
142+
RCLCPP_INFO(get_logger(), "Command interface '%s' requested",
143+
command_interface.name.c_str());
157144
}
158145

159146
// Check that at least one interface is requested
160147
bool has_state_interface = false;
161-
for (const auto & iface : state_interfaces_) {
162-
if (iface.requested) {
148+
for (const auto & [name, data] : state_interfaces_) {
149+
if (data.requested) {
163150
has_state_interface = true;
164151
break;
165152
}
166153
}
167154

168155
bool has_command_interface = false;
169-
for (const auto & iface : command_interfaces_) {
170-
if (iface.requested) {
156+
for (const auto & [name, data] : command_interfaces_) {
157+
if (data.requested) {
171158
has_command_interface = true;
172159
break;
173160
}
@@ -186,8 +173,6 @@ hardware_interface::CallbackReturn VescHardware::on_init(
186173
// Initialize state and command storage
187174
hw_state_position_ = 0.0;
188175
hw_state_velocity_ = 0.0;
189-
hw_command_position_ = 0.0;
190-
hw_command_velocity_ = 0.0;
191176
hw_command_servo_ = 0.0;
192177

193178
return hardware_interface::CallbackReturn::SUCCESS;
@@ -200,15 +185,15 @@ VescHardware::on_configure(const rclcpp_lifecycle::State & /*previous_state*/)
200185

201186
// Log which interfaces are active
202187
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());
188+
for (const auto & [name, data] : state_interfaces_) {
189+
if (data.requested) {
190+
RCLCPP_INFO(get_logger(), " - %s", name.c_str());
206191
}
207192
}
208193
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());
194+
for (const auto & [name, data] : command_interfaces_) {
195+
if (data.requested) {
196+
RCLCPP_INFO(get_logger(), " - %s", name.c_str());
212197
}
213198
}
214199

@@ -289,14 +274,9 @@ VescHardware::read(
289274
// (These are updated asynchronously by vescPacketCallback)
290275
for (const auto &[name, descr] : joint_state_interfaces_) {
291276
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_);
277+
auto it = state_interfaces_.find(interface_type);
278+
if (it != state_interfaces_.end() && it->second.get_value) {
279+
set_state(name, it->second.get_value());
300280
}
301281
}
302282

@@ -317,22 +297,9 @@ VescHardware::write(
317297
for (const auto &[name, descr] : joint_command_interfaces_) {
318298
// Extract interface type from full name "motor_joint/velocity"
319299
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());
300+
auto it = command_interfaces_.find(interface_type);
301+
if (it != command_interfaces_.end() && it->second.set_command) {
302+
it->second.set_command(get_command(name));
336303
}
337304
}
338305

@@ -368,6 +335,47 @@ double VescHardware::convertMechanicalRadSecToERPM(
368335
return erpm;
369336
}
370337

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

0 commit comments

Comments
 (0)