-
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
Conversation
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.
I'm having some gaps in ROS2 understanding to fully assess this PR. What's the behavior of ROS2/ros2_control when you cycle the active state to recover? There are multiple variants I could imagine:
Variant 1
- perform_command_mode_switch() is called to request stopping all interfaces (and thus, by convention of the ODrive hardware interface, put the ODrive to IDLE)
- on_deactivate() is called
- on_activate() is called
- perform_command_mode_switch() is called again to request starting the interfaces again (which, by ODrive convention, enables CLOSED_LOOP_CONTROL again)
Variant 2
- on_deactivate() is called and implicitly also means "stop all interfaces", which by ODrive convention puts the axis to IDLE
- on_activate() is called. All interfaces are still considered stopped.
- perform_command_mode_switch() is called to request starting the interfaces again (which puts the axis to CLOSED_LOOP_CONTROL)
Variant 3
- on_deactivate() is called but it does not implicitly stop any interfaces. Implementers of the hardware interface can decide if motor is stopped, put to IDLE, etc.
- on_activate() is called. Since the interfaces weren't implicitly stopped, the axis is expected to automatically resume operation with the same mode as when on_deactivate() was called.
Could you run a test to verify which of these variants is what's happening?
The link you sent is for managed nodes which may be good for reference, but which I believe does not directly apply here (unless we're expected to know that ros2_control is a managed node and active/inactive statemachine is directly passed through to the hardware interfaces).
Additional high level comments:
- "Restoring communication" is not the right way to think about this, it's rather "restoring CLOSED_LOOP_CONTROL state" (which may have been exited for any reason, including but not limited to communication being interrupted).
- Please rebase such that git history is clean (avoid merges and multiple commits for a single logical change and keep formatting changes in a formatting-only commit)
@@ -138,16 +143,33 @@ 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(). | |||
|
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()
and perform_command_mode_switch()
is called during the initialization in my screenshots.
axis.send(msg); // Set control mode | ||
} | ||
|
||
return any_enabled; |
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.
I think sending the actual state request should also be part of this function
Thank you for the detailed review, @samuelsadok! When cycling the active state of the hardware interface, the behavior aligns with what you described in Variant 3. For more context, There are some screenshots below of our terminal to demonstrate this behavior on our robot. I forgot to add some context, the interfaces and controllers are indeed managed nodes. This source code confirms that For your high-level comments: you’re absolutely correct. I’ll revise the PR description to clarify the focus on restoring the Thanks again for your feedback! Initialization of the interfaces and the controller (showing when
|
I see, that clears things up, thanks for the elaboration. So it seems that the active/inactive state is mostly independent from the "command mode". Extrapolating from this, I would assume that perform_command_mode_switch() could even be called while the hardware interface is inactive. In that case I suggest the following change:
This way, on the first call to on_activate() the CLOSED_LOOP_CONTROL request is deferred to perform_command_mode_switch(), but on a subsequent on_deactivate()/on_activate() cycle, CLOSED_LOOP_CONTROL is requested immediately. At each point we send a consistent set of messages and it also works if perform_command_mode_switch() is called while inactive. |
…d for ROS2 Control cycling
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.
cool looks mostly good now, just some minor comments
control_msg.Control_Mode = CONTROL_MODE_TORQUE_CONTROL; | ||
} else { | ||
RCLCPP_INFO(rclcpp::get_logger("ODriveHardwareInterface"), "No control mode specified"); | ||
state_msg.Axis_Requested_State = AXIS_STATE_IDLE; |
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.
I think it's best to treat this case and the !active_
case the same way, by sending a single IDLE request message. So only send the clear_error_msg
and control_msg
conditionally. The way it's written here, if all interfaces are stopped, it's sending a Set_Controller_Mode_msg_t
with an undefined Control_Mode
.
@@ -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(Axis axis); |
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.
Axis axis
=> const Axis&
(and with that the definition of send()
needs to be changed to const
)
|
||
axis.send(clear_error_msg); | ||
axis.send(state_msg); | ||
axis.send(control_msg); |
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.
control_msg
should be sent before the other two
Similar to this PR, pressing and releasing the estop on our robot causes communication with the hardware interface to be cut off.
Based on my understanding of the lifecycle nodes concept, deactivating and reactivating the hardware should restore CLOSED_LOOP_CONTROL state.
Implementing this functionality would align the hardware interface more closely with the intended behavior of lifecycle nodes.