Program Listing for File swerve_drive_controller.hpp

Return to documentation for file (include/ffw_swerve_drive_controller/swerve_drive_controller.hpp)

// Copyright 2025 ROBOTIS .co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
 * Author: Geonhee Lee
 */

#ifndef FFW_SWERVE_DRIVE_CONTROLLER__SWERVE_DRIVE_CONTROLLER_HPP_
#define FFW_SWERVE_DRIVE_CONTROLLER__SWERVE_DRIVE_CONTROLLER_HPP_

#include <memory>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <functional>
#include <stdexcept>
#include <algorithm>

#include "controller_interface/controller_interface.hpp"
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
#include "rclcpp_lifecycle/state.hpp"
#include "rclcpp/subscription.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "realtime_tools/realtime_buffer.hpp"
#include "realtime_tools/realtime_publisher.hpp"
#include "hardware_interface/loaned_command_interface.hpp"
#include "hardware_interface/loaned_state_interface.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rclcpp/parameter.hpp"

#include "ffw_swerve_drive_controller/odometry.hpp"
#include "nav_msgs/msg/odometry.hpp"
#include "tf2_ros/transform_broadcaster.h"
#include "tf2_msgs/msg/tf_message.hpp"
#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include "sensor_msgs/msg/joint_state.hpp"

#include "ffw_swerve_drive_controller/marker_visualize.hpp"
#include "ffw_swerve_drive_controller/speed_limiter.hpp"

// auto-generated by generate_parameter_library
#include "ffw_swerve_drive_controller/swerve_drive_controller_parameter.hpp"


namespace ffw_swerve_drive_controller
{

// Define aliases for convenience
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
// Use Twist directly as the command message type
using CmdVelMsg = geometry_msgs::msg::Twist;
using Twist = geometry_msgs::msg::Twist;
using OdomStateMsg = nav_msgs::msg::Odometry;
using TfStateMsg = tf2_msgs::msg::TFMessage;
using OdomStatePublisher = realtime_tools::RealtimePublisher<OdomStateMsg>;
using TfStatePublisher = realtime_tools::RealtimePublisher<TfStateMsg>;

// Enum definitions
enum Rotation { CCW, CW, STOP };

// Structure to hold module information for easier access
struct ModuleHandles
{
  // Use reference_wrapper for safe access in RT loop
  std::reference_wrapper<const hardware_interface::LoanedStateInterface> steering_state_pos;
  std::reference_wrapper<hardware_interface::LoanedCommandInterface> steering_cmd_pos;
  // Wheel state might be needed for odometry (not implemented here)
  std::reference_wrapper<const hardware_interface::LoanedStateInterface> wheel_state_vel;
  std::reference_wrapper<hardware_interface::LoanedCommandInterface> wheel_cmd_vel;
  // Store parameters associated with this module
  double x_offset;
  double y_offset;
  double angle_offset;
  double steering_limit_lower;
  double steering_limit_upper;
};

class SwerveDriveController : public controller_interface::ControllerInterface
{
public:
  SwerveDriveController();

  CallbackReturn on_init() override;

  CallbackReturn on_configure(const rclcpp_lifecycle::State & previous_state) override;

  CallbackReturn on_activate(const rclcpp_lifecycle::State & previous_state) override;

  CallbackReturn on_deactivate(const rclcpp_lifecycle::State & previous_state) override;

  controller_interface::InterfaceConfiguration command_interface_configuration() const override;

  controller_interface::InterfaceConfiguration state_interface_configuration() const override;

  controller_interface::return_type update(
    const rclcpp::Time & time, const rclcpp::Duration & period) override;

  void reference_callback(const std::shared_ptr<CmdVelMsg> msg);

  // Parameters from ROS for swerve drive controller
  std::shared_ptr<ParamListener> param_listener_;
  Params params_;

protected:
  // Parameters
  std::vector<std::string> steering_joint_names_;
  std::vector<std::string> wheel_joint_names_;
  double wheel_radius_;
  std::vector<double> module_x_offsets_;
  std::vector<double> module_y_offsets_;
  std::vector<double> module_angle_offsets_;
  std::vector<double> module_steering_limit_lower_;
  std::vector<double> module_steering_limit_upper_;
  std::vector<double> module_wheel_speed_limit_lower_;
  std::vector<double> module_wheel_speed_limit_upper_;
  bool enabled_steering_flip_;
  bool enabled_steering_angular_velocity_limit_;
  bool enabled_steering_angular_limit_;
  bool enabled_open_loop_;
  uint is_rotation_direction_;
  std::vector<double> previoud_steering_commands_;
  double steering_angular_velocity_limit_;
  double steering_alignment_angle_error_threshold_;
  double steering_alignment_start_angle_error_threshold_;
  double steering_alignment_start_speed_error_threshold_;
  std::string odom_solver_method_str_;

