Encodeurs¶
Qu’est-ce qu’un encodeur?¶
Très souvent, en FTCC®, vous voulez savoir où se trouve quelque chose. Qu’il s’agisse du nombre de rotations de votre roue motrice, de l’angle de votre bras ou de la distance parcourue par vos cordes, les encodeurs rotatifs peuvent vous aider. En FTC, un encodeur désigne tout capteur qui suit l’angle de rotation d’un mécanisme.
Deux types d’encodeurs sont couramment utilisés en FTC : les encodeurs relatifs et les encodeurs absolus. Les encodeurs relatifs sont ceux dont il est question ici, car ils sont les plus courants.
Enodeurs relatifs¶
Qu’il s’agisse d’un encodeur intégré dans chaque moteur autorisé en FTC ou des encodeurs externes courants tels que l’encodeur REV Through Bore, ces encodeurs suivent la position relative de l’arbre ou du mécanisme auquel ils sont attachés. Cela signifie que la sortie de position est relative à la position initiale lors de la mise sous tension du robot, ce qui signifie que l’information sur la position est perdue entre les cycles d’alimentation.
Astuce
Les encodeurs relatifs ne se remettent pas à zéro au début des OpModes ! Vous pouvez utiliser STOP_AND_RESET_ENCODERS pour vous assurer que vos encodeurs sont toujours à zéro au début d’un OpMode (voir ci-dessous).
Tous les encodeurs relatifs de FTC utilisent le protocole « quadrature » pour envoyer des informations de position au expansion hub. Par conséquent, les encodeurs relatifs doivent être branchés dans les ports d’encodeur situés près des ports de moteur pour fonctionner.
Terminologie¶
Compte : Un « compte » (parfois appelé « tick ») correspond à un incrément de la position de l’encodeur. Les encodeurs relatifs indiquent leur position sous la forme d’un nombre égal au nombre de « ticks » ou de « comptes » que l’encodeur s’est déplacé par rapport à son angle de départ.
Comptes par révolution : Le nombre de « comptes » qu’un encodeur indique après avoir effectué exactement un tour. Cette valeur est couramment utilisée pour convertir les « comptes » d’un encodeur en degrés ou en révolutions.
Avertissement
La terminologie de la quadrature peut prêter à confusion ! Certains encodeurs peuvent indiquer des « impulsions par tour ». Une impulsion peut correspondre à 1 ou 4 comptes. D’autres encodeurs peuvent indiquer des « cycles par révolution », dont l’acronyme est le même que celui des comptes par révolution, ce qui peut prêter à confusion. La meilleure façon de vérifier est de brancher l’encodeur sur le moyeu REV et de le faire tourner d’un tour complet, puis de vérifier ce qu’il indique.
Programmation des encodeurs¶
Lecture des encodeurs¶
Dans le logiciel FTC, les encodeurs en quadrature et les moteurs sont accessibles avec le même objet moteur. Cela signifie que nous pouvons accéder à la position d’un encodeur de la manière suivante :
Bien que cela soit pratique si l’on utilise l’encodeur de moteur intégré, cela peut facilement devenir déroutant si l’on utilise des encodeurs externes. Pour utiliser des encodeurs externes, vous devez utiliser l’objet moteur associé au port. Par exemple, s’il y a un moteur dans le port 1 nommé « Arm Motor » et un encodeur externe branché dans le port d’encodeur 1, vous devez faire ce qui suit pour obtenir la position de l’encodeur :
C’est parfait ! Nous avons maintenant la position relative de notre encodeur, indiquée en nombre de « comptes » par rapport à ce qu’il considère comme zéro. Cependant, il est souvent pratique de faire démarrer l’encodeur à zéro au début de l’OpMode. Bien que cela ne change techniquement rien, cela peut aider au débogage et simplifier le code futur. Pour ce faire, nous pouvons ajouter un appel pour réinitialiser les encodeurs avant de les lire.
DcMotor motor = hardwareMap.dcMotor.get("Arm Motor");
motor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER); // Reset the motor encoder
motor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER); // Turn the motor back on when we are done
int position = motor.getCurrentPosition();
Remarque : RUN_WITHOUT_ENCODER ne désactive pas l’encodeur. Il indique au SDK de ne pas utiliser l’encodeur du moteur pour le contrôle intégré de la vitesse. Nous verrons ce que cela signifie dans une section ultérieure, mais pour l’instant, sachez que cela remet le moteur en marche afin que nous puissions l’utiliser après la réinitialisation de l’encodeur.
Nous avons maintenant notre position (en nombre de comptes) par rapport à l’angle de départ de l’encodeur. Nous pouvons créer un programme rapide pour voir cela en action. Ici, nous utilisons un encodeur de moteur branché sur un port nommé « Arm Motor » dans la configuration.
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
@TeleOp
public class EncoderOpmode extends LinearOpMode {
@Override
public void runOpMode() throws InterruptedException {
// Find a motor in the hardware map named "Arm Motor"
DcMotor motor = hardwareMap.dcMotor.get("Arm Motor");
// Reset the motor encoder so that it reads zero ticks
motor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
// Turn the motor back on, required if you use STOP_AND_RESET_ENCODER
motor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
waitForStart();
while (opModeIsActive()) {
// Get the current position of the motor
int position = motor.getCurrentPosition();
// Show the position of the motor on telemetry
telemetry.addData("Encoder Position", position);
telemetry.update();
}
}
}
Si vous exécutez l’OpMode ci-dessus et tournez l’encodeur, vous devriez voir les valeurs changer au fur et à mesure que vous vous déplacez. Si vous faites tourner l’arbre jusqu’à son point de départ, vous verrez le nombre revenir à (très près de) zéro. À titre d’exercice, faites tourner l’arbre d’un tour complet (360) degrés et notez le nombre.
Il y a une autre chose que l’on peut faire avec les encodeurs. Bien qu’il soit utile de connaître le nombre de comptes que quelque chose s’est déplacé, on a souvent besoin d’un autre nombre, comme le nombre de tours que l’encodeur a fait ou l’angle qu’il a pris. Pour déterminer ces chiffres, nous avons besoin d’une constante, le nombre de tours de l’encodeur ou CPR (Comptes par revolution). Pour les encodeurs externes, ce nombre est souvent indiqué dans une fiche technique. Pour les moteurs, il figure généralement sur la page du produit, bien que certains moteurs (notamment le réducteur ultraplanétaire Rev) ne l’indiquent pas clairement.
Astuce
Vous pouvez calculer le nombre de comptes d’un moteur en prenant le nombre de comptes du moteur de base et en le multipliant par le rapport de boîte de vitesses. Veillez à utiliser le rapport de boîte de vitesses réel lors de cette opération ! Par exemple, un moteur ultraplanétaire 5:1 aura un nombre de tours de 28 * (5,23) = 146,44 parce que le moteur de base a 28 tours et que le réducteur 5:1 a un rapport de 5,23:1. N’oubliez pas que lorsque vous utilisez deux réducteurs l’un sur l’autre, vous multipliez les rapports de vitesse ensemble.
Dans l’exemple suivant, nous divisons la position de l’encodeur par ses comptes par tour pour obtenir le nombre de tours que l’encodeur a effectué. Vous devez remplacer [Vos comptes par tour ici] par les comptes par tour de votre moteur, que vous trouverez sur sa page produit ou que vous calculerez à l’aide de l’astuce ci-dessus.
double CPR = [Your Counts Per Revolution Here];
int position = motor.getCurrentPosition();
double revolutions = position/CPR;
Il y a un autre chiffre que nous pouvons obtenir : l’angle de l’arbre. Le calcul de ce nombre est très simple. Il suffit de multiplier le nombre de rotations par 360 (puisqu’il y a 360 degrés dans une révolution). Vous remarquerez que ce nombre peut dépasser 360 lorsque l’arbre effectue plusieurs rotations. C’est pourquoi nous introduisons l’angleNormalisé, qui sera toujours compris entre 0 et 360.
double CPR = [Your Counts Per Revolution Here];
int position = motor.getCurrentPosition();
double revolutions = position/CPR;
double angle = revolutions * 360;
double angleNormalized = angle % 360;
Si l’on met tout cela bout à bout, on obtient le programme de test suivant.
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
@TeleOp
public class EncoderOpmode extends LinearOpMode {
@Override
public void runOpMode() throws InterruptedException {
// Find a motor in the hardware map named "Arm Motor"
DcMotor motor = hardwareMap.dcMotor.get("Arm Motor");
// Reset the motor encoder so that it reads zero ticks
motor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
// Turn the motor back on, required if you use STOP_AND_RESET_ENCODER
motor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
waitForStart();
while (opModeIsActive()) {
double CPR = [Your Counts Per Revolution Here];
// Get the current position of the motor
int position = motor.getCurrentPosition();
double revolutions = position/CPR;
double angle = revolutions * 360;
double angleNormalized = angle % 360;
// Show the position of the motor on telemetry
telemetry.addData("Encoder Position", position);
telemetry.addData("Encoder Revolutions", revolutions);
telemetry.addData("Encoder Angle (Degrees)", angle);
telemetry.addData("Encoder Angle - Normalized (Degrees)", angleNormalized);
telemetry.update();
}
}
}
Roues et bobines de suivi¶
Jusqu’à présent, nous avons surtout travaillé avec des moteurs en rotation. Cependant, de nombreux mécanismes de la FTC sont linéaires, et il peut être souhaitable de les mesurer également dans une unité linéaire. Heureusement, c’est très simple. Tout ce dont nous avons besoin, c’est de connaître le diamètre de l’objet que nous mesurons.
Faites attention lorsque vous choisissez votre diamètre. Pour les roues, il s’agit du diamètre extérieur de la roue, mais pour les bobines, il s’agit du diamètre intérieur de la bobine, où repose la corde. Pour les chaînes et les courroies, il s’agit du « diamètre primitif » du pignon ou de la poulie.
À partir de là, nous pouvons calculer la circonférence (la longueur de l’arc de cercle, ou la distance que la roue/la bobine parcourra en une rotation).
double diameter = 1.0; // Replace with your wheel/spool's diameter
double circumference = Math.PI * diameter;
double distance = circumference * revolutions;
Note
Les unités sont très importantes lorsqu’il s’agit de programmation FTC, assurez-vous donc qu’elles sont toujours cohérentes ! Les unités utilisées pour le diamètre sont les mêmes que celles utilisées pour la distance calculée. Ainsi, si vous mesurez votre diamètre en pouces, la distance rapportée sera également exprimée en pouces.
En combinant tout cela avec notre programme d’essais précédent, nous obtenons
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
@TeleOp
public class SpoolEncoderOpmode extends LinearOpMode {
@Override
public void runOpMode() throws InterruptedException {
// Find a motor in the hardware map named "Arm Motor"
DcMotor motor = hardwareMap.dcMotor.get("Arm Motor");
// Reset the motor encoder so that it reads zero ticks
motor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
// Turn the motor back on, required if you use STOP_AND_RESET_ENCODER
motor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
waitForStart();
while (opModeIsActive()) {
double CPR = [Your Counts Per Revolution Here];
double diameter = 1.0; // Replace with your wheel/spool's diameter
double circumference = Math.PI * diameter;
// Get the current position of the motor
int position = motor.getCurrentPosition();
double revolutions = position/CPR;
double angle = revolutions * 360;
double angleNormalized = angle % 360;
double distance = circumference * revolutions;
//Show the position of the motor on telemetry
telemetry.addData("Encoder Position", position);
telemetry.addData("Encoder Revolutions", revolutions);
telemetry.addData("Encoder Angle (Degrees)", angle);
telemetry.addData("Encoder Angle - Normalized (Degrees)", angleNormalized);
telemetry.addData("Linear Distance", distance);
telemetry.update();
}
}
}
Faire tourner les moteurs avec des encodeurs¶
Nous avons appris à lire les valeurs de l’encodeur, mais comment définir l’endroit où l’on veut aller et demander au moteur de s’y rendre ?
Plus tôt, nous avons appris à connaître le mode RUN_WITHOUT_ENCODER pour le moteur. Nous pouvons utiliser un autre mode moteur, RUN_TO_POSITION, pour indiquer au moteur de fonctionner jusqu’à une position spécifique en ticks, comme suit :
DcMotor motor = hardwareMap.dcmotor.get("Arm Motor");
motor.setMode(DcMotor.RunMode.RUN_TO_POSITION); // Tells the motor to run to the specific position
Astuce
Vous pouvez en savoir plus sur les modes d’exécution sur la page de documentation officielle de REV Robotics <https://docs.revrobotics.com/duo-control/programming/using-encoder-feedback>`_
Cependant, avant de demander au moteur de se rendre à une position, nous devons lui indiquer la position à laquelle il doit se rendre. Notez que cette valeur doit être un nombre entier. Modifions le code ci-dessus à cet effet.
Avertissement
Mettre le moteur en mode RUN_TO_POSITION avant de définir une position cible génère une erreur. Veillez à ne pas faire cela !
DcMotor motor = hardwareMap.dcmotor.get("Arm Motor");
int desiredPosition = 1000; // The position (in ticks) that you want the motor to move to
motor.setTargetPosition(desiredPosition); // Tells the motor that the position it should go to is desiredPosition
motor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
Ce code demande au moteur de se déplacer à 1000 ticks, en utilisant une boucle PID pour contrôler la position du moteur. Vous pouvez en savoir plus sur les boucles PID ici. <https://gm0.org/en/latest/docs/software/concepts/control-loops.html#pid>`_
Nous pouvons déterminer la vitesse de rotation du moteur à l’aide du code suivant :
DcMotor motor = hardwareMap.dcmotor.get("Arm Motor");
int desiredPosition = 1000; // The position (in ticks) that you want the motor to move to
motor.setTargetPosition(desiredPosition); // Tells the motor that the position it should go to is desiredPosition
motor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
motor.setPower(0.5); // Sets the maximum power that the motor can go at
Utilisons maintenant ces informations pour contrôler un bras dans un OpMode.
package org.firstinspires.ftc.teamcode.Tests;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
@TeleOp
public class ArmOpMode extends LinearOpMode {
@Override
public void runOpMode() throws InterruptedException {
// Position of the arm when it's lifted
int armUpPosition = 1000;
// Position of the arm when it's down
int armDownPosition = 0;
// Find a motor in the hardware map named "Arm Motor"
DcMotor armMotor = hardwareMap.dcMotor.get("Arm Motor");
// Reset the motor encoder so that it reads zero ticks
armMotor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
// Sets the starting position of the arm to the down position
armMotor.setTargetPosition(armDownPosition);
armMotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
waitForStart();
while (opModeIsActive()) {
// If the A button is pressed, raise the arm
if (gamepad1.a) {
armMotor.setTargetPosition(armUpPosition);
armMotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
armMotor.setPower(0.5);
}
// If the B button is pressed, lower the arm
if (gamepad1.b) {
armMotor.setTargetPosition(armDownPosition);
armMotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
armMotor.setPower(0.5);
}
// Get the current position of the armMotor
double position = armMotor.getCurrentPosition();
// Get the target position of the armMotor
double desiredPosition = armMotor.getTargetPosition();
// Show the position of the armMotor on telemetry
telemetry.addData("Encoder Position", position);
// Show the target position of the armMotor on telemetry
telemetry.addData("Desired Position", desiredPosition);
telemetry.update();
}
}
}











