底盘:左摇杆前后控制前进后退,右摇杆左右控制转弯(Arcade 模式)Drivetrain: left stick forward/backward, right stick turning (Arcade mode)
升降臂:R1 按一下切换到下一个位置(DOWN → MIDDLE → UP → DOWN...)Lift arm: press R1 to cycle to the next position (DOWN → MIDDLE → UP → DOWN...)
升降臂在后台线程里运行,不阻塞底盘操控Lift arm runs in a background thread, not blocking drivetrain control
6.2 分步提示6.2 Step-by-Step Hints
不知道怎么开始?按这个顺序来:Not sure where to start? Follow this order:
先定义 enum class LiftState,三个状态First define enum class LiftState with three states
写升降臂控制函数(while + switch),准备放到后台线程Write the lift control function (while + switch), ready for a background thread
写主循环:Arcade 底盘 + R1 按钮切换状态(记得边沿检测!)Write the main loop: Arcade drivetrain + R1 button to switch states (remember edge detection!)
在 main/opcontrol 里启动线程Start the thread in main/opcontrol
先自己试!Try It Yourself First!
在看参考答案之前,先自己写一遍。写不出来也没关系,但一定要先动手试。Before looking at the answer, write it yourself. It's okay if you can't finish it, but you must try first.
提示:把 Level 1 的 Arcade 底盘代码和 Level 2 的状态机代码拼在一起,就完成了大半。Hint: combine Level 1's Arcade drivetrain code with Level 2's state machine code, and you're mostly done.
6.3 参考答案6.3 Reference Answer
C++ (VEXcode)
// ===== 升降臂状态定义 =====
enum class LiftState { DOWN, MIDDLE, UP };
LiftState liftState = LiftState::DOWN;
// ===== 升降臂后台控制 =====
void liftControl() {
while (true) {
switch (liftState) {
case LiftState::DOWN:
LiftMotor.spinToPosition(0, degrees, false);
break;
case LiftState::MIDDLE:
LiftMotor.spinToPosition(90, degrees, false);
break;
case LiftState::UP:
LiftMotor.spinToPosition(180, degrees, false);
break;
}
wait(20, msec);
}
}
int main() {
// 启动升降臂后台线程
thread liftThread = thread(liftControl);
// 主循环:底盘 + 按钮
while (true) {
// ----- Arcade 底盘 -----
int forward = Controller1.Axis3.position();
int turn = Controller1.Axis1.position();
int left = forward + turn;
int right = forward - turn;
LeftMotor.spin(fwd, left, percent);
RightMotor.spin(fwd, right, percent);
// ----- R1 切换升降臂状态(边沿检测) -----
// VEXcode 可以用 PRESSED 回调,这里用简单写法
// 实际中建议用回调方式
wait(20, msec);
}
}
// R1 回调函数(在 main 开头注册)
// 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;
// }
// });
C++ (VS Code)
// ===== 升降臂状态定义 =====
enum class LiftState { DOWN, MIDDLE, UP };
LiftState liftState = LiftState::DOWN;
// ===== 升降臂后台控制 =====
void liftControl() {
while (true) {
switch (liftState) {
case LiftState::DOWN:
LiftMotor.spinToPosition(0, degrees, false);
break;
case LiftState::MIDDLE:
LiftMotor.spinToPosition(90, degrees, false);
break;
case LiftState::UP:
LiftMotor.spinToPosition(180, degrees, false);
break;
}
wait(20, msec);
}
}
int main() {
// 启动升降臂后台线程
thread liftThread = thread(liftControl);
// 边沿检测变量
bool lastR1 = false;
// 主循环
while (true) {
// ----- Arcade 底盘 -----
int forward = Controller1.Axis3.position();
int turn = Controller1.Axis1.position();
int left = forward + turn;
int right = forward - turn;
LeftMotor.spin(fwd, left, percent);
RightMotor.spin(fwd, right, percent);
// ----- R1 切换升降臂状态 -----
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;
wait(20, msec);
}
}
C++ (PROS)
// ===== 升降臂状态定义 =====
enum class LiftState { DOWN, MIDDLE, UP };
LiftState liftState = LiftState::DOWN;
// ===== 升降臂后台控制 =====
void liftControl() {
while (true) {
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;
}
pros::delay(20);
}
}
void opcontrol() {
// 启动升降臂后台线程
pros::Task liftTask(liftControl);
pros::Controller master(pros::E_CONTROLLER_MASTER);
// 主循环
while (true) {
// ----- Arcade 底盘 -----
int forward = master.get_analog(ANALOG_LEFT_Y);
int turn = master.get_analog(ANALOG_RIGHT_X);
int left = forward + turn;
int right = forward - turn;
LeftMotor.move(left);
RightMotor.move(right);
// ----- R1 切换升降臂状态 -----
if (master.get_digital_new_press(DIGITAL_R1)) {
switch (liftState) {
case LiftState::DOWN: liftState = LiftState::MIDDLE; break;
case LiftState::MIDDLE: liftState = LiftState::UP; break;
case LiftState::UP: liftState = LiftState::DOWN; break;
}
}
pros::delay(20);
}
}
关键点回顾Key Points Review
底盘在主循环控制 — 实时响应摇杆Drivetrain in the main loop — real-time joystick response
升降臂在后台线程运行 — 独立执行状态机Lift arm in a background thread — runs its state machine independently
状态切换在主循环做 — 用边沿检测避免跳档State switching in the main loop — use edge detection to prevent skipping
主循环只改变量,后台线程读变量执行动作 — 从不直接控制同一个电机Main loop only changes variables, background thread reads them and acts — never directly control the same motor from both
检查点:综合理解Checkpoint: Comprehensive Understanding
在上面的程序中,如果把升降臂控制代码直接写在主循环里(不用后台线程),会有什么问题?In the program above, what's the problem if you put the lift control code directly in the main loop (without a background thread)?
正确!如果在主循环里用 wait() 等升降臂到位,那等待期间摇杆输入不会被读取,底盘就"卡住"了。用后台线程可以避免这个问题 — 升降臂慢慢转到位的同时,主循环继续处理操控。Correct! If you use wait() in the main loop for the lift to reach its position, joystick inputs won't be read during the wait, and the drivetrain "freezes." Using a background thread avoids this — the lift arm moves to position while the main loop continues handling driver control.