  std::string cmd_vel_topic_;
  bool use_stamped_cmd_vel_;
  double cmd_vel_timeout_;
  rclcpp::Duration ref_timeout_;

  // Store number of modules
  size_t num_modules_ = 0;

  // Realtime buffer for incoming Twist commands
  realtime_tools::RealtimeBuffer<std::shared_ptr<CmdVelMsg>> cmd_vel_buffer_;
  // Subscriber
  rclcpp::Subscription<CmdVelMsg>::SharedPtr cmd_vel_subscriber_ = nullptr;

  // Interface handles organized by module
  std::vector<ModuleHandles> module_handles_;

  // Internal state or variables for control logic
  double target_vx_ = 0.0;
  double target_vy_ = 0.0;
  double target_wz_ = 0.0;
  rclcpp::Time last_cmd_vel_time_;

  // Odometry
  Odometry odometry_;
  rclcpp::Publisher<OdomStateMsg>::SharedPtr odom_s_publisher_ = nullptr;
  std::unique_ptr<OdomStatePublisher> rt_odom_state_publisher_ = nullptr;
  OdomStateMsg odom_msg_;

  // joint commander publisher
  using CommandedJointStatePublisher =
    realtime_tools::RealtimePublisher<sensor_msgs::msg::JointState>;
  rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr commanded_joint_state_publisher_ =
    nullptr;
  std::unique_ptr<CommandedJointStatePublisher> rt_commanded_joint_state_publisher_ = nullptr;
  sensor_msgs::msg::JointState joint_state_msg_;

  // Odometry Parameters
  std::string odom_frame_id_;
  std::string base_frame_id_;
  bool enable_odom_tf_;
  std::vector<double> pose_covariance_diagonal_;
  std::vector<double> twist_covariance_diagonal_;
  int velocity_rolling_window_size_;
  std::string odom_source_;

  // TF Broadcaster
  rclcpp::Publisher<TfStateMsg>::SharedPtr tf_odom_s_publisher_;
  std::unique_ptr<TfStatePublisher> rt_tf_odom_state_publisher_;
  TfStateMsg tf_msg_;

  // Visualization
  bool enable_visualization_;
  std::string visualization_marker_topic_;
  std::unique_ptr<MarkerVisualize> visualizer_ = nullptr;
  double visualization_update_time_;
  rclcpp::Time last_visualization_publish_time_;

  // speed limiters
  bool enabled_speed_limits_;
  bool publish_limited_velocity_;
  std::shared_ptr<rclcpp::Publisher<geometry_msgs::msg::Twist>> limited_velocity_publisher_ =
    nullptr;
  std::shared_ptr<realtime_tools::RealtimePublisher<geometry_msgs::msg::Twist>>
  realtime_limited_velocity_publisher_ =
    nullptr;
  geometry_msgs::msg::Twist limited_velocity_msg_;
  std::queue<Twist> previous_commands_;

  SpeedLimiter limiter_linear_x_;
  SpeedLimiter limiter_linear_y_;
  SpeedLimiter limiter_angular_z_;

  // Flag to check if stopping due to timeout
  bool enable_direct_joint_commands_ = false;
  double wheel_saturation_scale_factor_ = 1.0;
  bool enabled_wheel_saturation_scaling_ = false;

  // Open Loop ctrl
  std::vector<double> previous_wheel_directions_;

  static double normalize_angle(double angle_rad);

  double shortest_angular_distance(double from, double to);

  double normalize_angle_positive(double angle);

  // Define ControllerReferenceMsg alias globally (if needed outside class)
  // using ControllerReferenceMsg = geometry_msgs::msg::Twist;
  // Not strictly needed if CmdVelMsg is used
  // Utility function prototype (global scope)
  void reset_controller_reference_msg(
    const std::shared_ptr<geometry_msgs::msg::Twist> & msg);
};
}  // namespace ffw_swerve_drive_controller

#endif  // FFW_SWERVE_DRIVE_CONTROLLER__SWERVE_DRIVE_CONTROLLER_HPP_