Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/control/examples/flywheels.md
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)
Copy link
Collaborator

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, kA in basicFF is actually unused, so it should be replaced with 0.

}
```

== 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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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()));
}
}
```
231 changes: 231 additions & 0 deletions src/guide/subsystems/flywheel.md
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,
Copy link
Collaborator

Choose a reason for hiding this comment

The 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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.)