001/************************* PROJECT RON *************************/ 002/* Copyright (c) 2026 StuyPulse Robotics. All rights reserved. */ 003/* Use of this source code is governed by an MIT-style license */ 004/* that can be found in the repository LICENSE file. */ 005/***************************************************************/ 006package com.stuypulse.robot.subsystems.swerve; 007 008import static edu.wpi.first.units.Units.Second; 009import static edu.wpi.first.units.Units.Seconds; 010import static edu.wpi.first.units.Units.Volts; 011 012import org.ironmaple.simulation.drivesims.SwerveDriveSimulation; 013 014import com.ctre.phoenix6.SignalLogger; 015import com.ctre.phoenix6.Utils; 016import com.ctre.phoenix6.swerve.SwerveDrivetrainConstants; 017import com.ctre.phoenix6.swerve.SwerveModule.DriveRequestType; 018import com.ctre.phoenix6.swerve.SwerveModuleConstants; 019import com.ctre.phoenix6.swerve.SwerveRequest; 020import com.pathplanner.lib.auto.AutoBuilder; 021import com.pathplanner.lib.config.RobotConfig; 022import com.pathplanner.lib.controllers.PPHolonomicDriveController; 023import com.pathplanner.lib.path.PathPlannerPath; 024import com.pathplanner.lib.util.PathPlannerLogging; 025import com.stuypulse.robot.Robot; 026import com.stuypulse.robot.constants.Field; 027import com.stuypulse.robot.constants.Gains; 028import com.stuypulse.robot.constants.Settings; 029import com.stuypulse.robot.subsystems.swerve.TunerConstants.TunerSwerveDrivetrain; 030import com.stuypulse.robot.util.simulation.MapleSimSwerveDrivetrain; 031import com.stuypulse.robot.util.simulation.SimulationConstants; 032 033import dev.doglog.DogLog; 034import edu.wpi.first.math.Matrix; 035import edu.wpi.first.math.geometry.Pose2d; 036import edu.wpi.first.math.geometry.Rotation2d; 037import edu.wpi.first.math.geometry.Translation2d; 038import edu.wpi.first.math.geometry.Twist2d; 039import edu.wpi.first.math.kinematics.ChassisSpeeds; 040import edu.wpi.first.math.kinematics.SwerveModuleState; 041import edu.wpi.first.math.numbers.N1; 042import edu.wpi.first.math.numbers.N3; 043import edu.wpi.first.math.system.plant.DCMotor; 044import edu.wpi.first.networktables.NetworkTableInstance; 045import edu.wpi.first.networktables.StructArrayPublisher; 046import edu.wpi.first.networktables.StructPublisher; 047import edu.wpi.first.wpilibj.Notifier; 048import edu.wpi.first.wpilibj.Timer; 049import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; 050import edu.wpi.first.wpilibj2.command.Command; 051import edu.wpi.first.wpilibj2.command.Subsystem; 052import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine; 053 054/** 055 * Class that extends the Phoenix 6 SwerveDrivetrain class and implements 056 * Subsystem so it can easily 057 * be used in command-based projects. 058 */ 059public class CommandSwerveDrivetrain extends TunerSwerveDrivetrain implements Subsystem { 060 061 private static final CommandSwerveDrivetrain instance; 062 063 static { 064 instance = TunerConstants.createDrivetrain(); 065 } 066 067 public static CommandSwerveDrivetrain getInstance() { 068 return instance; 069 } 070 071 // 5 ms 072 private static final double kSimLoopPeriod = 0.005; 073 074 private Notifier m_simNotifier = null; 075 076 private double m_lastSimTime; 077 078 /* Swerve requests to apply during SysId characterization */ 079 private final SwerveRequest.SysIdSwerveTranslation m_moduleTranslationCharacterization = new SwerveRequest.SysIdSwerveTranslation(); 080 081 private final SwerveRequest.SysIdSwerveSteerGains m_steerCharacterization = new SwerveRequest.SysIdSwerveSteerGains(); 082 083 private final SwerveRequest.SysIdSwerveRotation m_rotationCharacterization = new SwerveRequest.SysIdSwerveRotation(); 084 085 private final SwerveRequest.FieldCentric fieldCentricRequest = new SwerveRequest.FieldCentric() 086 .withDriveRequestType(DriveRequestType.OpenLoopVoltage) 087 .withDeadband(Settings.Swerve.MODULE_VELOCITY_DEADBAND_M_PER_S) 088 .withRotationalDeadband(Settings.Swerve.ROTATIONAL_DEADBAND_RAD_PER_S) 089 .withDesaturateWheelSpeeds(true); 090 091 private final SwerveRequest.RobotCentric robotCentricRequest = new SwerveRequest.RobotCentric() 092 .withDriveRequestType(DriveRequestType.OpenLoopVoltage) 093 .withDeadband(Settings.Swerve.MODULE_VELOCITY_DEADBAND_M_PER_S) 094 .withRotationalDeadband(Settings.Swerve.ROTATIONAL_DEADBAND_RAD_PER_S) 095 .withDesaturateWheelSpeeds(true); 096 097 public SwerveRequest.FieldCentric getFieldCentricSwerveRequest() { 098 return this.fieldCentricRequest; 099 } 100 101 public SwerveRequest.RobotCentric getRobotCentricSwerveRequest() { 102 return this.robotCentricRequest; 103 } 104 105 /* 106 * SysId routine for characterizing module translation. This is used to find PID 107 * gains for the drive motors. 108 */ 109 private final SysIdRoutine m_sysIdRoutineModuleTranslation = new SysIdRoutine( 110 new SysIdRoutine.Config( // Use default ramp rate (1 V/s) 111 null, // Reduce dynamic step voltage to 4 V to prevent brownout 112 Volts.of(4), // Use default timeout (10 s) 113 null, // Log state with SignalLogger class 114 state -> SignalLogger.writeString("SysIdModuleTranslation_State", 115 state.toString())), 116 new SysIdRoutine.Mechanism( 117 output -> setControl(m_moduleTranslationCharacterization.withVolts(output)), 118 null, 119 this)); 120 121 /* 122 * SysId routine for characterizing chassis translation. This is used to find 123 * PID gains PID to pose. 124 */ 125 private final SysIdRoutine m_sysIdRoutineChassisTranslation = new SysIdRoutine( 126 new SysIdRoutine.Config( 127 /* This is in meters per second², but SysId only supports "volts per second" */ 128 Volts.of(1) 129 .per(Second), /* 130 * This is in meters per second, but SysId only 131 * supports "volts" 132 */ 133 Volts.of( 134 Settings.Swerve.Constraints.MAX_VELOCITY_M_PER_S), // Use 135 // default 136 // timeout 137 // (10 s) 138 null, // Log state with SignalLogger class 139 state -> SignalLogger.writeString("SysIdChassisTranslation_State", 140 state.toString())), 141 new SysIdRoutine.Mechanism( 142 output -> { 143 /* 144 * output is actually meters per second, but SysId only supports "volts" 145 */ 146 setControl( 147 getFieldCentricSwerveRequest() 148 .withVelocityX(output.in(Volts)) 149 .withVelocityY(0) 150 .withRotationalRate(0)); 151 /* also log the requested output for SysId */ 152 SignalLogger.writeDouble("Target X Velocity ('voltage')", 153 output.in(Volts)); 154 SignalLogger.writeDouble("X Position", getPose().getX()); 155 SignalLogger.writeDouble( 156 "X Velocity", 157 getChassisSpeeds().vxMetersPerSecond 158 * getPose().getRotation().getCos()); 159 }, 160 null, 161 this)); 162 163 /* 164 * SysId routine for characterizing steer. This is used to find PID gains for 165 * the steer motors. 166 */ 167 private final SysIdRoutine m_sysIdRoutineSteer = new SysIdRoutine( 168 new SysIdRoutine.Config( // Use default ramp rate (1 V/s) 169 null, // Use dynamic voltage of 7 V 170 Volts.of(7), // Use default timeout (10 s) 171 null, // Log state with SignalLogger class 172 state -> SignalLogger.writeString("SysIdSteer_State", state.toString())), 173 new SysIdRoutine.Mechanism( 174 volts -> setControl(m_steerCharacterization.withVolts(volts)), null, this)); 175 176 /* 177 * SysId routine for characterizing rotation. 178 * This is used to find PID gains for the FieldCentricFacingAngle 179 * HeadingController. 180 * See the documentation of SwerveRequest.SysIdSwerveRotation for info on 181 * importing the log to SysId. 182 */ 183 private final SysIdRoutine m_sysIdRoutineRotation = new SysIdRoutine( 184 new SysIdRoutine.Config( 185 /* This is in radians per second², but SysId only supports "volts per second" */ 186 Volts.of(Math.PI / 6) 187 .per(Second), /* 188 * This is in radians per second, but SysId only 189 * supports "volts" 190 */ 191 Volts.of(Math.PI), // Use default timeout (10 s) 192 null, // Log state with SignalLogger class 193 state -> SignalLogger.writeString("SysIdRotation_State", state.toString())), 194 new SysIdRoutine.Mechanism( 195 output -> { 196 /* 197 * output is actually radians per second, but SysId only supports 198 * "volts" 199 */ 200 setControl(m_rotationCharacterization 201 .withRotationalRate(output.in(Volts))); 202 /* also log the requested output for SysId */ 203 SignalLogger.writeDouble("Rotational_Target_Rate ('voltage')", 204 output.in(Volts)); 205 SignalLogger.writeDouble( 206 "Rotational Position", 207 getPose().getRotation().getRadians()); 208 SignalLogger.writeDouble( 209 "Rotational_Velocity", 210 getState().Speeds.omegaRadiansPerSecond); 211 }, 212 null, 213 this)); 214 215 /* The SysId routine to test */ 216 private SysIdRoutine m_sysIdRoutineToApply = m_sysIdRoutineModuleTranslation; 217 218 /** 219 * Constructs a CTRE SwerveDrivetrain using the specified constants. 220 * 221 * <p> 222 * This constructs the underlying hardware devices, so users should not 223 * construct the devices 224 * themselves. If they need the devices, they can access them through getters in 225 * the classes. 226 * 227 * @param drivetrainConstants Drivetrain-wide constants for the swerve drive 228 * @param modules Constants for each specific module 229 */ 230 // With all constructors the maple sim module constant regulation is not done if 231 // the robot is not 232 // real 233 protected CommandSwerveDrivetrain( 234 SwerveDrivetrainConstants drivetrainConstants, SwerveModuleConstants<?, ?, ?>... modules) { 235 super( 236 drivetrainConstants, 237 MapleSimSwerveDrivetrain.regulateModuleConstantsForSimulation(modules)); 238 if (Utils.isSimulation()) { 239 startSimThread(); 240 } 241 } 242 243 /** 244 * Constructs a CTRE SwerveDrivetrain using the specified constants. 245 * 246 * <p> 247 * This constructs the underlying hardware devices, so users should not 248 * construct the devices 249 * themselves. If they need the devices, they can access them through getters in 250 * the classes. 251 * 252 * @param drivetrainConstants Drivetrain-wide constants for the swerve drive 253 * @param odometryUpdateFrequency The frequency to run the odometry loop. If 254 * unspecified or set to 255 * 0 Hz, this is 250 Hz on CAN FD, and 100 Hz on 256 * CAN 2.0. 257 * @param modules Constants for each specific module 258 */ 259 private CommandSwerveDrivetrain( 260 SwerveDrivetrainConstants drivetrainConstants, 261 double odometryUpdateFrequency, 262 SwerveModuleConstants<?, ?, ?>... modules) { 263 super( 264 drivetrainConstants, 265 odometryUpdateFrequency, 266 MapleSimSwerveDrivetrain.regulateModuleConstantsForSimulation(modules)); 267 if (Utils.isSimulation()) { 268 startSimThread(); 269 } 270 } 271 272 /** 273 * Constructs a CTRE SwerveDrivetrain using the specified constants. 274 * 275 * <p> 276 * This constructs the underlying hardware devices, so users should not 277 * construct the devices 278 * themselves. If they need the devices, they can access them through getters in 279 * the classes. 280 * 281 * @param drivetrainConstants Drivetrain-wide constants for the swerve 282 * drive 283 * @param odometryUpdateFrequency The frequency to run the odometry loop. If 284 * unspecified or set to 285 * 0 Hz, this is 250 Hz on CAN FD, and 100 Hz 286 * on CAN 2.0. 287 * @param odometryStandardDeviation The standard deviation for odometry 288 * calculation in the form 289 * [x, y, theta]ᵀ, with units in meters and 290 * radians 291 * @param visionStandardDeviation The standard deviation for vision 292 * calculation in the form [x, y, 293 * theta]ᵀ, with units in meters and radians 294 * @param modules Constants for each specific module 295 */ 296 private CommandSwerveDrivetrain( 297 SwerveDrivetrainConstants drivetrainConstants, 298 double odometryUpdateFrequency, 299 Matrix<N3, N1> odometryStandardDeviation, 300 Matrix<N3, N1> visionStandardDeviation, 301 SwerveModuleConstants<?, ?, ?>... modules) { 302 super( 303 drivetrainConstants, 304 odometryUpdateFrequency, 305 odometryStandardDeviation, 306 visionStandardDeviation, 307 MapleSimSwerveDrivetrain.regulateModuleConstantsForSimulation(modules)); 308 if (Utils.isSimulation()) { 309 startSimThread(); 310 } 311 } 312 313 @Override 314 public void setControl(SwerveRequest request) { 315 if (Settings.EnabledSubsystems.SWERVE.get()) { 316 super.setControl(request); 317 } else { 318 super.setControl(new SwerveRequest.Idle()); 319 } 320 } 321 322 /** 323 * Runs the SysId Quasistatic test in the given direction for the routine 324 * specified by {@link 325 * #m_sysIdRoutineToApply}. 326 * 327 * @param direction Direction of the SysId Quasistatic test 328 * @return Command to run 329 */ 330 public Command sysIdQuasistatic(SysIdRoutine.Direction direction) { 331 return m_sysIdRoutineToApply.quasistatic(direction); 332 } 333 334 /** 335 * Runs the SysId Dynamic test in the given direction for the routine specified 336 * by {@link 337 * #m_sysIdRoutineToApply}. 338 * 339 * @param direction Direction of the SysId Dynamic test 340 * @return Command to run 341 */ 342 public Command sysIdDynamic(SysIdRoutine.Direction direction) { 343 return m_sysIdRoutineToApply.dynamic(direction); 344 } 345 346 public Command sysidRotationDynamic(SysIdRoutine.Direction direction) { 347 return m_sysIdRoutineRotation.dynamic(direction); 348 } 349 350 public Command sysidRotationQuasiStatic(SysIdRoutine.Direction direction) { 351 return m_sysIdRoutineRotation.quasistatic(direction); 352 } 353 354 public Command sysidSteerDynamic(SysIdRoutine.Direction direction) { 355 return m_sysIdRoutineSteer.dynamic(direction); 356 } 357 358 public Command sysidSteerQuasistatic(SysIdRoutine.Direction direction) { 359 return m_sysIdRoutineSteer.quasistatic(direction); 360 } 361 362 private MapleSimSwerveDrivetrain mapleSimSwerveDrivetrain = null; 363 364 @SuppressWarnings("unchecked") 365 private void startSimThread() { 366 mapleSimSwerveDrivetrain = new MapleSimSwerveDrivetrain( 367 Seconds.of(kSimLoopPeriod), 368 SimulationConstants.Drivetrain.TOTAL_WEIGHT.get(), 369 SimulationConstants.Drivetrain.LENGTH, 370 SimulationConstants.Drivetrain.WIDTH, 371 DCMotor.getKrakenX60(1), 372 DCMotor.getKrakenX60(1), 373 SimulationConstants.Drivetrain.WHEEL_COF, 374 getModuleLocations(), 375 getPigeon2(), 376 getModules(), 377 TunerConstants.FrontLeft, 378 TunerConstants.FrontRight, 379 TunerConstants.BackLeft, 380 TunerConstants.BackRight); 381 m_simNotifier = new Notifier(mapleSimSwerveDrivetrain::update); 382 m_simNotifier.startPeriodic(kSimLoopPeriod); 383 } 384 385 public SwerveDriveSimulation getMapleSimDrive() { 386 return mapleSimSwerveDrivetrain.mapleSimDrive; 387 } 388 389 /** 390 * Adds a vision measurement to the Kalman Filter. This will correct the 391 * odometry pose estimate 392 * while still accounting for measurement noise. 393 * 394 * @param visionRobotPoseMeters The pose of the robot as measured by the vision 395 * camera. 396 * @param timestampSeconds The timestamp of the vision measurement in 397 * seconds. 398 */ 399 @Override 400 public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) { 401 super.addVisionMeasurement(visionRobotPoseMeters, Utils.fpgaToCurrentTime(timestampSeconds)); 402 } 403 404 /** 405 * Adds a vision measurement to the Kalman Filter. This will correct the 406 * odometry pose estimate 407 * while still accounting for measurement noise. 408 * 409 * <p> 410 * Note that the vision measurement standard deviations passed into this method 411 * will continue 412 * to apply to future measurements until a subsequent call to {@link 413 * #setVisionMeasurementStdDevs(Matrgix)} or this method. 414 * 415 * @param visionRobotPoseMeters The pose of the robot as measured by the 416 * vision camera. 417 * @param timestampSeconds The timestamp of the vision measurement in 418 * seconds. 419 * @param visionMeasurementStdDevs Standard deviations of the vision pose 420 * measurement in the form 421 * [x, y, theta]ᵀ, with units in meters and 422 * radians. 423 */ 424 @Override 425 public void addVisionMeasurement( 426 Pose2d visionRobotPoseMeters, 427 double timestampSeconds, 428 Matrix<N3, N1> visionMeasurementStdDevs) { 429 super.addVisionMeasurement( 430 visionRobotPoseMeters, Utils.fpgaToCurrentTime(timestampSeconds), 431 visionMeasurementStdDevs); 432 } 433 434 public Pose2d getPose() { 435 return getState().Pose; 436 } 437 438 public boolean isAlignedToTarget(Pose2d target) { 439 Pose2d currentPose = getPose(); 440 Rotation2d targetAngle = new Rotation2d( 441 Math.atan2(target.getY() - currentPose.getY(), target.getX() - currentPose.getX())); 442 return currentPose.getRotation().minus(targetAngle) 443 .getDegrees() < Settings.Swerve.Alignment.Tolerances.THETA_TOLERANCE.getDegrees(); 444 } 445 446 public Pose2d getShooterPose() { 447 // offset is negative because the shooter is behind the robot center 448 return SimulationConstants.Shooter.OFFSETS.applyToPose2d( 449 mapleSimSwerveDrivetrain == null 450 ? getPose() 451 : getMapleSimDrive().getSimulatedDriveTrainPose()); 452 } 453 454 @Override 455 public void resetPose(Pose2d pose) { 456 if (this.mapleSimSwerveDrivetrain != null) 457 mapleSimSwerveDrivetrain.mapleSimDrive.setSimulationWorldPose(pose); 458 // Wait for simulation to update 459 Timer.delay(0.05); 460 super.resetPose(pose); 461 } 462 463 public void configureAutoBuilder() { 464 try { 465 AutoBuilder.configure( 466 this::getPose, 467 this::resetPose, 468 this::getChassisSpeeds, 469 this::setChassisSpeeds, 470 new PPHolonomicDriveController(Gains.Swerve.Alignment.XY, 471 Gains.Swerve.Alignment.THETA), 472 RobotConfig.fromGUISettings(), 473 () -> false, 474 instance); 475 PathPlannerLogging.setLogActivePathCallback( 476 (poses) -> { 477 if (Robot.isBlue()) { 478 Field.FIELD2D.getObject("path").setPoses(poses); 479 } else { 480 Field.FIELD2D.getObject("path").setPoses( 481 Field.transformToOppositeAlliance(poses)); 482 } 483 }); 484 } catch (Exception e) { 485 e.printStackTrace(); 486 } 487 } 488 489 public Command followPathCommand(String pathName) { 490 try { 491 return followPathCommand(PathPlannerPath.fromPathFile(pathName)); 492 } catch (Exception e) { 493 throw new IllegalArgumentException(pathName + " does not exist"); 494 } 495 } 496 497 public Command followPathCommand(PathPlannerPath path) { 498 return AutoBuilder.followPath(path); 499 } 500 501 public SwerveModuleState[] getModuleStates() { 502 SwerveModuleState[] moduleStates = new SwerveModuleState[4]; 503 for (int i = 0; i < 4; i++) { 504 moduleStates[i] = getModule(i).getCurrentState(); 505 } 506 return moduleStates; 507 } 508 509 public ChassisSpeeds getChassisSpeeds() { 510 return getKinematics().toChassisSpeeds(getModuleStates()); 511 } 512 513 public Translation2d getFieldRelativeSpeeds() { 514 return new Translation2d(getChassisSpeeds().vxMetersPerSecond, getChassisSpeeds().vyMetersPerSecond) 515 .rotateBy(getPose().getRotation()); 516 } 517 518 private void setChassisSpeeds(ChassisSpeeds robotSpeeds) { 519 setControl( 520 new SwerveRequest.RobotCentric() 521 .withVelocityX(robotSpeeds.vxMetersPerSecond) 522 .withVelocityY(robotSpeeds.vyMetersPerSecond) 523 .withRotationalRate(robotSpeeds.omegaRadiansPerSecond)); 524 } 525 526 public void drive(Translation2d velocity, double rotation) { 527 ChassisSpeeds speeds = ChassisSpeeds.fromFieldRelativeSpeeds( 528 Robot.isBlue() ? velocity.getY() : -velocity.getY(), 529 Robot.isBlue() ? -velocity.getX() : velocity.getX(), 530 -rotation, 531 getPose().getRotation()); 532 Pose2d robotVel = new Pose2d( 533 Settings.DT.in(Seconds) * speeds.vxMetersPerSecond, 534 Settings.DT.in(Seconds) * speeds.vyMetersPerSecond, 535 Rotation2d.fromRadians(Settings.DT.in(Seconds) * speeds.omegaRadiansPerSecond)); 536 Twist2d twistVel = new Pose2d().log(robotVel); 537 setChassisSpeeds( 538 new ChassisSpeeds( 539 twistVel.dx / Settings.DT.in(Seconds), 540 twistVel.dy / Settings.DT.in(Seconds), 541 twistVel.dtheta / Settings.DT.in(Seconds))); 542 } 543 544 private final StructPublisher<Pose2d> posePublisher = NetworkTableInstance.getDefault() 545 .getStructTopic("AdvScope/DTPose", Pose2d.struct).publish(); 546 547 private final StructPublisher<ChassisSpeeds> chassisPublisher = NetworkTableInstance.getDefault() 548 .getStructTopic("AdvScope/ChassisSpeeds", ChassisSpeeds.struct) 549 .publish(); 550 551 private final StructArrayPublisher<SwerveModuleState> modulePublisher = NetworkTableInstance.getDefault() 552 .getStructArrayTopic("AdvScope/SwerveStates", SwerveModuleState.struct) 553 .publish(); 554 555 @Override 556 public void periodic() { 557 final Pose2d pose = mapleSimSwerveDrivetrain == null 558 ? getPose() 559 : mapleSimSwerveDrivetrain.mapleSimDrive.getSimulatedDriveTrainPose(); 560 if (Settings.DEBUG_MODE) { 561 // under debug flag to avoid log clutter 562 posePublisher.set(pose); 563 chassisPublisher.set(getChassisSpeeds()); 564 modulePublisher.set(getModuleStates()); 565 } 566 SmartDashboard.putNumber("Swerve/Pose X", getPose().getX()); 567 SmartDashboard.putNumber("Swerve/Pose Y", getPose().getY()); 568 SmartDashboard.putNumber("Swerve/Pose Theta", getPose().getRotation().getDegrees()); 569 DogLog.log("Swerve/Pose/X", getPose().getX()); 570 DogLog.log("Swerve/Pose/Y", getPose().getY()); 571 DogLog.log("Swerve/Pose/Theta", getPose().getRotation().getDegrees()); 572 DogLog.log("Swerve/Pose2D", Robot.isBlue() ? pose : Field.transformToOppositeAlliance(pose)); 573 for (int i = 0; i < 4; i++) { 574 DogLog.log( 575 "Swerve/Modules/Module " + i + "/Speed (m per s)", 576 getModule(i).getCurrentState().speedMetersPerSecond); 577 DogLog.log( 578 "Swerve/Modules/Module " + i + "/Target Speed (m per s)", 579 getModule(i).getTargetState().speedMetersPerSecond); 580 DogLog.log( 581 "Swerve/Modules/Module " + i + "/Angle (deg)", 582 getModule(i).getCurrentState().angle.getDegrees() % 360); 583 DogLog.log( 584 "Swerve/Modules/Module " + i + "/Target Angle (deg)", 585 getModule(i).getTargetState().angle.getDegrees() % 360); 586 } 587 Field.FIELD2D 588 .getRobotObject() 589 .setPose(Robot.isBlue() ? pose : Field.transformToOppositeAlliance(pose)); 590 if (Settings.DEBUG_MODE) { 591 for (int i = 0; i < 4; i++) { 592 DogLog.log( 593 "Swerve/Modules/Module " + i + "/Stator Current", 594 getModule(i).getDriveMotor().getStatorCurrent().getValueAsDouble()); 595 DogLog.log( 596 "Swerve/Modules/Module " + i + "/Supply Current", 597 getModule(i).getDriveMotor().getSupplyCurrent().getValueAsDouble()); 598 } 599 DogLog.log( 600 "Swerve/Velocity Robot Relative X (m per s)", 601 getChassisSpeeds().vxMetersPerSecond); 602 DogLog.log( 603 "Swerve/Velocity Robot Relative Y (m per s)", 604 getChassisSpeeds().vyMetersPerSecond); 605 DogLog.log("Swerve/Velocity Field Relative X (m per s)", getFieldRelativeSpeeds().getX()); 606 DogLog.log("Swerve/Field Relative Rotation", getPose().getRotation().getDegrees()); 607 DogLog.log("Swerve/Velocity Field Relative Y (m per s)", getFieldRelativeSpeeds().getY()); 608 DogLog.log("Swerve/Angular Velocity (rad per s)", getChassisSpeeds().omegaRadiansPerSecond); 609 610 // Distance in meters 611 DogLog.forceNt.log("Swerve/Distance From Hub", 612 pose.getTranslation().getDistance(Field.getHubPose().getTranslation())); 613 DogLog.forceNt.log("Swerve/Distance From Ferry Zone", 614 pose.getTranslation().getDistance(Field.getFerryZonePose(pose.getTranslation()).getTranslation())); 615 } 616 } 617}