commands.auto.pid_to_pose

  1# Native imports
  2from math import inf, pi
  3from typing import Callable
  4
  5# Internal imports
  6from config import OperatorRobotConfig
  7from subsystem.drivetrain.swerve_drivetrain import SwerveDrivetrain
  8
  9# Third-party imports
 10from commands2 import Command
 11from wpimath import applyDeadband
 12from wpimath.controller import ProfiledPIDController
 13from wpimath.geometry import Pose2d
 14from wpimath.kinematics import ChassisSpeeds
 15from wpimath.trajectory import TrapezoidProfile
 16
 17
 18class PIDToPose(Command):
 19    def __init__(
 20        self,
 21        drivetrain: SwerveDrivetrain,
 22        target_pose: Callable[[], Pose2d] | None,
 23        translation_pid_config: tuple = OperatorRobotConfig.pid_to_pose_translation_pid_profile,
 24        rotation_pid_config: tuple = OperatorRobotConfig.pid_to_pose_rotation_pid_profile,
 25        setpoint_tolerances: tuple = OperatorRobotConfig.pid_to_pose_setpoint_tolerances
 26    ) -> None:
 27        """
 28        Align to a target pose using PID. In this control system, the objective function is the
 29        absolute error between the current and goal x, y, and omega coordinates, separately. This
 30        objective function is minimized until all errors are within a given tolerance. The decision
 31        variables of the control system are the translational and rotational velocities of the
 32        drivetrain. The contraints are a trapezoidal motion profiles for translational and rotational
 33        motion, where slopes represent max acceleration and the plateaus represent max velocity.
 34
 35        Note that the robot will move in a straight line from its current position to the target
 36        pose and will rotate to the target orientation as quickly as possible.
 37
 38        Args:
 39            drivetrain: the drivetrain subsystem that effects movement from current pose to target pose
 40            target_pose: the desired always-blue position and orientation of the robot
 41            translation_pid_config: the PID and trapezoidal profile constants for translational motion
 42            rotation_pid_config: the PID and trapezoidal profile constants for rotational motion
 43            setpoint_tolerances: the thresholds for x, y, and omega, respectively, below which absolute
 44                positional error is low enough for the robot to be considered arrived at the target pose
 45
 46        Returns:
 47            None: class initialization executed upon construction
 48        """
 49        super().__init__()
 50
 51        self.drivetrain = drivetrain
 52        self.target_pose = target_pose
 53        self.x_translation_pid = ProfiledPIDController(
 54            *translation_pid_config[0:3], TrapezoidProfile.Constraints(*translation_pid_config[3:5])
 55        )
 56        self.y_translation_pid = ProfiledPIDController(
 57            *translation_pid_config[0:3], TrapezoidProfile.Constraints(*translation_pid_config[3:5])
 58        )
 59        self.rotation_pid = ProfiledPIDController(
 60            *rotation_pid_config[0:3], TrapezoidProfile.Constraints(*rotation_pid_config[3:5])
 61        )
 62
 63        self.rotation_pid.enableContinuousInput(-pi, pi)
 64
 65        self.x_translation_pid.setTolerance(setpoint_tolerances[0])
 66        self.y_translation_pid.setTolerance(setpoint_tolerances[1])
 67        self.rotation_pid.setTolerance(setpoint_tolerances[2])
 68
 69        self.addRequirements(self.drivetrain)
 70
 71    def execute(self) -> None:
 72        """
 73        If a target pose is available, run the profiled PID control system to move from the
 74        current pose to the target pose.
 75
 76        Returns:
 77            None: translational and rotational velocities are passed into the drivetrain's drive
 78                method, which runs the motors accordingly
 79        """
 80        if self.target_pose:
 81            current_pose = self.drivetrain.current_pose()
 82            
 83            x_pose_error = self.target_pose().X() - current_pose.X()
 84            y_pose_error = self.target_pose().Y() - current_pose.Y()
 85            rotation_error = (self.target_pose().rotation() - current_pose.rotation()).radians()
 86
 87            x_output = -applyDeadband(self.x_translation_pid.calculate(x_pose_error, 0), 0.04, inf)
 88            y_output = -applyDeadband(self.y_translation_pid.calculate(y_pose_error, 0), 0.04, inf)
 89            rotation_output = -applyDeadband(self.rotation_pid.calculate(rotation_error, 0), 0.04, inf)
 90
 91            drive_speed = ChassisSpeeds.fromFieldRelativeSpeeds(x_output, y_output, rotation_output, current_pose.rotation())
 92            self.drivetrain.drive(drive_speed.vx, drive_speed.vy, drive_speed.omega, False)
 93
 94    def end(self, interrupted: bool) -> None:
 95        """
 96        When this command is over, have the robot stop driving (set all velocities to zero).
 97
 98        Args:
 99            interrupted: whether the command was terminated by way of interruption
100
101        Returns:
102            None: drivetrain subsystem will apply zeroed velocities to motors
103        """
104        self.drivetrain.stop_driving(apply_to_modules=True)
105
106    def isFinished(self) -> bool:
107        """
108        The command is considered complete when the absolute error of each coodinate is within tolerance.
109        If there is no target pose for the robot to navigate to, this command instantly ends.
110
111        Returns:
112            True if the command is complete according to the definition above, False otherwise.
113        """
114        at_setpoints = self.x_translation_pid.atSetpoint() and self.y_translation_pid.atSetpoint() and self.rotation_pid.atSetpoint()
115        no_target_pose = True if not self.target_pose else False
116        return at_setpoints or no_target_pose
class PIDToPose(commands2.command.Command):
 19class PIDToPose(Command):
 20    def __init__(
 21        self,
 22        drivetrain: SwerveDrivetrain,
 23        target_pose: Callable[[], Pose2d] | None,
 24        translation_pid_config: tuple = OperatorRobotConfig.pid_to_pose_translation_pid_profile,
 25        rotation_pid_config: tuple = OperatorRobotConfig.pid_to_pose_rotation_pid_profile,
 26        setpoint_tolerances: tuple = OperatorRobotConfig.pid_to_pose_setpoint_tolerances
 27    ) -> None:
 28        """
 29        Align to a target pose using PID. In this control system, the objective function is the
 30        absolute error between the current and goal x, y, and omega coordinates, separately. This
 31        objective function is minimized until all errors are within a given tolerance. The decision
 32        variables of the control system are the translational and rotational velocities of the
 33        drivetrain. The contraints are a trapezoidal motion profiles for translational and rotational
 34        motion, where slopes represent max acceleration and the plateaus represent max velocity.
 35
 36        Note that the robot will move in a straight line from its current position to the target
 37        pose and will rotate to the target orientation as quickly as possible.
 38
 39        Args:
 40            drivetrain: the drivetrain subsystem that effects movement from current pose to target pose
 41            target_pose: the desired always-blue position and orientation of the robot
 42            translation_pid_config: the PID and trapezoidal profile constants for translational motion
 43            rotation_pid_config: the PID and trapezoidal profile constants for rotational motion
 44            setpoint_tolerances: the thresholds for x, y, and omega, respectively, below which absolute
 45                positional error is low enough for the robot to be considered arrived at the target pose
 46
 47        Returns:
 48            None: class initialization executed upon construction
 49        """
 50        super().__init__()
 51
 52        self.drivetrain = drivetrain
 53        self.target_pose = target_pose
 54        self.x_translation_pid = ProfiledPIDController(
 55            *translation_pid_config[0:3], TrapezoidProfile.Constraints(*translation_pid_config[3:5])
 56        )
 57        self.y_translation_pid = ProfiledPIDController(
 58            *translation_pid_config[0:3], TrapezoidProfile.Constraints(*translation_pid_config[3:5])
 59        )
 60        self.rotation_pid = ProfiledPIDController(
 61            *rotation_pid_config[0:3], TrapezoidProfile.Constraints(*rotation_pid_config[3:5])
 62        )
 63
 64        self.rotation_pid.enableContinuousInput(-pi, pi)
 65
 66        self.x_translation_pid.setTolerance(setpoint_tolerances[0])
 67        self.y_translation_pid.setTolerance(setpoint_tolerances[1])
 68        self.rotation_pid.setTolerance(setpoint_tolerances[2])
 69
 70        self.addRequirements(self.drivetrain)
 71
 72    def execute(self) -> None:
 73        """
 74        If a target pose is available, run the profiled PID control system to move from the
 75        current pose to the target pose.
 76
 77        Returns:
 78            None: translational and rotational velocities are passed into the drivetrain's drive
 79                method, which runs the motors accordingly
 80        """
 81        if self.target_pose:
 82            current_pose = self.drivetrain.current_pose()
 83            
 84            x_pose_error = self.target_pose().X() - current_pose.X()
 85            y_pose_error = self.target_pose().Y() - current_pose.Y()
 86            rotation_error = (self.target_pose().rotation() - current_pose.rotation()).radians()
 87
 88            x_output = -applyDeadband(self.x_translation_pid.calculate(x_pose_error, 0), 0.04, inf)
 89            y_output = -applyDeadband(self.y_translation_pid.calculate(y_pose_error, 0), 0.04, inf)
 90            rotation_output = -applyDeadband(self.rotation_pid.calculate(rotation_error, 0), 0.04, inf)
 91
 92            drive_speed = ChassisSpeeds.fromFieldRelativeSpeeds(x_output, y_output, rotation_output, current_pose.rotation())
 93            self.drivetrain.drive(drive_speed.vx, drive_speed.vy, drive_speed.omega, False)
 94
 95    def end(self, interrupted: bool) -> None:
 96        """
 97        When this command is over, have the robot stop driving (set all velocities to zero).
 98
 99        Args:
100            interrupted: whether the command was terminated by way of interruption
101
102        Returns:
103            None: drivetrain subsystem will apply zeroed velocities to motors
104        """
105        self.drivetrain.stop_driving(apply_to_modules=True)
106
107    def isFinished(self) -> bool:
108        """
109        The command is considered complete when the absolute error of each coodinate is within tolerance.
110        If there is no target pose for the robot to navigate to, this command instantly ends.
111
112        Returns:
113            True if the command is complete according to the definition above, False otherwise.
114        """
115        at_setpoints = self.x_translation_pid.atSetpoint() and self.y_translation_pid.atSetpoint() and self.rotation_pid.atSetpoint()
116        no_target_pose = True if not self.target_pose else False
117        return at_setpoints or no_target_pose

