Utilisation des classes WPILib pour piloter votre robot

WPILib comprend de nombreuses classes pour faciliter le développement de la programmation et obtenir un robot fonctionnel plus rapidement.

Transmissions standard

Robots à entraînement différentiel

Le câblage d’un simple robot à entraînement différentiel.

Ces bases d’entraînement ont généralement deux ou plusieurs roues motrices en ligne ou omnidirectionnelles par côté (par exemple, 6WD ou 8WD) et peuvent également être appelées « skid-steer », « tank drive » ou « West Coast Drive ». La transmission du Kit de pièces est un exemple d’entraînement différentiel. Ces transmissions sont capables de faire avancer ou reculer le robot et peuvent le faire tourner en activant les moteurs de chaque côté dans des directions opposées, ce qui fait déraper les roues latéralement. Ces transmissions ne sont pas capables d’exécuter des mouvements de translation latéraux.

Entraînement de type Mécanum

Un robot Mecanum à quatre roues utilisant le châssis fourni dans le KOP.

L’entraînement Mécanum est une méthode de conduite utilisant des roues spécialement conçues qui permettent au robot de conduire dans n’importe quelle direction sans changer l’orientation du robot. Un robot avec une transmission conventionnelle (toutes les roues pointant dans la même direction) doit tourner dans la direction qu’il doit conduire. Un robot Mécanum peut se déplacer dans n’importe quelle direction sans avoir à tourner au préalable et est appelé un entraînement holonomique. Les roues (montrées sur ce robot) ont des rouleaux qui font que les forces de la conduite sont appliquées à un angle de 45 degrés plutôt que droit comme dans le cas d’un entraînement conventionnel.

Lorsqu’ils sont vus du haut, les rouleaux d’une transmission Mécanum doivent former un motif en “X”. Il en résulte que les vecteurs de force (lors de la conduite de la roue vers l’avant) sur les deux roues avant pointent vers l’avant et vers l’intérieur et les deux roues arrière pointent vers l’avant et vers l’extérieur. En faisant tourner les roues dans différentes directions, divers composants des vecteurs de force s’annulent, entraînant le mouvement souhaité du robot. Un tableau rapide des différents mouvements a été fourni ci-dessous, en traçant les vecteurs de force pour chacun de ces mouvements peut aider à comprendre comment ces transmissions fonctionnent. En faisant varier les vitesses des roues en plus de la direction, les mouvements peuvent être combinés, ce qui entraîne une translation dans n’importe quelle direction et rotation, simultanément.

Conventions utilisées par les classes

Motor Inversion

Depuis 2022, le côté droit de la transmission n’est plus inversé par défaut. Il est de la responsabilité de l’utilisateur de gérer les inversions appropriées de sa transmission. Les utilisateurs peuvent inverser les moteurs en appelant setInverted()/SetInverted() sur leurs objets moteurs.

PWMSparkMax m_motorRight = new PWMSparkMax(0);

@Override
public void robotInit() {
   m_motorRight.setInverted(true);
}
frc::PWMSparkMax m_motorLeft{0};

public:
 void RobotInit() override {
   m_motorRight.SetInverted(true);
 }
def robotInit(self):
    self.motorRight = wpilib.PWMSparkMax(0)
    self.motorRight.setInverted(True)

Squaring Inputs

Lors de la conduite de robots, il est souvent souhaitable de manipuler les entrées du joystick de sorte que le robot ait un contrôle plus précis à basse vitesse tout en utilisant toute la plage de sortie disponible. Une façon d’y parvenir est d’élever au carré la valeur absolue de l’entrée du joystick, puis de réappliquer le signe. Par défaut, la classe Differential Drive mettra les entrées au carré. Si cela n’est pas souhaité (par exemple, si vous transmettez des valeurs à partir d’un PIDController), utilisez l’une des méthodes d’entraînement et initialiser le paramètre squaredInputs à False.

Input Deadband

