diff --git a/src/main/java/frc/robot/Constants.java b/src/main/java/frc/robot/Constants.java index df9b607..d5def06 100644 --- a/src/main/java/frc/robot/Constants.java +++ b/src/main/java/frc/robot/Constants.java @@ -157,6 +157,13 @@ public final class Constants { // atGoal 判定容差。为 0 会导致 atGoal 永远为 false、射击门控永远不通过; // 这里给出可用的占位值,真车调好位置闭环后再收紧。 public static final double positionToleranceRotations = 0.3; + + // 上电时按机构处于收拢(STOW)姿态直接归零。 + // 比赛规则要求开赛前机器人处于合法初始姿态,因此这是安全假设; + // 若维修后姿态不确定,置 false 并用操作员 A 键手动归零。 + // 没有这个假设,自动赛阶段无人能按归零键,收球和射击会被 + // 未归零保护永久拦下。 + public static final boolean assumeZeroedOnBoot = true; } public static class Conveyor { @@ -182,6 +189,10 @@ public final class Constants { // 没有球检测传感器,持球状态由状态机跟踪推断。 // 若跟踪不可靠导致射击被卡,可临时置 false 旁路 hasNote 门控。 public static final boolean hasNoteGateEnabled = true; + // 比赛开局机器人带预装球,开机默认持球为 true; + // 否则自动赛的第一发射击会被 hasNote 门控拦死。 + // 空车调试时可置 false,或用操作员 X 键运行时翻转持球状态。 + public static final boolean assumePreloadedAtBoot = true; // 射击门控是否要求底盘接近静止。 public static final boolean stationaryGateEnabled = true; } diff --git a/src/main/java/frc/robot/RobotContainer.java b/src/main/java/frc/robot/RobotContainer.java index 725f6f2..a3d693e 100644 --- a/src/main/java/frc/robot/RobotContainer.java +++ b/src/main/java/frc/robot/RobotContainer.java @@ -175,15 +175,20 @@ public class RobotContainer { private void configueShooter() { // 射击相关按钮只请求 Superstructure 状态;是否喂球由 Superstructure // 根据飞轮速度、底盘瞄准和进球旋转机构位置统一判断。 - controllerupper.rightTrigger() + // 这些命令 require Superstructure,与自动程序共用需求; + // 用 teleop 门控避免自动赛阶段误触直接取消整个 PathPlanner 自动。 + Trigger teleopOnly = RobotModeTriggers.teleop(); + controllerupper.rightTrigger().and(teleopOnly) .onTrue(operatorControls.requestShootCommand()) .onFalse(operatorControls.requestIdleCommand()); - controllerupper.y() + controllerupper.y().and(teleopOnly) .onTrue(operatorControls.requestPrepShootCommand()) .onFalse(operatorControls.requestIdleCommand()); - controllerupper.b() + controllerupper.b().and(teleopOnly) .onTrue(operatorControls.requestEjectCommand()) .onFalse(operatorControls.requestIdleCommand()); + // 持球状态是无传感器推断值,X 键允许操作员运行时纠正。 + controllerupper.x().onTrue(operatorControls.toggleNotePresentCommand()); controllerupper.povLeft().onTrue(Commands.runOnce(() -> operatorControls.adjustFlywheelSpeedOffset(-1))); controllerupper.povRight().onTrue(Commands.runOnce(() -> operatorControls.adjustFlywheelSpeedOffset(1))); controllerupper.povUp().onTrue(Commands.runOnce(() -> operatorControls.adjustBackboardRateOffset(100))); @@ -215,7 +220,7 @@ public class RobotContainer { private void configueIntaker() { // 进球相关按钮也只请求 Superstructure 状态。 // A 键是校准动作,只应在进球机构处于已知零点姿态时按下。 - controllerupper.leftTrigger() + controllerupper.leftTrigger().and(RobotModeTriggers.teleop()) .onTrue(operatorControls.requestIntakeCommand()) .onFalse(operatorControls.requestIdleCommand()); controllerupper.a().onTrue(operatorControls.resetIntakeRotaterEncoderCommand(intakeRotaterSubsystem)); diff --git a/src/main/java/frc/robot/controls/OperatorControls.java b/src/main/java/frc/robot/controls/OperatorControls.java index fbfbe6d..15afbfa 100644 --- a/src/main/java/frc/robot/controls/OperatorControls.java +++ b/src/main/java/frc/robot/controls/OperatorControls.java @@ -45,6 +45,10 @@ public class OperatorControls { return superstructure.requestManualCommand(); } + public Command toggleNotePresentCommand() { + return superstructure.toggleNotePresentCommand(); + } + public void adjustFlywheelSpeedOffset(double offset) { superstructure.adjustFlywheelSpeedOffset(offset); } diff --git a/src/main/java/frc/robot/subsystems/ConveyorSubsystem.java b/src/main/java/frc/robot/subsystems/ConveyorSubsystem.java index 217abf8..84063b3 100644 --- a/src/main/java/frc/robot/subsystems/ConveyorSubsystem.java +++ b/src/main/java/frc/robot/subsystems/ConveyorSubsystem.java @@ -23,7 +23,8 @@ public class ConveyorSubsystem extends SubsystemBase { private final NetworkTable table = NetworkTableInstance.getDefault().getTable("Conveyor"); private double voltage = 0.0; - private boolean notePresent = false; + // 开局带预装球时默认持球,否则自动赛第一发会被 hasNote 门控拦死。 + private boolean notePresent = Constants.Superstructure.assumePreloadedAtBoot; public ConveyorSubsystem() { mainConveyor = new TalonFX(Constants.Conveyor.mainConveyorID); diff --git a/src/main/java/frc/robot/subsystems/IntakeRotaterSubsystem.java b/src/main/java/frc/robot/subsystems/IntakeRotaterSubsystem.java index 6834aef..3a5376c 100644 --- a/src/main/java/frc/robot/subsystems/IntakeRotaterSubsystem.java +++ b/src/main/java/frc/robot/subsystems/IntakeRotaterSubsystem.java @@ -51,6 +51,15 @@ public class IntakeRotaterSubsystem extends SubsystemBase { rightIntakeRotater.getConfigurator().apply(Constants.Intaker.intakeRotaterSlot0Configs); 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; + } } public void setGoal(IntakePosition position) { diff --git a/src/main/java/frc/robot/subsystems/Superstructure.java b/src/main/java/frc/robot/subsystems/Superstructure.java index 1982ff9..818fe3b 100644 --- a/src/main/java/frc/robot/subsystems/Superstructure.java +++ b/src/main/java/frc/robot/subsystems/Superstructure.java @@ -131,6 +131,12 @@ public class Superstructure extends SubsystemBase { return Commands.runOnce(this::requestManual, this); } + public Command toggleNotePresentCommand() { + // 持球状态是推断值,中途 disable 或漏吸球都可能让它失真; + // 给操作员一个运行时纠正入口(X 键),不用重启机器人。 + return Commands.runOnce(() -> conveyor.setNotePresent(!conveyor.hasNote())); + } + public WantedState getWantedState() { return wantedState; } @@ -188,6 +194,13 @@ public class Superstructure extends SubsystemBase { public void periodic() { // 禁用状态优先级最高,防止旧按钮输入或自动请求在禁用后继续驱动机构。 if (DriverStation.isDisabled()) { + // 进入禁用前先结算持球推断,避免中途 disable 留下失真的 hasNote: + // 喂球中被禁用视为球已交给飞轮;收纳中被禁用视为球已在传送带里。 + if (systemState == SystemState.SHOOTING) { + conveyor.setNotePresent(false); + } else if (systemState == SystemState.INDEXING) { + conveyor.setNotePresent(true); + } setSystemState(SystemState.DISABLED); stopAllMechanisms(); publishTelemetry(); @@ -307,9 +320,11 @@ public class Superstructure extends SubsystemBase { && systemState != SystemState.SHOOTING && systemState != SystemState.CLEANUP) { // 从 intake 流程直接转射击:没有传感器,默认球已吸入。 - if (systemState == SystemState.DEPLOYING_INTAKE + // 未归零被安全逻辑拦下时滚轮从未启动,不可能有球,不置位。 + if ((systemState == SystemState.DEPLOYING_INTAKE || systemState == SystemState.INTAKING - || systemState == SystemState.INDEXING) { + || systemState == SystemState.INDEXING) + && !intakeRotater.isBlockedByNotZeroed()) { conveyor.setNotePresent(true); } setSystemState(SystemState.SPINNING_UP); @@ -342,8 +357,8 @@ public class Superstructure extends SubsystemBase { conveyor.stop(); shooter.stopShooterConveyor(); if (timeInState() > Constants.Superstructure.cleanupSeconds) { - // 一次按键完成一次射击;再射需要重新请求。 - wantedState = WantedState.IDLE; + // 不强行改写 wantedState:清理窗口内重按/按住射击键的请求不会被吞掉, + // 而 hasNote 已清空,门控自然挡住空喂球,只会保持飞轮转速待命。 setSystemState(conveyor.hasNote() ? SystemState.HOLDING_NOTE : SystemState.STOWED); } return;