修复审查发现的自动赛与状态机问题
- rotater 开机默认按收拢姿态归零(assumeZeroedOnBoot,可关), 否则自动赛无人能按归零键,收球和射击被未归零保护永久拦下 - 预装球开机默认持球(assumePreloadedAtBoot,可关), 否则自动赛第一发被 hasNote 门控拦死 - 新增操作员 X 键运行时翻转持球状态,纠正无传感器推断的失真 - 进入禁用前结算持球推断:SHOOTING 中被禁用视为球已射出, INDEXING 中被禁用视为球已入传送带 - 未归零被拦下的 intake 转射击不再误置持球 - CLEANUP 结束不再强写 wantedState=IDLE,清理窗口内重按射击键不会被吞 - 机构请求按键加 teleop 门控,防止自动赛误触取消 PathPlanner 自动程序 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTEBmjtkz1ouCmn5yytFti
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -45,6 +45,10 @@ public class OperatorControls {
|
||||
return superstructure.requestManualCommand();
|
||||
}
|
||||
|
||||
public Command toggleNotePresentCommand() {
|
||||
return superstructure.toggleNotePresentCommand();
|
||||
}
|
||||
|
||||
public void adjustFlywheelSpeedOffset(double offset) {
|
||||
superstructure.adjustFlywheelSpeedOffset(offset);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user