Skip to content

Commit 770d22f

Browse files
authored
Merge pull request #53 from robotpy/profiled-pid-command
Add ProfiledPIDCommand
2 parents a8de5bb + 2d75059 commit 770d22f

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

commands2/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .pidcommand import PIDCommand
1717
from .pidsubsystem import PIDSubsystem
1818
from .printcommand import PrintCommand
19+
from .profiledpidcommand import ProfiledPIDCommand
1920
from .profiledpidsubsystem import ProfiledPIDSubsystem
2021
from .proxycommand import ProxyCommand
2122
from .repeatcommand import RepeatCommand
@@ -53,6 +54,7 @@
5354
"PIDCommand",
5455
"PIDSubsystem",
5556
"PrintCommand",
57+
"ProfiledPIDCommand",
5658
"ProfiledPIDSubsystem",
5759
"ProxyCommand",
5860
"RepeatCommand",

commands2/profiledpidcommand.py

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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

Comments
 (0)