常见问题¶
警告
请小心参考本页中的代码!其中有些代码是故意写错的,以展示潜在的易错点。
异常处理¶
Exceptions are events that occur during the execution of a program, disrupting the normal flow of instructions, used in error events or problems that arise during runtime. An exception can be caught to avoid propagation, otherwise any exception that’s not handled will cause the program flow to stop immediately.
常见的异常情况包括
无效指针异常
当试图调用一个方法或从一个 空值 的变量获取一个对象的属性时,就会出现这种情况,这基本上意味着该变量 还没有 值,或者该值不存在。
该异常是 FTC® 中最常见的异常之一,下面是一个抛出 NullPointerException 的示例:
public class CrashyOpMode extends OpMode { // This call to the "get" method here will throw a NullPointerException. // // The value of the "hardwareMap" variable is null at this point, due to // the way the SDK is limited to define the value of this variable, its // value is defined right before the init() (or runOpMode() in LinearOpModes) // method is called. Servo clawServo = hardwareMap.get(Servo.class, "claw"); @Override public void init() { // This statement won't ever be reached due to the // thrown NullPointerException, explained above, // since it happens before the OpMode starts execution. clawServo.setPosition(0.5); } // ... }
This can be fixed by moving the “Servo” variable value definition to the init (or
runOpMode()in LinearOpModes) method as follows:
public class WorkingOpMode extends OpMode { Servo clawServo = null; @Override public void init() { // This won't throw a NullPointerException since the value of the // "hardwareMap" variable is defined at this point, but note that // the "get" method will return null if the name "claw" isn't // configured. (consult the "Using the SDK" section) clawServo = hardwareMap.get(Servo.class, "claw"); // This statement should be reached and executed now. // // Note that if the "claw" servo is not configured, the value returned // by the hardwareMap will be null as explained before, therefore, // a NullPointerException would be thrown here if that happens. clawServo.setPosition(0.5); } // ... }
目标位置未设置异常
这种异常类型是 SDK 自定义的。这意味着你在设置目标位置前将电机的
RunMode更改为 RUN_TO_POSITION:
// This will throw a "TargetPositionNotSetException" here! motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION); // And this statement won't be reached. motor.setTargetPosition(1120);
要解决这个问题,只需转换语句的顺序;先设置目标位置,然后更改
RunMode即可:
// Setting the target position first motor.setTargetPosition(1120); // Then switching the RunMode motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION);
算术异常
在执行任何非法算术运算(如除以零)时出现:
int number = 128 / 0; // This will throw an ArithmeticException!
处理方法是用一个 try catch 代码块 ,将可能抛出此类异常的代码包围起来:
int number; // Declaring the variable in the outside scope try { // Giving it a value that will possibly throw an ArithmeticException number = 128 / 0; } catch (ArithmeticException e) { // Do something when the ArithmeticException happens. // (The value of the "number" variable will remain 0) }
中断异常
这意味着 SDK 要求操作模式停止,并将其视为正常操作的一部分。中断意味着当前线程已被请求结束,所以当你在 logcat 中看到大量中断时不要惊慌!
如果调用可能抛出中断异常的方法(如
Thread.sleep()),则应使用前面提到的 try catch 语法进行类似处理:try { // Block for 500 milliseconds Thread.sleep(500); } catch(InterruptedException e) { // Tells the current thread (OpMode) to // end the execution as soon as possible Thread.currentThread().interrupt(); }
请注意,LinearOpMode 已经包含了一个速记的 sleep() 方法,本质上就是调用了
Thread.sleep()。(你不应该在 OpMode 中使用 sleep() ,因为它们受到更严格的控制。更多信息请阅读后续章节)
SDK 如何处理异常¶
除了中断异常和其他一些内部特殊情况(这些情况只会导致运行模式结束)外,FTC SDK 会在异常抛出且处理不当时执行 “紧急停止 “例程。这会停止运行模式,并在屏幕上显示完整的堆栈跟踪。使用 Android Studio 时,也可通过 Logcat 查看堆栈跟踪。
备注
在 SDK 8.0 之前,只会显示错误的第一行,并且需要从菜单中选择 “重新启动机器人 “才能再次运行 OpMode。
一般来说,在正式比赛前最好对所有操作模式进行全面调试,因为这些异常会造成干扰。
卡在启动、循环、停止…¶
OpModes是 严格控制的程序 ,SDK要求它们以一定的方式与 init() 、 loop() 等方法一起运行。如果你执行这些方法中的任何一个操作的时间超过了特定的时间(5 秒,或停止命令中的 900 毫秒 ),SDK 就会执行之前解释过的 “紧急停止 “例程,并显示 “卡在`操作`中`”的错误信息。
public class StuckyOpMode extends OpMode {
// ...
@Override
public void loop() {
// Don't do this in a normal iterative OpMode!
// This will cause a "stuck in stop" error after
// 5 seconds, since iterative OpModes shouldn't
// be blocked by loops or any lengthy operation.
while(true) {
// ...
}
}
}
如果需要在 OpMode 中运行任何较长的操作,另一种方法是使用 LinearOpMode。
LinearOpModes 没有那么严格,因为它们的单一 runOpMode() 方法可以更自由地流动,但它们仍然需要写点代码来配合停止请求。以下面的代码为例:
public class StuckyLinearOpMode extends LinearOpMode {
@Override
public void runOpMode() {
// Wait for the driver to press PLAY on the DS
waitForStart();
while(true) {
// Do stuff infinitely
}
}
}
由于 while 循环没有退出条件来配合操作模式的停止,因此,一旦在驱动站中停止,这段代码就会导致 “卡在停止中 “的错误。
为了配合 OpMode 的停止,需要在 runOpMode() 方法中执行的所有阻塞循环中添加 opModeIsActive() 或 !isStopRequested() 条件。有关这些方法的更多信息,请参阅 LinearOpMode与OpMode 页面。
配合停止请求的 LinearOpMode 的示例如下:
public class CooperativeLinearOpMode extends LinearOpMode {
@Override
public void runOpMode() {
while(someCondition && !isStopRequested()) {
// Do something while the "someOtherCondition"
// is true and the OpMode is not stopped.
}
// Wait for the driver to press PLAY on the DS
waitForStart();
while(someOtherCondition && opModeIsActive()) {
// Do something while the "someCondition" is true
// and the OpMode is running (started and not stopped).
}
}
}