Skip to content
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2b55554
Only "works" for talonfx but I need to commit to test
Strflightmight09 May 10, 2025
3b0f64d
Better prob
Strflightmight09 May 10, 2025
0393268
Oops
Strflightmight09 May 10, 2025
b99cc94
Trying smth
Strflightmight09 May 10, 2025
c4c8b1a
Oops
Strflightmight09 May 10, 2025
e8db114
Testing
Strflightmight09 May 10, 2025
e0b76dd
Remove testing (that part works)
Strflightmight09 May 10, 2025
ef1488b
More testing
Strflightmight09 May 10, 2025
75c3b8b
Omg I'm so stupid
Strflightmight09 May 10, 2025
49a3515
nvm
Strflightmight09 May 10, 2025
88e48ef
Wrong, but not _the_ problem
Strflightmight09 May 10, 2025
b416895
Test
Strflightmight09 May 11, 2025
4cf4d18
I should prob do this
Strflightmight09 May 11, 2025
c43315a
SImplify
Strflightmight09 May 11, 2025
68ecc92
_
Strflightmight09 May 11, 2025
a80bf39
Oops
Strflightmight09 May 11, 2025
efaf9fc
Better logging
Strflightmight09 May 11, 2025
a40296f
Fix
Strflightmight09 May 11, 2025
723cd44
Fix
Strflightmight09 May 11, 2025
948089f
Added arm even tho it doesn't work
Strflightmight09 May 14, 2025
395f300
Merge branch 'main' into subsystem-wrapper
Strflightmight09 Jul 1, 2025
e71735a
@ShmayaR - Have fun :)
Strflightmight09 Jul 1, 2025
0d053c8
fixed up the simpleMotorSubsystem (still missing simulation)
ShmayaR Jul 3, 2025
612c1a7
added logging stuff
ShmayaR Jul 3, 2025
17c777e
added option for setting target state with velocity
ShmayaR Jul 4, 2025
9be9ce1
@noamgreen12 CODEEEEEEEEEEEEEEEEEEEE
Strflightmight09 Jul 15, 2025
b9f1289
Added visualization offset @noamgreen12...
Strflightmight09 Jul 19, 2025
2340eac
Update SimpleMotorSubsystem.java
ShmayaR Jul 21, 2025
853e295
angular velocity gets logged instead of linear and set target velocit…
ShmayaR Jul 22, 2025
fd7a890
updated target state first
ShmayaR Jul 24, 2025
b933b18
set mechanism to update based on control mode
ShmayaR Jul 24, 2025
41fb35b
Update SimpleMotorSubsystem.java
ShmayaR Jul 24, 2025
1eab762
added position (in degrees) to the update log
ShmayaR Jul 24, 2025
a4f05cc
update name
ShmayaR Jul 24, 2025
8977f6d
put position on top
ShmayaR Jul 24, 2025
630e5b4
updated at target state to work with voltage control mode
ShmayaR Jul 24, 2025
91a72ee
added tolerances to at target state function
ShmayaR Jul 27, 2025
abc0f01
contol mode is now set when subsystem is created
ShmayaR Jul 27, 2025
62c0da4
Update SimpleMotorSubsystem.java
ShmayaR Jul 27, 2025
0316909
removed voltage tolerance and put things that belong in the config in…
ShmayaR Jul 27, 2025
c23b57d
at target state no longer checks the voltage but just if it has one
ShmayaR Jul 27, 2025
f03aecc
update name
ShmayaR Jul 28, 2025
c767967
Update SimpleMotorSubsystem.java
ShmayaR Jul 28, 2025
aef47a2
blahdwewed
ShmayaR Jul 28, 2025
49c96d7
fixed if statemennt
ShmayaR Jul 28, 2025
f420000
removed new line
ShmayaR Jul 28, 2025
081cabd
added java docs to flywheel
ShmayaR Aug 7, 2025
395e402
added docs for the rest of the configs
ShmayaR Aug 7, 2025
1e2ae32
changed interface to only have 1 thing difh
ShmayaR Aug 7, 2025
8b89049
added java docs for the interfaces
ShmayaR Aug 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.trigon.hardware.subsystems.arm;

import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.system.plant.DCMotor;
import edu.wpi.first.wpilibj.util.Color;

