Class Aim

java.lang.Object
com.crossbotics.ftchelper.robot.Aim

public final class Aim extends Object
Where the goal is, for everything that aims: the camera first, the odometry as a backup. One per robot: robot.aim(). It also turns the WHOLE robot toward the goal while the driver drives (mode(), the same as robot.aimMode()), and tells how far off the aim is (for the gamepad buzz, g1.rumbleWhenAimed()).

 // Controls.java, inside buttons(...):
 g1.rb.toggle(robot.aimMode()).says("aim the robot at the goal on/off");   // whole-robot aim
 g1.rumbleWhenAimed();                                                   // buzz: the closer, the stronger

 public static class Aim {                                   // Tunables.java (every line optional)
     public static double goalX = 13, goalY = -13;           // the goal on the field (odometry backup)
     public static boolean mirrorForRed = false;             // the robot's one setting (BIOBUZZ: false)
     public static double turnKp = 0.02, turnKd = 0.002, maxTurnPower = 0.5, minTurnPower = 0, toleranceDeg = 2;
     public static double cameraOffset = 0, rumbleDeg = 4;
     public static double maxGoalDistance = 170, cameraOdometryAgree = 6;   // inches: odometry sanity checks
     public static boolean shootOnMove = false;              // shooting while driving: off until tuned
     public static double flightSec = 0.8, flightSecPerIn = 0, maxLeadIn = 24;
     public static double shooterForwardIn = 0, shooterLeftIn = 0;   // the shooter (turret pivot) from the centre
     public static double cameraForwardIn = 0, cameraLeftIn = 0;     // a camera on the body, from the centre
 }
 

Whole-robot aim and a following turret are separate modes. Use one: a robot with a turret that follows the goal (follows(camera) or followsOdometry()) aims with the turret; robot.aimMode() is for a robot with no turret (or a turret that does not follow). Both at once would chase the same error with two motors and swing back and forth, so robot.aimMode() stops at INIT with a message on such a robot.

Camera first, odometry as a backup. While the camera sees the goal (a picture newer than cameraMaxAgeMs, 150 ms), that is used. When it does not (the tag is blocked, out of view, a bad picture), and the robot has an odometry(...) line and Tunables.Aim.goalX, goalY, the goal's direction and distance are worked out from where the robot is (robot.pose()). The Driver Station says which one is in use.

