用陀螺仪转弯Turning with the Gyroscope
4.1 为什么转弯要用陀螺仪?4.1 Why Use a Gyroscope for Turning?
上一节我们用编码器解决了直走不准的问题。但转弯呢?Last lesson we used encoders to fix inaccurate driving. But what about turning?
用编码器也能转弯(左右电机转不同度数),但有个问题:轮子可能打滑。轮子打滑了,编码器以为转了 90 度,实际只转了 70 度。You can turn with encoders too (left and right motors turn different amounts), but there's a problem: wheels can slip. When they slip, the encoder thinks it turned 90 degrees, but it actually only turned 70.
陀螺仪(IMU)直接测量机器人的朝向,不管轮子有没有打滑,它告诉你机器人真正面向哪个方向。The gyroscope (IMU) directly measures the robot's heading. Regardless of wheel slippage, it tells you the robot's true direction.
编码器Encoder:测轮子转了多少 → 推算机器人转了多少(轮子打滑就不准): Measures wheel rotation -> estimates robot rotation (inaccurate if wheels slip)
陀螺仪Gyroscope:直接测机器人朝向 → 准确知道转了多少度: Directly measures robot heading -> accurately knows how far it turned
4.2 turnToHeading:转到指定角度4.2 turnToHeading: Turn to a Specific Angle
陀螺仪的 heading() 返回 0-360 度的朝向。我们写一个函数:一直转,直到朝向达到目标角度。The gyroscope's heading() returns the heading from 0-360 degrees. Let's write a function: keep turning until the heading reaches the target angle.
inertial IMU = inertial(PORT10);
// 转到指定朝向(0-360 度)
void turnToHeading(double targetDeg) {
// 一直转,直到接近目标角度
while (fabs(IMU.heading(degrees) - targetDeg) > 2.0) {
// 判断转弯方向
double error = targetDeg - IMU.heading(degrees);
if (error > 0) {
// 需要右转
LeftMotor.spin(forward, 30, percent);
RightMotor.spin(reverse, 30, percent);
} else {
// 需要左转
LeftMotor.spin(reverse, 30, percent);
RightMotor.spin(forward, 30, percent);
}
wait(10, msec);
}
LeftMotor.stop();
RightMotor.stop();
}
void autonomous() {
IMU.calibrate();
wait(2, seconds); // 等陀螺仪校准完
turnToHeading(90); // 转到 90 度(右转)
turnToHeading(180); // 转到 180 度(转半圈)
}
inertial IMU = inertial(PORT10);
// 转到指定朝向(0-360 度)
void turnToHeading(double targetDeg) {
while (fabs(IMU.heading(degrees) - targetDeg) > 2.0) {
double error = targetDeg - IMU.heading(degrees);
if (error > 0) {
LeftMotor.spin(forward, 30, percent);
RightMotor.spin(reverse, 30, percent);
} else {
LeftMotor.spin(reverse, 30, percent);
RightMotor.spin(forward, 30, percent);
}
wait(10, msec);
}
LeftMotor.stop();
RightMotor.stop();
}
void autonomous() {
IMU.calibrate();
wait(2, seconds);
turnToHeading(90);
turnToHeading(180);
}
pros::Imu imu(10);
// 转到指定朝向(0-360 度)
void turnToHeading(double targetDeg) {
while (fabs(imu.get_heading() - targetDeg) > 2.0) {
double error = targetDeg - imu.get_heading();
if (error > 0) {
left_motor.move(40);
right_motor.move(-40);
} else {
left_motor.move(-40);
right_motor.move(40);
}
pros::delay(10);
}
left_motor.move(0);
right_motor.move(0);
}
void initialize() {
imu.reset();
pros::delay(2000); // 等陀螺仪校准完
}
void autonomous() {
turnToHeading(90);
turnToHeading(180);
}
陀螺仪开机后需要 2-3 秒校准。校准期间机器人不能动!如果校准时机器人在震动,读数会不准。通常在 initialize 或 main 开头校准。The gyroscope needs 2-3 seconds to calibrate after powering on. The robot must stay still during calibration! If the robot vibrates during calibration, readings will be inaccurate. Usually calibrate at the start of initialize or main.
4.3 先碰壁:"冲过头"现象4.3 Hit the Wall: The "Overshoot" Problem
试试上面的代码,你会发现一个问题:Try the code above and you'll find a problem:
你让机器人转到 90 度,但它停在了 95 度或 100 度!因为 motor.stop() 不是瞬间停下的——电机有惯性,会多转一点。You tell the robot to turn to 90 degrees, but it stops at 95 or 100! That's because motor.stop() isn't instant -- the motors have inertia and keep going a bit.
转速越快,冲过头越严重。The faster the turn speed, the worse the overshoot.
用上面的代码让机器人转到 90 度,看看实际停在多少度。试试把速度从 30% 改成 60%,冲过头会更严重吗?Use the code above to turn the robot to 90 degrees and see where it actually stops. Try changing the speed from 30% to 60% -- does the overshoot get worse?
在 Brain 屏幕上打印 IMU.heading(degrees) 来观察实际角度。Print IMU.heading(degrees) on the Brain screen to observe the actual angle.
4.4 简单的解决方法4.4 A Simple Fix
对付冲过头,最简单的方法是:快到了就减速。The simplest fix for overshoot: slow down as you get close.
void turnToHeading(double targetDeg) {
while (fabs(IMU.heading(degrees) - targetDeg) > 1.5) {
double error = targetDeg - IMU.heading(degrees);
// 关键改进:离目标越近,速度越慢
double speed = fabs(error) * 0.5; // 误差越小,速度越低
if (speed > 40) speed = 40; // 最大速度 40%
if (speed < 8) speed = 8; // 最小速度 8%(太低转不动)
if (error > 0) {
LeftMotor.spin(forward, speed, percent);
RightMotor.spin(reverse, speed, percent);
} else {
LeftMotor.spin(reverse, speed, percent);
RightMotor.spin(forward, speed, percent);
}
wait(10, msec);
}
LeftMotor.stop();
RightMotor.stop();
}
void turnToHeading(double targetDeg) {
while (fabs(IMU.heading(degrees) - targetDeg) > 1.5) {
double error = targetDeg - IMU.heading(degrees);
double speed = fabs(error) * 0.5;
if (speed > 40) speed = 40;
if (speed < 8) speed = 8;
if (error > 0) {
LeftMotor.spin(forward, speed, percent);
RightMotor.spin(reverse, speed, percent);
} else {
LeftMotor.spin(reverse, speed, percent);
RightMotor.spin(forward, speed, percent);
}
wait(10, msec);
}
LeftMotor.stop();
RightMotor.stop();
}
void turnToHeading(double targetDeg) {
while (fabs(imu.get_heading() - targetDeg) > 1.5) {
double error = targetDeg - imu.get_heading();
double speed = fabs(error) * 0.5;
if (speed > 50) speed = 50;
if (speed < 10) speed = 10;
if (error > 0) {
left_motor.move(speed);
right_motor.move(-speed);
} else {
left_motor.move(-speed);
right_motor.move(speed);
}
pros::delay(10);
}
left_motor.move(0);
right_motor.move(0);
}
这个 speed = fabs(error) * 0.5 的思路,其实就是 PID 控制中的 P(比例控制)。Level 5 会详细学 PID。This speed = fabs(error) * 0.5 approach is actually the P (proportional control) part of PID control. We'll learn PID in detail in Level 5.
上面的减速方法已经能减少冲过头,但还不够完美。Level 5 会学完整的 PID 控制,让转弯更精确、更平滑。现在先用这个简单版本就够了。The slowdown method above already reduces overshoot, but it's not perfect. Level 5 will teach full PID control for more precise and smoother turns. For now, this simple version is good enough.
turnToHeading(90) 后,再执行 turnToHeading(270)。机器人最终面向哪个方向?The robot starts facing 0 degrees. After running turnToHeading(90), then turnToHeading(270), which direction is the robot facing?