Skip to content

Commit 8db4a09

Browse files
committed
feat: create arm subsystem public interface
Signed-off-by: Dasun Abeykoon <[email protected]>
1 parent 851603b commit 8db4a09

File tree

1 file changed

+163
-0
lines changed
  • src/main/java/org/frc6423/robot/subsystems/arm

1 file changed

+163
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright (c) 2025 FRC 6423 - Ward Melville Iron Patriots
2+
// https://github.com/wmironpatriots
3+
//
4+
// Open Source Software; you can modify and/or share it under the terms of
5+
// MIT license file in the root directory of this project
6+
7+
package org.frc6423.robot.subsystems.arm;
8+
9+
import static edu.wpi.first.units.Units.Centimeters;
10+
import static edu.wpi.first.units.Units.Degrees;
11+
import static edu.wpi.first.units.Units.Inches;
12+
import static edu.wpi.first.units.Units.KilogramSquareMeters;
13+
import static edu.wpi.first.units.Units.Radians;
14+
15+
import edu.wpi.first.epilogue.Logged;
16+
import edu.wpi.first.math.MathUtil;
17+
import edu.wpi.first.math.filter.LinearFilter;
18+
import edu.wpi.first.math.geometry.Rotation2d;
19+
import edu.wpi.first.units.measure.Angle;
20+
import edu.wpi.first.units.measure.Distance;
21+
import edu.wpi.first.units.measure.MomentOfInertia;
22+
import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d;
23+
import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d;
24+
import edu.wpi.first.wpilibj.smartdashboard.MechanismRoot2d;
25+
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
26+
import edu.wpi.first.wpilibj.util.Color;
27+
import edu.wpi.first.wpilibj.util.Color8Bit;
28+
import edu.wpi.first.wpilibj2.command.Command;
29+
import edu.wpi.first.wpilibj2.command.Commands;
30+
import edu.wpi.first.wpilibj2.command.SubsystemBase;
31+
32+
public class Arm extends SubsystemBase implements AutoCloseable {
33+
/** CONSTANTS */
34+
public static final double PIVOT_GEARING = 50;
35+
36+
public static final double ROLLER_GEARING = 5.8677; // ! this is wrong
37+
38+
public static final MomentOfInertia MOI = KilogramSquareMeters.of(0.001);
39+
public static final Distance LENGTH = Inches.of(7.5);
40+
41+
public static final Angle MAX_ANGLE = Degrees.of(180);
42+
public static final Angle TOLERANCE = Degrees.of(1.5);
43+
44+
@Logged(name = "Arm Hardware Loggables")
45+
private final ArmIO hardware;
46+
47+
private final LinearFilter pivotCurrentFilter = LinearFilter.movingAverage(5);
48+
private final LinearFilter rollerCurrentFilter = LinearFilter.movingAverage(3);
49+
50+
@Logged(name = "Filted Pivot Motor Current (Amps)")
51+
private double filteredPivotCurrent;
52+
53+
@Logged(name = "Filted Roller Motor Current (Amps)")
54+
private double filteredRollerCurrent;
55+
56+
@Logged(name = "Is Zeroed (bool)")
57+
private boolean isZeroed = false;
58+
59+
private final Mechanism2d canvas =
60+
new Mechanism2d(LENGTH.in(Centimeters), LENGTH.in(Centimeters) * 2);
61+
private final MechanismRoot2d root =
62+
canvas.getRoot("pivot", LENGTH.in(Centimeters), LENGTH.in(Centimeters));
63+
private final MechanismLigament2d visualizer =
64+
root.append(
65+
new MechanismLigament2d(
66+
"arm", LENGTH.in(Centimeters), 0.0, 10, new Color8Bit(Color.kAliceBlue)));
67+
68+
public Arm(ArmIO hardware) {
69+
this.hardware = hardware;
70+
71+
SmartDashboard.putData("ArmVisualizer", canvas);
72+
}
73+
74+
@Override
75+
public void periodic() {
76+
hardware.periodic();
77+
78+
filteredPivotCurrent = pivotCurrentFilter.calculate(hardware.getPivotStatorCurrentAmps());
79+
filteredRollerCurrent = rollerCurrentFilter.calculate(hardware.getRollerStatorCurrentAmps());
80+
81+
visualizer.setAngle(
82+
Rotation2d.fromRadians(hardware.getPivotAngleRads()).plus(Rotation2d.kCCW_90deg));
83+
}
84+
85+
/**
86+
* @return true if arm is within a certain tolerance of the setpoint angle
87+
*/
88+
@Logged(name = "Near Setpoint Angle (bool)")
89+
public boolean nearSetpointAngle() {
90+
return MathUtil.isNear(
91+
hardware.getPivotSetpointAngleRads(), hardware.getPivotAngleRads(), TOLERANCE.in(Radians));
92+
}
93+
94+
/**
95+
* @return true when coral hits hardstop
96+
*/
97+
@Logged(name = "Coral Vectored (bool)")
98+
public boolean coralVectored() {
99+
// Checks if roller motors are stalled
100+
// TODO check this value
101+
return Math.abs(filteredRollerCurrent) > 30.0;
102+
}
103+
104+
/**
105+
* Run arm backwards until it hits hardstop to determine zero
106+
*
107+
* @return {@link Command}
108+
*/
109+
public Command runCurrentHoming() {
110+
return this.run(
111+
() -> {
112+
hardware.runPivotVolts(-2.5);
113+
// TODO check this value
114+
})
115+
.until(() -> Math.abs(filteredPivotCurrent) > 50.0)
116+
.finallyDo(
117+
(interupted) -> {
118+
if (!interupted) {
119+
hardware.resetPivotEncoder(0.0);
120+
isZeroed = true;
121+
}
122+
});
123+
}
124+
125+
/**
126+
* Run Arm to specified {@link ArmState}
127+
*
128+
* @param state {@link ArmState} representing hardware setpoints
129+
* @return {@link Command}
130+
*/
131+
public Command runState(ArmState state) {
132+
return this.run(
133+
() -> {
134+
hardware.runPivotAngle(state.angle.getRadians());
135+
hardware.runRollerSpeed(state.rollerSpeedRpm);
136+
});
137+
}
138+
139+
/**
140+
* Hold Arm at specified {@link ArmState}
141+
*
142+
* @param state {@link ArmState} representing hardware setpoints
143+
* @return {@link Command}
144+
*/
145+
public Command holdState(ArmState state) {
146+
return Commands.sequence(
147+
this.run(
148+
() -> {
149+
var currentAngle = hardware.getPivotAngleRads();
150+
var currentSpeed = hardware.getRollerSpeedRpm();
151+
152+
hardware.runPivotAngle(currentAngle);
153+
hardware.runRollerSpeed(currentSpeed);
154+
})
155+
.until(() -> true),
156+
this.run(() -> {}));
157+
}
158+
159+
@Override
160+
public void close() throws Exception {
161+
hardware.close();
162+
}
163+
}

0 commit comments

Comments
 (0)