The goal point is in the same numbers as driveTo and the autonomous start spots: seen from your drivers' wall, x inches to the right of the field's centre line, y inches from the centre toward the far wall, written for BLUE. For RED with mirrorForRed = true (the default, like autonomous) x is mirrored; a field turned half a turn between the alliances (BIOBUZZ) uses mirrorForRed = false: the same numbers from the red wall. It is the robot's one setting (RobotDefinition.mirrorForRed()): the autonomous and TeleOp's reset spot use it too. Put the point where the camera measures to (the goal's tags), so the two distances agree and one shot table works for both.

The odometry is only as good as its pose. It must start right: autonomous's start spot, carried into TeleOp. Without autonomous, TeleOp's pose would be (0, 0), the middle of the field, and the robot would aim at the wrong place, so the odometry backup stays OFF until the pose was set (autonomous ran in this match, or your code called robot.odometry().setPose(...)); the Driver Station says so. It also stays off while the odometry says the goal is farther than maxGoalDistance (170 in: more than the field allows, so the pose is wrong), and warns when the camera and the odometry disagree by more than cameraOdometryAgree (6 in). INIT shows the alliance and the mirrorForRed choice, so a wrong one is seen before the match.

Shooting on the move (shootOnMove = true, off by default: tune it on a real robot first). The goal every part gets (goal(), the turret's, the launcher's distance) is then corrected for the robot's motion: a camera picture is brought forward to now (the robot turned and moved since it was taken), and the aim leads the shot by the robot's speed x the flight time, because the piece keeps the robot's speed (see MovingShot). Needs odometry (the speed comes from it). It works for a turret that follows the goal (camera on the turret or on the body, or odometry only) and for whole-robot aim with a fixed shooter; the shooter and the camera need not be at the robot's centre (shooterForwardIn, shooterLeftIn, cameraForwardIn, cameraLeftIn). movingShotMode() switches it with a button. Docs: docs/modules/robot.md (section "Aim"). Tutorial: docs/tutorials/start/09-shooter-and-camera.md

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    Where the goal's direction and distance come from now.
  • Method Summary

    Modifier and Type
    Method
    Description
    When the camera and the odometry both see the goal and their distances differ by more than Tunables.Aim.cameraOdometryAgree (6 in): a warning (the pose drifted, or the goal point is not where the camera measures to).
    double
    How many degrees the aim is off now (any sign), NaN if unknown.
    The goal worked out from the odometry: its direction from the robot's front (degrees, counterclockwise + = to the LEFT, like a camera's bearing) and its distance from the robot's centre (inches).
    The goal now: what the camera sees (the first camera with a goal(...) in Robot.java), or, when it does not see it, the goal worked out from the odometry.
    boolean
    True if Tunables.Aim has goalX and goalY (the odometry backup can work).
    boolean
    True when aimed: whole-robot aim on and within toleranceDeg, or a following turret that is aimed AT THE GOAL NOW (see Turret.isAimed(): a goal seen this moment, homed, in range).
    boolean
    True while shooting on the move is on and the robot moves (faster than 2 in/s, or turning faster than 5 degrees per second): the aim is being led now.
    The lead worked out last (degrees and inches, see MovingShot.Result), or null if none in the last cameraMaxAgeMs (shooting on the move off, no goal, no odometry).
    Whole-robot aim, for a robot with no turret: while it is on, TeleOp turns the robot toward the goal and the driver keeps forward and sideways.
    Shooting on the move: while it is on, the goal every part gets is corrected for the robot's motion (the camera picture's age, and the lead for the flight time; see MovingShot).
    Why the odometry backup is off now, in a few words for the Driver Station; "" when it works (or the robot has no odometry or no goal point: then there is nothing to warn about).
    double
    The error that starts the buzz of rumbleWhenAimed() (Tunables.Aim.rumbleDeg, default 4).
    Where goal() comes from now: CAMERA, ODOMETRY or NONE.
    static Aim.Source
    CAMERA, ODOMETRY or NONE (null), for a target from goal() or fromOdometry().

    Methods inherited from class java.lang.Object

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

    • hasGoalPoint

      public boolean hasGoalPoint()
      True if Tunables.Aim has goalX and goalY (the odometry backup can work).
    • goal

      public TargetObservation goal()
      The goal now: what the camera sees (the first camera with a goal(...) in Robot.java), or, when it does not see it, the goal worked out from the odometry. Null if neither. Check for null.
    • source

      public Aim.Source source()
      Where goal() comes from now: CAMERA, ODOMETRY or NONE.
    • sourceOf

      public static Aim.Source sourceOf(TargetObservation o)
      CAMERA, ODOMETRY or NONE (null), for a target from goal() or fromOdometry().
    • fromOdometry

      public TargetObservation fromOdometry()
      The goal worked out from the odometry: its direction from the robot's front (degrees, counterclockwise + = to the LEFT, like a camera's bearing) and its distance from the robot's centre (inches). Null if the robot has no odometry, the odometry is not ready, Tunables.Aim has no goal point, the pose was never set (no autonomous, no setPose), or the goal would be farther than maxGoalDistance (a wrong pose). odometryProblem() says which.
    • odometryProblem

      public String odometryProblem()
      Why the odometry backup is off now, in a few words for the Driver Station; "" when it works (or the robot has no odometry or no goal point: then there is nothing to warn about).
    • disagreement

      public String disagreement()
      When the camera and the odometry both see the goal and their distances differ by more than Tunables.Aim.cameraOdometryAgree (6 in): a warning (the pose drifted, or the goal point is not where the camera measures to). "" otherwise.
    • movingShotMode

      public Mode movingShotMode()
      Shooting on the move: while it is on, the goal every part gets is corrected for the robot's motion (the camera picture's age, and the lead for the flight time; see MovingShot). On from START when Tunables.Aim.shootOnMove = true; off (and nothing changes) otherwise. A button can switch it: g1.y.toggle(robot.aim().movingShotMode()). Needs odometry.
    • lead

      public MovingShot.Result lead()
      The lead worked out last (degrees and inches, see MovingShot.Result), or null if none in the last cameraMaxAgeMs (shooting on the move off, no goal, no odometry).
    • isMovingShot

      public boolean isMovingShot()
      True while shooting on the move is on and the robot moves (faster than 2 in/s, or turning faster than 5 degrees per second): the aim is being led now.
    • mode

      public Mode mode()
      Whole-robot aim, for a robot with no turret: while it is on, TeleOp turns the robot toward the goal and the driver keeps forward and sideways. The driver's turn stick still wins: move it and the robot turns as the stick says; let go and the aim takes over again. Off at START. g1.rb.toggle(robot.aimMode()).
      Throws:
      IllegalStateException - if a turret follows the goal: whole-robot aim and turret-following are separate modes (both would chase the same error and swing back and forth)
    • errorDeg

      public double errorDeg()
      How many degrees the aim is off now (any sign), NaN if unknown. With whole-robot aim on: the robot's front to the goal. Otherwise, a turret that is following: its error. Neither: NaN.
    • isAimed

      public boolean isAimed()
      True when aimed: whole-robot aim on and within toleranceDeg, or a following turret that is aimed AT THE GOAL NOW (see Turret.isAimed(): a goal seen this moment, homed, in range). Losing the goal makes it false at once, so a launcher gated on it never fires blind. Use it as a launcher check: .requireReady("aimed", () -> aim().isAimed()).
    • rumbleDeg

      public double rumbleDeg()
      The error that starts the buzz of rumbleWhenAimed() (Tunables.Aim.rumbleDeg, default 4).