Changelog
New updates and improvements
Unreleased
These changes are on master and will ship in the next release.
Added
Commands v2
SwerveDrive.driveToPose(Pose2d, Distance, Angle), which ends once the robot is within the tolerances, andisNear(Pose2d, Distance, Angle)andstop(). These were only on the Commands v3SwerveDrive;isNearandstopnow live on the sharedSwerveDrive.SmartMechanism.getMeasurementSetpoint(): the mechanism's position setpoint as aDistance, converted with the mechanism circumference. It is empty when there is no position setpoint or no circumference is configured.EasyCRT.solve(Angle, Angle)/EasyCRT::Solve(...): solve for travel centered on zero, with asolve(a, b, minAngle, maxAngle)overload for other travel.EasyCRT.getUniqueRange()/EasyCRT::GetUniqueRange(): how far the shared gear can turn before the two encoder readings repeat.A
Turretthat seeds its motor fromEasyCRTat startup, with a unit test, in theBroncBotz_2026examples for Commands v2 and Commands v3.
Changed
Behavior change: the Commands v2
SwerveDrive.driveToPose(Pose2d)now stops the drive when it ends or is interrupted, as the Commands v3 one already did. It used to leave the last commanded speeds in place.Behavior change:
withTemperatureCutoffnow follows where the closed loop runs, because the cutoff is only checked by the closed loop on the SystemCore (Java and C++). On a SPARK MAX or SPARK Flex it needs an exponential profile or an LQR controller: a trapezoidal profile with a cutoff used to be accepted and silently ignored, and now throws at startup. On a TalonFX or TalonFXS it is now accepted with an LQR controller, where it used to always throw.Breaking:
EasyCRTis now built from three tooth counts and solves from two readings (Java and C++). The match tolerance is derived from the tooth counts, and a reading pair that does not agree returns an empty result.Breaking (Java):
DifferentialMechanism.set(...)now takes(tilt, twist)like every other method, and mixes them with the same signs assetPosition(left = tilt + twist, right = tilt - twist). It took(twist, tilt)and drove the twist the opposite way. Swap the arguments if you called it as(twist, tilt).EasyCRTConfigGuide.mdis replaced byEasyCRTGuide.md, which explains how to choose tooth counts, zero and invert the encoders, handle gear trains, and check your margin yourself.MIGRATION.mdnow covers the whole 2026 to 2027 upgrade.
Removed
Breaking:
EasyCRTConfig, including its gearing helpers, offset and inversion setters, match tolerance, and gear recommender, and theEasyCRTstatus, error and iteration getters.
Fixed
ExponentialProfilePIDController.createConstraints(maxVolts, maxVelocity, maxAcceleration)passed kV and kA as state space terms, which gave an unstable profile with a negative maximum velocity. It now builds the same constraints asSmartMotorControllerConfig.withExponentialProfile(Voltage, AngularVelocity, AngularAcceleration).Simulation:
Arm,Elevator,Pivot,FlyWheel,DoubleJointedArmandDifferentialMechanismno longer replace the sharedBatterySimvoltage with one computed from their own current draw alone. The simulated battery now sags from every mechanism's combined draw, andenableDischarge(...)is respected, no matter which mechanism iterates last (Java and C++).simple_robot-2027examples: the simulated gyro yaw was set in radians instead of degrees, and the elevators were missing the carriage weight and starting position thatElevatorrequires in simulation.C++
cpptestexample: the field-relativeSwerveInputStreamoutput is converted to robot-relative beforeSwerveDrive::Drive.GearBoxJavadoc:fromTeeth(...)is a train of meshing gears, sofromTeeth(12, 60, 12, 60)is 5:1, not 25:1.
2026.10.9
Added
NovaWrapper(Javayams.core.motorcontrollers.local.NovaWrapper, C++yams/motorcontrollers/local/NovaWrapper.hpp): aSmartMotorControllerfor the Thrifty Nova, with simulation, telemetry, and alert support. Requires the ThriftyLib 2027 vendordep.SmartMotorController.getRelativeMechanismPosition()/getRelativeMechanismVelocity()(Java) andGetRelativeMechanismPosition()/GetRelativeMechanismVelocity()(C++): read only the relative (motor) encoder, even when an external encoder is configured.getMechanismPosition()/getMechanismVelocity()still prefer the external encoder.C++:
SmartMotorController,SmartMotorControllerConfig, simulation, telemetry,SparkWrapper,TalonFXWrapper, andTalonFXSWrappernow match the Java API.
Changed
Breaking:
getExternalEncoderPosition()/getExternalEncoderVelocity()renamed togetExternalEncoderMechanismPosition()/getExternalEncoderMechanismVelocity()(C++:GetExternalEncoderMechanismPosition()/GetExternalEncoderMechanismVelocity()).Alerts now refer to the SystemCore instead of the RIO.
Maven artifacts (Java and C++) and the vendordep JSONs are now served from
https://cdn.yassrobotics.com/, so networks that blockgithub.iocan still install and update YAMS. Reinstall the vendordep from the new URL:Command frameworkVendordep URLCommands v2
https://cdn.yassrobotics.com/yams_commands2.jsonCommands v3
https://cdn.yassrobotics.com/yams_commands3.json
2026.10.8
Added
SwerveInputStream.withTelemetry(String, TelemetryVerbosity)(Java) /SwerveInputStream::WithTelemetry(...)(C++): publishes the stream's drive mode (LOW), its configuration (MID), and editable copies of the configuration plus aLive Tuningcommand (HIGH). The stream publishes every time it is read, so there is nothing to call each loop. Access it withgetTelemetry()/GetTelemetry().
2026.10.5
Added
GyroAxisenum (yams.core.mechanisms.config.enums:YAWdefault,ROLL,PITCH) andSwerveDriveConfig.withGyroHeadingAxis(GyroAxis)(Java): use a gyro mounted on its side as the heading source. The gyro attitude is rotated into the robot frame, so the heading keeps its full range and anti tipping reads the robot's tilt.SwerveModule.lock(Rotation2d)andSwerveModuleConfig.getLockedState(...)(Java).
Changed
Breaking (Java):
SwerveDriveConfig.getGyroAngle()andSwerveDrive.getGyroAngle()are removed. UsegetGyroRotation3d(). The heading now wraps at +/-180 degrees instead of counting continuously.Vendordeps now declare
requiresandconflictsWith, so the Commands v2 and Commands v3 variants of YAMS cannot be installed together.
Fixed
Java:
SwerveDrive.lockPose()now turns the wheels into the X pattern. Previously the module velocity deadband replaced the zero speed states, so the wheels held their current angle.Java: Simulated SPARKs now close their loop at 1 kHz like a real SPARK (and like simulated Talons), instead of once per 20 ms simulation period. Integral and derivative gains are no longer scaled by the loop period in simulation. Real robot gain conversion is unchanged.
2026.10.3
YAMS now targets WPILib 2027 (2027_alpha7) and the SystemCore. Robot code built against earlier releases needs to be migrated; see Upgrading from 2026 to 2027 in the YAMS documentation.
Added
Two vendordeps, one per WPILib command framework. Install exactly one:
Commands v2:
yams_commands2.json, with command bindings inyams.commands2.*.Commands v3:
yams_commands3.json, with command bindings inyams.commands3.*(Java only, since WPILib has no C++ Commands v3).
An offline installer,
<version>-YAMS-Offline.zip, attached to each GitHub release. Extract it into your WPILib2027_alpha7folder, then install the vendordep offline.Example projects for both command frameworks under
examples/commands2andexamples/commands3.
Changed
Breaking: WPILib 2027 packages (
org.wpilib.*in Java,wpi::in C++), Java 25, Phoenix 6 2027, and REVLib 2027.Breaking (Java): packages split into
yams.core.*(mechanism and motor controller state, simulation, telemetry, configs, wrappers, gearing, math, units) andyams.commands2.*/yams.commands3.*(subsystem bound mechanisms,SmartMotorControllerConfig,SwerveDriveConfig,SwerveDrive,SwerveInputStream, and theirCommand/Triggerfactories). Robot code imports mechanisms and those configs from the command framework package.Breaking (Java): the
isNear(...)Triggerfactory is renamednear(...), matchinggte(),lte(),between(),max(), andmin(). The booleanisNear(...)check is unchanged.Live tuning registers itself when telemetry is
TelemetryVerbosity.HIGH. In other cases, opt in withSmartMotorControllerConfig.setupLiveTuning().Breaking:
withIdleMode(MotorMode)is renamedwithZeroPower(MotorMode)(C++:WithZeroPower).
2026.9.16
Changed
Breaking:
SmartMotorControllerConfig.getZeroOffset()is renamedgetExternalEncoderZeroOffset(), matching the existingwithExternalEncoderZeroOffset(...)setter.
Fixed
TalonFXWrapperno longer applies the zero offset to the rotor as well, which double applied the offset.
2026.9.10
Added
SmartMotorController.setVelocity(AngularVelocity, Force)/setVelocity(LinearVelocity, Force)andSwerveDrive.setRobotRelativeChassisSpeeds(..., Force[]): pass a feedforward force (for example from a path follower) straight through to the drive motors.SwerveModulegained the matching overloads.SwerveDriveConfig.clone().SmartMotorControllerConfig.withSimulationPeriod(Time)(C++:WithSimulationPeriod).SimSupplier.getStatorCurrent()/getSupplyCurrent().The gyro angular velocity is derived from the heading when no gyro velocity supplier is set, so angular velocity scaling works without one.
Fixed
The simulated swerve pose was integrated twice, so it moved robot relative instead of field relative.
2026.9.2
Added
Advanced telemetry for
SwerveDriveandSwerveModule:SwerveDriveConfig.withTelemetry(String, TelemetryVerbosity)/withTelemetry(String, SwerveDriveTelemetryConfig)andSwerveModuleConfig.withTelemetry(String, SwerveModuleTelemetryConfig), with more live tunable fields.SwerveDrive.setTranslationPID(PIDController),setRotationPID(PIDController),driveToPoseSetpoint(Pose2d), andgetModules().SwerveModule.getRawAbsoluteEncoderAngle().
Changed
SwerveDrive.resetAzimuthPID()is renamedresetRotationPID().SwerveInputStreamtakes its maximum velocities from theSwerveDrive.
Removed
Breaking:
SwerveDriveConfig.withDataLogName(...)andSwerveModuleConfig.withDataLogName(...). Use thewithTelemetry(...)overloads above.Breaking: drive to pose in
SwerveInputStream, includingatTargetPose(double). UseSwerveDrive.driveToPoseSetpoint(Pose2d).
Fixed
The absolute encoder supplier is zeroed when an absolute encoder is supplied to
SwerveModuleConfig.
2026.8.26
Added
SwerveDrive.getSimPose()(Java) /SwerveDrive::GetSimPose()(C++) — a ground-truth simulated pose that assumes every module reached its last-commandedSwerveModuleStateperfectly. It's integrated insimIterate()/SimIterate()from the desired (not measured) module states via aTwist2d/frc::Twist2d, independent of the noisy, gyro/odometry-fused pose returned bygetPose()/GetPose().resetOdometry(...)/ResetOdometry(...)also snaps it to the given pose, keeping it in sync with the fused estimate. Intended as a "known truth" source for simulated vision systems.SwerveDrive.getRobotRelativeChassisSpeedsFromState(SwerveModuleState[])(Java) /SwerveDrive::GetRobotRelativeChassisSpeedsFromState(...)(C++) — converts an arbitrary set of module states back into robot-relative chassis speeds.SwerveDrive.getDesiredModuleStates()(Java) /SwerveDrive::GetDesiredModuleStates()(C++) — returns the last-commanded module states, mirroring the existinggetDesiredChassisSpeeds()/GetDesiredChassisSpeeds().
Removed
Breaking:
SwerveDrive.getPoseEstimator()(Java) /SwerveDrive::GetPoseEstimator()(C++) — direct access to the underlyingSwerveDrivePoseEstimatorwas too easy to misuse (callingupdate(...)/resetPosition(...)/resetPose(...)on it directly corrupts the pose estimate or desyncs it from the drive's gyro offset). UsegetPose(),resetOdometry(Pose2d), andaddVisionMeasurement(...)instead.
2026.8.20
Added
BatterySim(Java & C++) now models a single, shared robot battery in simulation. Every simulatedSmartMotorController— via itsSimSupplier(ArmSimSupplier,ElevatorSimSupplier,DCMotorSimSupplier) or hardware wrapper (SparkWrapper,TalonFXWrapper,TalonFXSWrapper) — reports its own current draw into one combined loaded-voltage calculation, instead of each mechanism computingRoboRioSimvoltage sag from only its own current draw and overwriting the others.BatterySim.enableDischarge(double, Voltage, Resistance)(Java) /BatterySim::EnableDischarge(double, units::volt_t, units::ohm_t)(C++) — optional realistic battery discharge simulation. Current draw is integrated into amp-hours consumed over time, sagging the open-circuit voltage and raising internal resistance as the battery empties, following a typical FRC sealed lead-acid discharge curve. Also addeddisableDischarge()/DisableDischarge(),resetDischarge()/ResetDischarge(), andgetStateOfCharge()/GetStateOfCharge().BatterySim.replaceSOCInterpolation(InterpolatingDoubleTreeMap)(Java) /BatterySim::ReplaceSOCInterpolation(const std::map<double, double>&)(C++) — swap in a custom state-of-charge → voltage discharge curve, for modeling a well-used competition battery, a different chemistry, or a curve measured from a real battery on a load tester.
Changed
C++:
Arm,Elevator,Pivot, andFlyWheelno longer callRoboRioSim::SetVInVoltage(...)directly from their own current draw. Voltage now comes from the sharedBatterySim, so combined load across multiple mechanisms sags the simulated bus voltage realistically instead of only reflecting whichever mechanism updated last.
2026.8.18
Added
SwerveDriveConfig.getDiscretization()— returns the discretization time used byoptimizeRobotRelativeChassisSpeeds(...), automatically preferring the simulation-specific override fromwithSimDiscretizationTime(...)when running in simulation.
2026.8.17
Added
SmartMotorControllerTelemetryConfig.withCustom(DoubleTelemetryField, boolean)/withCustom(BooleanTelemetryField, boolean)(Java, plus array overloads) — an escape hatch to enable or disable a telemetry field directly when there isn't a dedicatedwith*()setter for it yet.
Changed
Telemetry now only publishes fields that are actually enabled, instead of publishing every registered field regardless of its enabled state.
Java & C++: Swerve module telemetry now nests each module under
swerve/modules/<name>/...instead ofswerve/<name>/..., removing a double/that showed up in some NetworkTables/DataLog paths.
Fixed
Java & C++: Cosine compensation in
SwerveModuleConfignow reads the azimuth angle once per control cycle and reuses that same value for both state optimization and the cosine scalar, instead of re-querying the absolute encoder separately for each step. Previously those two reads could disagree, which showed up as skewed drive speed or the wheel losing its commanded direction right around a 180° heading error.
2026.8.16
Added
MechanismTelemetryis now the primary coordinator of all mechanism telemetry, and gained explicit DataLog support alongside NetworkTables.SwerveDriveConfig.withDataLogName(String)/getDataLogName()— logs the drive's pose, gyro, and chassis speeds/module states to a WPILib DataLog under the given name, in addition to NetworkTables.SwerveModuleConfig.withDataLogName(String)/getDataLogName()— logs that module's absolute encoder angle to a WPILib DataLog under the given name, in addition to NetworkTables.
Changed
SwerveModulenow shares a singleMechanismTelemetryinstance (and loop timer) across its drive and azimuth motors instead of each motor re-running its own telemetry setup.
2026.7.10
Added
C++:
SmartMotorControllerConfig::WithFeedforward(...)— a single overloaded setter that takes a WPILib feedforward object directly (frc::ArmFeedforward,frc::ElevatorFeedforward, orfrc::SimpleMotorFeedforward<units::turns>), replacing the setters removed below.
Removed
Breaking (C++):
SmartMotorControllerConfigremovedWithArmFeedforward,WithElevatorFeedforward,WithSimpleFeedforward, and the standaloneWithKp/WithKi/WithKd/WithKs/WithKv/WithKa/WithKgsetters. See the migration note above.
2026.7.09
Added
SwerveModulenow publishes the azimuth absolute encoder angle to telemetry.Added
SwerveModuleConfig.getAbsoluteEncoderSupplier()andgetRawAbsoluteEncoderAngle()(Java) to expose the absolute encoder reading directly, including a variant with the configured zero offset stripped out. Swerve telemetry now reports this raw, offset-free angle.
Changed
SwerveModule.setSwerveModuleState(SwerveModuleState)now returns the optimizedSwerveModuleStateinstead ofvoid.
Fixed
SwerveDrive's desired module states now reflect the actual post-optimizationSwerveModuleStatereturned bysetSwerveModuleState(...)above, instead of the pre-optimization input — fixes desired-state telemetry/logging showing the wrong angle after a flip.
2026.7.06
Fixed
Fixed
SwerveModuleConfig's state optimizer entering an infinite re-optimization loop right at a flip boundary. The flip decision now debounces against the last commanded angle instead of being re-derived from the live absolute encoder every loop.
2026.6.25.1
Added
SmartMotorControllerCommandRegistrynow throwsIllegalStateExceptionwhen two differentSubsysteminstances register commands under the same name, and addsremoveCommands(Subsystem)to clear a subsystem's registered commands (e.g. between tests).
2026.6.25
Added
Added a full C++ port of YAMS: mechanisms (
Arm,Elevator,Pivot,FlyWheel,DoubleFlyWheel,DifferentialDrive), motor controller wrappers (Spark, TalonFX, TalonFXS), telemetry, units, and unit tests, mirroring the existing Java API.Added
SwerveDriveandSwerveInputStream(new in both Java and C++).Added Doxygen documentation generation for the C++ API.
Removed
Breaking: Removed ThriftyNova motor controller support (
NovaWrapper) from the Java API; there is no direct replacement.
Changed
Adopted
wpiformatfor consistent Java/C++ formatting across the codebase.
2026.1.17
Empirical Method for CRT by @mjjbycicle in #68
SmartMotorController Control Request Fix by @thenetworkgrinch in #69
Fixed the DCMotorSim to work with the correct units by @thenetworkgrinch in #72
Swerve drive sysId by @thenetworkgrinch in #73
Inversions shouldn't apply in Simulation by @thenetworkgrinch in #74
MaybeCRT by @alexalex02 in #70
Kitbot added by @nobodyhere4 in #62
2026.1.16
Fixed non-profiled Closed Loop Control in TalonFXS, TalonFX, and SparkMax.
Added
Conversionfor ThriftyNova encoder readings to account for encoder ticks.Fixed
SwerveDrivetemplate.Added Shoot on the move example.
Added Hood + Turret example (by 9738)
2026.1.4
Added Imperial
YUnits.PoundSquareInchesYUnits.PoundsSquareFeetDeprecated
withMOI(double)in favor ofwithMOI(MomentOfInertia).Added
withMOI(MomentOfInertia)
2026.1.2
Added
SmartMotorControllerConfig().clone()to allow for clonable configs.Added
MechanismConfig.withSmartMotorController()for all mechanism and null param constructorsAdded
SensorData.set()overloads'Trapazoidal' Spelling Error Correction by @akhaled247 in #56
REV 2026 with Feedforwards and PIDs by @thenetworkgrinch in #55
Added the option to loosely coupled followers. by @thenetworkgrinch in #58
Allow for independently created config classes for constants files. by @thenetworkgrinch in #59
Changed
SwerveInputStream.copy()toSwerveInputStream.clone()Modified
SmartMotorControllerConfigto allow for creation without aSubsystembut still require a subsystem eventually.Modified
SmartMotorControllertelemetry to only create Live Tuning and other useful commands if tunable setpoints are not enabled.Added
MechanismGearing(double...)overloadAdded DifferentialDrive example.
2025.12.22
Added the ability to use multiple SMC's with Live Tuning in one subsystem
Added AdvantageKit example
Modified
SwerveDriveto support AdvantageKit better.Updated Telemetry Setup for standalone SMC's to be automatic.
Added Units to Telemetry Fields.
Added DoubleFlyWheel example.
Fixed ElevatorConfig comments saying "length" instead of "angle" #54
Added support for user moment of inertia in ExponentialProfilePIDController #50
Improved Exponential Elevator Examples #46
Added ExponentialProfile.
Add MapleSim Integration to SwerveDrive by @konnorreynolds in #49
2025.11.05
Renamed
.withExternalGearingto.withExternalEncoderGearing.Added several new easy commands, like "Up", "Down", and "ZeroEncoder".
Moved commands to "SmartDashboard/Mechanisms/MECHNAME/MOTORNAME/Commands/".
Fixed immutable SensorData fields.
Fixed
SparkWrapper.setEncoderPositionto set the sim position correctly.
2025.11.02
Renamed
SmartMotorControllerConfig.withZeroOffsettoSmartMotorControllerConfig.withExternalEncoderZeroOffset
2025.10.31
Updated
SwerveInputStreamto use.with*for shared styling.Added
swerve_driveexample.
2025.10.29
Added setpoint visualization to Arm Mechanism2d
Fixed ArmFeedforward calculation on Spark's, and Nova's. Changed velocity calculation from Rotations to Radians. Found by 8114 (The Rocketeers)
2025.10.27
Renamed
ShootertoFlyWheelAdded Loop Time to each mechanism.
2025.10.14
Added
YUnitswhich allows you to include more exotic units in your code.Added
Sensors which allow you to seamlessly fetch automatically modified sensor data during sim and real data when running IRL.
Last updated
Was this helpful?