A state machine representing a complete action to be performed by the robot. Commands are run by the commands2.CommandScheduler, and can be composed into CommandGroups to allow users to build complicated multistep actions without the need to roll the state machine logic themselves.

Commands are run synchronously from the main robot loop; no multithreading is used, unless specified explicitly from the command implementation.

PIDToPose( drivetrain: subsystem.drivetrain.swerve_drivetrain.SwerveDrivetrain, target_pose: Callable[[], wpimath.geometry._geometry.Pose2d] | None, translation_pid_config: tuple = (2.0, 0.0, 0.0, 3, 1.5), rotation_pid_config: tuple = (1.0, 0.0, 0.0, 6.283185307179586, 6.283185307179586), setpoint_tolerances: tuple = (0.25, 0.25, 0.17453292519943295))
20    def __init__(
21        self,
22        drivetrain: SwerveDrivetrain,
23        target_pose: Callable[[], Pose2d] | None,
24        translation_pid_config: tuple = OperatorRobotConfig.pid_to_pose_translation_pid_profile,
25        rotation_pid_config: tuple = OperatorRobotConfig.pid_to_pose_rotation_pid_profile,
26        setpoint_tolerances: tuple = OperatorRobotConfig.pid_to_pose_setpoint_tolerances
27    ) -> None:
28        """
29        Align to a target pose using PID. In this control system, the objective function is the
30        absolute error between the current and goal x, y, and omega coordinates, separately. This
31        objective function is minimized until all errors are within a given tolerance. The decision
32        variables of the control system are the translational and rotational velocities of the
33        drivetrain. The contraints are a trapezoidal motion profiles for translational and rotational
34        motion, where slopes represent max acceleration and the plateaus represent max velocity.
35
36        Note that the robot will move in a straight line from its current position to the target
37        pose and will rotate to the target orientation as quickly as possible.
38
39        Args:
40            drivetrain: the drivetrain subsystem that effects movement from current pose to target pose
41            target_pose: the desired always-blue position and orientation of the robot
42            translation_pid_config: the PID and trapezoidal profile constants for translational motion
43            rotation_pid_config: the PID and trapezoidal profile constants for rotational motion
44            setpoint_tolerances: the thresholds for x, y, and omega, respectively, below which absolute
45                positional error is low enough for the robot to be considered arrived at the target pose
46
47        Returns:
48            None: class initialization executed upon construction
49        """
50        super().__init__()
51
52        self.drivetrain = drivetrain
53        self.target_pose = target_pose
54        self.x_translation_pid = ProfiledPIDController(
55            *translation_pid_config[0:3], TrapezoidProfile.Constraints(*translation_pid_config[3:5])
56        )
57        self.y_translation_pid = ProfiledPIDController(
58            *translation_pid_config[0:3], TrapezoidProfile.Constraints(*translation_pid_config[3:5])
59        )
60        self.rotation_pid = ProfiledPIDController(
61            *rotation_pid_config[0:3], TrapezoidProfile.Constraints(*rotation_pid_config[3:5])
62        )
63
64        self.rotation_pid.enableContinuousInput(-pi, pi)
65
66        self.x_translation_pid.setTolerance(setpoint_tolerances[0])
67        self.y_translation_pid.setTolerance(setpoint_tolerances[1])
68        self.rotation_pid.setTolerance(setpoint_tolerances[2])
69
70        self.addRequirements(self.drivetrain)