public class ArmConfiguration {
public String name = "";
public double
lengthMeters = 1,
maximumVelocity = 1,
maximumAcceleration = 1,
maximumJerk = 1,
sysIDRampRate = 1,
sysIDStepVoltage = 1,
gearRatio = 1,
massKilograms = 1,
visualizationOffset = 1;
public Rotation2d
maximumAngle = Rotation2d.kZero,
minimumAngle = Rotation2d.kZero,
angleTolerance = Rotation2d.kZero;
public boolean
focEnabled = true,
shouldSimulateGravity = true;
public Color mechanismColor = Color.kBlue;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public Color mechanismColor = Color.kBlue;
public Color mechanism2DColor = Color.kBlue;

Rn it sounds like the physical colour of the mechanism

public DCMotor gearbox = DCMotor.getKrakenX60Foc(1);
}
160 changes: 160 additions & 0 deletions src/main/java/org/trigon/hardware/subsystems/arm/ArmSubsystem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package org.trigon.hardware.subsystems.arm;

import com.ctre.phoenix6.controls.ControlRequest;
import com.ctre.phoenix6.controls.DynamicMotionMagicVoltage;
import com.ctre.phoenix6.controls.VoltageOut;
import edu.wpi.first.math.geometry.*;
import edu.wpi.first.units.Units;
import edu.wpi.first.wpilibj.sysid.SysIdRoutineLog;
import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine;
import org.littletonrobotics.junction.Logger;
import org.trigon.hardware.RobotHardwareStats;
import org.trigon.hardware.phoenix6.talonfx.TalonFXMotor;
import org.trigon.hardware.phoenix6.talonfx.TalonFXSignal;
import org.trigon.hardware.simulation.SingleJointedArmSimulation;
import org.trigon.utilities.mechanisms.SingleJointedArmMechanism2d;

public class ArmSubsystem {
private final TalonFXMotor motor;
private final Pose3d originPoint;
private final String name;
private final double
maximumVelocity,
maximumAcceleration,
maximumJerk,
visualizationOffsetFromGravityOffset;
private final Rotation2d angleTolerance;
private final VoltageOut voltageRequest;
private final DynamicMotionMagicVoltage positionRequest;
private final SingleJointedArmMechanism2d mechanism;
private final SysIdRoutine.Config sysIDConfig;
private ArmState targetState;

public ArmSubsystem(TalonFXMotor motor, ArmConfiguration config, Pose3d originPoint) {
this.motor = motor;
this.originPoint = originPoint;

name = config.name;
maximumVelocity = config.maximumVelocity;
maximumAcceleration = config.maximumAcceleration;
maximumJerk = config.maximumJerk;
angleTolerance = config.angleTolerance;
visualizationOffsetFromGravityOffset = config.visualizationOffset;
mechanism = new SingleJointedArmMechanism2d(name + "Mechanism", config.lengthMeters, config.mechanismColor);
voltageRequest = new VoltageOut(0).withEnableFOC(config.focEnabled);
positionRequest = new DynamicMotionMagicVoltage(0, maximumVelocity, maximumAcceleration, maximumJerk).withEnableFOC(config.focEnabled);
sysIDConfig = new SysIdRoutine.Config(
Units.Volts.of(config.sysIDRampRate).per(Units.Seconds),
Units.Volts.of(config.sysIDStepVoltage),
Units.Second.of(1000)
);
motor.setPhysicsSimulation(
new SingleJointedArmSimulation(
config.gearbox,
config.gearRatio,
config.massKilograms,
config.lengthMeters,
config.minimumAngle,
config.maximumAngle,
config.shouldSimulateGravity
));
}

public String getName() {
return name;
}

public SysIdRoutine.Config getSysIdConfig() {
return sysIDConfig;
}

public void setBrake(boolean brake) {
motor.setBrake(brake);
}

public void stop() {
motor.stopMotor();
}

public void updateLog(SysIdRoutineLog log) {
log.motor(name)
.angularPosition(Units.Rotations.of(motor.getSignal(TalonFXSignal.POSITION)))
.angularVelocity(Units.RotationsPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
.voltage(Units.Volts.of(motor.getSignal(TalonFXSignal.MOTOR_VOLTAGE)));
}

public void updateMechanism() {
logComponentPose();

mechanism.update(
getAngle(),
Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE) + (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset))
);
}

