Command

Command

Cấu trúc command để mô tả từng hành động riêng lẻ của robot.

Cấu trúc command để mô tả từng hành động riêng lẻ của robot.

Level

Advanced

Source

Source

Author

Author

FTC Lib

FTC Lib

Translator

Translator

FTC26749 aDudu

FTC26749 aDudu

Date Published

Date Published

Jan 18, 2026

Jan 18, 2026

import com.arcrobotics.ftclib.command.Command
import com.arcrobotics.ftclib.command.Command

Command (Lệnh) là các máy trạng thái đơn giản (simple state machines) thực hiện các chức năng robot ở mức cao bằng cách sử dụng những phương thức được định nghĩa trong Subsystem (hệ con).

Một Command có thể ở trạng thái:

  • Idle (nhàn rỗi): không làm gì cả

  • Scheduled (được lập lịch): bộ lập lịch (scheduler) sẽ thực thi một tập mã cụ thể của command tùy theo trạng thái hiện tại của nó

CommandScheduler nhận biết một command đã được lập lịch là đang ở một trong ba trạng thái:

  1. Initializing (khởi tạo)

  2. Executing (thực thi)

  3. Ending (kết thúc)

Command xác định hành vi của mình trong từng trạng thái này thông qua các phương thức:

  • initialize()

  • execute()

  • end()

Trong thư viện command-based, Command được biểu diễn thông qua interface Command.

Creating Commands (Tạo Command)

Tương tự như Subsystem, cách được khuyến nghị cho đa số người dùng để tạo một command là kế thừa (subclass) từ lớp trừu tượng CommandBase, như trong template command-based.

import com.arcrobotics.ftclib.command.CommandBase;
/**
 * An example command that uses an example subsystem.
 */
public class ExampleCommand extends CommandBase {
  @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField"})
  private final ExampleSubsystem m_subsystem;
  /**
   * Creates a new ExampleCommand.
   *
   * @param subsystem The subsystem used by this command.
   */
  public ExampleCommand(ExampleSubsystem subsystem) {
    m_subsystem = subsystem;
    // Use addRequirements() here to declare subsystem dependencies.
    addRequirements(subsystem);
  }
}
import com.arcrobotics.ftclib.command.CommandBase;
/**
 * An example command that uses an example subsystem.
 */
public class ExampleCommand extends CommandBase {
  @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField"})
  private final ExampleSubsystem m_subsystem;
  /**
   * Creates a new ExampleCommand.
   *
   * @param subsystem The subsystem used by this command.
   */
  public ExampleCommand(ExampleSubsystem subsystem) {
    m_subsystem = subsystem;
    // Use addRequirements() here to declare subsystem dependencies.
    addRequirements(subsystem);
  }
}

Như trước, lớp này cung cấp nhiều tiện ích sẵn có.
tự động override phương thức getRequirements(), trả về danh sách các subsystem mà command yêu cầu.

  • Danh sách này mặc định là rỗng

  • Có thể bổ sung bằng phương thức addRequirements()

Ngoài ra, những người dùng nâng cao cần nhiều linh hoạt hơn có thể tự tạo một lớp riêngimplement trực tiếp interface Command.

Scheduling a Command (Lập lịch cho Command)

Để đưa một command vào scheduler, bạn cần gọi phương thức schedule() của instance command đó.

m_command.schedule();
m_command.schedule();

The Structure of a Command (Cấu trúc của một Command)

Trong khi Subsystem khá linh hoạt về hình thức, thì Command bị ràng buộc chặt chẽ hơn.

Mã của một command phải xác định rõ hành vi trong từng trạng thái có thể có, bằng cách override các phương thức:

  • initialize()

  • execute()

  • end()

Ngoài ra, command phải cho scheduler biết khi nào nó kết thúc, thông qua phương thức:

  • isFinished()

Các phương thức này đều có giá trị mặc định nhằm giảm sự rườm rà trong code:

  • initialize(), execute(), end()mặc định không làm gì

  • isFinished()mặc định trả về false (khiến command không bao giờ kết thúc)

Initialization (Khởi tạo)

Phương thức initialize() chỉ chạy đúng một lần mỗi khi command được lập lịch, và được gọi như một phần của phương thức schedule() trong scheduler.

⚠️ Không cần gọi run() của scheduler thì initialize() vẫn được thực thi.

Khối initialize() nên dùng để:

  • Đặt command về trạng thái khởi đầu xác định

  • Thực hiện các tác vụ chỉ cần chạy một lần, ví dụ:

    • Cài đặt motor chạy với tốc độ cố định

Execution (Thực thi)

Phương thức execute() được gọi lặp đi lặp lại khi command đang được lập lịch, mỗi lần scheduler gọi run().

Khối execute() nên dùng cho các tác vụ:

  • Cần thực hiện liên tục

  • Ví dụ:

    • Cập nhật công suất motor theo joystick

    • Sử dụng đầu ra của một vòng điều khiển (control loop)

Ending (Kết thúc)

Phương thức end() được gọi một lần duy nhất khi command kết thúc, dù là:

  • Kết thúc bình thường (isFinished() trả về true)

  • Bị ngắt (bởi command khác hoặc bị hủy thủ công)

Tham số truyền vào cho biết cách command kết thúc, cho phép người dùng xử lý khác nhau tùy tình huống.

