Lift Subsystem
A subsystem that is found on almost all FTC robots in most seasons is a lift, a type of linear slide. Here, you will learn how to program your own lift subsystem.
TIP
This subsystem can be generalized to any subsystem with a motor.
Step 1: Create your 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.
Creating our subsystem with the object
keyword makes it a singleton:
object Lift : Subsystem {
}
Step 2: Create your motor
Next, we need to set up a motor to power our lift. Let's start by creating a variable to store our motor. It should be of type MotorEx
.
private val motor = MotorEx("lift_motor")
We also need a ControlSystem
, since we want to move our motor. We'll be using NextControl, 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 gravity-compensating feedforward.
private val controller = controlSystem {
posPid(0.005, 0.0, 0.0)
elevatorFF(0.0)
}
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.
override fun periodic() {
motor.power = controlSystem.calculate(
KineticState(
motor.currentPosition,
motor.velocity
)
)
}
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 three commands that each move the lift to a different height: toLow
, toMiddle
, and toHigh
.
To control our motor's position, we will be using the RunToPosition
command.
Let's create our first RunToPosition
command.
val toLow = RunToPosition(controlSystem, 0.0).setSubsystems(this)
Note the setSubsystems(this)
. This is what tells NextFTC which commands should be allowed to run at the same time. If it weren't set, toLow
would be able to run at the same time as other commands that use the Lift
subsystem - so there would be multiple things fighting to set the motor's power. Generally, you need to pass this
as the subsystem, although there are exceptions with more complicated custom commands.
Pretty easy, right? Let's duplicate it and update our variable name and target position to create our other two commands:
val toMiddle = RunToPosition(controlSystem, 500.0).setSubsystems(this)
val toHigh = RunToPosition(controlSystem, 1200.0).setSubsystems(this)
Final result
That's it! You've created your first subsystem! Here is the final result:
object Lift : Subsystem {
private val motor = MotorEx("lift_motor")
private val controlSystem = controlSystem {
posPid(0.005, 0.0, 0.0)
elevatorFF(0.0)
}
val toLow = RunToPosition(controlSystem, 0.0).setSubsystems(this)
val toMiddle = RunToPosition(controlSystem, 500.0).setSubsystems(this)
val toHigh = RunToPosition(controlSystem, 1200.0).setSubsystems(this)
override fun periodic() {
motor.power = controlSystem.calculate(
KineticState(
motor.currentPosition,
motor.velocity
)
)
}
}