Skip to content

Commit

Permalink
Have the autos execute as commands instead of running in their own th…
Browse files Browse the repository at this point in the history
…read
  • Loading branch information
varun7654 committed Nov 6, 2023
1 parent 4c948f1 commit d0d1fe5
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 503 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,75 @@
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.trajectory.Trajectory;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj2.command.CommandBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;

import static com.dacubeking.AutoBuilder.robot.robotinterface.AutonomousContainer.getCommandTranslator;
public class GuiAuto extends CommandBase {

public class GuiAuto implements Runnable {

@Override
public void execute() {
if (isFirstRun) {
if (initialPose != null) {
//TODO: Set initial pose
AutonomousContainer.getInstance().printDebug("Set initial pose: " + initialPose);
} else {
AutonomousContainer.getInstance().printDebug("No initial pose set");
}

if (!abstractAutonomousSteps.isEmpty()) {
abstractAutonomousSteps.peek().initialize();
}

isFirstRun = false;
}

if (abstractAutonomousSteps.isEmpty()) {
return;
}

AbstractAutonomousStep autonomousStep = abstractAutonomousSteps.peek();

try {
if (autonomousStep.execute(scriptsToExecuteByTime, scriptsToExecuteByPercent)) {
autonomousStep.end();
abstractAutonomousSteps.remove();
if (!abstractAutonomousSteps.isEmpty()) {
abstractAutonomousSteps.peek().initialize();
}

//Sort the lists to make sure they are sorted by time
Collections.sort(scriptsToExecuteByTime);
Collections.sort(scriptsToExecuteByPercent);
}
} catch (CommandExecutionFailedException | ExecutionException e) {
DriverStation.reportError("Failed to execute autonomous step. " + e.getMessage(), e.getStackTrace());
abstractAutonomousSteps.clear(); // Will end the auto
}
}

@Override
public void end(boolean interrupted) {
if (!abstractAutonomousSteps.isEmpty()) {
abstractAutonomousSteps.peek().end();
}

abstractAutonomousSteps.clear();
}

@Override
public boolean isFinished() {
return abstractAutonomousSteps.isEmpty();
}

private static final Autonomous DO_NOTHING_AUTONOMOUS = new Autonomous(new ArrayList<>());
private @NotNull Autonomous autonomous = DO_NOTHING_AUTONOMOUS; // default to do nothing in case of some error
Expand All @@ -37,7 +93,8 @@ public class GuiAuto implements Runnable {
public GuiAuto(File autonomousFile) throws IOException {
autonomous = (Autonomous) Serializer.deserializeFromFile(autonomousFile, Autonomous.class,
autonomousFile.getName().endsWith(".json"));
init();
this.setName(autonomousFile.getPath() + "GuiAuto");
initialize();
}

/**
Expand All @@ -53,85 +110,35 @@ public GuiAuto(String autonomousJson) {
DriverStation.reportError("Failed to deserialize auto. " + e.getMessage(), e.getStackTrace());
// The do nothing auto will be used
}
init();
this.setName("JsonGuiAuto");
initialize();
}

/**
* Finds and saves the initial pose of the robot.
*/
private void init() {
@Override
public void initialize() {
for (AbstractAutonomousStep autonomousStep : autonomous.getAutonomousSteps()) {
if (autonomousStep instanceof TrajectoryAutonomousStep) {
TrajectoryAutonomousStep trajectoryAutonomousStep = (TrajectoryAutonomousStep) autonomousStep;
if (autonomousStep instanceof TrajectoryAutonomousStep trajectoryAutonomousStep) {
Trajectory.State initialState = trajectoryAutonomousStep.getTrajectory().getStates().get(0);
initialPose = new Pose2d(initialState.poseMeters.getTranslation(),
trajectoryAutonomousStep.getRotations().get(0).getRotation());
break;
}
}
}

/**
* Runs the autonomous.
*/
@Override
public void run() {
AutonomousContainer.getInstance().isInitialized();

Thread.currentThread().setUncaughtExceptionHandler((t, e) -> {
DriverStation.reportError("Uncaught exception in auto thread: " + e.getMessage(), e.getStackTrace());
getCommandTranslator().stopRobot();
});

if (autonomous == DO_NOTHING_AUTONOMOUS) {
DriverStation.reportError("No auto was loaded. Doing nothing.", false);
return;
}

AutonomousContainer.getInstance().printDebug("Started Running: " + Timer.getFPGATimestamp());


//Set our initial pose in our robot tracker
if (initialPose != null) {
getCommandTranslator().setRobotPose(initialPose);
AutonomousContainer.getInstance().printDebug("Set initial pose: " + initialPose);
} else {
AutonomousContainer.getInstance().printDebug("No initial pose set");
}

//Loop though all the steps and execute them
List<SendableScript> scriptsToExecuteByTime = new ArrayList<>();
List<SendableScript> scriptsToExecuteByPercent = new ArrayList<>();

for (AbstractAutonomousStep autonomousStep : autonomous.getAutonomousSteps()) {
AutonomousContainer.getInstance().printDebug("Doing a step: " + Timer.getFPGATimestamp());

if (Thread.interrupted()) {
getCommandTranslator().stopRobot();
AutonomousContainer.getInstance().printDebug("Auto was interrupted " + Timer.getFPGATimestamp());
return;
}
scriptsToExecuteByPercent = new ArrayList<>();
scriptsToExecuteByTime = new ArrayList<>();
abstractAutonomousSteps = new LinkedList<>(autonomous.getAutonomousSteps());
isFirstRun = true;
}

try {
autonomousStep.execute(scriptsToExecuteByTime, scriptsToExecuteByPercent);
} catch (InterruptedException e) {
getCommandTranslator().stopRobot();
AutonomousContainer.getInstance().printDebug("Auto prematurely stopped at " + Timer.getFPGATimestamp() +
". This is not an error if you disabled your robot.");
if (AutonomousContainer.getInstance().areDebugPrintsEnabled()) {
e.printStackTrace();
}
return;
} catch (CommandExecutionFailedException | ExecutionException e) {
getCommandTranslator().stopRobot();
e.printStackTrace(); // We should always print this out since it is a fatal error
return;
}
}
private List<SendableScript> scriptsToExecuteByTime;
private List<SendableScript> scriptsToExecuteByPercent;
private LinkedList<AbstractAutonomousStep> abstractAutonomousSteps;

System.out.println("Finished Autonomous at " + Timer.getFPGATimestamp());
getCommandTranslator().stopRobot();
}
private boolean isFirstRun = true;

/**
* Gets the initial pose of the robot.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.dacubeking.AutoBuilder.robot.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation that marks that this field/method is only used when running on the robot
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
public @interface AutoBuilderRobotSide {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.dacubeking.AutoBuilder.robot.command;

import com.dacubeking.AutoBuilder.robot.robotinterface.TrajectoryBuilderInfo;
import edu.wpi.first.math.controller.HolonomicDriveController;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.controller.ProfiledPIDController;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
import edu.wpi.first.math.kinematics.SwerveModuleState;
import edu.wpi.first.math.trajectory.Trajectory;
import edu.wpi.first.wpilibj2.command.Subsystem;
import edu.wpi.first.wpilibj2.command.SwerveControllerCommand;

import java.util.function.Consumer;
import java.util.function.Supplier;

import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;

/**
* {@link SwerveControllerCommand}, but adds a check for position error before finishing a path, and removes constructors the
* imply the heading from the trajectory. (as the {@link TrajectoryBuilderInfo#desiredHeadingSupplier()} should be passed to the
* desiredRotation
*/
public class AutoBuilderSwerveControllerCommand extends SwerveControllerCommand {
/**
* Constructs a new SwerveControllerCommand that when executed will follow the provided trajectory. This command will not
* return output voltages but rather raw module states from the position controllers which need to be put into a velocity
* PID.
*
* <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path.
* This is left to the user to do since it is not appropriate for paths with nonstationary endstates.
*
* @param trajectory The trajectory to follow.
* @param pose A function that supplies the robot pose - use one of the odometry classes to provide this.
* @param kinematics The kinematics for the robot drivetrain.
* @param xController The Trajectory Tracker PID controller for the robot's x position.
* @param yController The Trajectory Tracker PID controller for the robot's y position.
* @param thetaController The Trajectory Tracker PID controller for angle for the robot.
* @param tolerance tolerance for the HolonomicDriveController. Will prevent the path from completing if the robot is
* not within the defined tolerance. see: {@link HolonomicDriveController#setTolerance(Pose2d)}
* @param desiredRotation The angle that the drivetrain should be facing. This is sampled at each time step.
* @param outputModuleStates The raw output module states from the position controllers.
* @param requirements The subsystems to require.
*/
public AutoBuilderSwerveControllerCommand(Trajectory trajectory,
Supplier<Pose2d> pose,
SwerveDriveKinematics kinematics,
PIDController xController,
PIDController yController,
ProfiledPIDController thetaController,
Pose2d tolerance,
Supplier<Rotation2d> desiredRotation,
Consumer<SwerveModuleState[]> outputModuleStates,
Subsystem... requirements) {

this(
trajectory,
pose,
kinematics,
new HolonomicDriveController(
requireNonNullParam(xController, "xController", "SwerveControllerCommand"),
requireNonNullParam(yController, "yController", "SwerveControllerCommand"),
requireNonNullParam(thetaController, "thetaController", "SwerveControllerCommand")),
tolerance,
desiredRotation,
outputModuleStates,
requirements);
}

/**
* Constructs a new SwerveControllerCommand that when executed will follow the provided trajectory. This command will not
* return output voltages but rather raw module states from the position controllers which need to be put into a velocity
* PID.
*
* <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path-
* this is left to the user, since it is not appropriate for paths with nonstationary endstates.
*
* @param trajectory The trajectory to follow.
* @param pose A function that supplies the robot pose - use one of the odometry classes to provide this.
* @param kinematics The kinematics for the robot drivetrain.
* @param controller The HolonomicDriveController for the drivetrain.
* @param tolerance tolerance for the HolonomicDriveController. Will prevent the path from completing if the robot is
* not within the defined tolerance. see: {@link HolonomicDriveController#setTolerance(Pose2d)}
* @param desiredRotation The angle that the drivetrain should be facing. This is sampled at each time step.
* @param outputModuleStates The raw output module states from the position controllers.
* @param requirements The subsystems to require.
*/
public AutoBuilderSwerveControllerCommand(Trajectory trajectory, Supplier<Pose2d> pose, SwerveDriveKinematics kinematics,
HolonomicDriveController controller,
Pose2d tolerance, Supplier<Rotation2d> desiredRotation,
Consumer<SwerveModuleState[]> outputModuleStates, Subsystem... requirements) {
super(trajectory, pose, kinematics, controller, desiredRotation, outputModuleStates, requirements);
this.controller = controller;
controller.setTolerance(tolerance);
}

private final HolonomicDriveController controller;

@Override
public boolean isFinished() {
assert controller != null;
return controller.atReference();
}
}
Loading

0 comments on commit d0d1fe5

Please sign in to comment.