mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-20 00:51:42 +00:00
Add TrapezoidProfile external PID examples (#2131)
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.armbotoffboard;
|
||||
|
||||
/**
|
||||
* The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean
|
||||
* constants. This class should not be used for any other purpose. All constants should be
|
||||
* declared globally (i.e. public static). Do not put anything functional in this class.
|
||||
*
|
||||
* <p>It is advised to statically import this class (or one of its inner classes) wherever the
|
||||
* constants are needed, to reduce verbosity.
|
||||
*/
|
||||
public final class Constants {
|
||||
public static final class DriveConstants {
|
||||
public static final int kLeftMotor1Port = 0;
|
||||
public static final int kLeftMotor2Port = 1;
|
||||
public static final int kRightMotor1Port = 2;
|
||||
public static final int kRightMotor2Port = 3;
|
||||
|
||||
public static final int[] kLeftEncoderPorts = new int[]{0, 1};
|
||||
public static final int[] kRightEncoderPorts = new int[]{2, 3};
|
||||
public static final boolean kLeftEncoderReversed = false;
|
||||
public static final boolean kRightEncoderReversed = true;
|
||||
|
||||
public static final int kEncoderCPR = 1024;
|
||||
public static final double kWheelDiameterInches = 6;
|
||||
public static final double kEncoderDistancePerPulse =
|
||||
// Assumes the encoders are directly mounted on the wheel shafts
|
||||
(kWheelDiameterInches * Math.PI) / (double) kEncoderCPR;
|
||||
}
|
||||
|
||||
public static final class ArmConstants {
|
||||
public static final int kMotorPort = 4;
|
||||
|
||||
public static final double kP = 1;
|
||||
|
||||
// These are fake gains; in actuality these must be determined individually for each robot
|
||||
public static final double kSVolts = 1;
|
||||
public static final double kCosVolts = 1;
|
||||
public static final double kVVoltSecondPerRad = 0.5;
|
||||
public static final double kAVoltSecondSquaredPerRad = 0.1;
|
||||
|
||||
public static final double kMaxVelocityRadPerSecond = 3;
|
||||
public static final double kMaxAccelerationRadPerSecSquared = 10;
|
||||
|
||||
// The offset of the arm from the horizontal in its neutral position,
|
||||
// measured from the horizontal
|
||||
public static final double kArmOffsetRads = 0.5;
|
||||
}
|
||||
|
||||
public static final class OIConstants {
|
||||
public static final int kDriverControllerPort = 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.armbotoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.SpeedController;
|
||||
|
||||
/**
|
||||
* A simplified stub class that simulates the API of a common "smart" motor controller.
|
||||
*
|
||||
* <p>Has no actual functionality.
|
||||
*/
|
||||
public class ExampleSmartMotorController implements SpeedController {
|
||||
public enum PIDMode {
|
||||
kPosition,
|
||||
kVelocity,
|
||||
kMovementWitchcraft
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ExampleSmartMotorController.
|
||||
*
|
||||
* @param port The port for the controller.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public ExampleSmartMotorController(int port) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example method for setting the PID gains of the smart controller.
|
||||
*
|
||||
* @param kp The proportional gain.
|
||||
* @param ki The integral gain.
|
||||
* @param kd The derivative gain.
|
||||
*/
|
||||
public void setPID(double kp, double ki, double kd) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example method for setting the setpoint of the smart controller in PID mode.
|
||||
*
|
||||
* @param mode The mode of the PID controller.
|
||||
* @param setpoint The controller setpoint.
|
||||
* @param arbFeedforward An arbitrary feedforward output (from -1 to 1).
|
||||
*/
|
||||
public void setSetpoint(PIDMode mode, double setpoint, double arbFeedforward) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Places this motor controller in follower mode.
|
||||
*
|
||||
* @param master The master to follow.
|
||||
*/
|
||||
public void follow(ExampleSmartMotorController master) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoder distance.
|
||||
*
|
||||
* @return The current encoder distance.
|
||||
*/
|
||||
public double getEncoderDistance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoder rate.
|
||||
*
|
||||
* @return The current encoder rate.
|
||||
*/
|
||||
public double getEncoderRate() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the encoder to zero distance.
|
||||
*/
|
||||
public void resetEncoder() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(double speed) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double get() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInverted(boolean isInverted) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getInverted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopMotor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pidWrite(double output) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.armbotoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.RobotBase;
|
||||
|
||||
/**
|
||||
* Do NOT add any static variables to this class, or any initialization at all. Unless you know what
|
||||
* you are doing, do not modify this file except to change the parameter class to the startRobot
|
||||
* call.
|
||||
*/
|
||||
public final class Main {
|
||||
private Main() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Main initialization function. Do not perform any initialization here.
|
||||
*
|
||||
* <p>If you change your main robot class, change the parameter type.
|
||||
*/
|
||||
public static void main(String... args) {
|
||||
RobotBase.startRobot(Robot::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.armbotoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.TimedRobot;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||
|
||||
/**
|
||||
* The VM is configured to automatically run this class, and to call the functions corresponding to
|
||||
* each mode, as described in the TimedRobot documentation. If you change the name of this class or
|
||||
* the package after creating this project, you must also update the build.gradle file in the
|
||||
* project.
|
||||
*/
|
||||
public class Robot extends TimedRobot {
|
||||
private Command m_autonomousCommand;
|
||||
|
||||
private RobotContainer m_robotContainer;
|
||||
|
||||
/**
|
||||
* This function is run when the robot is first started up and should be used for any
|
||||
* initialization code.
|
||||
*/
|
||||
@Override
|
||||
public void robotInit() {
|
||||
// Instantiate our RobotContainer. This will perform all our button bindings, and put our
|
||||
// autonomous chooser on the dashboard.
|
||||
m_robotContainer = new RobotContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called every robot packet, no matter the mode. Use this for items like
|
||||
* diagnostics that you want ran during disabled, autonomous, teleoperated and test.
|
||||
*
|
||||
* <p>This runs after the mode specific periodic functions, but before
|
||||
* LiveWindow and SmartDashboard integrated updating.
|
||||
*/
|
||||
@Override
|
||||
public void robotPeriodic() {
|
||||
// Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled
|
||||
// commands, running already-scheduled commands, removing finished or interrupted commands,
|
||||
// and running subsystem periodic() methods. This must be called from the robot's periodic
|
||||
// block in order for anything in the Command-based framework to work.
|
||||
CommandScheduler.getInstance().run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called once each time the robot enters Disabled mode.
|
||||
*/
|
||||
@Override
|
||||
public void disabledInit() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disabledPeriodic() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This autonomous runs the autonomous command selected by your {@link RobotContainer} class.
|
||||
*/
|
||||
@Override
|
||||
public void autonomousInit() {
|
||||
m_autonomousCommand = m_robotContainer.getAutonomousCommand();
|
||||
|
||||
/*
|
||||
* String autoSelected = SmartDashboard.getString("Auto Selector",
|
||||
* "Default"); switch(autoSelected) { case "My Auto": autonomousCommand
|
||||
* = new MyAutoCommand(); break; case "Default Auto": default:
|
||||
* autonomousCommand = new ExampleCommand(); break; }
|
||||
*/
|
||||
|
||||
// schedule the autonomous command (example)
|
||||
if (m_autonomousCommand != null) {
|
||||
m_autonomousCommand.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called periodically during autonomous.
|
||||
*/
|
||||
@Override
|
||||
public void autonomousPeriodic() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleopInit() {
|
||||
// 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 (m_autonomousCommand != null) {
|
||||
m_autonomousCommand.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called periodically during operator control.
|
||||
*/
|
||||
@Override
|
||||
public void teleopPeriodic() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testInit() {
|
||||
// Cancels all running commands at the start of test mode.
|
||||
CommandScheduler.getInstance().cancelAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called periodically during test mode.
|
||||
*/
|
||||
@Override
|
||||
public void testPeriodic() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.armbotoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.GenericHID;
|
||||
import edu.wpi.first.wpilibj.XboxController;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.InstantCommand;
|
||||
import edu.wpi.first.wpilibj2.command.RunCommand;
|
||||
import edu.wpi.first.wpilibj2.command.button.JoystickButton;
|
||||
|
||||
import edu.wpi.first.wpilibj.examples.armbotoffboard.subsystems.ArmSubsystem;
|
||||
import edu.wpi.first.wpilibj.examples.armbotoffboard.subsystems.DriveSubsystem;
|
||||
|
||||
import static edu.wpi.first.wpilibj.XboxController.Button;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.OIConstants.kDriverControllerPort;
|
||||
|
||||
/**
|
||||
* 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 {@link Robot}
|
||||
* periodic methods (other than the scheduler calls). Instead, the structure of the robot
|
||||
* (including subsystems, commands, and button mappings) should be declared here.
|
||||
*/
|
||||
public class RobotContainer {
|
||||
// The robot's subsystems
|
||||
private final DriveSubsystem m_robotDrive = new DriveSubsystem();
|
||||
private final ArmSubsystem m_robotArm = new ArmSubsystem();
|
||||
|
||||
// The driver's controller
|
||||
XboxController m_driverController = new XboxController(kDriverControllerPort);
|
||||
|
||||
/**
|
||||
* The container for the robot. Contains subsystems, OI devices, and commands.
|
||||
*/
|
||||
public RobotContainer() {
|
||||
// Configure the button bindings
|
||||
configureButtonBindings();
|
||||
|
||||
// Configure default commands
|
||||
// Set the default drive command to split-stick arcade drive
|
||||
m_robotDrive.setDefaultCommand(
|
||||
// A split-stick arcade command, with forward/backward controlled by the left
|
||||
// hand, and turning controlled by the right.
|
||||
new RunCommand(() -> m_robotDrive
|
||||
.arcadeDrive(m_driverController.getY(GenericHID.Hand.kLeft),
|
||||
m_driverController.getX(GenericHID.Hand.kRight)), m_robotDrive));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to define your button->command mappings. Buttons can be created by
|
||||
* instantiating a {@link GenericHID} or one of its subclasses ({@link
|
||||
* edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a
|
||||
* {@link JoystickButton}.
|
||||
*/
|
||||
private void configureButtonBindings() {
|
||||
|
||||
// Move the arm to 2 radians above horizontal when the 'A' button is pressed.
|
||||
new JoystickButton(m_driverController, Button.kA.value)
|
||||
.whenPressed(() -> m_robotArm.setGoal(2), m_robotArm);
|
||||
|
||||
// Move the arm to neutral position when the 'B' button is pressed.
|
||||
new JoystickButton(m_driverController, Button.kB.value)
|
||||
.whenPressed(() -> m_robotArm.setGoal(Constants.ArmConstants.kArmOffsetRads), m_robotArm);
|
||||
|
||||
// Drive at half speed when the bumper is held
|
||||
new JoystickButton(m_driverController, Button.kBumperRight.value)
|
||||
.whenPressed(() -> m_robotDrive.setMaxOutput(0.5))
|
||||
.whenReleased(() -> m_robotDrive.setMaxOutput(1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this to pass the autonomous command to the main {@link Robot} class.
|
||||
*
|
||||
* @return the command to run in autonomous
|
||||
*/
|
||||
public Command getAutonomousCommand() {
|
||||
return new InstantCommand();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.armbotoffboard.subsystems;
|
||||
|
||||
import edu.wpi.first.wpilibj.controller.ArmFeedforward;
|
||||
import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile;
|
||||
import edu.wpi.first.wpilibj2.command.TrapezoidProfileSubsystem;
|
||||
|
||||
import edu.wpi.first.wpilibj.examples.armbotoffboard.ExampleSmartMotorController;
|
||||
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kAVoltSecondSquaredPerRad;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kArmOffsetRads;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kCosVolts;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kMaxAccelerationRadPerSecSquared;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kMaxVelocityRadPerSecond;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kMotorPort;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kP;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kSVolts;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants.kVVoltSecondPerRad;
|
||||
|
||||
/**
|
||||
* A robot arm subsystem that moves with a motion profile.
|
||||
*/
|
||||
public class ArmSubsystem extends TrapezoidProfileSubsystem {
|
||||
private final ExampleSmartMotorController m_motor = new ExampleSmartMotorController(kMotorPort);
|
||||
private final ArmFeedforward m_feedforward =
|
||||
new ArmFeedforward(kSVolts, kCosVolts, kVVoltSecondPerRad, kAVoltSecondSquaredPerRad);
|
||||
|
||||
/**
|
||||
* Create a new ArmSubsystem.
|
||||
*/
|
||||
public ArmSubsystem() {
|
||||
super(new TrapezoidProfile.Constraints(kMaxVelocityRadPerSecond,
|
||||
kMaxAccelerationRadPerSecSquared),
|
||||
kArmOffsetRads);
|
||||
m_motor.setPID(kP, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useState(TrapezoidProfile.State setpoint) {
|
||||
// Calculate the feedforward from the sepoint
|
||||
double feedforward = m_feedforward.calculate(setpoint.position, setpoint.velocity);
|
||||
// Add the feedforward to the PID output to get the motor output
|
||||
m_motor.setSetpoint(ExampleSmartMotorController.PIDMode.kPosition,
|
||||
setpoint.position,
|
||||
feedforward / 12.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.armbotoffboard.subsystems;
|
||||
|
||||
import edu.wpi.first.wpilibj.Encoder;
|
||||
import edu.wpi.first.wpilibj.PWMVictorSPX;
|
||||
import edu.wpi.first.wpilibj.SpeedControllerGroup;
|
||||
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
|
||||
import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
||||
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kEncoderDistancePerPulse;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kLeftEncoderPorts;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kLeftEncoderReversed;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kLeftMotor1Port;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kLeftMotor2Port;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kRightEncoderPorts;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kRightEncoderReversed;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kRightMotor1Port;
|
||||
import static edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants.kRightMotor2Port;
|
||||
|
||||
public class DriveSubsystem extends SubsystemBase {
|
||||
// The motors on the left side of the drive.
|
||||
private final SpeedControllerGroup m_leftMotors =
|
||||
new SpeedControllerGroup(new PWMVictorSPX(kLeftMotor1Port),
|
||||
new PWMVictorSPX(kLeftMotor2Port));
|
||||
|
||||
// The motors on the right side of the drive.
|
||||
private final SpeedControllerGroup m_rightMotors =
|
||||
new SpeedControllerGroup(new PWMVictorSPX(kRightMotor1Port),
|
||||
new PWMVictorSPX(kRightMotor2Port));
|
||||
|
||||
// The robot's drive
|
||||
private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotors, m_rightMotors);
|
||||
|
||||
// The left-side drive encoder
|
||||
private final Encoder m_leftEncoder =
|
||||
new Encoder(kLeftEncoderPorts[0], kLeftEncoderPorts[1], kLeftEncoderReversed);
|
||||
|
||||
// The right-side drive encoder
|
||||
private final Encoder m_rightEncoder =
|
||||
new Encoder(kRightEncoderPorts[0], kRightEncoderPorts[1], kRightEncoderReversed);
|
||||
|
||||
/**
|
||||
* Creates a new DriveSubsystem.
|
||||
*/
|
||||
public DriveSubsystem() {
|
||||
// Sets the distance per pulse for the encoders
|
||||
m_leftEncoder.setDistancePerPulse(kEncoderDistancePerPulse);
|
||||
m_rightEncoder.setDistancePerPulse(kEncoderDistancePerPulse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drives the robot using arcade controls.
|
||||
*
|
||||
* @param fwd the commanded forward movement
|
||||
* @param rot the commanded rotation
|
||||
*/
|
||||
public void arcadeDrive(double fwd, double rot) {
|
||||
m_drive.arcadeDrive(fwd, rot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the drive encoders to currently read a position of 0.
|
||||
*/
|
||||
public void resetEncoders() {
|
||||
m_leftEncoder.reset();
|
||||
m_rightEncoder.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the average distance of the two encoders.
|
||||
*
|
||||
* @return the average of the two encoder readings
|
||||
*/
|
||||
public double getAverageEncoderDistance() {
|
||||
return (m_leftEncoder.getDistance() + m_rightEncoder.getDistance()) / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the left drive encoder.
|
||||
*
|
||||
* @return the left drive encoder
|
||||
*/
|
||||
public Encoder getLeftEncoder() {
|
||||
return m_leftEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the right drive encoder.
|
||||
*
|
||||
* @return the right drive encoder
|
||||
*/
|
||||
public Encoder getRightEncoder() {
|
||||
return m_rightEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max output of the drive. Useful for scaling the drive to drive more slowly.
|
||||
*
|
||||
* @param maxOutput the maximum output to which the drive will be constrained
|
||||
*/
|
||||
public void setMaxOutput(double maxOutput) {
|
||||
m_drive.setMaxOutput(maxOutput);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.drivedistanceoffboard;
|
||||
|
||||
/**
|
||||
* The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean
|
||||
* constants. This class should not be used for any other purpose. All constants should be
|
||||
* declared globally (i.e. public static). Do not put anything functional in this class.
|
||||
*
|
||||
* <p>It is advised to statically import this class (or one of its inner classes) wherever the
|
||||
* constants are needed, to reduce verbosity.
|
||||
*/
|
||||
public final class Constants {
|
||||
public static final class DriveConstants {
|
||||
public static final int kLeftMotor1Port = 0;
|
||||
public static final int kLeftMotor2Port = 1;
|
||||
public static final int kRightMotor1Port = 2;
|
||||
public static final int kRightMotor2Port = 3;
|
||||
|
||||
// These are example values only - DO NOT USE THESE FOR YOUR OWN ROBOT!
|
||||
// These characterization values MUST be determined either experimentally or theoretically
|
||||
// for *your* robot's drive.
|
||||
// The RobotPy Characterization Toolsuite provides a convenient tool for obtaining these
|
||||
// values for your robot.
|
||||
public static final double ksVolts = 1;
|
||||
public static final double kvVoltSecondsPerMeter = 0.8;
|
||||
public static final double kaVoltSecondsSquaredPerMeter = 0.15;
|
||||
|
||||
public static final double kp = 1;
|
||||
|
||||
public static final double kMaxSpeedMetersPerSecond = 3;
|
||||
public static final double kMaxAccelerationMetersPerSecondSquared = 3;
|
||||
}
|
||||
|
||||
public static final class OIConstants {
|
||||
public static final int kDriverControllerPort = 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.drivedistanceoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.SpeedController;
|
||||
|
||||
/**
|
||||
* A simplified stub class that simulates the API of a common "smart" motor controller.
|
||||
*
|
||||
* <p>Has no actual functionality.
|
||||
*/
|
||||
public class ExampleSmartMotorController implements SpeedController {
|
||||
public enum PIDMode {
|
||||
kPosition,
|
||||
kVelocity,
|
||||
kMovementWitchcraft
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ExampleSmartMotorController.
|
||||
*
|
||||
* @param port The port for the controller.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public ExampleSmartMotorController(int port) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example method for setting the PID gains of the smart controller.
|
||||
*
|
||||
* @param kp The proportional gain.
|
||||
* @param ki The integral gain.
|
||||
* @param kd The derivative gain.
|
||||
*/
|
||||
public void setPID(double kp, double ki, double kd) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example method for setting the setpoint of the smart controller in PID mode.
|
||||
*
|
||||
* @param mode The mode of the PID controller.
|
||||
* @param setpoint The controller setpoint.
|
||||
* @param arbFeedforward An arbitrary feedforward output (from -1 to 1).
|
||||
*/
|
||||
public void setSetpoint(PIDMode mode, double setpoint, double arbFeedforward) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Places this motor controller in follower mode.
|
||||
*
|
||||
* @param master The master to follow.
|
||||
*/
|
||||
public void follow(ExampleSmartMotorController master) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoder distance.
|
||||
*
|
||||
* @return The current encoder distance.
|
||||
*/
|
||||
public double getEncoderDistance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoder rate.
|
||||
*
|
||||
* @return The current encoder rate.
|
||||
*/
|
||||
public double getEncoderRate() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the encoder to zero distance.
|
||||
*/
|
||||
public void resetEncoder() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(double speed) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double get() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInverted(boolean isInverted) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getInverted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopMotor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pidWrite(double output) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.drivedistanceoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.RobotBase;
|
||||
|
||||
/**
|
||||
* Do NOT add any static variables to this class, or any initialization at all. Unless you know what
|
||||
* you are doing, do not modify this file except to change the parameter class to the startRobot
|
||||
* call.
|
||||
*/
|
||||
public final class Main {
|
||||
private Main() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Main initialization function. Do not perform any initialization here.
|
||||
*
|
||||
* <p>If you change your main robot class, change the parameter type.
|
||||
*/
|
||||
public static void main(String... args) {
|
||||
RobotBase.startRobot(Robot::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.drivedistanceoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.TimedRobot;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||
|
||||
/**
|
||||
* The VM is configured to automatically run this class, and to call the functions corresponding to
|
||||
* each mode, as described in the TimedRobot documentation. If you change the name of this class or
|
||||
* the package after creating this project, you must also update the build.gradle file in the
|
||||
* project.
|
||||
*/
|
||||
public class Robot extends TimedRobot {
|
||||
private Command m_autonomousCommand;
|
||||
|
||||
private RobotContainer m_robotContainer;
|
||||
|
||||
/**
|
||||
* This function is run when the robot is first started up and should be used for any
|
||||
* initialization code.
|
||||
*/
|
||||
@Override
|
||||
public void robotInit() {
|
||||
// Instantiate our RobotContainer. This will perform all our button bindings, and put our
|
||||
// autonomous chooser on the dashboard.
|
||||
m_robotContainer = new RobotContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called every robot packet, no matter the mode. Use this for items like
|
||||
* diagnostics that you want ran during disabled, autonomous, teleoperated and test.
|
||||
*
|
||||
* <p>This runs after the mode specific periodic functions, but before
|
||||
* LiveWindow and SmartDashboard integrated updating.
|
||||
*/
|
||||
@Override
|
||||
public void robotPeriodic() {
|
||||
// Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled
|
||||
// commands, running already-scheduled commands, removing finished or interrupted commands,
|
||||
// and running subsystem periodic() methods. This must be called from the robot's periodic
|
||||
// block in order for anything in the Command-based framework to work.
|
||||
CommandScheduler.getInstance().run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called once each time the robot enters Disabled mode.
|
||||
*/
|
||||
@Override
|
||||
public void disabledInit() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disabledPeriodic() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This autonomous runs the autonomous command selected by your {@link RobotContainer} class.
|
||||
*/
|
||||
@Override
|
||||
public void autonomousInit() {
|
||||
m_autonomousCommand = m_robotContainer.getAutonomousCommand();
|
||||
|
||||
/*
|
||||
* String autoSelected = SmartDashboard.getString("Auto Selector",
|
||||
* "Default"); switch(autoSelected) { case "My Auto": autonomousCommand
|
||||
* = new MyAutoCommand(); break; case "Default Auto": default:
|
||||
* autonomousCommand = new ExampleCommand(); break; }
|
||||
*/
|
||||
|
||||
// schedule the autonomous command (example)
|
||||
if (m_autonomousCommand != null) {
|
||||
m_autonomousCommand.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called periodically during autonomous.
|
||||
*/
|
||||
@Override
|
||||
public void autonomousPeriodic() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleopInit() {
|
||||
// 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 (m_autonomousCommand != null) {
|
||||
m_autonomousCommand.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called periodically during operator control.
|
||||
*/
|
||||
@Override
|
||||
public void teleopPeriodic() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testInit() {
|
||||
// Cancels all running commands at the start of test mode.
|
||||
CommandScheduler.getInstance().cancelAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called periodically during test mode.
|
||||
*/
|
||||
@Override
|
||||
public void testPeriodic() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.drivedistanceoffboard;
|
||||
|
||||
import edu.wpi.first.wpilibj.GenericHID;
|
||||
import edu.wpi.first.wpilibj.XboxController;
|
||||
import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.InstantCommand;
|
||||
import edu.wpi.first.wpilibj2.command.RunCommand;
|
||||
import edu.wpi.first.wpilibj2.command.TrapezoidProfileCommand;
|
||||
import edu.wpi.first.wpilibj2.command.button.JoystickButton;
|
||||
|
||||
import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.commands.DriveDistanceProfiled;
|
||||
import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.subsystems.DriveSubsystem;
|
||||
|
||||
import static edu.wpi.first.wpilibj.XboxController.Button;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kMaxAccelerationMetersPerSecondSquared;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kMaxSpeedMetersPerSecond;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.OIConstants.kDriverControllerPort;
|
||||
|
||||
/**
|
||||
* 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 {@link Robot}
|
||||
* periodic methods (other than the scheduler calls). Instead, the structure of the robot
|
||||
* (including subsystems, commands, and button mappings) should be declared here.
|
||||
*/
|
||||
public class RobotContainer {
|
||||
// The robot's subsystems
|
||||
private final DriveSubsystem m_robotDrive = new DriveSubsystem();
|
||||
|
||||
// The driver's controller
|
||||
XboxController m_driverController = new XboxController(kDriverControllerPort);
|
||||
|
||||
/**
|
||||
* The container for the robot. Contains subsystems, OI devices, and commands.
|
||||
*/
|
||||
public RobotContainer() {
|
||||
// Configure the button bindings
|
||||
configureButtonBindings();
|
||||
|
||||
// Configure default commands
|
||||
// Set the default drive command to split-stick arcade drive
|
||||
m_robotDrive.setDefaultCommand(
|
||||
// A split-stick arcade command, with forward/backward controlled by the left
|
||||
// hand, and turning controlled by the right.
|
||||
new RunCommand(() -> m_robotDrive
|
||||
.arcadeDrive(m_driverController.getY(GenericHID.Hand.kLeft),
|
||||
m_driverController.getX(GenericHID.Hand.kRight)), m_robotDrive));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to define your button->command mappings. Buttons can be created by
|
||||
* instantiating a {@link GenericHID} or one of its subclasses ({@link
|
||||
* edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a
|
||||
* {@link JoystickButton}.
|
||||
*/
|
||||
private void configureButtonBindings() {
|
||||
// Drive forward by 3 meters when the 'A' button is pressed, with a timeout of 10 seconds
|
||||
new JoystickButton(m_driverController, Button.kA.value)
|
||||
.whenPressed(new DriveDistanceProfiled(3, m_robotDrive).withTimeout(10));
|
||||
|
||||
// Do the same thing as above when the 'B' button is pressed, but defined inline
|
||||
new JoystickButton(m_driverController, Button.kB.value)
|
||||
.whenPressed(
|
||||
new TrapezoidProfileCommand(
|
||||
new TrapezoidProfile(
|
||||
// Limit the max acceleration and velocity
|
||||
new TrapezoidProfile.Constraints(kMaxSpeedMetersPerSecond,
|
||||
kMaxAccelerationMetersPerSecondSquared),
|
||||
// End at desired position in meters; implicitly starts at 0
|
||||
new TrapezoidProfile.State(3, 0)),
|
||||
// Pipe the profile state to the drive
|
||||
setpointState -> m_robotDrive.setDriveStates(
|
||||
setpointState,
|
||||
setpointState),
|
||||
// Require the drive
|
||||
m_robotDrive)
|
||||
.beforeStarting(m_robotDrive::resetEncoders)
|
||||
.withTimeout(10));
|
||||
|
||||
// Drive at half speed when the bumper is held
|
||||
new JoystickButton(m_driverController, Button.kBumperRight.value)
|
||||
.whenPressed(() -> m_robotDrive.setMaxOutput(0.5))
|
||||
.whenReleased(() -> m_robotDrive.setMaxOutput(1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this to pass the autonomous command to the main {@link Robot} class.
|
||||
*
|
||||
* @return the command to run in autonomous
|
||||
*/
|
||||
public Command getAutonomousCommand() {
|
||||
return new InstantCommand();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.drivedistanceoffboard.commands;
|
||||
|
||||
import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile;
|
||||
import edu.wpi.first.wpilibj2.command.TrapezoidProfileCommand;
|
||||
|
||||
import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.subsystems.DriveSubsystem;
|
||||
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kMaxAccelerationMetersPerSecondSquared;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kMaxSpeedMetersPerSecond;
|
||||
|
||||
/**
|
||||
* Drives a set distance using a motion profile.
|
||||
*/
|
||||
public class DriveDistanceProfiled extends TrapezoidProfileCommand {
|
||||
/**
|
||||
* Creates a new DriveDistanceProfiled command.
|
||||
*
|
||||
* @param meters The distance to drive.
|
||||
* @param drive The drive subsystem to use.
|
||||
*/
|
||||
public DriveDistanceProfiled(double meters, DriveSubsystem drive) {
|
||||
super(
|
||||
new TrapezoidProfile(
|
||||
// Limit the max acceleration and velocity
|
||||
new TrapezoidProfile.Constraints(kMaxSpeedMetersPerSecond,
|
||||
kMaxAccelerationMetersPerSecondSquared),
|
||||
// End at desired position in meters; implicitly starts at 0
|
||||
new TrapezoidProfile.State(meters, 0)),
|
||||
// Pipe the profile state to the drive
|
||||
setpointState -> drive.setDriveStates(setpointState, setpointState),
|
||||
// Require the drive
|
||||
drive);
|
||||
// Reset drive encoders since we're starting at 0
|
||||
drive.resetEncoders();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.drivedistanceoffboard.subsystems;
|
||||
|
||||
import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward;
|
||||
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
|
||||
import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile;
|
||||
import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
||||
|
||||
import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.ExampleSmartMotorController;
|
||||
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kLeftMotor1Port;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kLeftMotor2Port;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kRightMotor1Port;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kRightMotor2Port;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kaVoltSecondsSquaredPerMeter;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kp;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.ksVolts;
|
||||
import static edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants.kvVoltSecondsPerMeter;
|
||||
|
||||
public class DriveSubsystem extends SubsystemBase {
|
||||
// The motors on the left side of the drive.
|
||||
private final ExampleSmartMotorController m_leftMaster =
|
||||
new ExampleSmartMotorController(kLeftMotor1Port);
|
||||
|
||||
private final ExampleSmartMotorController m_leftSlave =
|
||||
new ExampleSmartMotorController(kLeftMotor2Port);
|
||||
|
||||
private final ExampleSmartMotorController m_rightMaster =
|
||||
new ExampleSmartMotorController(kRightMotor1Port);
|
||||
|
||||
private final ExampleSmartMotorController m_rightSlave =
|
||||
new ExampleSmartMotorController(kRightMotor2Port);
|
||||
|
||||
private final SimpleMotorFeedforward m_feedforward =
|
||||
new SimpleMotorFeedforward(ksVolts,
|
||||
kvVoltSecondsPerMeter,
|
||||
kaVoltSecondsSquaredPerMeter);
|
||||
|
||||
// The robot's drive
|
||||
private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMaster, m_rightMaster);
|
||||
|
||||
/**
|
||||
* Creates a new DriveSubsystem.
|
||||
*/
|
||||
public DriveSubsystem() {
|
||||
m_leftSlave.follow(m_leftMaster);
|
||||
m_rightSlave.follow(m_rightMaster);
|
||||
|
||||
m_leftMaster.setPID(kp, 0, 0);
|
||||
m_rightMaster.setPID(kp, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drives the robot using arcade controls.
|
||||
*
|
||||
* @param fwd the commanded forward movement
|
||||
* @param rot the commanded rotation
|
||||
*/
|
||||
public void arcadeDrive(double fwd, double rot) {
|
||||
m_drive.arcadeDrive(fwd, rot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to follow the given drive states using offboard PID.
|
||||
*
|
||||
* @param left The left wheel state.
|
||||
* @param right The right wheel state.
|
||||
*/
|
||||
public void setDriveStates(TrapezoidProfile.State left, TrapezoidProfile.State right) {
|
||||
m_leftMaster.setSetpoint(ExampleSmartMotorController.PIDMode.kPosition,
|
||||
left.position,
|
||||
m_feedforward.calculate(left.velocity));
|
||||
m_rightMaster.setSetpoint(ExampleSmartMotorController.PIDMode.kPosition,
|
||||
right.position,
|
||||
m_feedforward.calculate(right.velocity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the left encoder distance.
|
||||
*
|
||||
* @return the left encoder distance
|
||||
*/
|
||||
public double getLeftEncoderDistance() {
|
||||
return m_leftMaster.getEncoderDistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the right encoder distance.
|
||||
*
|
||||
* @return the right encoder distance
|
||||
*/
|
||||
public double getRightEncoderDistance() {
|
||||
return m_rightMaster.getEncoderDistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the drive encoders.
|
||||
*/
|
||||
public void resetEncoders() {
|
||||
m_leftMaster.resetEncoder();
|
||||
m_rightMaster.resetEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max output of the drive. Useful for scaling the drive to drive more slowly.
|
||||
*
|
||||
* @param maxOutput the maximum output to which the drive will be constrained
|
||||
*/
|
||||
public void setMaxOutput(double maxOutput) {
|
||||
m_drive.setMaxOutput(maxOutput);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.elevatortrapezoidprofile;
|
||||
|
||||
import edu.wpi.first.wpilibj.SpeedController;
|
||||
|
||||
/**
|
||||
* A simplified stub class that simulates the API of a common "smart" motor controller.
|
||||
*
|
||||
* <p>Has no actual functionality.
|
||||
*/
|
||||
public class ExampleSmartMotorController implements SpeedController {
|
||||
public enum PIDMode {
|
||||
kPosition,
|
||||
kVelocity,
|
||||
kMovementWitchcraft
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ExampleSmartMotorController.
|
||||
*
|
||||
* @param port The port for the controller.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public ExampleSmartMotorController(int port) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example method for setting the PID gains of the smart controller.
|
||||
*
|
||||
* @param kp The proportional gain.
|
||||
* @param ki The integral gain.
|
||||
* @param kd The derivative gain.
|
||||
*/
|
||||
public void setPID(double kp, double ki, double kd) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example method for setting the setpoint of the smart controller in PID mode.
|
||||
*
|
||||
* @param mode The mode of the PID controller.
|
||||
* @param setpoint The controller setpoint.
|
||||
* @param arbFeedforward An arbitrary feedforward output (from -1 to 1).
|
||||
*/
|
||||
public void setSetpoint(PIDMode mode, double setpoint, double arbFeedforward) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Places this motor controller in follower mode.
|
||||
*
|
||||
* @param master The master to follow.
|
||||
*/
|
||||
public void follow(ExampleSmartMotorController master) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoder distance.
|
||||
*
|
||||
* @return The current encoder distance.
|
||||
*/
|
||||
public double getEncoderDistance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoder rate.
|
||||
*
|
||||
* @return The current encoder rate.
|
||||
*/
|
||||
public double getEncoderRate() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the encoder to zero distance.
|
||||
*/
|
||||
public void resetEncoder() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(double speed) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double get() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInverted(boolean isInverted) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getInverted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopMotor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pidWrite(double output) {
|
||||
}
|
||||
}
|
||||
@@ -7,21 +7,18 @@
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.elevatortrapezoidprofile;
|
||||
|
||||
import edu.wpi.first.wpilibj.Encoder;
|
||||
import edu.wpi.first.wpilibj.Joystick;
|
||||
import edu.wpi.first.wpilibj.PWMVictorSPX;
|
||||
import edu.wpi.first.wpilibj.SpeedController;
|
||||
import edu.wpi.first.wpilibj.TimedRobot;
|
||||
import edu.wpi.first.wpilibj.controller.PIDController;
|
||||
import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward;
|
||||
import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile;
|
||||
|
||||
public class Robot extends TimedRobot {
|
||||
private static double kDt = 0.02;
|
||||
|
||||
private final Joystick m_joystick = new Joystick(1);
|
||||
private final Encoder m_encoder = new Encoder(1, 2);
|
||||
private final SpeedController m_motor = new PWMVictorSPX(1);
|
||||
private final PIDController m_controller = new PIDController(1.3, 0.0, 0.7, kDt);
|
||||
private final ExampleSmartMotorController m_motor = new ExampleSmartMotorController(1);
|
||||
// Note: These gains are fake, and will have to be tuned for your robot.
|
||||
private final SimpleMotorFeedforward m_feedforward = new SimpleMotorFeedforward(1, 1.5);
|
||||
|
||||
private final TrapezoidProfile.Constraints m_constraints =
|
||||
new TrapezoidProfile.Constraints(1.75, 0.75);
|
||||
@@ -30,7 +27,8 @@ public class Robot extends TimedRobot {
|
||||
|
||||
@Override
|
||||
public void robotInit() {
|
||||
m_encoder.setDistancePerPulse(1.0 / 360.0 * 2.0 * Math.PI * 1.5);
|
||||
// Note: These gains are fake, and will have to be tuned for your robot.
|
||||
m_motor.setPID(1.3, 0.0, 0.7);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,10 +48,8 @@ public class Robot extends TimedRobot {
|
||||
// toward the goal while obeying the constraints.
|
||||
m_setpoint = profile.calculate(kDt);
|
||||
|
||||
double output = m_controller.calculate(m_encoder.getDistance(),
|
||||
m_setpoint.position);
|
||||
|
||||
// Run controller with profiled setpoint and update motor output
|
||||
m_motor.set(output);
|
||||
// Send setpoint to offboard controller PID
|
||||
m_motor.setSetpoint(ExampleSmartMotorController.PIDMode.kPosition, m_setpoint.position,
|
||||
m_feedforward.calculate(m_setpoint.velocity) / 12.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +485,7 @@
|
||||
},
|
||||
{
|
||||
"name": "ArmBot",
|
||||
"description": "An example command-based robot demonstrating the use of a ProfiledPIDCommand to control an arm.",
|
||||
"description": "An example command-based robot demonstrating the use of a ProfiledPIDSubsystem to control an arm.",
|
||||
"tags": [
|
||||
"ArmBot",
|
||||
"PID",
|
||||
@@ -496,6 +496,32 @@
|
||||
"mainclass": "Main",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "ArmBotOffboard",
|
||||
"description": "An example command-based robot demonstrating the use of a TrapezoidProfileSubsystem to control an arm with an offboard PID.",
|
||||
"tags": [
|
||||
"ArmBotOffboard",
|
||||
"PID",
|
||||
"Motion Profile"
|
||||
],
|
||||
"foldername": "armbotoffboard",
|
||||
"gradlebase": "java",
|
||||
"mainclass": "Main",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "DriveDistanceOffboard",
|
||||
"description": "An example command-based robot demonstrating the use of a TrapezoidProfileCommand to drive a robot a set distance with offboard PID on the drive.",
|
||||
"tags": [
|
||||
"DriveDistance",
|
||||
"PID",
|
||||
"Motion Profile"
|
||||
],
|
||||
"foldername": "drivedistanceoffboard",
|
||||
"gradlebase": "java",
|
||||
"mainclass": "Main",
|
||||
"commandversion": 2
|
||||
},
|
||||
{
|
||||
"name": "MecanumControllerCommand",
|
||||
"description": "An example command-based robot demonstrating the use of a MecanumControllerCommand to follow a pregenerated trajectory.",
|
||||
|
||||
@@ -43,6 +43,9 @@ public final class Constants {
|
||||
public static final double kTurnI = 0;
|
||||
public static final double kTurnD = 0;
|
||||
|
||||
public static final double kMaxTurnRateDegPerS = 100;
|
||||
public static final double kMaxTurnAccelerationDegPerSSquared = 300;
|
||||
|
||||
public static final double kTurnToleranceDeg = 5;
|
||||
public static final double kTurnRateToleranceDegPerS = 10; // degrees per second
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import edu.wpi.first.wpilibj2.command.RunCommand;
|
||||
import edu.wpi.first.wpilibj2.command.button.JoystickButton;
|
||||
|
||||
import edu.wpi.first.wpilibj.examples.gyrodrivecommands.commands.TurnToAngle;
|
||||
import edu.wpi.first.wpilibj.examples.gyrodrivecommands.commands.TurnToAngleProfiled;
|
||||
import edu.wpi.first.wpilibj.examples.gyrodrivecommands.subsystems.DriveSubsystem;
|
||||
|
||||
import static edu.wpi.first.wpilibj.XboxController.Button;
|
||||
@@ -85,6 +86,10 @@ public class RobotContainer {
|
||||
// Turn to 90 degrees when the 'X' button is pressed, with a 5 second timeout
|
||||
new JoystickButton(m_driverController, Button.kX.value)
|
||||
.whenPressed(new TurnToAngle(90, m_robotDrive).withTimeout(5));
|
||||
|
||||
// Turn to -90 degrees with a profile when the 'A' button is pressed, with a 5 second timeout
|
||||
new JoystickButton(m_driverController, Button.kA.value)
|
||||
.whenPressed(new TurnToAngleProfiled(-90, m_robotDrive).withTimeout(5));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.wpilibj.examples.gyrodrivecommands.commands;
|
||||
|
||||
import edu.wpi.first.wpilibj.controller.ProfiledPIDController;
|
||||
import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile;
|
||||
import edu.wpi.first.wpilibj2.command.ProfiledPIDCommand;
|
||||
|
||||
import edu.wpi.first.wpilibj.examples.gyrodrivecommands.subsystems.DriveSubsystem;
|
||||
|
||||
import static edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants.kMaxTurnAccelerationDegPerSSquared;
|
||||
import static edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants.kMaxTurnRateDegPerS;
|
||||
import static edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants.kTurnD;
|
||||
import static edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants.kTurnI;
|
||||
import static edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants.kTurnP;
|
||||
import static edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants.kTurnRateToleranceDegPerS;
|
||||
import static edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants.kTurnToleranceDeg;
|
||||
|
||||
/**
|
||||
* A command that will turn the robot to the specified angle using a motion profile.
|
||||
*/
|
||||
public class TurnToAngleProfiled extends ProfiledPIDCommand {
|
||||
/**
|
||||
* Turns to robot to the specified angle using a motion profile.
|
||||
*
|
||||
* @param targetAngleDegrees The angle to turn to
|
||||
* @param drive The drive subsystem to use
|
||||
*/
|
||||
public TurnToAngleProfiled(double targetAngleDegrees, DriveSubsystem drive) {
|
||||
super(
|
||||
new ProfiledPIDController(kTurnP, kTurnI, kTurnD,
|
||||
new TrapezoidProfile.Constraints(
|
||||
kMaxTurnRateDegPerS,
|
||||
kMaxTurnAccelerationDegPerSSquared
|
||||
)),
|
||||
// Close loop on heading
|
||||
drive::getHeading,
|
||||
// Set reference to target
|
||||
targetAngleDegrees,
|
||||
// Pipe output to turn robot
|
||||
(output, setpoint) -> drive.arcadeDrive(0, output),
|
||||
// Require the drive
|
||||
drive);
|
||||
|
||||
// Set the controller to be continuous (because it is an angle controller)
|
||||
getController().enableContinuousInput(-180, 180);
|
||||
// Set the controller tolerance - the delta tolerance ensures the robot is stationary at the
|
||||
// setpoint before it is considered as having reached the reference
|
||||
getController().setTolerance(kTurnToleranceDeg, kTurnRateToleranceDegPerS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
// End when the controller is at the reference.
|
||||
return getController().atGoal();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user