From 3725ff3814e79fc2056f899a4b483a554dcee977 Mon Sep 17 00:00:00 2001 From: NewtonCrosby <lospugs@> Date: Tue, 5 Dec 2023 10:12:15 -0500 Subject: [PATCH] Added PIDSubsystem to Commands2. robotpy/robotpy-commands-v2#28 --- commands2/pidsubsystem.py | 91 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 commands2/pidsubsystem.py diff --git a/commands2/pidsubsystem.py b/commands2/pidsubsystem.py new file mode 100644 index 00000000..9b0ec3b5 --- /dev/null +++ b/commands2/pidsubsystem.py @@ -0,0 +1,91 @@ +# Copyright (c) FIRST and other WPILib contributors. +# Open Source Software; you can modify and/or share it under the terms of +# the WPILib BSD license file in the root directory of this project. +from __future__ import annotations + +from wpimath.controller import PIDController +from .subsystem import Subsystem + + +class PIDSubsystem(Subsystem): + def __init__(self, controller: PIDController, initial_position: float = 0.0): + """ + Creates a new PIDSubsystem. + + :param controller: The PIDController to use. + :param initial_position: The initial setpoint of the subsystem. + """ + self.m_controller: PIDController = controller + self.setSetpoint(initial_position) + self.addChild("PID Controller", self.m_controller) + self.m_enabled = False + + def periodic(self): + """ + Executes the PID control logic during each periodic update. + + This method is called synchronously from the subsystem's periodic() method. + """ + if self.m_enabled: + self.useOutput( + self.m_controller.calculate(self.getMeasurement()), self.getSetpoint() + ) + + def getController(self) -> PIDController: + """ + Returns the PIDController used by the subsystem. + + :return: The PIDController. + """ + return self.m_controller + + def setSetpoint(self, setpoint: float): + """ + Sets the setpoint for the subsystem. + + :param setpoint: The setpoint for the subsystem. + """ + self.m_controller.setSetpoint(setpoint) + + def getSetpoint(self) -> float: + """ + Returns the current setpoint of the subsystem. + + :return: The current setpoint. + """ + return self.m_controller.getSetpoint() + + def useOutput(self, output: float, setpoint: float): + """ + Uses the output from the PIDController. + + :param output: The output of the PIDController. + :param setpoint: The setpoint of the PIDController (for feedforward). + """ + raise NotImplementedError("Subclasses must implement this method") + + def getMeasurement(self) -> float: + """ + Returns the measurement of the process variable used by the PIDController. + + :return: The measurement of the process variable. + """ + raise NotImplementedError("Subclasses must implement this method") + + def enable(self): + """Enables the PID control. Resets the controller.""" + self.m_enabled = True + self.m_controller.reset() + + def disable(self): + """Disables the PID control. Sets output to zero.""" + self.m_enabled = False + self.useOutput(0, 0) + + def isEnabled(self) -> bool: + """ + Returns whether the controller is enabled. + + :return: Whether the controller is enabled. + """ + return self.m_enabled