Class Launcher

java.lang.Object
com.crossbotics.ftchelper.robot.Module<Launcher>
com.crossbotics.ftchelper.robot.Launcher
All Implemented Interfaces:
Subsystem, Toggleable

public final class Launcher extends Module<Launcher> implements Toggleable
A flywheel launcher: a motor that spins a wheel to a speed, and an Indexer that feeds one piece at a time once the speed is right.

 launcher = add(Launcher.flywheel("launcher", "gobilda-5203-6000").feeder(Indexer.servo("indexer")));
 launcher = add(Launcher.flywheel("launcher", "gobilda-5203-6000").feeder(Indexer.servo("indexer"))
         .speedFromDistance(camera));                       // speed from the camera's distance

 public static class Launcher {                             // Tunables.java
     public static double speedRpm = 2600, stepRpm = 100, readyBandRpm = 60, idleRpm = 0;
     public static double[][] speedByDistance = {{24, 2200}, {48, 2550}, {72, 2900}};   // inches, rpm
     public static double kP = 1.17, kI = 0.117, kD = 0, kF = 11.70, nominalVolts = 12.6;   // Flywheel Finder
     public static double trimStepPercent = 1, trimMaxPercent = 15;   // optional: the speed trim
 }
 

First run (no kF, no speedRpm written): the hub's gains start from the parts catalog for the motor (Flywheel.startingPidf(double): kF 11.7, kP 1.17, kI 0.117 for a goBILDA 6000 RPM motor) and the speed at 60% of its top speed (3600). INIT says so once; the Flywheel Finder and the Shot Tuner find the real ones. A written number always wins.

