Thrifty Nova
  • Software Resources
    • Getting Started
    • Configure Controller Settings
      • Factory Default
      • Motor Type - Minion Setting
      • Inverting the Motor
      • Brake Mode
      • Maximum Output
      • Ramp Up/Ramp Down
      • Current Limiting
      • Soft Limits
      • Hard Limits
      • Setting Encoder Position
      • Follower Mode
    • Configure Onboard PID
    • Configure CAN Frequency
    • IO Signal Management
    • Set Output
    • Logging
    • Get Feedback
    • Unit Conversions
    • Subsystem Examples
      • Simple Elevator Example
      • Swerve Module Example
      • Simple Arm Example
  • Electrical Resources
    • Wiring the Thrifty Nova
    • LED Color Codes
    • Brushless Hall Sensor Connector
    • USB Communications
    • 10 Pin Data Connector
      • Intro to Sensors
      • Sensor Hat
      • Motor Runner Board
    • Hard Reset
  • Mechanical
    • Mounting Options
  • Software Releases
    • Software Releases
  • Thrifty Config
    • Thrifty Config Demo Video
Powered by GitBook
On this page

Was this helpful?

Export as PDF
  1. Software Resources
  2. Subsystem Examples

Swerve Module Example

Here is an example implementation of a swerve module using Nova. The turn controller is configured with an external absolute encoder. You would create a swerve module by calling the constructor as follows.

// Assume module1 is defined in class scope
module1 = new NovaSwerveModule(1, 4, 77); // drive ID, turn ID, encoder offset.

Below is the swerve class definition.

public class NovaSwerveModule {
  // Constants for drive and turn motors (use your own)
  private static final double MAX_VELOCITY = 4.0; // max velocity
  private static final double GEAR_RATIO = 6.75; // gear ratio drive motor to wheel
  private static final double WHEEL_CIRCUM = .1; // wheel circumference
  
  // Thrify Novas driving NEO
  private ThriftyNova drive;
  private ThriftyNova turn;

  // Unit convertors for drive velocity, drive position, and turn
  private Conversion driveConverter; 
  private Conversion distanceConverter; 
  private Conversion turnConverter; 
  
  // Store the turn encoder offset
  private double turnEncoderOffset;
  
  // Construct swerve module
  private NovaSwerveModule(int driveId, int turnId, double turnEncoderOffset) {
    this.turnEncoderOffset = turnEncoderOffset;
    initializeDriveMotor(driveId);
    initializeTurnMotor(turnId);
  }

  // Construct and configure turn motor
  private void initializeDriveMotor(int driveId) {

    // For drive we need to convert between RPM and internal NEO encoder units
    driveConverter = new Conversion(VelcotiyUnit.ROTATIONS_PER_MIN, EncoderType.INTERNAL);
    distanceConverter = new Conversion(PositionUnit.ROTATIONS, EncoderType.INTERNAL);

    drive = new ThriftyNova(driveId) // create with correct CAN ID
      .setBrakeMode(true) // brake mode
      .setInverted(false) // not inverted 
      .setRampUp(0.1)    // small ramp up used during auto 
      .setRampDown(0.1)  // same for ramp down
      .setMaxOutput(1, 1)  // full power because drive motor
      .setMaxCurrent(CurrentType.SUPPLY, 80) // set a 80amp 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 (examples, please tune your own values)
      .pid0.setP(0.5) 
      .pid0.setI(0)
      .pid0.setD(0)
      .pid0.setFF(1.2);
      
    // Iterate through errors and check them
    for (var err : drive.getErrors()) {
    // The user can handle the errors
      System.err.println("Drive Motor Config Error: " + err.toString());
    }
    // Clear errors here
    drive.clearErrors();
  }

  // Construct and configure turn motor
  private void initializeTurnMotor(int turnId) {

    // For drive we need to convert between radians and external absolute encoder units
    turnConverter = new Conversion(PositionUnit.RADIANS, EncoderType.ABS);

    turn = new ThriftyNova(turnId) // create with correct CAN ID
      .setBrakeMode(true) // brake mode
      .setInverted(false) // not inverted 
      .setMaxOutput(1, 1)  // full power because we sometimes want to
                           // change direction quickly
      .setMaxCurrent(CurrentType.SUPPLY, 35) // set a 35amp current limit
                                             // on supply side
      .useEncoderType(EncoderType.ABS)      // use external absolute encoder
      .usePIDSlot(PIDSlot.SLOT0)            // use the first PID slot
      
      // Configure the first PID slot (examples, please tune your own values)
      .pid0.setP(0.5) 
      .pid0.setI(0)
      .pid0.setD(0)
      .pid0.setFF(1.2);
      
    // Iterate through errors and check them
    for (var err : turn.getErrors()) {
    // The user can handle the errors
      System.err.println("Drive Motor Config Error: " + err.toString());
    }
    // Clear errors here
    turn.clearErrors();
  }
  
  
  // This method is called periodically
  public void getState() {
    // Set the position based on the setpoint
    pivot.setPosition(convertor.toMotor(setPoint.pos / inchesPerRad));
    
    // Get the real position from the encoder
    double 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()); 
  }

  // Get position, required for swerve
  public SwerveModulePosition getPosition() {
    // Get the position of the drive motor and use the distance convertor to get the real position
    double distance = distanceConverter.fromMotor((drive.getPosition() * WHEEL_CIRCUM) / GEAR_RATIO);
    // Return the position object using the distance and rotation
    return new SwerveModulePosition(distance, getRotation());
  }

  // Get rotation, required for swerve
  public Rotation2d getRotation() {
    // Return the current rotation of the swerve rotation
    return Rotation2d.fromRadians(turnConverter.fromMotor(turn.getPosition()) - turnEncoderOffset);
  }

  // Set desired state, required for swerve
  public void setDesiredState(SwerveModuleState state) {
    // Optimize the state
    final SwerveModuleState optimized = SwerveModuleState.optimize(state, getRotation());

    // Output the desired velocity on the drive motor
    if (DriverStation.isAutonomous()) {
      // Calculate the motor velocity input
      double driveVelocity = driveConvertor.toMotor((optimized.speedMetersPerSecond * GEAR_RATIO) / WHEEL_CIRCUM);
      drive.setVelocity(driveVelocity);
    } else {
      // Calculate the motor power input
      drive.setPercent(optimized.speedMetersPerSecond / MAX_VELOCITY);
    }

    // Calculate the turn position input
    double turnPosition = turnConvertor.toMotor(optimized.angle.getRadians());
    // Output the desired angle on the turn motor
    turn.setPosition(turnPosition);
  }
}

PreviousSimple Elevator ExampleNextSimple Arm Example

Last updated 6 months ago

Was this helpful?