We have a mechanism and behaviors. Now connect them to the robot without creating new commands or bindings on every periodic call.
Let the framework run the scheduler¶
Change your robot base class to commands2.TimedCommandRobot. This framework
class runs the command scheduler for you. Do not add a second scheduler call.
Construct one Intake in robotInit, then give that same object to Acquire,
Release, and the manual behavior. Keep the supplied simulationPeriodic support.
A supplier gives the latest value¶
ManualIntake receives read_request, a function it can call later. Passing
self.read_request without parentheses passes the callable. Passing
self.read_request() would call it immediately and pass a number instead.
This callable is often called a supplier because it supplies a current value.
During every execute, ManualIntake calls the supplier and sends its result to
the subsystem. Its end stops the output. It has no custom isFinished; the base
command’s default doesn’t finish on its own.
Assign it with intake.setDefaultCommand(...). A default command runs when
that subsystem is free. It is interrupted when another command needs the intake,
and can resume afterward. Our supplier returns zero outside enabled teleop so
an autonomous timeout cannot resume an old operator request.
Trigger on a changing condition¶
Trigger watches a boolean supplier. onTrue(command) schedules when the condition
changes from false to true. It does not schedule repeatedly just because a button
stays true. In our dashboard demonstration, set Acquire false before setting it
true again to create another press.
Construct and bind triggers once in robotInit. A physical controller button
can provide an equivalent boolean later. For now the dashboard keeps hardware
setup separate from learning scheduling.
import commands2
from commands2.button import Trigger
import wpilib
from examples.support.intake_io import IntakeSim
from intake_subsystem import Intake
from intake_commands import Acquire, Release, ManualIntake
class Robot(commands2.TimedCommandRobot):
def robotInit(self):
if wpilib.RobotBase.isReal():
raise RuntimeError("This course example is simulation-only")
self.io = IntakeSim()
self.intake = Intake(self.io)
self.acquire = Acquire(self.intake)
self.release = Release(self.intake)
self.intake.setDefaultCommand(ManualIntake(self.intake, self.read_request))
for name in ("Acquire", "Release"):
wpilib.SmartDashboard.putBoolean(name, False)
wpilib.SmartDashboard.putBoolean("Piece available", True)
wpilib.SmartDashboard.putNumber("Intake request", 0.0)
self.acquire_trigger = Trigger(self.acquire_pressed)
self.acquire_trigger.onTrue(self.acquire)
self.release_trigger = Trigger(self.release_pressed)
self.release_trigger.onTrue(self.release)
def read_request(self):
if self.isTeleopEnabled():
return wpilib.SmartDashboard.getNumber("Intake request", 0.0)
return 0.0
def acquire_pressed(self):
return self.isTeleopEnabled() and wpilib.SmartDashboard.getBoolean("Acquire", False)
def release_pressed(self):
return self.isTeleopEnabled() and wpilib.SmartDashboard.getBoolean("Release", False)
def autonomousInit(self):
self.acquire.schedule()
def teleopInit(self):
self.acquire.cancel()
self.release.cancel()
def disabledInit(self):
commands2.CommandScheduler.getInstance().cancelAll()
self.intake.stop()
def simulationPeriodic(self):
self.io.enabled = self.isEnabled()
self.io.piece_available = wpilib.SmartDashboard.getBoolean("Piece available", True)
self.io.step()
wpilib.SmartDashboard.putBoolean("Has piece", self.intake.has_piece())
wpilib.SmartDashboard.putNumber("Intake output", self.io.output)
wpilib.SmartDashboard.putBoolean("Acquire succeeded", self.acquire.succeeded)
Connect your version¶
Implement ManualIntake and the robot setup in your assignment. Use imports from
your own sibling intake_subsystem.py and intake_commands.py, as the example does.
The launcher puts the selected assignment on the import path. Run:
python tools/run_robot.py student-work/intake-subsystemUse zero manual request first. In enabled teleop, set Acquire true; the output
should collect then stop. After detecting a piece, set Release true; it should
reverse and stop after release. Reset each dashboard button to false before its
next use. Watch Acquire succeeded to distinguish sensor success from timeout.
Now hold a nonzero manual request and observe what happens after a command releases the intake. The default can resume the request; that is expected ownership behavior, not a reason to remove cleanup. Return all requests to zero before the next test.
Explain why self.read_request has no parentheses where it is passed to the
command, and why trigger bindings belong in initialization. Next, explore
how feedback chooses motor effort before the final project.