Subsistemas

Los subsistemas son la unidad básica de organización de los robots en el paradigma basado en comandos. Un subsistema es una abstracción de un conjunto de hardware del robot que funciona como una unidad. Los subsistemas encapsulan este hardware, «escondiéndolo» del resto del código del robot (por ejemplo, los comandos) y restringiendo el acceso a él excepto a través de los métodos públicos del subsistema. Restringir el acceso de esta manera proporciona un único lugar conveniente para el código que de otro modo podría estar duplicado en varios lugares (como el escalado de las salidas del motor o la comprobación de los interruptores de límite) si los internos del subsistema estuvieran expuestos. También permite que los cambios en los detalles específicos de cómo funciona el subsistema (la «implementación») se aíslen del resto del código del robot, lo que facilita enormemente la realización de cambios sustanciales si/cuando cambian las restricciones de diseño.

Los subsistemas también sirven como columna vertebral del sistema de gestión de recursos de CommandScheduler. Los comandos pueden declarar los requisitos de recursos especificando con qué subsistemas interactúan; el programador nunca programará simultáneamente más de un comando que requiera un subsistema determinado. Un intento de programar un comando que requiera un subsistema que ya está en uso interrumpirá el comando que se está ejecutando actualmente (si el comando se programó como interrumpible) o se ignorará.

Los subsistemas se pueden asociar con «comandos predeterminados» que se programarán automáticamente cuando ningún otro comando esté utilizando el subsistema. Esto es útil para acciones continuas «en segundo plano», como controlar el accionamiento del robot o mantener un brazo sujeto en un punto de ajuste. Se puede lograr una funcionalidad similar en el método periodic() del subsistema, que se ejecuta una vez por ejecución del programador; los equipos deben tratar de ser coherentes dentro de su base de código sobre qué funcionalidad se logra a través de cualquiera de estos métodos. También hay un método simulationPeriodic() que es similar a periodic() excepto que solo se ejecuta durante Simulation y puede usarse para actualizar el estado del robot. Los subsistemas se representan en la biblioteca basada en comandos mediante la interfaz del subsistema (Java, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1Subsystem.html> __).

Creando un subsistema

El método recomendado para crear un subsistema para la mayoría de los usuarios es subclasificar la clase abstracta SubsystemBase (Java, C++), como se ve en el modelo basado en comandos (Java, C++):

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
  }
}

Esta clase contiene algunas características de conveniencia además de la interfaz básica del Subsistema: llama automáticamente al método register() en su constructor para registrar el subsistema con el planificador (esto es necesario para el método periodic() que se llamará cuando se ejecute el planificador), y también implementa la interfaz Sendable para que pueda enviarse al dashboard para mostrar/registrar información de estado relevante.

Los usuarios avanzados que buscan más flexibilidad pueden simplemente crear una clase que implemente la interfaz del Subsistema.

Ejemplo de un subsistema simple

¿Cómo sería un subsistema funcional en la práctica? A continuación se muestra un mecanismo de trama simple accionado neumáticamente del proyecto de ejemplo HatchBot (Java, C++):

 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);
  }
}

Fíjese en que el subsistema oculta la presencia del DoubleSolenoid al código exterior (está declarado como privado), y en su lugar expone públicamente dos acciones de robot de nivel superior y descriptivo: grabHatch() y releaseHatch(). Es extremadamente importante que los «detalles de implementación» como el solenoide doble se «oculten» de esta manera; esto asegura que el código fuera del subsistema nunca causará que el solenoide esté en un estado inesperado. También permite al usuario cambiar la implementación (por ejemplo, se podría utilizar un motor en lugar de un neumático) sin que el código fuera del subsistema tenga que cambiar con él.

Configuración de comandos predeterminados

Nota

En la biblioteca basada en comandos de C ++, CommandScheduler tiene los objetos de comando predeterminados; en consecuencia, el objeto pasado al método SetDefaultCommand() se moverá o copiará, dependiendo de si es un rvalue o un lvalue (` explicación de rvalue/lvalue <http://thbecker.net/articles/rvalue_references/section_01.html>`__). Los ejemplos aquí aseguran que la semántica de movimiento se use al convertir a un rvalue con std::move().

Los «comandos predeterminados» son comandos que se ejecutan automáticamente siempre que un subsistema no está siendo utilizado por otro comando.

Establecer un comando predeterminado para un subsistema es muy fácil; uno simplemente llama CommandScheduler.getInstance().setDefaultCommand(), o, más simplemente, el método setDefaultCommand() de la interfaz Subsystem:

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