Skip to content

Commit 319caaa

Browse files
Add gyro-drive-commands example (#63)
* added elevator trapezoid profile example * updated tests * No self parameter in setSetPoint function * format with black * license and enviroment stuff * Add gyro-drive-commands example * format with black
1 parent ea8a5c0 commit 319caaa

File tree

7 files changed

+488
-0
lines changed

7 files changed

+488
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright (c) FIRST and other WPILib contributors.
2+
# Open Source Software; you can modify and/or share it under the terms of
3+
# the WPILib BSD license file in the root directory of this project.
4+
5+
import wpilib
6+
import commands2
7+
import commands2.cmd
8+
import wpimath.controller
9+
10+
from subsystems.drivesubsystem import DriveSubsystem
11+
12+
import constants
13+
14+
15+
class TurnToAngle(commands2.PIDCommand):
16+
"""A command that will turn the robot to the specified angle."""
17+
18+
def __init__(self, targetAngleDegrees: float, drive: DriveSubsystem) -> None:
19+
"""
20+
Turns to robot to the specified angle.
21+
22+
:param: targetAngleDegrees The angle to turn to
23+
:param: drive The drive subsystem to
24+
"""
25+
super().__init__(
26+
wpimath.controller.PIDController(
27+
constants.DriveConstants.kTurnP,
28+
constants.DriveConstants.kTurnI,
29+
constants.DriveConstants.kTurnD,
30+
),
31+
# Close loop on heading
32+
drive.getHeading,
33+
# Set reference to target
34+
targetAngleDegrees,
35+
# Pipe output to turn robot
36+
lambda output: drive.arcadeDrive(0, output),
37+
# Require the drive
38+
[drive],
39+
)
40+
41+
# Set the controller to be continuous (because it is an angle controller)
42+
self.getController().enableContinuousInput(-180, 180)
43+
44+
# Set the controller tolerance - the delta tolerance ensures the robot is stationary at the
45+
# setpoint before it is considered as having reached the reference
46+
self.getController().setTolerance(
47+
constants.DriveConstants.kTurnToleranceDeg,
48+
constants.DriveConstants.kTurnRateToleranceDegPerS,
49+
)
50+
51+
def isFinished(self) -> bool:
52+
# End when the controller is at the reference.
53+
return self.getController().atSetpoint()
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) FIRST and other WPILib contributors.
2+
# Open Source Software; you can modify and/or share it under the terms of
3+
# the WPILib BSD license file in the root directory of this project.
4+
5+
import wpilib
6+
import commands2
7+
import commands2.cmd
8+
import wpimath.controller
9+
import wpimath.trajectory
10+
11+
from subsystems.drivesubsystem import DriveSubsystem
12+
13+
import constants
14+
15+
16+
class TurnToAngleProfiled(commands2.ProfiledPIDCommand):
17+
"""A command that will turn the robot to the specified angle using a motion profile."""
18+
19+
def __init__(self, targetAngleDegrees: float, drive: DriveSubsystem) -> None:
20+
"""
21+
Turns to robot to the specified angle.
22+
23+
:param: targetAngleDegrees The angle to turn to
24+
:param: drive The drive subsystem to
25+
"""
26+
super().__init__(
27+
wpimath.controller.ProfiledPIDController(
28+
constants.DriveConstants.kTurnP,
29+
constants.DriveConstants.kTurnI,
30+
constants.DriveConstants.kTurnD,
31+
wpimath.trajectory.TrapezoidProfile.Constraints(
32+
constants.DriveConstants.kMaxTurnRateDegPerS,
33+
constants.DriveConstants.kMaxTurnAccelerationDegPerSSquared,
34+
),
35+
),
36+
# Close loop on heading
37+
drive.getHeading,
38+
# Set reference to target
39+
targetAngleDegrees,
40+
# Pipe output to turn robot
41+
lambda output, setpoint: drive.arcadeDrive(0, output),
42+
# Require the drive
43+
[drive],
44+
)
45+
46+
# Set the controller to be continuous (because it is an angle controller)
47+
self.getController().enableContinuousInput(-180, 180)
48+
49+
# Set the controller tolerance - the delta tolerance ensures the robot is stationary at the
50+
# setpoint before it is considered as having reached the reference
51+
self.getController().setTolerance(
52+
constants.DriveConstants.kTurnToleranceDeg,
53+
constants.DriveConstants.kTurnRateToleranceDegPerS,
54+
)
55+
56+
def isFinished(self) -> bool:
57+
# End when the controller is at the reference.
58+
return self.getController().atSetpoint()
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright (c) FIRST and other WPILib contributors.
2+
# Open Source Software; you can modify and/or share it under the terms of
3+
# the WPILib BSD license file in the root directory of this project.
4+
5+
"""
6+
A place for the constant values in the code that may be used in more than one place.
7+
This offers a convenient resources to teams who need to make both quick and universal
8+
changes.
9+
"""
10+
11+
import math
12+
13+
14+
class DriveConstants:
15+
kLeftMotor1Port = 0
16+
kLeftMotor2Port = 1
17+
kRightMotor1Port = 2
18+
kRightMotor2Port = 3
19+
20+
kLeftEncoderPorts = (0, 1)
21+
kRightEncoderPorts = (2, 3)
22+
kLeftEncoderReversed = False
23+
kRightEncoderReversed = True
24+
25+
kEncoderCPR = 1024
26+
kWheelDiameterInches = 6
27+
28+
# Assumes the encoders are directly mounted on the wheel shafts
29+
kEncoderDistancePerPulse = (kWheelDiameterInches * math.pi) / kEncoderCPR
30+
31+
kGyroReversed = False
32+
33+
kStabilizationP = 1
34+
kStabilizationI = 0.5
35+
kStabilizationD = 0
36+
37+
kTurnP = 1
38+
kTurnI = 0
39+
kTurnD = 0
40+
41+
kMaxTurnRateDegPerS = 100
42+
kMaxTurnAccelerationDegPerSSquared = 300
43+
44+
kTurnToleranceDeg = 5
45+
kTurnRateToleranceDegPerS = 10 # degrees per second
46+
47+
48+
class OIConstants:
49+
kDriverControllerPort = 0
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env python3
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+
import typing
9+
10+
import wpilib
11+
import commands2
12+
import commands2.cmd
13+
14+
import robotcontainer
15+
16+
"""
17+
The VM is configured to automatically run this class, and to call the functions corresponding to
18+
each mode, as described in the TimedRobot documentation. If you change the name of this class or
19+
the package after creating this project, you must also update the build.gradle file in the
20+
project.
21+
"""
22+
23+
24+
class MyRobot(commands2.TimedCommandRobot):
25+
"""
26+
Our default robot class, pass it to wpilib.run
27+
Command v2 robots are encouraged to inherit from TimedCommandRobot, which
28+
has an implementation of robotPeriodic which runs the scheduler for you
29+
"""
30+
31+
def robotInit(self) -> None:
32+
"""
33+
This function is run when the robot is first started up and should be used for any
34+
initialization code.
35+
"""
36+
self.autonomousCommand: typing.Optional[commands2.Command] = None
37+
38+
# Instantiate our RobotContainer. This will perform all our button bindings, and put our
39+
# autonomous chooser on the dashboard.
40+
self.container = robotcontainer.RobotContainer()
41+
42+
def disabledInit(self) -> None:
43+
"""This function is called once each time the robot enters Disabled mode."""
44+
45+
def disabledPeriodic(self) -> None:
46+
"""This function is called periodically when disabled"""
47+
48+
def autonomousInit(self) -> None:
49+
"""This autonomous runs the autonomous command selected by your RobotContainer class."""
50+
self.autonomousCommand = self.container.getAutonomousCommand()
51+
52+
# schedule the autonomous command (example)
53+
if self.autonomousCommand is not None:
54+
self.autonomousCommand.schedule()
55+
else:
56+
print("no auto command?")
57+
58+
def autonomousPeriodic(self) -> None:
59+
"""This function is called periodically during autonomous"""
60+
61+
def teleopInit(self) -> None:
62+
# This makes sure that the autonomous stops running when
63+
# teleop starts running. If you want the autonomous to
64+
# continue until interrupted by another command, remove
65+
# this line or comment it out.
66+
if self.autonomousCommand is not None:
67+
self.autonomousCommand.cancel()
68+
69+
def teleopPeriodic(self) -> None:
70+
"""This function is called periodically during operator control"""
71+
72+
def testInit(self) -> None:
73+
# Cancels all running commands at the start of test mode
74+
commands2.CommandScheduler.getInstance().cancelAll()
75+
76+
77+
if __name__ == "__main__":
78+
wpilib.run(MyRobot)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Copyright (c) FIRST and other WPILib contributors.
2+
# Open Source Software; you can modify and/or share it under the terms of
3+
# the WPILib BSD license file in the root directory of this project.
4+
5+
import wpilib
6+
import wpimath.controller
7+
8+
import commands2
9+
import commands2.cmd
10+
import commands2.button
11+
12+
import constants
13+
import subsystems.drivesubsystem
14+
import commands.turntoangle
15+
import commands.turntoangleprofiled
16+
17+
18+
class RobotContainer:
19+
"""
20+
This class is where the bulk of the robot should be declared. Since Command-based is a
21+
"declarative" paradigm, very little robot logic should actually be handled in the :class:`.Robot`
22+
periodic methods (other than the scheduler calls). Instead, the structure of the robot (including
23+
subsystems, commands, and button mappings) should be declared here.
24+
25+
"""
26+
27+
def __init__(self):
28+
"""The container for the robot. Contains subsystems, OI devices, and commands."""
29+
# The robot's subsystems
30+
self.robotDrive = subsystems.drivesubsystem.DriveSubsystem()
31+
32+
# The driver's controller
33+
self.driverController = commands2.button.CommandPS4Controller(
34+
constants.OIConstants.kDriverControllerPort
35+
)
36+
37+
# Configure the button bindings
38+
self.configureButtonBindings()
39+
40+
# Configure default commands
41+
# Set the default drive command to split-stick arcade drive
42+
self.robotDrive.setDefaultCommand(
43+
# A split-stick arcade command, with forward/backward controlled by the left
44+
# hand, and turning controlled by the right.
45+
commands2.RunCommand(
46+
lambda: self.robotDrive.arcadeDrive(
47+
-self.driverController.getLeftY(),
48+
-self.driverController.getRightX(),
49+
),
50+
[self.robotDrive],
51+
)
52+
)
53+
54+
def configureButtonBindings(self):
55+
"""
56+
Use this method to define your button->command mappings. Buttons can be created via the button
57+
factories on commands2.button.CommandGenericHID or one of its
58+
subclasses (commands2.button.CommandJoystick or command2.button.CommandXboxController).
59+
"""
60+
# Drive at half speed when the right bumper is held
61+
commands2.button.JoystickButton(
62+
self.driverController, wpilib.PS4Controller.Button.kR1
63+
).onTrue(
64+
commands2.InstantCommand(
65+
(lambda: self.robotDrive.setMaxOutput(0.5)), [self.robotDrive]
66+
)
67+
).onFalse(
68+
commands2.InstantCommand(
69+
(lambda: self.robotDrive.setMaxOutput(1)), [self.robotDrive]
70+
)
71+
)
72+
73+
# Stabilize robot to drive straight with gyro when left bumper is held
74+
commands2.button.JoystickButton(
75+
self.driverController, wpilib.PS4Controller.Button.kL1
76+
).whileTrue(
77+
commands2.PIDCommand(
78+
wpimath.controller.PIDController(
79+
constants.DriveConstants.kStabilizationP,
80+
constants.DriveConstants.kStabilizationI,
81+
constants.DriveConstants.kStabilizationD,
82+
),
83+
# Close the loop on the turn rate
84+
self.robotDrive.getTurnRate,
85+
# Setpoint is 0
86+
0,
87+
# Pipe the output to the turning controls
88+
lambda output: self.robotDrive.arcadeDrive(
89+
-self.driverController.getLeftY(), output
90+
),
91+
# Require the robot drive
92+
[self.robotDrive],
93+
)
94+
)
95+
96+
# Turn to 90 degrees when the 'X' button is pressed, with a 5 second timeout
97+
commands2.button.JoystickButton(
98+
self.driverController, wpilib.PS4Controller.Button.kCross
99+
).onTrue(commands.turntoangle.TurnToAngle(90, self.robotDrive).withTimeout(5))
100+
101+
# Turn to -90 degrees with a profile when the Circle button is pressed, with a 5 second timeout
102+
commands2.button.JoystickButton(
103+
self.driverController, wpilib.PS4Controller.Button.kCircle
104+
).onTrue(
105+
commands.turntoangleprofiled.TurnToAngleProfiled(
106+
-90, self.robotDrive
107+
).withTimeout(5)
108+
)
109+
110+
def getAutonomousCommand(self) -> commands2.Command:
111+
"""
112+
Use this to pass the autonomous command to the main :class:`.Robot` class.
113+
114+
:returns: the command to run in autonomous
115+
"""
116+
return commands2.InstantCommand()

0 commit comments

Comments
 (0)