diff --git a/pathplannerlib/src/main/java/com/pathplanner/lib/commands/PathfindingCommand.java b/pathplannerlib/src/main/java/com/pathplanner/lib/commands/PathfindingCommand.java index 03e5a0240..d29737de7 100644 --- a/pathplannerlib/src/main/java/com/pathplanner/lib/commands/PathfindingCommand.java +++ b/pathplannerlib/src/main/java/com/pathplanner/lib/commands/PathfindingCommand.java @@ -1,5 +1,6 @@ package com.pathplanner.lib.commands; +import static edu.wpi.first.units.Units.Meters; import static edu.wpi.first.units.Units.MetersPerSecond; import com.pathplanner.lib.config.ModuleConfig; @@ -52,22 +53,31 @@ public class PathfindingCommand extends Command { private boolean finish = false; /** - * Constructs a new base pathfinding command that will generate a path towards the given path. + * Constructs a new base pathfinding command that will generate a path towards + * the given path. * - * @param targetPath the path to pathfind to - * @param constraints the path constraints to use while pathfinding - * @param poseSupplier a supplier for the robot's current pose - * @param speedsSupplier a supplier for the robot's current robot relative speeds - * @param output Output function that accepts robot-relative ChassisSpeeds and feedforwards for - * each drive motor. If using swerve, these feedforwards will be in FL, FR, BL, BR order. If - * using a differential drive, they will be in L, R order. - *
NOTE: These feedforwards are assuming unoptimized module states. When you optimize your - * module states, you will need to reverse the feedforwards for modules that have been flipped - * @param controller Path following controller that will be used to follow the path - * @param robotConfig The robot configuration - * @param shouldFlipPath Should the target path be flipped to the other side of the field? This - * will maintain a global blue alliance origin. - * @param requirements the subsystems required by this command + * @param targetPath the path to pathfind to + * @param constraints the path constraints to use while pathfinding + * @param poseSupplier a supplier for the robot's current pose + * @param speedsSupplier a supplier for the robot's current robot relative + * speeds + * @param output Output function that accepts robot-relative + * ChassisSpeeds and feedforwards for + * each drive motor. If using swerve, these feedforwards + * will be in FL, FR, BL, BR order. If + * using a differential drive, they will be in L, R order. + *
+ * NOTE: These feedforwards are assuming unoptimized + * module states. When you optimize your + * module states, you will need to reverse the + * feedforwards for modules that have been flipped + * @param controller Path following controller that will be used to follow + * the path + * @param robotConfig The robot configuration + * @param shouldFlipPath Should the target path be flipped to the other side of + * the field? This + * will maintain a global blue alliance origin. + * @param requirements the subsystems required by this command */ public PathfindingCommand( PathPlannerPath targetPath, @@ -86,7 +96,8 @@ public PathfindingCommand( Rotation2d targetRotation = Rotation2d.kZero; double goalEndVel = targetPath.getGlobalConstraints().maxVelocityMPS(); if (targetPath.isChoreoPath()) { - // Can get() here without issue since all choreo trajectories have ideal trajectories + // Can get() here without issue since all choreo trajectories have ideal + // trajectories PathPlannerTrajectory choreoTraj = targetPath.getIdealTrajectory(robotConfig).orElseThrow(); targetRotation = choreoTraj.getInitialState().pose.getRotation(); goalEndVel = choreoTraj.getInitialState().linearVelocity; @@ -101,8 +112,7 @@ public PathfindingCommand( this.targetPath = targetPath; this.targetPose = new Pose2d(this.targetPath.getPoint(0).position, targetRotation); - this.originalTargetPose = - new Pose2d(this.targetPose.getTranslation(), this.targetPose.getRotation()); + this.originalTargetPose = new Pose2d(this.targetPose.getTranslation(), this.targetPose.getRotation()); this.goalEndState = new GoalEndState(goalEndVel, targetRotation); this.constraints = constraints; this.controller = controller; @@ -117,22 +127,31 @@ public PathfindingCommand( } /** - * Constructs a new base pathfinding command that will generate a path towards the given pose. + * Constructs a new base pathfinding command that will generate a path towards + * the given pose. * - * @param targetPose the pose to pathfind to, the rotation component is only relevant for - * holonomic drive trains - * @param constraints the path constraints to use while pathfinding - * @param goalEndVel The goal end velocity when reaching the target pose - * @param poseSupplier a supplier for the robot's current pose - * @param speedsSupplier a supplier for the robot's current robot relative speeds - * @param output Output function that accepts robot-relative ChassisSpeeds and feedforwards for - * each drive motor. If using swerve, these feedforwards will be in FL, FR, BL, BR order. If - * using a differential drive, they will be in L, R order. - *
NOTE: These feedforwards are assuming unoptimized module states. When you optimize your - * module states, you will need to reverse the feedforwards for modules that have been flipped - * @param controller Path following controller that will be used to follow the path - * @param robotConfig The robot configuration - * @param requirements the subsystems required by this command + * @param targetPose the pose to pathfind to, the rotation component is only + * relevant for + * holonomic drive trains + * @param constraints the path constraints to use while pathfinding + * @param goalEndVel The goal end velocity when reaching the target pose + * @param poseSupplier a supplier for the robot's current pose + * @param speedsSupplier a supplier for the robot's current robot relative + * speeds + * @param output Output function that accepts robot-relative + * ChassisSpeeds and feedforwards for + * each drive motor. If using swerve, these feedforwards + * will be in FL, FR, BL, BR order. If + * using a differential drive, they will be in L, R order. + *
+ * NOTE: These feedforwards are assuming unoptimized + * module states. When you optimize your + * module states, you will need to reverse the + * feedforwards for modules that have been flipped + * @param controller Path following controller that will be used to follow + * the path + * @param robotConfig The robot configuration + * @param requirements the subsystems required by this command */ public PathfindingCommand( Pose2d targetPose, @@ -150,8 +169,7 @@ public PathfindingCommand( this.targetPath = null; this.targetPose = targetPose; - this.originalTargetPose = - new Pose2d(this.targetPose.getTranslation(), this.targetPose.getRotation()); + this.originalTargetPose = new Pose2d(this.targetPose.getTranslation(), this.targetPose.getRotation()); this.goalEndState = new GoalEndState(goalEndVel, targetPose.getRotation()); this.constraints = constraints; this.controller = controller; @@ -166,22 +184,31 @@ public PathfindingCommand( } /** - * Constructs a new base pathfinding command that will generate a path towards the given pose. + * Constructs a new base pathfinding command that will generate a path towards + * the given pose. * - * @param targetPose the pose to pathfind to, the rotation component is only relevant for - * holonomic drive trains - * @param constraints the path constraints to use while pathfinding - * @param goalEndVel The goal end velocity when reaching the target pose - * @param poseSupplier a supplier for the robot's current pose - * @param speedsSupplier a supplier for the robot's current robot relative speeds - * @param output Output function that accepts robot-relative ChassisSpeeds and feedforwards for - * each drive motor. If using swerve, these feedforwards will be in FL, FR, BL, BR order. If - * using a differential drive, they will be in L, R order. - *
NOTE: These feedforwards are assuming unoptimized module states. When you optimize your - * module states, you will need to reverse the feedforwards for modules that have been flipped - * @param controller Path following controller that will be used to follow the path - * @param robotConfig The robot configuration - * @param requirements the subsystems required by this command + * @param targetPose the pose to pathfind to, the rotation component is only + * relevant for + * holonomic drive trains + * @param constraints the path constraints to use while pathfinding + * @param goalEndVel The goal end velocity when reaching the target pose + * @param poseSupplier a supplier for the robot's current pose + * @param speedsSupplier a supplier for the robot's current robot relative + * speeds + * @param output Output function that accepts robot-relative + * ChassisSpeeds and feedforwards for + * each drive motor. If using swerve, these feedforwards + * will be in FL, FR, BL, BR order. If + * using a differential drive, they will be in L, R order. + *
+ * NOTE: These feedforwards are assuming unoptimized + * module states. When you optimize your + * module states, you will need to reverse the + * feedforwards for modules that have been flipped + * @param controller Path following controller that will be used to follow + * the path + * @param robotConfig The robot configuration + * @param requirements the subsystems required by this command */ public PathfindingCommand( Pose2d targetPose, @@ -206,21 +233,30 @@ public PathfindingCommand( } /** - * Constructs a new base pathfinding command that will generate a path towards the given pose. + * Constructs a new base pathfinding command that will generate a path towards + * the given pose. * - * @param targetPose the pose to pathfind to, the rotation component is only relevant for - * holonomic drive trains - * @param constraints the path constraints to use while pathfinding - * @param poseSupplier a supplier for the robot's current pose - * @param speedsSupplier a supplier for the robot's current robot relative speeds - * @param output Output function that accepts robot-relative ChassisSpeeds and feedforwards for - * each drive motor. If using swerve, these feedforwards will be in FL, FR, BL, BR order. If - * using a differential drive, they will be in L, R order. - *
NOTE: These feedforwards are assuming unoptimized module states. When you optimize your - * module states, you will need to reverse the feedforwards for modules that have been flipped - * @param controller Path following controller that will be used to follow the path - * @param robotConfig The robot configuration - * @param requirements the subsystems required by this command + * @param targetPose the pose to pathfind to, the rotation component is only + * relevant for + * holonomic drive trains + * @param constraints the path constraints to use while pathfinding + * @param poseSupplier a supplier for the robot's current pose + * @param speedsSupplier a supplier for the robot's current robot relative + * speeds + * @param output Output function that accepts robot-relative + * ChassisSpeeds and feedforwards for + * each drive motor. If using swerve, these feedforwards + * will be in FL, FR, BL, BR order. If + * using a differential drive, they will be in L, R order. + *
+ * NOTE: These feedforwards are assuming unoptimized
+ * module states. When you optimize your
+ * module states, you will need to reverse the
+ * feedforwards for modules that have been flipped
+ * @param controller Path following controller that will be used to follow
+ * the path
+ * @param robotConfig The robot configuration
+ * @param requirements the subsystems required by this command
*/
public PathfindingCommand(
Pose2d targetPose,
@@ -254,8 +290,7 @@ public void initialize() {
controller.reset(currentPose, speedsSupplier.get());
if (targetPath != null) {
- originalTargetPose =
- new Pose2d(this.targetPath.getPoint(0).position, originalTargetPose.getRotation());
+ originalTargetPose = new Pose2d(this.targetPath.getPoint(0).position, originalTargetPose.getRotation());
if (shouldFlipPath.getAsBoolean()) {
targetPose = FlippingUtil.flipFieldPose(this.originalTargetPose);
goalEndState = new GoalEndState(goalEndState.velocityMPS(), targetPose.getRotation());
@@ -284,20 +319,19 @@ public void execute() {
PPLibTelemetry.setCurrentPose(currentPose);
// Skip new paths if we are close to the end
- boolean skipUpdates =
- currentTrajectory != null
- && currentPose
- .getTranslation()
- .getDistance(currentTrajectory.getEndState().pose.getTranslation())
- < 2.0;
+ boolean skipUpdates = currentTrajectory != null
+ && currentPose
+ .getTranslation()
+ .getDistance(
+ currentTrajectory.getEndState().pose.getTranslation()) < Pathfinding.getMinDistanceToPathfind()
+ .in(Meters);
if (!skipUpdates && Pathfinding.isNewPathAvailable()) {
currentPath = Pathfinding.getCurrentPath(constraints, goalEndState);
if (currentPath != null) {
- currentTrajectory =
- new PathPlannerTrajectory(
- currentPath, currentSpeeds, currentPose.getRotation(), robotConfig);
+ currentTrajectory = new PathPlannerTrajectory(
+ currentPath, currentSpeeds, currentPose.getRotation(), robotConfig);
if (!Double.isFinite(currentTrajectory.getTotalTimeSeconds())) {
finish = true;
return;
@@ -307,18 +341,14 @@ public void execute() {
int closestState1Idx = 0;
int closestState2Idx = 1;
while (closestState2Idx < currentTrajectory.getStates().size() - 1) {
- double closest2Dist =
- currentTrajectory
- .getState(closestState2Idx)
- .pose
- .getTranslation()
- .getDistance(currentPose.getTranslation());
- double nextDist =
- currentTrajectory
- .getState(closestState2Idx + 1)
- .pose
- .getTranslation()
- .getDistance(currentPose.getTranslation());
+ double closest2Dist = currentTrajectory
+ .getState(closestState2Idx).pose
+ .getTranslation()
+ .getDistance(currentPose.getTranslation());
+ double nextDist = currentTrajectory
+ .getState(closestState2Idx + 1).pose
+ .getTranslation()
+ .getDistance(currentPose.getTranslation());
if (nextDist < closest2Dist) {
closestState1Idx++;
closestState2Idx++;
@@ -332,17 +362,17 @@ public void execute() {
var closestState1 = currentTrajectory.getState(closestState1Idx);
var closestState2 = currentTrajectory.getState(closestState2Idx);
- double d =
- closestState1.pose.getTranslation().getDistance(closestState2.pose.getTranslation());
- double t =
- (currentPose.getTranslation().getDistance(closestState1.pose.getTranslation())) / d;
+ double d = closestState1.pose.getTranslation().getDistance(closestState2.pose.getTranslation());
+ double t = (currentPose.getTranslation().getDistance(closestState1.pose.getTranslation())) / d;
t = MathUtil.clamp(t, 0.0, 1.0);
timeOffset = MathUtil.interpolate(closestState1.timeSeconds, closestState2.timeSeconds, t);
- // If the robot is stationary and at the start of the path, set the time offset to the next
+ // If the robot is stationary and at the start of the path, set the time offset
+ // to the next
// loop
- // This can prevent an issue where the robot will remain stationary if new paths come in
+ // This can prevent an issue where the robot will remain stationary if new paths
+ // come in
// every loop
if (timeOffset <= 0.02
&& Math.hypot(currentSpeeds.vxMetersPerSecond, currentSpeeds.vyMetersPerSecond) < 0.1) {
@@ -360,11 +390,9 @@ public void execute() {
if (currentTrajectory != null) {
var targetState = currentTrajectory.sample(timer.get() + timeOffset);
- ChassisSpeeds targetSpeeds =
- controller.calculateRobotRelativeSpeeds(currentPose, targetState);
+ ChassisSpeeds targetSpeeds = controller.calculateRobotRelativeSpeeds(currentPose, targetState);
- double currentVel =
- Math.hypot(currentSpeeds.vxMetersPerSecond, currentSpeeds.vyMetersPerSecond);
+ double currentVel = Math.hypot(currentSpeeds.vxMetersPerSecond, currentSpeeds.vyMetersPerSecond);
PPLibTelemetry.setCurrentPose(currentPose);
PathPlannerLogging.logCurrentPose(currentPose);
@@ -392,12 +420,10 @@ public boolean isFinished() {
Pose2d currentPose = poseSupplier.get();
ChassisSpeeds currentSpeeds = speedsSupplier.get();
- double currentVel =
- Math.hypot(currentSpeeds.vxMetersPerSecond, currentSpeeds.vyMetersPerSecond);
+ double currentVel = Math.hypot(currentSpeeds.vxMetersPerSecond, currentSpeeds.vyMetersPerSecond);
double stoppingDistance = Math.pow(currentVel, 2) / (2 * constraints.maxAccelerationMPSSq());
- return currentPose.getTranslation().getDistance(targetPose.getTranslation())
- <= stoppingDistance;
+ return currentPose.getTranslation().getDistance(targetPose.getTranslation()) <= stoppingDistance;
}
if (currentTrajectory != null) {
@@ -411,7 +437,8 @@ public boolean isFinished() {
public void end(boolean interrupted) {
timer.stop();
- // Only output 0 speeds when ending a path that is supposed to stop, this allows interrupting
+ // Only output 0 speeds when ending a path that is supposed to stop, this allows
+ // interrupting
// the command to smoothly transition into some auto-alignment routine
if (!interrupted && goalEndState.velocityMPS() < 0.1) {
output.accept(new ChassisSpeeds(), DriveFeedforwards.zeros(robotConfig.numModules));
@@ -427,19 +454,20 @@ public void end(boolean interrupted) {
*/
public static Command warmupCommand() {
return new PathfindingCommand(
- new Pose2d(15.0, 4.0, Rotation2d.k180deg),
- new PathConstraints(4, 3, 4, 4),
- () -> new Pose2d(1.5, 4, Rotation2d.kZero),
- ChassisSpeeds::new,
- (speeds, feedforwards) -> {},
- new PPHolonomicDriveController(
- new PIDConstants(5.0, 0.0, 0.0), new PIDConstants(5.0, 0.0, 0.0)),
- new RobotConfig(
- 75,
- 6.8,
- new ModuleConfig(
- 0.048, 5.0, 1.2, DCMotor.getKrakenX60(1).withReduction(6.14), 60.0, 1),
- 0.55))
+ new Pose2d(15.0, 4.0, Rotation2d.k180deg),
+ new PathConstraints(4, 3, 4, 4),
+ () -> new Pose2d(1.5, 4, Rotation2d.kZero),
+ ChassisSpeeds::new,
+ (speeds, feedforwards) -> {
+ },
+ new PPHolonomicDriveController(
+ new PIDConstants(5.0, 0.0, 0.0), new PIDConstants(5.0, 0.0, 0.0)),
+ new RobotConfig(
+ 75,
+ 6.8,
+ new ModuleConfig(
+ 0.048, 5.0, 1.2, DCMotor.getKrakenX60(1).withReduction(6.14), 60.0, 1),
+ 0.55))
.andThen(Commands.print("[PathPlanner] PathfindingCommand finished warmup"))
.ignoringDisable(true);
}
diff --git a/pathplannerlib/src/main/java/com/pathplanner/lib/pathfinding/Pathfinding.java b/pathplannerlib/src/main/java/com/pathplanner/lib/pathfinding/Pathfinding.java
index f5a2a74f2..57e71d7bb 100644
--- a/pathplannerlib/src/main/java/com/pathplanner/lib/pathfinding/Pathfinding.java
+++ b/pathplannerlib/src/main/java/com/pathplanner/lib/pathfinding/Pathfinding.java
@@ -5,14 +5,20 @@
import com.pathplanner.lib.path.PathPlannerPath;
import edu.wpi.first.math.Pair;
import edu.wpi.first.math.geometry.Translation2d;
+import edu.wpi.first.units.measure.Distance;
+
+import static edu.wpi.first.units.Units.Meters;
+
import java.util.List;
/**
- * Static class for interacting with the chosen pathfinding implementation from the pathfinding
+ * Static class for interacting with the chosen pathfinding implementation from
+ * the pathfinding
* commands
*/
public class Pathfinding {
private static Pathfinder pathfinder = null;
+ private static Distance minDistanceToPathfind = Meters.of(2.0);
/**
* Set the pathfinder that should be used by the path following commands
@@ -23,7 +29,10 @@ public static void setPathfinder(Pathfinder pathfinder) {
Pathfinding.pathfinder = pathfinder;
}
- /** Ensure that a pathfinding implementation has been chosen. If not, set it to the default. */
+ /**
+ * Ensure that a pathfinding implementation has been chosen. If not, set it to
+ * the default.
+ */
public static void ensureInitialized() {
if (pathfinder == null) {
// Hasn't been initialized yet, use the default implementation
@@ -33,7 +42,8 @@ public static void ensureInitialized() {
}
/**
- * Get if a new path has been calculated since the last time a path was retrieved
+ * Get if a new path has been calculated since the last time a path was
+ * retrieved
*
* @return True if a new path is available
*/
@@ -41,12 +51,39 @@ public static boolean isNewPathAvailable() {
return pathfinder.isNewPathAvailable();
}
+ /**
+ * Sets the minimum distance required between the robot's current position and
+ * the path
+ * before a new pathfinding update is triggered. If the given value is negative,
+ * it will be clamped to 0 and a warning will be issued.
+ *
+ * @param minDistance The minimum distance to trigger a pathfinding update.
+ */
+ public static void setMinDistanceToPathfind(Distance minDistance) {
+ if (minDistance.in(Meters) < 0) {
+ System.err.println(
+ "[Pathplanner] Warning: minDistanceToPathfind was set to a negative value, which will have no affect.");
+ }
+
+ minDistanceToPathfind = minDistance;
+ }
+
+ /**
+ * Gets the current minimum distance required to trigger a pathfinding update.
+ *
+ * @return The minimum distance as a {@link Distance} object.
+ */
+ public static Distance getMinDistanceToPathfind() {
+ return minDistanceToPathfind;
+ }
+
/**
* Get the most recently calculated path
*
- * @param constraints The path constraints to use when creating the path
+ * @param constraints The path constraints to use when creating the path
* @param goalEndState The goal end state to use when creating the path
- * @return The PathPlannerPath created from the points calculated by the pathfinder
+ * @return The PathPlannerPath created from the points calculated by the
+ * pathfinder
*/
public static PathPlannerPath getCurrentPath(
PathConstraints constraints, GoalEndState goalEndState) {
@@ -56,8 +93,9 @@ public static PathPlannerPath getCurrentPath(
/**
* Set the start position to pathfind from
*
- * @param startPosition Start position on the field. If this is within an obstacle it will be
- * moved to the nearest non-obstacle node.
+ * @param startPosition Start position on the field. If this is within an
+ * obstacle it will be
+ * moved to the nearest non-obstacle node.
*/
public static void setStartPosition(Translation2d startPosition) {
pathfinder.setStartPosition(startPosition);
@@ -66,8 +104,9 @@ public static void setStartPosition(Translation2d startPosition) {
/**
* Set the goal position to pathfind to
*
- * @param goalPosition Goal position on the field. f this is within an obstacle it will be moved
- * to the nearest non-obstacle node.
+ * @param goalPosition Goal position on the field. f this is within an obstacle
+ * it will be moved
+ * to the nearest non-obstacle node.
*/
public static void setGoalPosition(Translation2d goalPosition) {
pathfinder.setGoalPosition(goalPosition);
@@ -76,10 +115,13 @@ public static void setGoalPosition(Translation2d goalPosition) {
/**
* Set the dynamic obstacles that should be avoided while pathfinding.
*
- * @param obs A List of Translation2d pairs representing obstacles. Each Translation2d represents
- * opposite corners of a bounding box.
- * @param currentRobotPos The current position of the robot. This is needed to change the start
- * position of the path if the robot is now within an obstacle.
+ * @param obs A List of Translation2d pairs representing obstacles.
+ * Each Translation2d represents
+ * opposite corners of a bounding box.
+ * @param currentRobotPos The current position of the robot. This is needed to
+ * change the start
+ * position of the path if the robot is now within an
+ * obstacle.
*/
public static void setDynamicObstacles(
List