mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[robotpy][examples] Split examples and snippets (#8944)
This also updates the bazel scripts to behave more like the C++ and Java examples, and updates the copybara scripts to be able to sync up `mostrobotpy`
This commit is contained in:
75
robotpyExamples/examples/HatchbotInlined/commands/autos.py
Normal file
75
robotpyExamples/examples/HatchbotInlined/commands/autos.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import commands2
|
||||
import commands2.cmd
|
||||
|
||||
import subsystems.drivesubsystem
|
||||
import subsystems.hatchsubsystem
|
||||
|
||||
import constants
|
||||
|
||||
|
||||
class Autos:
|
||||
"""Container for auto command factories."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
raise Exception("This is a utility class!")
|
||||
|
||||
@staticmethod
|
||||
def simpleAuto(drive: subsystems.drivesubsystem.DriveSubsystem):
|
||||
"""A simple auto routine that drives forward a specified distance, and then stops."""
|
||||
return commands2.FunctionalCommand(
|
||||
# Reset encoders on command start
|
||||
drive.resetEncoders,
|
||||
# Drive forward while the command is executing,
|
||||
lambda: drive.arcadeDrive(constants.kAutoDriveVelocity, 0),
|
||||
# Stop driving at the end of the command
|
||||
lambda interrupt: drive.arcadeDrive(0, 0),
|
||||
# End the command when the robot's driven distance exceeds the desired value
|
||||
lambda: drive.getAverageEncoderDistance()
|
||||
>= constants.kAutoDriveDistanceInches,
|
||||
# Require the drive subsystem
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def complexAuto(
|
||||
driveSubsystem: subsystems.drivesubsystem.DriveSubsystem,
|
||||
hatchSubsystem: subsystems.hatchsubsystem.HatchSubsystem,
|
||||
):
|
||||
"""A complex auto routine that drives forward, drops a hatch, and then drives backward."""
|
||||
return commands2.cmd.sequence(
|
||||
# Drive forward up to the front of the cargo ship
|
||||
commands2.FunctionalCommand(
|
||||
# Reset encoders on command start
|
||||
driveSubsystem.resetEncoders,
|
||||
# Drive forward while the command is executing
|
||||
lambda: driveSubsystem.arcadeDrive(constants.kAutoDriveVelocity, 0),
|
||||
# Stop driving at the end of the command
|
||||
lambda interrupt: driveSubsystem.arcadeDrive(0, 0),
|
||||
# End the command when the robot's driven distance exceeds the desired value
|
||||
lambda: driveSubsystem.getAverageEncoderDistance()
|
||||
>= constants.kAutoDriveDistanceInches,
|
||||
# Require the drive subsystem
|
||||
driveSubsystem,
|
||||
),
|
||||
# Release the hatch
|
||||
hatchSubsystem.releaseHatch(),
|
||||
# Drive backward the specified distance
|
||||
commands2.FunctionalCommand(
|
||||
# Reset encoders on command start
|
||||
driveSubsystem.resetEncoders,
|
||||
# Drive backwards while the command is executing
|
||||
lambda: driveSubsystem.arcadeDrive(-constants.kAutoDriveVelocity, 0),
|
||||
# Stop driving at the end of the command
|
||||
lambda interrupt: driveSubsystem.arcadeDrive(0, 0),
|
||||
# End the command when the robot's driven distance exceeds the desired value
|
||||
lambda: abs(driveSubsystem.getAverageEncoderDistance())
|
||||
>= constants.kAutoBackupDistanceInches,
|
||||
# Require the drive subsystem
|
||||
driveSubsystem,
|
||||
),
|
||||
)
|
||||
51
robotpyExamples/examples/HatchbotInlined/constants.py
Normal file
51
robotpyExamples/examples/HatchbotInlined/constants.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#
|
||||
# The constants module is a convenience place for teams to hold robot-wide
|
||||
# numerical or boolean constants. Don't use this for any other purpose!
|
||||
#
|
||||
|
||||
import math
|
||||
import wpilib
|
||||
|
||||
# Motors
|
||||
kLeftMotor1Port = 0
|
||||
kLeftMotor2Port = 1
|
||||
kRightMotor1Port = 2
|
||||
kRightMotor2Port = 3
|
||||
|
||||
# Encoders
|
||||
kLeftEncoderPorts = (0, 1)
|
||||
kRightEncoderPorts = (2, 3)
|
||||
kLeftEncoderReversed = False
|
||||
kRightEncoderReversed = True
|
||||
|
||||
kEncoderCPR = 1024
|
||||
kWheelDiameterInches = 6
|
||||
# Assumes the encoders are directly mounted on the wheel shafts
|
||||
kEncoderDistancePerPulse = (kWheelDiameterInches * math.pi) / kEncoderCPR
|
||||
|
||||
# Hatch
|
||||
kHatchSolenoidModuleType = wpilib.PneumaticsModuleType.CTRE_PCM
|
||||
kHatchSolenoidModule = 0
|
||||
kHatchSolenoidPorts = (0, 1)
|
||||
|
||||
# Autonomous
|
||||
kAutoDriveDistanceInches = 60
|
||||
kAutoBackupDistanceInches = 20
|
||||
kAutoDriveVelocity = 0.5
|
||||
|
||||
# Operator Interface
|
||||
kDriverControllerPort = 0
|
||||
|
||||
# Physical parameters
|
||||
kDriveTrainMotorCount = 2
|
||||
kTrackWidth = 0.381 * 2
|
||||
kGearingRatio = 8
|
||||
kWheelRadius = 0.0508
|
||||
|
||||
# kEncoderResolution = -
|
||||
71
robotpyExamples/examples/HatchbotInlined/robot.py
Normal file
71
robotpyExamples/examples/HatchbotInlined/robot.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import typing
|
||||
|
||||
import commands2
|
||||
import wpilib
|
||||
|
||||
from robotcontainer import RobotContainer
|
||||
|
||||
|
||||
class MyRobot(commands2.TimedCommandRobot):
|
||||
"""
|
||||
Command v2 robots are encouraged to inherit from TimedCommandRobot, which
|
||||
has an implementation of robotPeriodic which runs the scheduler for you
|
||||
"""
|
||||
|
||||
autonomousCommand: typing.Optional[commands2.Command] = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
This function is run when the robot is first started up and should be used for any
|
||||
initialization code.
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
# Instantiate our RobotContainer. This will perform all our button bindings, and put our
|
||||
# autonomous chooser on the dashboard.
|
||||
self.container = RobotContainer()
|
||||
|
||||
# Start recording to data log
|
||||
wpilib.DataLogManager.start()
|
||||
|
||||
# Record DS control and joystick data.
|
||||
# Change to `false` to not record joystick data.
|
||||
wpilib.DriverStation.startDataLog(wpilib.DataLogManager.getLog(), True)
|
||||
|
||||
def disabledInit(self) -> None:
|
||||
"""This function is called once each time the robot enters Disabled mode."""
|
||||
|
||||
def disabledPeriodic(self) -> None:
|
||||
"""This function is called periodically when disabled"""
|
||||
|
||||
def autonomousInit(self) -> None:
|
||||
"""This autonomous runs the autonomous command selected by your RobotContainer class."""
|
||||
self.autonomousCommand = self.container.getAutonomousCommand()
|
||||
|
||||
if self.autonomousCommand:
|
||||
self.autonomousCommand.schedule()
|
||||
|
||||
def autonomousPeriodic(self) -> None:
|
||||
"""This function is called periodically during autonomous"""
|
||||
|
||||
def teleopInit(self) -> None:
|
||||
# This makes sure that the autonomous stops running when
|
||||
# teleop starts running. If you want the autonomous to
|
||||
# continue until interrupted by another command, remove
|
||||
# this line or comment it out.
|
||||
if self.autonomousCommand:
|
||||
self.autonomousCommand.cancel()
|
||||
|
||||
def teleopPeriodic(self) -> None:
|
||||
"""This function is called periodically during operator control"""
|
||||
|
||||
def utilityInit(self) -> None:
|
||||
# Cancels all running commands at the start of utility mode
|
||||
commands2.CommandScheduler.getInstance().cancelAll()
|
||||
95
robotpyExamples/examples/HatchbotInlined/robotcontainer.py
Normal file
95
robotpyExamples/examples/HatchbotInlined/robotcontainer.py
Normal file
@@ -0,0 +1,95 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import wpilib
|
||||
|
||||
import commands2
|
||||
import commands2.button
|
||||
import commands2.cmd
|
||||
|
||||
import constants
|
||||
|
||||
import commands.autos
|
||||
|
||||
from subsystems.drivesubsystem import DriveSubsystem
|
||||
from subsystems.hatchsubsystem import HatchSubsystem
|
||||
|
||||
|
||||
class RobotContainer:
|
||||
"""
|
||||
This class is where the bulk of the robot should be declared. Since Command-based is a
|
||||
"declarative" paradigm, very little robot logic should actually be handled in the :class:`.Robot`
|
||||
periodic methods (other than the scheduler calls). Instead, the structure of the robot (including
|
||||
subsystems, commands, and button mappings) should be declared here.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
# The robot's subsystems
|
||||
self.driveSubsystem = DriveSubsystem()
|
||||
self.hatchSubsystem = HatchSubsystem()
|
||||
|
||||
# Retained command handles
|
||||
|
||||
# A simple auto routine that drives forward a specified distance, and then stops.
|
||||
self.simpleAuto = commands.autos.Autos.simpleAuto(self.driveSubsystem)
|
||||
|
||||
# A complex auto routine that drives forward, drops a hatch, and then drives backward.
|
||||
self.complexAuto = commands.autos.Autos.complexAuto(
|
||||
self.driveSubsystem, self.hatchSubsystem
|
||||
)
|
||||
|
||||
# The driver's controller
|
||||
self.driverController = commands2.button.CommandNiDsPS4Controller(
|
||||
constants.kDriverControllerPort
|
||||
)
|
||||
|
||||
# Configure the button bindings
|
||||
self.configureButtonBindings()
|
||||
|
||||
# Configure default commands
|
||||
# Set the default drive command to split-stick arcade drive
|
||||
self.driveSubsystem.setDefaultCommand(
|
||||
# A split-stick arcade command, with forward/backward controlled by the left
|
||||
# hand, and turning controlled by the right.
|
||||
commands2.cmd.run(
|
||||
lambda: self.driveSubsystem.arcadeDrive(
|
||||
-self.driverController.getLeftY(),
|
||||
-self.driverController.getRightX(),
|
||||
),
|
||||
self.driveSubsystem,
|
||||
)
|
||||
)
|
||||
|
||||
# Chooser
|
||||
self.chooser = wpilib.SendableChooser()
|
||||
|
||||
# Add commands to the autonomous command chooser
|
||||
self.chooser.setDefaultOption("Simple Auto", self.simpleAuto)
|
||||
self.chooser.addOption("Complex Auto", self.complexAuto)
|
||||
|
||||
# Put the chooser on the dashboard
|
||||
wpilib.SmartDashboard.putData("Autonomous", self.chooser)
|
||||
|
||||
def configureButtonBindings(self):
|
||||
"""
|
||||
Use this method to define your button->command mappings. Buttons can be created by
|
||||
instantiating a wpilib.GenericHID or one of its subclasses (Joystick or XboxController),
|
||||
and then passing it to a JoystickButton.
|
||||
"""
|
||||
|
||||
# Grab the hatch when the Circle button is pressed.
|
||||
self.driverController.circle().onTrue(self.hatchSubsystem.grabHatch())
|
||||
|
||||
# Release the hatch when the Square button is pressed.
|
||||
self.driverController.square().onTrue(self.hatchSubsystem.releaseHatch())
|
||||
|
||||
# While holding R1, drive at half velocity
|
||||
self.driverController.R1().onTrue(
|
||||
commands2.cmd.runOnce(lambda: self.driveSubsystem.setMaxOutput(0.5))
|
||||
).onFalse(commands2.cmd.runOnce(lambda: self.driveSubsystem.setMaxOutput(1)))
|
||||
|
||||
def getAutonomousCommand(self) -> commands2.Command:
|
||||
return self.chooser.getSelected()
|
||||
@@ -0,0 +1,68 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import commands2
|
||||
import wpilib
|
||||
import constants
|
||||
|
||||
|
||||
class DriveSubsystem(commands2.Subsystem):
|
||||
def __init__(self) -> None:
|
||||
|
||||
self.left1 = wpilib.PWMVictorSPX(constants.kLeftMotor1Port)
|
||||
self.left2 = wpilib.PWMVictorSPX(constants.kLeftMotor2Port)
|
||||
self.right1 = wpilib.PWMVictorSPX(constants.kRightMotor1Port)
|
||||
self.right2 = wpilib.PWMVictorSPX(constants.kRightMotor2Port)
|
||||
|
||||
self.left1.addFollower(self.left2)
|
||||
self.right1.addFollower(self.right2)
|
||||
|
||||
# We need to invert one side of the drivetrain so that positive velocities
|
||||
# result in both sides moving forward. Depending on how your robot's
|
||||
# drivetrain is constructed, you might have to invert the left side instead.
|
||||
self.right1.setInverted(True)
|
||||
|
||||
# The robot's drive
|
||||
self.drive = wpilib.DifferentialDrive(self.left1, self.right1)
|
||||
|
||||
# The left-side drive encoder
|
||||
self.leftEncoder = wpilib.Encoder(
|
||||
*constants.kLeftEncoderPorts,
|
||||
reverseDirection=constants.kLeftEncoderReversed
|
||||
)
|
||||
|
||||
# The right-side drive encoder
|
||||
self.rightEncoder = wpilib.Encoder(
|
||||
*constants.kRightEncoderPorts,
|
||||
reverseDirection=constants.kRightEncoderReversed
|
||||
)
|
||||
|
||||
# Sets the distance per pulse for the encoders
|
||||
self.leftEncoder.setDistancePerPulse(constants.kEncoderDistancePerPulse)
|
||||
self.rightEncoder.setDistancePerPulse(constants.kEncoderDistancePerPulse)
|
||||
|
||||
def arcadeDrive(self, fwd: float, rot: float) -> None:
|
||||
"""
|
||||
Drives the robot using arcade controls.
|
||||
|
||||
:param fwd: the commanded forward movement
|
||||
:param rot: the commanded rotation
|
||||
"""
|
||||
self.drive.arcadeDrive(fwd, rot)
|
||||
|
||||
def resetEncoders(self) -> None:
|
||||
"""Resets the drive encoders to currently read a position of 0."""
|
||||
|
||||
def getAverageEncoderDistance(self) -> float:
|
||||
"""Gets the average distance of the TWO encoders."""
|
||||
return (self.leftEncoder.getDistance() + self.rightEncoder.getDistance()) / 2.0
|
||||
|
||||
def setMaxOutput(self, maxOutput: float):
|
||||
"""
|
||||
Sets the max output of the drive. Useful for scaling the
|
||||
drive to drive more slowly.
|
||||
"""
|
||||
self.drive.setMaxOutput(maxOutput)
|
||||
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import wpilib
|
||||
import commands2
|
||||
import commands2.cmd
|
||||
|
||||
import constants
|
||||
|
||||
|
||||
class HatchSubsystem(commands2.Subsystem):
|
||||
def __init__(self) -> None:
|
||||
|
||||
self.hatchSolenoid = wpilib.DoubleSolenoid(
|
||||
constants.kHatchSolenoidModule,
|
||||
constants.kHatchSolenoidModuleType,
|
||||
*constants.kHatchSolenoidPorts
|
||||
)
|
||||
|
||||
def grabHatch(self) -> commands2.Command:
|
||||
"""Grabs the hatch"""
|
||||
return commands2.cmd.runOnce(
|
||||
lambda: self.hatchSolenoid.set(wpilib.DoubleSolenoid.Value.FORWARD), self
|
||||
)
|
||||
|
||||
def releaseHatch(self) -> commands2.Command:
|
||||
"""Releases the hatch"""
|
||||
return commands2.cmd.runOnce(
|
||||
lambda: self.hatchSolenoid.set(wpilib.DoubleSolenoid.Value.REVERSE), self
|
||||
)
|
||||
Reference in New Issue
Block a user