subsystem修复
This commit is contained in:
@@ -9,40 +9,29 @@ import com.ctre.phoenix6.signals.MotorAlignmentValue;
|
||||
import edu.wpi.first.math.MathUtil;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
||||
import frc.robot.Constants;
|
||||
|
||||
/**
|
||||
* 进球旋转臂的位置闭环和开环控制。
|
||||
* Intaker rotation control for the 2026 fuel path.
|
||||
*
|
||||
* 机构使用 TalonFX 集成编码器。归零前只允许 SAFE 或受限手动移动,
|
||||
* 避免相对编码器未校准时发生大幅度意外旋转。
|
||||
* The normal match posture is lowered. During shooting, the arm alternates
|
||||
* between lowered and an upward assist position to help fuel enter the chamber.
|
||||
*/
|
||||
public class IntakeRotaterSubsystem extends SubsystemBase {
|
||||
public enum IntakePosition {
|
||||
STOW(Constants.Intaker.intakeRotaterStowPosition),
|
||||
GROUND_INTAKE(Constants.Intaker.intakeRotaterGroundPosition),
|
||||
HANDOFF(Constants.Intaker.intakeRotaterHandoffPosition),
|
||||
SAFE(Constants.Intaker.intakeRotaterSafePosition);
|
||||
|
||||
public final double rotations;
|
||||
|
||||
IntakePosition(double rotations) {
|
||||
this.rotations = rotations;
|
||||
}
|
||||
}
|
||||
|
||||
public final TalonFX leftIntakeRotater;
|
||||
public final TalonFX rightIntakeRotater;
|
||||
private final TalonFX leftIntakeRotater;
|
||||
private final TalonFX rightIntakeRotater;
|
||||
|
||||
private final PositionVoltage positionRequest = new PositionVoltage(0);
|
||||
private final VoltageOut voltageRequest = new VoltageOut(0);
|
||||
private final Follower rightFollower;
|
||||
private final NetworkTable table = NetworkTableInstance.getDefault().getTable("IntakeRotater");
|
||||
|
||||
private IntakePosition goal = IntakePosition.SAFE;
|
||||
private boolean zeroed = false;
|
||||
private double manualVoltage = 0.0;
|
||||
private double targetPosition = Constants.Intaker.intakeRotaterLoweredPosition;
|
||||
private double outputVoltage = 0.0;
|
||||
|
||||
public IntakeRotaterSubsystem() {
|
||||
leftIntakeRotater = new TalonFX(Constants.Intaker.leftIntakeRotaterID);
|
||||
@@ -52,100 +41,61 @@ public class IntakeRotaterSubsystem extends SubsystemBase {
|
||||
rightFollower = new Follower(leftIntakeRotater.getDeviceID(), MotorAlignmentValue.Opposed).withUpdateFreqHz(50);
|
||||
followLeft();
|
||||
|
||||
if (Constants.Intaker.assumeZeroedOnBoot) {
|
||||
// 开机默认机构处于收拢姿态,直接把当前位置记为零点,
|
||||
// 让自动赛阶段的收球/射击门控可以工作;只标定编码器,不命令运动。
|
||||
leftIntakeRotater.setPosition(0.0);
|
||||
rightIntakeRotater.setPosition(0.0);
|
||||
zeroed = true;
|
||||
goal = IntakePosition.STOW;
|
||||
}
|
||||
leftIntakeRotater.setPosition(0.0);
|
||||
rightIntakeRotater.setPosition(0.0);
|
||||
}
|
||||
|
||||
public void setGoal(IntakePosition position) {
|
||||
// 相对编码器没有对准机械零点前,禁止执行大幅度位置闭环移动。
|
||||
goal = position;
|
||||
if (!zeroed && position != IntakePosition.SAFE) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
double target = MathUtil.clamp(
|
||||
position.rotations,
|
||||
Constants.Intaker.intakeRotaterDownLimit,
|
||||
Constants.Intaker.intakeRotaterUpLimit
|
||||
);
|
||||
leftIntakeRotater.setControl(positionRequest.withPosition(target));
|
||||
manualVoltage = 0.0;
|
||||
followLeft();
|
||||
public void lowerForMatch() {
|
||||
setPosition(Constants.Intaker.intakeRotaterLoweredPosition);
|
||||
}
|
||||
|
||||
public void stow() {
|
||||
setGoal(IntakePosition.STOW);
|
||||
public Command lowerForMatchCommand() {
|
||||
return Commands.runOnce(this::lowerForMatch, this);
|
||||
}
|
||||
|
||||
public void deployToGround() {
|
||||
setGoal(IntakePosition.GROUND_INTAKE);
|
||||
public void shootAssist() {
|
||||
double period = Constants.Intaker.intakeRotaterShootAssistPeriodSeconds;
|
||||
double phase = (Timer.getFPGATimestamp() % period) / period;
|
||||
double target = phase < Constants.Intaker.intakeRotaterShootAssistUpDutyCycle
|
||||
? Constants.Intaker.intakeRotaterShootAssistUpPosition
|
||||
: Constants.Intaker.intakeRotaterLoweredPosition;
|
||||
setPosition(target);
|
||||
}
|
||||
|
||||
public void moveToHandoff() {
|
||||
setGoal(IntakePosition.HANDOFF);
|
||||
public void manualRaise() {
|
||||
setVoltage(Constants.Intaker.intakeRotaterManualRaiseVoltage);
|
||||
}
|
||||
|
||||
public void manualLower() {
|
||||
setVoltage(Constants.Intaker.intakeRotaterManualLowerVoltage);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
setVoltage(0.0);
|
||||
}
|
||||
|
||||
public boolean atGoal() {
|
||||
// 未归零时编码器读数没有意义,一律视为未到位,
|
||||
// 让上层射击门控和状态流转不会被假读数放行。
|
||||
if (!zeroed) {
|
||||
return false;
|
||||
}
|
||||
return Math.abs(getPosition() - goal.rotations) <= Constants.Intaker.positionToleranceRotations;
|
||||
}
|
||||
|
||||
public boolean isBlockedByNotZeroed() {
|
||||
// 归零前收到了 SAFE 以外的位置请求:机构被安全逻辑拦下,需要先归零。
|
||||
return !zeroed && goal != IntakePosition.SAFE;
|
||||
}
|
||||
|
||||
public void zeroPosition() {
|
||||
// 只有 intake 物理上处于已知零点姿态时才调用此方法。
|
||||
leftIntakeRotater.setPosition(0.0);
|
||||
rightIntakeRotater.setPosition(0.0);
|
||||
zeroed = true;
|
||||
goal = IntakePosition.STOW;
|
||||
stow();
|
||||
}
|
||||
|
||||
public boolean isZeroed() {
|
||||
return zeroed;
|
||||
}
|
||||
|
||||
public void clearZeroed() {
|
||||
zeroed = false;
|
||||
goal = IntakePosition.SAFE;
|
||||
stop();
|
||||
return Math.abs(getPosition() - targetPosition) <= Constants.Intaker.positionToleranceRotations;
|
||||
}
|
||||
|
||||
public double getPosition() {
|
||||
return leftIntakeRotater.getPosition().getValueAsDouble();
|
||||
}
|
||||
|
||||
public IntakePosition getGoal() {
|
||||
return goal;
|
||||
}
|
||||
|
||||
public void setOpenLoopVoltage(double voltage) {
|
||||
// 归零前允许手动小幅移动,但输出会被限制到较温和的范围。
|
||||
if (!zeroed && Math.abs(voltage) > Constants.Intaker.intakeRotaterManualVoltage) {
|
||||
voltage = Math.copySign(Constants.Intaker.intakeRotaterManualVoltage, voltage);
|
||||
}
|
||||
manualVoltage = MathUtil.clamp(voltage, -12.0, 12.0);
|
||||
leftIntakeRotater.setControl(voltageRequest.withOutput(manualVoltage));
|
||||
private void setPosition(double rotations) {
|
||||
targetPosition = MathUtil.clamp(
|
||||
rotations,
|
||||
Constants.Intaker.intakeRotaterDownLimit,
|
||||
Constants.Intaker.intakeRotaterUpLimit
|
||||
);
|
||||
outputVoltage = 0.0;
|
||||
leftIntakeRotater.setControl(positionRequest.withPosition(targetPosition));
|
||||
followLeft();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
manualVoltage = 0.0;
|
||||
leftIntakeRotater.setControl(voltageRequest.withOutput(0.0));
|
||||
private void setVoltage(double volts) {
|
||||
outputVoltage = MathUtil.clamp(volts, -12.0, 12.0);
|
||||
leftIntakeRotater.setControl(voltageRequest.withOutput(outputVoltage));
|
||||
followLeft();
|
||||
}
|
||||
|
||||
@@ -155,11 +105,9 @@ public class IntakeRotaterSubsystem extends SubsystemBase {
|
||||
|
||||
@Override
|
||||
public void periodic() {
|
||||
table.getEntry("goal").setString(goal.name());
|
||||
table.getEntry("zeroed").setBoolean(zeroed);
|
||||
table.getEntry("blockedByNotZeroed").setBoolean(isBlockedByNotZeroed());
|
||||
table.getEntry("position").setDouble(getPosition());
|
||||
table.getEntry("targetPosition").setDouble(targetPosition);
|
||||
table.getEntry("atGoal").setBoolean(atGoal());
|
||||
table.getEntry("manualVoltage").setDouble(manualVoltage);
|
||||
table.getEntry("outputVoltage").setDouble(outputVoltage);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user