Par défaut, la classe DifferentialDrive applique une zone morte d’entrée de 0.02. Cela signifie que toutes les valeurs d’entrée qui sont inférieures à 0.02 (après la mise au carré décrite ci-dessus) seront plutôt considérées comme ayant une valeur de zéro. Dans la plupart des cas, ces petites valeurs d’entrée résultent d’un centrage imparfait du joystick et ne sont pas suffisantes pour provoquer un mouvement du robot. En appliquant une zone morte, on réduit l’échauffement des moteurs provenant de l’énergie appliquée inutilement aux moteurs, lorsque le robot ne bouge pas. Pour modifier la zone morte, utilisez la méthode setDeadband().

Maximum Output

Parfois, les pilotes ont l’impression que leur transmission roule trop vite et veulent limiter la sortie. Cela peut être accompli avec la méthode setMaxOutput(). Cette sortie maximale est multipliée par le résultat des fonctions d’entraînement précédentes comme deadband et squared inputs.

La sécurité moteur (Motor Safety)

Motor Safety est un mécanisme dans WPILib qui prend le concept d’un chien de garde (Watchdog) et crée une minuterie de sécurité pour chaque dispositif actionneur qui contrôle un moteur. Notez que ce mécanisme de protection s’ajoute au System Watchdog, celui-ci étant contrôlé par le code de communication réseau et le FPGA. Le Watchdog désactivera toutes les sorties du dispositif actionneur s’il ne reçoit pas de paquet de données valides pendant un délai de 125 ms.

The purpose of the Motor Safety mechanism is the same as the purpose of a watchdog timer, to disable mechanisms which may cause harm to themselves, people or property if the code locks up and does not properly update the actuator output. Motor Safety breaks this concept out on a per actuator basis so that you can appropriately determine where it is necessary and where it is not. Examples of mechanisms that should have motor safety enabled are systems like drive trains and arms. If these systems get latched on a particular value they could cause damage to their environment or themselves. An example of a mechanism that may not need motor safety is a spinning flywheel for a shooter. If this mechanism gets latched on a particular value it will simply continue spinning until the robot is disabled. By default Motor Safety is enabled for DifferentialDrive and MecanumDrive objects and disabled for all other motor controllers and servos.

La fonction Motor Safety d’un actionneur fonctionne en maintenant un timer qui surveille et enregistre le temps qui s’est écoulé depuis que le dernier appel de la méthode feed() pour cet actionneur. Un code dans la classe DriverStation initie une comparaison de ces timers aux valeurs de délai d’attente pour tout actionneur avec la sécurité activé tous les 5 paquets reçus (100ms nominal). Les méthodes set() de chaque classe de contrôleur de moteur et les méthodes set() et setAngle() de la classe servo font un appel à la méthode feed() pour indiquer que la sortie de l’actionneur a été mise à jour.

L’interface Motor Safety des contrôleurs moteurs peut être en interaction avec l’utilisateur par l’intermédiaire des méthodes suivantes:

m_motorRight.setSafetyEnabled(true);
m_motorRight.setSafetyEnabled(false);
m_motorRight.setExpiration(.1);
m_motorRight.feed()
m_motorRight->SetSafetyEnabled(true);
m_motorRight->SetSafetyEnabled(false);
m_motorRight->SetExpiration(.1);
m_motorRight->Feed();
m_motorRight.setSafetyEnabled(True)
m_motorRight.setSafetyEnabled(False)
m_motorRight.setExpiration(.1)
m_motorRight.feed()

Par défaut, tous les objets Drive activent la sécurité moteur. Selon le mécanisme et la structure de votre programme, vous pouvez souhaiter configurer la durée de temporisation de la sécurité moteur (en secondes). La durée du timeout est configurée par actionneur et n’est pas un paramètre global. La valeur par défaut (et minimale utile) est de 100 ms.

Conventions des axes

The drive classes use the NWU axes convention (North-West-Up as external reference in the world frame). The positive X axis points ahead, the positive Y axis points left, and the positive Z axis points up. We use NWU here because the rest of the library, and math in general, use NWU axes convention.

