子系统

子系统是基于命令的范例中机器人组织的基本单元。子系统是*可以作为一个单元*一起操作的机器人硬件集合的抽象。子系统将这个硬件封装为<https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)>,将其“隐藏”在机器人代码的其余部分(例如命令)中,并限制对它的访问,除非通过子系统的公共方法。如果子系统内部暴露在外,以这种方式限制访问可为代码提供一个方便的位置,否则该代码可能会在多个位置重复(例如缩放电动机输出或检查限位开关)。它还允许将子系统工作方式的特定细节(“实现”)的更改与其余的机器人代码隔离开,从而在设计约束发生更改时进行实质性更改要容易得多。

子系统还充当“ CommandScheduler”资源管理系统的骨干。命令可以通过指定与哪些子系统交互来声明资源需求。调度程序永远不会同时调度多个需要给定子系统的命令。尝试调度需要已在使用中的子系统的命令时,将中断当前正在运行的命令(如果该命令已被安排为可中断),否则将被忽略。

子系统可以与“默认命令”相关联,当当前没有其他命令正在使用子系统时,将自动计划这些子系统。这对于连续的“后台”操作(例如控制机器人驱动器或将手臂保持在设定点)很有用。子系统的“ periodic()”方法可以实现类似的功能,该方法在每次调度程序运行时运行一次;团队应该尝试在其代码库中保持一致,以了解通过这两种方法可以实现哪些功能。还有一个SimulationPeriodic()方法与periodic()类似,不同的是它仅在模拟</docs/software/wpilib-tools/robot-simulation/introduction>期间运行,可用于更新机器人的状态。子系统在子系统的基于命令的库中由子系统接口(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Subsystem.html>`__,C ++)表示。

创建子系统

对于大多数用户而言,创建子系统的推荐方法是将抽象类SubsystemBase子类化(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/SubsystemBase.html> __,C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1SubsystemBase.html> __),如基于命令的模板(Java <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java>`__, C ++ <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/subsystems/ExampleSubsystem.h>`__):

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import edu.wpi.first.wpilibj2.command.SubsystemBase;

public class ExampleSubsystem extends SubsystemBase {
  /**
   * Creates a new ExampleSubsystem.
   */
  public ExampleSubsystem() {

  }

  @Override
  public void periodic() {
    // This method will be called once per scheduler run
  }
}

此类在基本的“Subsystem”接口之上包含一些便利功能:它会在其构造函数中自动调用”register()”方法以向调度程序注册子系统(这对于在调度程序运行时调用“periodic()”方法是必须的),并实现“Sendable”接口,以便将其发送到仪表板以显示/记录相关状态信息。

寻求更高灵活性的高级用户可以简单地创建一个实现“Subsystem”接口的类。

简单子系统示例

在实践中,功能子系统会是什么样?以下是HatchBot示例项目中的一个简单的气动孵化机制(JavaC ++):

 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems;

import edu.wpi.first.wpilibj.DoubleSolenoid;
import edu.wpi.first.wpilibj2.command.SubsystemBase;

import edu.wpi.first.wpilibj.examples.hatchbottraditional.Constants.HatchConstants;

import static edu.wpi.first.wpilibj.DoubleSolenoid.Value.kForward;
import static edu.wpi.first.wpilibj.DoubleSolenoid.Value.kReverse;

/**
 * A hatch mechanism actuated by a single {@link DoubleSolenoid}.
 */
public class HatchSubsystem extends SubsystemBase {
  private final DoubleSolenoid m_hatchSolenoid =
      new DoubleSolenoid(HatchConstants.kHatchSolenoidModule, HatchConstants.kHatchSolenoidPorts[0],
                         HatchConstants.kHatchSolenoidPorts[1]);

  /**
   * Grabs the hatch.
   */
  public void grabHatch() {
    m_hatchSolenoid.set(kForward);
  }

  /**
   * Releases the hatch.
   */
  public void releaseHatch() {
    m_hatchSolenoid.set(kReverse);
  }
}

请注意,子系统从外部代码隐藏了DoubleSolenoid的存在(被声明为``private’’),而是公开公开了两个更高级别的描述性机器人动作:``grabHatch()’’和``releaseHatch()’’ ``。以这种方式“隐藏”双螺线管之类的“实现细节”是极为重要的;这样可以确保子系统外部的代码永远不会导致螺线管处于意外状态。它还允许用户更改实现方式(例如,可以使用电动机代替气动方式),而子系统外部的任何代码都不必随之更改。

设置默认命令

注解

在基于C ++命令的库中,CommandScheduler“拥有”默认命令对象——因此,传递给“SetDefaultCommand()”方法的对象将被移动或复制,具体取决于它是右值还是左值(rvalue/lvalue explanation)。这里的示例通过使用“std::move()”强制转换为右值来确保使用移动语义。

“默认命令”是每当子系统不被另一个命令使用时自动运行的命令。

为子系统设置默认命令非常容易:简单地调用“CommandScheduler.getInstance().setDefaultCommand()”,或调用“Subsystem”接口的“setDefaultCommand()”方法:

CommandScheduler.getInstance().setDefaultCommand(exampleSubsystem, exampleCommand);
exampleSubsystem.setDefaultCommand(exampleCommand);