-
Notifications
You must be signed in to change notification settings - Fork 40
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
feat: Using lifecycle nodes to their full potential #35
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,7 +36,9 @@ class ODriveHardwareInterface final : public hardware_interface::SystemInterface | |
|
||
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_; | ||
|
@@ -88,7 +90,7 @@ struct Axis { | |
bool torque_input_enabled_ = false; | ||
|
||
template <typename T> | ||
void send(const T& msg) { | ||
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; | ||
|
@@ -121,7 +123,11 @@ CallbackReturn ODriveHardwareInterface::on_init(const hardware_interface::Hardwa | |
|
||
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()); | ||
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()); | ||
|
@@ -138,16 +144,21 @@ CallbackReturn ODriveHardwareInterface::on_activate(const State&) { | |
|
||
// 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_State_msg_t msg; | ||
msg.Axis_Requested_State = AXIS_STATE_IDLE; | ||
axis.send(msg); | ||
set_axis_command_mode(axis); | ||
} | ||
|
||
return CallbackReturn::SUCCESS; | ||
|
@@ -210,8 +221,7 @@ return_type ODriveHardwareInterface::perform_command_mode_switch( | |
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_}} | ||
}; | ||
{info_.joints[i].name + "/" + hardware_interface::HW_IF_EFFORT, &axis.torque_input_enabled_}}}; | ||
|
||
bool mode_switch = false; | ||
|
||
|
@@ -234,36 +244,7 @@ return_type ODriveHardwareInterface::perform_command_mode_switch( | |
} | ||
|
||
if (mode_switch) { | ||
Set_Controller_Mode_msg_t msg; | ||
if (axis.pos_input_enabled_) { | ||
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Setting %s to position control", info_.joints[i].name.c_str()); | ||
msg.Control_Mode = CONTROL_MODE_POSITION_CONTROL; | ||
msg.Input_Mode = INPUT_MODE_PASSTHROUGH; | ||
} else if (axis.vel_input_enabled_) { | ||
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Setting %s to velocity control", info_.joints[i].name.c_str()); | ||
msg.Control_Mode = CONTROL_MODE_VELOCITY_CONTROL; | ||
msg.Input_Mode = INPUT_MODE_PASSTHROUGH; | ||
} else { | ||
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "Setting %s to torque control", info_.joints[i].name.c_str()); | ||
msg.Control_Mode = CONTROL_MODE_TORQUE_CONTROL; | ||
msg.Input_Mode = INPUT_MODE_PASSTHROUGH; | ||
} | ||
|
||
bool any_enabled = axis.pos_input_enabled_ || axis.vel_input_enabled_ || axis.torque_input_enabled_; | ||
|
||
if (any_enabled) { | ||
axis.send(msg); // Set control mode | ||
} | ||
|
||
// Set axis state | ||
Clear_Errors_msg_t msg1; | ||
msg1.Identify = 0; | ||
axis.send(msg1); | ||
|
||
// Set axis state | ||
Set_Axis_State_msg_t msg2; | ||
msg2.Axis_Requested_State = any_enabled ? AXIS_STATE_CLOSED_LOOP_CONTROL : AXIS_STATE_IDLE; | ||
axis.send(msg2); | ||
set_axis_command_mode(axis); | ||
} | ||
} | ||
|
||
|
@@ -286,7 +267,7 @@ return_type ODriveHardwareInterface::write(const rclcpp::Time&, const rclcpp::Du | |
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.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_) { | ||
|
@@ -314,6 +295,44 @@ void ODriveHardwareInterface::on_can_msg(const can_frame& 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's best to treat this case and the |
||
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; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment above suggests that the axis should not be put to CLOSED_LOOP_CONTROL in here. But I need to fill in the gap in understanding to know what's the best thing to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it helps, I show when
on_activate()
andperform_command_mode_switch()
is called during the initialization in my screenshots.