Joysticks follow NED (North-East-Down) convention, where the positive X axis points ahead, the positive Y axis points right, and the positive Z axis points down. However, it’s important to note that axes values are rotations around the respective axes, not translations. When viewed with each axis pointing toward you, CCW is a positive value and CW is a negative value. Pushing forward on the joystick is a CW rotation around the Y axis, so you get a negative value. Pushing to the right is a CCW rotation around the X axis, so you get a positive value.

Note

See the Coordinate System section for more detail about the axis conventions and coordinate systems.

Utilisation de la classe DifferentialDrive pour contrôler les robots à entraînement différentiel (WCD)

Note

WPILib provides separate Robot Drive classes for the most common drive train configurations (differential and mecanum). The DifferentialDrive class handles the differential drivetrain configuration. These drive bases typically have two or more in-line traction or omni wheels per side (e.g., 6WD or 8WD) and may also be known as « skid-steer », « tank drive », or « West Coast Drive » (WCD). The Kit of Parts drivetrain is an example of a differential drive. There are methods to control the drive with 3 different styles (« Tank », « Arcade », or « Curvature »), explained in the article below.

DifferentialDrive est une méthode fournie pour le contrôle des transmissions « skid-steer » ou « West Coast », comme le châssis du Kit de pièces. L’instanciation d’un DifferentialDrive est aussi simple que cela:

public class Robot extends TimedRobot {
  private DifferentialDrive m_robotDrive;
  private final PWMSparkMax m_leftMotor = new PWMSparkMax(0);
  private final PWMSparkMax m_rightMotor = new PWMSparkMax(1);

  @Override
  public void robotInit() {
    // We need to invert one side of the drivetrain so that positive voltages
    // result in both sides moving forward. Depending on how your robot's
    // gearbox is constructed, you might have to invert the left side instead.
    m_rightMotor.setInverted(true);

    m_robotDrive = new DifferentialDrive(m_leftMotor::set, m_rightMotor::set);
  }
  frc::PWMSparkMax m_leftMotor{0};
  frc::PWMSparkMax m_rightMotor{1};
  frc::DifferentialDrive m_robotDrive{
      [&](double output) { m_leftMotor.Set(output); },
      [&](double output) { m_rightMotor.Set(output); }};
  void RobotInit() override {
    // We need to invert one side of the drivetrain so that positive voltages
    // result in both sides moving forward. Depending on how your robot's
    // gearbox is constructed, you might have to invert the left side instead.
    m_rightMotor.SetInverted(true);
  }
    def robotInit(self):
        """Robot initialization function"""

        leftMotor = wpilib.PWMSparkMax(0)
        rightMotor = wpilib.PWMSparkMax(1)
        self.robotDrive = wpilib.drive.DifferentialDrive(leftMotor, rightMotor)
        # We need to invert one side of the drivetrain so that positive voltages
        # result in both sides moving forward. Depending on how your robot's
        # gearbox is constructed, you might have to invert the left side instead.
        rightMotor.setInverted(True)

Multi-Motor DifferentialDrive

Many FRC® drivetrains have more than 1 motor on each side. Classes derived from PWMMotorController (Java / C++ / Python) have an addFollower method so that multiple follower motor controllers can be updated when the leader motor controller is commanded. CAN motor controllers have similar features, review the vendor’s documention to see how to use them. The examples below show a 4 motor (2 per side) drivetrain. To extend to more motors, simply create the additional controllers and use additional addFollower calls.

Class variables (e.g. in Robot.java or Subystem):

  // The motors on the left side of the drive.
  private final PWMSparkMax m_leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port);
  private final PWMSparkMax m_leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port);

  // The motors on the right side of the drive.
  private final PWMSparkMax m_rightLeader = new PWMSparkMax(DriveConstants.kRightMotor1Port);
  private final PWMSparkMax m_rightFollower = new PWMSparkMax(DriveConstants.kRightMotor2Port);

