常见问题#
警告
请小心参考本页中的代码!其中有些代码是故意写错的,以展示潜在的易错点。
异常处理#
异常是在程序执行过程中发生的事件,会扰乱正常的指令流程,用于错误事件或运行时出现的问题。异常可以被捕获以避免传播,否则任何未处理的异常都会导致程序流程立即停止。
常见的异常情况包括
无效指针异常
当试图调用一个方法或从一个 空值 的变量获取一个对象的属性时,就会出现这种情况,这基本上意味着该变量* 还没有* 值,或者该值不存在。
该异常是 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); } // ... }
可以将 “Servo”变量值定义移至 init(或 LinearOpModes 中的 runOpMode())方法,如下所示:
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).
}
}
}