public void updatePeriodically() {
motor.update();
Logger.recordOutput(name + "/CurrentAngle", getAngle());
}

public void sysIDDrive(double targetVoltage) {
motor.setControl(voltageRequest.withOutput(targetVoltage));
}

public boolean atState(ArmState targetState) {
return this.targetState == targetState && atTargetState();
}

public boolean atTargetState() {
if (targetState == null)
return false;
final double currentToTargetStateDifferenceDegrees = Math.abs(targetState.getTargetAngle().minus(getAngle()).getDegrees());
return currentToTargetStateDifferenceDegrees < angleTolerance.getDegrees();
}

public Rotation2d getAngle() {
return Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.POSITION) + (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset));
}

public void setTargetState(ArmState targetState) {
this.targetState = targetState;
setTargetState(targetState.getTargetAngle(), targetState.getSpeedScalar());
}

public void setTargetState(Rotation2d targetAngle, double speedScalar) {
scalePositionRequestSpeed(speedScalar);
setTargetAngle(targetAngle);
}

public void setControl(ControlRequest request) {
motor.setControl(request);
}

private void scalePositionRequestSpeed(double speedScalar) {
positionRequest.Velocity = maximumVelocity * speedScalar;
positionRequest.Acceleration = maximumAcceleration * speedScalar;
positionRequest.Jerk = maximumJerk * speedScalar;
}

private void setTargetAngle(Rotation2d targetAngle) {
setControl(positionRequest.withPosition(targetAngle.getRotations() - (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset)));
}

private void logComponentPose() {
Logger.recordOutput("Poses/Components/" + name + "Pose", calculateComponentPose());
}

private Pose3d calculateComponentPose() {
final Transform3d armTransform = new Transform3d(
new Translation3d(),
new Rotation3d(0, getAngle().getRadians(), 0)
);
return originPoint.transformBy(armTransform);
}