In robotInit or Subsystem constructor:

    m_leftLeader.addFollower(m_leftFollower);
    m_rightLeader.addFollower(m_rightFollower);

    // We need to invert one side of the drivetrain so that positive voltages
    // result in both sides moving forward. Depending on how your robot's
    // gearbox is constructed, you might have to invert the left side instead.
    m_rightLeader.setInverted(true);
 private:
  // The motor controllers
  frc::PWMSparkMax m_left1;
  frc::PWMSparkMax m_left2;
  frc::PWMSparkMax m_right1;
  frc::PWMSparkMax m_right2;

  // The robot's drive
  frc::DifferentialDrive m_drive{[&](double output) { m_left1.Set(output); },
                                 [&](double output) { m_right1.Set(output); }};

In robotInit or Subsystem constructor:

  m_left1.AddFollower(m_left2);
  m_right1.AddFollower(m_right2);

  // We need to invert one side of the drivetrain so that positive voltages
  // result in both sides moving forward. Depending on how your robot's
  // gearbox is constructed, you might have to invert the left side instead.
  m_right1.SetInverted(true);

Note

MotorControllerGroup is deprecated in 2024. Can you help update this example?

def robotInit(self):
    frontLeft = wpilib.Spark(1)
    rearLeft = wpilib.Spark(2)
    left = wpilib.MotorControllerGroup(frontLeft, rearLeft)
    left.setInverted(True) # if you want to invert the entire side you can do so here

    frontRight = wpilib.Spark(3)
    rearRight = wpilib.Spark(4)
    right = wpilib.MotorControllerGroup(frontLeft, rearLeft)

    self.drive = wpilib.drive.DifferentialDrive(left, right)

Modes de conduite

Note

La classe DifferentialDrive contient trois différents modes de pilotage (par défaut)

  • Tank Drive, qui contrôle les côtés gauche et droit de façon indépendante

  • Arcade Drive, qui contrôle la vitesse vers l’avant/arrière et le virage

  • Curvature Drive, un sous-ensemble d’Arcade Drive, qui fait que votre robot se comporte comme une voiture avec des virages à courbure constante.

La classe DifferentialDrive contient trois méthodes par défaut pour contrôler les robots de dérapage ou de WCD. Notez que vous pouvez créer vos propres méthodes de contrôle de la conduite du robot et les faire appeler tankDrive() avec les entrées dérivées pour les moteurs gauche et droit.

Le mode Tank Drive est utilisé pour contrôler chaque côté du train d’entraînement de façon indépendante (généralement avec un axe de joystick (manette) individuel contrôlant chacun des côtés). Cet exemple montre comment utiliser l’axe Y de deux joysticks séparés pour un train d’entraînement opéré en mode Tank. La construction des objets a été omise, ci-dessus pour la transmission et ci-dessous pour la construction du joystick.

Le mode Arcade Drive est utilisé pour contrôler le train d’entraînement en utilisant la vitesse/l’accélérateur et le taux de rotation. Ceci est implémenté soit avec les deux axes provenant d’un seul joystick, soit divisé en joysticks (souvent sur une seule manette de jeu) avec l’accélérateur venant d’un joystick et la rotation d’un autre. Cet exemple montre comment utiliser un seul joystick avec le mode Arcade. La construction des objets a été omise, ci-dessus pour la transmission et c-dessous pour la construction du joystick.

Comme Arcade Drive, le mode Curvature Drive est utilisé pour contrôler la transmission en utilisant la vitesse/accélérateur et la vitesse de rotation. La différence est que l’entrée de commande de rotation contrôle le rayon de courbure au lieu du taux de changement de cap, un peu comme le volant d’une voiture. Ce mode prend également en charge la mise en place, qui est activée lorsque le troisième paramètre boolean est à l’état vrai (true).

