实战:PushBack 进球系统Real-World: PushBack Ball System
3.1 真实比赛中的状态机3.1 State Machines in Real Competitions
上一节我们用升降臂学了状态机的基本概念。现在来看一个真实比赛代码里的状态机 — PushBack 赛季的进球系统(Block System)。In the previous section, we learned the basics of state machines using a lift arm. Now let's look at a state machine from real competition code — the PushBack season's ball scoring system (Block System).
这个系统控制了机器人的吸球、存球、射球整个流程。This system controls the robot's entire process of intaking, storing, and shooting balls.
因为机器人内部有多个"区域"处理球:吸取区(Intake)、中转区(Index)、射球区(Roller)。整个系统像一条流水线,把球从吸入到射出的过程分成多个步骤。Because the robot has multiple "zones" for processing balls: Intake zone, Index zone, and Roller zone. The whole system works like an assembly line, breaking the process from intake to scoring into multiple steps.
3.2 八个状态3.2 Eight States
和升降臂的 3 个状态不同,Block 系统有 8 个状态:Unlike the lift arm's 3 states, the Block system has 8 states:
enum class Block {
IDLE, // 空闲:所有电机停
STORE, // 存球:吸取运转,传感器控速
SCOREHIGH, // 高杆射球:全速正转
SCOREMIDFAST, // 中杆得分:快速射出
SCOREMIDSKILLS, // 中杆得分(技能赛模式)
EJECTFAST, // 快速吐球:全部反转
EJECTSLOW, // 慢速吐球:控制节奏
POSTHOLD // 射球后防掉:短暂反转
};
enum class Block {
IDLE, // 空闲:所有电机停
STORE, // 存球:吸取运转,传感器控速
SCOREHIGH, // 高杆射球:全速正转
SCOREMIDFAST, // 中杆得分:快速射出
SCOREMIDSKILLS, // 中杆得分(技能赛模式)
EJECTFAST, // 快速吐球:全部反转
EJECTSLOW, // 慢速吐球:控制节奏
POSTHOLD // 射球后防掉:短暂反转
};
enum class Block {
IDLE, // 空闲:所有电机停
STORE, // 存球:吸取运转,传感器控速
SCOREHIGH, // 高杆射球:全速正转
SCOREMIDFAST, // 中杆得分:快速射出
SCOREMIDSKILLS, // 中杆得分(技能赛模式)
EJECTFAST, // 快速吐球:全部反转
EJECTSLOW, // 慢速吐球:控制节奏
POSTHOLD // 射球后防掉:短暂反转
};
看起来很多?别慌。核心逻辑和升降臂一模一样:当前是什么状态 → 执行对应动作。Looks like a lot? Don't panic. The core logic is exactly the same as the lift arm: check current state → execute corresponding action.
3.3 状态机主函数3.3 State Machine Main Function
不管有多少个状态,状态机的结构永远是一样的 — 一个 switch:No matter how many states there are, the structure is always the same — one switch:
void blocksystem() {
switch (block_state) {
case Block::IDLE:
idle(); // 所有电机停
break;
case Block::STORE:
store(); // 吸球+存球逻辑
break;
case Block::SCOREHIGH:
score_high(); // 射球逻辑
break;
// ... 其他状态类似
default:
idle(); // 未知状态 → 安全停下
break;
}
}
void blocksystem() {
switch (block_state) {
case Block::IDLE:
idle(); // 所有电机停
break;
case Block::STORE:
store(); // 吸球+存球逻辑
break;
case Block::SCOREHIGH:
score_high(); // 射球逻辑
break;
// ... 其他状态类似
default:
idle(); // 未知状态 → 安全停下
break;
}
}
void blocksystem() {
switch (block_state) {
case Block::IDLE:
idle(); // 所有电机停
break;
case Block::STORE:
store(); // 吸球+存球逻辑
break;
case Block::SCOREHIGH:
score_high(); // 射球逻辑
break;
// ... 其他状态类似
default:
idle(); // 未知状态 → 安全停下
break;
}
}
在 switch 末尾加一个 default,处理"万一状态变量被设成了奇怪的值"的情况。让电机停下来是最安全的选择。Add a default at the end of the switch to handle "what if the state variable gets set to something unexpected." Stopping the motors is the safest choice.
3.4 传感器触发状态转换3.4 Sensor-Triggered State Transitions
升降臂靠按钮切换状态。Block 系统更聪明 — 它靠传感器自动切换。The lift arm uses buttons to switch states. The Block system is smarter — it uses sensors to switch automatically.
机器人内部装了光学传感器,能检测"这个位置有没有球":Optical sensors inside the robot detect "is there a ball at this position":
// 每 20ms 读一次传感器
rollerHasBall = OptRoller.isNearObject(); // 射球区有球?
indexHasBall = OptIndex.isNearObject(); // 中转区有球?
shootHasBall = disShoot.objectDistance(mm) < 100; // 出口有球?
// 每 20ms 读一次传感器
rollerHasBall = OptRoller.isNearObject(); // 射球区有球?
indexHasBall = OptIndex.isNearObject(); // 中转区有球?
shootHasBall = disShoot.objectDistance(mm) < 100; // 出口有球?
// 每 20ms 读一次传感器
rollerHasBall = OptRoller.isNearObject(); // 射球区有球?
indexHasBall = OptIndex.isNearObject(); // 中转区有球?
shootHasBall = disShoot.objectDistance(mm) < 100; // 出口有球?
比如在 STORE(存球)状态中,传感器检测到球已经到位了,就减速或停下来,防止球堵住。这比"转 2 秒停下来"精确得多!For example, in the STORE state, when the sensor detects the ball is in position, it slows down or stops to prevent jamming. This is much more precise than "spin for 2 seconds and stop"!
3.5 后台线程持续运行3.5 Running Continuously in a Background Thread
Block 系统不是只运行一次就完了。它在后台线程里持续运行(下一节会详细讲线程):The Block system doesn't just run once. It runs continuously in a background thread (we'll cover threads in detail next):
// Block 系统后台线程
void block_control() {
while (true) {
// 1. 读传感器
rollerHasBall = OptRoller.isNearObject();
indexHasBall = OptIndex.isNearObject();
// 2. 根据当前状态执行动作
blocksystem();
// 3. 等 20ms 再来一轮
wait(20, msec);
}
}
// Block 系统后台线程
void block_control() {
while (true) {
// 1. 读传感器
rollerHasBall = OptRoller.isNearObject();
indexHasBall = OptIndex.isNearObject();
// 2. 根据当前状态执行动作
blocksystem();
// 3. 等 20ms 再来一轮
wait(20, msec);
}
}
// Block 系统后台线程
void block_control() {
while (true) {
// 1. 读传感器
rollerHasBall = OptRoller.isNearObject();
indexHasBall = OptIndex.isNearObject();
// 2. 根据当前状态执行动作
blocksystem();
// 3. 等 20ms 再来一轮
pros::delay(20);
}
}
每 20 毫秒循环一次:读传感器 → 执行状态对应的动作 → 等一下 → 再来。操控手在主循环里只需要改 block_state 的值,后台线程会自动执行对应的动作。It loops every 20 milliseconds: read sensors → execute the action for the current state → wait → repeat. The driver only needs to change the block_state value in the main loop, and the background thread automatically executes the corresponding action.
3.6 为什么状态机好用?3.6 Why Are State Machines Useful?
- 清晰 — 所有可能的情况一目了然,不会遗漏Clear — all possible situations are visible at a glance, nothing gets missed
- 安全 — 任何时刻机构只做一件事,不会出现"又吸又吐"的混乱Safe — the mechanism only does one thing at a time, no "intaking and ejecting at the same time" chaos
- 好调试 — 出问题了?打印当前状态就知道卡在哪一步Easy to debug — something wrong? Print the current state to see exactly where it's stuck
STORE(存球)状态,操控手按下了"射球"按钮。程序把 block_state 改成了 Block::SCOREHIGH。后台线程下一次循环时会发生什么?The robot is currently in
STORE state. The driver presses the "shoot" button. The program changes block_state to Block::SCOREHIGH.What happens on the background thread's next loop?