Simple Elevator Example

Below is an example of a simple elevator subsystem. Forward motion pulls the elevator up.

public class Elevator extends Subsystem {
  // CAN ID = 2
  public static int canId = 2;
  
  // How many inches the elavator tranvels
  // per radian that the motor turns
  public static double inchesPerRad = 2.5; 
  // where theta is rotation in radians and x is travel in inches:
  // - theta(x) = x / inchesPerRad
  // - x(theta) = theta * inchesPerRad
  
  // Thrify Nova driving NEO
  private ThriftyNova pivot;
  
  // From NEO internal encoder units, to radians
  private Conversion converter; 
  
  // Enum which contains setpoint data
  enum SetPoint {
    BOTTOM(0), LOW(18), MID(32), HIGH(42);
    final double pos;
    private SetPoint(double pos) { this.pos = pos; }
  }
  public SetPoint setPoint = SetPoint.BOTTOM;
  
  // Storing the real position 
  private double realPosition = 0;
  
  private Elevator() {
  
    converter = new Conversion(PositionUnit.RADIANS, EncoderType.INTERNAL);
    
    pivot = new ThriftyNova(canId) // create with CAN ID
      .setBrakeMode(true) // brake mode
      .setInverted(false) // not inverted 
      .setRampUp(0.25)    // 1/4 second ramp up
      .setRampDown(0.05)  // tiny ramp dowm
      
      .setMaxOutput(1, 0.25)  // full power for forward because the 
                              // system is fighting against gravity
                              // limits power on reverse because the 
                              // system is falling with gravity
                              
      .setSoftLimits(0, 7 * Math.PI) // constrain the motor [0, 4pi]
      .enableSoftLimits(true)        // enable the soft limits
      
      .setMaxCurrent(CurrentType.SUPPLY, 50) // set a 50amp current limit
                                             // on supply side
      
      .useEncoderType(EncoderType.INTERNAL) // use internal NEO encoder
      .usePIDSlot(PIDSlot.SLOT0)            // use the first PID slot
      
      // Configure the first PID slot
      .pid0.setP(0.5) 
      .pid0.setI(0)
      .pid0.setD(0)
      .pid0.setFF(1.2);
      
    // Iterate through errors and check them
    for (var err : pivot.getErrors()) {
    // The user can handle the errors
      System.err.println("Error", err.toString());
    }
    // Clear errors here
    pivot.clearErrors();
  }
  
  // This method is called periodically
  public void update() {
    // Set the position based on the setpoint
    pivot.setPosition(convertor.toMotor(setPoint.pos / inchesPerRad));
    
    // Get the real position from the encoder
   realPosition = convertor.fromMotor(pivot.getPosition()) * inchesPerRad;
    
    // Log the real position and the current draw
    SmartDashboard.putNumber("Elevator Position", realPosition); 
    SmartDashboard.putNumber("Elevator Current", pivot.getCurrent()); 
  }
  
  // Is the elevator at the correct setpoint
  public boolean atSetpoint() {
    // Return true if the absolute distance is under the 1/4 inch tolerance
    return Math.abs(realPosition - setPoint.pos) < 0.25; 
  } 
  
  // Begin singleton boilerplate
  private static volatile Elevator instance;
  public static synchronized Elevator getInstance() { 
    return instance == null ? instance = new Elevator() : instance;
  }
  // End singleton boilerplate
}

Last updated