Speed trim (a fix during a match, when every shot is a little short or long: a worn wheel, different balls): trimBy(double) makes every shot speed (the table's or the fixed one) that many percent faster or slower, kept to +/- trimMaxPercent (15) and to the motor's top speed (then the Driver Station says "(motor limit)"). It starts at 0 at every INIT and is the same for every table. The Driver Station shows Speed trim: +3% READY as the launcher's first line. The trim used last time is remembered (until the robot app restarts) and offered at INIT: "Trim last time: +3% (double-tap D-pad up after START to use it)"; it is never applied by itself. The Shot Tuner never uses it (tables are recorded clean). If you trim the same way match after match, the table is off: tune it again (or fold the trim into Tunables.java) instead.

Battery compensation (OFF by default; batteryCompensation = true with kF turns it on — not yet proven on a real robot): the hub's F gain is scaled by nominalVolts / the battery now, so a low battery gets the wheel back to speed after a shot as fast as a full one (see Flywheel.batteryCompensation(java.util.function.DoubleSupplier, double)).

fire() spins up if needed and feeds one piece as soon as the wheel is at speed (it gives up after 3 s and says why). The driver can step the speed up and down (adjust(int)). With speedFromDistance(com.crossbotics.ftchelper.robot.Camera), the speed comes from speedByDistance (blended between rows) while the goal is seen; when the camera does not see it, from the odometry's distance (with an odometry(...) line and Tunables.Aim.goalX, goalY, see Aim); with neither it keeps the last speed. speedFromOdometry(): the odometry's distance only (no camera).

Several shot tables, one per game piece (they fly differently): every speedByDistance... table in the section is one, named by the rest of its name.


 public static double[][] speedByDistancePollen = {{24, 2100}, {48, 2450}, {72, 2800}};   // "pollen"
 public static double[][] speedByDistanceNectar = {{24, 2400}, {48, 2800}, {72, 3200}};   // "nectar"
 
Switch with useTable(String) (a button, an action, an autonomous), nextTable(), or let a sensor choose: tableFrom(java.util.function.Supplier). It starts on the plain speedByDistance if there is one, else the first by name, or the one given to startTable(String). The Driver Station shows the table in use.

A hood (hood(Hood)): the shot table's rows get a third number, the hood angle ({48, 2550, 12}), blended between rows like the speed; the launcher waits for the hood to arrive before it feeds. Two-number rows still work (then the hood is set by hand). See Hood.

Suggested buttons (operator): LB spins up / down, RB fires one (hold it: keeps firing). D-pad up/down: with the speed from the distance, the speed trim +/- trimStepPercent (1%); with a fixed speed, +/- stepRpm; held, one more step every 0.25 s. Double-tap D-pad down: back to no trim. Double-tap D-pad up: use last time's trim (only when there is one). D-pad right picks the next shot table (with two or more). Each only when that button is free. Docs: docs/modules/robot.md (section "Launcher"), docs/modules/launcher.md

  • Method Summary

    Modifier and Type
    Method
    Description
    void
    The part's telemetry line; each module adds its own.
    void
    adjust(int steps)
    Changes the speed by steps x stepRpm (e.g.
    boolean
    True when the trimmed shot speed is cut to the motor's top speed (more trim up does nothing).
    void
    The button is let go: no more shots.
    double
    The distance to the goal last used for the speed (inches), NaN if none yet.
    Where that distance came from last loop: CAMERA, ODOMETRY or NONE (keeps the last speed).
    feeder(Indexer feeder)
    The indexer that feeds it.
    void
    Fires one piece (spins up first if needed; feeds as soon as it is at speed).
    void
    fire(int count)
    Fires count pieces, one after another.
    void
    For a held button, every loop: fires one piece, then the next as soon as that one is fed, for as long as the button is held (the launcher keeps its time between shots).
    static Launcher
    flywheel(String motor, String motorId)
    A launcher with its flywheel on this motor.
    The hood given with hood(Hood), or null.
    hood(Hood hood)
    The hood that sets how steep the shot leaves (Hood): its angle comes from the shot table's third column ({inches, rpm, hood}), and the launcher waits for it before feeding.
    void
    Back to the idle speed (idleRpm, 0 = coast to a stop); cancels shots still waiting.
    boolean
    True when the wheel is at speed (and every other check passes).
    boolean
    True while shots are waiting or a piece is being fed.
    boolean
    True while it is spun up (or spinning up), false when idle.
    Last time's trim in words ("+3%"), while it is offered and not used yet; "" when there is none.
    void
    The next shot table (after the last, the first again), for a button.
    The launcher.Launcher inside, for anything this module does not offer.
    Another check before each piece is fed, e.g.
    void
    Back to the Tunables speed: the trim to 0, the adjust(int) steps to 0, and the hood trim to 0.
    The flywheel turns the other way.
    void
    setTrim(double percent)
    Sets the speed trim to this many percent (0 = none), kept to +/- trimMaxPercent.
    shotAngle(double degrees)
    Which way a piece leaves the wheel, for the simulator: degrees up from the wheel shape's forward, seen from the side (45 = forward and up, 90 = straight up, 135 = backward and up).
    Sets the speed from this camera's distance to the goal (Tunables...speedByDistance).
    Sets the speed from the odometry's distance to the goal, with NO camera: where the robot is (robot.pose()) and Tunables.Aim.goalX, goalY (see Aim).
    Speed from the distance to the goal: on from START when speedFromDistance(com.crossbotics.ftchelper.robot.Camera) or speedFromOdometry() is used.
    double
    Tunables.Launcher.speedRpm: the speed with no shot table (RPM).
    Spins up at START (in autonomous and TeleOp), so it is at speed when the first shot is wanted.
    void
    Spins up to the shot speed and keeps it there.
    The shot table to start with, by name ("pollen" for speedByDistancePollen).
    void
    Stops all motion.
    table(String name)
    A shot table by name, as Tunables.java has it ("pollen", "main"; case does not matter), or null if there is no such table.
    The Tunables field a table came from ("speedByDistancePollen"), or null if there is no such table.
    tableFor(String table, String... kinds)
    Says which shot table some game-piece kinds use, for tableFrom(java.util.function.Supplier<java.lang.String>): e.g.
    Picks the shot table from your own code every loop, e.g.
    The shot table in use ("pollen", "main"), or null if there is none.
    Every shot table's name, in order ("main" first, then by name).
    double
    The speed it is aiming for now, RPM.
    void
    Spins up if idle, idles if spinning.
    double
    The speed trim now, percent (0 = none; +3 = 3% faster).
    void
    trimBy(double change)
    Trims the shot speed by this many percent more: trimBy(+2) = every shot 2% faster than the table (or the fixed speed) says, trimBy(-1) = 1% slower.
    void
    Called once every loop.
    boolean
    Uses the trim from last time (an earlier OpMode since the robot app started), if there is one: the Driver Station offers it at INIT ("Trim last time: +3%"); double-tap D-pad up uses it.
    void
    Uses this shot table from now on, e.g.

    Methods inherited from class com.crossbotics.ftchelper.robot.Module

    deviceName, isOptional, isPresent, looks, name, optional, part, tunables, zone, zoneAbove, zoneBelow

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • flywheel

      public static Launcher flywheel(String motor, String motorId)
      A launcher with its flywheel on this motor.
      Parameters:
      motor - the motor's configuration name
      motorId - the motor's parts-catalog id (see Motors), e.g. "gobilda-5203-6000"
    • hood

      public Launcher hood(Hood hood)
      The hood that sets how steep the shot leaves (Hood): its angle comes from the shot table's third column ({inches, rpm, hood}), and the launcher waits for it before feeding.
      Parameters:
      hood - e.g. Hood.servo("hood")
    • hood

      public Hood hood()
      The hood given with hood(Hood), or null.
    • feeder

      public Launcher feeder(Indexer feeder)
      The indexer that feeds it. Without one, fire() only spins up.
    • shotAngle

      public Launcher shotAngle(double degrees)
      Which way a piece leaves the wheel, for the simulator: degrees up from the wheel shape's forward, seen from the side (45 = forward and up, 90 = straight up, 135 = backward and up). For a wheel mounted level on the robot that is the robot's forward; on a turned part (an arm, a tilted slide) the angle turns with it.
      Parameters:
      degrees - e.g. 147 for a shot up and out of the back
    • spinAtStart

      public Launcher spinAtStart()
      Spins up at START (in autonomous and TeleOp), so it is at speed when the first shot is wanted. LB (or idle()) still lets it coast down.
    • reversed

      public Launcher reversed()
      The flywheel turns the other way.
    • requireReady

      public Launcher requireReady(String what, BooleanSupplier check)
      Another check before each piece is fed, e.g. .requireReady("turret aimed", () -> turret.isAimed()). While it is false the launcher waits (and gives up after 3 s, saying which check failed).
      Parameters:
      what - a few words for the Driver Station
      check - true when ready
    • speedFromDistance

      public Launcher speedFromDistance(Camera camera)
      Sets the speed from this camera's distance to the goal (Tunables...speedByDistance). With an odometry(...) line and Tunables.Aim.goalX, goalY, the odometry's distance is used while the camera does not see the goal.
    • speedFromOdometry

      public Launcher speedFromOdometry()
      Sets the speed from the odometry's distance to the goal, with NO camera: where the robot is (robot.pose()) and Tunables.Aim.goalX, goalY (see Aim). Needs an odometry(...) line.
    • startTable

      public Launcher startTable(String name)
      The shot table to start with, by name ("pollen" for speedByDistancePollen). Default: the plain speedByDistance if there is one, else the first by name.
    • tableFrom

      public Launcher tableFrom(Supplier<String> tableName)
      Picks the shot table from your own code every loop, e.g. a sensor that tells which game piece is loaded: .tableFrom(() -> robot.indexer.pieceName()). Each time its answer CHANGES to a table's name, that table is used (case does not matter); null or "" changes nothing. A button can still pick another table until the answer changes again. Call it in Robot.java (chained) or later.
      Parameters:
      tableName - gives a table's name ("pollen"), or null when it does not know
    • tableFor

      public Launcher tableFor(String table, String... kinds)
      Says which shot table some game-piece kinds use, for tableFrom(java.util.function.Supplier<java.lang.String>): e.g. with .tableFrom(pieces.nextKind()).tableFor("nectar", "RED_NECTAR", "BLUE_NECTAR") both NECTAR colours use speedByDistanceNectar. A kind with the same name as a table needs no line ("POLLEN" uses "pollen"). "NONE" and "UNKNOWN" (from Pieces) never change the table.
      Parameters:
      table - the table's name ("nectar")
      kinds - the kinds that use it ("RED_NECTAR", "BLUE_NECTAR")
    • spinUp

      public void spinUp()
      Spins up to the shot speed and keeps it there.
    • idle

      public void idle()
      Back to the idle speed (idleRpm, 0 = coast to a stop); cancels shots still waiting.
    • isSpinning

      public boolean isSpinning()
      True while it is spun up (or spinning up), false when idle.
    • toggle

      public void toggle()
      Spins up if idle, idles if spinning.
      Specified by:
      toggle in interface Toggleable
    • fire

      public void fire()
      Fires one piece (spins up first if needed; feeds as soon as it is at speed).
    • fire

      public void fire(int count)
      Fires count pieces, one after another.
    • fireWhileHeld

      public void fireWhileHeld()
      For a held button, every loop: fires one piece, then the next as soon as that one is fed, for as long as the button is held (the launcher keeps its time between shots). Pair it with ceaseFire() on release: g1.rb.hold(robot.launcher::fireWhileHeld, robot.launcher::ceaseFire).
    • ceaseFire

      public void ceaseFire()
      The button is let go: no more shots. The press's first shot still goes (a quick tap fires one, even while the wheel is still spinning up); a later shot still waiting is dropped.
    • adjust

      public void adjust(int steps)
      Changes the speed by steps x stepRpm (e.g. +1 or -1 from a D-pad). Kept until the program ends; the Tunables value is not changed.
    • trimBy

      public void trimBy(double change)
      Trims the shot speed by this many percent more: trimBy(+2) = every shot 2% faster than the table (or the fixed speed) says, trimBy(-1) = 1% slower. Adds up, kept to +/- Tunables.Launcher.trimMaxPercent (15). From a button, an action (.then(() -> r.launcher.trimBy(+2))) or code. The same name as the hood's trim (Hood.trimBy(double)), which is in the hood's angle instead of percent. Starts at 0 at every INIT; the Tunables numbers are not changed. A fix for this match: if you need it every match, tune the table again.
      Parameters:
      change - percent to add, e.g. +1 or -1
    • setTrim

      public void setTrim(double percent)
      Sets the speed trim to this many percent (0 = none), kept to +/- trimMaxPercent.
      Parameters:
      percent - e.g. 3 for 3% faster, -2 for 2% slower
    • resetTrim

      public void resetTrim()
      Back to the Tunables speed: the trim to 0, the adjust(int) steps to 0, and the hood trim to 0.
    • useLastTrim

      public boolean useLastTrim()
      Uses the trim from last time (an earlier OpMode since the robot app started), if there is one: the Driver Station offers it at INIT ("Trim last time: +3%"); double-tap D-pad up uses it. Never runs by itself.
      Returns:
      true if there was one to use
    • lastTrimOffered

      public String lastTrimOffered()
      Last time's trim in words ("+3%"), while it is offered and not used yet; "" when there is none.
    • atMotorLimit

      public boolean atMotorLimit()
      True when the trimmed shot speed is cut to the motor's top speed (more trim up does nothing).
    • trim

      public double trim()
      The speed trim now, percent (0 = none; +3 = 3% faster).
    • speedMode

      public Mode speedMode()
      Speed from the distance to the goal: on from START when speedFromDistance(com.crossbotics.ftchelper.robot.Camera) or speedFromOdometry() is used.
    • useTable

      public void useTable(String name)
      Uses this shot table from now on, e.g. robot.launcher.useTable("nectar") for speedByDistanceNectar (case does not matter). From a button, an action (.then(() -> r.launcher.useTable("nectar"))) or another part's code. A name with no table keeps the table in use and warns on the Driver Station (it never stops the robot in a match).
      Parameters:
      name - the table's name: the end of its Tunables name ("pollen"), or "main" for speedByDistance
    • nextTable

      public void nextTable()
      The next shot table (after the last, the first again), for a button.
    • tableName

      public String tableName()
      The shot table in use ("pollen", "main"), or null if there is none.
    • tableNames

      public List<String> tableNames()
      Every shot table's name, in order ("main" first, then by name). Empty before INIT or with none.
    • tableField

      public String tableField(String name)
      The Tunables field a table came from ("speedByDistancePollen"), or null if there is no such table.
    • table

      public ShotTable table(String name)
      A shot table by name, as Tunables.java has it ("pollen", "main"; case does not matter), or null if there is no such table. The Shot Tuner starts each table's speed from it.
    • speedRpm

      public double speedRpm()
      Tunables.Launcher.speedRpm: the speed with no shot table (RPM). NaN before INIT.
    • distanceIn

      public double distanceIn()
      The distance to the goal last used for the speed (inches), NaN if none yet.
    • distanceSource

      public Aim.Source distanceSource()
      Where that distance came from last loop: CAMERA, ODOMETRY or NONE (keeps the last speed).
    • isReady

      public boolean isReady()
      True when the wheel is at speed (and every other check passes).
    • isShooting

      public boolean isShooting()
      True while shots are waiting or a piece is being fed.
    • targetRpm

      public double targetRpm()
      The speed it is aiming for now, RPM.
    • part

      public Launcher part()
      The launcher.Launcher inside, for anything this module does not offer.
    • update

      public void update()
      Description copied from interface: Subsystem
      Called once every loop. Do the hardware work here. Must be quick: no sleep, no waiting. While update() waits, the whole robot is frozen (no driving, no limits, no STOP).
      Specified by:
      update in interface Subsystem
    • stop

      public void stop()
      Description copied from interface: Subsystem
      Stops all motion. Called at the end of every OpMode.
      Specified by:
      stop in interface Subsystem
    • addTelemetry

      public void addTelemetry(RobotTelemetry t)
      Description copied from class: Module
      The part's telemetry line; each module adds its own.
      Specified by:
      addTelemetry in interface Subsystem
      Overrides:
      addTelemetry in class Module<Launcher>