public void teleopPeriodic() {
    // Tank drive with a given left and right rates
    myDrive.tankDrive(-leftStick.getY(), -rightStick.getY());

    // Arcade drive with a given forward and turn rate
    myDrive.arcadeDrive(-driveStick.getY(), -driveStick.getX());

    // Curvature drive with a given forward and turn rate, as well as a button for turning in-place.
    myDrive.curvatureDrive(-driveStick.getY(), -driveStick.getX(), driveStick.getButton(1));
}
void TeleopPeriodic() override {
    // Tank drive with a given left and right rates
    myDrive.TankDrive(-leftStick.GetY(), -rightStick.GetY());

    // Arcade drive with a given forward and turn rate
    myDrive.ArcadeDrive(-driveStick.GetY(), -driveStick.GetX());

    // Curvature drive with a given forward and turn rate, as well as a quick-turn button
    myDrive.CurvatureDrive(-driveStick.GetY(), -driveStick.GetX(), driveStick.GetButton(1));
}
def teleopPeriodic(self):
    # Tank drive with a given left and right rates
    self.myDrive.tankDrive(-self.leftStick.getY(), -self.rightStick.getY())

    # Arcade drive with a given forward and turn rate
    self.myDrive.arcadeDrive(-self.driveStick.getY(), -self.driveStick.getX())

    # Curvature drive with a given forward and turn rate, as well as a button for turning in-place.
    self.myDrive.curvatureDrive(-self.driveStick.getY(), -self.driveStick.getX(), self.driveStick.getButton(1))

Utilisation de la classe MecanumDrive pour contrôler les robots avec trains d’entraînement Mécanum

MecanumDrive est une méthode fournie pour le contrôle des transmissions holonomiques avec roues Mécanum, telles que le châssis du Kit de pièces, augmenté du kit Mécanum, comme illustré ci-dessus. Instancier un MecanumDrive est aussi simple que ceci:

  private static final int kFrontLeftChannel = 2;
  private static final int kRearLeftChannel = 3;
  private static final int kFrontRightChannel = 1;
  private static final int kRearRightChannel = 0;

  @Override
  public void robotInit() {
    PWMSparkMax frontLeft = new PWMSparkMax(kFrontLeftChannel);
    PWMSparkMax rearLeft = new PWMSparkMax(kRearLeftChannel);
    PWMSparkMax frontRight = new PWMSparkMax(kFrontRightChannel);
    PWMSparkMax rearRight = new PWMSparkMax(kRearRightChannel);
    // Invert the right side motors.
    // You may need to change or remove this to match your robot.
    frontRight.setInverted(true);
    rearRight.setInverted(true);

    m_robotDrive = new MecanumDrive(frontLeft::set, rearLeft::set, frontRight::set, rearRight::set);
  }
 private:
  static constexpr int kFrontLeftChannel = 0;
  static constexpr int kRearLeftChannel = 1;
  static constexpr int kFrontRightChannel = 2;
  static constexpr int kRearRightChannel = 3;

  frc::PWMSparkMax m_frontLeft{kFrontLeftChannel};
  frc::PWMSparkMax m_rearLeft{kRearLeftChannel};
  frc::PWMSparkMax m_frontRight{kFrontRightChannel};
  frc::PWMSparkMax m_rearRight{kRearRightChannel};
  frc::MecanumDrive m_robotDrive{
      [&](double output) { m_frontLeft.Set(output); },
      [&](double output) { m_rearLeft.Set(output); },
      [&](double output) { m_frontRight.Set(output); },
      [&](double output) { m_rearRight.Set(output); }};

  void RobotInit() override {
    // Invert the right side motors. You may need to change or remove this to
    // match your robot.
    m_frontRight.SetInverted(true);
    m_rearRight.SetInverted(true);
  }
    # Channels on the roboRIO that the motor controllers are plugged in to
    kFrontLeftChannel = 2
    kRearLeftChannel = 3
    kFrontRightChannel = 1
    kRearRightChannel = 0

    def robotInit(self):
        self.frontLeft = wpilib.PWMSparkMax(self.kFrontLeftChannel)
        self.rearLeft = wpilib.PWMSparkMax(self.kRearLeftChannel)
        self.frontRight = wpilib.PWMSparkMax(self.kFrontRightChannel)
        self.rearRight = wpilib.PWMSparkMax(self.kRearRightChannel)

        # invert the right side motors
        # you may need to change or remove this to match your robot
        self.frontRight.setInverted(True)
        self.rearRight.setInverted(True)

        self.robotDrive = wpilib.drive.MecanumDrive(
            self.frontLeft, self.rearLeft, self.frontRight, self.rearRight
        )

        self.stick = wpilib.Joystick(self.kJoystickChannel)

