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 8625e5c8..a62663a2 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 @@ -18,6 +18,7 @@ import org.wpilib.hardware.imu.OnboardIMU; import org.wpilib.hardware.imu.OnboardIMU.MountOrientation; +// [RobotTop] /** * The methods in this class are called automatically as described in the OpModeRobot documentation. * OpMode classes anywhere in the package (or sub-packages) where this class is located are @@ -27,47 +28,75 @@ */ public class Robot extends OpModeRobot { + // [DriveMotorsLeft] private final int leftLeaderID = 0; public TalonFX leftLeader = new TalonFX(leftLeaderID, CANBus.systemcore(0)); private TalonFX leftFollower = new TalonFX(1, CANBus.systemcore(0)); + // [/DriveMotorsLeft] + // [DriveMotorsRight] private final int rightLeaderID = 2; public TalonFX rightLeader = new TalonFX(rightLeaderID, CANBus.systemcore(0)); private TalonFX rightFollower = new TalonFX(3, CANBus.systemcore(0)); + // [/DriveMotorsRight] - public TalonFX intakeLauncher = new TalonFX(4, CANBus.systemcore(0)); - public TalonFX feeder = new TalonFX(5, CANBus.systemcore(0)); - - private OnboardIMU imu = new OnboardIMU(MountOrientation.FLAT); - + // [DrivetrainInstance] public final DifferentialDrive drivetrain = new DifferentialDrive(leftLeader::setThrottle, rightLeader::setThrottle); + // [/DrivetrainInstance] + + // [IMU] + private OnboardIMU imu = new OnboardIMU(MountOrientation.FLAT); + // [/IMU] + // [/RobotTop] + // [DrivetrainSim] private DrivetrainSim drivetrainSim = new DrivetrainSim(leftLeader, rightLeader); + // [/DrivetrainSim] + + public TalonFX intakeLauncher = new TalonFX(4, CANBus.systemcore(0)); + public TalonFX feeder = new TalonFX(5, CANBus.systemcore(0)); + private SingleFlywheelSim intakeLauncherSim = new SingleFlywheelSim(intakeLauncher, "intakeLauncher"); private SingleFlywheelSim feederSim = new SingleFlywheelSim(feeder, "feeder"); + // [AllConfigs] /** * This function is run when the robot is first started up and should be used for any * initialization code. */ public Robot() { + // [MotorConfigCreationLeft] var leftConfig = new TalonFXConfiguration(); + // [/MotorConfigCreationLeft] + // [MotorConfigSetLeft] leftConfig.MotorOutput.withInverted(InvertedValue.Clockwise_Positive); + // [/MotorConfigSetLeft] + // [MotorConfigLeft] leftLeader.getConfigurator().apply(leftConfig); + leftFollower.getConfigurator().apply(leftConfig); + leftFollower.setControl(new Follower(leftLeaderID, MotorAlignmentValue.Aligned)); + // [/MotorConfigLeft] + + // [MotorConfig] var rightConfig = new TalonFXConfiguration(); rightConfig.MotorOutput.withInverted(InvertedValue.CounterClockwise_Positive); rightLeader.getConfigurator().apply(rightConfig); + rightFollower.getConfigurator().apply(leftConfig); - leftFollower.setControl(new Follower(leftLeaderID, MotorAlignmentValue.Aligned)); rightFollower.setControl(new Follower(rightLeaderID, MotorAlignmentValue.Aligned)); + // [/MotorConfig] } + // [/AllConfigs] + + // [DriveSimPeriodic] @Override public void simulationPeriodic() { drivetrainSim.periodic(); + // [/DriveSimPeriodic] intakeLauncherSim.periodic(); feederSim.periodic(); } 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 316f7dce..7c849cfa 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 @@ -13,17 +13,22 @@ @Teleop public class MyTeleop extends PeriodicOpMode { private final Robot robot; + // [Controller] private final NiDsXboxController xboxController = new NiDsXboxController(0); + // [/Controller] + /** The Robot instance is passed into the opmode via the constructor. */ public MyTeleop(Robot robot) { this.robot = robot; } + // [DriveSimPeriodic] @Override public void periodic() { /* Called periodically (set time interval) while the robot is enabled. */ robot.drivetrain.arcadeDrive(-xboxController.getLeftY(), xboxController.getRightX()); + // [/DriveSimPeriodic] if (xboxController.getRightBumperButton()) { // shoot 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 65dcdd60..76608356 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 @@ -17,16 +17,39 @@ import org.wpilib.hardware.imu.OnboardIMU; import org.wpilib.hardware.imu.OnboardIMU.MountOrientation; +// [RobotTop] +/** + * The methods in this class are called automatically as described in the OpModeRobot documentation. + * OpMode classes anywhere in the package (or sub-packages) where this class is located are + * automatically registered to display in the Driver Station. If you change the name of this class + * or the package after creating this project, you must also update the Main.java file in the + * project. + */ public class Robot extends OpModeRobot { + // [DriveMotorsLeft] private SparkMax leftLeader = new SparkMax(0, 0, MotorType.kBrushless); private SparkMax leftFollower = new SparkMax(0, 1, MotorType.kBrushless); + // [/DriveMotorsLeft] + // [DriveMotorsRight] private SparkMax rightLeader = new SparkMax(0, 2, MotorType.kBrushless); private SparkMax rightFollower = new SparkMax(0, 3, MotorType.kBrushless); + // [/DriveMotorsRight] + // [DrivetrainInstance] + public final DifferentialDrive drivetrain = + new DifferentialDrive(leftLeader::setThrottle, rightLeader::setThrottle); + // [/DrivetrainInstance] + + // [IMU] private OnboardIMU imu = new OnboardIMU(MountOrientation.FLAT); + // [/IMU] + // [/RobotTop] + // [DrivetrainSim] private DrivetrainSim drivetrainSim = new DrivetrainSim(leftLeader, rightLeader); + // [/DrivetrainSim] + public SparkMax intakeLauncher = new SparkMax(0, 4, MotorType.kBrushless); public SparkMax feeder = new SparkMax(0, 5, MotorType.kBrushless); @@ -34,31 +57,46 @@ public class Robot extends OpModeRobot { new SingleFlywheelSim(intakeLauncher, "IntakeLauncher"); private SingleFlywheelSim feederSim = new SingleFlywheelSim(feeder, "Feeder"); - public final DifferentialDrive drivetrain = - new DifferentialDrive(leftLeader::setThrottle, rightLeader::setThrottle); - + // [AllConfigs] + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ public Robot() { - - var leftConfig = new SparkMaxConfig().inverted(true); + // [MotorConfigCreationLeft] + var leftConfig = new SparkMaxConfig(); + // [/MotorConfigCreationLeft] + // [MotorConfigSetLeft] + leftConfig.inverted(true); + // [/MotorConfigSetLeft] + // [MotorConfigLeft] leftLeader.configure( leftConfig, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); leftFollower.configure( leftConfig.follow(leftLeader), ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + // [/MotorConfigLeft] - var rightConfig = new SparkMaxConfig().inverted(false); + // [MotorConfig] + var rightConfig = new SparkMaxConfig(); + rightConfig.inverted(false); rightLeader.configure( rightConfig, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); rightFollower.configure( rightConfig.follow(rightLeader), ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + // [/MotorConfig] } + // [/AllConfigs] + + // [DriveSimPeriodic] @Override public void simulationPeriodic() { drivetrainSim.periodic(); + // [/DriveSimPeriodic] intakeLauncherSim.periodic(); feederSim.periodic(); } 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 09fd6ab1..38d257df 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 @@ -13,15 +13,20 @@ @Teleop public class MyTeleop extends PeriodicOpMode { private final Robot robot; + // [Controller] private final NiDsXboxController xboxController = new NiDsXboxController(0); + // [/Controller] + public MyTeleop(Robot robot) { this.robot = robot; } + // [DriveSimPeriodic] @Override public void periodic() { robot.drivetrain.arcadeDrive(-xboxController.getLeftY(), xboxController.getRightX()); + // [/DriveSimPeriodic] if (xboxController.getRightBumperButton()) { // shoot diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2256b1f2..c34ead41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,7 +47,7 @@ importers: version: 4.0.4 '@vvago/vale': specifier: ^3.16.0 - version: 3.16.0 + version: 3.17.0 astro-eslint-parser: specifier: ^1.4.0 version: 1.4.0 @@ -1278,8 +1278,8 @@ packages: '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} - '@vvago/vale@3.16.0': - resolution: {integrity: sha512-Cm5cW8bEsnXQHqJDcwWtrONtbpNxA41gpp/YNRLmax8Wn+lWg0YROmk4zs3VAldQ813QOARqoUfZIbUVp2LOzw==} + '@vvago/vale@3.17.0': + resolution: {integrity: sha512-vzVFOzr0y9dbXfHqnpgfs8juCflYZpqDmbNyUqdjuTzfNuATtzthTRrqE67RZt5N51FBkJiv58vFjc6ySVgjjg==} hasBin: true abbrev@2.0.0: @@ -5204,7 +5204,7 @@ snapshots: '@vscode/l10n@0.0.18': {} - '@vvago/vale@3.16.0': + '@vvago/vale@3.17.0': dependencies: rimraf: 6.1.3 tar: 7.5.22 diff --git a/public/learning-course/stage1/stage1a/importLayout.webp b/public/learning-course/stage1/stage1a/importLayout.webp new file mode 100644 index 00000000..fcf3320d Binary files /dev/null and b/public/learning-course/stage1/stage1a/importLayout.webp differ diff --git a/public/learning-course/stage1/stage1a/kitbotDrivetrain.webp b/public/learning-course/stage1/stage1a/kitbotDrivetrain.webp new file mode 100644 index 00000000..951a28af Binary files /dev/null and b/public/learning-course/stage1/stage1a/kitbotDrivetrain.webp differ diff --git a/public/learning-course/stage1/stage1a/robotMode.webp b/public/learning-course/stage1/stage1a/robotMode.webp new file mode 100644 index 00000000..375d525d Binary files /dev/null and b/public/learning-course/stage1/stage1a/robotMode.webp differ diff --git a/public/learning-course/stage1/stage1a/simGUIscreen.webp b/public/learning-course/stage1/stage1a/simGUIscreen.webp new file mode 100644 index 00000000..55ed78ba Binary files /dev/null and b/public/learning-course/stage1/stage1a/simGUIscreen.webp differ diff --git a/public/learning-course/stage1/stage1a/simulateSelection.webp b/public/learning-course/stage1/stage1a/simulateSelection.webp new file mode 100644 index 00000000..a1917a8b Binary files /dev/null and b/public/learning-course/stage1/stage1a/simulateSelection.webp differ diff --git a/public/learning-course/stage1/stage1a/wpilibIcon.webp b/public/learning-course/stage1/stage1a/wpilibIcon.webp new file mode 100644 index 00000000..af0457dd Binary files /dev/null and b/public/learning-course/stage1/stage1a/wpilibIcon.webp differ diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts index aff81701..e25ad09f 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -99,6 +99,14 @@ export const sidebarSections: Record = { label: 'Stage 1A Introduction', slug: 'learning-course/stage1/stage1a/stage-overview', }, + { + label: 'Kitbot Drivetrain', + slug: 'learning-course/stage1/stage1a/kitbot-drivetrain', + }, + { + label: 'Stage 1A Drivetrain Simulation', + slug: 'learning-course/stage1/stage1a/drivetrain-sim', + }, // { // label: 'TBD', // slug: 'stage-1a-commands/the-command-body', diff --git a/src/content/docs/learning-course/stage1/stage1a/drivetrain-sim.mdx b/src/content/docs/learning-course/stage1/stage1a/drivetrain-sim.mdx new file mode 100644 index 00000000..e5eab108 --- /dev/null +++ b/src/content/docs/learning-course/stage1/stage1a/drivetrain-sim.mdx @@ -0,0 +1,140 @@ +--- +title: Kitbot Drivetrain Simulation +description: Writing the code to simulate the kitbot drivetrain +prev: kitbot-drivetrain +next: false +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Simulation + +It's common to be write code without immediate access to a physical robot to test changes. +Luckily, your computer can also run robot code allowing it to be tested without a robot. +While there is some things that can be tested by simulating pure robot code, there are no physical motors to move and respond with new positions. +Instead we use simulation classes that use physics to take the desired input voltage to the motors and estimate how the physical mechanism would respond and update our motor controller instances to match. +For this stage custom classes have been provided that abstract away much of this logic. +You can find these files under the `simulation` folder if you would like to read the implementation. +You can also read the [WPILib docs on simulation](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/index.html) if you would like to learn more about simulation. + +# Drivetrain Sim + +To simulate the drivetrain simply create an instance of the `DrivetrainSim` class underneath the `DifferentialDrive` instance using the left and right Leader motors as an input. +This class will read the voltage commanded to the motors and, using its physics sim, update the motor controllers with new positions. +The class will then publish the new drivetrain position so it can be viewed in AdvantageScope. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#DrivetrainSim + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#DrivetrainSim + ``` + + + + + +# Periodic Methods + +Robot code needs to be run in a loop so that it can continually make new commands to motor controllers based on new controller and sensor input. +To accomplish this Periodic Methods are provided in the `Robot` class and `OpMode` classes. +Periodic methods get called every 20ms by default causing any code placed inside of them to be ran 50 times per second. +Additional periodic methods exist that only run during specific robot states. +For example, the `teleopPeriodic()` function will only be called when teleop mode is selected on the driverstation. + +At the moment the `DrivetrainSim` will not actually do anything because it is not being told to update periodically. +To fix this, the `DrivetrainSim`'s `periodic()` function should be called inside of the `Robot` class' `simulationPeriodic()` function. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#DriveSimPeriodic + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#DriveSimPeriodic + ``` + + + + + + + +# Simulating code + +To actually run the code use the WPILib Icon in the top left to open the command palette. + + + +Then search for Simulate and select `WPILib: Simulate Robot Code`. + + + +After your code builds, a new tab will pop-up. +Ensure that only `SIM GUI` is selected and click ok. +This will start simulating your ode and open the Sim GUI in a new tab pictured below. +Your code will continue running until the Sim GUI is closed. + + + +The most important boxes in the Sim GUI are as follows... + +- `Robot State` is the most important box because it allows you to select robot mode, Opmode, and enabled state. +- `FMS` allows you to select alliance side and station while the robot is in the Disconnected mode. +- `Other Devices` shows information about created sim devices and allows you to change some of their values. + +To view the robot, First open AdvantageScope. +It should come installed with your WPILib install. +Then, open the File menu and select Import Layout and select the `AdvantageScopeLayout.json` file in your robot project. + + + +After importing the layout, open the File menu and select Connect To Simulator then NetworkTables4 (AdvantageScope). +AdvantageScope should now be connected to the robot simulation. +If AdvantageScope still says Searching, ensure that the robot code is still being simulated and that you selected the correct button. + + + +Now go back to the Sim GUI. +In the `Robot State` box select Teleop. +Then pick `MyTeleop` from the dropdown and enable the robot. +You should now be able to control the robot with WASD on the keyboard while the Sim GUI is in focus. +In the Field 2d tab of AdvantageScope you should be able to see the robot move. diff --git a/src/content/docs/learning-course/stage1/stage1a/kitbot-drivetrain.mdx b/src/content/docs/learning-course/stage1/stage1a/kitbot-drivetrain.mdx new file mode 100644 index 00000000..7faefdb2 --- /dev/null +++ b/src/content/docs/learning-course/stage1/stage1a/kitbot-drivetrain.mdx @@ -0,0 +1,383 @@ +--- +title: Kitbot Drivetrain +description: Programming the kitbot drivetrain +prev: stage-overview +next: drivetrain-sim +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; +import ContentImage from '@components/ContentImage.astro'; + +# Drivetrain + + + +The kitbot uses a 4 motor tank drive meaning the left and right sides are driven independently by 2 motors each. +This allows +the robot to move similar to a tank by driving the left and right sides at different speeds. +For this stage, the four drivetrain motors will be referred to as `leftLeader`, `leftFollower`, `rightLeader`, `rightFollower`. + +# Motor Controllers + +Motors can not be controlled directly. +Instead SystemCore talks to a motor controller and the motor controller then drives the motors. +Motor controllers can also provide sensor data from the motor such as its velocity or temperature. +Vendors, such as REV or CTRE, provide classes that can be used to both control and get sensor data from their motor controllers. +While each individual type of motor controller has its own class, motor controllers from the same vendor are +mostly interacted with in the same way so this stage will only use the `SparkMax` for REV code and the `TalonFX` for CTRE code. + +When creating a motor controller object, the physical motor controller's CAN ID and the CAN Bus ID are given. +CAN Bus refers to which of the 5 SystemCore CAN ports, or which CANivore, the device is plugged into. +CAN ID is an integer that each CAN device is configured to have. +All devices on a given CAN Bus must have a unique ID. +Using the combination of CAN Bus and CAN ID SystemCore can give commands to the correct motor controller. + +For this exercise the motor controllers will have the IDs: + +- `leftLeader`: CAN Bus 0, CAN ID 0 +- `leftFollower`: CAN Bus 0, CAN ID 1 +- `rightLeader`: CAN Bus 0, CAN ID 2 +- `rightFollower`: CAN Bus 0, CAN ID 3 + +The motor controller objects should be created inside the `Robot.java` at the top of the class. +This is how the motor controller objects for the left motors will look. + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#DriveMotorsLeft + ``` + +For the CTRE code, the CAN ID of the leader motors are stored as a variable since they will be used later on to tell the follower what motor controller to follow. +This helps prevent errors from occurring by ensuring that there is a single source of truth for the correct CAN ID. +Additionally, CTRE uses a CANBus object to store the CANBus instead of just an integer. + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#DriveMotorsLeft + ``` + +Since the Spark Max can control both brushed and brushless motors it's necessary to specify the type as `MotorType.kBrushless` for motors like a NEO and `MotorType.kBrushed` for motors like a CIM. + +When writing code for a real robot, make sure your motor type is set correctly! Setting a NEO to `MotorType.kBrushed` will break the NEO. + + + + + +Now try creating the right motor controllers on your own. + +
+ Solution + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#DriveMotorsRight + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#DriveMotorsRight + ``` + + + + +
+Motor Controllers have many settings that can be changed such as IDs, motor +types, and limits. Vendors provide +software, such as REV's REV Hardware Client 2 and CTRE's Phoenix Tuner X, to run +and configure their devices from a computer. However, it is recommended to +configure devices through code to ensure that all motor controllers are properly +configured. This is especially useful because it can be easy to forget all of +the configurations that need to be added and their proper values. + + + +For this section only the motor controller's invert setting will be configured. +This setting controls what direction +Since there are 2 motors on each side of the drivetrain, its important to ensure that the each of the motors on a side +move in sync with eachother. +This can be accomplished by telling one of the motor controllers to follow the other. +This is why one motor is named Leader and the other is Follower. +The code tells the Follower to listen to the commands given to the Leader. + +The motor controller configuration will be done inside of the constructor for the robot class +Motor Controllers are configured by first creating a motor controller configuration object. +This object stores the configuration so it can be changed and shared across different Motor Controllers. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#MotorConfigCreationLeft + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#MotorConfigCreationLeft + ``` + + + + + +Next, settings can be changed from their default by calling various functions on the configuration object with their new values. +For the left motors, the invert setting will be `true` for REV code and `Clockwise_Positive` for CTRE code. +This will cause the motors to spin in a direction that would drive the robot forward when a positive input is given. +Since the motors on the right side of the drivetrain are facing the opposite direction they would cause the wheels try and drive the robot backwards when given a positive input if they were configured the same way. +Instead they should be configured with an invert setting of `false` or `Counter_Clockwise_Positive` so they also drive the robot forward when given a positive input. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#MotorConfigSetLeft + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#MotorConfigSetLeft + ``` + + + + + +Finally, the configuration object gets given to the motor controller object. +It's important to remember that settings only get changed when the configuration gets given to the motor controller. + + + + For CTRE Motor Controllers, becoming a follower is a `ControlRequest` instead of a configuration. + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#MotorConfigLeft + ``` + + + + For REV Motor Controllers, becoming a follower is a configuration so the follower setting gets changed before being applied to the follower motor using the leader motor as the function parameter. + This must happen after the configuration is applied to the Leader so the motor controller doesn't want to follow itself. + `ResetMode.kResetSafeParameters` gets to tell the motor controller to reset all unchanged configurations to their default value. + This prevents old configurations from being left on the controller. + `PersistMode.kPersistParameters` is used so the configuration gets saved on the motor control so it persists even after power is removed. + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#MotorConfigLeft + ``` + + + + + +Now try configuring the right motor controllers on your own. +Remember that some of the configurations may be different. + +
+ Solution + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#MotorConfig + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#MotorConfig + ``` + + + + +
+ +# Arcade Drive + +ADD IMAGE OF LABELED JOYSTICK FOR ARCADE DRIVE PLS + +While there are several ways to control a tank drive, this stage will be using arcade drive. +Arcade drive uses a single joystick to control both the direction of travel and the rotation of the robot. +The y-axis of the joystick is used to control how fast the robot drives forward or backward while the x-axis controls how fast the robot rotates clockwise or counter clockwise. +WPIlib provides a class to convert joystick inputs into commands for the motors to follow called `DifferentialDrive`. + +An instance of `DifferentialDrive` should be created under where the motor controllers were declared. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#DrivetrainInstance + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#DrivetrainInstance + ``` + + + + + +Notice how the `DifferentialDrive` constructor takes the form `DifferentialDrive(DoubleConsumer leftMotor, DoubleConsumer rightMotor)`. +When created the `DifferentialDrive` is asking for a `DoubleConsumer` that it can use to drive the left and right motors. +A `Consumer` is simply a function that takes something as an input so a `DoubleConsumer` is a function that takes a double as an input when called. +The `motorController:setThrottle` syntax is used to proved the `DifferentialDrive` instance with the motor controllers `setThrottle()` function. +This function commands the motor to run a percentage of their maximum speed, also known as duty cycle, with an input from -1.0 to 1.0. +This allows the `DifferentialDrive` instance to call the provided motors controller's `setThrottle()` functions with the correct duty cycle when provided with input from the joysticks. + + + +# IMU + +An IMU, Inertial Measurement Unit, is a sensor that allows the robot to accurately track its 3d rotation (roll, pitch, and yaw) as it moves around the field. +While there are several vendors that sell very accurate IMUs, this stage will make use of Systemcore's built in IMU. +An instance of Systemcore's IMU should be created beneath the `differentialDrive`. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#IMU + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#IMU + ``` + + + + + + + +# Check Up + +By now the top of your `Robot` class in `Robot.java` should look like this: + +
+ Solution + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#RobotTop + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#RobotTop + ``` + + + + +
+ +The constructor for your `Robot` class should look like this: + +
+ Solution + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/Robot.java#AllConfigs + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/Robot.java#AllConfigs + ``` + + + + +
+ +And VS code should not be giving you any errors. + +# OpModes + +`OpMode`s are a class that registers itself with the driverstation providing a name and robot mode (autonomous, teleop, or utility). +This allows the robot to run different code based on what is selected on the driverstation. +This stage will use classes that extend `PeriodicOpMode.` +By extending `PeriodicOpMode` these classes gain a few useful functions that are only called when the OpMode is selected on the driverstation. + +- `start()` is called once when the robot transitions from disabled to enabled. +- `periodic()` is called repeatedly when the robot is enabled. +- `end()` is called once when the robot transitions from enabled to disabled. +- `disabledPeriodic()` is called repeatedly when the robot is disabled. + Further information about OpModes can be found in [this blog post](https://zharel.me/blog/opmodes/) if you would like to learn more. + +Two blank `PeriodicOpMode`s, `MyTeleop.java` and `MyAuto.java` are provided under the `opmode` folder. + +To control the robot with joysticks a Teleop OpMode needs to be created that periodically gives the `DifferentialDrive` instance new values from the controller. +First a instance of `NiDsXboxController` needs to be created. +This class has functions that provide the state of different buttons on the controller. +Multiple controllers can be used at once so the driverstation gives each a slot. +The index provided in the constructor tells the `NiDsXboxController`which slot to listen too. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java#Controller + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java#Controller + ``` + + + + + + + +Next, the `periodic()` method inside of `MyTeleop.java' will be used to continually update the `DifferentialDrive`index through the`arcadeDrive`function. +The`MyTeleop`class has the`Robot`class as a parameter in its constructor. +This allows the Opmode to access the methods and fields of the`Robot` class. + + + + + ```java stage1/stage1a/solutions/ctre/src/main/java/first/robot/opmode/MyTeleop.java#DriveSimPeriodic + ``` + + + + + ```java stage1/stage1a/solutions/rev/src/main/java/first/robot/opmode/MyTeleop.java#DriveSimPeriodic + ``` + + + + diff --git a/src/content/docs/learning-course/stage1/stage1a/stage-overview.mdx b/src/content/docs/learning-course/stage1/stage1a/stage-overview.mdx index 80c5a487..5c41d044 100644 --- a/src/content/docs/learning-course/stage1/stage1a/stage-overview.mdx +++ b/src/content/docs/learning-course/stage1/stage1a/stage-overview.mdx @@ -2,7 +2,7 @@ title: Stage 1A Overview description: An overview of Stage 1A prev: ../stage-overview -next: false +next: kitbot-drivetrain --- import YouTube from '../../../../../components/YouTube.astro';