001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.wpilibj2.command; 006 007import static edu.wpi.first.util.ErrorMessages.requireNonNullParam; 008 009import edu.wpi.first.math.controller.HolonomicDriveController; 010import edu.wpi.first.math.controller.PIDController; 011import edu.wpi.first.math.controller.ProfiledPIDController; 012import edu.wpi.first.math.controller.SimpleMotorFeedforward; 013import edu.wpi.first.math.geometry.Pose2d; 014import edu.wpi.first.math.geometry.Rotation2d; 015import edu.wpi.first.math.kinematics.ChassisSpeeds; 016import edu.wpi.first.math.kinematics.MecanumDriveKinematics; 017import edu.wpi.first.math.kinematics.MecanumDriveMotorVoltages; 018import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; 019import edu.wpi.first.math.trajectory.Trajectory; 020import edu.wpi.first.wpilibj.Timer; 021import java.util.function.Consumer; 022import java.util.function.Supplier; 023 024/** 025 * A command that uses two PID controllers ({@link PIDController}) and a ProfiledPIDController 026 * ({@link ProfiledPIDController}) to follow a trajectory {@link Trajectory} with a mecanum drive. 027 * 028 * <p>The command handles trajectory-following, Velocity PID calculations, and feedforwards 029 * internally. This is intended to be a more-or-less "complete solution" that can be used by teams 030 * without a great deal of controls expertise. 031 * 032 * <p>Advanced teams seeking more flexibility (for example, those who wish to use the onboard PID 033 * functionality of a "smart" motor controller) may use the secondary constructor that omits the PID 034 * and feedforward functionality, returning only the raw wheel speeds from the PID controllers. 035 * 036 * <p>The robot angle controller does not follow the angle given by the trajectory but rather goes 037 * to the angle given in the final state of the trajectory. 038 * 039 * <p>This class is provided by the NewCommands VendorDep 040 */ 041@SuppressWarnings("removal") 042public class MecanumControllerCommand extends Command { 043 private final Timer m_timer = new Timer(); 044 private final boolean m_usePID; 045 private final Trajectory m_trajectory; 046 private final Supplier<Pose2d> m_pose; 047 private final SimpleMotorFeedforward m_feedforward; 048 private final MecanumDriveKinematics m_kinematics; 049 private final HolonomicDriveController m_controller; 050 private final Supplier<Rotation2d> m_desiredRotation; 051 private final double m_maxWheelVelocity; 052 private final PIDController m_frontLeftController; 053 private final PIDController m_rearLeftController; 054 private final PIDController m_frontRightController; 055 private final PIDController m_rearRightController; 056 private final Supplier<MecanumDriveWheelSpeeds> m_currentWheelSpeeds; 057 private final MecanumVoltagesConsumer m_outputDriveVoltages; 058 private final Consumer<MecanumDriveWheelSpeeds> m_outputWheelSpeeds; 059 private double m_prevFrontLeftSpeedSetpoint; // m/s 060 private double m_prevRearLeftSpeedSetpoint; // m/s 061 private double m_prevFrontRightSpeedSetpoint; // m/s 062 private double m_prevRearRightSpeedSetpoint; // m/s 063 064 /** 065 * Constructs a new MecanumControllerCommand that when executed will follow the provided 066 * trajectory. PID control and feedforward are handled internally. Outputs are scaled from -12 to 067 * 12 as a voltage output to the motor. 068 * 069 * <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path 070 * this is left to the user, since it is not appropriate for paths with nonstationary endstates. 071 * 072 * @param trajectory The trajectory to follow. 073 * @param pose A function that supplies the robot pose - use one of the odometry classes to 074 * provide this. 075 * @param feedforward The feedforward to use for the drivetrain. 076 * @param kinematics The kinematics for the robot drivetrain. 077 * @param xController The Trajectory Tracker PID controller for the robot's x position. 078 * @param yController The Trajectory Tracker PID controller for the robot's y position. 079 * @param thetaController The Trajectory Tracker PID controller for angle for the robot. 080 * @param desiredRotation The angle that the robot should be facing. This is sampled at each time 081 * step. 082 * @param maxWheelVelocity The maximum velocity of a drivetrain wheel in m/s. 083 * @param frontLeftController The front left wheel velocity PID. 084 * @param rearLeftController The rear left wheel velocity PID. 085 * @param frontRightController The front right wheel velocity PID. 086 * @param rearRightController The rear right wheel velocity PID. 087 * @param currentWheelSpeeds A MecanumDriveWheelSpeeds object containing the current wheel speeds. 088 * @param outputDriveVoltages A MecanumVoltagesConsumer that consumes voltages of mecanum motors. 089 * @param requirements The subsystems to require. 090 */ 091 @SuppressWarnings("this-escape") 092 public MecanumControllerCommand( 093 Trajectory trajectory, 094 Supplier<Pose2d> pose, 095 SimpleMotorFeedforward feedforward, 096 MecanumDriveKinematics kinematics, 097 PIDController xController, 098 PIDController yController, 099 ProfiledPIDController thetaController, 100 Supplier<Rotation2d> desiredRotation, 101 double maxWheelVelocity, 102 PIDController frontLeftController, 103 PIDController rearLeftController, 104 PIDController frontRightController, 105 PIDController rearRightController, 106 Supplier<MecanumDriveWheelSpeeds> currentWheelSpeeds, 107 MecanumVoltagesConsumer outputDriveVoltages, 108 Subsystem... requirements) { 109 m_trajectory = requireNonNullParam(trajectory, "trajectory", "MecanumControllerCommand"); 110 m_pose = requireNonNullParam(pose, "pose", "MecanumControllerCommand"); 111 m_feedforward = requireNonNullParam(feedforward, "feedforward", "MecanumControllerCommand"); 112 m_kinematics = requireNonNullParam(kinematics, "kinematics", "MecanumControllerCommand"); 113 114 m_controller = 115 new HolonomicDriveController( 116 requireNonNullParam(xController, "xController", "MecanumControllerCommand"), 117 requireNonNullParam(yController, "yController", "MecanumControllerCommand"), 118 requireNonNullParam(thetaController, "thetaController", "MecanumControllerCommand")); 119 120 m_desiredRotation = 121 requireNonNullParam(desiredRotation, "desiredRotation", "MecanumControllerCommand"); 122 123 m_maxWheelVelocity = maxWheelVelocity; 124 125 m_frontLeftController = 126 requireNonNullParam(frontLeftController, "frontLeftController", "MecanumControllerCommand"); 127 m_rearLeftController = 128 requireNonNullParam(rearLeftController, "rearLeftController", "MecanumControllerCommand"); 129 m_frontRightController = 130 requireNonNullParam( 131 frontRightController, "frontRightController", "MecanumControllerCommand"); 132 m_rearRightController = 133 requireNonNullParam(rearRightController, "rearRightController", "MecanumControllerCommand"); 134 135 m_currentWheelSpeeds = 136 requireNonNullParam(currentWheelSpeeds, "currentWheelSpeeds", "MecanumControllerCommand"); 137 138 m_outputDriveVoltages = 139 requireNonNullParam(outputDriveVoltages, "outputDriveVoltages", "MecanumControllerCommand"); 140 141 m_outputWheelSpeeds = null; 142 143 m_usePID = true; 144 145 addRequirements(requirements); 146 } 147 148 /** 149 * Constructs a new MecanumControllerCommand that when executed will follow the provided 150 * trajectory. PID control and feedforward are handled internally. Outputs are scaled from -12 to 151 * 12 as a voltage output to the motor. 152 * 153 * <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path 154 * this is left to the user, since it is not appropriate for paths with nonstationary endstates. 155 * 156 * @param trajectory The trajectory to follow. 157 * @param pose A function that supplies the robot pose - use one of the odometry classes to 158 * provide this. 159 * @param feedforward The feedforward to use for the drivetrain. 160 * @param kinematics The kinematics for the robot drivetrain. 161 * @param xController The Trajectory Tracker PID controller for the robot's x position. 162 * @param yController The Trajectory Tracker PID controller for the robot's y position. 163 * @param thetaController The Trajectory Tracker PID controller for angle for the robot. 164 * @param desiredRotation The angle that the robot should be facing. This is sampled at each time 165 * step. 166 * @param maxWheelVelocityMetersPerSecond The maximum velocity of a drivetrain wheel. 167 * @param frontLeftController The front left wheel velocity PID. 168 * @param rearLeftController The rear left wheel velocity PID. 169 * @param frontRightController The front right wheel velocity PID. 170 * @param rearRightController The rear right wheel velocity PID. 171 * @param currentWheelSpeeds A MecanumDriveWheelSpeeds object containing the current wheel speeds. 172 * @param outputDriveVoltages A MecanumDriveMotorVoltages object containing the output motor 173 * voltages. 174 * @param requirements The subsystems to require. 175 * @deprecated Use {@link MecanumVoltagesConsumer} instead of {@code 176 * Consumer<MecanumDriveMotorVoltages}. 177 */ 178 @Deprecated(since = "2025", forRemoval = true) 179 public MecanumControllerCommand( 180 Trajectory trajectory, 181 Supplier<Pose2d> pose, 182 SimpleMotorFeedforward feedforward, 183 MecanumDriveKinematics kinematics, 184 PIDController xController, 185 PIDController yController, 186 ProfiledPIDController thetaController, 187 Supplier<Rotation2d> desiredRotation, 188 double maxWheelVelocityMetersPerSecond, 189 PIDController frontLeftController, 190 PIDController rearLeftController, 191 PIDController frontRightController, 192 PIDController rearRightController, 193 Supplier<MecanumDriveWheelSpeeds> currentWheelSpeeds, 194 Consumer<MecanumDriveMotorVoltages> outputDriveVoltages, 195 Subsystem... requirements) { 196 this( 197 trajectory, 198 pose, 199 feedforward, 200 kinematics, 201 xController, 202 yController, 203 thetaController, 204 desiredRotation, 205 maxWheelVelocityMetersPerSecond, 206 frontLeftController, 207 rearLeftController, 208 frontRightController, 209 rearRightController, 210 currentWheelSpeeds, 211 (frontLeft, frontRight, rearLeft, rearRight) -> 212 outputDriveVoltages.accept( 213 new MecanumDriveMotorVoltages(frontLeft, frontRight, rearLeft, rearRight)), 214 requirements); 215 } 216 217 /** 218 * Constructs a new MecanumControllerCommand that when executed will follow the provided 219 * trajectory. PID control and feedforward are handled internally. Outputs are scaled from -12 to 220 * 12 as a voltage output to the motor. 221 * 222 * <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path 223 * this is left to the user, since it is not appropriate for paths with nonstationary endstates. 224 * 225 * <p>Note 2: The final rotation of the robot will be set to the rotation of the final pose in the 226 * trajectory. The robot will not follow the rotations from the poses at each timestep. If 227 * alternate rotation behavior is desired, the other constructor with a supplier for rotation 228 * should be used. 229 * 230 * @param trajectory The trajectory to follow. 231 * @param pose A function that supplies the robot pose - use one of the odometry classes to 232 * provide this. 233 * @param feedforward The feedforward to use for the drivetrain. 234 * @param kinematics The kinematics for the robot drivetrain. 235 * @param xController The Trajectory Tracker PID controller for the robot's x position. 236 * @param yController The Trajectory Tracker PID controller for the robot's y position. 237 * @param thetaController The Trajectory Tracker PID controller for angle for the robot. 238 * @param maxWheelVelocityMetersPerSecond The maximum velocity of a drivetrain wheel. 239 * @param frontLeftController The front left wheel velocity PID. 240 * @param rearLeftController The rear left wheel velocity PID. 241 * @param frontRightController The front right wheel velocity PID. 242 * @param rearRightController The rear right wheel velocity PID. 243 * @param currentWheelSpeeds A MecanumDriveWheelSpeeds object containing the current wheel speeds. 244 * @param outputDriveVoltages A MecanumVoltagesConsumer that consumes voltages of mecanum motors. 245 * @param requirements The subsystems to require. 246 */ 247 public MecanumControllerCommand( 248 Trajectory trajectory, 249 Supplier<Pose2d> pose, 250 SimpleMotorFeedforward feedforward, 251 MecanumDriveKinematics kinematics, 252 PIDController xController, 253 PIDController yController, 254 ProfiledPIDController thetaController, 255 double maxWheelVelocityMetersPerSecond, 256 PIDController frontLeftController, 257 PIDController rearLeftController, 258 PIDController frontRightController, 259 PIDController rearRightController, 260 Supplier<MecanumDriveWheelSpeeds> currentWheelSpeeds, 261 MecanumVoltagesConsumer outputDriveVoltages, 262 Subsystem... requirements) { 263 this( 264 trajectory, 265 pose, 266 feedforward, 267 kinematics, 268 xController, 269 yController, 270 thetaController, 271 () -> trajectory.getStates().get(trajectory.getStates().size() - 1).pose.getRotation(), 272 maxWheelVelocityMetersPerSecond, 273 frontLeftController, 274 rearLeftController, 275 frontRightController, 276 rearRightController, 277 currentWheelSpeeds, 278 outputDriveVoltages, 279 requirements); 280 } 281 282 /** 283 * Constructs a new MecanumControllerCommand that when executed will follow the provided 284 * trajectory. PID control and feedforward are handled internally. Outputs are scaled from -12 to 285 * 12 as a voltage output to the motor. 286 * 287 * <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path 288 * this is left to the user, since it is not appropriate for paths with nonstationary endstates. 289 * 290 * <p>Note 2: The final rotation of the robot will be set to the rotation of the final pose in the 291 * trajectory. The robot will not follow the rotations from the poses at each timestep. If 292 * alternate rotation behavior is desired, the other constructor with a supplier for rotation 293 * should be used. 294 * 295 * @param trajectory The trajectory to follow. 296 * @param pose A function that supplies the robot pose - use one of the odometry classes to 297 * provide this. 298 * @param feedforward The feedforward to use for the drivetrain. 299 * @param kinematics The kinematics for the robot drivetrain. 300 * @param xController The Trajectory Tracker PID controller for the robot's x position. 301 * @param yController The Trajectory Tracker PID controller for the robot's y position. 302 * @param thetaController The Trajectory Tracker PID controller for angle for the robot. 303 * @param maxWheelVelocity The maximum velocity of a drivetrain wheel in m/s. 304 * @param frontLeftController The front left wheel velocity PID. 305 * @param rearLeftController The rear left wheel velocity PID. 306 * @param frontRightController The front right wheel velocity PID. 307 * @param rearRightController The rear right wheel velocity PID. 308 * @param currentWheelSpeeds A MecanumDriveWheelSpeeds object containing the current wheel speeds. 309 * @param outputDriveVoltages A MecanumDriveMotorVoltages object containing the output motor 310 * voltages. 311 * @param requirements The subsystems to require. 312 * @deprecated Use {@link MecanumVoltagesConsumer} instead of {@code 313 * Consumer<MecanumDriveMotorVoltages>}. 314 */ 315 @Deprecated(since = "2025", forRemoval = true) 316 public MecanumControllerCommand( 317 Trajectory trajectory, 318 Supplier<Pose2d> pose, 319 SimpleMotorFeedforward feedforward, 320 MecanumDriveKinematics kinematics, 321 PIDController xController, 322 PIDController yController, 323 ProfiledPIDController thetaController, 324 double maxWheelVelocity, 325 PIDController frontLeftController, 326 PIDController rearLeftController, 327 PIDController frontRightController, 328 PIDController rearRightController, 329 Supplier<MecanumDriveWheelSpeeds> currentWheelSpeeds, 330 Consumer<MecanumDriveMotorVoltages> outputDriveVoltages, 331 Subsystem... requirements) { 332 this( 333 trajectory, 334 pose, 335 feedforward, 336 kinematics, 337 xController, 338 yController, 339 thetaController, 340 maxWheelVelocity, 341 frontLeftController, 342 rearLeftController, 343 frontRightController, 344 rearRightController, 345 currentWheelSpeeds, 346 (frontLeft, frontRight, rearLeft, rearRight) -> 347 outputDriveVoltages.accept( 348 new MecanumDriveMotorVoltages(frontLeft, frontRight, rearLeft, rearRight)), 349 requirements); 350 } 351 352 /** 353 * Constructs a new MecanumControllerCommand that when executed will follow the provided 354 * trajectory. The user should implement a velocity PID on the desired output wheel velocities. 355 * 356 * <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path - 357 * this is left to the user, since it is not appropriate for paths with nonstationary end-states. 358 * 359 * @param trajectory The trajectory to follow. 360 * @param pose A function that supplies the robot pose - use one of the odometry classes to 361 * provide this. 362 * @param kinematics The kinematics for the robot drivetrain. 363 * @param xController The Trajectory Tracker PID controller for the robot's x position. 364 * @param yController The Trajectory Tracker PID controller for the robot's y position. 365 * @param thetaController The Trajectory Tracker PID controller for angle for the robot. 366 * @param desiredRotation The angle that the robot should be facing. This is sampled at each time 367 * step. 368 * @param maxWheelVelocity The maximum velocity of a drivetrain wheel in m/s. 369 * @param outputWheelSpeeds A MecanumDriveWheelSpeeds object containing the output wheel speeds. 370 * @param requirements The subsystems to require. 371 */ 372 @SuppressWarnings("this-escape") 373 public MecanumControllerCommand( 374 Trajectory trajectory, 375 Supplier<Pose2d> pose, 376 MecanumDriveKinematics kinematics, 377 PIDController xController, 378 PIDController yController, 379 ProfiledPIDController thetaController, 380 Supplier<Rotation2d> desiredRotation, 381 double maxWheelVelocity, 382 Consumer<MecanumDriveWheelSpeeds> outputWheelSpeeds, 383 Subsystem... requirements) { 384 m_trajectory = requireNonNullParam(trajectory, "trajectory", "MecanumControllerCommand"); 385 m_pose = requireNonNullParam(pose, "pose", "MecanumControllerCommand"); 386 m_feedforward = new SimpleMotorFeedforward(0, 0, 0); 387 m_kinematics = requireNonNullParam(kinematics, "kinematics", "MecanumControllerCommand"); 388 389 m_controller = 390 new HolonomicDriveController( 391 requireNonNullParam(xController, "xController", "MecanumControllerCommand"), 392 requireNonNullParam(yController, "yController", "MecanumControllerCommand"), 393 requireNonNullParam(thetaController, "thetaController", "MecanumControllerCommand")); 394 395 m_desiredRotation = 396 requireNonNullParam(desiredRotation, "desiredRotation", "MecanumControllerCommand"); 397 398 m_maxWheelVelocity = maxWheelVelocity; 399 400 m_frontLeftController = null; 401 m_rearLeftController = null; 402 m_frontRightController = null; 403 m_rearRightController = null; 404 405 m_currentWheelSpeeds = null; 406 407 m_outputWheelSpeeds = 408 requireNonNullParam(outputWheelSpeeds, "outputWheelSpeeds", "MecanumControllerCommand"); 409 410 m_outputDriveVoltages = null; 411 412 m_usePID = false; 413 414 addRequirements(requirements); 415 } 416 417 /** 418 * Constructs a new MecanumControllerCommand that when executed will follow the provided 419 * trajectory. The user should implement a velocity PID on the desired output wheel velocities. 420 * 421 * <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path - 422 * this is left to the user, since it is not appropriate for paths with nonstationary end-states. 423 * 424 * <p>Note 2: The final rotation of the robot will be set to the rotation of the final pose in the 425 * trajectory. The robot will not follow the rotations from the poses at each timestep. If 426 * alternate rotation behavior is desired, the other constructor with a supplier for rotation 427 * should be used. 428 * 429 * @param trajectory The trajectory to follow. 430 * @param pose A function that supplies the robot pose - use one of the odometry classes to 431 * provide this. 432 * @param kinematics The kinematics for the robot drivetrain. 433 * @param xController The Trajectory Tracker PID controller for the robot's x position. 434 * @param yController The Trajectory Tracker PID controller for the robot's y position. 435 * @param thetaController The Trajectory Tracker PID controller for angle for the robot. 436 * @param maxWheelVelocity The maximum velocity of a drivetrain wheel. 437 * @param outputWheelSpeeds A MecanumDriveWheelSpeeds object containing the output wheel speeds. 438 * @param requirements The subsystems to require. 439 */ 440 public MecanumControllerCommand( 441 Trajectory trajectory, 442 Supplier<Pose2d> pose, 443 MecanumDriveKinematics kinematics, 444 PIDController xController, 445 PIDController yController, 446 ProfiledPIDController thetaController, 447 double maxWheelVelocity, 448 Consumer<MecanumDriveWheelSpeeds> outputWheelSpeeds, 449 Subsystem... requirements) { 450 this( 451 trajectory, 452 pose, 453 kinematics, 454 xController, 455 yController, 456 thetaController, 457 () -> trajectory.getStates().get(trajectory.getStates().size() - 1).pose.getRotation(), 458 maxWheelVelocity, 459 outputWheelSpeeds, 460 requirements); 461 } 462 463 @Override 464 public void initialize() { 465 var initialState = m_trajectory.sample(0); 466 467 var initialXVelocity = initialState.velocity * initialState.pose.getRotation().getCos(); 468 var initialYVelocity = initialState.velocity * initialState.pose.getRotation().getSin(); 469 470 MecanumDriveWheelSpeeds prevSpeeds = 471 m_kinematics.toWheelSpeeds(new ChassisSpeeds(initialXVelocity, initialYVelocity, 0.0)); 472 473 m_prevFrontLeftSpeedSetpoint = prevSpeeds.frontLeft; 474 m_prevRearLeftSpeedSetpoint = prevSpeeds.rearLeft; 475 m_prevFrontRightSpeedSetpoint = prevSpeeds.frontRight; 476 m_prevRearRightSpeedSetpoint = prevSpeeds.rearRight; 477 478 m_timer.restart(); 479 } 480 481 @Override 482 public void execute() { 483 double curTime = m_timer.get(); 484 485 var desiredState = m_trajectory.sample(curTime); 486 487 var targetChassisSpeeds = 488 m_controller.calculate(m_pose.get(), desiredState, m_desiredRotation.get()); 489 var targetWheelSpeeds = m_kinematics.toWheelSpeeds(targetChassisSpeeds); 490 491 targetWheelSpeeds = targetWheelSpeeds.desaturate(m_maxWheelVelocity); 492 493 double frontLeftSpeedSetpoint = targetWheelSpeeds.frontLeft; 494 double rearLeftSpeedSetpoint = targetWheelSpeeds.rearLeft; 495 double frontRightSpeedSetpoint = targetWheelSpeeds.frontRight; 496 double rearRightSpeedSetpoint = targetWheelSpeeds.rearRight; 497 498 double frontLeftOutput; 499 double rearLeftOutput; 500 double frontRightOutput; 501 double rearRightOutput; 502 503 if (m_usePID) { 504 final double frontLeftFeedforward = 505 m_feedforward.calculate(m_prevFrontLeftSpeedSetpoint, frontLeftSpeedSetpoint); 506 507 final double rearLeftFeedforward = 508 m_feedforward.calculate(m_prevRearLeftSpeedSetpoint, rearLeftSpeedSetpoint); 509 510 final double frontRightFeedforward = 511 m_feedforward.calculate(m_prevFrontRightSpeedSetpoint, frontRightSpeedSetpoint); 512 513 final double rearRightFeedforward = 514 m_feedforward.calculate(m_prevRearRightSpeedSetpoint, rearRightSpeedSetpoint); 515 516 frontLeftOutput = 517 frontLeftFeedforward 518 + m_frontLeftController.calculate( 519 m_currentWheelSpeeds.get().frontLeft, frontLeftSpeedSetpoint); 520 521 rearLeftOutput = 522 rearLeftFeedforward 523 + m_rearLeftController.calculate( 524 m_currentWheelSpeeds.get().rearLeft, rearLeftSpeedSetpoint); 525 526 frontRightOutput = 527 frontRightFeedforward 528 + m_frontRightController.calculate( 529 m_currentWheelSpeeds.get().frontRight, frontRightSpeedSetpoint); 530 531 rearRightOutput = 532 rearRightFeedforward 533 + m_rearRightController.calculate( 534 m_currentWheelSpeeds.get().rearRight, rearRightSpeedSetpoint); 535 536 m_outputDriveVoltages.accept( 537 frontLeftOutput, frontRightOutput, rearLeftOutput, rearRightOutput); 538 539 } else { 540 m_outputWheelSpeeds.accept( 541 new MecanumDriveWheelSpeeds( 542 frontLeftSpeedSetpoint, 543 frontRightSpeedSetpoint, 544 rearLeftSpeedSetpoint, 545 rearRightSpeedSetpoint)); 546 } 547 } 548 549 @Override 550 public void end(boolean interrupted) { 551 m_timer.stop(); 552 } 553 554 @Override 555 public boolean isFinished() { 556 return m_timer.hasElapsed(m_trajectory.getTotalTime()); 557 } 558 559 /** A consumer to represent an operation on the voltages of a mecanum drive. */ 560 @FunctionalInterface 561 public interface MecanumVoltagesConsumer { 562 /** 563 * Accepts the voltages to perform some operation with them. 564 * 565 * @param frontLeftVoltage The voltage of the front left motor. 566 * @param frontRightVoltage The voltage of the front right motor. 567 * @param rearLeftVoltage The voltage of the rear left motor. 568 * @param rearRightVoltage The voltage of the rear left motor. 569 */ 570 void accept( 571 double frontLeftVoltage, 572 double frontRightVoltage, 573 double rearLeftVoltage, 574 double rearRightVoltage); 575 } 576}