Choreo v2025.0.0-beta-8
Pre-releaseChoreo v2025.0.0-beta-8
Please read through the post entirely, since many breaking changes have been made.
This release focuses on internal infrastructure and breaking API improvements in ChoreoLib.
v2025.0.0-beta-8 is built for WPILib beta 3. It is likely mostly identical to the first full 2025 release of Choreo, which will be released after the WPILib full 2025 release.
NOTE: This changelog describes changes since beta-6.
https://choreo.autos : New Docs Domain
We recently registered the domain choreo.autos
to replace sleipnirgroup.github.io/Choreo
. We have set up the latter to redirect to the former. Documentation is still being updated over the coming weeks before kickoff.
Document Schemas and Auto Upgrade
The version control in the .chor and .traj schemas is no longer a semver string. It is now an integer, starting at 1, and is different for .traj and .chor. A string in the version
key is interpreted as version 0.
The Choreo app is able to automatically load non-current versions of both files (including existing files from beta-6) and upgrade them to the current schema. Usually, this will require regeneration, depending on the changes in the update.
This is a feature Choreo had in 2024, but it has been completely reimplemented.
For contributors, a guide to defining new schema versions has been added here.
The three ChoreoLib languages will not automatically upgrade old trajectories when reading them.
Removal of C++ AutoFactory API
Unfortunately, we found many severe bugs in our port of the Java AutoFactory API to C++, and decided to delete that attempt. We apologize for the inconvenience.
ChoreoLib Changes (Mostly Breaking)
See below for details on these changes.
General
- Changes to how Choreo handles alliance flipping. See below.
- Many bugs with the sample flipping algorithms have been fixed.
- ChoreoLib no longer reads the .chor file to know what sample type to expect, or what the differential track width is, as needed for
DifferentialSample.getChassisSpeeds()
. This information is now in the .traj file. Details below.
Raw API
Trajectory
- [Java]
Trajectory.getInitialSample()
,getFinalSample()
,getInitialPose()
,getFinalPose()
,sampleAt()
now returnOptional<Pose2d>
instead of a potentially nullPose2d
. This matches prior behavior of the other two languages (C++ withstd::optional
and Python with returningNone
) - [C++]
Trajectory::GetInitialState()
is nowGetInitialSample()
. - [Java, C++]
Trajectory.getInitialSample()/GetInitialSample()
andgetFinalSample()/GetFinalSample()
now take aboolean
parameter that is true if the sample should be returned flipped. This does not check the current alliance; if the parameter is true, the sample will be flipped. - [Java]
Trajectory.sampleArray()
has been removed due to causing runtime crashes and being type-unsafe.
ProjectFile
Removed, since it is no longer needed.
DifferentialSample
A new field omega
was added, instead of calculating angular velocity from vl
, vr
and the trackwidth from the robot config in the .chor file.
Java Auto API
- Driver Station warnings from ChoreoLib have moved to use the new Alerts API.
New Recommended Practices
- The
AutoRoutine.trajectory(String name)
has been added and is the new recommended way to loadAutoTrajectories
, instead ofAutoFactory.trajectory(String name, AutoRoutine routine)
. Trigger AutoRoutine.observe(BooleanSupplier condition)
has been added. This can be used to create Triggers that are part of theAutoRoutine
and will only be polled during the routine. It can also be used to "sanitize" Triggers that are used elsewhere. See below for details on why this is necessary.- To help handle odometry resetting when the start pose depends on alliance, AutoFactory and AutoTrajectory now have
resetOdometry
capability. More details in the changelog just below.
AutoLoop
choreo.auto.AutoLoop
is nowchoreo.auto.AutoRoutine
.AutoFactory.newLoop()
andvoidLoop()
were renamed tonewRoutine()
andvoidRoutine()
.
AutoRoutine.cmd()
will end immediately with a console warning if alliance flipping is enabled and the alliance is not known on command initialize.AutoLoop.enabled()
was replaces withAutoRoutine.active()
.AutoLoop.poll()
will return immediately if the alliance is needed for flipping and not known. This is the same behavior as ifpoll()
is called on a killed routine, or not while enabled and in autonomous.Trigger anyDone(AutoTrajectory... trajectories)
has been added, which creates a trigger that is true for one cycle when any of the input trajectories finishes. A variant also exists that applies a delay to the rising edge.Trigger anyActive(AutoTrajectory... trajectories)
creates a trigger that is true whenever any input trajectory is active.
AutoTrajectory
AutoTrajectory.done()
behavior has changed. The trigger will become true for one cycle when the trajectory completes without being interrupted. Previously, interrupted trajectories would still fire thedone()
trigger.AutoTrajectory.getInitialPose()
andgetFinalPose()
will now returnOptional.empty()
if alliance flipping is enabled and the alliance is not known.- Removed
AutoTrajectory.atTimeAndPlace()
. Usetrajectory.atTime(String eventName).and(trajectory.atPose(String eventName))
or similar. AutoTrajectory.atPose(...)
did not check the rotation portion of the pose.atPose(...)
now takes a rotation tolerance (default 3 degrees).- For the previous behavior of just checking the translation,
AutoTrajectory.atTranslation()
has been added, which is similar toatPose()
but acceptsTranslation2d
s. AutoTrajectory.atPose(...)
andatTranslation(..)
now properly flip the target pose/translation every time the Trigger is checked, based on the current alliance if alliance flipping is enabled. If the alliance is unknown and flipping is enabled, the Trigger will be false.AutoTrajectory.collectEventPoses
now returns anArrayList<Supplier<Optional<Pose2d>>>
of poses that flip based on the current alliance and flipping enabled status.
AutoFactory
- The Choreo controller function now only takes a
SwerveSample
orDifferentialSample
, not an additionalPose2d
for the current pose. AutoFactory.clearCache()
was replaced withAutoFactory.cache().clear()
since the user can now access the trajectory cache.- A new parameter
Consumer<Pose2d> resetOdometry
has been added. This is used withAutoTrajectory
to create aCommand
that calls theresetOdometry
callback with the first pose of trajectoryCommand()
has been renamed totrajectoryCmd()
trajectoryLogger
is no longer anOptional
. It is a no-op if left unspecified.
AutoChooser
-
AutoChooser
now implementsSendable
, so can be logged withSmartDashboard.putData()
or similar. -
The API to add options has changed.
- One option is to call
addRoutine(String name, Supplier<AutoRoutine> generator)
. - The other is to call
addCmd(String name, Supplier<Command> generator)
. - Both versions will call the given function when the option becomes selected.
- One option is to call
-
AutoChooser
's options are now functions that consume nothing and return anAutoRoutine
or aCommand
. It is expected that users will call to theirAutoFactory
instance like they would a subsystem, instead of having it passed into the function. -
The constructor is now
new AutoChooser()
, since the table name is handled by logging it like aSendable
, and the chooser does not need to pass a factory into the generator functions. -
AutoChooser.getSelectedAutoRoutine()
has been removed. -
To run the selected auto, use
Command selectedCommand()
orCommand selectedCommandScheduler()
.
- The former is the analogue to the
SendableChooser
approach of getting the selected command and directly scheduling it inautonomousInit()
. - The latter returns a deferred Command. Use this if binding autonomous commands to run on
autonomousInit()
withRobotModeTriggers.autonomous()
, since it does not evaluate the selected command as of the time of binding.
AutoChooser
will only update if the DriverStation is connected (so that the alliance is known).
Changes to alliance-based trajectory flipping
Several issues were identified with the way ChoreoLib, especially the Java higher-level API, handled alliance flipping, given that the alliance can change after auto routines are created, and the alliance can be unknown before the call of autonmousInit
.
- The higher-level API now uses
Optional<Pose2d>
andSupplier<Optional<Pose2d>>
instead ofPose2d
in many places, to better represent poses that depend on the currently selected alliance. AutoFactory
/Choreo.createAutoFactory
no longer take aBooleanSupplier mirrorTrajectory
that returns true when trajectories should be flipped. Instead they take aBooleanSupplier useAllianceFlipping
to enable alliance-based flipping in general and a separateSupplier<Optional<Alliance>>
that defaults toDriverStation::getAlliance()
. The BooleanSupplier was moved elsewhere in the parameter list to force a compiler error.
Trigger Sanitization
AutoRoutine.observe
was added to help prevent problems where a Trigger on the default CommandScheduler loop is used when creating an AutoRoutine
. This is especially easy to mess up when that Trigger is combined with a Trigger on the AutoRoutine
event loop, using decorators like .and()
, .or()
, etc.
Combined Triggers are polled on the first Trigger's loop. Thus if the default-loop Trigger is first in a combination, the combined trigger will be polled every loop, even if the auto routine is not running and has not been run.
Example:
Pose2d shotLocation = ...;
// trajectory.atPose(...) is only polled during the AutoRoutine.
Trigger atShotLocation = trajectory.atPose("shoot", 0.5);
// shooter has a public static Trigger hasGamepiece.
// This is on the default loop
Trigger hasGamepiece = shooter.hasGamepiece;
// Compare:
// This is polled every loop during auto and teleop!
// If the robot is at any "shoot" marker's location during teleop, the game piece will be shot.
hasGamepiece.and(atShotLocation).onTrue(shooter.shoot()); // BAD
// RECOMMENDED:
// To fix this, use AutoRoutine.observe() to make a hasGamepiece condition that is routine-specific:
Trigger hasGamepiece = routine.observe(shooter.hasGamepiece)
// ALTERNATIVES:
// Put the AutoRoutine-specific Trigger first:
atShotLocation.and(hasGamepiece).onTrue(shooter.shoot());
// Or, access the routine's EventLoop directly to create a new Trigger
shooter.hasGamepiece(routine.loop()).and(atShotLocation).onTrue(shooter.shoot());
File Schema Changes
.TRAJ SCHEMA VERSION: 1
The field trajectory.sampleType
will be either "Swerve"
or "Differential"
, according to the type of trajectory represented.
The field omega
in a differential sample was added, and represents the chassis angular velocity in radians per second, just like in the swerve sample.
.CHOR SCHEMA VERSION: 1
Added wheel coefficient of friction cof
as a robot configuration option. This helps constrain robot acceleration.
Choreo Changes
- Files written by Choreo now end with a newline, as required by formatters like wpiformat.
- Fixed a bug with control interval guessing between two translation waypoints.
- Added wheel coefficient of friction as a robot configuration option. This helps constrain robot acceleration.
- Reimplemented expression input boxes to fix bugs with undo histories
- Expression input and non-expression numerical input boxes no longer select all the contents when being edited.
- Changed "zone" to "region" in the text relating to keep-in and keep-out regions.
- Fixed a bug with .chor file saving that prevented some changes from triggering resaves, even though the file contents would have changed.
- Motor Calculator Panel is removed from the robot config. It was broken and historically not very accurate anyway.
New Contributors
- @NoahStricker made their first contribution in #895
- @GrahamSH-LLK made their first contribution in #948
- @Daniel1464 made their first contribution in #947
- @bryceroethel made their first contribution in #980
- @mjansen4857 made their first contribution in #1034
- @roboteer5291 made their first contribution in #1036
Full Changelog: v2025.0.0-beta-6...v2025.0.0-beta-8