forked from odriverobotics/ros_odrive
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathodrive_hardware_interface.cpp
More file actions
365 lines (303 loc) · 12.7 KB
/
Copy pathodrive_hardware_interface.cpp
File metadata and controls
365 lines (303 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#include "can_helpers.hpp"
#include "can_simple_messages.hpp"
#include "hardware_interface/system_interface.hpp"
#include "hardware_interface/types/hardware_interface_type_values.hpp"
#include "odrive_enums.h"
#include "pluginlib/class_list_macros.hpp"
#include "rclcpp/rclcpp.hpp"
#include "socket_can.hpp"
namespace odrive_ros2_control {
class Axis;
class ODriveHardwareInterface final : public hardware_interface::SystemInterface {
public:
using return_type = hardware_interface::return_type;
using State = rclcpp_lifecycle::State;
CallbackReturn on_init(const hardware_interface::HardwareInfo& info) override;
CallbackReturn on_configure(const State& previous_state) override;
CallbackReturn on_cleanup(const State& previous_state) override;
CallbackReturn on_activate(const State& previous_state) override;
CallbackReturn on_deactivate(const State& previous_state) override;
std::vector<hardware_interface::StateInterface> export_state_interfaces() override;
std::vector<hardware_interface::CommandInterface> export_command_interfaces() override;
return_type perform_command_mode_switch(
const std::vector<std::string>& start_interfaces,
const std::vector<std::string>& stop_interfaces
) override;
return_type read(const rclcpp::Time&, const rclcpp::Duration&) override;
return_type write(const rclcpp::Time&, const rclcpp::Duration&) override;
private:
void on_can_msg(const can_frame& frame);
void set_axis_command_mode(const Axis& axis);
bool active_;
EpollEventLoop event_loop_;
std::vector<Axis> axes_;
std::string can_intf_name_;
SocketCanIntf can_intf_;
rclcpp::Time timestamp_;
};
struct Axis {
Axis(SocketCanIntf* can_intf, uint32_t node_id) : can_intf_(can_intf), node_id_(node_id) {}
void on_can_msg(const rclcpp::Time& timestamp, const can_frame& frame);
void on_can_msg();
SocketCanIntf* can_intf_;
uint32_t node_id_;
// Commands (ros2_control => ODrives)
double pos_setpoint_ = 0.0f; // [rad]
double vel_setpoint_ = 0.0f; // [rad/s]
double torque_setpoint_ = 0.0f; // [Nm]
// State (ODrives => ros2_control)
// rclcpp::Time encoder_estimates_timestamp_;
// uint32_t axis_error_ = 0;
// uint8_t axis_state_ = 0;
// uint8_t procedure_result_ = 0;
// uint8_t trajectory_done_flag_ = 0;
double pos_estimate_ = NAN; // [rad]
double vel_estimate_ = NAN; // [rad/s]
// double iq_setpoint_ = NAN;
// double iq_measured_ = NAN;
double torque_target_ = NAN; // [Nm]
double torque_estimate_ = NAN; // [Nm]
// uint32_t active_errors_ = 0;
// uint32_t disarm_reason_ = 0;
// double fet_temperature_ = NAN;
// double motor_temperature_ = NAN;
// double bus_voltage_ = NAN;
// double bus_current_ = NAN;
// Indicates which controller inputs are enabled. This is configured by the
// controller that sits on top of this hardware interface. Multiple inputs
// can be enabled at the same time, in this case the non-primary inputs are
// used as feedforward terms.
// This implicitly defines the ODrive's control mode.
bool pos_input_enabled_ = false;
bool vel_input_enabled_ = false;
bool torque_input_enabled_ = false;
template <typename T>
void send(const T& msg) const {
struct can_frame frame;
frame.can_id = node_id_ << 5 | msg.cmd_id;
frame.can_dlc = msg.msg_length;
msg.encode_buf(frame.data);
can_intf_->send_can_frame(frame);
}
};
} // namespace odrive_ros2_control
using namespace odrive_ros2_control;
using hardware_interface::CallbackReturn;
using hardware_interface::return_type;
CallbackReturn ODriveHardwareInterface::on_init(const hardware_interface::HardwareInfo& info) {
if (hardware_interface::SystemInterface::on_init(info) != CallbackReturn::SUCCESS) {
return CallbackReturn::ERROR;
}
can_intf_name_ = info_.hardware_parameters["can"];
for (auto& joint : info_.joints) {
axes_.emplace_back(&can_intf_, std::stoi(joint.parameters.at("node_id")));
}
return CallbackReturn::SUCCESS;
}
CallbackReturn ODriveHardwareInterface::on_configure(const State&) {
if (!can_intf_.init(can_intf_name_, &event_loop_, std::bind(&ODriveHardwareInterface::on_can_msg, this, _1))) {
RCLCPP_ERROR(
rclcpp::get_logger("ODriveHardwareInterface"),
"Failed to initialize SocketCAN on %s",
can_intf_name_.c_str()
);
return CallbackReturn::ERROR;
}
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Initialized SocketCAN on %s", can_intf_name_.c_str());
return CallbackReturn::SUCCESS;
}
CallbackReturn ODriveHardwareInterface::on_cleanup(const State&) {
can_intf_.deinit();
return CallbackReturn::SUCCESS;
}
CallbackReturn ODriveHardwareInterface::on_activate(const State&) {
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "activating ODrives...");
// This can be called several seconds before the controller finishes starting.
// Therefore we enable the ODrives only in perform_command_mode_switch().
active_ = true;
for (auto& axis : axes_) {
set_axis_command_mode(axis);
}
return CallbackReturn::SUCCESS;
}
CallbackReturn ODriveHardwareInterface::on_deactivate(const State&) {
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "deactivating ODrives...");
active_ = false;
for (auto& axis : axes_) {
set_axis_command_mode(axis);
}
return CallbackReturn::SUCCESS;
}
std::vector<hardware_interface::StateInterface> ODriveHardwareInterface::export_state_interfaces() {
std::vector<hardware_interface::StateInterface> state_interfaces;
for (size_t i = 0; i < info_.joints.size(); i++) {
state_interfaces.emplace_back(hardware_interface::StateInterface(
info_.joints[i].name,
hardware_interface::HW_IF_EFFORT,
&axes_[i].torque_target_
));
state_interfaces.emplace_back(hardware_interface::StateInterface(
info_.joints[i].name,
hardware_interface::HW_IF_VELOCITY,
&axes_[i].vel_estimate_
));
state_interfaces.emplace_back(hardware_interface::StateInterface(
info_.joints[i].name,
hardware_interface::HW_IF_POSITION,
&axes_[i].pos_estimate_
));
}
return state_interfaces;
}
std::vector<hardware_interface::CommandInterface> ODriveHardwareInterface::export_command_interfaces() {
std::vector<hardware_interface::CommandInterface> command_interfaces;
for (size_t i = 0; i < info_.joints.size(); i++) {
command_interfaces.emplace_back(hardware_interface::CommandInterface(
info_.joints[i].name,
hardware_interface::HW_IF_EFFORT,
&axes_[i].torque_setpoint_
));
command_interfaces.emplace_back(hardware_interface::CommandInterface(
info_.joints[i].name,
hardware_interface::HW_IF_VELOCITY,
&axes_[i].vel_setpoint_
));
command_interfaces.emplace_back(hardware_interface::CommandInterface(
info_.joints[i].name,
hardware_interface::HW_IF_POSITION,
&axes_[i].pos_setpoint_
));
}
return command_interfaces;
}
return_type ODriveHardwareInterface::perform_command_mode_switch(
const std::vector<std::string>& start_interfaces,
const std::vector<std::string>& stop_interfaces
) {
for (size_t i = 0; i < axes_.size(); ++i) {
Axis& axis = axes_[i];
std::array<std::pair<std::string, bool*>, 3> interfaces = {
{{info_.joints[i].name + "/" + hardware_interface::HW_IF_POSITION, &axis.pos_input_enabled_},
{info_.joints[i].name + "/" + hardware_interface::HW_IF_VELOCITY, &axis.vel_input_enabled_},
{info_.joints[i].name + "/" + hardware_interface::HW_IF_EFFORT, &axis.torque_input_enabled_}}};
bool mode_switch = false;
for (const std::string& key : stop_interfaces) {
for (auto& kv : interfaces) {
if (kv.first == key) {
*kv.second = false;
mode_switch = true;
}
}
}
for (const std::string& key : start_interfaces) {
for (auto& kv : interfaces) {
if (kv.first == key) {
*kv.second = true;
mode_switch = true;
}
}
}
if (mode_switch) {
set_axis_command_mode(axis);
}
}
return return_type::OK;
}
return_type ODriveHardwareInterface::read(const rclcpp::Time& timestamp, const rclcpp::Duration&) {
timestamp_ = timestamp;
while (can_intf_.read_nonblocking()) {
// repeat until CAN interface has no more messages
}
return return_type::OK;
}
return_type ODriveHardwareInterface::write(const rclcpp::Time&, const rclcpp::Duration&) {
for (auto& axis : axes_) {
// Send the CAN message that fits the set of enabled setpoints
if (axis.pos_input_enabled_) {
Set_Input_Pos_msg_t msg;
msg.Input_Pos = axis.pos_setpoint_ / (2 * M_PI);
msg.Vel_FF = axis.vel_input_enabled_ ? (axis.vel_setpoint_ / (2 * M_PI)) : 0.0f;
msg.Torque_FF = axis.torque_input_enabled_ ? axis.torque_setpoint_ : 0.0f;
axis.send(msg);
} else if (axis.vel_input_enabled_) {
Set_Input_Vel_msg_t msg;
msg.Input_Vel = axis.vel_setpoint_ / (2 * M_PI);
msg.Input_Torque_FF = axis.torque_input_enabled_ ? axis.torque_setpoint_ : 0.0f;
axis.send(msg);
} else if (axis.torque_input_enabled_) {
Set_Input_Torque_msg_t msg;
msg.Input_Torque = axis.torque_setpoint_;
axis.send(msg);
} else {
// no control enabled - don't send any setpoint
}
}
return return_type::OK;
}
void ODriveHardwareInterface::on_can_msg(const can_frame& frame) {
for (auto& axis : axes_) {
if ((frame.can_id >> 5) == axis.node_id_) {
axis.on_can_msg(timestamp_, frame);
}
}
}
void ODriveHardwareInterface::set_axis_command_mode(const Axis& axis) {
if (!active_) {
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Interface inactive. Setting axis to idle.");
Set_Axis_State_msg_t idle_msg;
idle_msg.Axis_Requested_State = AXIS_STATE_IDLE;
axis.send(idle_msg);
return;
}
Set_Controller_Mode_msg_t control_msg;
Clear_Errors_msg_t clear_error_msg;
Set_Axis_State_msg_t state_msg;
clear_error_msg.Identify = 0;
control_msg.Input_Mode = INPUT_MODE_PASSTHROUGH;
state_msg.Axis_Requested_State = AXIS_STATE_CLOSED_LOOP_CONTROL;
if (axis.pos_input_enabled_) {
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Setting to position control.");
control_msg.Control_Mode = CONTROL_MODE_POSITION_CONTROL;
} else if (axis.vel_input_enabled_) {
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Setting to velocity control.");
control_msg.Control_Mode = CONTROL_MODE_VELOCITY_CONTROL;
} else if (axis.torque_input_enabled_) {
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Setting to torque control.");
control_msg.Control_Mode = CONTROL_MODE_TORQUE_CONTROL;
} else {
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "No control mode specified. Setting to idle.");
state_msg.Axis_Requested_State = AXIS_STATE_IDLE;
axis.send(state_msg);
return;
}
axis.send(control_msg);
axis.send(clear_error_msg);
axis.send(state_msg);
}
void Axis::on_can_msg(const rclcpp::Time&, const can_frame& frame) {
uint8_t cmd = frame.can_id & 0x1f;
auto try_decode = [&]<typename TMsg>(TMsg& msg) {
if (frame.can_dlc < Get_Encoder_Estimates_msg_t::msg_length) {
RCLCPP_WARN(rclcpp::get_logger("ODriveHardwareInterface"), "message %d too short", cmd);
return false;
}
msg.decode_buf(frame.data);
return true;
};
switch (cmd) {
case Get_Encoder_Estimates_msg_t::cmd_id: {
if (Get_Encoder_Estimates_msg_t msg; try_decode(msg)) {
pos_estimate_ = msg.Pos_Estimate * (2 * M_PI);
vel_estimate_ = msg.Vel_Estimate * (2 * M_PI);
}
} break;
case Get_Torques_msg_t::cmd_id: {
if (Get_Torques_msg_t msg; try_decode(msg)) {
torque_target_ = msg.Torque_Target;
torque_estimate_ = msg.Torque_Estimate;
}
} break;
// silently ignore unimplemented command IDs
}
}
PLUGINLIB_EXPORT_CLASS(odrive_ros2_control::ODriveHardwareInterface, hardware_interface::SystemInterface)