From d89e93b3339975fe96508a957bdd8f549c8e75b2 Mon Sep 17 00:00:00 2001 From: yxhej Date: Mon, 12 Aug 2024 02:03:35 -0400 Subject: [PATCH 01/21] add incomplete arm bot project + sensors doc --- BasicArmBot.md | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ Sensors.md | 25 +++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 BasicArmBot.md create mode 100644 Sensors.md diff --git a/BasicArmBot.md b/BasicArmBot.md new file mode 100644 index 0000000..8f9985a --- /dev/null +++ b/BasicArmBot.md @@ -0,0 +1,97 @@ +# Basic Arm Bot Tutorial + +(insert image/gif of advantagescope visualizer here) + +## Description & Prerequisites + +In this tutorial, you will write code for a robot with a single-jointed arm and simulate its movement. The desired robot will have a single arm joint with an elevated axis of rotation, equipped with a static (non-pivoting) intake mechanism at the end of the arm. + +You are expected to have completed some level of the [Differential Drive projects](/DifferentialDriveBasic.md), as this project will build upon that codebase. +You should also be familiar with the concepts and usage of interfaces, inheritance, and the command-based paradigm. + +## Setting up your environment + +Using your knowledge of Git, create a new branch for your arm bot. Name it something like `basic-arm-bot`. **Make sure to also move to that branch!** +If unfamiliar, please check our [style sheet]() for ideal code and git practices. + +In your robot folder of your project, create an [`Arm.java` subsystem]() and an associated `ArmConstants.java` file. + +## Creating your arm + +Before we start, we'll be abstracting the hardware components to streamline our code. + +Moving hardware like motors and encoders to class implementations of an interface decouples them from subsystem code, enabling modularity that supports simulation and flexibility in using different hardware or none at all! For a deeper dive, you should look at our [hardware abstraction datasheet](/HardwareAbstraction.md). + +Begin by creating your first IO interface in its Java file. Remember: this will act as an abstraction for a real and simulated set of hardware, so only include method headers that you think they will share. + +It should look something like this: + +```java +public interface ArmIO { // introduce logging later when it comes time to visualize + void setVoltage(double voltage); // reminder: interfaces only have method headers; + // explain why these methods + double position(); // all method bodies are specified in classes that implement ArmIO! +} +``` + +Now, create a real implementation of your `ArmIO` named `RealArm` in its own file. Following Java rules, it must implement the bodies of the three methods above. Try it on your own before looking at the snippets below, and remember your goal! + +We declare and instantiate our hardware components... + +```java +public class RealArm implements ArmIO { + private final PWMSparkFlex motor = new PWMSparkFlex(0); + private final DutyCycleEncoder encoder = new DutyCycleEncoder(1); + + public RealArm() { + // ... + } +} +``` + +...and create implementations for the methods in `ArmIO`. + +```java + @Override + public void setVoltage(double voltage) { + motor.setVoltage(voltage); + } + + @Override + public double position() { + return encoder.get(); + } +``` + +Now, create a simulated implementation of `ArmIO` called `SimArm`. It'll look very similar to the above, only that the motor and encoder will be simulated by WPILIB's `SingleJointedArmSim` class + +## Repairing the file system + +With the introduction of this IO system, notice the substantial number of files. Multiply that by the two more subsystems you have, and that makes for an unreadable file system. So, let's change that. + +On the SciBorgs, subsystem files and their constants are put in one specific subsystem folder. This folder is located in a subsystems folder on the same level as the key robot files, defining the levels of contact each part of the robot should have with each other. In other words... + +``` +├── Autos.java +├── Constants.java +├── Main.java +├── Robot.java +└── subsystems + └── arm + └── ArmIO.java + └── RealArm.java + └── SimArm.java + └── claw + └── ... + └── ... +``` + +This file system can be referenced at any time here, or in our [style sheet](). + +## Making the claw + +You've learned how to make an IO subsystem, and now's the time you put your skills to the test. Write IO subsystem code for the static claw at the end of the arm. + +Think about the requirements of this claw. Here's a hint: how fast the claw runs is nearly irrelevant for what the claw should do. Its only goal is to intake, hold, and spit out whatever's in its grasp when told to. In the same vein, it won't be substantial to create a simulated version; we don't care about it. + +Try to do this one by yourself before looking below! diff --git a/Sensors.md b/Sensors.md new file mode 100644 index 0000000..ac84645 --- /dev/null +++ b/Sensors.md @@ -0,0 +1,25 @@ +# [Fundamentals of Sensors](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/sensor-overview-software.html#sensor-overview-software) + +This aims to be an brief reference to the different sensors that you will most commonly see and use on the robot. For further details, see the linked materials or consult your favorite search engine. + +## [Encoders](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/encoders-software.html#encoders-software) + +They measure rotation. Many brushless motors have encoders integrated inside of them, while others can be found outside of the robot. + +All encoders have some sort of default unit. Various vendors will have different methods of changing the units returned; read the docs! + +### [Relative Encoders](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/sensor-overview-software.html#sensor-overview-software) + +When powered on, its distance measurement will read 0. The zero-point will change on code boot, making its measurements "relative" to whenever it started. Think displacement. + +### [Absolute Encoders](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/encoders-software.html#encoders-software) + +Has a set zero point. Will always know where it is, even between code deploys. + +## [Gyroscope](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/gyros-software.html) + +It measures the rate of rotation of whatever plane it is on (and sometimes its relative axes). Usually found on the base of the robot. + +## [Cameras]() (got carried away here lmao) + +Using the known position of a camera and the known position of targets in its view, the target-relative and field-relative position of a robot can be calculated. These can be used to auto-correct odometry, auto-aim towards a target, or [automate movement entirely](https://www.youtube.com/watch?v=2zB0w69P4mc&t=73s). From ccacd19fb85ba686dd55c5804dede61cb0120def Mon Sep 17 00:00:00 2001 From: Yxhej Date: Mon, 12 Aug 2024 15:38:00 -0400 Subject: [PATCH 02/21] update license, more arm bot! --- BasicArmBot.md | 163 ++++++++++++++++++++++++++++++++++++++++++++++--- LICENSE | 2 +- Sensors.md | 6 +- 3 files changed, 160 insertions(+), 11 deletions(-) diff --git a/BasicArmBot.md b/BasicArmBot.md index 8f9985a..01415b9 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -11,7 +11,7 @@ You should also be familiar with the concepts and usage of interfaces, inheritan ## Setting up your environment -Using your knowledge of Git, create a new branch for your arm bot. Name it something like `basic-arm-bot`. **Make sure to also move to that branch!** +Using your knowledge of Git, create a new branch for your arm bot. Name it something like `basic-arm-bot`. *Make sure to also move to that branch!* If unfamiliar, please check our [style sheet]() for ideal code and git practices. In your robot folder of your project, create an [`Arm.java` subsystem]() and an associated `ArmConstants.java` file. @@ -36,7 +36,7 @@ public interface ArmIO { // introduce logging later when it comes time to visual Now, create a real implementation of your `ArmIO` named `RealArm` in its own file. Following Java rules, it must implement the bodies of the three methods above. Try it on your own before looking at the snippets below, and remember your goal! -We declare and instantiate our hardware components... +We create our hardware components... ```java public class RealArm implements ArmIO { @@ -63,11 +63,152 @@ public class RealArm implements ArmIO { } ``` -Now, create a simulated implementation of `ArmIO` called `SimArm`. It'll look very similar to the above, only that the motor and encoder will be simulated by WPILIB's `SingleJointedArmSim` class +Now, create a [simulated implementation](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html) of `ArmIO` called `SimArm`. WPILIB has lots of classes to simulate all different types of mechanisms, seen [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html#wpilib-s-simulation-classes). It'll look very similar to the above, only that the motor and encoder will be simulated by WPILIB's `SingleJointedArmSim` class. + +### Constants + +Digging deep into the innards, you'll notice that the constructors are a bit... packed. + +```java + // One of its many constructors; this is the one that we will be using + public SingleJointedArmSim( + LinearSystem plant, + DCMotor gearbox, + double gearing, + double armLengthMeters, + double minAngleRads, + double maxAngleRads, + boolean simulateGravity, + double startingAngleRads) +``` + +(might just include the ArmConstants file in the template) + +They need physical information to be able to accurately simulate your system, and we obviously don't have a real robot. + +All this information also requires lots of organization. In `ArmConstants`, you may copy-paste these relevant predetermined constants in: + +```java + import static edu.wpi.first.units.Units.*; + + // ... + + /** + * The factors by which encoder measurements are different from actual motor rotation; default + * units. + */ + public static final double MOTOR_GEARING = 12.0 / 64.0 * 20.0 / 70.0 * 36.0 / 56.0 * 16.0 / 54.0; + + public static final double THROUGHBORE_GEARING = 16.0 / 54.0; + + /** Unit conversions objects for the encoders. Uses the Java units library. */ + public static final Measure POSITION_FACTOR = Rotations.of(THROUGHBORE_GEARING); + + public static final Measure> VELOCITY_FACTOR = POSITION_FACTOR.per(Minute); + + /** Offset from the center of the robot to the pivot's axis of rotation */ + public static final Translation3d AXLE_FROM_CHASSIS = + new Translation3d(Inches.of(-10.465), Inches.zero(), Inches.of(25)); + + /** The arm's moment of inertia; resistance to rotational movement. */ + public static final Measure, Mass>> MOI = + (Meters).mult(Meters).mult(Kilograms).of(0.17845); + + public static final Measure POSITION_TOLERANCE = Degrees.of(0.8); + + public static final Measure MASS = Kilograms.of(1); + public static final Measure LENGTH = Inches.of(16); + + public static final Measure> MAX_VELOCITY = RadiansPerSecond.of(9.0); + public static final Measure>> MAX_ACCEL = + RadiansPerSecond.per(Second).of(13.0); + + public static final Measure STARTING_ANGLE = Degrees.of(63.3); + + public static final Measure MIN_ANGLE = Degrees.of(-45.7); + public static final Measure MAX_ANGLE = STARTING_ANGLE.minus(Degrees.of(1.2)); + + public static final double kP = 8.0; + public static final double kI = 0.0; + public static final double kD = 0.5; + + public static final double kS = 0.14296; + public static final double kV = 1.7305; + public static final double kA = 0.01; + public static final double kG = 0.12055; +``` + +Cool thing we can do: static imports, to save a bit of space. At the top of `SimArm`, we can add + +```java +import static org.sciborgs1155.robot.arm.ArmConstants.*; +``` + +to import all of the static objects in `ArmConstants` and turn `ArmConstants.MAX_VELOCITY` to just `MAX_VELOCITY`. + +Only use static imports for constants and when it makes sense stylistically and syntactically. + +### Finishing up + +Initialize your arm simulator like below... + +```java + private final SingleJointedArmSim sim = + new SingleJointedArmSim( + LinearSystemId.createSingleJointedArmSystem( + DCMotor.getNEO(4), + MOI.in((Meters).mult(Meters).mult(Kilograms)), + 1.0 / MOTOR_GEARING), + DCMotor.getNEO(4), + 1.0 / MOTOR_GEARING, + -LENGTH.in(Meters), + MIN_ANGLE.in(Radians), + MAX_ANGLE.in(Radians), + true, + STARTING_ANGLE.in(Radians)); +``` + +and implement the ArmIO methods using the methods provided by the sim. Make sure to update the sim periodically in your input methods with its `update(double dtSeconds)` method. + +To complete this IO subsystem, create the last implementation called `NoArm`. It should describe a non-existent arm, without any hardware but with methods returning 0 / doing nothing. + +This class is useful for when a mechanism is broken or is not on the robot, allowing us to run the rest of the robot without major errors. + +### Subsystem Integration + +Now, it's time to work with the actual subsystem. `Arm.java` should include a few things: + +- IO implementations of the hardware (that we just created!) +- any control run on the RoboRIO (versus a motor or other hardware) +- all [commands & command factories]() + +Think about how exactly we want our arm to act, and what kinds of control would suit that. + +For a long and heavy arm (relative to other mechanisms), it's dangerous to use a regular PID controller, causing the arm to straight down at high speeds. Of course, we still want the arm to reach the setpoint quickly. + +A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity and acceleration will increase, coast, and then decrease over time to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILIB has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. **Read the docs**. + +Considering that the mechanism is an arm, it should be intuitive that gravity will have a much greater effect on it than other mechanisms. To account for this, we can use [WPILIB's `ArmFeedForward`](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/feedforward.html#armfeedforward), which incorporates a G term for output. + +Declare all of your components... + +```java +public class Arm extends SubsystemBase implements Logged { + private final ArmIO hardware; + private final ProfiledPIDController pid; + private final ArmFeedforward ff; + + public Arm(ArmIO hardware) { + this.hardware = hardware; + pid = new ProfiledPIDController(0, 0, 0, null); + ff = new ArmFeedforward(0, 0, 0); + } + // ... +``` ## Repairing the file system -With the introduction of this IO system, notice the substantial number of files. Multiply that by the two more subsystems you have, and that makes for an unreadable file system. So, let's change that. +With the introduction of this IO system, notice the substantial number of files. Multiply that by the two additional subsystems, and that makes for an unreadable file system. So, let's change that. On the SciBorgs, subsystem files and their constants are put in one specific subsystem folder. This folder is located in a subsystems folder on the same level as the key robot files, defining the levels of contact each part of the robot should have with each other. In other words... @@ -78,20 +219,28 @@ On the SciBorgs, subsystem files and their constants are put in one specific sub ├── Robot.java └── subsystems └── arm + └── Arm.java └── ArmIO.java └── RealArm.java └── SimArm.java + └── NoArm.java + └── ArmConstants.java └── claw └── ... └── ... ``` -This file system can be referenced at any time here, or in our [style sheet](). +This file system structure can be referenced at any time here, or in our [style sheet](). ## Making the claw -You've learned how to make an IO subsystem, and now's the time you put your skills to the test. Write IO subsystem code for the static claw at the end of the arm. +Congrats! We've learned how to make an IO subsystem, and now's the time you put your skills to the test. Write IO subsystem code for the static claw at the end of the arm. -Think about the requirements of this claw. Here's a hint: how fast the claw runs is nearly irrelevant for what the claw should do. Its only goal is to intake, hold, and spit out whatever's in its grasp when told to. In the same vein, it won't be substantial to create a simulated version; we don't care about it. +Think about the requirements of this claw. Here's a hint: how fast the claw runs is nearly irrelevant for what the claw should do. Its only goal is to intake, hold, and spit out whatever's in its grasp when told to. In the same vein, it won't be substantial to create a simulated version; we don't care about simulating it, unless we want to make a full-fledged game relying on intaking (maybe an idea)? Try to do this one by yourself before looking below! + +## Converting the drivetrain + +Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. For simulation, you can use [WPILIB's `DifferentialDrivetrainSim`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.html). Good luck! + diff --git a/LICENSE b/LICENSE index 9fb0b1b..4063384 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 SciBorgs Programming +Copyright (c) 2024 SciBorgs Programming Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sensors.md b/Sensors.md index ac84645..eb2f865 100644 --- a/Sensors.md +++ b/Sensors.md @@ -4,13 +4,13 @@ This aims to be an brief reference to the different sensors that you will most c ## [Encoders](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/encoders-software.html#encoders-software) -They measure rotation. Many brushless motors have encoders integrated inside of them, while others can be found outside of the robot. +They measure rotation. Many brushless motors have encoders integrated inside of them, while others can be found outside of the robot. Most measure what is effectively rotational displacement, as encoders read negative values from relative backwards movement. All encoders have some sort of default unit. Various vendors will have different methods of changing the units returned; read the docs! ### [Relative Encoders](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/sensor-overview-software.html#sensor-overview-software) -When powered on, its distance measurement will read 0. The zero-point will change on code boot, making its measurements "relative" to whenever it started. Think displacement. +When powered on, its distance measurement will read 0. The zero-point will change on code boot, making its measurements "relative" to whenever it started. ### [Absolute Encoders](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/encoders-software.html#encoders-software) @@ -18,7 +18,7 @@ Has a set zero point. Will always know where it is, even between code deploys. ## [Gyroscope](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/gyros-software.html) -It measures the rate of rotation of whatever plane it is on (and sometimes its relative axes). Usually found on the base of the robot. +It measures the rate of rotation of whatever plane it is on (and sometimes its relative axes). Usually found on the base of the drivetrain. ## [Cameras]() (got carried away here lmao) From ac534e49148e551d6a7531f4ca1356c15c81a1d8 Mon Sep 17 00:00:00 2001 From: Yxhej Date: Mon, 12 Aug 2024 15:58:48 -0400 Subject: [PATCH 03/21] 1/8 sim --- BasicArmBot.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/BasicArmBot.md b/BasicArmBot.md index 01415b9..6842f55 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -244,3 +244,8 @@ Try to do this one by yourself before looking below! Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. For simulation, you can use [WPILIB's `DifferentialDrivetrainSim`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.html). Good luck! +## The Simulation GUI + +Now that you've completed all of your subsystems and mechanisms, it's time for the fun part. Piecing it all together! + +You should begin your exploration [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). Continuing, open up the sim gui and explore it. More specific details on it can be found in the [next entry](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/simulation-gui.html). From d7a5eb8e8acb5aa58c43e34b23dddf45d60e5769 Mon Sep 17 00:00:00 2001 From: yxhej Date: Tue, 13 Aug 2024 00:26:52 -0400 Subject: [PATCH 04/21] finish arm subsystem guide + 1/4 sim stuff done --- BasicArmBot.md | 73 ++++++++++++++++++++++++++++++++----------- archive/Simulation.md | 24 +++++++++++++- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/BasicArmBot.md b/BasicArmBot.md index 6842f55..70b9a66 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -6,15 +6,14 @@ In this tutorial, you will write code for a robot with a single-jointed arm and simulate its movement. The desired robot will have a single arm joint with an elevated axis of rotation, equipped with a static (non-pivoting) intake mechanism at the end of the arm. -You are expected to have completed some level of the [Differential Drive projects](/DifferentialDriveBasic.md), as this project will build upon that codebase. -You should also be familiar with the concepts and usage of interfaces, inheritance, and the command-based paradigm. +You are expected to have completed some level of the [Differential Drive projects](/DifferentialDriveBasic.md), as this project will build upon that codebase. You should also be familiar with the concepts and usage of interfaces, inheritance, and the command-based paradigm. ## Setting up your environment Using your knowledge of Git, create a new branch for your arm bot. Name it something like `basic-arm-bot`. *Make sure to also move to that branch!* If unfamiliar, please check our [style sheet]() for ideal code and git practices. -In your robot folder of your project, create an [`Arm.java` subsystem]() and an associated `ArmConstants.java` file. +Before you move on, create an [`Arm.java` subsystem]() and an associated `ArmConstants.java` file in the robot folder of your project. ## Creating your arm @@ -138,6 +137,8 @@ All this information also requires lots of organization. In `ArmConstants`, you public static final double kG = 0.12055; ``` +Note the `Measure` constants; this is part of WPILIB's [Java Units Library](https://docs.wpilib.org/en/stable/docs/software/basic-programming/java-units.html), which helps ensure correct units are used throughout the code. You'll see it used later on as well. + Cool thing we can do: static imports, to save a bit of space. At the top of `SimArm`, we can add ```java @@ -161,14 +162,14 @@ Initialize your arm simulator like below... 1.0 / MOTOR_GEARING), DCMotor.getNEO(4), 1.0 / MOTOR_GEARING, - -LENGTH.in(Meters), - MIN_ANGLE.in(Radians), - MAX_ANGLE.in(Radians), + -LENGTH.in(Meters), // the Units library in action + MIN_ANGLE.in(Radians), // initialized as degrees, these are converted to radians + MAX_ANGLE.in(Radians), // as required by the constructor true, STARTING_ANGLE.in(Radians)); ``` -and implement the ArmIO methods using the methods provided by the sim. Make sure to update the sim periodically in your input methods with its `update(double dtSeconds)` method. +...and implement the ArmIO methods using the methods provided by the sim. Make sure to update the sim periodically in your input methods with its `update(double dtSeconds)` method. To complete this IO subsystem, create the last implementation called `NoArm`. It should describe a non-existent arm, without any hardware but with methods returning 0 / doing nothing. @@ -184,29 +185,61 @@ Now, it's time to work with the actual subsystem. `Arm.java` should include a fe Think about how exactly we want our arm to act, and what kinds of control would suit that. -For a long and heavy arm (relative to other mechanisms), it's dangerous to use a regular PID controller, causing the arm to straight down at high speeds. Of course, we still want the arm to reach the setpoint quickly. +For a long and heavy arm (relative to other mechanisms), it's dangerous to use a regular PID controller, which will command the arm to straight down at high speeds. Of course, we still want the arm to reach the setpoint quickly. -A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity and acceleration will increase, coast, and then decrease over time to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILIB has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. **Read the docs**. +A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity will increase, coast, and then decrease over time under a set of velocity and acceleration constraints to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILIB has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. **Read the docs**. -Considering that the mechanism is an arm, it should be intuitive that gravity will have a much greater effect on it than other mechanisms. To account for this, we can use [WPILIB's `ArmFeedForward`](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/feedforward.html#armfeedforward), which incorporates a G term for output. +Considering that the mechanism is an arm, it should be intuitive that gravity will have a much greater (non-negligible) effect on it than other mechanisms. To account for this, we can use [WPILIB's `ArmFeedForward`](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/feedforward.html#armfeedforward), which incorporates a G term for output. -Declare all of your components... +The top of your file should look similar to this: ```java +import static org.sciborgs1155.robot.arm.ArmConstants.*; + public class Arm extends SubsystemBase implements Logged { + // We declare our objects at the top... private final ArmIO hardware; private final ProfiledPIDController pid; private final ArmFeedforward ff; public Arm(ArmIO hardware) { + // ...and initialize them in the constructor (for stylistic purposes). this.hardware = hardware; - pid = new ProfiledPIDController(0, 0, 0, null); - ff = new ArmFeedforward(0, 0, 0); + pid = new ProfiledPIDController(kP, kI, kD, new TrapezoidProfile.Constraints(MAX_VELOCITY, MAX_ACCEL)); + ff = new ArmFeedforward(kS, kV, kA); } // ... +} +``` + +Now, let's create our command factories to make this arm move given a certain goal to reach. If you haven't already, read the [`ProfiledPIDController` docs](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) for usage details and clarifications. + +First, we'll create a non-factory method to set voltage with our controllers given a goal. This helps us avoid use of a multi-line lambda in the command factory. + +```java + public void updateGoal(double goal) { + double pidOutput = pid.calculate(hardware.position(), goal); + // Notice the distinction between goal and setpoint; the feedforward takes the pid's newly generated setpoint as input to help reach that setpoint. + double ffOutput = ff.calculate(pid.getSetpoint().position, pid.getSetpoint().velocity); + hardware.setVoltage(pidOutput + ffOutput); + } ``` -## Repairing the file system +Then, we'll use this method in our command factory. Think about how we want to ideally control it. + +Hint: we'd want to repeatedly run the method above until we reach our goal, after which we stop the arm (and the command!). Never forget your end condition. + +It'll look something like this: + +```java + public Command moveTo(double goal) { + return run(() -> updateGoal(goal)).until(pid::atGoal).finallyDo(() -> hardware.setVoltage(0)); + } +``` + +You might even want to [overload](https://www.w3schools.com/java/java_methods_overloading.asp) it to take in a goal using the Units library, but that's just for safety and not strictly required. + +## Organizing the file system With the introduction of this IO system, notice the substantial number of files. Multiply that by the two additional subsystems, and that makes for an unreadable file system. So, let's change that. @@ -234,18 +267,20 @@ This file system structure can be referenced at any time here, or in our [style ## Making the claw -Congrats! We've learned how to make an IO subsystem, and now's the time you put your skills to the test. Write IO subsystem code for the static claw at the end of the arm. +Congrats! We've learned how to make an IO subsystem, so now's the time you put your skills to the test. Write IO subsystem code for the static claw at the end of the arm. + +Think about the requirements of this claw. Here's a hint: how fast the claw runs is nearly irrelevant for what the claw should do. Its only goal is to intake, hold, and spit out whatever's in its grasp when told to. In the same vein, it won't be substantial to create a simulated version; we don't care about simulating it, unless we want to make a full-fledged game relying on intaking (maybe a cool idea)? -Think about the requirements of this claw. Here's a hint: how fast the claw runs is nearly irrelevant for what the claw should do. Its only goal is to intake, hold, and spit out whatever's in its grasp when told to. In the same vein, it won't be substantial to create a simulated version; we don't care about simulating it, unless we want to make a full-fledged game relying on intaking (maybe an idea)? +Try to do this one by yourself! -Try to do this one by yourself before looking below! +^^ Should this one be guided through like before? Maybe just doing the interface would be fine, and leaving impls + subsystem up to them ## Converting the drivetrain -Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. For simulation, you can use [WPILIB's `DifferentialDrivetrainSim`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.html). Good luck! +Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. Good luck! ## The Simulation GUI Now that you've completed all of your subsystems and mechanisms, it's time for the fun part. Piecing it all together! -You should begin your exploration [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). Continuing, open up the sim gui and explore it. More specific details on it can be found in the [next entry](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/simulation-gui.html). +For starters, get the gist of what's happening [here](/Simulation.md). You'll be using a Mechanism2d to simulate your arm like in the blocky gif you say earlier. diff --git a/archive/Simulation.md b/archive/Simulation.md index 57dc798..99eb4bf 100644 --- a/archive/Simulation.md +++ b/archive/Simulation.md @@ -1 +1,23 @@ -# Simulating a robot +# Robot Simulation + +Simulation in WPILib allows for code and logic to be tested onboard your computer, rather than physical hardware. It's exceptionally useful for when construction has not built the robot, but you have code to run! + +There are a few different facets of simulation to take note of before you can start, including: + +- Logging & Dashboards +- The Simulation GUI +- [WPILIB's simulation classes](#simulation-classes) + - Physics simulators + - Mechanism2d + - Field2d + +## Simulation Classes + +This section will heavily reference the WPILIB docs [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). + +Before you start, make sure desktop support is turned on. Follow [these instructions](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). + +This [helpful guide](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation) shows the different elements of the GUI. + + + From a42bbed65c66b158b5a91bfc40fb70850f4a8c21 Mon Sep 17 00:00:00 2001 From: yxhej Date: Tue, 13 Aug 2024 18:11:01 -0400 Subject: [PATCH 05/21] more sim + gif --- BasicArmBot.md | 20 ++++++++++++++++---- archive/Simulation.md | 23 +++++++++++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/BasicArmBot.md b/BasicArmBot.md index 70b9a66..0afda7f 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -1,6 +1,6 @@ # Basic Arm Bot Tutorial -(insert image/gif of advantagescope visualizer here) +![Simulated arm moving side to side](https://i.gyazo.com/8d105b8f9776487789333497fa72cd9e.gif) ## Description & Prerequisites @@ -277,10 +277,22 @@ Try to do this one by yourself! ## Converting the drivetrain -Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. Good luck! +Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. If you've completed the previous Differential Drive projects, you should have everything good to go. Good luck! -## The Simulation GUI +## Simulating the arm Now that you've completed all of your subsystems and mechanisms, it's time for the fun part. Piecing it all together! -For starters, get the gist of what's happening [here](/Simulation.md). You'll be using a Mechanism2d to simulate your arm like in the blocky gif you say earlier. +For starters, get the gist of what's happening [here](/Simulation.md). You'll be using a `Mechanism2d` to simulate your arm, like in the blocky arm you saw at the beginning of the tutorial. + +We'll only be simulating the arm; the claw won't be that useful. + +This section will be heavily based on [these docs](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html). + +In `Arm`, create your initial encapsulating `Mechanism2d`. + +```java + private final Mechanism2d mech = new Mechanism2d(2, 2); +``` + +Continue following the docs above diff --git a/archive/Simulation.md b/archive/Simulation.md index 99eb4bf..56cf211 100644 --- a/archive/Simulation.md +++ b/archive/Simulation.md @@ -4,20 +4,27 @@ Simulation in WPILib allows for code and logic to be tested onboard your compute There are a few different facets of simulation to take note of before you can start, including: -- Logging & Dashboards -- The Simulation GUI +- [The Simulation GUI](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation) - [WPILIB's simulation classes](#simulation-classes) - - Physics simulators - - Mechanism2d - - Field2d + - [Physics simulators](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html) + - [Mechanism2d](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html) + - [Field2d](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/field2d-widget.html) +- Logging & Dashboards + +## Logging & Log Viewers + +In testing, it is common to want to directly observe robot measurements and values in order to tune your systems or debug. This is also incredibly important when working with simulation, as you otherwise have no reference to what is going on without a physical robot. For more details, visit [the docs](https://docs.wpilib.org/en/stable/docs/software/telemetry/telemetry.html). + +On the SciBorgs, we currently use [Monologue](https://github.com/shueja/Monologue/wiki) (general) and [URCL](https://github.com/Mechanical-Advantage/URCL) (for REV motors), both third-party logging programs. More options can be found [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/3rd-party-libraries.html). + +On the data-viewing end, we like ## Simulation Classes This section will heavily reference the WPILIB docs [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). -Before you start, make sure desktop support is turned on. Follow [these instructions](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). - -This [helpful guide](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation) shows the different elements of the GUI. +Before doing anything with simulation, make sure desktop support is turned on. Follow [these instructions](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). +WPILIB contains generic simulation classes for different mechanisms (like `ElevatorSim`) based on physical details and constraints about your system. You can see a full list of them and examples on using them [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html). From e0091804e608fbb50d48d147fd8c94fcc08a2389 Mon Sep 17 00:00:00 2001 From: yxhej Date: Tue, 13 Aug 2024 23:11:39 -0400 Subject: [PATCH 06/21] logging + lots of arm sim --- BasicArmBot.md | 31 ++++++++++++++++++++++++++----- Telemetry.md | 34 ++++++++++++++++++++++++++++++++++ archive/Simulation.md | 16 +++++++++------- 3 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 Telemetry.md diff --git a/BasicArmBot.md b/BasicArmBot.md index 0afda7f..96e309d 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -283,16 +283,37 @@ Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem Now that you've completed all of your subsystems and mechanisms, it's time for the fun part. Piecing it all together! -For starters, get the gist of what's happening [here](/Simulation.md). You'll be using a `Mechanism2d` to simulate your arm, like in the blocky arm you saw at the beginning of the tutorial. +For starters, get set up and get the gist of what's happening in [our simulation docs](/Simulation.md). You'll be using a `Mechanism2d` to simulate your arm, like in the blocky arm you saw at the beginning of the tutorial. We'll only be simulating the arm; the claw won't be that useful. -This section will be heavily based on [these docs](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html). +Follow [these docs](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html) to help create your arm. -In `Arm`, create your initial encapsulating `Mechanism2d`. +There should be one central encapsulating `Mechanism2d` to create the widget, a `Mechanism2dRoot` as the axis of rotation, and one `Mechanism2dLigament` to act as the actual arm. This ligament is the part that directly receives position measurements and moves to fit that, effectively simulating the arm's position. + +Be sure to use the provided constants in the constructor. + +Create your `Mechanism2d` with a proper window size and an associated root with 3D translations to the axis of rotation on the physical robot... + +```java + private final Mechanism2d mech = new Mechanism2d(2, 2); + private final MechanismRoot2d chassis = + mech.getRoot("Chassis", 1 + AXLE_FROM_CHASSIS.getX(), AXLE_FROM_CHASSIS.getZ()); +``` + +before adding a `Mechanism2dLigament` to the root, and updating its position with proper position coordinates accordingly. ```java - private final Mechanism2d mech = new Mechanism2d(2, 2); + private final MechanismLigament2d arm = + chassis.append( + new MechanismLigament2d("Arm", LENGTH.in(Meters), 0, 4, new Color8Bit(Color.kAliceBlue))); + + // ... + + @Override + public void simulationPeriodic() { + arm.setAngle(angle); + } ``` -Continue following the docs above +Note that `Mechanism2d`'s `setAngle(double)` only takes in measurements as degrees. \ No newline at end of file diff --git a/Telemetry.md b/Telemetry.md new file mode 100644 index 0000000..fc2de67 --- /dev/null +++ b/Telemetry.md @@ -0,0 +1,34 @@ +# Telemetry + +### The art of logging and log-viewing + +In testing (real or simulated), it is common to want to directly observe robot measurements and values in order to tune your systems and debug. Rather than spamming hundreds of print statements, WPILIB provides interfaces allowing certain information to be logged while the robot runs. + +It sends data to [NetworkTables](https://docs.wpilib.org/en/stable/docs/software/networktables/networktables-intro.html), which can be read [while running](#dashboards) or later on in [log files](#log-viewers). + +Only classes that implement `Sendable` can be sent over NetworkTables; see [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/3rd-party-libraries.html) for more details. + +For more in-depth information on the inner workings, visit [the docs](https://docs.wpilib.org/en/stable/docs/software/telemetry/telemetry.html). + +## Logging libraries + +Logging libraries, third-party or WPILIB-made, are not monolithic. Some are annotation-based (using Java `@Annotations`), while others are framework-based. All are unique; a nice list of them can be found [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/3rd-party-libraries.html). + +- [Monologue](https://github.com/shueja/Monologue/wiki) (we use this!) +- Epilogue (official WPILIB; soon to exist documentation) +- [AdvantageKit](https://github.com/Mechanical-Advantage/AdvantageKit/blob/main/docs/WHAT-IS-ADVANTAGEKIT.md) +- URCL + +## Dashboards + +There are two types of dashboards: driving and programming. We'll be talking about the ones related to debugging. + +These programs allow you to view data over NetworkTables in real time, allowing you to see logged data while working on a real or simulated robot. These **will be your main tools for debugging**. + +Here's a [nice list](https://docs.wpilib.org/en/stable/docs/software/dashboards/dashboard-intro.html) of them. + +## Log Viewers + +Log viewers will pull measurements from log files and most commonly allow you to graph data or visualize them in some way. These are especially useful for direct post-match debugging, given you have the ability to process and debug with the data formats. + +See [this doc](https://docs.wpilib.org/en/stable/docs/software/telemetry/datalog-download.html#downloading-processing-data-logs) for more information. diff --git a/archive/Simulation.md b/archive/Simulation.md index 56cf211..7ac1cb3 100644 --- a/archive/Simulation.md +++ b/archive/Simulation.md @@ -9,22 +9,24 @@ There are a few different facets of simulation to take note of before you can st - [Physics simulators](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html) - [Mechanism2d](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html) - [Field2d](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/field2d-widget.html) -- Logging & Dashboards +- [Logging & Dashboards](#logging--dashboards) -## Logging & Log Viewers +## Logging & Dashboards -In testing, it is common to want to directly observe robot measurements and values in order to tune your systems or debug. This is also incredibly important when working with simulation, as you otherwise have no reference to what is going on without a physical robot. For more details, visit [the docs](https://docs.wpilib.org/en/stable/docs/software/telemetry/telemetry.html). +In testing, it is common to want to directly observe robot measurements and values in order to tune your systems or debug. This is also incredibly important when working with simulation, as you otherwise have no reference to what is going on without a physical robot. -On the SciBorgs, we currently use [Monologue](https://github.com/shueja/Monologue/wiki) (general) and [URCL](https://github.com/Mechanical-Advantage/URCL) (for REV motors), both third-party logging programs. More options can be found [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/3rd-party-libraries.html). - -On the data-viewing end, we like +For more details, visit [our doc](/Telemetry.md). ## Simulation Classes This section will heavily reference the WPILIB docs [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). -Before doing anything with simulation, make sure desktop support is turned on. Follow [these instructions](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). +Before doing anything with simulation, make sure desktop support is turned on. Follow [these instructions](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). Use [the next doc](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation) as reference for the GUI when working with it (same as above). WPILIB contains generic simulation classes for different mechanisms (like `ElevatorSim`) based on physical details and constraints about your system. You can see a full list of them and examples on using them [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html). +There's also the specific widget classes `Field2d` and `Mechanism2d`, which allow for pixel representations of the field and parts of mechanisms. + +Physical objects, like game pieces, wheels, the drivetrain, etc., can be added to the Field2d object, allowing for nice representations without a real field. [Here's more](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/field2d-widget.html) on how you can use it. +Mechanism2ds allow for block representations of mechanisms. This is most commonly used for simulating arms and elevators. See [the docs](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html) for usage details and further clarifications. From d1a0bb8f9155336e92dc171666e2be9c691cc00e Mon Sep 17 00:00:00 2001 From: yxhej Date: Wed, 14 Aug 2024 14:47:42 -0400 Subject: [PATCH 07/21] almost.... --- BasicArmBot.md | 91 +++++++++++++++++++++++++++++++++++++++---- archive/Simulation.md | 6 ++- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/BasicArmBot.md b/BasicArmBot.md index 96e309d..02c5aff 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -1,6 +1,6 @@ # Basic Arm Bot Tutorial -![Simulated arm moving side to side](https://i.gyazo.com/8d105b8f9776487789333497fa72cd9e.gif) +![Simulated arm moving side to side](https://i.gyazo.com/cb6189d0c58c3c28f8558cf88af827ff.gif) ## Description & Prerequisites @@ -8,12 +8,14 @@ In this tutorial, you will write code for a robot with a single-jointed arm and You are expected to have completed some level of the [Differential Drive projects](/DifferentialDriveBasic.md), as this project will build upon that codebase. You should also be familiar with the concepts and usage of interfaces, inheritance, and the command-based paradigm. +All code examples are subject to change as a result of future library updates and improvements. + ## Setting up your environment Using your knowledge of Git, create a new branch for your arm bot. Name it something like `basic-arm-bot`. *Make sure to also move to that branch!* If unfamiliar, please check our [style sheet]() for ideal code and git practices. -Before you move on, create an [`Arm.java` subsystem]() and an associated `ArmConstants.java` file in the robot folder of your project. +Before you move on, create an `Arm.java` subsystem and an associated `ArmConstants.java` file in the robot folder of your project. ## Creating your arm @@ -185,7 +187,13 @@ Now, it's time to work with the actual subsystem. `Arm.java` should include a fe Think about how exactly we want our arm to act, and what kinds of control would suit that. -For a long and heavy arm (relative to other mechanisms), it's dangerous to use a regular PID controller, which will command the arm to straight down at high speeds. Of course, we still want the arm to reach the setpoint quickly. +For a long and heavy arm (relative to other mechanisms), it's dangerous to use a regular PID controller, which will command the arm straight down at high speeds. + +| ![Arm with regular PID](https://i.gyazo.com/37f07bb43986f5102ebde16da9a641f8.gif) | +|:--:| +| Regular PID; same constants as gif at top of file | + +Of course, we still want the arm to reach the setpoint quickly. A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity will increase, coast, and then decrease over time under a set of velocity and acceleration constraints to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILIB has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. **Read the docs**. @@ -239,6 +247,19 @@ It'll look something like this: You might even want to [overload](https://www.w3schools.com/java/java_methods_overloading.asp) it to take in a goal using the Units library, but that's just for safety and not strictly required. +Finally, make a static `create()` method that will actually instantiate the `Arm` with IO implementations. You can read on why we do this in our [style sheet](). + +```java + /** + * A factory to create a new arm based on if the robot is real or simulated. + */ + public static Arm create() { + return Robot.isReal() ? new Arm(new RealArm()) : new Arm(new SimArm()); + } +``` + +We use the ternary operator; if the robot is real, we create the subsystem with real hardware, otherwise we create the subsystem with simulated hardware. + ## Organizing the file system With the introduction of this IO system, notice the substantial number of files. Multiply that by the two additional subsystems, and that makes for an unreadable file system. So, let's change that. @@ -269,7 +290,9 @@ This file system structure can be referenced at any time here, or in our [style Congrats! We've learned how to make an IO subsystem, so now's the time you put your skills to the test. Write IO subsystem code for the static claw at the end of the arm. -Think about the requirements of this claw. Here's a hint: how fast the claw runs is nearly irrelevant for what the claw should do. Its only goal is to intake, hold, and spit out whatever's in its grasp when told to. In the same vein, it won't be substantial to create a simulated version; we don't care about simulating it, unless we want to make a full-fledged game relying on intaking (maybe a cool idea)? +Think about the requirements of this claw. Here's a hint: how fast the claw runs is nearly irrelevant for what the claw should do. Its only goal is to intake, hold, and spit out whatever's in its grasp when told to. + +In the same vein, it won't be substantial to create a simulated version; we don't care about simulating it, unless we want to make a full-fledged game relying on intaking (maybe a cool idea)? Try to do this one by yourself! @@ -289,9 +312,11 @@ We'll only be simulating the arm; the claw won't be that useful. Follow [these docs](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html) to help create your arm. +### Putting the widget together + There should be one central encapsulating `Mechanism2d` to create the widget, a `Mechanism2dRoot` as the axis of rotation, and one `Mechanism2dLigament` to act as the actual arm. This ligament is the part that directly receives position measurements and moves to fit that, effectively simulating the arm's position. -Be sure to use the provided constants in the constructor. +Be sure to use the provided constants in the constructor(s). Create your `Mechanism2d` with a proper window size and an associated root with 3D translations to the axis of rotation on the physical robot... @@ -301,7 +326,7 @@ Create your `Mechanism2d` with a proper window size and an associated root with mech.getRoot("Chassis", 1 + AXLE_FROM_CHASSIS.getX(), AXLE_FROM_CHASSIS.getZ()); ``` -before adding a `Mechanism2dLigament` to the root, and updating its position with proper position coordinates accordingly. +...before adding a `Mechanism2dLigament` to the root, and updating its position with proper position coordinates accordingly. ```java private final MechanismLigament2d arm = @@ -312,8 +337,58 @@ before adding a `Mechanism2dLigament` to the root, and updating its position wit @Override public void simulationPeriodic() { - arm.setAngle(angle); + arm.setAngle(hardware.position()); + } +``` + +### Conversions + +Note that `Mechanism2d`'s `setAngle(double)` only takes in degree measurements, which may require a conversion depending on the units of the returned measurement. + +In sim, the `SingleJointedArmSim` method for returning position only returns radian measurements[^1]. Since this shouldn't ever change, use of Java's conversion library is acceptable. + +[^1]: This may be subject to change; check the [method comment](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.html#getAngleRads()) to see if the above is still valid! + +```java + @Override + public void simulationPeriodic() { + arm.setAngle(Units.radiansToDegrees(hardware.position())); + } +``` + +While we're here, let's also set the real robot's encoder conversion factor. + +REV encoders' default measurements are rotations, which should be converted to radians or degrees for use on the robot. This can be easily done with Java's Units library. + +Use the predetermined `POSITION_FACTOR` constant in `ArmConstants` as the base, and convert that into your unit of choice. + +```java + public RealArm() { + // ... + + encoder.setDistancePerRotation(POSITION_FACTOR.in(Radians)); + // or + encoder.setDistancePerRotation(POSITION_FACTOR.in(Degrees)); } ``` -Note that `Mechanism2d`'s `setAngle(double)` only takes in measurements as degrees. \ No newline at end of file +### Button & subsystem bindings + +From this point, you can actually see the widget in action in the sim GUI. Follow [these directions](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html#viewing-the-mechanism2d-in-glass). + +However, you'll notice it just... sitting there. Which makes sense... we haven't given it any commands. + +Like you've done before, create your subsystem objects with `create()`, and use the `CommandXboxController` in `Robot.java` to run your subsystem commands with it. + +They might look something like this: + +```java + operator.x().onTrue(arm.moveTo(x)); +``` + +Don't forget to add your [default command]()! + +### The payoff + +Open up the sim GUI. Display the widget on your screen. Reminders on how to do that here. + diff --git a/archive/Simulation.md b/archive/Simulation.md index 7ac1cb3..67a503c 100644 --- a/archive/Simulation.md +++ b/archive/Simulation.md @@ -25,8 +25,10 @@ Before doing anything with simulation, make sure desktop support is turned on. F WPILIB contains generic simulation classes for different mechanisms (like `ElevatorSim`) based on physical details and constraints about your system. You can see a full list of them and examples on using them [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html). -There's also the specific widget classes `Field2d` and `Mechanism2d`, which allow for pixel representations of the field and parts of mechanisms. +### Widgets -Physical objects, like game pieces, wheels, the drivetrain, etc., can be added to the Field2d object, allowing for nice representations without a real field. [Here's more](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/field2d-widget.html) on how you can use it. +There's also the widget classes `Field2d` and `Mechanism2d`, which respectively allow for pixel representations of the field and parts of mechanisms. + +Physical objects, like game pieces, wheels, the drivetrain, etc., can be added to a Field2d object, allowing for digital representations of a real-life field. [Here's more](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/field2d-widget.html) on how you can use it. Mechanism2ds allow for block representations of mechanisms. This is most commonly used for simulating arms and elevators. See [the docs](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html) for usage details and further clarifications. From dff614b5667c544171be451ab5b179f4be56c183 Mon Sep 17 00:00:00 2001 From: yxhej Date: Thu, 15 Aug 2024 12:04:06 -0400 Subject: [PATCH 08/21] introduce logging & tuning, pretty much finish sim docs --- BasicArmBot.md | 69 +++++++++++++++++++++++++++++++++++++++---- Telemetry.md | 4 +-- archive/Simulation.md | 2 +- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/BasicArmBot.md b/BasicArmBot.md index 02c5aff..bbc254f 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -360,7 +360,7 @@ While we're here, let's also set the real robot's encoder conversion factor. REV encoders' default measurements are rotations, which should be converted to radians or degrees for use on the robot. This can be easily done with Java's Units library. -Use the predetermined `POSITION_FACTOR` constant in `ArmConstants` as the base, and convert that into your unit of choice. +Use the predetermined `POSITION_FACTOR` constant in `ArmConstants` as the base, and convert that into your angular unit of choice. ```java public RealArm() { @@ -372,23 +372,82 @@ Use the predetermined `POSITION_FACTOR` constant in `ArmConstants` as the base, } ``` +### Logging + +Before we can actually see your widget, we need to send it to [NetworkTables](https://docs.wpilib.org/en/stable/docs/software/networktables/networktables-intro.html). Using [Monologue](https://github.com/shueja/Monologue/wiki), this can easily be done using the `@Log.NT` annotation, like so: + +```java + @Log.NT private final Mechanism2d mech = new Mechanism2d(2, 2); + // like other Java annotations, can be placed next to or above +``` + +Using this, you can also log other useful data, including your PID object, and even return values from methods. Be aware though; only primitive types and classes implementing `Sendable` can be sent over NetworkTables. + +For more in-depth information, we highly encourage you to read our [telemetry doc](/Telemetry.md). + ### Button & subsystem bindings -From this point, you can actually see the widget in action in the sim GUI. Follow [these directions](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html#viewing-the-mechanism2d-in-glass). +From this point, you can actually see the widget in action in the sim GUI. However, you'll notice it just... sitting there. Which makes sense... we haven't given it any commands. -Like you've done before, create your subsystem objects with `create()`, and use the `CommandXboxController` in `Robot.java` to run your subsystem commands with it. +Like you've done before, create your subsystem objects with `create()`, and use the operator `CommandXboxController` in `Robot.java` to run your subsystem commands with it. They might look something like this: ```java - operator.x().onTrue(arm.moveTo(x)); + operator.x().onTrue(arm.moveTo(your-setpoint-here)); ``` Don't forget to add your [default command]()! ### The payoff -Open up the sim GUI. Display the widget on your screen. Reminders on how to do that here. +Open up the [sim GUI](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/simulation-gui.html). + +[Display the widget]((https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html#viewing-the-mechanism2d-in-glass)) on your screen. Make sure your [joystick inputs](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/simulation-gui.html#adding-a-system-joystick-to-joysticks) are correct, keyboard and the actual selection. + +Hit one of the button axes (you'll know when the yellow square lights up) and that should run your command. + +Congrats! You've successfully simulated an arm. Play around with it, set it to different angles, have fun with it! + +Of course, this isn't the end. + +### Tuning your simulated arm + +You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's setpoint is in a different quadrant. + +This is the result of those constants being poorly tuned for that range of motion; it'll be up to you to tune the closed-loop control system. + +Current sim behavior[^2] + +[^2] As of the 24-25 school year. + +One way we can make this job much easier is with the use of our `Tuning.java` utility class, which uses WPILIB's `DoubleEntry`. + +```java + private final DoubleEntry p = Tuning.entry("/Robot/arm/P", kP); + private final DoubleEntry i = Tuning.entry("/Robot/arm/I", kI); + private final DoubleEntry d = Tuning.entry("/Robot/arm/D", kD); + // feel free to copy the above into Arm.java +``` + +These values can be modified during runtime, allowing for code modification and tuning without needing to redeploy code to test each new value. + +In your `periodic()` method, update your PID constants using the values above with the `DoubleEntry.get()` method. + +```java + @Override + public void periodic() { + pid.setP(p.get()); + // include the rest + } +``` + +In the sim GUI, you can click on the values of the DoubleEntry above to set new values. + +In AdvantageScope, tuning mode can be turned on [like so](https://github.com/Mechanical-Advantage/AdvantageScope/blob/main/docs/OPEN-LIVE.md#tuning-mode). + +Note that the constants themselves will not change in code, only for the simulation. Be sure to note down what values worked and update your constants with those correctly tuned values! +For guidance on tuning your arm control, consult the [WPILIB docs](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/introduction/tuning-vertical-arm.html). diff --git a/Telemetry.md b/Telemetry.md index fc2de67..36d1577 100644 --- a/Telemetry.md +++ b/Telemetry.md @@ -4,9 +4,9 @@ In testing (real or simulated), it is common to want to directly observe robot measurements and values in order to tune your systems and debug. Rather than spamming hundreds of print statements, WPILIB provides interfaces allowing certain information to be logged while the robot runs. -It sends data to [NetworkTables](https://docs.wpilib.org/en/stable/docs/software/networktables/networktables-intro.html), which can be read [while running](#dashboards) or later on in [log files](#log-viewers). +Loggers send data to [NetworkTables](https://docs.wpilib.org/en/stable/docs/software/networktables/networktables-intro.html), which can be read [while running](#dashboards) or stored for later viewing in [log files](#log-viewers). -Only classes that implement `Sendable` can be sent over NetworkTables; see [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/3rd-party-libraries.html) for more details. +Only classes that implement `Sendable` can be sent over NetworkTables; see [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/robot-telemetry-with-sendable.html#what-is-sendable) for more details. For more in-depth information on the inner workings, visit [the docs](https://docs.wpilib.org/en/stable/docs/software/telemetry/telemetry.html). diff --git a/archive/Simulation.md b/archive/Simulation.md index 67a503c..7dba95e 100644 --- a/archive/Simulation.md +++ b/archive/Simulation.md @@ -1,6 +1,6 @@ # Robot Simulation -Simulation in WPILib allows for code and logic to be tested onboard your computer, rather than physical hardware. It's exceptionally useful for when construction has not built the robot, but you have code to run! +Simulation in WPILib allows for code and logic to be tested onboard your computer, rather than physical hardware. It's exceptionally useful for when construction and electronics have not finished the robot, but you have code to run! There are a few different facets of simulation to take note of before you can start, including: From d51db290b9324da7a199d33010a9d844258f41fe Mon Sep 17 00:00:00 2001 From: yxhej Date: Thu, 15 Aug 2024 14:29:21 -0400 Subject: [PATCH 09/21] cleanup comments, remove empty hlinks, add clarifications --- BasicArmBot.md | 114 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 39 deletions(-) diff --git a/BasicArmBot.md b/BasicArmBot.md index bbc254f..6869fd0 100644 --- a/BasicArmBot.md +++ b/BasicArmBot.md @@ -13,7 +13,9 @@ All code examples are subject to change as a result of future library updates an ## Setting up your environment Using your knowledge of Git, create a new branch for your arm bot. Name it something like `basic-arm-bot`. *Make sure to also move to that branch!* -If unfamiliar, please check our [style sheet]() for ideal code and git practices. +If unfamiliar, please check our style sheet for ideal code and git practices. + +[comment]: # (add style sheet link when completed) Before you move on, create an `Arm.java` subsystem and an associated `ArmConstants.java` file in the robot folder of your project. @@ -23,19 +25,25 @@ Before we start, we'll be abstracting the hardware components to streamline our Moving hardware like motors and encoders to class implementations of an interface decouples them from subsystem code, enabling modularity that supports simulation and flexibility in using different hardware or none at all! For a deeper dive, you should look at our [hardware abstraction datasheet](/HardwareAbstraction.md). -Begin by creating your first IO interface in its Java file. Remember: this will act as an abstraction for a real and simulated set of hardware, so only include method headers that you think they will share. +Begin by creating your first IO interface in its Java file. This will act as an abstraction for a real and simulated set of hardware, so only include method headers that you think they will share. It should look something like this: ```java -public interface ArmIO { // introduce logging later when it comes time to visualize +public interface ArmIO { void setVoltage(double voltage); // reminder: interfaces only have method headers; - // explain why these methods + double position(); // all method bodies are specified in classes that implement ArmIO! } ``` -Now, create a real implementation of your `ArmIO` named `RealArm` in its own file. Following Java rules, it must implement the bodies of the three methods above. Try it on your own before looking at the snippets below, and remember your goal! +Now, create a real implementation of your `ArmIO` named `RealArm` in its own file. + +Think about what kind of hardware sensors the robot should use, especially when thinking about the encoder. See our [sensors doc](/Sensors.md) for background. + +Since we want to know exactly where our arm is at all times, we will use absolute encoders. WPILIB has support for RIO-connected absolute encoders with `AnalogEncoder` and [`DutyCycleEncoder`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/DutyCycleEncoder.html); we'll be using the latter. + +Following Java rules, `RealArm` must implement the bodies of the three methods above. Try it on your own before looking at the snippets below, and remember your goal! We create our hardware components... @@ -64,11 +72,13 @@ public class RealArm implements ArmIO { } ``` -Now, create a [simulated implementation](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html) of `ArmIO` called `SimArm`. WPILIB has lots of classes to simulate all different types of mechanisms, seen [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html#wpilib-s-simulation-classes). It'll look very similar to the above, only that the motor and encoder will be simulated by WPILIB's `SingleJointedArmSim` class. +Now, create a [simulated implementation](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html) of `ArmIO` called `SimArm`. + +WPILIB has many classes to simulate numerous different types of mechanisms, seen [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html#wpilib-s-simulation-classes). It'll look very similar to the above, only that the motor and encoder will be simulated by WPILIB's `SingleJointedArmSim` class. ### Constants -Digging deep into the innards, you'll notice that the constructors are a bit... packed. +Digging deep into the innards, you'll notice that the constructors are BIG. ```java // One of its many constructors; this is the one that we will be using @@ -83,11 +93,11 @@ Digging deep into the innards, you'll notice that the constructors are a bit... double startingAngleRads) ``` -(might just include the ArmConstants file in the template) +[comment]: # (might just include the ArmConstants file in the template) -They need physical information to be able to accurately simulate your system, and we obviously don't have a real robot. +They need a plethora of physical information to be able to accurately simulate your system, and we obviously don't have a real robot. All this information will also require a lot of organization. -All this information also requires lots of organization. In `ArmConstants`, you may copy-paste these relevant predetermined constants in: +For your convenience, you may copy-paste these relevant predetermined constants into `ArmConstants`: ```java import static edu.wpi.first.units.Units.*; @@ -104,7 +114,6 @@ All this information also requires lots of organization. In `ArmConstants`, you /** Unit conversions objects for the encoders. Uses the Java units library. */ public static final Measure POSITION_FACTOR = Rotations.of(THROUGHBORE_GEARING); - public static final Measure> VELOCITY_FACTOR = POSITION_FACTOR.per(Minute); /** Offset from the center of the robot to the pivot's axis of rotation */ @@ -120,14 +129,14 @@ All this information also requires lots of organization. In `ArmConstants`, you public static final Measure MASS = Kilograms.of(1); public static final Measure LENGTH = Inches.of(16); - public static final Measure> MAX_VELOCITY = RadiansPerSecond.of(9.0); + public static final Measure> MAX_VELOCITY = RadiansPerSecond.of(4); public static final Measure>> MAX_ACCEL = - RadiansPerSecond.per(Second).of(13.0); + RadiansPerSecond.per(Second).of(6); - public static final Measure STARTING_ANGLE = Degrees.of(63.3); + public static final Measure STARTING_ANGLE = Degrees.of(20); - public static final Measure MIN_ANGLE = Degrees.of(-45.7); - public static final Measure MAX_ANGLE = STARTING_ANGLE.minus(Degrees.of(1.2)); + public static final Measure MIN_ANGLE = Degrees.of(-45); + public static final Measure MAX_ANGLE = Degrees.of(225); public static final double kP = 8.0; public static final double kI = 0.0; @@ -141,19 +150,19 @@ All this information also requires lots of organization. In `ArmConstants`, you Note the `Measure` constants; this is part of WPILIB's [Java Units Library](https://docs.wpilib.org/en/stable/docs/software/basic-programming/java-units.html), which helps ensure correct units are used throughout the code. You'll see it used later on as well. -Cool thing we can do: static imports, to save a bit of space. At the top of `SimArm`, we can add +One last we can add are static imports, which will save a bit of space. At the top of `SimArm`, type ```java import static org.sciborgs1155.robot.arm.ArmConstants.*; ``` -to import all of the static objects in `ArmConstants` and turn `ArmConstants.MAX_VELOCITY` to just `MAX_VELOCITY`. +to import all static objects in `ArmConstants` and simplify `ArmConstants.MAX_VELOCITY` to `MAX_VELOCITY`. Only use static imports for constants and when it makes sense stylistically and syntactically. ### Finishing up -Initialize your arm simulator like below... +Initialize your arm simulator with the new constants, like below... ```java private final SingleJointedArmSim sim = @@ -165,15 +174,20 @@ Initialize your arm simulator like below... DCMotor.getNEO(4), 1.0 / MOTOR_GEARING, -LENGTH.in(Meters), // the Units library in action - MIN_ANGLE.in(Radians), // initialized as degrees, these are converted to radians + MIN_ANGLE.in(Radians), // initialized as degrees, converted to radians MAX_ANGLE.in(Radians), // as required by the constructor true, STARTING_ANGLE.in(Radians)); ``` -...and implement the ArmIO methods using the methods provided by the sim. Make sure to update the sim periodically in your input methods with its `update(double dtSeconds)` method. +...and implement the ArmIO methods using the methods provided by the sim. Be sure to update the sim periodically in your input methods with its `update(double dtSeconds)` method. + +```java + sim.setInputVoltage(voltage); + sim.update(PERIOD.in(Seconds)); // every 0.02 secs +``` -To complete this IO subsystem, create the last implementation called `NoArm`. It should describe a non-existent arm, without any hardware but with methods returning 0 / doing nothing. +To complete this IO subsystem, create the last implementation called `NoArm`. It should describe a non-existent arm, without any hardware but with methods returning 0 or doing nothing. This class is useful for when a mechanism is broken or is not on the robot, allowing us to run the rest of the robot without major errors. @@ -183,19 +197,21 @@ Now, it's time to work with the actual subsystem. `Arm.java` should include a fe - IO implementations of the hardware (that we just created!) - any control run on the RoboRIO (versus a motor or other hardware) -- all [commands & command factories]() +- all commands, command factories, and related methods -Think about how exactly we want our arm to act, and what kinds of control would suit that. +[comment]: # (add commands doc when merged) + +Think about how exactly we want our arm to act. It should be quick, but safe. For a long and heavy arm (relative to other mechanisms), it's dangerous to use a regular PID controller, which will command the arm straight down at high speeds. -| ![Arm with regular PID](https://i.gyazo.com/37f07bb43986f5102ebde16da9a641f8.gif) | -|:--:| -| Regular PID; same constants as gif at top of file | +| ![Arm with regular PID](https://i.gyazo.com/37f07bb43986f5102ebde16da9a641f8.gif) | ![Simulated arm moving side to side](https://i.gyazo.com/cb6189d0c58c3c28f8558cf88af827ff.gif) | +|:--:|:--:| +| Regular PID; with identical constants | ProfiledPID; with constraints | Of course, we still want the arm to reach the setpoint quickly. -A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity will increase, coast, and then decrease over time under a set of velocity and acceleration constraints to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILIB has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. **Read the docs**. +A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity will increase, coast, and then decrease over time under a set of velocity and acceleration limits to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILIB has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. *Please read the docs*. Considering that the mechanism is an arm, it should be intuitive that gravity will have a much greater (non-negligible) effect on it than other mechanisms. To account for this, we can use [WPILIB's `ArmFeedForward`](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/feedforward.html#armfeedforward), which incorporates a G term for output. @@ -215,6 +231,10 @@ public class Arm extends SubsystemBase implements Logged { this.hardware = hardware; pid = new ProfiledPIDController(kP, kI, kD, new TrapezoidProfile.Constraints(MAX_VELOCITY, MAX_ACCEL)); ff = new ArmFeedforward(kS, kV, kA); + + // set tolerance since setpoint will never be reached + pid.setTolerance(POSITION_TOLERANCE.in(unit-of-choice)); + // unit should be same as input unit } // ... } @@ -227,7 +247,9 @@ First, we'll create a non-factory method to set voltage with our controllers giv ```java public void updateGoal(double goal) { double pidOutput = pid.calculate(hardware.position(), goal); - // Notice the distinction between goal and setpoint; the feedforward takes the pid's newly generated setpoint as input to help reach that setpoint. + // Notice the distinction between goal and setpoint; + // the feedforward uses the pid's newly generated setpoint as input + // and thus will help reach that setpoint. double ffOutput = ff.calculate(pid.getSetpoint().position, pid.getSetpoint().velocity); hardware.setVoltage(pidOutput + ffOutput); } @@ -235,7 +257,7 @@ First, we'll create a non-factory method to set voltage with our controllers giv Then, we'll use this method in our command factory. Think about how we want to ideally control it. -Hint: we'd want to repeatedly run the method above until we reach our goal, after which we stop the arm (and the command!). Never forget your end condition. +Hint: we'd want to repeatedly run the method above until we reach our goal, after which we stop the arm (and the command!). Never forget your end condition & behavior. It'll look something like this: @@ -247,7 +269,9 @@ It'll look something like this: You might even want to [overload](https://www.w3schools.com/java/java_methods_overloading.asp) it to take in a goal using the Units library, but that's just for safety and not strictly required. -Finally, make a static `create()` method that will actually instantiate the `Arm` with IO implementations. You can read on why we do this in our [style sheet](). +Finally, make a static `create()` method that will actually instantiate the `Arm` with IO implementations. You can read on why we do this in our style sheet. + +[comment]: # (add style sheet link when completed) ```java /** @@ -266,7 +290,7 @@ With the introduction of this IO system, notice the substantial number of files. On the SciBorgs, subsystem files and their constants are put in one specific subsystem folder. This folder is located in a subsystems folder on the same level as the key robot files, defining the levels of contact each part of the robot should have with each other. In other words... -``` +```text ├── Autos.java ├── Constants.java ├── Main.java @@ -284,7 +308,9 @@ On the SciBorgs, subsystem files and their constants are put in one specific sub └── ... ``` -This file system structure can be referenced at any time here, or in our [style sheet](). +This file system structure can be referenced at any time here, or in our style sheet. + +[comment]: # (add style sheet link when completed) ## Making the claw @@ -296,7 +322,7 @@ In the same vein, it won't be substantial to create a simulated version; we don' Try to do this one by yourself! -^^ Should this one be guided through like before? Maybe just doing the interface would be fine, and leaving impls + subsystem up to them +[comment]: # (Should this one be guided through like before? Maybe just doing the interface would be fine, and leaving impls + subsystem up to them) ## Converting the drivetrain @@ -396,10 +422,12 @@ Like you've done before, create your subsystem objects with `create()`, and use They might look something like this: ```java - operator.x().onTrue(arm.moveTo(your-setpoint-here)); + operator.x().onTrue(arm.moveTo(your-setpoint-here.in(unit-of-choice))); ``` -Don't forget to add your [default command]()! +Don't forget to add your default command! + +[comment]: # (add default command doc link when created; no wpilib doc) ### The payoff @@ -407,7 +435,7 @@ Open up the [sim GUI](https://docs.wpilib.org/en/stable/docs/software/wpilib-too [Display the widget]((https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html#viewing-the-mechanism2d-in-glass)) on your screen. Make sure your [joystick inputs](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/simulation-gui.html#adding-a-system-joystick-to-joysticks) are correct, keyboard and the actual selection. -Hit one of the button axes (you'll know when the yellow square lights up) and that should run your command. +Hit one of the button axes (you'll know when the yellow square lights up) and that should run your command. Boom! Your arm should be moving. If you find it isn't, review review review! Congrats! You've successfully simulated an arm. Play around with it, set it to different angles, have fun with it! @@ -419,9 +447,9 @@ You might have noticed that your arm does not consistently reach its setpoint, e This is the result of those constants being poorly tuned for that range of motion; it'll be up to you to tune the closed-loop control system. -Current sim behavior[^2] +Current sim behavior[^2] dictates that values changed in code will only be seen in simulation after a restart, which is non-ideal and will take a while. -[^2] As of the 24-25 school year. +[^2]: As of the 24-25 school year. One way we can make this job much easier is with the use of our `Tuning.java` utility class, which uses WPILIB's `DoubleEntry`. @@ -451,3 +479,11 @@ In AdvantageScope, tuning mode can be turned on [like so](https://github.com/Mec Note that the constants themselves will not change in code, only for the simulation. Be sure to note down what values worked and update your constants with those correctly tuned values! For guidance on tuning your arm control, consult the [WPILIB docs](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/introduction/tuning-vertical-arm.html). + +## Continuance + +If you're interested in coding a project similar to this one, we recommned our [basic shooting project](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/DutyCycleEncoder.html)! It introduces the same concepts as this tutorial, and is a great choice if you want to test the skills learned from this guide. + +If you're feeling exceptionally confident, you can try creating a more advanced arm or shooting project. + +[comment]: # (add links to projects above when created) From ae98cdbaaceeae04a71832403b7cf8705449fb0e Mon Sep 17 00:00:00 2001 From: sigalrmp Date: Wed, 4 Sep 2024 17:51:35 -0400 Subject: [PATCH 10/21] organization --- BasicArmBot.md => projects/BasicArmBot.md | 0 Sensors.md => reference-sheets/Sensors.md | 0 Telemetry.md => reference-sheets/Telemetry.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename BasicArmBot.md => projects/BasicArmBot.md (100%) rename Sensors.md => reference-sheets/Sensors.md (100%) rename Telemetry.md => reference-sheets/Telemetry.md (100%) diff --git a/BasicArmBot.md b/projects/BasicArmBot.md similarity index 100% rename from BasicArmBot.md rename to projects/BasicArmBot.md diff --git a/Sensors.md b/reference-sheets/Sensors.md similarity index 100% rename from Sensors.md rename to reference-sheets/Sensors.md diff --git a/Telemetry.md b/reference-sheets/Telemetry.md similarity index 100% rename from Telemetry.md rename to reference-sheets/Telemetry.md From 78dfd8e8e86b4b4a98daf05999c7ac7f55e98edf Mon Sep 17 00:00:00 2001 From: sigalrmp Date: Wed, 4 Sep 2024 17:57:15 -0400 Subject: [PATCH 11/21] updated links --- projects/BasicArmBot.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 6869fd0..99db964 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -39,7 +39,7 @@ public interface ArmIO { Now, create a real implementation of your `ArmIO` named `RealArm` in its own file. -Think about what kind of hardware sensors the robot should use, especially when thinking about the encoder. See our [sensors doc](/Sensors.md) for background. +Think about what kind of hardware sensors the robot should use, especially when thinking about the encoder. See our [sensors doc](/reference-sheets/Sensors.md) for background. Since we want to know exactly where our arm is at all times, we will use absolute encoders. WPILIB has support for RIO-connected absolute encoders with `AnalogEncoder` and [`DutyCycleEncoder`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/DutyCycleEncoder.html); we'll be using the latter. @@ -409,7 +409,7 @@ Before we can actually see your widget, we need to send it to [NetworkTables](ht Using this, you can also log other useful data, including your PID object, and even return values from methods. Be aware though; only primitive types and classes implementing `Sendable` can be sent over NetworkTables. -For more in-depth information, we highly encourage you to read our [telemetry doc](/Telemetry.md). +For more in-depth information, we highly encourage you to read our [telemetry doc](/reference-sheets/Telemetry.md). ### Button & subsystem bindings From 02738f2e4ee87b1e595600742ebe7e98f5db5181 Mon Sep 17 00:00:00 2001 From: Ivan <116034917+Yxhej@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:32:35 -0400 Subject: [PATCH 12/21] address comments and create new sections --- projects/BasicArmBot.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 99db964..9b9a6ec 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -264,6 +264,7 @@ It'll look something like this: ```java public Command moveTo(double goal) { return run(() -> updateGoal(goal)).until(pid::atGoal).finallyDo(() -> hardware.setVoltage(0)); + // repeatedly runs the updateGoal() method with the given goal until the arm has reached that goal, and ensures stopping on completion } ``` @@ -322,8 +323,7 @@ In the same vein, it won't be substantial to create a simulated version; we don' Try to do this one by yourself! -[comment]: # (Should this one be guided through like before? Maybe just doing the interface would be fine, and leaving impls + subsystem up to them) - +For reference, the generic IO interface for the claw only really needs one method to run the rollers and pick up gamepieces. ## Converting the drivetrain Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. If you've completed the previous Differential Drive projects, you should have everything good to go. Good luck! @@ -441,9 +441,15 @@ Congrats! You've successfully simulated an arm. Play around with it, set it to d Of course, this isn't the end. +### Unit Testing + +On the SciBorgs, we utilize unit tests + +### Systems Checks + ### Tuning your simulated arm -You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's setpoint is in a different quadrant. +You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's goal is in a different quadrant. This is the result of those constants being poorly tuned for that range of motion; it'll be up to you to tune the closed-loop control system. From 940fda6588e3de0457f8b693b5a08357cfcc3ac1 Mon Sep 17 00:00:00 2001 From: yxhej Date: Mon, 9 Sep 2024 23:58:15 -0400 Subject: [PATCH 13/21] i'm gonna crash i need to push --- projects/BasicArmBot.md | 3 +- reference-sheets/Command-based.md | 48 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 reference-sheets/Command-based.md diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 9b9a6ec..647a913 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -443,7 +443,8 @@ Of course, this isn't the end. ### Unit Testing -On the SciBorgs, we utilize unit tests +On the SciBorgs, we utilize unit tests as a quick, organized way to see if any untested code have large, potentially dangerous effects on robot operation. Refer [here]() for details. + ### Systems Checks diff --git a/reference-sheets/Command-based.md b/reference-sheets/Command-based.md new file mode 100644 index 0000000..1c42ec0 --- /dev/null +++ b/reference-sheets/Command-based.md @@ -0,0 +1,48 @@ +# [The Command-based Paradigm](https://docs.wpilib.org/en/stable/docs/software/commandbased/what-is-command-based.html) + +For its wide capabilities and ease of use, we use WPILIB's command-based paradigm to compartmentalize and control the different parts of our robot. + +Subsystems represent independent parts of the robot and their hardware, which work together to achieve a desired action. + +Commands safely direct those subsystems to perform actions in a wide suite of ways. They can be chained together in parallel or made sequential to one another, among other handy functions. + +While these are generally their definitions, there's a fair bit of nuance to both. + +## [Subsystems](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html) + +For our purposes, all subsystems extend WPILib's `SubsystemBase`, providing command safety functionality, its [inherited periodic methods](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#periodic), and [default commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#default-commands). See in greater detail on [the official WPILIB docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html). + +### A word on what makes a subsystem a subsystem + +More specifically, subsystems can be defined as collections of hardware that are not dependent on others to function well. As a result, multiple different mechanisms can be part of a single subsystem. + +For instance, imagine a double-jointed arm. When the joint connected to the main pivot moves, the position of the arm stemming from the joint will change, making their movement and position dependent on one another. + +As a result, it is good practice to contain both mechanisms in the same subsystem so that they have easy access to each other's information, which makes accounting for relative position easier. This limits code duplication and makes working with the whole simpler. + +**This is not a finite definition, and from year to year, the requirements for robot operation may change. Use your own discretion to see what sense of organization works best for your robot!** + +## [Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/commands.html) + +Every individual command has a defined structure, with methods that are called when it begins, throughout its runtime, and when it ends (as a result of a set end condition OR by interruption). + +### Command Compositions + +Commands can also be chained together to create much larger commands for complex routines. You'll likely be using these a lot: + +- [Parallel Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#parallel) - allow multiple commands to be run at once with differing end conditions + - Deadline + - Race +- [Sequential Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#sequence) - allow commands to be run one after another + +## Complications & Common Problems + +Each instance of a command can only be scheduled once at a time, otherwise risking unpredictable behavior. To remedy this, we create command factories that return new instances of a command each time it is called, like below: + +```java + public Command updateSetpoint(double velocity) { + return run(() -> hardware.setVelocity(velocity)); + } +``` + +- epxlain proxy (my eyes are going to fall) From e95dcc9b46e56c9f73e7de2aec814c204f49847c Mon Sep 17 00:00:00 2001 From: Yxhej Date: Tue, 10 Sep 2024 12:37:17 -0400 Subject: [PATCH 14/21] more commands update readme and add beambreak to sensors --- archive/Simulation.md | 6 ++--- projects/BasicArmBot.md | 14 +++++----- reference-sheets/Command-based.md | 44 ++++++++++++++++++++++++++----- reference-sheets/README.md | 3 +++ reference-sheets/Sensors.md | 14 ++++++++-- reference-sheets/Telemetry.md | 8 +++--- 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/archive/Simulation.md b/archive/Simulation.md index 7dba95e..c58fae5 100644 --- a/archive/Simulation.md +++ b/archive/Simulation.md @@ -5,7 +5,7 @@ Simulation in WPILib allows for code and logic to be tested onboard your compute There are a few different facets of simulation to take note of before you can start, including: - [The Simulation GUI](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation) -- [WPILIB's simulation classes](#simulation-classes) +- [WPILib's simulation classes](#simulation-classes) - [Physics simulators](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html) - [Mechanism2d](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html) - [Field2d](https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/field2d-widget.html) @@ -19,11 +19,11 @@ For more details, visit [our doc](/Telemetry.md). ## Simulation Classes -This section will heavily reference the WPILIB docs [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). +This section will heavily reference the WPILib docs [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). Before doing anything with simulation, make sure desktop support is turned on. Follow [these instructions](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). Use [the next doc](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation) as reference for the GUI when working with it (same as above). -WPILIB contains generic simulation classes for different mechanisms (like `ElevatorSim`) based on physical details and constraints about your system. You can see a full list of them and examples on using them [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html). +WPILib contains generic simulation classes for different mechanisms (like `ElevatorSim`) based on physical details and constraints about your system. You can see a full list of them and examples on using them [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html). ### Widgets diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 647a913..7e37e9e 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -41,7 +41,7 @@ Now, create a real implementation of your `ArmIO` named `RealArm` in its own fil Think about what kind of hardware sensors the robot should use, especially when thinking about the encoder. See our [sensors doc](/reference-sheets/Sensors.md) for background. -Since we want to know exactly where our arm is at all times, we will use absolute encoders. WPILIB has support for RIO-connected absolute encoders with `AnalogEncoder` and [`DutyCycleEncoder`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/DutyCycleEncoder.html); we'll be using the latter. +Since we want to know exactly where our arm is at all times, we will use absolute encoders. WPILib has support for RIO-connected absolute encoders with `AnalogEncoder` and [`DutyCycleEncoder`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/DutyCycleEncoder.html); we'll be using the latter. Following Java rules, `RealArm` must implement the bodies of the three methods above. Try it on your own before looking at the snippets below, and remember your goal! @@ -74,7 +74,7 @@ public class RealArm implements ArmIO { Now, create a [simulated implementation](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html) of `ArmIO` called `SimArm`. -WPILIB has many classes to simulate numerous different types of mechanisms, seen [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html#wpilib-s-simulation-classes). It'll look very similar to the above, only that the motor and encoder will be simulated by WPILIB's `SingleJointedArmSim` class. +WPILib has many classes to simulate numerous different types of mechanisms, seen [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/physics-sim.html#wpilib-s-simulation-classes). It'll look very similar to the above, only that the motor and encoder will be simulated by WPILib's `SingleJointedArmSim` class. ### Constants @@ -148,7 +148,7 @@ For your convenience, you may copy-paste these relevant predetermined constants public static final double kG = 0.12055; ``` -Note the `Measure` constants; this is part of WPILIB's [Java Units Library](https://docs.wpilib.org/en/stable/docs/software/basic-programming/java-units.html), which helps ensure correct units are used throughout the code. You'll see it used later on as well. +Note the `Measure` constants; this is part of WPILib's [Java Units Library](https://docs.wpilib.org/en/stable/docs/software/basic-programming/java-units.html), which helps ensure correct units are used throughout the code. You'll see it used later on as well. One last we can add are static imports, which will save a bit of space. At the top of `SimArm`, type @@ -211,9 +211,9 @@ For a long and heavy arm (relative to other mechanisms), it's dangerous to use a Of course, we still want the arm to reach the setpoint quickly. -A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity will increase, coast, and then decrease over time under a set of velocity and acceleration limits to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILIB has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. *Please read the docs*. +A smoother alternative to the regular PID would be the addition of a trapezoid profile. Velocity will increase, coast, and then decrease over time under a set of velocity and acceleration limits to reach a goal smoothly, plotting a graph in the shape of a trapezoid. WPILib has [its own implementation of this](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/profiled-pidcontroller.html) in `ProfiledPIDController`. *Please read the docs*. -Considering that the mechanism is an arm, it should be intuitive that gravity will have a much greater (non-negligible) effect on it than other mechanisms. To account for this, we can use [WPILIB's `ArmFeedForward`](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/feedforward.html#armfeedforward), which incorporates a G term for output. +Considering that the mechanism is an arm, it should be intuitive that gravity will have a much greater (non-negligible) effect on it than other mechanisms. To account for this, we can use [WPILib's `ArmFeedForward`](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/feedforward.html#armfeedforward), which incorporates a G term for output. The top of your file should look similar to this: @@ -458,7 +458,7 @@ Current sim behavior[^2] dictates that values changed in code will only be seen [^2]: As of the 24-25 school year. -One way we can make this job much easier is with the use of our `Tuning.java` utility class, which uses WPILIB's `DoubleEntry`. +One way we can make this job much easier is with the use of our `Tuning.java` utility class, which uses WPILib's `DoubleEntry`. ```java private final DoubleEntry p = Tuning.entry("/Robot/arm/P", kP); @@ -485,7 +485,7 @@ In AdvantageScope, tuning mode can be turned on [like so](https://github.com/Mec Note that the constants themselves will not change in code, only for the simulation. Be sure to note down what values worked and update your constants with those correctly tuned values! -For guidance on tuning your arm control, consult the [WPILIB docs](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/introduction/tuning-vertical-arm.html). +For guidance on tuning your arm control, consult the [WPILib docs](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/introduction/tuning-vertical-arm.html). ## Continuance diff --git a/reference-sheets/Command-based.md b/reference-sheets/Command-based.md index 1c42ec0..37d0356 100644 --- a/reference-sheets/Command-based.md +++ b/reference-sheets/Command-based.md @@ -1,6 +1,6 @@ # [The Command-based Paradigm](https://docs.wpilib.org/en/stable/docs/software/commandbased/what-is-command-based.html) -For its wide capabilities and ease of use, we use WPILIB's command-based paradigm to compartmentalize and control the different parts of our robot. +For its wide capabilities and ease of use, we use WPILib's command-based paradigm to compartmentalize and control the different parts of our robot. Subsystems represent independent parts of the robot and their hardware, which work together to achieve a desired action. @@ -10,7 +10,7 @@ While these are generally their definitions, there's a fair bit of nuance to bot ## [Subsystems](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html) -For our purposes, all subsystems extend WPILib's `SubsystemBase`, providing command safety functionality, its [inherited periodic methods](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#periodic), and [default commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#default-commands). See in greater detail on [the official WPILIB docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html). +For our purposes, all subsystems extend WPILib's `SubsystemBase`, providing command safety functionality, its [inherited periodic methods](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#periodic), and [default commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#default-commands). See in greater detail on [the official WPILib docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html). ### A word on what makes a subsystem a subsystem @@ -20,23 +20,47 @@ For instance, imagine a double-jointed arm. When the joint connected to the main As a result, it is good practice to contain both mechanisms in the same subsystem so that they have easy access to each other's information, which makes accounting for relative position easier. This limits code duplication and makes working with the whole simpler. -**This is not a finite definition, and from year to year, the requirements for robot operation may change. Use your own discretion to see what sense of organization works best for your robot!** +**This is not set in stone, and from year to year, the requirements for robot operation may change. Use your own discretion to see what sense of organization works best for your robot!** ## [Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/commands.html) -Every individual command has a defined structure, with methods that are called when it begins, throughout its runtime, and when it ends (as a result of a set end condition OR by interruption). +Commands represent robot actions with the hardware. They should be used for actions that may interfere with how the robot is run (i.e actual robot actions or changing PID constants) or cause dangerous movement. + +Every individual command has a defined structure, with methods that are called when it begins, throughout its runtime, and when it ends (as a result of a set end condition OR by interruption). Check the [official docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/commands.html) for specfics. + +A nice list of these individual commands can be found under the "Direct Known Subclasses" heading [here](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html). ### Command Compositions Commands can also be chained together to create much larger commands for complex routines. You'll likely be using these a lot: - [Parallel Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#parallel) - allow multiple commands to be run at once with differing end conditions - - Deadline - - Race + - Deadline and Race Commands - [Sequential Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#sequence) - allow commands to be run one after another +For more examples, see a good list [here](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#composition-types). + +**Don't forget the end condition and behavior!** + +### Decorators + +WPILib provides command methods that can chain onto other commands to dynamically create compositions like the one above. + +These include methods like `andThen()` and `alongWith()`, representing the creation of a sequential and parallel command respectively. + +When properly composed, complex commands can often be read through like plain english, like below: + +```java + operator + .leftTrigger() + .whileTrue( + shooting.shootWithPivot(PivotConstants.FEED_ANGLE, ShooterConstants.DEFAULT_VELOCITY)); +``` + ## Complications & Common Problems +### Singular Command Instances + Each instance of a command can only be scheduled once at a time, otherwise risking unpredictable behavior. To remedy this, we create command factories that return new instances of a command each time it is called, like below: ```java @@ -45,4 +69,10 @@ Each instance of a command can only be scheduled once at a time, otherwise riski } ``` -- epxlain proxy (my eyes are going to fall) +### Command Composition Requirements & Proxying + +When created, command compositions take on the subsystem requirements of all of its parts, sometimes creating undesirable behavior as no other commands can be run on a subsystem even if the composition has sequenced past that point. + +A solution to this is command proxying. This wraps a command to be scheduled only when it is called in the composition (as opposed to scheduling the entire composition at once), causing subsystem requirements to only be held while that wrapped command is running, avoiding the issue. For more in-depth discussion, see [the docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands). + +For instance, this is useful in instances where a default command should hold an arm in place. In a composition containing a command on the arm, the arm will not be able to run its default and instead slowly fall as a result of gravity. When proxied, the arm's default command will run when it is not doing anything. diff --git a/reference-sheets/README.md b/reference-sheets/README.md index e69de29..70f9873 100644 --- a/reference-sheets/README.md +++ b/reference-sheets/README.md @@ -0,0 +1,3 @@ +# Document Reference Sheets + +This folder contains guides that introduce and summarize key topics (and practices) with links to official WPILib / vendor documentation for further, in-depth exploration. diff --git a/reference-sheets/Sensors.md b/reference-sheets/Sensors.md index eb2f865..3bf0e0f 100644 --- a/reference-sheets/Sensors.md +++ b/reference-sheets/Sensors.md @@ -6,7 +6,7 @@ This aims to be an brief reference to the different sensors that you will most c They measure rotation. Many brushless motors have encoders integrated inside of them, while others can be found outside of the robot. Most measure what is effectively rotational displacement, as encoders read negative values from relative backwards movement. -All encoders have some sort of default unit. Various vendors will have different methods of changing the units returned; read the docs! +All encoders have some sort of default unit. Various vendors will have different methods of changing the units returned; read their docs! ### [Relative Encoders](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/sensor-overview-software.html#sensor-overview-software) @@ -20,6 +20,16 @@ Has a set zero point. Will always know where it is, even between code deploys. It measures the rate of rotation of whatever plane it is on (and sometimes its relative axes). Usually found on the base of the drivetrain. -## [Cameras]() (got carried away here lmao) +## [Beambreaks]() + +Detect if something has passed through them. Two key components: a part that shoots a ray of light, and a receiver. When the receiver no longer detects light, a signal is returned. + +For our program, beambreak sensors return true when unblocked, and false when blocked. **THIS IS SUBJECT TO CHANGE.** + +## [Cameras](https://docs.photonvision.org/en/latest/docs/integration/aprilTagStrategies.html) Using the known position of a camera and the known position of targets in its view, the target-relative and field-relative position of a robot can be calculated. These can be used to auto-correct odometry, auto-aim towards a target, or [automate movement entirely](https://www.youtube.com/watch?v=2zB0w69P4mc&t=73s). + +## [Interactions with Software](https://docs.wpilib.org/en/stable/docs/software/hardware-apis/sensors/digital-inputs-software.html#digital-inputs-software) + +From a code perspective, nearly all will be integrated into hardware (that can be interacted with their apis) or plugged into the RoboRIO (that can be interacted with using built-in WPILib classes, like `RelativeEncoder` or `DigitalInput`). diff --git a/reference-sheets/Telemetry.md b/reference-sheets/Telemetry.md index 36d1577..e5cff65 100644 --- a/reference-sheets/Telemetry.md +++ b/reference-sheets/Telemetry.md @@ -2,7 +2,7 @@ ### The art of logging and log-viewing -In testing (real or simulated), it is common to want to directly observe robot measurements and values in order to tune your systems and debug. Rather than spamming hundreds of print statements, WPILIB provides interfaces allowing certain information to be logged while the robot runs. +In testing (real or simulated), it is common to want to directly observe robot measurements and values in order to tune your systems and debug. Rather than looping print statements, WPILib provides interfaces allowing certain information to be logged while the robot runs. Loggers send data to [NetworkTables](https://docs.wpilib.org/en/stable/docs/software/networktables/networktables-intro.html), which can be read [while running](#dashboards) or stored for later viewing in [log files](#log-viewers). @@ -12,12 +12,12 @@ For more in-depth information on the inner workings, visit [the docs](https://do ## Logging libraries -Logging libraries, third-party or WPILIB-made, are not monolithic. Some are annotation-based (using Java `@Annotations`), while others are framework-based. All are unique; a nice list of them can be found [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/3rd-party-libraries.html). +Logging libraries, third-party or WPILib-made, are not monolithic. Some are annotation-based (using Java `@Annotations`), while others are framework-based. All are unique; a nice list of them can be found [here](https://docs.wpilib.org/en/stable/docs/software/telemetry/3rd-party-libraries.html). - [Monologue](https://github.com/shueja/Monologue/wiki) (we use this!) -- Epilogue (official WPILIB; soon to exist documentation) +- [Epilogue](https://docs.wpilib.org/pt/latest/docs/software/telemetry/robot-telemetry-with-annotations.html) (official WPILib) - [AdvantageKit](https://github.com/Mechanical-Advantage/AdvantageKit/blob/main/docs/WHAT-IS-ADVANTAGEKIT.md) -- URCL +- [URCL](https://github.com/Mechanical-Advantage/URCL) ## Dashboards From f6b45f68ac9601aad89b2e63fb70d33d92ed8fd1 Mon Sep 17 00:00:00 2001 From: Yxhej Date: Thu, 12 Sep 2024 12:17:25 -0400 Subject: [PATCH 15/21] add hlaf of tests --- projects/BasicArmBot.md | 49 ++++++++++++++++++++++++++++--- reference-sheets/Command-based.md | 10 +++++-- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 7e37e9e..d1b3d07 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -435,18 +435,44 @@ Open up the [sim GUI](https://docs.wpilib.org/en/stable/docs/software/wpilib-too [Display the widget]((https://docs.wpilib.org/en/stable/docs/software/dashboards/glass/mech2d-widget.html#viewing-the-mechanism2d-in-glass)) on your screen. Make sure your [joystick inputs](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/simulation-gui.html#adding-a-system-joystick-to-joysticks) are correct, keyboard and the actual selection. -Hit one of the button axes (you'll know when the yellow square lights up) and that should run your command. Boom! Your arm should be moving. If you find it isn't, review review review! +Hit one of the button axes (you'll know when the yellow square lights up) and that should run your command. Boom! Your arm should be moving. If you find it isn't, review review review (make sure your kP constant is >0)! Congrats! You've successfully simulated an arm. Play around with it, set it to different angles, have fun with it! Of course, this isn't the end. -### Unit Testing +### Unit Testing & Systems Checks -On the SciBorgs, we utilize unit tests as a quick, organized way to see if any untested code have large, potentially dangerous effects on robot operation. Refer [here]() for details. +On the SciBorgs, we like to emphasize [unit tests](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/unit-testing.html) and systems checks to make sure all code works and performs as intended. +As a reminder: + +- Off the robot, we utilize unit tests as a quick way to see if untested code has potentially unsafe effects on robot performance using our underlying simulation. +- During competition, systems checks are a way for us to quickly determine if our physical robot is operating as intended. + +To these ends, we've created libraries to simplify the process. + +In the initial differential drive project, unit tests and systems check commands were made separately. However, our testing libraries allow for a single `Test` routine to be used both as a unit test or systems check. + +Please review our [Unit Tests and Systems Checks reference sheets]() to gain an understanding of how these libraries work. + +Take our arm, for example. We'll create a `Test` that will be run as either of the tests when required. + +Before doing anything, think about what we want to test about the arm. We want it to operate properly, which means the arm moves where we want to. With a trapezoid profile as well, we want to see + +- if the arm's position ultimately reaches its goal +- if the arm's velocity reaches its velocity setpoints + +We first define our test factory; call it anything, but ensure it is distinguished as a `Test`: + +```java + public Test moveToTest() {} +``` + +Tests comprise a test command, and related assertions (things that should / should not be true). Otherwise, there would be nothing to test! + +For the sake of example, we'll create one EqualityAssertion and one TruthAssertion. -### Systems Checks ### Tuning your simulated arm @@ -494,3 +520,18 @@ If you're interested in coding a project similar to this one, we recommned our [ If you're feeling exceptionally confident, you can try creating a more advanced arm or shooting project. [comment]: # (add links to projects above when created) + + public Command systemsCheck() { + return Test.toCommand( + shooter.goToTest(RadiansPerSecond.of(100)), + Test.fromCommand( + intake + .intake() + .asProxy() + .deadlineWith(feeder.forward(), shooter.runShooter(100)) + .withTimeout(1)), + pivot.goToTest(Radians.of(0)), + pivot.goToTest(STARTING_ANGLE), + drive.systemsCheck()) + .withName("Test Mechanisms"); + } \ No newline at end of file diff --git a/reference-sheets/Command-based.md b/reference-sheets/Command-based.md index 37d0356..85e2f8b 100644 --- a/reference-sheets/Command-based.md +++ b/reference-sheets/Command-based.md @@ -34,9 +34,9 @@ A nice list of these individual commands can be found under the "Direct Known Su Commands can also be chained together to create much larger commands for complex routines. You'll likely be using these a lot: -- [Parallel Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#parallel) - allow multiple commands to be run at once with differing end conditions - - Deadline and Race Commands -- [Sequential Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#sequence) - allow commands to be run one after another +- [Parallel Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#parallel) - run multiple commands / runnables at once + - Deadline and Race Commands (different end conditions) +- [Sequential Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#sequence) - run commands / runnables after the previous one ends For more examples, see a good list [here](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#composition-types). @@ -57,6 +57,10 @@ When properly composed, complex commands can often be read through like plain en shooting.shootWithPivot(PivotConstants.FEED_ANGLE, ShooterConstants.DEFAULT_VELOCITY)); ``` +Individual commands and command groups each have their own singular / group of decorators. The majority can be found [here](). + +Commands can also be accessed through WPILib's `Commands` class. + ## Complications & Common Problems ### Singular Command Instances From 34907ae9e32bc9fa6f4e578dfd04d66cd5a37626 Mon Sep 17 00:00:00 2001 From: Yxhej Date: Sun, 15 Sep 2024 01:03:49 -0400 Subject: [PATCH 16/21] small more test --- projects/BasicArmBot.md | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index d1b3d07..0852e63 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -324,6 +324,7 @@ In the same vein, it won't be substantial to create a simulated version; we don' Try to do this one by yourself! For reference, the generic IO interface for the claw only really needs one method to run the rollers and pick up gamepieces. + ## Converting the drivetrain Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem implementation. If you've completed the previous Differential Drive projects, you should have everything good to go. Good luck! @@ -443,14 +444,7 @@ Of course, this isn't the end. ### Unit Testing & Systems Checks -On the SciBorgs, we like to emphasize [unit tests](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/unit-testing.html) and systems checks to make sure all code works and performs as intended. - -As a reminder: - -- Off the robot, we utilize unit tests as a quick way to see if untested code has potentially unsafe effects on robot performance using our underlying simulation. -- During competition, systems checks are a way for us to quickly determine if our physical robot is operating as intended. - -To these ends, we've created libraries to simplify the process. +On the SciBorgs, we like to emphasize [unit tests](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/unit-testing.html) and systems checks to make sure all code works and performs as intended. To these ends, we've created libraries to simplify the process. In the initial differential drive project, unit tests and systems check commands were made separately. However, our testing libraries allow for a single `Test` routine to be used both as a unit test or systems check. @@ -471,8 +465,36 @@ We first define our test factory; call it anything, but ensure it is distinguish Tests comprise a test command, and related assertions (things that should / should not be true). Otherwise, there would be nothing to test! -For the sake of example, we'll create one EqualityAssertion and one TruthAssertion. +Let's first create the test command, which should just move to a certain position (since we want to test how the arm moves): + +```java + Measure angle = Degrees.of(45); + Command testCommand = moveTo(angle); +``` + +Then, let's create the checks for those tests. For the sake of example, we'll create one `EqualityAssertion` testing position and one `TruthAssertion` testing velocity setpoint. +We like to use the `tAssert()` and `eAssert()` factories in `Assertions.java` to actually instantiate these tests. They accept a string name, as well as functions for the expected and real values (with a tolerance for the equality assertion): + +```java + EqualityAssertion velocityCheck = + Assertion.eAssert( + "Arm Position Check: expected 0 rad/s, actually " + hardware.velocity() + " rad/s", + () -> 0, + hardware::position, + 0.5); + TruthAssertion atPositionCheck = + Assertion.tAssert( + pid::atGoal, + "Arm At Goal Check", + "expected " + angle.in(Radians) + " rad, actually " + hardware.position() + " rad"); +``` + +And finally, let's return the created test: + +```java + return new Test(testCommand, Set.of(velocityCheck, atPositionCheck)); +``` ### Tuning your simulated arm From 3e47a0976256e3ef7496e3cf83098170b5dd4617 Mon Sep 17 00:00:00 2001 From: yxhej Date: Thu, 19 Sep 2024 23:40:41 -0400 Subject: [PATCH 17/21] add main readme w/ contribution info + command sheet org --- README.md | 25 +++++++++++++++++++++ projects/BasicArmBot.md | 20 +++++++---------- reference-sheets/Command-based.md | 37 ++++++++++++++++--------------- 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index e69de29..068a177 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,25 @@ +# SciGuides + +Welcome to SciGuides, a repository made by FRC Team 1155 and 2265 to gather all of our experience and knowledge for public and internal use. + +## Usage + +SciGuides is project-based, containing guides and reference sheets aimed at guiding readers towards a high level of competency around FRC code. + +It can be used as teaching material, robot code practice, or for referencing information and generational knowledge. + +This repository additionally functions as the curriculum of our school's central robotics program. + +## Contributing & Updating + +There are a few values we emphasize when writing SciGuides: + +- Don't reinvent the wheel. + - Maintaining more than necessary, or redundant information, would be a waste of time, so we heavily reference existing documentation from vendors, libraries, and WPILib. + - Minimize what must be changed from year-to-year! There is no need to restate information with existing documentation past a simple sentence or past the purposes of introduction. + +- Experiencing is the heart of learning. + - It is our firm belief that people learn best when actually **writing** and **seeing** code (not just text) on the screen. Code examples, code examples, code examples (and the occasional image and gif, too!) + +- These guidelines are not immutable. + - Use your own discretion (or that of your fellow team members) to determine what is best for this repository to achieve its goals. diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 0852e63..0d92101 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -197,9 +197,7 @@ Now, it's time to work with the actual subsystem. `Arm.java` should include a fe - IO implementations of the hardware (that we just created!) - any control run on the RoboRIO (versus a motor or other hardware) -- all commands, command factories, and related methods - -[comment]: # (add commands doc when merged) +- all [commands](/reference-sheets/Command-based.md#commands), command factories, and related methods Think about how exactly we want our arm to act. It should be quick, but safe. @@ -412,7 +410,7 @@ Using this, you can also log other useful data, including your PID object, and e For more in-depth information, we highly encourage you to read our [telemetry doc](/reference-sheets/Telemetry.md). -### Button & subsystem bindings +### Button & Subsystem Bindings From this point, you can actually see the widget in action in the sim GUI. @@ -426,9 +424,7 @@ They might look something like this: operator.x().onTrue(arm.moveTo(your-setpoint-here.in(unit-of-choice))); ``` -Don't forget to add your default command! - -[comment]: # (add default command doc link when created; no wpilib doc) +Don't forget to add your [default command](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#default-commands)! ### The payoff @@ -444,11 +440,11 @@ Of course, this isn't the end. ### Unit Testing & Systems Checks -On the SciBorgs, we like to emphasize [unit tests](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/unit-testing.html) and systems checks to make sure all code works and performs as intended. To these ends, we've created libraries to simplify the process. +On the SciBorgs, we like to emphasize [unit tests](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/unit-testing.html) and systems checks to make sure all code works and performs as intended. To these ends, we've created libraries to simplify the process. Please review our [Unit Tests and Systems Checks reference sheets]() to understand how they work. -In the initial differential drive project, unit tests and systems check commands were made separately. However, our testing libraries allow for a single `Test` routine to be used both as a unit test or systems check. +[comment]: # (add sheet link when completed) -Please review our [Unit Tests and Systems Checks reference sheets]() to gain an understanding of how these libraries work. +In the initial differential drive project, unit tests and systems check commands were made separately. However, our testing libraries allow for a single `Test` routine to be used both as a unit test or systems check. Take our arm, for example. We'll create a `Test` that will be run as either of the tests when required. @@ -463,9 +459,9 @@ We first define our test factory; call it anything, but ensure it is distinguish public Test moveToTest() {} ``` -Tests comprise a test command, and related assertions (things that should / should not be true). Otherwise, there would be nothing to test! +Tests comprise a test command and related assertions (things that should / should not be true). Otherwise, there would be nothing to test! -Let's first create the test command, which should just move to a certain position (since we want to test how the arm moves): +Since we want to test movement, let's make the test command move to a certain position: ```java Measure angle = Degrees.of(45); diff --git a/reference-sheets/Command-based.md b/reference-sheets/Command-based.md index 85e2f8b..51320a7 100644 --- a/reference-sheets/Command-based.md +++ b/reference-sheets/Command-based.md @@ -12,15 +12,7 @@ While these are generally their definitions, there's a fair bit of nuance to bot For our purposes, all subsystems extend WPILib's `SubsystemBase`, providing command safety functionality, its [inherited periodic methods](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#periodic), and [default commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#default-commands). See in greater detail on [the official WPILib docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html). -### A word on what makes a subsystem a subsystem - -More specifically, subsystems can be defined as collections of hardware that are not dependent on others to function well. As a result, multiple different mechanisms can be part of a single subsystem. - -For instance, imagine a double-jointed arm. When the joint connected to the main pivot moves, the position of the arm stemming from the joint will change, making their movement and position dependent on one another. - -As a result, it is good practice to contain both mechanisms in the same subsystem so that they have easy access to each other's information, which makes accounting for relative position easier. This limits code duplication and makes working with the whole simpler. - -**This is not set in stone, and from year to year, the requirements for robot operation may change. Use your own discretion to see what sense of organization works best for your robot!** +Advanced readers may want to know [more about what a subsystem is](#a-word-on-what-makes-a-subsystem-a-subsystem). ## [Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/commands.html) @@ -28,7 +20,9 @@ Commands represent robot actions with the hardware. They should be used for acti Every individual command has a defined structure, with methods that are called when it begins, throughout its runtime, and when it ends (as a result of a set end condition OR by interruption). Check the [official docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/commands.html) for specfics. -A nice list of these individual commands can be found under the "Direct Known Subclasses" heading [here](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html). +A nice list of these individual commands can be found under the subclasses of WPILib's [Command class](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html). + +We avoid using specific control commands like the `PIDCommand` or `SwerveControllerCommand` as they limit our precision and capabilities compared to using their components individually. ### Command Compositions @@ -40,11 +34,9 @@ Commands can also be chained together to create much larger commands for complex For more examples, see a good list [here](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#composition-types). -**Don't forget the end condition and behavior!** - ### Decorators -WPILib provides command methods that can chain onto other commands to dynamically create compositions like the one above. +WPILib provides command methods that can chain onto other commands to dynamically create compositions like the one below. These include methods like `andThen()` and `alongWith()`, representing the creation of a sequential and parallel command respectively. @@ -55,13 +47,14 @@ When properly composed, complex commands can often be read through like plain en .leftTrigger() .whileTrue( shooting.shootWithPivot(PivotConstants.FEED_ANGLE, ShooterConstants.DEFAULT_VELOCITY)); + // while the operator controller's left trigger button is held, shoot ``` -Individual commands and command groups each have their own singular / group of decorators. The majority can be found [here](). +Individual commands and command groups each have their own singular / group of decorators. The majority can be found [here](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html). -Commands can also be accessed through WPILib's `Commands` class. +Commands can also be accessed through [WPILib's `Commands`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Commands.html) class. -## Complications & Common Problems +## Specifics & Common Issues ### Singular Command Instances @@ -77,6 +70,14 @@ Each instance of a command can only be scheduled once at a time, otherwise riski When created, command compositions take on the subsystem requirements of all of its parts, sometimes creating undesirable behavior as no other commands can be run on a subsystem even if the composition has sequenced past that point. -A solution to this is command proxying. This wraps a command to be scheduled only when it is called in the composition (as opposed to scheduling the entire composition at once), causing subsystem requirements to only be held while that wrapped command is running, avoiding the issue. For more in-depth discussion, see [the docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands). +The current best solution to this (as of 24-25) is command proxying. See [the docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands) for a more in-depth discussion. + +### A word on what makes a subsystem a subsystem + +More specifically, subsystems can be defined as collections of hardware that are not dependent on others to function well. As a result, multiple different mechanisms can be part of a single subsystem. + +For instance, imagine a double-jointed arm. When the joint connected to the main pivot moves, the position of the arm stemming from the joint will change, making their movement and position dependent on one another. -For instance, this is useful in instances where a default command should hold an arm in place. In a composition containing a command on the arm, the arm will not be able to run its default and instead slowly fall as a result of gravity. When proxied, the arm's default command will run when it is not doing anything. +As a result, it is good practice to contain both mechanisms in the same subsystem so that they have easy access to each other's information, which makes accounting for relative position easier. This limits code duplication and makes working with the whole simpler. + +**This is not set in stone, and from year to year, the requirements for robot operation may change. Use your own discretion to see what sense of organization works best for your robot!** From 6176569adb00e0eebb8a4ae1ba4f5507ba8778a3 Mon Sep 17 00:00:00 2001 From: Yxhej Date: Mon, 23 Sep 2024 11:55:50 -0400 Subject: [PATCH 18/21] almost done w systems checks and unit tests --- projects/BasicArmBot.md | 75 +++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 0d92101..87a621b 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -453,18 +453,17 @@ Before doing anything, think about what we want to test about the arm. We want i - if the arm's position ultimately reaches its goal - if the arm's velocity reaches its velocity setpoints -We first define our test factory; call it anything, but ensure it is distinguished as a `Test`: +We first define our test factory; call it anything, but ensure it is distinguished as a `Test`, and takes in certain angles to move to (you'll see why when we run it as a unit test): ```java - public Test moveToTest() {} + public Test moveToTest(Measure angle) {} ``` Tests comprise a test command and related assertions (things that should / should not be true). Otherwise, there would be nothing to test! -Since we want to test movement, let's make the test command move to a certain position: +Since we want to test movement, let's make the test command move to the given position: ```java - Measure angle = Degrees.of(45); Command testCommand = moveTo(angle); ``` @@ -492,11 +491,60 @@ And finally, let's return the created test: return new Test(testCommand, Set.of(velocityCheck, atPositionCheck)); ``` +#### As a unit test + +As you know, all unit tests are made in their own separate control files. Create a file `ArmTest.java` in the nested robot folder of the test directory. For reminder, read the [Unit Test]() section of the docs. + +Like in the differential drive project, create the simulated version of the arm that we will use to access the hardware simulation and `moveToTest()` command. Be sure to include the annotated setup() and destroy() methods that run before and after each test run. + +```java + @BeforeEach + public void setup() { + setupTests(); + arm = Arm.create(); + } + + @AfterEach + public void destroy() throws Exception { + reset(arm); + } + ``` + +Now, we can run our test with `runUnitTest(Test test)` in `TestingUtils.java`, like below: + +```java + public void armSystemCheck(double theta) { + runUnitTest(arm.moveToTest(Degrees.of(theta))); + } +``` + +You may notice that we're not giving it any values. That's because JUnit has `ParameterizedTests`, which take in a source of one or more values that get passed into the test, and run separately. See below: + +```java + @ParameterizedTest + @ValueSource(doubles = {-40, -30, 0, 30, 40, 60}) + public void armSystemCheck(double theta) { + runUnitTest(arm.moveToTest(Degrees.of(theta))); + } +``` + +This will run the `armSystemCheck` once, separately, for each of the given values, destroying when completed as it goes down the line. This is useful to test for inconsistency in movement, especially with wide range of motion. + +#### As a systems check + +This is achieved in much the same way as in the Differential Drive project: binding a command that runs the test to the `test()` trigger in `Robot.java`. Functionally, this means that when Test Mode is activated on DriverStation, it will run the binded command once. + +Bind your systems check to the `test()` trigger with any reasonable value: + +```java + +``` + ### Tuning your simulated arm -You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's goal is in a different quadrant. +You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's goal is in a different quadrant. This will also cause your unit tests and systems checks to fail, as the assertions do not match the simukation. -This is the result of those constants being poorly tuned for that range of motion; it'll be up to you to tune the closed-loop control system. +This is the result of those constants being poorly tuned for your system; it'll be up to you to tune the closed-loop control system. Current sim behavior[^2] dictates that values changed in code will only be seen in simulation after a restart, which is non-ideal and will take a while. @@ -538,18 +586,3 @@ If you're interested in coding a project similar to this one, we recommned our [ If you're feeling exceptionally confident, you can try creating a more advanced arm or shooting project. [comment]: # (add links to projects above when created) - - public Command systemsCheck() { - return Test.toCommand( - shooter.goToTest(RadiansPerSecond.of(100)), - Test.fromCommand( - intake - .intake() - .asProxy() - .deadlineWith(feeder.forward(), shooter.runShooter(100)) - .withTimeout(1)), - pivot.goToTest(Radians.of(0)), - pivot.goToTest(STARTING_ANGLE), - drive.systemsCheck()) - .withName("Test Mechanisms"); - } \ No newline at end of file From 143b39e0a02183a5fca82b379c8066f2f3122af5 Mon Sep 17 00:00:00 2001 From: Yxhej Date: Mon, 23 Sep 2024 15:35:49 -0400 Subject: [PATCH 19/21] add trigger section & finish tests --- projects/BasicArmBot.md | 14 ++++++++++++-- reference-sheets/Command-based.md | 13 +++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 87a621b..1e4ad6f 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -528,18 +528,28 @@ You may notice that we're not giving it any values. That's because JUnit has `Pa } ``` -This will run the `armSystemCheck` once, separately, for each of the given values, destroying when completed as it goes down the line. This is useful to test for inconsistency in movement, especially with wide range of motion. +This will run `armSystemCheck` once, separately, for each of the given values, destroying when completed as it goes down the line. This is useful to test for inconsistency in movement, especially with wide range of motion. + +To run your unit tests, you can use the WPILib command palette and run the "Test Robot Code" command. #### As a systems check -This is achieved in much the same way as in the Differential Drive project: binding a command that runs the test to the `test()` trigger in `Robot.java`. Functionally, this means that when Test Mode is activated on DriverStation, it will run the binded command once. +This is achieved in much the same way as in the Differential Drive project: binding a command that runs the systems checks to the `test()` trigger in `Robot.java`. Use `onTrue(Command command)`; functionally, this means that when Test Mode is activated on DriverStation, it will run the binded command once. Bind your systems check to the `test()` trigger with any reasonable value: ```java + test().onTrue(systemsCheck); + public Command systemsCheck() { + return Test.toCommand(drive.systemsCheck(), arm.moveToTest(Degrees.of(100))).withName("Test Mechanisms"); + } ``` +We create a central `systemsCheck` command to check all of our mechanisms one after the other with the `Test.toCommand(Commands...)` method, which centralizes all of the logic to be more accessible. + +And boom! When you simulate test mode, your arm should be commanded to a position of 100 degrees and receive a report on whether that goal was reached or not. Speaking of... + ### Tuning your simulated arm You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's goal is in a different quadrant. This will also cause your unit tests and systems checks to fail, as the assertions do not match the simukation. diff --git a/reference-sheets/Command-based.md b/reference-sheets/Command-based.md index 51320a7..a78f30a 100644 --- a/reference-sheets/Command-based.md +++ b/reference-sheets/Command-based.md @@ -54,6 +54,19 @@ Individual commands and command groups each have their own singular / group of d Commands can also be accessed through [WPILib's `Commands`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Commands.html) class. +## Triggers + +A big part of the command-based ecosystem are triggers. Users can bind commands and `Runnable` actions to triggers, which are run in specified ways when the trigger is activated. Common operations with trigger commands include... + +- `onTrue()`, run once on trigger activation +- `whileTrue()`, run periodically while trigger is active + +...and more. + +For instance, the `teleop()` trigger in `Robot.java` (and its sisters) run binded commands when teleop mode is activated on the robot (by DriverStation or FMS). + +See [here for more specifics on usage in WPILib](https://docs.wpilib.org/en/stable/docs/software/commandbased/binding-commands-to-triggers.html). + ## Specifics & Common Issues ### Singular Command Instances From 1904ab2f6da2172facc97b86a92be32f208d30cc Mon Sep 17 00:00:00 2001 From: Yxhej Date: Tue, 24 Sep 2024 15:35:02 -0400 Subject: [PATCH 20/21] change hardware to cansparkmax --- projects/BasicArmBot.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 1e4ad6f..77de82e 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -49,7 +49,7 @@ We create our hardware components... ```java public class RealArm implements ArmIO { - private final PWMSparkFlex motor = new PWMSparkFlex(0); + private final CANSparkMAX motor = new CANSparkMAX(0, MotorType.kBrushless); private final DutyCycleEncoder encoder = new DutyCycleEncoder(1); public RealArm() { @@ -93,8 +93,6 @@ Digging deep into the innards, you'll notice that the constructors are BIG. double startingAngleRads) ``` -[comment]: # (might just include the ArmConstants file in the template) - They need a plethora of physical information to be able to accurately simulate your system, and we obviously don't have a real robot. All this information will also require a lot of organization. For your convenience, you may copy-paste these relevant predetermined constants into `ArmConstants`: From 66fd9d28802e3565378ac8f7fb443b6af8ea22d0 Mon Sep 17 00:00:00 2001 From: yxhej Date: Tue, 15 Oct 2024 00:10:36 -0400 Subject: [PATCH 21/21] move sim, fix dead links, style fixes in command doc --- projects/BasicArmBot.md | 10 ++++++---- reference-sheets/Command-based.md | 20 ++++++++++---------- {archive => reference-sheets}/Simulation.md | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) rename {archive => reference-sheets}/Simulation.md (97%) diff --git a/projects/BasicArmBot.md b/projects/BasicArmBot.md index 77de82e..ae8fb7c 100644 --- a/projects/BasicArmBot.md +++ b/projects/BasicArmBot.md @@ -6,7 +6,9 @@ In this tutorial, you will write code for a robot with a single-jointed arm and simulate its movement. The desired robot will have a single arm joint with an elevated axis of rotation, equipped with a static (non-pivoting) intake mechanism at the end of the arm. -You are expected to have completed some level of the [Differential Drive projects](/DifferentialDriveBasic.md), as this project will build upon that codebase. You should also be familiar with the concepts and usage of interfaces, inheritance, and the command-based paradigm. +You are expected to have completed some level of the Differential Drive projects, as this project will build upon that codebase. You should also be familiar with the concepts and usage of interfaces, inheritance, and the command-based paradigm. + +[comment]: # (add diff drive link when completed) All code examples are subject to change as a result of future library updates and improvements. @@ -23,7 +25,7 @@ Before you move on, create an `Arm.java` subsystem and an associated `ArmConstan Before we start, we'll be abstracting the hardware components to streamline our code. -Moving hardware like motors and encoders to class implementations of an interface decouples them from subsystem code, enabling modularity that supports simulation and flexibility in using different hardware or none at all! For a deeper dive, you should look at our [hardware abstraction datasheet](/HardwareAbstraction.md). +Moving hardware like motors and encoders to class implementations of an interface decouples them from subsystem code, enabling modularity that supports simulation and flexibility in using different hardware or none at all! For a deeper dive, you should look at our [hardware abstraction datasheet](/archive/HardwareAbstraction.md). Begin by creating your first IO interface in its Java file. This will act as an abstraction for a real and simulated set of hardware, so only include method headers that you think they will share. @@ -329,7 +331,7 @@ Here's your final challenge! Turn your basic drivetrain subsystem to a subsystem Now that you've completed all of your subsystems and mechanisms, it's time for the fun part. Piecing it all together! -For starters, get set up and get the gist of what's happening in [our simulation docs](/Simulation.md). You'll be using a `Mechanism2d` to simulate your arm, like in the blocky arm you saw at the beginning of the tutorial. +For starters, get set up and get the gist of what's happening in [our simulation docs](/reference-sheets/Simulation.md). You'll be using a `Mechanism2d` to simulate your arm, like in the blocky arm you saw at the beginning of the tutorial. We'll only be simulating the arm; the claw won't be that useful. @@ -550,7 +552,7 @@ And boom! When you simulate test mode, your arm should be commanded to a positio ### Tuning your simulated arm -You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's goal is in a different quadrant. This will also cause your unit tests and systems checks to fail, as the assertions do not match the simukation. +You might have noticed that your arm does not consistently reach its setpoint, especially if the arm's goal is in a different quadrant. This will also cause your unit tests and systems checks to fail, as the assertions do not match the simulation. This is the result of those constants being poorly tuned for your system; it'll be up to you to tune the closed-loop control system. diff --git a/reference-sheets/Command-based.md b/reference-sheets/Command-based.md index a78f30a..a7f8d1c 100644 --- a/reference-sheets/Command-based.md +++ b/reference-sheets/Command-based.md @@ -6,23 +6,23 @@ Subsystems represent independent parts of the robot and their hardware, which wo Commands safely direct those subsystems to perform actions in a wide suite of ways. They can be chained together in parallel or made sequential to one another, among other handy functions. -While these are generally their definitions, there's a fair bit of nuance to both. +These are general definitions; there's actually a fair bit of nuance to both. ## [Subsystems](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html) -For our purposes, all subsystems extend WPILib's `SubsystemBase`, providing command safety functionality, its [inherited periodic methods](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#periodic), and [default commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#default-commands). See in greater detail on [the official WPILib docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html). +For our purposes, all subsystems extend WPILib's `SubsystemBase`. It provides command safety functionality, its [inherited periodic methods](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#periodic), and [default commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html#default-commands). See in greater detail on [the official WPILib docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html). -Advanced readers may want to know [more about what a subsystem is](#a-word-on-what-makes-a-subsystem-a-subsystem). +Advanced readers may want to know [more about specifics that define subsystems](#a-word-on-what-makes-a-subsystem-a-subsystem). ## [Commands](https://docs.wpilib.org/en/stable/docs/software/commandbased/commands.html) -Commands represent robot actions with the hardware. They should be used for actions that may interfere with how the robot is run (i.e actual robot actions or changing PID constants) or cause dangerous movement. +Commands represent robot actions with the hardware. They should be used for actions that may interfere with how the robot is run (i.e actual robot movement or changing PID constants.) Every individual command has a defined structure, with methods that are called when it begins, throughout its runtime, and when it ends (as a result of a set end condition OR by interruption). Check the [official docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/commands.html) for specfics. A nice list of these individual commands can be found under the subclasses of WPILib's [Command class](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html). -We avoid using specific control commands like the `PIDCommand` or `SwerveControllerCommand` as they limit our precision and capabilities compared to using their components individually. +_Note: we avoid using specific control commands like `PIDCommand` or `SwerveControllerCommand` as they limit our precision and capabilities compared to using their components individually._ ### Command Compositions @@ -56,22 +56,22 @@ Commands can also be accessed through [WPILib's `Commands`](https://github.wpili ## Triggers -A big part of the command-based ecosystem are triggers. Users can bind commands and `Runnable` actions to triggers, which are run in specified ways when the trigger is activated. Common operations with trigger commands include... +A big part of the command-based ecosystem are triggers. Users can bind commands and `Runnable` actions to triggers, which are run in specified ways when the trigger is activated. + +Common operations with trigger commands include, but are not limited to: - `onTrue()`, run once on trigger activation - `whileTrue()`, run periodically while trigger is active -...and more. - For instance, the `teleop()` trigger in `Robot.java` (and its sisters) run binded commands when teleop mode is activated on the robot (by DriverStation or FMS). -See [here for more specifics on usage in WPILib](https://docs.wpilib.org/en/stable/docs/software/commandbased/binding-commands-to-triggers.html). +See [here for examples and specific usage in WPILib](https://docs.wpilib.org/en/stable/docs/software/commandbased/binding-commands-to-triggers.html). ## Specifics & Common Issues ### Singular Command Instances -Each instance of a command can only be scheduled once at a time, otherwise risking unpredictable behavior. To remedy this, we create command factories that return new instances of a command each time it is called, like below: +Each instance of a command can only be scheduled once, otherwise risking unpredictable behavior. To remedy this, we create command factories that return new instances of a command each time it is called, like below: ```java public Command updateSetpoint(double velocity) { diff --git a/archive/Simulation.md b/reference-sheets/Simulation.md similarity index 97% rename from archive/Simulation.md rename to reference-sheets/Simulation.md index c58fae5..fb6a4be 100644 --- a/archive/Simulation.md +++ b/reference-sheets/Simulation.md @@ -15,7 +15,7 @@ There are a few different facets of simulation to take note of before you can st In testing, it is common to want to directly observe robot measurements and values in order to tune your systems or debug. This is also incredibly important when working with simulation, as you otherwise have no reference to what is going on without a physical robot. -For more details, visit [our doc](/Telemetry.md). +For more details, visit [our doc](/reference-sheets/Telemetry.md). ## Simulation Classes