### Configure Example Project Build Gradle Source: https://github.com/yet-another-software-suite/yams/blob/master/README.md This snippet shows how to configure the build.gradle file in an example project to link the YAMS source code directly from a relative path. This is used in the YAMS examples to avoid direct dependency management. ```groovy sourceSets { main { java { srcDirs 'src/main/java' srcDirs '../../yams/' } } } ``` -------------------------------- ### Configure Absolute Encoder Gearing Stages in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets up absolute encoder gearing using explicit driver-driven gear pairs, suitable for compound or same-shaft gear trains. ```java easyCrt.withAbsoluteEncoder1GearingStages(12, 36, 18, 60); // 12->36, then 18->60 easyCrt.withAbsoluteEncoder2GearingStages(12, 60); // single stage 12->60 ``` -------------------------------- ### Configure Absolute Encoder Inversions in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets the inversion state for absolute encoders. This should be the single source of truth for inversion, overriding device-side settings. ```java easyCrt.withAbsoluteEncoderInversions(/* enc1 inverted */ false, /* enc2 inverted */ true); ``` -------------------------------- ### Configure Common Drive Gear in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets up gearing using a common drive gear that meshes with both encoder pinions. It calculates ratios based on provided gear teeth counts and a common ratio. ```java easyCrt.withCommonDriveGear(/* commonRatio */ 11.0, /* driveGearTeeth */ 50, /* encoder1Pinion */ 30, /* encoder2Pinion */ 31); ``` -------------------------------- ### Configure EasyCRT with Chain Helpers (Java) Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets up an EasyCRTConfig using chain helpers for defining encoder gearing. This approach is suitable when explicit gear stage definitions or common drive gears are not provided. It simplifies the configuration for common chain-driven mechanisms. ```java var easyCrt = new EasyCRTConfig(enc1, enc2) .withAbsoluteEncoder1Gearing(72, 24) // 72 drives 24 .withAbsoluteEncoder2Gearing(50, 20, 40) // 50 drives 20, 20 drives 40 .withMechanismRange(Rotations.of(0.0), Rotations.of(1.2)) .withAbsoluteEncoderInversions(false, false); ``` -------------------------------- ### Configure SmartMotorController with YAMS Source: https://context7.com/yet-another-software-suite/yams/llms.txt Example of configuring a motor controller using YAMS' `SmartMotorControllerConfig`. This demonstrates setting up PID, feedforward, current limits, gearing, soft limits, and telemetry settings via a fluent builder pattern. ```java import static edu.wpi.first.units.Units.*; import edu.wpi.first.math.controller.ArmFeedforward; import edu.wpi.first.math.system.plant.DCMotor; import yams.gearing.GearBox; import yams.gearing.MechanismGearing; import yams.motorcontrollers.SmartMotorControllerConfig; import yams.motorcontrollers.SmartMotorControllerConfig.*; // Create configuration for an arm motor SmartMotorControllerConfig motorConfig = new SmartMotorControllerConfig(this) // 'this' is the Subsystem // PID with trapezoidal motion profile (kP, kI, kD, maxVelocity, maxAcceleration) .withClosedLoopController(4.0, 0.0, 0.0, DegreesPerSecond.of(180), DegreesPerSecondPerSecond.of(90)) // Mechanism gearing (motor rotations to mechanism rotations) .withGearing(new MechanismGearing(GearBox.fromReductionStages(3, 4))) // 12:1 total reduction // Soft limits prevent movement beyond safe range .withSoftLimit(Degrees.of(-30), Degrees.of(100)) // Arm feedforward (kS, kG, kV, kA) - kG compensates for gravity .withFeedforward(new ArmFeedforward(0.1, 0.5, 0.8, 0.01)) // Current limiting protects motor and mechanism .withStatorCurrentLimit(Amps.of(40)) // Motor behavior settings .withIdleMode(MotorMode.BRAKE) .withMotorInverted(false) .withClosedLoopRampRate(Seconds.of(0.25)) // Telemetry for debugging and tuning .withTelemetry("ArmMotor", TelemetryVerbosity.HIGH) // Control mode selection .withControlMode(ControlMode.CLOSED_LOOP); ``` -------------------------------- ### Initialize Swerve Drive Subsystem in Java Source: https://context7.com/yet-another-software-suite/yams/llms.txt This Java code initializes the main SwerveSubsystem. It creates a Pigeon2 IMU for gyro data and then instantiates four swerve modules (front-left, front-right, back-left, back-right) using the `createModule` helper method. Each module is assigned specific SparkMax controllers, CANcoders, and relative positions. Finally, it configures the overall SwerveDrive with the created modules, gyro, and starting pose. ```java import static edu.wpi.first.units.Units.*; import com.ctre.phoenix6.hardware.CANcoder; import com.ctre.phoenix6.hardware.Pigeon2; import com.revrobotics.spark.SparkMax; import com.revrobotics.spark.SparkLowLevel.MotorType; import edu.wpi.first.math.controller.PIDController; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Translation2d; import edu.wpi.first.wpilibj2.command.SubsystemBase; import yams.mechanisms.config.SwerveDriveConfig; import yams.mechanisms.swerve.SwerveDrive; import yams.mechanisms.swerve.SwerveModule; public class SwerveSubsystem extends SubsystemBase { private final SwerveDrive drive; private final Field2d field = new Field2d(); // Create a swerve module with drive and azimuth motors private SwerveModule createModule( SparkMax driveMotor, SparkMax azimuthMotor, CANcoder absoluteEncoder, String name, Translation2d location) { // ... (implementation from previous snippet) return null; // Placeholder for brevity } public SwerveSubsystem() { Pigeon2 gyro = new Pigeon2(14); // Create four swerve modules (FL, FR, BL, BR) SwerveModule frontLeft = createModule( new SparkMax(1, MotorType.kBrushless), new SparkMax(2, MotorType.kBrushless), new CANcoder(3), "frontLeft", new Translation2d(Inches.of(12), Inches.of(12))); SwerveModule frontRight = createModule( new SparkMax(4, MotorType.kBrushless), new SparkMax(5, MotorType.kBrushless), new CANcoder(6), "frontRight", new Translation2d(Inches.of(12), Inches.of(-12))); SwerveModule backLeft = createModule( new SparkMax(7, MotorType.kBrushless), new SparkMax(8, MotorType.kBrushless), new CANcoder(9), "backLeft", new Translation2d(Inches.of(-12), Inches.of(12))); SwerveModule backRight = createModule( new SparkMax(10, MotorType.kBrushless), new SparkMax(11, MotorType.kBrushless), new CANcoder(12), "backRight", new Translation2d(Inches.of(-12), Inches.of(-12))); // Swerve drive configuration SwerveDriveConfig config = new SwerveDriveConfig(this, frontLeft, frontRight, backLeft, backRight) .withGyro(gyro.getYaw().asSupplier()) .withStartingPose(new Pose2d(0, 0, Rotation2d.fromDegrees(0))) .withTranslationController(new PIDController(5, 0, 0)); drive = new SwerveDrive(config); } // ... rest of the SwerveSubsystem class } ``` -------------------------------- ### Configure Direct Encoder Ratios in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets the direct rotation-per-mechanism-rotation ratios for two absolute encoders. This is used when the ratios are already computed. ```java easyCrt.withEncoderRatios(/* enc1 */ 50.0, /* enc2 */ 18.3333); ``` -------------------------------- ### Configure Absolute Encoder Gearing Chain in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Defines the gear train for absolute encoders as a sequence of meshing gears, from the mechanism-side gear to the encoder pinion. ```java easyCrt.withAbsoluteEncoder1Gearing(50, 20, 40); // 50 drives 20, 20 drives 40 easyCrt.withAbsoluteEncoder2Gearing(60, 20); // 60 drives 20 ``` -------------------------------- ### Configure Mechanism Motion Range in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets the minimum and maximum angles for the mechanism's rotation. The solver will only consider solutions within this specified range. ```java easyCrt.withMechanismRange(minAngle, maxAngle) ``` -------------------------------- ### Install YAMS Vendor Dependency in WPILib Source: https://context7.com/yet-another-software-suite/yams/llms.txt Instructions for adding the YAMS library to a WPILib project as a vendor dependency using the VS Code extension. This involves managing vendor libraries and pasting a provided URL. ```java // In VS Code with WPILib extension: // 1. Open Command Palette (Ctrl+Shift+P / Cmd+Shift+P) // 2. Select: WPILib: Manage Vendor Libraries // 3. Choose: Install new library (online) // 4. Paste URL: https://yet-another-software-suite.github.io/YAMS/yams.json ``` -------------------------------- ### Get RoboRIO Deploy Directory Path (Java) Source: https://github.com/yet-another-software-suite/yams/blob/master/examples/differential_mechanism/src/main/deploy/example.txt This snippet demonstrates how to obtain the path to the RoboRIO's deploy directory using the `Filesystem.getDeployDirectory()` function in Java. This path is relative to the deploy directory on the RoboRIO. Ensure WPILib is included in your project dependencies. ```java import edu.wpi.first.wpilibj.Filesystem; import java.nio.file.Path; // ... inside a method or constructor ... Path deployDirectory = Filesystem.getDeployDirectory().toPath(); System.out.println("Deploy directory: " + deployDirectory); // You can then construct paths to files within the deploy directory // Path myFile = deployDirectory.resolve("my_config.json"); ``` -------------------------------- ### Configure EasyCRT with Common Drive Gear (Java) Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets up an EasyCRTConfig object using common drive gear ratios and encoder pinion teeth. This configuration is suitable when encoders are driven through the mechanism reduction. It allows for setting mechanism range, tolerance, inversions, and constraints for gear recommendations. ```java var easyCrt = new EasyCRTConfig(enc1Supplier, enc2Supplier) .withCommonDriveGear( /* commonRatio (mech:drive) */ 12.0, /* driveGearTeeth */ 50, /* encoder1Pinion */ 19, /* encoder2Pinion */ 23) .withAbsoluteEncoderOffsets(Rotations.of(0.0), Rotations.of(0.0)) // set after mechanical zero .withMechanismRange(Rotations.of(-1.0), Rotations.of(2.0)) // -360 deg to +720 deg .withMatchTolerance(Rotations.of(0.06)) // ~1.08 deg at encoder2 for the example ratio .withAbsoluteEncoderInversions(false, false) .withCrtGearRecommendationConstraints( /* coverageMargin */ 1.2, /* minTeeth */ 15, /* maxTeeth */ 45, /* maxIterations */ 30); // you can inspect: easyCrt.getUniqueCoverage(); // Optional coverage from prime counts and common scale easyCrt.coverageSatisfiesRange(); // Does coverage exceed maxMechanismAngle? easyCrt.getRecommendedCrtGearPair(); // Suggested pair within constraints // Create the solver: var easyCrtSolver = new EasyCRT(easyCrt); ``` -------------------------------- ### Configure EasyCRT with Explicit Gearing Stages (Java) Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Configures EasyCRT using explicit gearing stage definitions for each absolute encoder. This method is used when the common drive inputs are not directly provided, and the recommender needs to be seeded manually. It allows for defining compound gear trains for each encoder. ```java var easyCrt = new EasyCRTConfig(enc1, enc2) .withAbsoluteEncoder1GearingStages(14, 50, 16, 60) // compound to encoder 1 .withAbsoluteEncoder2GearingStages(12, 36, 18, 54) // compound to encoder 2 .withMechanismRange(Rotations.of(0.0), Rotations.of(2.0)) .withAbsoluteEncoderOffsets(Rotations.of(0.01), Rotations.of(0.0)) // align to hard stop .withAbsoluteEncoderInversions(false, true) // Seed recommender since common drive inputs were not provided above .withCrtGearRecommendationInputs( /* stage1GearTeeth (shared driver) */ 48, /* stage2Ratio (mech:drive) */ 10.0) .withCrtGearRecommendationConstraints( /* coverageMargin */ 1.15, /* minTeeth */ 15, /* maxTeeth */ 55, /* maxIterations */ 30); easyCrt.getRecommendedCrtGearPair(); ``` -------------------------------- ### Set Up Arm Mechanism with Physics Simulation in Java Source: https://context7.com/yet-another-software-suite/yams/llms.txt Creates arm mechanisms with physics-based simulation, angle control, and built-in commands. This includes configuring motor controllers, defining arm properties like length and mass, and setting up control modes and limits. ```java import static edu.wpi.first.units.Units.*; import edu.wpi.first.math.controller.ArmFeedforward; import edu.wpi.first.math.system.plant.DCMotor; import edu.wpi.first.units.measure.Angle; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; import yams.mechanisms.config.ArmConfig; import yams.mechanisms.positional.Arm; import yams.motorcontrollers.SmartMotorController; import yams.motorcontrollers.SmartMotorControllerConfig; import yams.motorcontrollers.SmartMotorControllerConfig.*; import yams.motorcontrollers.local.SparkWrapper; import yams.gearing.GearBox; import yams.gearing.MechanismGearing; import com.revrobotics.spark.SparkMax; import com.revrobotics.spark.SparkLowLevel.MotorType; public class ArmSubsystem extends SubsystemBase { private final SparkMax armMotor = new SparkMax(1, MotorType.kBrushless); private final SmartMotorControllerConfig motorConfig = new SmartMotorControllerConfig(this) .withClosedLoopController(4, 0, 0, DegreesPerSecond.of(180), DegreesPerSecondPerSecond.of(90)) .withSoftLimit(Degrees.of(-30), Degrees.of(100)) .withGearing(new MechanismGearing(GearBox.fromReductionStages(3, 4))) .withIdleMode(MotorMode.BRAKE) .withTelemetry("ArmMotor", TelemetryVerbosity.HIGH) .withStatorCurrentLimit(Amps.of(40)) .withFeedforward(new ArmFeedforward(0, 0.5, 0, 0)) .withControlMode(ControlMode.CLOSED_LOOP); private final SmartMotorController motor = new SparkWrapper(armMotor, DCMotor.getNEO(1), motorConfig); private final ArmConfig armConfig = new ArmConfig(motor) .withLength(Meters.of(0.5)) // Arm length for simulation .withMass(Pounds.of(5)) // Arm mass for MOI calculation .withHardLimit(Degrees.of(-45), Degrees.of(135)) // Physical stops .withStartingPosition(Degrees.of(0)) // Initial position .withHorizontalZero(Degrees.of(0)) // Encoder offset for horizontal .withTelemetry("Arm", TelemetryVerbosity.HIGH); private final Arm arm = new Arm(armConfig); @Override public void periodic() { arm.updateTelemetry(); } @Override public void simulationPeriodic() { arm.simIterate(); // Physics simulation update } // Command to set arm to specific angle (runs continuously) public Command setAngle(Angle angle) { return arm.setAngle(angle); } // Command to move to angle and finish when reached public Command runToAngle(Angle angle) { return arm.runTo(angle, Degrees.of(2)); // 2 degree tolerance } // Open-loop duty cycle control public Command manualControl(double dutyCycle) { return arm.set(dutyCycle); } // SysId characterization routine public Command sysIdRoutine() { return arm.sysId(Volts.of(3), Volts.of(1).per(Second), Seconds.of(10)); } // Triggers for conditional logic public boolean isAtTarget(Angle target, Angle tolerance) { return arm.isNear(target, tolerance).getAsBoolean(); } public Angle getCurrentAngle() { return arm.getAngle(); } } ``` -------------------------------- ### Configure Absolute Encoder Offsets in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Applies offsets to absolute encoder readings before wrapping. This is used after mechanical zeroing to align encoder readings close to 0.0 rotations. ```java easyCrt.withAbsoluteEncoderOffsets(enc1Offset, enc2Offset) ``` -------------------------------- ### Configure Exponential Motion Profiles (Java) Source: https://context7.com/yet-another-software-suite/yams/llms.txt This Java code demonstrates configuring motor controllers with exponential motion profiles, which provide physically accurate movement respecting motor voltage limits. It shows three methods: auto-calculation from motor characteristics for an arm, auto-calculation for an elevator, and manual specification of maximum velocity and acceleration. ```java import edu.wpi.first.math.system.plant.DCMotor; import yams.motorcontrollers.SmartMotorControllerConfig; import static edu.wpi.first.units.Units.*; // Method 1: Auto-calculate from motor characteristics (arm) SmartMotorControllerConfig armConfig = new SmartMotorControllerConfig(this) .withClosedLoopController(3, 0, 0.1) .withGearing(new MechanismGearing(100)) // Exponential profile based on motor and mechanism physics .withExponentialProfile( Volts.of(10), // Maximum voltage DCMotor.getNEO(1), // Motor type KilogramSquareMeters.of(0.5) // Moment of inertia ); // Method 2: Auto-calculate for elevator SmartMotorControllerConfig elevatorConfig = new SmartMotorControllerConfig(this) .withClosedLoopController(2, 0, 0) .withMechanismCircumference(Inches.of(1.5 * Math.PI)) .withGearing(new MechanismGearing(20)) .withExponentialProfile( Volts.of(12), // Maximum voltage DCMotor.getNEO(2), // 2 NEO motors Kilograms.of(7), // Carriage mass Inches.of(0.75) // Drum radius ); // Method 3: Manual specification SmartMotorControllerConfig manualConfig = new SmartMotorControllerConfig(this) .withClosedLoopController(4, 0, 0) .withGearing(new MechanismGearing(50)) .withExponentialProfile( Volts.of(10), RotationsPerSecond.of(2), // Max velocity RotationsPerSecondPerSecond.of(4) // Max acceleration ); ``` -------------------------------- ### Configure Match Tolerance in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Sets the tolerance for matching encoder readings to mechanism angles. This value, in rotations of encoder 2, affects the likelihood of finding a solution or encountering ambiguity. ```java easyCrt.withMatchTolerance(tolerance) ``` -------------------------------- ### Configure and Create an Arm Mechanism in Java Source: https://github.com/yet-another-software-suite/yams/blob/master/docs/README.md This Java code demonstrates how to configure a SmartMotorController with various settings like closed-loop control, soft limits, gearing, and telemetry. It then creates a TalonFXS motor controller wrapper and an Arm mechanism using the configured settings. This snippet is part of the YAMS framework and is intended for use with WPILib. ```java import edu.wpi.first.wpilibjcontrol.DCMotor; import edu.wpi.first.wpilibjcontrol.MotorMode; import edu.wpi.first.units.*; import com.team752.yams.mechanisms.Arm; import com.team752.yams.mechanisms.ArmConfig; import com.team752.yams.mechanisms.ArmFeedforward; import com.team752.yams.mechanisms.MechanismGearing; import com.team752.yams.mechanisms.SmartMotorController; import com.team752.yams.mechanisms.SmartMotorControllerConfig; import com.team752.yams.mechanisms.TalonFXS; import com.team752.yams.mechanisms.TalonFXSWrapper; import com.team752.yams.mechanisms.TelemetryVerbosity; import com.team752.yams.mechanisms.ControlMode; // Create your motor controller config SmartMotorControllerConfig motorConfig = new SmartMotorControllerConfig(this) .withClosedLoopController(4, 0, 0, DegreesPerSecond.of(180), DegreesPerSecondPerSecond.of(90)) .withSoftLimit(Degrees.of(-30), Degrees.of(100)) .withGearing(new MechanismGearing(GearBox.fromReductionStages(3, 4))) .withIdleMode(MotorMode.BRAKE) .withTelemetry("ArmMotor", TelemetryVerbosity.HIGH) .withStatorCurrentLimit(Amps.of(40)) .withMotorInverted(false) .withClosedLoopRampRate(Seconds.of(0.25)) .withOpenLoopRampRate(Seconds.of(0.25)) .withFeedforward(new ArmFeedforward(0, 0, 0, 0)) .withControlMode(ControlMode.CLOSED_LOOP); // Create your motor TalonFXS armMotor = new TalonFXS(1); SmartMotorController motor = new TalonFXSWrapper(armMotor, DCMotor.getNEO(1), motorConfig); // Create your mechanism config ArmConfig config = new ArmConfig(motor) .withLength(Meters.of(0.135)) .withHardLimit(Degrees.of(-100), Degrees.of(200)) .withTelemetry("ArmExample", TelemetryVerbosity.HIGH) .withMass(Pounds.of(1)) .withStartingPosition(Degrees.of(0)) .withHorizontalZero(Degrees.of(0)); // Create the Arm! Arm arm = new Arm(config); ``` -------------------------------- ### Configure Follower Motors for Mechanisms (Java) Source: https://context7.com/yet-another-software-suite/yams/llms.txt Demonstrates two methods for configuring follower motors: hardware followers for directly linked motors of the same vendor, and loosely coupled followers for motors that may be of different vendors or require custom control. This allows a single 'leader' motor to control the speed and position of multiple 'follower' motors, simplifying mechanism control. ```java import edu.wpi.first.math.Pair; import yams.motorcontrollers.SmartMotorController; import yams.motorcontrollers.SmartMotorControllerConfig; // Method 1: Hardware followers (same vendor, directly linked) SmartMotorControllerConfig elevatorConfig = new SmartMotorControllerConfig(this) // ... other config .withFollowers( Pair.of(elevatorMotor2, false), // Motor object, inverted Pair.of(elevatorMotor3, true) // Motor object, not inverted (opposite side) ); // Method 2: Loosely coupled followers (different vendors or custom control) SmartMotorController leader = new SparkWrapper(motor1, DCMotor.getNEO(1), leaderConfig); SmartMotorController follower1 = new TalonFXWrapper(motor2, DCMotor.getKrakenX60(1), followerConfig); SmartMotorController follower2 = new SparkWrapper(motor3, DCMotor.getNEO(1), followerConfig); // Configure leader with loosely coupled followers SmartMotorControllerConfig config = new SmartMotorControllerConfig(this) // ... other config .withLooselyCoupledFollowers(follower1, follower2); // Loosely coupled followers receive the same position/velocity requests // but maintain their own closed-loop control ``` -------------------------------- ### Check if Coverage Satisfies Range Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Verifies if the computed mechanism coverage meets or exceeds the configured maximum mechanism angle. This function is crucial for ensuring sufficient travel is covered and is often used as a guardrail when selecting pinion teeth. ```java /** * Checks if the computed coverage is sufficient for the configured maximum mechanism angle. * @param computedCoverage The calculated unique coverage in rotations. * @param maxMechanismAngle The maximum allowed mechanism angle. * @return true if the coverage satisfies the range, false otherwise. */ public boolean coverageSatisfiesRange(double computedCoverage, double maxMechanismAngle) { // Assuming computedCoverage is in the same units as maxMechanismAngle (e.g., rotations or degrees) return computedCoverage >= maxMechanismAngle; } ``` -------------------------------- ### Access RoboRIO Deploy Directory using WPILib Source: https://github.com/yet-another-software-suite/yams/blob/master/examples/advantage_kit/src/main/deploy/example.txt Demonstrates how to obtain the path to the RoboRIO's deploy directory using the `Filesystem.getDeployDirectory` function from WPILib. This is essential for accessing files deployed to the RoboRIO. ```java String deployDirectory = edu.wpi.first.wpilibj.Filesystem.getDeployDirectory(); System.out.println("Deploy directory: " + deployDirectory); ``` -------------------------------- ### Configure and Control Elevator Mechanism in Java Source: https://context7.com/yet-another-software-suite/yams/llms.txt This Java code demonstrates how to configure and control an elevator mechanism. It utilizes the YAMS library for motor control, gearing, and physics simulation. The configuration includes setting motor parameters, gearing ratios, soft limits, feedforward gains, and defining robot-relative positioning. It also provides commands for setting specific heights, manual open-loop control, and reaching preset positions. ```java import static edu.wpi.first.units.Units.*; import edu.wpi.first.math.controller.ElevatorFeedforward; import edu.wpi.first.math.geometry.Translation3d; import edu.wpi.first.math.system.plant.DCMotor; import edu.wpi.first.units.measure.Distance; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; import yams.mechanisms.config.ElevatorConfig; import yams.mechanisms.config.MechanismPositionConfig; import yams.mechanisms.positional.Elevator; import yams.motorcontrollers.SmartMotorController; import yams.motorcontrollers.SmartMotorControllerConfig; import yams.motorcontrollers.SmartMotorControllerConfig.*; import yams.motorcontrollers.local.SparkWrapper; import yams.gearing.GearBox; import yams.gearing.MechanismGearing; import com.revrobotics.spark.SparkMax; import com.revrobotics.spark.SparkLowLevel.MotorType; public class ElevatorSubsystem extends SubsystemBase { private final SparkMax elevatorMotor = new SparkMax(2, MotorType.kBrushless); // Configure for linear (distance) control private final SmartMotorControllerConfig motorConfig = new SmartMotorControllerConfig(this) // Mechanism circumference converts rotations to distance .withMechanismCircumference(Meters.of(Inches.of(1.5).in(Meters) * Math.PI)) // 1.5" diameter spool // Linear velocity/acceleration profile (meters) .withClosedLoopController(4, 0, 0, MetersPerSecond.of(1), MetersPerSecondPerSecond.of(2)) .withSoftLimit(Meters.of(0), Meters.of(1.5)) .withGearing(new MechanismGearing(GearBox.fromReductionStages(5, 4))) // 20:1 .withIdleMode(MotorMode.BRAKE) .withTelemetry("ElevatorMotor", TelemetryVerbosity.HIGH) .withStatorCurrentLimit(Amps.of(40)) // Elevator feedforward (kS, kG, kV, kA) - kG compensates for gravity .withFeedforward(new ElevatorFeedforward(0.1, 0.3, 2.0, 0.1)) .withControlMode(ControlMode.CLOSED_LOOP); private final SmartMotorController motor = new SparkWrapper(elevatorMotor, DCMotor.getNEO(1), motorConfig); // Robot-relative position for visualization private final MechanismPositionConfig mechPosition = new MechanismPositionConfig() .withMaxRobotHeight(Meters.of(2.0)) .withMaxRobotLength(Meters.of(0.75)) .withRelativePosition(new Translation3d(Meters.of(0), Meters.of(0), Meters.of(0.3))); private final ElevatorConfig elevatorConfig = new ElevatorConfig(motor) .withStartingHeight(Meters.of(0.3)) // Initial height .withHardLimits(Meters.of(0), Meters.of(1.8)) // Physical travel limits .withMass(Pounds.of(15)) // Carriage mass .withDrumRadius(Inches.of(0.75)) // Spool radius for simulation .withMechanismPositionConfig(mechPosition) .withTelemetry("Elevator", TelemetryVerbosity.HIGH); private final Elevator elevator = new Elevator(elevatorConfig); @Override public void periodic() { elevator.updateTelemetry(); } @Override public void simulationPeriodic() { elevator.simIterate(); } // Command to set elevator height public Command setHeight(Distance height) { return elevator.setHeight(height); } // Move to height and finish when reached public Command runToHeight(Distance height) { return elevator.runTo(height, Centimeters.of(2)); } // Open-loop control public Command manualControl(double dutyCycle) { return elevator.set(dutyCycle); } // Preset positions public Command goToBottom() { return runToHeight(Meters.of(0.1)); } public Command goToTop() { return runToHeight(Meters.of(1.5)); } public Distance getHeight() { return elevator.getHeight(); } // Trigger-based state checks public boolean isAbove(Distance height) { return elevator.gte(height).getAsBoolean(); } } ``` -------------------------------- ### Configure External Encoders for Position Feedback (Java) Source: https://context7.com/yet-another-software-suite/yams/llms.txt Explains how to integrate external absolute encoders, such as REV Through Bore Encoders with SparkMax or CANcoders with TalonFX, for precise position feedback. This includes setting up the encoder, defining its gearing ratio, zero offset, and whether to use it for PID control. This is crucial for accurate mechanism positioning and state tracking. ```java import com.ctre.phoenix6.hardware.CANcoder; import com.revrobotics.spark.SparkMax; import yams.motorcontrollers.SmartMotorControllerConfig; import yams.gearing.MechanismGearing; import static edu.wpi.first.units.Units.*; // Using REV Through Bore Encoder with SparkMax SparkMax motor = new SparkMax(1, MotorType.kBrushless); SmartMotorControllerConfig config = new SmartMotorControllerConfig(this) // Attach the absolute encoder .withExternalEncoder(motor.getAbsoluteEncoder()) // External encoder gearing (encoder rotations to mechanism rotations) // Default is 1:1, set if encoder is not directly on mechanism output .withExternalEncoderGearing(new MechanismGearing(0.5)) // 2:1 encoder reduction // Zero offset to align encoder reading with mechanism position .withExternalEncoderZeroOffset(Degrees.of(45)) // Invert encoder if needed .withExternalEncoderInverted(false) // Use absolute encoder as feedback device for PID .withUseExternalFeedbackEncoder(true) // Synchronization threshold - re-seed relative encoder when error exceeds this .withFeedbackSynchronizationThreshold(Degrees.of(2)); // Using CANcoder with TalonFX CANcoder cancoder = new CANcoder(10); SmartMotorControllerConfig ctreConfig = new SmartMotorControllerConfig(this) .withExternalEncoder(cancoder) .withExternalEncoderZeroOffset(Degrees.of(90)) .withUseExternalFeedbackEncoder(true); ``` -------------------------------- ### Calculate Unique Coverage with Prime Teeth Counts Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Calculates the mechanism rotations that can be uniquely represented given prime tooth counts and a common scale. This is often automatically provided when using `withCommonDriveGear`. The formula used is `coverageRot = lcm(encoder1PrimeTeeth, encoder2PrimeTeeth) / commonScaleK`. ```java /** * Calculates the mechanism rotations that can be uniquely represented. * @param encoder1PrimeTeeth Prime tooth count for encoder 1. * @param encoder2PrimeTeeth Prime tooth count for encoder 2. * @param commonScaleK The common scale factor (commonRatio * driveGearTeeth). * @return The number of unique mechanism rotations. */ public double getUniqueCoverage(int encoder1PrimeTeeth, int encoder2PrimeTeeth, double commonScaleK) { // Implementation details would go here, likely involving LCM calculation long lcm = calculateLcm(encoder1PrimeTeeth, encoder2PrimeTeeth); return (double) lcm / commonScaleK; } // Helper function for LCM (Least Common Multiple) private long calculateLcm(long a, long b) { return (a * b) / calculateGcd(a, b); } // Helper function for GCD (Greatest Common Divisor) private long calculateGcd(long a, long b) { while (b != 0) { long temp = b; b = a % b; a = temp; } return a; } ``` -------------------------------- ### Configure Simulation-Specific PID and Feedforward (Java) Source: https://context7.com/yet-another-software-suite/yams/llms.txt This Java code configures a motor controller with separate PID and feedforward gains for real-world operation and simulation. The `withSimClosedLoopController` and `withSimFeedforward` methods allow overriding the default values when `RobotBase.isSimulation()` is true, enabling tailored tuning for different environments. ```java import edu.wpi.first.math.controller.ArmFeedforward; import edu.wpi.first.math.controller.SimpleMotorFeedforward; import yams.motorcontrollers.SmartMotorControllerConfig; import static edu.wpi.first.units.Units.*; SmartMotorControllerConfig config = new SmartMotorControllerConfig(this) // Real robot values (used on actual hardware) .withClosedLoopController(4, 0, 0.1, DegreesPerSecond.of(180), DegreesPerSecondPerSecond.of(90)) .withFeedforward(new ArmFeedforward(0.1, 0.45, 0.8, 0.01)) // Simulation-specific values (override real values in sim) .withSimClosedLoopController(2, 0, 0.05, DegreesPerSecond.of(200), DegreesPerSecondPerSecond.of(100)) .withSimFeedforward(new ArmFeedforward(0, 0.4, 0.7, 0)) // Other config... .withGearing(new MechanismGearing(12)); // At runtime, YAMS automatically selects the appropriate values: // - RobotBase.isSimulation() == true -> sim values // - RobotBase.isSimulation() == false -> real values ``` -------------------------------- ### Initialize Motor Controller Encoder Position with EasyCRT Source: https://github.com/yet-another-software-suite/yams/blob/master/EasyCRTConfigGuide.md Initializes a motor controller's encoder with the solved mechanism angle obtained from EasyCRT. This ensures the motor controller knows its absolute position after booting, which is essential for closed-loop control. This should be called once at startup after sensors are ready. ```java import com.example.yams.EasyCRT; import com.example.yams.MotorController; // Assume easyCrtConfig and motor are already initialized var easyCrtConfig = ...; // build EasyCRTConfig as shown above var easyCrtSolver = new EasyCRT(easyCrtConfig); var motor = new MotorController(); // Assume MotorController has a setEncoderPosition method // If the mechanism is configured using YAMS, use setEncoderPosition easyCrtSolver.getAngleOptional().ifPresent(mechAngle -> { motor.setEncoderPosition(mechAngle); }); ``` -------------------------------- ### Configure Triggers for State-Based Commands (Java) Source: https://context7.com/yet-another-software-suite/yams/llms.txt Defines various triggers based on mechanism states like arm position, elevator height, and flywheel velocity. These triggers can be combined using logical AND operations to activate commands when specific conditions are met. This is useful for creating complex command sequences based on real-time robot state. ```java import edu.wpi.first.wpilibj2.command.button.Trigger; import static edu.wpi.first.units.Units.*; // Arm angle triggers Trigger armAtStow = arm.isNear(Degrees.of(0), Degrees.of(3)); Trigger armAtScore = arm.isNear(Degrees.of(90), Degrees.of(5)); Trigger armAboveHorizontal = arm.gte(Degrees.of(0)); Trigger armBelowMax = arm.lte(Degrees.of(100)); Trigger armInSafeZone = arm.between(Degrees.of(-10), Degrees.of(110)); // Elevator height triggers Trigger elevatorAtBottom = elevator.isNear(Meters.of(0), Centimeters.of(2)); Trigger elevatorAtTop = elevator.gte(Meters.of(1.5)); Trigger elevatorExtended = elevator.between(Meters.of(0.5), Meters.of(1.5)); // Flywheel velocity triggers Trigger shooterAtSpeed = shooter.isNear(RPM.of(4000), RPM.of(100)); Trigger shooterSpunUp = shooter.gte(RPM.of(3500)); // Use in commands scoreButton.and(armAtScore).and(shooterAtSpeed) .onTrue(Commands.runOnce(() -> indexer.feed())); // Mechanism max/min triggers (uses soft limits or hard limits) Trigger atMax = arm.max(); Trigger atMin = arm.min(); ``` -------------------------------- ### Configure GearBox Ratios in Java Source: https://context7.com/yet-another-software-suite/yams/llms.txt Defines complex gearbox configurations using reduction stages, gear ratios, or tooth counts. Supports multiple methods for creating GearBox objects, including direct ratio and combining with sprocket reductions. ```java import yams.gearing.GearBox; import yams.gearing.MechanismGearing; // Method 1: Reduction stages (each stage is input:output ratio) GearBox gearbox1 = GearBox.fromReductionStages(3, 4); // 3:1 then 4:1 = 12:1 total // Method 2: String format "input:output" GearBox gearbox2 = GearBox.fromStages("5:1", "4:1", "3:1"); // 60:1 total // Method 3: Gear teeth (driven gear teeth, in sequence) GearBox gearbox3 = GearBox.fromTeeth(12, 48, 16, 64); // (48/12) * (64/16) = 16:1 // Method 4: Direct ratio (for pre-calculated gearboxes) MechanismGearing directGearing = new MechanismGearing(12.75); // 12.75:1 reduction // Combine gearbox with sprocket reduction MechanismGearing fullGearing = new MechanismGearing( GearBox.fromReductionStages(4, 3), // 12:1 gearbox new Sprocket(18, 36) // 2:1 sprocket reduction ); // Total: 24:1 ``` -------------------------------- ### Run Arm Mechanism System Identification (SysId) Source: https://context7.com/yet-another-software-suite/yams/llms.txt This Java code snippet defines a command to run the System Identification (SysId) routine for the arm mechanism. It configures the test with specified maximum voltage, voltage ramp rate, and test duration for both forward and reverse dynamic and quasistatic tests. This is crucial for determining accurate feedforward constants for precise arm control. ```java import static edu.wpi.first.units.Units.*; import edu.wpi.first.wpilibj2.command.Command; import yams.mechanisms.positional.Arm; // Arm SysId - runs forward/reverse dynamic and quasistatic tests public Command armSysId() { return arm.sysId( Volts.of(4), // Maximum voltage Volts.of(1).per(Second), // Voltage ramp rate Seconds.of(10) // Test duration per direction ); } ``` -------------------------------- ### Wrap Hardware Motor Controllers with YAMS Source: https://context7.com/yet-another-software-suite/yams/llms.txt Demonstrates how to create YAMS `SmartMotorController` instances by wrapping hardware-specific motor controllers from REV (SparkMax), CTRE (TalonFX, TalonFXS), and Thriftybot (Nova). This provides a unified API across different vendors. ```java import com.revrobotics.spark.SparkMax; import com.revrobotics.spark.SparkLowLevel.MotorType; import com.ctre.phoenix6.hardware.TalonFX; import com.thethriftybot.devices.ThriftyNova; import edu.wpi.first.math.system.plant.DCMotor; import yams.motorcontrollers.SmartMotorController; import yams.motorcontrollers.local.SparkWrapper; import yams.motorcontrollers.local.NovaWrapper; import yams.motorcontrollers.remote.TalonFXWrapper; import yams.motorcontrollers.remote.TalonFXSWrapper; // REV SparkMax with NEO motor SparkMax sparkMax = new SparkMax(1, MotorType.kBrushless); SmartMotorController sparkMotor = new SparkWrapper(sparkMax, DCMotor.getNEO(1), motorConfig); // CTRE TalonFX with Kraken X60 TalonFX talonFX = new TalonFX(2); SmartMotorController talonMotor = new TalonFXWrapper(talonFX, DCMotor.getKrakenX60(1), motorConfig); // CTRE TalonFXS with NEO motor TalonFXS talonFXS = new TalonFXS(3); SmartMotorController talonSMotor = new TalonFXSWrapper(talonFXS, DCMotor.getNEO(1), motorConfig); // Thrifty Nova with NEO motor ThriftyNova nova = new ThriftyNova(4); SmartMotorController novaMotor = new NovaWrapper(nova, DCMotor.getNEO(1), motorConfig); ``` -------------------------------- ### Configure Continuous Rotation with Wrapping (Java) Source: https://context7.com/yet-another-software-suite/yams/llms.txt This Java code configures a motor controller for continuous rotation, enabling wrapping between specified angle limits. It utilizes the SmartMotorControllerConfig class to set PID gains, gearing, and continuous wrapping parameters, allowing the controller to find the shortest path between angles. ```java import yams.motorcontrollers.SmartMotorControllerConfig; import yams.mechanisms.config.ArmConfig; import yams.mechanisms.positional.Arm; // Arm can be used as Pivot/Turret import static edu.wpi.first.units.Units.*; // Configure for continuous rotation with wrapping SmartMotorControllerConfig turretConfig = new SmartMotorControllerConfig(this) .withClosedLoopController(2, 0, 0.1, DegreesPerSecond.of(360), DegreesPerSecondPerSecond.of(180)) .withGearing(new MechanismGearing(100)) // High reduction for turret // Enable continuous input wrapping // PID will take shortest path between -180 and 180 .withContinuousWrapping(Degrees.of(-180), Degrees.of(180)) .withIdleMode(MotorMode.BRAKE) .withStatorCurrentLimit(Amps.of(30)) .withControlMode(ControlMode.CLOSED_LOOP); // Turret can use Arm mechanism (no gravity compensation needed) // Or use Pivot for more turret-specific features ArmConfig turretArmConfig = new ArmConfig(turretMotor) .withLength(Inches.of(6)) .withMass(Pounds.of(3)) // Wrapping is handled by motor config .withHardLimit(Degrees.of(-360), Degrees.of(360)); Arm turret = new Arm(turretArmConfig); // Commands will take shortest path public Command aimToAngle(Angle targetAngle) { return turret.setAngle(targetAngle); } ```