Khối end() nên dùng để:

  • Dọn dẹp trạng thái

  • Ví dụ:

    • Đặt motor về 0

    • Trả solenoid về trạng thái “mặc định”

Specifying End Conditions (Xác định điều kiện kết thúc)

Phương thức isFinished() được gọi liên tục khi command đang được lập lịch, mỗi lần scheduler gọi run().

Ngay khi isFinished() trả về true:

  • end() được gọi

  • Command bị hủy lập lịch

⚠️ isFinished() được gọi sau execute(), vì vậy command vẫn thực thi thêm một lần cuối trong cùng chu kỳ mà nó bị hủy.

Simple Command Example (Ví dụ Command đơn giản)

Lấy ví dụ gripper từ trang Subsystem, ta có command để gắp một viên đá như sau:

import com.arcrobotics.ftclib.command.CommandBase;
/**
 * A simple command that grabs a stone with the
 * {@link GripperSubsystem}.  Written explicitly for
 * pedagogical purposes. Actual code should inline a
 * command this simple with {@link
 * com.arcrobotics.ftclib.command.InstantCommand}.
 */
public class GrabStone extends CommandBase {
    // The subsystem the command runs on
    private final GripperSubsystem m_gripperSubsystem;
    public GrabStone(GripperSubsystem subsystem) {
        m_gripperSubsystem = subsystem;
        addRequirements(m_gripperSubsystem);
    }
    @Override
    public void initialize() {
        m_gripperSubsystem.grab();
    }
    @Override
    public boolean isFinished() {
        return true;
    }
}
import com.arcrobotics.ftclib.command.CommandBase;
/**
 * A simple command that grabs a stone with the
 * {@link GripperSubsystem}.  Written explicitly for
 * pedagogical purposes. Actual code should inline a
 * command this simple with {@link
 * com.arcrobotics.ftclib.command.InstantCommand}.
 */
public class GrabStone extends CommandBase {
    // The subsystem the command runs on
    private final GripperSubsystem m_gripperSubsystem;
    public GrabStone(GripperSubsystem subsystem) {
        m_gripperSubsystem = subsystem;
        addRequirements(m_gripperSubsystem);
    }
    @Override
    public void initialize() {
        m_gripperSubsystem.grab();
    }
    @Override
    public boolean isFinished() {
        return true;
    }
}

Ở đây:

  • Subsystem gripper được truyền vào constructor → tạo hành động cho command

  • Đây gọi là Dependency Injection (tiêm phụ thuộc), giúp tránh việc khai báo subsystem dưới dạng biến toàn cục

  • Đây là best practice được chấp nhận rộng rãi

Command này:

  • Gọi phương thức grab() một lần duy nhất trong initialize()

  • Kết thúc ngay lập tức vì isFinished() trả về true

More Complex Command Example (Ví dụ phức tạp hơn)

import com.arcrobotics.ftclib.command.CommandBase;
import java.util.function.DoubleSupplier;
/**
 * A command to drive the robot with joystick input
 * (passed in as {@link DoubleSupplier}s). Written
 * explicitly for pedagogical purposes.
 */
public class DefaultDrive extends CommandBase {
    private final DriveSubsystem m_drive;
    private final DoubleSupplier m_forward;
    private final DoubleSupplier m_rotation;
    /**
     * Creates a new DefaultDrive.
     *
     * @param subsystem The drive subsystem this command will run on.
     * @param forward The control input for driving forwards/backwards
     * @param rotation The control input for turning
     */
    public DefaultDrive(DriveSubsystem subsystem,
        DoubleSupplier forward, DoubleSupplier rotation) {
        m_drive = subsystem;
        m_forward = forward;
        m_rotation = rotation;
        addRequirements(m_drive);
    }
    @Override
    public void execute() {
        m_drive.drive(
            m_forward.getAsDouble(),
            m_rotation.getAsDouble()
        );
    }
}
import com.arcrobotics.ftclib.command.CommandBase;
import java.util.function.DoubleSupplier;
/**
 * A command to drive the robot with joystick input
 * (passed in as {@link DoubleSupplier}s). Written
 * explicitly for pedagogical purposes.
 */
public class DefaultDrive extends CommandBase {
    private final DriveSubsystem m_drive;
    private final DoubleSupplier m_forward;
    private final DoubleSupplier m_rotation;
    /**
     * Creates a new DefaultDrive.
     *
     * @param subsystem The drive subsystem this command will run on.
     * @param forward The control input for driving forwards/backwards
     * @param rotation The control input for turning
     */
    public DefaultDrive(DriveSubsystem subsystem,
        DoubleSupplier forward, DoubleSupplier rotation) {
        m_drive = subsystem;
        m_forward = forward;
        m_rotation = rotation;
        addRequirements(m_drive);
    }
    @Override
    public void execute() {
        m_drive.drive(
            m_forward.getAsDouble(),
            m_rotation.getAsDouble()
        );
    }
}

Lưu ý:

  • Command này không override isFinished()

Vì vậy, nó không bao giờ kết thúc, trừ khi bị ngắt bởi command khác

ADUDU

A proud team of passionate Robotics Enthusiasts competing in nation-wide Technology competitions in Vietnam, the FIRST Tech Challenge and the FIRST Robotics Competition.

Copyright ©

, all rights reserved

Made by aDudu's Programming Department

made by aDudu

made by aDudu

Command