diff --git a/odrive_base/include/socket_can.hpp b/odrive_base/include/socket_can.hpp index 35b8c3a..283d340 100644 --- a/odrive_base/include/socket_can.hpp +++ b/odrive_base/include/socket_can.hpp @@ -16,6 +16,9 @@ class SocketCanIntf { bool send_can_frame(const can_frame& frame); bool read_nonblocking(); + + void reset_can_connection_check(); + bool has_can_reconnected(); private: std::string interface_; int socket_id_ = -1; @@ -23,6 +26,8 @@ class SocketCanIntf { EpollEventLoop::EvtId socket_evt_id_; FrameProcessor frame_processor_; bool broken_ = false; + bool can_send_failed_ = false; + bool has_been_can_send_failed_ = false; void on_socket_event(uint32_t mask); void process_can_frame(const can_frame& frame) { diff --git a/odrive_base/src/socket_can.cpp b/odrive_base/src/socket_can.cpp index 643daf7..953b10a 100644 --- a/odrive_base/src/socket_can.cpp +++ b/odrive_base/src/socket_can.cpp @@ -78,8 +78,11 @@ bool SocketCanIntf::send_can_frame(const can_frame& frame) { ssize_t nbytes = write(socket_id_, &frame, sizeof(frame)); if (nbytes == -1) { std::cerr << "Failed to send CAN frame" << std::endl; + can_send_failed_ = true; + has_been_can_send_failed_ = true; return false; } + can_send_failed_ = false; return true; } @@ -135,3 +138,11 @@ bool SocketCanIntf::read_nonblocking() { process_can_frame(frame); return true; } + +void SocketCanIntf::reset_can_connection_check() { + has_been_can_send_failed_ = false; +} + +bool SocketCanIntf::has_can_reconnected() { + return can_send_failed_ == false && has_been_can_send_failed_ == true; +} diff --git a/odrive_botwheel_explorer/description/urdf/diffbot.ros2_control.xacro b/odrive_botwheel_explorer/description/urdf/diffbot.ros2_control.xacro index 2ba4352..f5f93c9 100644 --- a/odrive_botwheel_explorer/description/urdf/diffbot.ros2_control.xacro +++ b/odrive_botwheel_explorer/description/urdf/diffbot.ros2_control.xacro @@ -8,6 +8,7 @@ odrive_ros2_control_plugin/ODriveHardwareInterface can0 + false diff --git a/odrive_ros2_control/src/odrive_hardware_interface.cpp b/odrive_ros2_control/src/odrive_hardware_interface.cpp index 1c4133e..b8eac72 100644 --- a/odrive_ros2_control/src/odrive_hardware_interface.cpp +++ b/odrive_ros2_control/src/odrive_hardware_interface.cpp @@ -1,6 +1,7 @@ #include "can_helpers.hpp" #include "can_simple_messages.hpp" +#include "hardware_interface/lexical_casts.hpp" #include "hardware_interface/system_interface.hpp" #include "hardware_interface/types/hardware_interface_type_values.hpp" #include "odrive_enums.h" @@ -37,6 +38,8 @@ class ODriveHardwareInterface final : public hardware_interface::SystemInterface private: void on_can_msg(const can_frame& frame); + void on_can_reconnect(); + EpollEventLoop event_loop_; std::vector axes_; std::string can_intf_name_; @@ -61,10 +64,10 @@ struct Axis { // 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; + 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; @@ -281,6 +284,10 @@ return_type ODriveHardwareInterface::read(const rclcpp::Time& timestamp, const r } return_type ODriveHardwareInterface::write(const rclcpp::Time&, const rclcpp::Duration&) { + if (hardware_interface::parse_bool(info_.hardware_parameters["can_reconnect"]) && can_intf_.has_can_reconnected()) { + on_can_reconnect(); + } + for (auto& axis : axes_) { // Send the CAN message that fits the set of enabled setpoints if (axis.pos_input_enabled_) { @@ -339,7 +346,55 @@ void Axis::on_can_msg(const rclcpp::Time&, const can_frame& frame) { torque_estimate_ = msg.Torque_Estimate; } } break; - // silently ignore unimplemented command IDs + case Heartbeat_msg_t::cmd_id: { + if (Heartbeat_msg_t msg; try_decode(msg)) { + axis_error_ = msg.Axis_Error; + axis_state_ = msg.Axis_State; + procedure_result_ = msg.Procedure_Result; + trajectory_done_flag_ = msg.Trajectory_Done_Flag; + } + } break; + // silently ignore unimplemented command IDs + } +} + +void ODriveHardwareInterface::on_can_reconnect() { + for (size_t i = 0; i < axes_.size(); ++i) { + Axis& axis = axes_[i]; + if (!(axis.axis_error_ == 0 && axis.axis_state_ == 8 && axis.procedure_result_ == 0 && axis.trajectory_done_flag_ == 1)) { + 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); + } else { + can_intf_.reset_can_connection_check(); + } } }