Skip to content

File gpio_controller.hpp

File List > controls > sae_2025_ws > src > payload > include > payload > gpio_controller.hpp

Go to the documentation of this file

#ifndef GPIO_CONTROLLER_HPP
#define GPIO_CONTROLLER_HPP

#include <pigpiod_if2.h>
#include <rclcpp/rclcpp.hpp>

#include <atomic>
#include <chrono>
#include <memory>
#include <thread>

#include "payload/controller.hpp"
#include "payload/control_math.hpp"
#include "payload/encoder.hpp"
#include "payload/motor.hpp"
#include "payload/payload_parameters.hpp"
#include "payload/servo.hpp"
#include "payload_interfaces/msg/motor_state.hpp"
#include "payload_interfaces/srv/compute_pid_ziegler_nichols.hpp"
#include "payload_interfaces/srv/dead_reckon.hpp"

enum class ControlMode { NORMAL, DEAD_RECKONING };

class GPIOController : public Controller
{
public:
  GPIOController();
  GPIOController(std::unique_ptr<Motor> left_motor, std::unique_ptr<Motor> right_motor);
  ~GPIOController() override;

  void initialize(rclcpp::Node * node) override;
  void drive_command(double linear, double angular) override;
  void servo_command(double degree) override;
  void safe_shutdown() override;

private:
  void compute_pid_zn_callback(
    const std::shared_ptr<payload_interfaces::srv::ComputePidZieglerNichols::Request> request,
    std::shared_ptr<payload_interfaces::srv::ComputePidZieglerNichols::Response> response);

  void dead_reckon_callback(
    const std::shared_ptr<payload_interfaces::srv::DeadReckon::Request> request,
    std::shared_ptr<payload_interfaces::srv::DeadReckon::Response> response);

  void control_loop();

  rclcpp::Node * node_ {nullptr};
  bool initialized_ {false};
  int pi_ {-1};

  std::shared_ptr<payload::ParamListener> param_listener_;

  std::unique_ptr<Motor> left_motor_;
  std::unique_ptr<Motor> right_motor_;

  std::atomic<double> cmd_linear_ {0.0};
  std::atomic<double> cmd_angular_ {0.0};
  std::atomic<bool> running_ {false};
  std::atomic<bool> shutdown_requested_ {false};

  std::unique_ptr<QuadratureEncoder> left_encoder_;
  std::unique_ptr<QuadratureEncoder> right_encoder_;

  int64_t prev_left_count_ {0};
  int64_t prev_right_count_ {0};
  std::chrono::steady_clock::time_point prev_loop_time_ {};

  double left_filtered_rpm_ {0.0};
  double right_filtered_rpm_ {0.0};
  payload::control_math::PidState left_pid_state_ {};
  payload::control_math::PidState right_pid_state_ {};

  std::unique_ptr<Servo> servo_;

  std::thread control_thread_;

  // Dead reckoning state
  std::atomic<ControlMode> control_mode_   {ControlMode::NORMAL};
  std::atomic<int64_t> dr_left_goal_   {0};        // absolute encoder count to reach
  std::atomic<int64_t> dr_right_goal_  {0};
  std::atomic<int64_t> dr_left_start_  {0};        // snapshot at service call time
  std::atomic<int64_t> dr_right_start_ {0};
  std::atomic<bool> dr_left_done_   {false};
  std::atomic<bool> dr_right_done_  {false};

  rclcpp::Publisher<payload_interfaces::msg::MotorState>::SharedPtr motor_state_pub_;
  rclcpp::Service<payload_interfaces::srv::ComputePidZieglerNichols>::SharedPtr zn_service_;
  rclcpp::CallbackGroup::SharedPtr dead_reckon_cbg_;
  rclcpp::Service<payload_interfaces::srv::DeadReckon>::SharedPtr dead_reckon_srv_;
};

#endif