-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/arm 2024 #7
base: main
Are you sure you want to change the base?
Conversation
The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure to add all files and use Phoenix 6 API only.
import org.littletonrobotics.junction.LogTable; | ||
import org.littletonrobotics.junction.inputs.LoggableInputs; | ||
|
||
public class ArmInputsLogged implements LoggableInputs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use @Autolog
?
|
||
public boolean armIsOutOfFrame () { | ||
Translation2d elbowJoint = getElbowJointPosition(), endPosition = getEndPosition(); | ||
return !(elbowJoint.getX() < 0) || !(endPosition.getX() < 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return !(elbowJoint.getX() < 0) || !(endPosition.getX() < 0); | |
return elbowJoint.getX() >= 0 || endPosition.getX() >= 0; |
But is that true, though? Shouldn't positive values indicate "in frame"?
} | ||
|
||
public boolean armIsOutOfFrame () { | ||
Translation2d elbowJoint = getElbowJointPosition(), endPosition = getEndPosition(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What benefit does creating these variables have?
@Override | ||
public String getSubsystemName () { | ||
return "Arm"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is that needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job!
|
||
private Arm() { | ||
if (Robot.isReal()) { | ||
io = new ArmIOReal(); | ||
} else { | ||
io = new ArmIOReal(); | ||
} | ||
} | ||
|
||
public static Arm getINSTANCE() { | ||
if (INSTANCE == null) { | ||
if (Robot.isReal()) { | ||
INSTANCE = new Arm(new ArmIOSim(inputs)); | ||
|
||
} else { | ||
INSTANCE = new Arm(new ArmIOSim(inputs)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The arm object doesn't need to know whether or not it's real. In the future (already exists in the robot template), there is going to be a static initializing function for each subsystem, to which the robot mode will be passed. So for now, the creation of the io should be done in the getInstance function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if there are more than 2 implementations? For example, Falcon 500 and NEO swerve implementations (Robotinyo? 👀).
io = new ArmIOReal(); | ||
} else { | ||
io = new ArmIOReal(); | ||
} | ||
} | ||
|
||
public static Arm getINSTANCE() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public static Arm getINSTANCE() { | |
public static Arm getInstance() { |
|
||
|
||
|
||
ArmIOReal() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArmIOReal() { | |
public ArmIOReal() { |
elbowMainMotorConfig.CurrentLimits.SupplyCurrentLimit = ArmConstants.CURRENT_LIMIT; | ||
elbowMainMotorConfig.CurrentLimits.StatorCurrentLimit = ArmConstants.CURRENT_LIMIT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this constant 10 ampere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did we do that during the season?
.withKI(ArmConstants.elbowI) | ||
.withKD(ArmConstants.elbowD); | ||
|
||
public static final double SENSOR_TO_MECHANISM_RATIO= 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used to calculate gear ratio in the motor. When using the getPosition()
function it's necessary, because it returns a position times the gear ratio. However, I believe that the parallel function used in the phoenix 5 version was getRotorPosition()
, with the gear ratio taken into account later. Here https://pro.docs.ctr-electronics.com/en/latest/docs/migration/migration-guide/status-signals-guide.html#using-status-signals.
angle = AngleUtil.normalize(angle); | ||
Rotation2d error = new Rotation2d(angle).minus(new Rotation2d(currentShoulderAngle)); | ||
if (elbowEncoder.isConnected()) { | ||
motorControlRequest = new PositionVoltage(angle); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are both the shoulder and the elbow updating the same member?
elbowMainMotor.setControl(new PositionDutyCycle(angle, velocity, true, | ||
elbowFeedforward * ffMultiplier, 1, true) | ||
.withEnableFOC(true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here as before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the appeal of the SingleJointedArmSim
class. However, I recommend using the TalonFXSim
class, because the "api" is the same as the TalonFX
class, allowing us to find bugs in the real usage of the api before the robot comes.
public void updateInputs() { | ||
public void updateInputs(ArmInputs inputs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't actually necessary. You could pass the inputs through the io constructor and have it as a member in the io class (both real and sim).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say to put this class in the io interface, but this is enormous, so I get it.
} | ||
|
||
|
||
private void setShoulderJointAngle(double angle, double ffMultiplier, int velocity) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything in your code (including IO implementations, subsystem code, arm constants, etc) should work with Rotation2d
objects. That way you won't need to cast back and forth everywhere.
public static final Slot0Configs SHOULDER_PID= new Slot0Configs() | ||
.withKP(ArmConstants.shoulderP) | ||
.withKI(ArmConstants.shoulderI) | ||
.withKD(ArmConstants.shoulderD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to create the gains as separate gains. Just write them directly here.
@@ -29,27 +30,12 @@ default void setElbowP(double kP) { | |||
default void updateInputs() { | |||
} | |||
|
|||
String getSubsystemName(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary?
this.shoulderEncoder = new DutyCycleEncoder(Ports.ArmPorts.SHOULDER_ENCODER); | ||
this.elbowEncoder = new DutyCycleEncoder(Ports.ArmPorts.ELBOW_ENCODER); | ||
|
||
this.shoulderMainMotor = new TalonFX(Ports.ArmPorts.SHOULDER_MAIN_MOTOR); | ||
|
||
this.shoulderAuxMotor = new TalonFX(Ports.ArmPorts.SHOULDER_AUX_MOTOR); | ||
|
||
this.elbowMainMotor = new TalonFX(Ports.ArmPorts.ELBOW_MAIN_MOTOR); | ||
|
||
this.elbowAuxMotor = new TalonFX(Ports.ArmPorts.ELBOW_AUX_MOTOR); | ||
|
||
|
||
|
||
shoulderAuxMotor.setControl(new StrictFollower(shoulderMainMotor.getDeviceID())); | ||
elbowAuxMotor.setControl(new StrictFollower(elbowMainMotor.getDeviceID())); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too much whitespace leads to tedious scrolling.
shoulderMainMotorConfig.Voltage.PeakForwardVoltage = ArmConstants.VOLT_COMP_SATURATION; | ||
shoulderMainMotorConfig.Voltage.PeakReverseVoltage = -ArmConstants.VOLT_COMP_SATURATION; | ||
shoulderMainMotorConfig.Slot0 = SHOULDER_PID; | ||
elbowMainMotorConfig.CurrentLimits.SupplyCurrentLimit = ArmConstants.CURRENT_LIMIT; | ||
elbowMainMotorConfig.CurrentLimits.StatorCurrentLimit = ArmConstants.CURRENT_LIMIT; | ||
shoulderMainMotorConfig.Feedback.SensorToMechanismRatio = SENSOR_TO_MECHANISM_RATIO; | ||
shoulderMainMotorConfig.Feedback.RotorToSensorRatio = ROTOR_TO_SENSOR_RATIO; | ||
shoulderMainMotor.getConfigurator().apply(shoulderMainMotorConfig); | ||
|
||
shoulderMainMotor.setNeutralMode(NeutralModeValue.Brake); | ||
shoulderMainMotor.setInverted(MAIN_CLOCKWISE); | ||
|
||
elbowMainMotorConfig.Voltage.PeakForwardVoltage = ArmConstants.VOLT_COMP_SATURATION; | ||
elbowMainMotorConfig.Voltage.PeakReverseVoltage = -ArmConstants.VOLT_COMP_SATURATION; | ||
elbowMainMotorConfig.Slot1 = ELBOW_PID; | ||
elbowMainMotorConfig.CurrentLimits.SupplyCurrentLimit = ArmConstants.CURRENT_LIMIT; | ||
elbowMainMotorConfig.CurrentLimits.StatorCurrentLimit = ArmConstants.CURRENT_LIMIT; | ||
elbowMainMotorConfig.Feedback.SensorToMechanismRatio = SENSOR_TO_MECHANISM_RATIO; | ||
elbowMainMotorConfig.Feedback.RotorToSensorRatio = ROTOR_TO_SENSOR_RATIO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of that should be in the constants file. Use the with*
methods and remove the constants, this will be cleaner and shorter -- you won't need one line to define the constant and another to use it, you'll just have one line total.
angle = AngleUtil.normalize(angle); | ||
Rotation2d error = new Rotation2d(angle).minus(new Rotation2d(currentShoulderAngle)); | ||
if (shoulderEncoder.isConnected()) { | ||
motorControlRequest = new PositionVoltage(angle); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shouldn't instantiate a new object every time, create it only once and change it using withPosition()
.
return Rotation2d.fromRotations(shoulderEncoder.getAbsolutePosition() - shoulderOffset); | ||
} | ||
|
||
public Rotation2d getElbowJointAngle() { | ||
return Rotation2d.fromRotations(elbowEncoder.getAbsolutePosition() - elbowOffset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to set the offset and ration once instead of calculating it yourself. You can find the relevant methods here.
public void setElbowJointAngle(Rotation2d angle) { | ||
setElbowJointAngle(angle.getRadians(), ELBOW_FEED_FORWARD, ELBOW_VELOCITY); | ||
} | ||
|
||
/** | ||
* Sets the angle of the elbow joint. | ||
* | ||
* @param angle desired angle. [degrees] | ||
*/ | ||
private void setElbowJointAngle(double angle, double ffMultiplier, int velocity) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two should accept the same types, hence the private method should also receive a Rotation2d
.
|
||
|
||
@Override | ||
public void toLog(LogTable table) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In your case you can remove toLog
and fromLog
and just use @AutoLog
.
@@ -199,7 +193,7 @@ private void setElbowJointAngle(double angle, double ffMultiplier, int velocity) | |||
public Translation2d getEndPosition() { | |||
Rotation2d shoulderAngle = getShoulderJointAngle(); | |||
Rotation2d elbowAngle = getElbowJointAngle(); | |||
return kinematics.forwardKinematics(shoulderAngle.getRadians(),shoulderAngle.getRadians() + elbowAngle.getRadians() - Math.PI); | |||
return kinematics.forwardKinematics(shoulderAngle.getRadians(), shoulderAngle.getRadians() + elbowAngle.getRadians() - Math.PI); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kinematics should work with Rotation2d
too.
Changing the arm subsystem to work with phoenix 6.