状态机思维State Machine Thinking
2.1 什么是状态机?2.1 What Is a State Machine?
想象一下你家的电风扇。它有一个按钮,按一下:Imagine your electric fan at home. It has one button — press it:
- 关 → 低速Off → Low Speed
- 低速 → 中速Low Speed → Medium Speed
- 中速 → 高速Medium Speed → High Speed
- 高速 → 关High Speed → Off
风扇在任何时刻都处于一个"状态",按按钮就切换到下一个状态。这就是状态机的核心思想。The fan is always in one "state" at any given moment, and pressing the button switches it to the next state. This is the core idea of a state machine.
机构在任何时刻只能处于一个状态。通过按钮、传感器或条件判断,从一个状态切换到另一个状态。A mechanism can only be in one state at any given time. It switches from one state to another through buttons, sensors, or conditions.
2.2 用 enum class 定义状态2.2 Defining States with enum class
在 C++ 里,我们用 enum class(枚举类)来列出所有可能的状态。In C++, we use enum class (enumeration class) to list all possible states.
比如升降臂有三个位置:最低、中间、最高。For example, a lift arm has three positions: lowest, middle, and highest.
// 定义升降臂的三个状态
enum class LiftState {
DOWN, // 最低位置
MIDDLE, // 中间位置
UP // 最高位置
};
// 当前状态(一开始在最低位置)
LiftState liftState = LiftState::DOWN;
// 定义升降臂的三个状态
enum class LiftState {
DOWN, // 最低位置
MIDDLE, // 中间位置
UP // 最高位置
};
// 当前状态(一开始在最低位置)
LiftState liftState = LiftState::DOWN;
// 定义升降臂的三个状态
enum class LiftState {
DOWN, // 最低位置
MIDDLE, // 中间位置
UP // 最高位置
};
// 当前状态(一开始在最低位置)
LiftState liftState = LiftState::DOWN;
enum class 就是给每个状态起一个名字。比起用数字 0、1、2 代表状态,LiftState::DOWN 一看就知道是什么意思。enum class gives each state a name. Compared to using numbers 0, 1, 2 for states, LiftState::DOWN is instantly clear.
2.3 用 switch/case 执行不同状态的动作2.3 Using switch/case for Different State Actions
定义好状态后,用 switch 来判断当前是哪个状态,然后执行对应的动作:After defining states, use switch to check the current state and execute the corresponding action:
// 根据当前状态,让升降臂去到对应位置
switch (liftState) {
case LiftState::DOWN:
LiftMotor.spinToPosition(0, degrees);
break;
case LiftState::MIDDLE:
LiftMotor.spinToPosition(90, degrees);
break;
case LiftState::UP:
LiftMotor.spinToPosition(180, degrees);
break;
}
// 根据当前状态,让升降臂去到对应位置
switch (liftState) {
case LiftState::DOWN:
LiftMotor.spinToPosition(0, degrees);
break;
case LiftState::MIDDLE:
LiftMotor.spinToPosition(90, degrees);
break;
case LiftState::UP:
LiftMotor.spinToPosition(180, degrees);
break;
}
// 根据当前状态,让升降臂去到对应位置
switch (liftState) {
case LiftState::DOWN:
LiftMotor.move_absolute(0, 100);
break;
case LiftState::MIDDLE:
LiftMotor.move_absolute(90, 100);
break;
case LiftState::UP:
LiftMotor.move_absolute(180, 100);
break;
}
每个 case 后面都要写 break;,否则程序会"穿透"到下一个 case 继续执行。这是 C++ 初学者最容易犯的错误之一。Every case must end with break;, otherwise the program will "fall through" to the next case and keep running. This is one of the most common mistakes for C++ beginners.
2.4 用按钮切换状态2.4 Switching States with Buttons
现在我们要实现:按一下遥控器按钮,升降臂切换到下一个状态。Now we want to implement: press a controller button once, and the lift arm switches to the next state.
关键概念:边沿检测。我们要检测按钮"刚按下"的那一瞬间,而不是"一直按着"。Key concept: edge detection. We need to detect the moment the button is "just pressed," not "being held down."
如果用 pressing() 检测,按钮按下的那一帧状态已经切了,但你手还没松开,下一帧又切了!一按就跳好几个状态。If you use pressing(), the state switches on the first frame the button is pressed, but your finger is still holding it — next frame it switches again! One press skips through multiple states.
解决办法:用 justPressed(边沿检测),只在按下的第一帧触发一次。Solution: use justPressed (edge detection), which only triggers once on the first frame of the press.
// 在 while(true) 循环里
// ButtonR1 刚按下时,切换到下一个状态
if (Controller1.ButtonR1.PRESSED) {
switch (liftState) {
case LiftState::DOWN:
liftState = LiftState::MIDDLE;
break;
case LiftState::MIDDLE:
liftState = LiftState::UP;
break;
case LiftState::UP:
liftState = LiftState::DOWN;
break;
}
}
// 注意:VEXcode 中 PRESSED 是事件回调
// 实际中可以在 callback 里写状态切换
// 边沿检测变量
bool lastR1 = false;
// 在 while(true) 循环里
bool currR1 = Controller1.ButtonR1.pressing();
if (currR1 && !lastR1) {
// 按下的瞬间才触发
switch (liftState) {
case LiftState::DOWN:
liftState = LiftState::MIDDLE;
break;
case LiftState::MIDDLE:
liftState = LiftState::UP;
break;
case LiftState::UP:
liftState = LiftState::DOWN;
break;
}
}
lastR1 = currR1;
// 边沿检测变量
bool lastR1 = false;
// 在 while(true) 循环里
bool currR1 = master.get_digital(DIGITAL_R1);
if (currR1 && !lastR1) {
// 按下的瞬间才触发
switch (liftState) {
case LiftState::DOWN:
liftState = LiftState::MIDDLE;
break;
case LiftState::MIDDLE:
liftState = LiftState::UP;
break;
case LiftState::UP:
liftState = LiftState::DOWN;
break;
}
}
lastR1 = currR1;
边沿检测的原理:记住上一帧按钮的状态,只有"上一帧没按、这一帧按了"才算一次按下。How edge detection works: remember the button state from the last frame. Only count it as a press when "not pressed last frame, pressed this frame."
2.5 动手练习2.5 Practice
画一个状态转换图:升降臂有 DOWN、MIDDLE、UP 三个状态,按 R1 向上切换,按 R2 向下切换。Draw a state transition diagram: the lift arm has DOWN, MIDDLE, UP states. R1 switches up, R2 switches down.
思考:按 R2 时如果已经在 DOWN 状态,应该怎么处理?Think: what should happen if R2 is pressed when already in the DOWN state?
MIDDLE。操控手快速按了 R1 两次。最终状态是什么?The lift arm is currently in MIDDLE state. The driver quickly presses R1 twice. What's the final state?