|  | 
|  | 1 | +# validated: 2024-01-24 DS f29a7d2e501b ProfiledPIDCommand.java | 
|  | 2 | +# | 
|  | 3 | +# Copyright (c) FIRST and other WPILib contributors. | 
|  | 4 | +# Open Source Software; you can modify and/or share it under the terms of | 
|  | 5 | +# the WPILib BSD license file in the root directory of this project. | 
|  | 6 | +# | 
|  | 7 | + | 
|  | 8 | +from typing import Any, Callable, Union | 
|  | 9 | + | 
|  | 10 | +from .command import Command | 
|  | 11 | +from .subsystem import Subsystem | 
|  | 12 | + | 
|  | 13 | +from wpimath.controller import ProfiledPIDController, ProfiledPIDControllerRadians | 
|  | 14 | +from wpimath.trajectory import TrapezoidProfile, TrapezoidProfileRadians | 
|  | 15 | + | 
|  | 16 | + | 
|  | 17 | +class ProfiledPIDCommand(Command): | 
|  | 18 | +    """A command that controls an output with a :class:`.ProfiledPIDController`. Runs forever by default - | 
|  | 19 | +    to add exit conditions and/or other behavior, subclass this class. The controller calculation and | 
|  | 20 | +    output are performed synchronously in the command's execute() method. | 
|  | 21 | +    """ | 
|  | 22 | + | 
|  | 23 | +    _stateCls: Any | 
|  | 24 | + | 
|  | 25 | +    def __init__( | 
|  | 26 | +        self, | 
|  | 27 | +        controller, | 
|  | 28 | +        measurementSource: Callable[[], float], | 
|  | 29 | +        goalSource: Union[float, Callable[[], float]], | 
|  | 30 | +        useOutput: Callable[[float, Any], Any], | 
|  | 31 | +        *requirements: Subsystem, | 
|  | 32 | +    ): | 
|  | 33 | +        """Creates a new ProfiledPIDCommand, which controls the given output with a ProfiledPIDController. Goal | 
|  | 34 | +        velocity is specified. | 
|  | 35 | +
 | 
|  | 36 | +        :param controller:        the controller that controls the output. | 
|  | 37 | +        :param measurementSource: the measurement of the process variable | 
|  | 38 | +        :param goalSource:        the controller's goal | 
|  | 39 | +        :param useOutput:         the controller's output | 
|  | 40 | +        :param requirements:      the subsystems required by this command | 
|  | 41 | +        """ | 
|  | 42 | + | 
|  | 43 | +        if isinstance(controller, ProfiledPIDController): | 
|  | 44 | +            self._stateCls = TrapezoidProfile.State | 
|  | 45 | +        elif isinstance(controller, ProfiledPIDControllerRadians): | 
|  | 46 | +            self._stateCls = TrapezoidProfileRadians.State | 
|  | 47 | +        else: | 
|  | 48 | +            raise ValueError(f"unknown controller type {controller!r}") | 
|  | 49 | + | 
|  | 50 | +        self._controller = controller | 
|  | 51 | +        self._useOutput = useOutput | 
|  | 52 | +        self._measurement = measurementSource | 
|  | 53 | +        if isinstance(goalSource, (float, int)): | 
|  | 54 | +            self._goal = lambda: float(goalSource) | 
|  | 55 | +        else: | 
|  | 56 | +            self._goal = goalSource | 
|  | 57 | + | 
|  | 58 | +        self.addRequirements(*requirements) | 
|  | 59 | + | 
|  | 60 | +    def initialize(self): | 
|  | 61 | +        self._controller.reset(self._measurement()) | 
|  | 62 | + | 
|  | 63 | +    def execute(self): | 
|  | 64 | +        self._useOutput.accept( | 
|  | 65 | +            self._controller.calculate(self._measurement(), self._goal()), | 
|  | 66 | +            self._controller.getSetpoint(), | 
|  | 67 | +        ) | 
|  | 68 | + | 
|  | 69 | +    def end(self, interrupted: bool): | 
|  | 70 | +        self._useOutput(0.0, self._stateCls()) | 
|  | 71 | + | 
|  | 72 | +    def getController(self): | 
|  | 73 | +        """Gets the controller used by the command""" | 
|  | 74 | +        return self._controller | 
0 commit comments