|
1 | | -// Copyright 2021-2024 FRC 6328 |
2 | | -// http://github.com/Mechanical-Advantage |
3 | | -// |
4 | | -// This program is free software; you can redistribute it and/or |
5 | | -// modify it under the terms of the GNU General Public License |
6 | | -// version 3 as published by the Free Software Foundation or |
7 | | -// available in the root directory of this project. |
8 | | -// |
9 | | -// This program is distributed in the hope that it will be useful, |
10 | | -// but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | -// GNU General Public License for more details. |
13 | | - |
14 | 1 | package org.curtinfrc.frc2025.subsystems.drive; |
15 | 2 |
|
16 | 3 | import com.ctre.phoenix6.swerve.SwerveModuleConstants; |
17 | | -import edu.wpi.first.math.MathUtil; |
18 | | -import edu.wpi.first.math.controller.PIDController; |
19 | | -import edu.wpi.first.math.geometry.Rotation2d; |
20 | 4 | import edu.wpi.first.math.system.plant.DCMotor; |
21 | 5 | import edu.wpi.first.math.system.plant.LinearSystemId; |
22 | | -import edu.wpi.first.math.util.Units; |
23 | | -import edu.wpi.first.wpilibj.Timer; |
24 | 6 | import edu.wpi.first.wpilibj.simulation.DCMotorSim; |
25 | 7 |
|
26 | | -/** |
27 | | - * Physics sim implementation of module IO. The sim models are configured using a set of module |
28 | | - * constants from Phoenix. Simulation is always based on voltage control. |
29 | | - */ |
30 | | -public class ModuleIOSim implements ModuleIO { |
31 | | - // TunerConstants doesn't support separate sim constants, so they are declared locally |
32 | | - private static final double DRIVE_KP = 0.05; |
33 | | - private static final double DRIVE_KD = 0.0; |
34 | | - private static final double DRIVE_KS = 0.0; |
35 | | - private static final double DRIVE_KV_ROT = |
36 | | - 0.91035; // Same units as TunerConstants: (volt * secs) / rotation |
37 | | - private static final double DRIVE_KV = 1.0 / Units.rotationsToRadians(1.0 / DRIVE_KV_ROT); |
38 | | - private static final double TURN_KP = 8.0; |
39 | | - private static final double TURN_KD = 0.0; |
| 8 | +public class ModuleIOSim extends ModuleIOTalonFX { |
40 | 9 | private static final DCMotor DRIVE_GEARBOX = DCMotor.getKrakenX60Foc(1); |
41 | | - private static final DCMotor TURN_GEARBOX = DCMotor.getKrakenX60Foc(1); |
| 10 | + private static final DCMotor TURN_GEARBOX = DCMotor.getKrakenX60(1); |
42 | 11 |
|
43 | 12 | private final DCMotorSim driveSim; |
44 | | - private final DCMotorSim turnSim; |
45 | | - |
46 | | - private boolean driveClosedLoop = false; |
47 | | - private boolean turnClosedLoop = false; |
48 | | - private PIDController driveController = new PIDController(DRIVE_KP, 0, DRIVE_KD); |
49 | | - private PIDController turnController = new PIDController(TURN_KP, 0, TURN_KD); |
50 | | - private double driveFFVolts = 0.0; |
51 | | - private double driveAppliedVolts = 0.0; |
52 | | - private double turnAppliedVolts = 0.0; |
| 13 | + private final DCMotorSim steerSim; |
53 | 14 |
|
54 | 15 | public ModuleIOSim(SwerveModuleConstants constants) { |
55 | | - // Create drive and turn sim models |
| 16 | + super(constants); |
| 17 | + // Create drive and steer sim models |
56 | 18 | driveSim = |
57 | 19 | new DCMotorSim( |
58 | 20 | LinearSystemId.createDCMotorSystem( |
59 | 21 | DRIVE_GEARBOX, constants.DriveInertia, constants.DriveMotorGearRatio), |
60 | 22 | DRIVE_GEARBOX); |
61 | | - turnSim = |
| 23 | + steerSim = |
62 | 24 | new DCMotorSim( |
63 | 25 | LinearSystemId.createDCMotorSystem( |
64 | 26 | TURN_GEARBOX, constants.SteerInertia, constants.SteerMotorGearRatio), |
65 | 27 | TURN_GEARBOX); |
66 | | - |
67 | | - // Enable wrapping for turn PID |
68 | | - turnController.enableContinuousInput(-Math.PI, Math.PI); |
69 | 28 | } |
70 | 29 |
|
71 | | - @Override |
72 | | - public void updateInputs(ModuleIOInputs inputs) { |
73 | | - // Run closed-loop control |
74 | | - if (driveClosedLoop) { |
75 | | - driveAppliedVolts = |
76 | | - driveFFVolts + driveController.calculate(driveSim.getAngularVelocityRadPerSec()); |
77 | | - } else { |
78 | | - driveController.reset(); |
79 | | - } |
80 | | - if (turnClosedLoop) { |
81 | | - turnAppliedVolts = turnController.calculate(turnSim.getAngularPositionRad()); |
82 | | - } else { |
83 | | - turnController.reset(); |
84 | | - } |
85 | | - |
86 | | - // Update simulation state |
87 | | - driveSim.setInputVoltage(MathUtil.clamp(driveAppliedVolts, -12.0, 12.0)); |
88 | | - turnSim.setInputVoltage(MathUtil.clamp(turnAppliedVolts, -12.0, 12.0)); |
89 | | - driveSim.update(0.02); |
90 | | - turnSim.update(0.02); |
91 | | - |
92 | | - // Update drive inputs |
93 | | - inputs.driveConnected = true; |
94 | | - inputs.drivePositionRad = driveSim.getAngularPositionRad(); |
95 | | - inputs.driveVelocityRadPerSec = driveSim.getAngularVelocityRadPerSec(); |
96 | | - inputs.driveAppliedVolts = driveAppliedVolts; |
97 | | - inputs.driveCurrentAmps = Math.abs(driveSim.getCurrentDrawAmps()); |
98 | | - |
99 | | - // Update turn inputs |
100 | | - inputs.turnConnected = true; |
101 | | - inputs.turnEncoderConnected = true; |
102 | | - inputs.turnAbsolutePosition = new Rotation2d(turnSim.getAngularPositionRad()); |
103 | | - inputs.turnPosition = new Rotation2d(turnSim.getAngularPositionRad()); |
104 | | - inputs.turnVelocityRadPerSec = turnSim.getAngularVelocityRadPerSec(); |
105 | | - inputs.turnAppliedVolts = turnAppliedVolts; |
106 | | - inputs.turnCurrentAmps = Math.abs(turnSim.getCurrentDrawAmps()); |
| 30 | + private double driveOutput(double raw) { |
| 31 | + return raw * constants.DriveMotorGearRatio; |
| 32 | + } |
107 | 33 |
|
108 | | - // Update odometry inputs (50Hz because high-frequency odometry in sim doesn't matter) |
109 | | - inputs.odometryTimestamps = new double[] {Timer.getFPGATimestamp()}; |
110 | | - inputs.odometryDrivePositionsRad = new double[] {inputs.drivePositionRad}; |
111 | | - inputs.odometryTurnPositions = new Rotation2d[] {inputs.turnPosition}; |
| 34 | + private double driveVoltage(double raw) { |
| 35 | + return (Math.abs(raw) > constants.DriveFrictionVoltage) ? raw : 0; |
112 | 36 | } |
113 | 37 |
|
114 | | - @Override |
115 | | - public void setDriveOpenLoop(double output) { |
116 | | - driveClosedLoop = false; |
117 | | - driveAppliedVolts = output; |
| 38 | + private double steerOutput(double raw) { |
| 39 | + return (constants.SteerMotorInverted ? -raw : raw) * constants.SteerMotorGearRatio; |
118 | 40 | } |
119 | 41 |
|
120 | | - @Override |
121 | | - public void setTurnOpenLoop(double output) { |
122 | | - turnClosedLoop = false; |
123 | | - turnAppliedVolts = output; |
| 42 | + private double steerVoltage(double raw) { |
| 43 | + return (Math.abs(raw) > constants.SteerFrictionVoltage) ? raw : 0; |
124 | 44 | } |
125 | 45 |
|
126 | | - @Override |
127 | | - public void setDriveVelocity(double velocityRadPerSec, double feedforward) { |
128 | | - driveClosedLoop = true; |
129 | | - driveFFVolts = DRIVE_KS * Math.signum(velocityRadPerSec) + DRIVE_KV * velocityRadPerSec; |
130 | | - driveController.setSetpoint(velocityRadPerSec); |
| 46 | + private void updateSimState(double dt) { |
| 47 | + var driveSimState = driveTalon.getSimState(); |
| 48 | + driveSim.setInputVoltage(driveVoltage(driveSimState.getMotorVoltage())); |
| 49 | + driveSim.update(dt); |
| 50 | + driveSimState.setRawRotorPosition(driveOutput(driveSim.getAngularPositionRotations())); |
| 51 | + driveSimState.setRotorVelocity(driveOutput(driveSim.getAngularVelocityRPM() / 60)); |
| 52 | + |
| 53 | + var steerSimState = turnTalon.getSimState(); |
| 54 | + var encoderSimState = cancoder.getSimState(); |
| 55 | + steerSim.setInputVoltage(steerVoltage(steerSimState.getMotorVoltage())); |
| 56 | + steerSim.update(dt); |
| 57 | + encoderSimState.setRawPosition(-steerSim.getAngularPositionRotations()); |
| 58 | + steerSimState.setRawRotorPosition(steerOutput(steerSim.getAngularPositionRotations())); |
| 59 | + steerSimState.setRotorVelocity(steerOutput(steerSim.getAngularVelocityRPM() / 60)); |
131 | 60 | } |
132 | 61 |
|
133 | 62 | @Override |
134 | | - public void setTurnPosition(Rotation2d rotation) { |
135 | | - turnClosedLoop = true; |
136 | | - turnController.setSetpoint(rotation.getRadians()); |
| 63 | + public void updateInputs(ModuleIOInputs inputs) { |
| 64 | + super.updateInputs(inputs); |
| 65 | + updateSimState(0.02); |
137 | 66 | } |
138 | 67 | } |
0 commit comments