Files
allwpilib/robotpyExamples/ElevatorSimulation/subsystems/elevator.py

108 lines
4.0 KiB
Python
Raw Normal View History

#
# 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 wpilib.simulation
import wpimath
import constants
class Elevator:
"""Represents the elevator subsystem."""
def __init__(self) -> None:
# This gearbox represents a gearbox containing 4 Vex 775pro motors.
self.elevatorGearbox = wpimath.DCMotor.vex775Pro(4)
# Standard classes for controlling our elevator
self.controller = wpimath.ProfiledPIDController(
constants.kElevatorKp,
constants.kElevatorKi,
constants.kElevatorKd,
wpimath.TrapezoidProfile.Constraints(2.45, 2.45),
)
self.feedforward = wpimath.ElevatorFeedforward(
constants.kElevatorkS,
constants.kElevatorkG,
constants.kElevatorkV,
constants.kElevatorkA,
)
self.encoder = wpilib.Encoder(
constants.kEncoderAChannel, constants.kEncoderBChannel
)
self.motor = wpilib.PWMSparkMax(constants.kMotorPort)
# Simulation classes help us simulate what's going on, including gravity.
self.elevatorSim = wpilib.simulation.ElevatorSim(
self.elevatorGearbox,
constants.kElevatorGearing,
constants.kCarriageMass,
constants.kElevatorDrumRadius,
constants.kMinElevatorHeight,
constants.kMaxElevatorHeight,
True,
0,
[0.01, 0.0],
)
self.encoderSim = wpilib.simulation.EncoderSim(self.encoder)
self.motorSim = wpilib.simulation.PWMMotorControllerSim(self.motor)
# Create a Mechanism2d visualization of the elevator
self.mech2d = wpilib.Mechanism2d(20, 50)
self.mech2dRoot = self.mech2d.getRoot("Elevator Root", 10, 0)
self.elevatorMech2d = self.mech2dRoot.appendLigament(
"Elevator", self.elevatorSim.getPosition(), 90
)
self.encoder.setDistancePerPulse(constants.kElevatorEncoderDistPerPulse)
# Publish Mechanism2d to SmartDashboard
# To view the Elevator visualization, select Network Tables -> SmartDashboard -> Elevator Sim
wpilib.SmartDashboard.putData("Elevator Sim", self.mech2d)
def simulationPeriodic(self) -> None:
# In this method, we update our simulation of what our elevator is doing
# First, we set our "inputs" (voltages)
self.elevatorSim.setInputVoltage(
self.motorSim.getThrottle() * wpilib.RobotController.getBatteryVoltage()
)
# Next, we update it. The standard loop time is 20ms.
self.elevatorSim.update(0.020)
# Finally, we set our simulated encoder's readings and simulated battery voltage
self.encoderSim.setDistance(self.elevatorSim.getPosition())
# SimBattery estimates loaded battery voltages
wpilib.simulation.RoboRioSim.setVInVoltage(
wpilib.simulation.BatterySim.calculate([self.elevatorSim.getCurrentDraw()])
)
def reachGoal(self, goal: float) -> None:
"""Run control loop to reach and maintain goal.
:param goal: the position to maintain
"""
self.controller.setGoal(goal)
# With the setpoint value we run PID control like normal
pidOutput = self.controller.calculate(self.encoder.getDistance())
feedforwardOutput = self.feedforward.calculate(
self.controller.getSetpoint().velocity
)
self.motor.setVoltage(pidOutput + feedforwardOutput)
def stop(self) -> None:
"""Stop the control loop and motor output."""
self.controller.setGoal(0.0)
self.motor.setThrottle(0.0)
def updateTelemetry(self) -> None:
"""Update telemetry, including the mechanism visualization."""
# Update elevator visualization with position
self.elevatorMech2d.setLength(self.encoder.getDistance())