-
Notifications
You must be signed in to change notification settings - Fork 22
Flywheel example #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Flywheel example #19
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # Flywheel Example | ||
| Flywheels are commonly used for launching game elements, | ||
| They are typically controlled using a velocity PID feedforward controller. | ||
|
|
||
| With NextControl, that would be implemented like this (using hypothetical constants): | ||
|
|
||
| ::: tabs key:code | ||
|
|
||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| controlSystem { | ||
| velPid(0.005, 0.0, 0.0) | ||
| basicFF(0.01, 0.02, 0.03) | ||
| } | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| ControlSystem.builder() | ||
| .velPid(0.005, 0.0, 0.0) | ||
| .basicFF(0.01, 0.02, 0.03) | ||
| .build(); | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| ## What can I do with this? | ||
| If you're using a command-based framework like NextFTC, | ||
| you can create commands that spin the flywheel up or shut it down. | ||
|
|
||
| An example of a Flywheel subsystem using NextControl can be found | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The example on this page should be without NextFTC, as NextControl does not depend on NextFTC. See the slides page. |
||
| [here](../../guide/subsystems/flywheel.md). | ||
|
|
||
| ::: tabs key:code | ||
|
|
||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| object Flywheel : Subsystem { | ||
| private val motor = MotorEx("flywheel_motor") | ||
|
|
||
| private val controller: ControlSystem = controlSystem { | ||
| velPid(0.005, 0.0, 0.0) | ||
| basicFF(0.01, 0.02, 0.03) | ||
| } | ||
|
|
||
| val off: Command = RunToVelocity(controller, 0.0).named("FlywheelOff") | ||
| val on: Command = RunToVelocity(controller, 500.0).named("FlywheelOn") | ||
|
|
||
| override fun periodic() { | ||
| motor.power = controller.calculate(motor.state) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| public class Flywheel implements Subsystem { | ||
| public static final Flywheel INSTANCE = new Flywheel(); | ||
| private Flywheel() { } | ||
|
|
||
| private final MotorEx motor = new MotorEx("flywheel_motor"); | ||
|
|
||
| private final ControlSystem controller = ControlSystem.builder() | ||
| .velPid(0.005, 0, 0) | ||
| .basicFF(0.01, 0.02, 0.03) | ||
| .build(); | ||
|
|
||
| public final Command off = new RunToVelocity(controller, 0.0) | ||
| .requires(this) | ||
| .named("FlywheelOff"); | ||
|
|
||
| public final Command on = new RunToVelocity(controller, 500.0) | ||
| .requires(this) | ||
| .named("FlywheelOn"); | ||
|
|
||
| @Override | ||
| public void periodic() { | ||
| motor.setPower(controller.calculate(motor.getState())); | ||
| } | ||
| } | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,231 @@ | ||
| # Flywheel Subsystem | ||
|
|
||
| A subsystem that is found in almost all FTC robots in most seasons is a flywheel, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most seasons is not true. (Although it is true of every season you've competed in.) |
||
| here will learn how to program your own flywheel subsystem. | ||
|
|
||
| > [!TIP] | ||
| > This subsystem can be generalized to any subsystem with a motor. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be "to any subsystem with a motor whose velocity needs to be controlled," or something along those lines. |
||
|
|
||
| ## Step 1: Create your subsystem | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Capitalize "subsystem." |
||
|
|
||
| The first step to creating your subsystem is setting up the structure for it. | ||
| There should only be one instance of each | ||
| subsystem. To do this, we will make our subsystem | ||
| a [singleton](https://www.geeksforgeeks.org/singleton-design-pattern/). | ||
|
|
||
| :::tabs key:codes | ||
|
|
||
| == Kotlin | ||
|
|
||
| Creating our subsystem with the `object` keyword makes it a singleton: | ||
|
|
||
| ```kotlin | ||
| object Flywheel : Subsystem { | ||
|
|
||
| } | ||
| ``` | ||
|
|
||
| == Java | ||
| In Java, there are a couple lines of boilerplate you will need to add to the top | ||
| of every subsystem you create to | ||
| make it a singleton: | ||
|
|
||
| ```java | ||
| public class Flywheel implements Subsystem { | ||
| public static final Flywheel INSTANCE = new Flywheel(); | ||
| private Flywheel() { } | ||
|
|
||
| } | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| ## Step 2: Create your motor | ||
|
|
||
| Next, we need to set up a motor to power our flywheel. Let's start by creating a | ||
| variable to store our motor. It should be | ||
| of type `MotorEx`. | ||
|
|
||
| :::tabs key:code | ||
|
|
||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| private val motor = MotorEx("flywheel_motor") | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| private MotorEx motor = new MotorEx("flywheel_motor"); | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| We also need a `ControlSystem`, since we want to move our motor. We'll be | ||
| using [NextControl](/control), NextFTC's | ||
| solution to control in FTC. NextControl makes it very easy to create more | ||
| complex controllers if you ever need them. | ||
| Let's use a PID controller with a basic feedforward. | ||
|
|
||
| :::tabs key:code | ||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| private val controller = controlSystem { | ||
| velPid(0.005, 0.0, 0.0) | ||
| basicFF(0.0, 0.0, 0.0) | ||
| } | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| private ControlSystem controlSystem = ControlSystem.builder() | ||
| .velPid(0.005, 0.0, 0.0) | ||
| .basicFF(0.0, 0.0, 0.0) | ||
| .build(); | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| > [!IMPORTANT] | ||
| > Don't forget to tune your controller! | ||
|
|
||
| Now, we must set our motor power to the `ControlSystem`'s output every loop. We | ||
| can run code every loop by overriding the `periodic()` function. | ||
|
|
||
| The reason we set the motor power in `periodic()` instead of in our commands is | ||
| that the `ControlSystem` needs to be updated every loop to work properly. If we | ||
| only set the motor power in our commands, the motor power would only be updated | ||
| when a command is running, meaning once a command finishes, the motor power | ||
| would stop being updated and thus remain at whatever it was last set to, | ||
| pushing it past its target velocity. | ||
|
|
||
| :::tabs key:code | ||
|
|
||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| override fun periodic() { | ||
| motor.power = controlSystem.calculate(motor.state) | ||
| } | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| @Override | ||
| public void periodic() { | ||
| motor.setPower(controlSystem.calculate(motor.getState())); | ||
| } | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| That's all you need to do to create a motor in NextFTC. | ||
|
|
||
| We're not quite done, though. We still need to create our commands! | ||
|
|
||
| ## Step 3: Create commands | ||
|
|
||
| The last step when you create a Subsystem is to create the commands you'll be | ||
| using. This process varies with each | ||
| subsystem. Here, we'll create two commands. | ||
|
|
||
| To control our motor's velocity , we will be using the `RunToVelocity` command. | ||
|
|
||
| Let's create our first `RunToVelocity` command. | ||
|
|
||
| :::tabs key:code | ||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| val on: Command = RunToVelocity(controller, 500.0).named("FlywheelOn") | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| public final Command on = new RunToVelocity(controller, 500.0) | ||
| .requires(this) | ||
| .named("FlywheelOn"); | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| Pretty easy, right? Let's duplicate it and update our variable name and target | ||
| velocity to create our off command: | ||
|
|
||
| :::tabs key:code | ||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| val off: Command = RunToVelocity(controller, 0.0).named("FlywheelOff") | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| public final Command off = new RunToVelocity(controller, 0.0) | ||
| .requires(this) | ||
| .named("FlywheelOff"); | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| ## Final result | ||
|
|
||
| That's it! You've created your first subsystem! Here is the final result: | ||
|
|
||
| :::tabs key:code | ||
| == Kotlin | ||
|
|
||
| ```kotlin | ||
| object Flywheel : Subsystem { | ||
| private val motor = MotorEx("flywheel_motor") | ||
|
|
||
| private val controller: ControlSystem = controlSystem { | ||
| velPid(0.005, 0.0, 0.0) | ||
| basicFF(0.01, 0.02, 0.03) | ||
| } | ||
|
|
||
| val off: Command = RunToVelocity(controller, 0.0).named("FlywheelOff") | ||
| val on: Command = RunToVelocity(controller, 500.0).named("FlywheelOn") | ||
|
|
||
| override fun periodic() { | ||
| motor.power = controller.calculate(motor.state) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| == Java | ||
|
|
||
| ```java | ||
| public class Flywheel implements Subsystem { | ||
| public static final Flywheel INSTANCE = new Flywheel(); | ||
| private Flywheel() { } | ||
|
|
||
| private final MotorEx motor = new MotorEx("flywheel_motor"); | ||
|
|
||
| private final ControlSystem controller = ControlSystem.builder() | ||
| .velPid(0.005, 0, 0) | ||
| .basicFF(0.01, 0.02, 0.03) | ||
| .build(); | ||
|
|
||
| public final Command off = new RunToVelocity(controller, 0.0) | ||
| .requires(this) | ||
| .named("FlywheelOff"); | ||
|
|
||
| public final Command on = new RunToVelocity(controller, 500.0) | ||
| .requires(this) | ||
| .named("FlywheelOn"); | ||
|
|
||
| @Override | ||
| public void periodic() { | ||
| motor.setPower(controller.calculate(motor.getState())); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ::: | ||
|
Comment on lines
+1
to
+231
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general on this page, it seems to be copy-pasted from the lift page. Since users should have already read that page, it is mostly duplicate information. Instead, in places say "as we did in the lift," or something similar (or at least don't make it the exact same text verbatim, that's boring.) |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know these are the numbers used in NextFTC/Examples, but in this case,
kAinbasicFFis actually unused, so it should be replaced with 0.