Modes d’entraînement Mécanum

Note

Les conventions d’axe d’entraînement sont différentes des conventions d’axe de joystick courantes. Voir les Conventions d’axe ci-dessus pour plus d’informations.

La classe MecanumDrive contient deux différents modes de pilotage (par défaut)

  • driveCartesian: Les angles sont mesurés dans le sens horaire à partir de l’axe X positif. La vitesse du robot est indépendante de son angle ou de sa vitesse de rotation.

  • drivePolar: Les angles sont mesurés dans le sens antihoraire à partir de la ligne droite. La vitesse à laquelle le robot roule (translation) est indépendante de son angle ou de sa vitesse de rotation.

public void teleopPeriodic() {
    // Drive using the X, Y, and Z axes of the joystick.
    m_robotDrive.driveCartesian(-m_stick.getY(), -m_stick.getX(), -m_stick.getZ());
    // Drive at 45 degrees relative to the robot, at the speed given by the Y axis of the joystick, with no rotation.
    m_robotDrive.drivePolar(-m_stick.getY(), Rotation2d.fromDegrees(45), 0);
}
void TeleopPeriodic() override {
    // Drive using the X, Y, and Z axes of the joystick.
    m_robotDrive.driveCartesian(-m_stick.GetY(), -m_stick.GetX(), -m_stick.GetZ());
    // Drive at 45 degrees relative to the robot, at the speed given by the Y axis of the joystick, with no rotation.
    m_robotDrive.drivePolar(-m_stick.GetY(), 45_deg, 0);
}
def teleopPeriodic(self):
    // Drive using the X, Y, and Z axes of the joystick.
    self.robotDrive.driveCartesian(-self.stick.getY(), -self.stick.getX(), -self.stick.getZ())
    // Drive at 45 degrees relative to the robot, at the speed given by the Y axis of the joystick, with no rotation.
    self.robotDrive.drivePolar(-self.stick.getY(), Rotation2d.fromDegrees(45), 0)

Conduite orientée sur le terrain

Un 4ème paramètre peut être fourni à la méthode driveCartesian (double ySpeed, double xSpeed, double zRotation, double gyroAngle), soit l’angle renvoyé par un capteur gyroscopique. Cela ajustera la valeur de rotation fournie. Ceci est particulièrement utile avec le Mécanum car, aux fins de la direction, le robot n’a vraiment ni avant, ni arrière, ni côtés. Il peut aller dans n’importe quelle direction. L’ajout de l’angle en degrés par rapport à un objet gyroscopique entraînera le robot à s’éloigner des pilotes lorsque le joystick est poussé vers l’avant et vers les pilotes lorsqu’il est tiré vers eux, quelle que soit la direction dans laquelle le robot fait face.

L’utilisation de la conduite orientée sur le terrain rend souvent le robot beaucoup plus facile à conduire, en particulier par rapport à un système d’entraînement « orienté robot » où les commandes sont inversées lorsque le robot fait face aux conducteurs.

N’oubliez pas d’obtenir l’angle gyroscopique chaque fois que la méthode driveCartesian() est appelée.

Note

Beaucoup d’équipes aiment aussi augmenter linéairement les entrées des joysticks au fil du temps pour avoir une accélération en douceur et réduire le jerk. Ceci peut être accompli avec un Slew Rate Limiter.