Skip to content

Commit cc628e8

Browse files
committed
Add methods for wrapping a Motor with a simulated motor
1 parent 6e321e2 commit cc628e8

2 files changed

Lines changed: 205 additions & 0 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright 2026 Prospect Robotics SWENext Club
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package com.team2813.lib2813.control.motor;
17+
18+
import com.team2813.lib2813.control.ControlMode;
19+
import com.team2813.lib2813.control.Motor;
20+
import edu.wpi.first.units.measure.Current;
21+
22+
/**
23+
* Base class for classes that decorate motors.
24+
*
25+
* <p>All methods in this class delegate to the passed-in motor.
26+
*/
27+
public abstract class MotorDecorator<M extends Motor> implements Motor {
28+
protected final M motor;
29+
30+
protected MotorDecorator(M motor) {
31+
this.motor = motor;
32+
}
33+
34+
@Override
35+
public void set(ControlMode mode, double demand) {
36+
motor.set(mode, demand);
37+
}
38+
39+
@Override
40+
public void set(ControlMode mode, double demand, double feedForward) {
41+
motor.set(mode, demand, feedForward);
42+
}
43+
44+
@Override
45+
public Current getAppliedCurrent() {
46+
return motor.getAppliedCurrent();
47+
}
48+
49+
@Override
50+
public void stopMotor() {
51+
motor.stopMotor();
52+
}
53+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
Copyright 2026 Prospect Robotics SWENext Club
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package com.team2813.lib2813.control.motor;
17+
18+
import com.team2813.lib2813.control.ControlMode;
19+
import com.team2813.lib2813.control.Motor;
20+
import com.team2813.lib2813.control.PIDMotor;
21+
import com.team2813.lib2813.robot.PeriodicRegistry;
22+
import com.team2813.lib2813.robot.RobotState;
23+
import edu.wpi.first.math.numbers.N1;
24+
import edu.wpi.first.units.measure.Angle;
25+
import edu.wpi.first.units.measure.AngularVelocity;
26+
import edu.wpi.first.wpilibj.RobotController;
27+
import edu.wpi.first.wpilibj.simulation.LinearSystemSim;
28+
29+
/** Helper methods for working with simulation for motors. */
30+
public class MotorSimulation {
31+
32+
/**
33+
* Wraps a motor and a simulated motor to simulate a flywheel mechanism.
34+
*
35+
* <p>The simulated motor will be updated when the motor is updated.
36+
*
37+
* @param motor Motor to wrap
38+
* @param periodicRegistry Registry that will be used to register periodic functions
39+
* @param sim Simulation to associate with the motor
40+
* @return Wrapped motor
41+
*/
42+
public static Motor flywheel(
43+
Motor motor, PeriodicRegistry periodicRegistry, LinearSystemSim<?, N1, ?> sim) {
44+
if (motor instanceof PIDMotor pidMotor) {
45+
return new PIDMotorWrapper(pidMotor, periodicRegistry, sim);
46+
}
47+
return new MotorWrapper<>(motor, periodicRegistry, sim);
48+
}
49+
50+
/**
51+
* Wraps a pid motor and a simulated motor to simulate a flywheel mechanism.
52+
*
53+
* <p>The simulated motor will be updated when the motor is updated.
54+
*
55+
* @param motor Motor to wrap
56+
* @param periodicRegistry Registry that will be used to register periodic functions
57+
* @param sim Simulation to associate with the motor
58+
* @return Wrapped motor
59+
*/
60+
public static PIDMotor flywheel(
61+
PIDMotor motor, PeriodicRegistry periodicRegistry, LinearSystemSim<?, N1, ?> sim) {
62+
return new PIDMotorWrapper(motor, periodicRegistry, sim);
63+
}
64+
65+
private static class MotorWrapper<M extends Motor> extends MotorDecorator<M> {
66+
private final LinearSystemSim<?, N1, ?> sim;
67+
private long lastSimUpdateTimeMillis = 0;
68+
69+
MotorWrapper(M motor, PeriodicRegistry periodicRegistry, LinearSystemSim<?, N1, ?> sim) {
70+
super(motor);
71+
this.sim = sim;
72+
periodicRegistry.addSimulationPeriodic(this::simulationPeriodic);
73+
}
74+
75+
private void simulationPeriodic(RobotState robotState) {
76+
long currentTimeMicros = RobotController.getFPGATime();
77+
sim.update((currentTimeMicros - lastSimUpdateTimeMillis) / 1000.0);
78+
lastSimUpdateTimeMillis = currentTimeMicros;
79+
}
80+
81+
private double toVelocity(ControlMode mode, double demand) {
82+
return switch (mode) {
83+
case VELOCITY, DUTY_CYCLE -> demand;
84+
case VOLTAGE -> demand / RobotController.getBatteryVoltage();
85+
default -> {
86+
throw new IllegalArgumentException("Mode not supported: " + mode);
87+
}
88+
};
89+
}
90+
91+
@Override
92+
public void set(ControlMode mode, double demand) {
93+
double velocity = toVelocity(mode, demand);
94+
super.set(mode, demand);
95+
sim.setInput(velocity);
96+
}
97+
98+
@Override
99+
public void set(ControlMode mode, double demand, double feedForward) {
100+
double velocity = toVelocity(mode, demand);
101+
motor.set(mode, demand, feedForward);
102+
sim.setInput(velocity);
103+
}
104+
}
105+
106+
private static class PIDMotorWrapper extends MotorWrapper<PIDMotor> implements PIDMotor {
107+
108+
PIDMotorWrapper(
109+
PIDMotor motor, PeriodicRegistry periodicRegistry, LinearSystemSim<?, N1, ?> sim) {
110+
super(motor, periodicRegistry, sim);
111+
}
112+
113+
@Override
114+
public void configPIDF(int slot, double p, double i, double d, double f) {
115+
motor.configPIDF(slot, p, i, d, f);
116+
}
117+
118+
@Override
119+
public void configPIDF(double p, double i, double d, double f) {
120+
motor.configPIDF(p, i, d, f);
121+
}
122+
123+
@Override
124+
public void configPID(int slot, double p, double i, double d) {
125+
motor.configPID(slot, p, i, d);
126+
}
127+
128+
@Override
129+
public void configPID(double p, double i, double d) {
130+
motor.configPID(p, i, d);
131+
}
132+
133+
@Override
134+
public Angle getPositionMeasure() {
135+
return motor.getPositionMeasure();
136+
}
137+
138+
@Override
139+
public void setPosition(Angle position) {
140+
motor.setPosition(position);
141+
}
142+
143+
@Override
144+
public AngularVelocity getVelocityMeasure() {
145+
return motor.getVelocityMeasure();
146+
}
147+
}
148+
149+
private MotorSimulation() {
150+
throw new AssertionError("Not instantiable");
151+
}
152+
}

0 commit comments

Comments
 (0)