githubEdit

Swerve Drive

This tutorial covers how to create a swerve drive using YAMS. YAMS provides a complete swerve drive implementation with built-in simulation, odometry, and telemetry.

Overview

YAMS swerve consists of three main components:

  • SwerveModule - Individual module with drive and azimuth motors

  • SwerveDrive - The complete drive system managing all modules

  • SwerveInputStream - Helper for converting controller inputs to chassis speeds

circle-exclamation

Creating a Swerve Module

Each swerve module requires two SmartMotorControllers (drive and azimuth) and a SwerveModuleConfig:

import static edu.wpi.first.units.Units.*;

import com.ctre.phoenix6.hardware.CANcoder;
import com.revrobotics.spark.SparkLowLevel.MotorType;
import com.revrobotics.spark.SparkMax;
import edu.wpi.first.math.controller.SimpleMotorFeedforward;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.system.plant.DCMotor;
import yams.gearing.MechanismGearing;
import yams.mechanisms.config.SwerveModuleConfig;
import yams.mechanisms.swerve.SwerveModule;
import yams.motorcontrollers.SmartMotorController;
import yams.motorcontrollers.SmartMotorControllerConfig;
import yams.motorcontrollers.SmartMotorControllerConfig.TelemetryVerbosity;
import yams.motorcontrollers.local.SparkWrapper;

public SwerveModule createModule(
    SparkMax driveMotor, 
    SparkMax azimuthMotor, 
    CANcoder absoluteEncoder, 
    String moduleName,
    Translation2d location) 
{
  // Define gearing ratios
  MechanismGearing driveGearing = new MechanismGearing(12.75);  // L2 SDS MK4i
  MechanismGearing azimuthGearing = new MechanismGearing(6.75);
  Distance wheelDiameter = Inches.of(4);

  // Drive motor configuration
  SmartMotorControllerConfig driveCfg = new SmartMotorControllerConfig(this)
      .withWheelDiameter(wheelDiameter)
      .withClosedLoopController(0.3, 0, 0)
      .withGearing(driveGearing)
      .withFeedforward(new SimpleMotorFeedforward(0, 12.0 / 4.5, 0.01))  // kS, kV, kA
      .withStatorCurrentLimit(Amps.of(40))
      .withTelemetry("driveMotor", TelemetryVerbosity.HIGH);

  // Azimuth motor configuration
  SmartMotorControllerConfig azimuthCfg = new SmartMotorControllerConfig(this)
      .withClosedLoopController(1, 0, 0)
      .withFeedforward(new SimpleMotorFeedforward(0, 1))
      .withGearing(azimuthGearing)
      .withStatorCurrentLimit(Amps.of(20))
      .withTelemetry("angleMotor", TelemetryVerbosity.HIGH);

  // Create SmartMotorControllers
  SmartMotorController driveSMC = new SparkWrapper(driveMotor, DCMotor.getNEO(1), driveCfg);
  SmartMotorController azimuthSMC = new SparkWrapper(azimuthMotor, DCMotor.getNEO(1), azimuthCfg);

  // Create module configuration
  SwerveModuleConfig moduleConfig = new SwerveModuleConfig(driveSMC, azimuthSMC)
      .withAbsoluteEncoder(absoluteEncoder.getAbsolutePosition().asSupplier())
      .withTelemetry(moduleName, TelemetryVerbosity.HIGH)
      .withLocation(location)
      .withOptimization(true);  // Enable state optimization

  return new SwerveModule(moduleConfig);
}

Module Configuration Options

Method
Description

withAbsoluteEncoder(Supplier<Angle>)

Absolute encoder for azimuth position

withLocation(Translation2d)

Module position relative to robot center

withOptimization(boolean)

Enable state optimization (recommended)

withTelemetry(String, TelemetryVerbosity)

Configure telemetry output

Creating the SwerveDrive

Once you have your modules, create the SwerveDrive:

SwerveDriveConfig Options

Method
Description

withGyro(Supplier<Angle>)

Gyroscope angle supplier

withStartingPose(Pose2d)

Initial robot pose

withTranslationController(PIDController)

PID for drive-to-pose translation

withRotationController(PIDController)

PID for drive-to-pose rotation

withGyroOffset(Angle)

Offset to apply to gyro reading

Driving the Robot

Using SwerveInputStream

SwerveInputStream is a powerful helper for converting joystick inputs to ChassisSpeeds:

SwerveInputStream Options

Method
Description

withMaximumLinearVelocity(LinearVelocity)

Max translation speed

withMaximumAngularVelocity(AngularVelocity)

Max rotation speed

withDeadband(double)

Controller deadband

withCubeTranslationControllerAxis()

Cube translation for smoother control

withCubeRotationControllerAxis()

Cube rotation for smoother control

withAllianceRelativeControl()

Alliance-aware field-relative driving

withRobotRelative()

Robot-relative driving

withControllerHeadingAxis(DoubleSupplier, DoubleSupplier)

Heading-based control

Drive Commands

SwerveDrive Methods Reference

Driving

Method
Description

drive(Supplier<ChassisSpeeds>)

Command to drive with robot-relative speeds

setRobotRelativeChassisSpeeds(ChassisSpeeds)

Set robot-relative speeds directly

setFieldRelativeChassisSpeeds(ChassisSpeeds)

Set field-relative speeds

setSwerveModuleStates(SwerveModuleState[])

Set module states directly

lockPose()

Lock wheels in X pattern

driveToPose(Pose2d)

Command to drive to a pose

Odometry & Pose

Method
Description

getPose()

Get current estimated pose

resetOdometry(Pose2d)

Reset pose estimator

getGyroAngle()

Get current gyro angle

zeroGyro()

Zero the gyroscope

addVisionMeasurement(Pose2d, double)

Add vision measurement

addVisionMeasurement(Pose2d, double, Matrix)

Add vision measurement with std devs

Utility

Method
Description

getKinematics()

Get SwerveDriveKinematics

getModuleStates()

Get current module states

getModulePositions()

Get current module positions

getRobotRelativeSpeed()

Get current robot-relative speeds

getFieldRelativeSpeed()

Get current field-relative speeds

getDistanceFromPose(Pose2d)

Distance to a pose

getAngleDifferenceFromPose(Pose2d)

Angle difference to a pose

Periodic Updates

You must call these methods in your periodic functions:

SysId for Characterization

YAMS provides built-in SysId routines for swerve:

Complete Example

Here's a complete swerve subsystem:

Next Steps

Last updated