Align to a target pose using PID. In this control system, the objective function is the absolute error between the current and goal x, y, and omega coordinates, separately. This objective function is minimized until all errors are within a given tolerance. The decision variables of the control system are the translational and rotational velocities of the drivetrain. The contraints are a trapezoidal motion profiles for translational and rotational motion, where slopes represent max acceleration and the plateaus represent max velocity.

Note that the robot will move in a straight line from its current position to the target pose and will rotate to the target orientation as quickly as possible.

Args: drivetrain: the drivetrain subsystem that effects movement from current pose to target pose target_pose: the desired always-blue position and orientation of the robot translation_pid_config: the PID and trapezoidal profile constants for translational motion rotation_pid_config: the PID and trapezoidal profile constants for rotational motion setpoint_tolerances: the thresholds for x, y, and omega, respectively, below which absolute positional error is low enough for the robot to be considered arrived at the target pose

Returns: None: class initialization executed upon construction

drivetrain
target_pose
x_translation_pid
y_translation_pid
rotation_pid
def execute(self) -> None:
72    def execute(self) -> None:
73        """
74        If a target pose is available, run the profiled PID control system to move from the
75        current pose to the target pose.
76
77        Returns:
78            None: translational and rotational velocities are passed into the drivetrain's drive
79                method, which runs the motors accordingly
80        """
81        if self.target_pose:
82            current_pose = self.drivetrain.current_pose()
83            
84            x_pose_error = self.target_pose().X() - current_pose.X()
85            y_pose_error = self.target_pose().Y() - current_pose.Y()
86            rotation_error = (self.target_pose().rotation() - current_pose.rotation()).radians()
87
88            x_output = -applyDeadband(self.x_translation_pid.calculate(x_pose_error, 0), 0.04, inf)
89            y_output = -applyDeadband(self.y_translation_pid.calculate(y_pose_error, 0), 0.04, inf)
90            rotation_output = -applyDeadband(self.rotation_pid.calculate(rotation_error, 0), 0.04, inf)
91
92            drive_speed = ChassisSpeeds.fromFieldRelativeSpeeds(x_output, y_output, rotation_output, current_pose.rotation())
93            self.drivetrain.drive(drive_speed.vx, drive_speed.vy, drive_speed.omega, False)

