构建基于命令的机器人项目

用户可以随意使用自己喜欢的基于命令的库(鼓励高级用户使用),但是新用户可能需要一些有关如何构建基本的基于命令的机器人项目的指南。

WPILib示例存储库中包含基于命令的机器人项目的标准模板(Java <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased> __,C ++ <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/templates/commandbased> __)。本节将向用户介绍此模板的结构。

根包/目录通常将包含四个类:

“Main”,这是主要的机器人应用程序(仅Java)。新用户*不应*接触此类。“Robot”,负责机器人代码的主控制流程。 “RobotContainer”包含机器人子系统和命令,是执行大多数声明性机器人设置(例如按钮绑定)的地方。 “Constants”,其中包含可在整个机器人中使用的全局可访问常量。

根目录还将包含两个子包/子目录:“Subsystems”包含所有用户定义的子系统类。 “Commands”包含所有用户定义的命令类。

Robot

由于``Robot’’(Java <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java>`__,`C ++(Header)<https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/Robot.h>`__`C ++(Source)<https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp>`__)负责程序的控制流,基于命令的声明式范例旨在最大程度地减少由于用户必须对显式程序控制流程给予足够的关注,基于命令的项目的“机器人”类应该基本上为空。但是,必须包含一些重要的内容

22  /**
23   * This function is run when the robot is first started up and should be used for any
24   * initialization code.
25   */
26  @Override
27  public void robotInit() {
28    // Instantiate our RobotContainer.  This will perform all our button bindings, and put our
29    // autonomous chooser on the dashboard.
30    m_robotContainer = new RobotContainer();
31  }

在Java中,应在“ robotInit()”方法期间构造“RobotContainer”的实例——这很重要,因为大多数声明式机器人设置将从“RobotContainer”构造函数调用。

在C ++中,这是不需要的,因为RobotContainer是值成员,并且将在构建“Robot”时进行构造。

33  /**
34   * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics
35   * that you want ran during disabled, autonomous, teleoperated and test.
36   *
37   * <p>This runs after the mode specific periodic functions, but before LiveWindow and
38   * SmartDashboard integrated updating.
39   */
40  @Override
41  public void robotPeriodic() {
42    // Runs the Scheduler.  This is responsible for polling buttons, adding newly-scheduled
43    // commands, running already-scheduled commands, removing finished or interrupted commands,
44    // and running subsystem periodic() methods.  This must be called from the robot's periodic
45    // block in order for anything in the Command-based framework to work.
46    CommandScheduler.getInstance().run();
47  }
11/**
12 * This function is called every 20 ms, no matter the mode. Use
13 * this for items like diagnostics that you want to run during disabled,
14 * autonomous, teleoperated and test.
15 *
16 * <p> This runs after the mode specific periodic functions, but before
17 * LiveWindow and SmartDashboard integrated updating.
18 */
19void Robot::RobotPeriodic() {
20  frc2::CommandScheduler::GetInstance().Run();
21}

“robotPeriodic()”方法中必须包含“CommandScheduler.getInstance().run()”调用;没有此调用,调度程序将不会执行任何调度的命令。由于“TimedRobot”以默认的主循环频率50Hz运行,因此这是将调用定期命令和子系统方法的频率。不建议新用户从其代码中的其他任何地方调用此方法。

56  /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */
57  @Override
58  public void autonomousInit() {
59    m_autonomousCommand = m_robotContainer.getAutonomousCommand();
60
61    // schedule the autonomous command (example)
62    if (m_autonomousCommand != null) {
63      m_autonomousCommand.schedule();
64    }
65  }
33/**
34 * This autonomous runs the autonomous command selected by your {@link
35 * RobotContainer} class.
36 */
37void Robot::AutonomousInit() {
38  m_autonomousCommand = m_container.GetAutonomousCommand();
39
40  if (m_autonomousCommand) {
41    m_autonomousCommand->Schedule();
42  }
43}

“autonomousInit()”方法调度由“RobotContainer”实例返回的自治命令。选择要运行的自治命令的逻辑可以在“RobotContainer”内部处理。

71  @Override
72  public void teleopInit() {
73    // This makes sure that the autonomous stops running when
74    // teleop starts running. If you want the autonomous to
75    // continue until interrupted by another command, remove
76    // this line or comment it out.
77    if (m_autonomousCommand != null) {
78      m_autonomousCommand.cancel();
79    }
80  }
46void Robot::TeleopInit() {
47  // This makes sure that the autonomous stops running when
48  // teleop starts running. If you want the autonomous to
49  // continue until interrupted by another command, remove
50  // this line or comment it out.
51  if (m_autonomousCommand) {
52    m_autonomousCommand->Cancel();
53  }
54}

“teleopInit()”方法取消所有仍在运行的自治命令。这通常是好的做法。

高级用户可以随意在自己认为合适的各种初始化和定期方法中添加其他代码;但是,应该注意的是,在“Robot.java”中包含大量命令式机器人代码与基于命令的范例的声明式设计哲学相反,并且可能导致结构混乱/结构混乱的代码。

RobotContainer

此类(Java`C ++(Header)<https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/RobotContainer.h>`__`C ++(Source)<https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp>`__)是大多数基于命令的机器人的设置地方。在此类中,您将定义机器人的子系统和命令,将这些命令绑定到触发事件(例如按钮),并指定将在自治例程中运行的命令。新手可能需要对此类进行解释:

23  private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem();
32  ExampleSubsystem m_subsystem;

请注意,子系统在“RobotContainer”中被声明为私有字段。这与基于命令的框架的前身形成了鲜明的对比,但与商定的面向对象的最佳实践更加一致。如果子系统被声明为全局变量,则它允许用户从代码中的任何位置访问它们。虽然这可以使某些事情变得容易(例如,无需将子系统传递给命令以使这些命令可以访问它们),但由于不立即执行,因此使得程序的控制流更难以跟踪很明显,代码的哪些部分可以更改或可以被代码的其他哪些部分更改。这也妨碍了资源管理系统执行其工作的能力,因为易于访问使用户更容易意外地在资源管理命令之外意外调用子系统方法。

61    return Autos.exampleAuto(m_exampleSubsystem);
34  return autos::ExampleAuto(&m_subsystem);

由于子系统被声明为私有成员,因此必须将它们显式传递给命令(一种称为“依赖注入”的模式),以便这些命令在其上调用方法。这是通过“ExampleCommand”完成的,该示例传递了指向“ExampleSubsystem”的指针。

35  /**
36   * Use this method to define your trigger->command mappings. Triggers can be created via the
37   * {@link Trigger#Trigger(java.util.function.BooleanSupplier)} constructor with an arbitrary
38   * predicate, or via the named factories in {@link
39   * edu.wpi.first.wpilibj2.command.button.CommandGenericHID}'s subclasses for {@link
40   * CommandXboxController Xbox}/{@link edu.wpi.first.wpilibj2.command.button.CommandPS4Controller
41   * PS4} controllers or {@link edu.wpi.first.wpilibj2.command.button.CommandJoystick Flight
42   * joysticks}.
43   */
44  private void configureBindings() {
45    // Schedule `ExampleCommand` when `exampleCondition` changes to `true`
46    new Trigger(m_exampleSubsystem::exampleCondition)
47        .onTrue(new ExampleCommand(m_exampleSubsystem));
48
49    // Schedule `exampleMethodCommand` when the Xbox controller's B button is pressed,
50    // cancelling on release.
51    m_driverController.b().whileTrue(m_exampleSubsystem.exampleMethodCommand());
52  }
19void RobotContainer::ConfigureBindings() {
20  // Configure your trigger bindings here
21
22  // Schedule `ExampleCommand` when `exampleCondition` changes to `true`
23  frc2::Trigger([this] {
24    return m_subsystem.ExampleCondition();
25  }).OnTrue(ExampleCommand(&m_subsystem).ToPtr());
26
27  // Schedule `ExampleMethodCommand` when the Xbox controller's B button is
28  // pressed, cancelling on release.
29  m_driverController.B().WhileTrue(m_subsystem.ExampleMethodCommand());
30}

As mentioned before, the RobotContainer() constructor is where most of the declarative setup for the robot should take place, including button bindings, configuring autonomous selectors, etc. If the constructor gets too “busy,” users are encouraged to migrate code into separate subroutines (such as the configureBindings() method included by default) which are called from the constructor.

54  /**
55   * Use this to pass the autonomous command to the main {@link Robot} class.
56   *
57   * @return the command to run in autonomous
58   */
59  public Command getAutonomousCommand() {
60    // An example command will be run in autonomous
61    return Autos.exampleAuto(m_exampleSubsystem);
62  }
63}
32frc2::CommandPtr RobotContainer::GetAutonomousCommand() {
33  // An example command will be run in autonomous
34  return autos::ExampleAuto(&m_subsystem);
35}

最后,“getAutonomousCommand()”方法为用户提供了一种方便的方法,可以将所选的自主命令发送到主要的Robot类(需要在启动自主类时对其进行调度)。

Constants

``Constants’’类(Java`C ++(Header)<https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/Constants.h>`__)(在C ++中这不是一个类,而是一个定义了多个名称空间的头文件)是可全局访问的机器人的位置可以存储常数(例如速度,单位转换系数,PID增益和传感器/电动机端口)。建议用户将这些常量分成与子系统或机械手模式相对应的各个内部类,以使变量名更短。

在Java中,所有常量都应声明为“public static final”,以便它们可以全局访问且不能更改。在C ++中,所有常量都应为“constexpr”。

有关实践中“constants”类的外观的更多说明性示例,请参见各种基于命令的示例项目的示例:

在Java中,建议通过静态导入必要的内部类从其他类中使用常量。“import static”语句将类的静态名称空间导入到您正在工作的类中,以便可以直接引用任何“static”常量,就好像它们是在该类中定义的一样。在C ++中,使用``使用命名空间’’可以达到相同的效果:

import static edu.wpi.first.wpilibj.templates.commandbased.Constants.OIConstants.*;
using namespace OIConstants;

Subsystems

用户定义的子系统应放在此软件包/目录中。

Commands

用户定义的命令应放在此软件包/目录中。