diff --git a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java index 7695d159..d6f041d7 100644 --- a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java +++ b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java @@ -12,12 +12,14 @@ import com.ctre.phoenix6.signals.InvertedValue; import com.ctre.phoenix6.signals.MotorAlignmentValue; import first.robot.simulation.DrivetrainSim; +import first.robot.simulation.FuelSim; import first.robot.simulation.SingleFlywheelSim; import org.wpilib.drive.DifferentialDrive; import org.wpilib.framework.OpModeRobot; import org.wpilib.hardware.imu.OnboardIMU; import org.wpilib.hardware.imu.OnboardIMU.MountOrientation; +// [FullRobot] // [RobotWithSimPart1] // [RobotTop] /** @@ -56,12 +58,19 @@ public class Robot extends OpModeRobot { // [/DrivetrainSim] // [/RobotWithSimPart1] + // [AdditionalMotors] public TalonFX intakeLauncher = new TalonFX(4, CANBus.systemcore(0)); public TalonFX feeder = new TalonFX(5, CANBus.systemcore(0)); + // [/AdditionalMotors] + // [IntakeLauncherSim] private SingleFlywheelSim intakeLauncherSim = SingleFlywheelSim.forIntakeLauncher(intakeLauncher); + // [/IntakeLauncherSim] + // [FeederSim] private SingleFlywheelSim feederSim = SingleFlywheelSim.forFeeder(feeder); + // [/FeederSim] + // [RobotWithSimPart2] // [AllConfigs] /** @@ -99,8 +108,15 @@ public Robot() { public void simulationPeriodic() { drivetrainSim.periodic(); // [/DriveSimPeriodic] + // [MotorSimPeriodic] intakeLauncherSim.periodic(); feederSim.periodic(); + // [/MotorSimPeriodic] + + // [FuelSimPeriodic] + FuelSim.periodic(); + // [/FuelSimPeriodic] } // [/RobotWithSimPart2] + // [/FullRobot] } diff --git a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyAuto.java b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyAuto.java index 0e2ce261..2f56822c 100644 --- a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyAuto.java +++ b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyAuto.java @@ -25,6 +25,7 @@ public void start() { autoTimer.restart(); // Reset the timer to zero at the start of auto } + // [SimpleAuto] /* * This method runs periodically, using the same period as the Robot instance. * @@ -39,4 +40,5 @@ public void periodic() { robot.drivetrain.arcadeDrive(0.5, 0.0); // Drive forward at half speed with no rotation } } + // [/SimpleAuto] } diff --git a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java index 29b59bb6..cffbacca 100644 --- a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java +++ b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java @@ -32,25 +32,33 @@ public void periodic() { // [/DriveSimPeriodic] // [/FullDrivetrain] + // [launchFuel] if (xboxController.getRightBumperButton()) { - // shoot + // launch robot.intakeLauncher.setThrottle(0.9); robot.feeder.setThrottle(0.75); + // [/launchFuel] + // [intakeFuel] } else if (xboxController.getLeftBumperButton()) { // intake robot.intakeLauncher.setThrottle(0.8); robot.feeder.setThrottle(-1.0); + // [/intakeFuel] + // [outakeFuel] } else if (xboxController.getAButton()) { // outake robot.intakeLauncher.setThrottle(-0.8); robot.feeder.setThrottle(1.0); + // [/outakeFuel] + // [stopMotors] } else { // stop robot.intakeLauncher.setThrottle(0.0); robot.feeder.setThrottle(0.0); } + // [/stopMotors] } } diff --git a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/simulation/FuelSim.java b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/simulation/FuelSim.java index 60a7b648..daa5096d 100644 --- a/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/simulation/FuelSim.java +++ b/examples/stage1/stage1a/solutions/ctre/src/main/java/first/robot/simulation/FuelSim.java @@ -54,7 +54,7 @@ private enum Mode { static Supplier robotPoseSupplier = () -> Pose2d.kZero; /** Updates the fuel sim. */ - public static void update() { + public static void periodic() { updateMode(); updateVisualization(); } diff --git a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java index 074ef915..77d03bf3 100644 --- a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java +++ b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java @@ -18,6 +18,7 @@ import org.wpilib.hardware.imu.OnboardIMU; import org.wpilib.hardware.imu.OnboardIMU.MountOrientation; +// [FullRobot] // [RobotWithSimPart1] // [RobotTop] /** @@ -53,12 +54,19 @@ public class Robot extends OpModeRobot { // [/DrivetrainSim] // [/RobotWithSimPart1] + // [AdditionalMotors] public SparkMax intakeLauncher = new SparkMax(0, 4, MotorType.kBrushless); public SparkMax feeder = new SparkMax(0, 5, MotorType.kBrushless); + // [/AdditionalMotors] + // [IntakeLauncherSim] private SingleFlywheelSim intakeLauncherSim = SingleFlywheelSim.forIntakeLauncher(intakeLauncher); + // [/IntakeLauncherSim] + // [FeederSim] private SingleFlywheelSim feederSim = SingleFlywheelSim.forFeeder(feeder); + // [/FeederSim] + // [RobotWithSimPart2] // [AllConfigs] /** @@ -101,8 +109,14 @@ public void simulationPeriodic() { drivetrainSim.periodic(); // [/DriveSimPeriodic] intakeLauncherSim.periodic(); + // [MotorSimPeriodic] feederSim.periodic(); - FuelSim.update(); + // [/MotorSimPeriodic] + + // [FuelSimPeriodic] + FuelSim.periodic(); + // [/FuelSimPeriodic] } // [/RobotWithSimPart2] } +// [/FullRobot] diff --git a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyAuto.java b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyAuto.java index 826a6956..c06c9743 100644 --- a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyAuto.java +++ b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyAuto.java @@ -15,6 +15,7 @@ public class MyAuto extends PeriodicOpMode { private final Robot robot; private Timer timer = new Timer(); + /** The Robot instance is passed into the opmode via the constructor. */ public MyAuto(Robot robot) { this.robot = robot; } @@ -24,6 +25,13 @@ public void start() { timer.restart(); } + // [SimpleAuto] + /* + * This method runs periodically, using the same period as the Robot instance. + * + * Additional periodic methods may be configured with addPeriodic(), + * which can have periods that differ from the main Robot instance. + */ @Override public void periodic() { if (timer.hasElapsed(4)) { @@ -32,4 +40,5 @@ public void periodic() { robot.drivetrain.arcadeDrive(0.5, 0.0); // Drive forward at half speed with no rotation } } + // [/SimpleAuto] } diff --git a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java index 50e6fa0c..5ae1e5e3 100644 --- a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java +++ b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java @@ -31,22 +31,33 @@ public void periodic() { // [/DriveSimPeriodic] // [/FullDrivetrain] + // [launchFuel] if (xboxController.getRightBumperButton()) { - // shoot + // launch robot.intakeLauncher.setThrottle(0.9); robot.feeder.setThrottle(0.75); + + // [/launchFuel] + // [intakeFuel] } else if (xboxController.getLeftBumperButton()) { // intake robot.intakeLauncher.setThrottle(0.8); robot.feeder.setThrottle(-1.0); + + // [/intakeFuel] + // [outakeFuel] } else if (xboxController.getAButton()) { // outtake robot.intakeLauncher.setThrottle(-0.8); robot.feeder.setThrottle(1.0); + + // [/outakeFuel] + // [stopMotors] } else { // stop robot.intakeLauncher.setThrottle(0.0); robot.feeder.setThrottle(0.0); } + // [/stopMotors] } } diff --git a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/simulation/FuelSim.java b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/simulation/FuelSim.java index 60a7b648..daa5096d 100644 --- a/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/simulation/FuelSim.java +++ b/examples/stage1/stage1a/solutions/rev/src/main/java/first/robot/simulation/FuelSim.java @@ -54,7 +54,7 @@ private enum Mode { static Supplier robotPoseSupplier = () -> Pose2d.kZero; /** Updates the fuel sim. */ - public static void update() { + public static void periodic() { updateMode(); updateVisualization(); } diff --git a/public/learning-course/stage1/stage1a/3dField.mp4 b/public/learning-course/stage1/stage1a/3dField.mp4 new file mode 100644 index 00000000..7a9675ff Binary files /dev/null and b/public/learning-course/stage1/stage1a/3dField.mp4 differ diff --git a/public/learning-course/stage1/stage1a/LineGraph.mp4 b/public/learning-course/stage1/stage1a/LineGraph.mp4 new file mode 100644 index 00000000..546d5c11 Binary files /dev/null and b/public/learning-course/stage1/stage1a/LineGraph.mp4 differ diff --git a/public/learning-course/stage1/stage1a/RunningMyAuto.webm b/public/learning-course/stage1/stage1a/RunningMyAuto.webm new file mode 100644 index 00000000..8244ad88 Binary files /dev/null and b/public/learning-course/stage1/stage1a/RunningMyAuto.webm differ diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts index aa086a4e..f65f3455 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -107,14 +107,14 @@ export const sidebarSections: Record = { label: 'Drivetrain Simulation', slug: 'learning-course/stage1/stage1a/drivetrain-sim', }, - // { - // label: 'TBD', - // slug: 'stage-1a-commands/the-command-body', - // }, - // { - // label: 'TBD', - // slug: 'stage-1a-commands/commands-and-mechanisms', - // }, + { + label: 'Simple Auto', + slug: 'learning-course/stage1/stage1a/simple-auto', + }, + { + label: 'Additional Motors', + slug: 'learning-course/stage1/stage1a/kitbot-additional-motors', + }, ], }, { diff --git a/src/content/docs/learning-course/stage1/stage1a/kitbot-additional-motors.mdx b/src/content/docs/learning-course/stage1/stage1a/kitbot-additional-motors.mdx new file mode 100644 index 00000000..dda45286 --- /dev/null +++ b/src/content/docs/learning-course/stage1/stage1a/kitbot-additional-motors.mdx @@ -0,0 +1,257 @@ +--- +title: Kitbot Additional Motors +description: Programming the remaining kitbot motors +prev: simple-auto +next: false +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; +import { REV_CTRE_CHOOSER_KEY } from '@data/tabsSyncKeys.ts'; + +# Remaining Motors + +The kitbot has two additional motors that allow it to intake and shoot fuel. +The IntakeLauncher motor powers the intake roller and the launcher flywheel while the Feeder motor feeds fuel into the hopper, into the launcher, or out of the intake. +The IntakeLauncher motor will be on CAN Bus 0 with a CAN Id of 4 while the Feeder motor will be on CAN Bus 0 with a CAN Id of 5. + +Inside of the `Robot.java` file create a motor controller instance for the IntakeLauncher and Feeder motors. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#AdditionalMotors + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#AdditionalMotors + ``` + + + + + +# Simulating Additional Motors + +Like the drivetrain, the `SingleFlywheelSim` class is provided which abstracts much of the simulation code. +The `SingleFlywheelSim` has different static methods for the LauncherFeeder and Intake motors which return instances of `SingleFlywheelSim` with settings for those specific motors. + +Creating the `SingleFlywheelSim` instance for the LauncherFeeder motor will look like this + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#IntakeLauncherSim + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#IntakeLauncherSim + ``` + + + + + +Now try creating the `SingleFlywheelSim` instance for the Intake motor. +This will instead use the `forFeeder()` method. + +
+ Solution + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#FeederSim + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#FeederSim + ``` + + + + +
+ +To make the simulated motor controllers update, the `SingleFlywheelSim` instances' `periodic()` functions need to be called inside of `simulationPeriodic()`. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#MotorSimPeriodic + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#MotorSimPeriodic + ``` + + + + + +# Fuel Sim + +If the code was to be simulated at this point, the motors could be controlled using keyboard inputs and the resulting motor speeds could be viewed with a graph inside of Advantage Scope. +While this is a functional solution, it can be made more interesting by taking advantage of Advantage Scope's 3d tab. +Advantage Scope's 3d tab displays robots and game pieces inside of a game field using poses published from the robot code. +This allows fuel to be shown entering or exiting the robot while is intaking or launching. + +The `FuelSim` class is provided which takes the current state of the IntakeLauncher and Feeder motors and publishes new poses for the fuel to be visualized at in Advantage Scope. +References to the IntakeLauncher and Feeder motors are given to the `FuelSim` class when the `SingleFlywheelSim` instances are created. + +To make the `FuelSim` class function, its `periodic()` function needs to be called inside of `simulationPeriodic()`. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#FuelSimPeriodic + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#FuelSimPeriodic + ``` + + + + + +# Check Up + +The `Robot` class is now completed! +At this point your `Robot` class should look like this + +
+ Robot Solution + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#FullRobot + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#FullRobot + ``` + + + + +
+ +# Additional Motors in Teleop + +To control the new motors, the motors need to be commanded inside of the `MyTeleop` `OpMode`'s `periodic()` function. +When the controller's right bumper is pressed, the IntakeLauncher motor should have a throttle of 0.9 while the Feeder motor should have a throttle of 0.75. +This will launch the fuel. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java#launchFuel + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java#launchFuel + ``` + + + + + +When the controller's left bumper is pressed the IntakeLauncher motor should have a throttle of 0.8 while the Feeder motor should have a throttle of -1.0. +This will intake the fuel. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java#intakeFuel + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java#intakeFuel + ``` + + + + + +When the controller's A button is pressed the IntakeLauncher motor should have a throttle of -0.8 while the Feeder motor should have a throttle of 1.0. +This will outake the fuel. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java#outakeFuel + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java#outakeFuel + ``` + + + + + +When none of the previous button are pressed the IntakeLauncher should have a throttle of 0.0 while the Feeder motor should have a throttle of 0.0. +This will stop the motors. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java#stopMotors + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java#stopMotors + ``` + + + + + +# Line Graph + +The process for testing the new motors in teleop is very similar to the process used to test the drivetrain. +Holding the "E" key will cause the robot to intake, "Q" will cause the robot to launch. +and "R" will cause the robot to outake. +Instead of looking at the 2D Field tab in Advantage Scope, use the Line Graph tab to view data from the motors. +The voltage applied from the motor controllers to their motors can be viewed on the left axis while the right axis shows the motors' velocities. + +Video showing line 2d graph running goes here. + + + +The 3D Field tab will show a 3D rendering of the kitbot, fuel, and game field. +As the robot preforms different actions the fuel should appear or disappear to show these actions. +You should also still be able to move using the WASD keys. + + + +# Additional Practice + +Try improving upon previously created autos by adding robot actions too them. diff --git a/src/content/docs/learning-course/stage1/stage1a/simple-auto.mdx b/src/content/docs/learning-course/stage1/stage1a/simple-auto.mdx new file mode 100644 index 00000000..123a236f --- /dev/null +++ b/src/content/docs/learning-course/stage1/stage1a/simple-auto.mdx @@ -0,0 +1,57 @@ +--- +title: Simple Auto +description: Programming a time based autonomous +prev: drivetrain-sim +next: kitbot-additional-motors +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; +import { REV_CTRE_CHOOSER_KEY } from '@data/tabsSyncKeys.ts'; + +# Autonomous OpMode + +Now that the kitbot can move in teleop mode, it is time to make it move during the auto mode. +To do this, another `OpMode` needs to be written that contains all the code needed to make the robot do what's wanted in auto. +Additionally the `@Autonomous` annotation is added above the class definition so it shows up in the driverstation under autonomous OpModes. +This auto will be a timed based auto, meaning everything the robot does will be based on how long it has been since the start of autonomous. +To keep track of how long it has been since the start of auto an instance of `Timer` is created in the OpMode and reset when the robot is enabled. + +The code for the auto should be added to the `MyAuto.java` file inside the opmode folder. +The goal of the auto is to drive forward at half speed for four seconds then stop. +This can be accomplished by periodically checking if it has been four seconds since the start of auto using the `Timer` instance. +If it has been at least four seconds then command the drivetrain with zero speed. +Otherwise command the drivetrain to drive forward at half speed. +Writing that out in code will look like this + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyAuto.java#SimpleAuto + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyAuto.java#SimpleAuto + ``` + + + + + +# Running Your Auto + +Testing the auto routine is similar to testing the teleop drivetrain code. +However, instead of selecting the teleop mode the autonomous mode and MyAuto opmode should be selected. + + + +# Practice + +Try adding on to this auto yourself. +For example, the robot could drive forward at half speed for four seconds, turn counter clockwise for two seconds, then drive forward at full speed for three seconds. +You could also try creating additional autonomous OpModes so you can select between the different autos you have created.