If a target pose is available, run the profiled PID control system to move from the current pose to the target pose.

Returns: None: translational and rotational velocities are passed into the drivetrain's drive method, which runs the motors accordingly

def end(self, interrupted: bool) -> None:
 95    def end(self, interrupted: bool) -> None:
 96        """
 97        When this command is over, have the robot stop driving (set all velocities to zero).
 98
 99        Args:
100            interrupted: whether the command was terminated by way of interruption
101
102        Returns:
103            None: drivetrain subsystem will apply zeroed velocities to motors
104        """
105        self.drivetrain.stop_driving(apply_to_modules=True)

When this command is over, have the robot stop driving (set all velocities to zero).

Args: interrupted: whether the command was terminated by way of interruption

Returns: None: drivetrain subsystem will apply zeroed velocities to motors

def isFinished(self) -> bool:
107    def isFinished(self) -> bool:
108        """
109        The command is considered complete when the absolute error of each coodinate is within tolerance.
110        If there is no target pose for the robot to navigate to, this command instantly ends.
111
112        Returns:
113            True if the command is complete according to the definition above, False otherwise.
114        """
115        at_setpoints = self.x_translation_pid.atSetpoint() and self.y_translation_pid.atSetpoint() and self.rotation_pid.atSetpoint()
116        no_target_pose = True if not self.target_pose else False
117        return at_setpoints or no_target_pose

The command is considered complete when the absolute error of each coodinate is within tolerance. If there is no target pose for the robot to navigate to, this command instantly ends.

Returns: True if the command is complete according to the definition above, False otherwise.