public interface ArmState {
Rotation2d getTargetAngle();

double getSpeedScalar();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.trigon.hardware.subsystems.elevator;

import edu.wpi.first.math.system.plant.DCMotor;
import edu.wpi.first.wpilibj.util.Color;

public class ElevatorConfiguration {
public String name = "";
public double
positionToleranceMeters = 1,
drumRadiusMeters = 1,
minimumHeight = 1,
maximumHeight = 1,
maximumVelocity = 1,
maximumAcceleration = 1,
maximumJerk = 1,
sysIDRampRate = 1,
sysIDStepVoltage = 1,
gearRatio = 1,
massKilograms = 1;
public boolean
focEnabled = true,
shouldSimulateGravity = true;
public Color mechanismColor = Color.kBlue;
public DCMotor gearbox = DCMotor.getKrakenX60Foc(1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package org.trigon.hardware.subsystems.elevator;

import com.ctre.phoenix6.controls.ControlRequest;
import com.ctre.phoenix6.controls.DynamicMotionMagicVoltage;
import com.ctre.phoenix6.controls.VoltageOut;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.math.geometry.Translation3d;
import edu.wpi.first.units.Units;
import edu.wpi.first.wpilibj.sysid.SysIdRoutineLog;
import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine;
import org.littletonrobotics.junction.Logger;
import org.trigon.hardware.phoenix6.talonfx.TalonFXMotor;
import org.trigon.hardware.phoenix6.talonfx.TalonFXSignal;
import org.trigon.hardware.simulation.ElevatorSimulation;
import org.trigon.utilities.Conversions;
import org.trigon.utilities.mechanisms.ElevatorMechanism2d;

public class ElevatorSubsystem {
private final TalonFXMotor motor;
private final Pose3d[] stagesOriginPoints;
private final String name;
private final double
positionToleranceMeters,
drumRadiusMeters,
maximumVelocity,
maximumAcceleration,
maximumJerk;
private final VoltageOut voltageRequest;
private final DynamicMotionMagicVoltage positionRequest;
private final ElevatorMechanism2d mechanism;
private final SysIdRoutine.Config sysIDConfig;
private ElevatorState targetState;

public ElevatorSubsystem(TalonFXMotor motor, ElevatorConfiguration config, Pose3d... stagesOriginPoints) {
this.motor = motor;
this.stagesOriginPoints = stagesOriginPoints;

name = config.name;
positionToleranceMeters = config.positionToleranceMeters;
drumRadiusMeters = config.drumRadiusMeters;
maximumVelocity = config.maximumVelocity;
maximumAcceleration = config.maximumAcceleration;
maximumJerk = config.maximumJerk;
mechanism = new ElevatorMechanism2d(name + "Mechanism", config.maximumHeight, config.minimumHeight, config.mechanismColor);
voltageRequest = new VoltageOut(0).withEnableFOC(config.focEnabled);
positionRequest = new DynamicMotionMagicVoltage(0, maximumVelocity, maximumAcceleration, maximumJerk).withEnableFOC(config.focEnabled);
sysIDConfig = new SysIdRoutine.Config(
Units.Volts.of(config.sysIDRampRate).per(Units.Seconds),
Units.Volts.of(config.sysIDStepVoltage),
Units.Second.of(1000)
);
motor.setPhysicsSimulation(
new ElevatorSimulation(
config.gearbox,
config.gearRatio,
config.massKilograms,
config.drumRadiusMeters,
config.minimumHeight,
config.maximumHeight,
config.shouldSimulateGravity
));
}

public String getName() {
return name;
}

public SysIdRoutine.Config getSysIdConfig() {
return sysIDConfig;
}

public void setBrake(boolean brake) {
motor.setBrake(brake);
}

public void stop() {
motor.stopMotor();
}

public void updateLog(SysIdRoutineLog log) {
log.motor(name)
.linearPosition(Units.Meters.of(getPositionRotations()))
.linearVelocity(Units.MetersPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
.voltage(Units.Volts.of(motor.getSignal(TalonFXSignal.MOTOR_VOLTAGE)));
}

public void updateMechanism() {
logComponentPoses();

mechanism.update(
getPositionRotations(),
motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE)
);
}

public void updatePeriodically() {
motor.update();
Logger.recordOutput(name + "/CurrentPositionMeters", getPositionMeters());
}

public void sysIDDrive(double targetVoltage) {
motor.setControl(voltageRequest.withOutput(targetVoltage));
}

public boolean atState(ElevatorState targetState) {
return this.targetState == targetState && atTargetState();
}

public boolean atTargetState() {
if (targetState == null)
return false;
final double currentToTargetStateDifference = Math.abs(targetState.getTargetPositionMeters() - getPositionMeters());
return currentToTargetStateDifference < positionToleranceMeters;
}

public double getPositionRotations() {
return motor.getSignal(TalonFXSignal.POSITION);
}

public double getPositionMeters() {
return rotationsToMeters(getPositionRotations());
}

public void setTargetState(ElevatorState targetState) {
this.targetState = targetState;
setTargetState(targetState.getTargetPositionMeters(), targetState.getSpeedScalar());
}

public void setTargetState(double targetPositionMeters, double speedScalar) {
scalePositionRequestSpeed(speedScalar);
setTargetPositionRotations(metersToRotations(targetPositionMeters));
}

public void setControl(ControlRequest request) {
motor.setControl(request);
}

private void scalePositionRequestSpeed(double speedScalar) {
positionRequest.Velocity = maximumVelocity * speedScalar;
positionRequest.Acceleration = maximumAcceleration * speedScalar;
positionRequest.Jerk = maximumJerk * speedScalar;
}

private void setTargetPositionRotations(double targetPositionRotations) {
setControl(positionRequest.withPosition(targetPositionRotations));
}

private void logComponentPoses() {
for (int i = 0; i < stagesOriginPoints.length; i++)
Logger.recordOutput("Poses/Components/" + name + "Pose" + i, calculateComponentPose(i));
}

private Pose3d calculateComponentPose(int targetStage) {
final Transform3d elevatorTransform = new Transform3d(
new Translation3d(0, 0, getPositionMeters() / (targetStage + 1)),
new Rotation3d()
);
return stagesOriginPoints[targetStage].transformBy(elevatorTransform);
}

private double rotationsToMeters(double positionRotations) {
return Conversions.rotationsToDistance(positionRotations, drumRadiusMeters * 2);
}

private double metersToRotations(double positionMeters) {
return Conversions.distanceToRotations(positionMeters, drumRadiusMeters * 2);
}

public interface ElevatorState {
double getTargetPositionMeters();

double getSpeedScalar();
}
}
Loading
Loading