常见问题#

警告

请小心参考本页中的代码!其中有些代码是故意写错的,以展示潜在的易错点。

异常处理#

异常是在程序执行过程中发生的事件,会扰乱正常的指令流程,用于错误事件或运行时出现的问题。异常可以被捕获以避免传播,否则任何未处理的异常都会导致程序流程立即停止。

常见的异常情况包括

  • 无效指针异常

    • 当试图调用一个方法或从一个 空值 的变量获取一个对象的属性时,就会出现这种情况,这基本上意味着该变量* 还没有* 值,或者该值不存在。

    • 该异常是 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).
       }
   }

}