Lire et écrire sur du matériel¶
Lorsque vous utilisez le SDK FTC®, il existe une variété de classes matérielles intégrées qui peuvent être utilisées pour communiquer avec le matériel du robot, comme les moteurs CC, Servos, et les capteurs.
Création et instanciation d’objets matériels¶
La première chose à faire pour créer correctement un objet est d’importer sa classe. Dans Android Studio, si la classe est référencée sans être importée, il est possible d’appuyer sur Alt+Enter pour l’importer automatiquement. Une fois la classe importée, l’étape suivante consiste à créer l’objet: :
private DcMotor liftMotor;
Une fois l’objet créé, il doit être instancié. Une partie de la superclasse OpMode est appelée hardwareMap. hardwareMap est utilisé dans le SDK FTC pour instancier les objets plutôt que d’appeler un constructeur.
Il contient toutes les informations saisies dans la configuration du contrôleur de robot, telles que les noms du matériel et le port sur lequel il est branché. Voici un exemple d’instanciation du moteur que nous avons créé ci-dessus: :
liftMotor = hardwareMap.get(DcMotor.class, "Lift Motor");
Quel que soit le capteur que vous utilisez, vous passerez cette classe à l’endroit où se trouve DcMotor.class. Par exemple, si liftMotor était un Servo, Servo.class serait passé à la place.
Pour le second argument, vous passez le nom du périphérique dans la configuration du contrôleur de robot. hardwareMap va alors chercher le port sur lequel le périphérique portant ce nom est branché, ce qui permet d’accéder au matériel.
Exemples d’utilisation de composants matériels communs¶
Moteur à courant continu¶
DcMotor leftMotor = hardwareMap.get(DcMotor.class, "Left Motor");
DcMotor rightMotor = hardwareMap.get(DcMotor.class, "Right Motor");
DcMotor elevatorMotor = hardware.get(DcMotor.class, "Elevator Motor");
DcMotor intakeMotor = hardware.get(DcMotor.class, "Intake Motor");
Après l’instanciation d’un DcMotor, il y a quelques variables que vous pouvez définir pour affecter la façon dont le moteur DC fonctionne. La première d’entre elles est la direction: :
leftMotor.setDirection(DcMotor.Direction.REVERSE);
rightMotor.setDirection(DcMotor.Direction.FORWARD);
Le changement de direction du moteur fait exactement ce que l’on attend de lui : il change de direction. Si une puissance de 1 est appliquée au moteur lorsqu’il est en mode marche avant, il tournera dans un sens. S’il est en marche arrière, une puissance de 1 le fera tourner dans l’autre sens. Si vous faites face à l’arbre du moteur, la marche avant est dans le sens inverse des aiguilles d’une montre (à l’exception des moteurs NeveRest).
Ensuite, il y a deux comportements de puissance nulle qui peuvent être ajustés: :
leftMotor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
rightMotor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.FLOAT);
La modification de cette variable affecte le comportement du moteur à courant continu lorsqu’une puissance de 0 est appliquée. BRAKE fera en sorte que le moteur essaie de se ralentir s’il est en mouvement (il ne fera PAS en sorte que le moteur maintienne sa position s’il n’est pas déjà en mouvement), tandis que FLOTTANT fera en sorte que le moteur glisse jusqu’à l’arrêt, laissant la friction faire tout le travail.
Enfin, il existe quatre modes de fonctionnement différents qui peuvent être utilisés avec les moteurs à courant continu : : :
leftMotor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
rightMotor.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
elevatorMotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
intakeMotor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
Il est important de noter que les valeurs du codeur peuvent être lues dans n’importe lequel de ces modes, à condition qu’un codeur soit correctement branché. Ces modes modifient simplement la façon dont le moteur réagit à ces valeurs d’encodage. La documentation de REV Robotics contient une explication des quatre modes de fonctionnement <https://docs.revrobotics.com/duo-control/programming/using-encoder-feedback#choosing-a-motor-mode>`_.
Avertissement
RUN_TO_POSITION peut être un moyen pratique de contrôler un mécanisme à un seul moteur, car il décharge tout le travail de contrôle ; cependant, comme chaque moteur est traité indépendamment, il est déconseillé de l’utiliser sur des mécanismes à plusieurs moteurs, en particulier sur les chaînes cinématiques.
Encodeurs¶
Définition
- Encodeur¶
Un encodeur est un dispositif qui suit (généralement) un mouvement de rotation autour d’un axe.
Il existe des encodeurs absolus et des codeurs relatifs. Un encodeur absolu indique exactement l’angle de rotation de l’arbre par rapport à son « zéro » absolu. Un encodeur relatif indique la distance à laquelle l’arbre shaft a tourné depuis le début du suivi (par exemple, lorsque l’autonomie commence). Les encodeurs relatifs ont une sortie en quadrature, tandis que les encodeurs absolus ont généralement des sorties analogiques ou i2c.
Les encodeurs sont utilisés pour déterminer la position du robot ou de l’un de ses mécanismes.
Bien que tous les moteurs légaux de la FTC contiennent des encodeurs relatifs en quadrature intégrés, ils doivent être câblés séparément et ne sont pas nécessaires à leur utilisation. Des encodeurs externes peuvent être utilisés et branchés sur un port de codeur à condition qu’ils utilisent le protocole de communication en quadrature.
L’accès aux encodeurs nécessite d’appeler une méthode sur l’objet DcMotor, getCurrentPosition(), qui renvoie la position actuelle de l’encodeur branché sur le port. Ce nombre peut être arbitraire au début d’un mode op, et n’est pas remis à 0 à moins que STOP_AND_RESET_ENCODERS ne soit utilisé ou que l’alimentation du hub d’expansion ne soit cyclique.
Important
Il n’existe pas vraiment de terminologie normalisée pour les encodeurs en quadrature. Le SDK utilise par défaut « CPR » ou « Counts Per Revolution ». Certaines fiches techniques indiquent également « PPR » ou Impulsions par Révolution. Une impulsion peut être équivalente à 1 à 4 « comptes » du SDK. Soyez prudent lorsque vous lisez des fiches techniques !
Avertissement
Les encodeurs ayant un nombre élevé de comptes par révolution, tels que le codeur à alésage traversant REV, peuvent perdre des pas s’ils sont branchés sur les ports 1 ou 2. De plus, les appels à getVelocity() sur un objet DcMotorEx peuvent déborder avec des encodeurs à grand nombre de comptes par révolution, car le nombre retourné n’est qu’un entier signé de 16 bits.
Servo¶
Servo relicServo = hardwareMap.get(Servo.class, "Release Servo");
Après avoir instancié un Servo, il y a deux fonctions principales qui peuvent être appelées : setPosition() et getPosition(). : :
releaseServo.setPosition(0.75);
telemetry.addData("Release Servo Target", releaseServo.getPosition());
setPosition() définit la position du servo. Le SDK utilisera une boucle de contrôle intégrée avec le potentiomètre servo’s pour conduire le servo à cette position et la maintenir. setPosition() prend un double entre 0 et 1, où 0 est la limite inférieure de rotation du servo et 1 est la limite supérieure de rotation du servo. Tout ce qui se trouve entre les deux est directement proportionnel, donc 0,5 est le milieu, 0,75 est les 3/4 du chemin vers le haut, etc.
getPosition() ne renvoie pas la position actuelle du servo, mais plutôt sa position cible actuelle. Si une variable pour la position actuelle du servo est stockée correctement, cette fonction ne devrait jamais être nécessaire.
Servo à rotation continue¶
CRServo intakeServo = hardwareMap.get(CRServo.class, "Intake Servo");
Un CRServo a une méthode principale, setPower(). Son fonctionnement est très similaire à celui de setPower() de DcMotor, ce qui signifie que lui passer 0 le fait s’arrêter, lui passer 1 le fait avancer à pleine vitesse, lui passer -1 le fait reculer à pleine vitesse, et tout ce qui se trouve entre les deux… : :
intakeServo.setPower(0.75);
Digital IO¶
DigitalChannel digitalDevice = hardwareMap.get(DigitalChannel.class, "digital device");
Un DigitalChannel possède quelques méthodes principales. setMode() est utilisée pour définir le port en tant que port OUTPUT ou INPUT, ``getState()` retourne l’état actuel du port (ne fonctionne qu’en mode INPUT), et ``setState()` définit l’état du port (ne fonctionne qu’en mode OUTPUT).
Astuce
Les ports numériques démarrent par défaut en mode INPUT
Danger
Les ports numériques sont tirés vers le haut pour éviter le flottement. Cela signifie qu’il y a une résistance entre le port et 3,3 V, de sorte que le port se lit HAUT par défaut lorsque rien n’est connecté. Par conséquent, les dispositifs numériques DOIVENT connecter la broche numérique à la terre lorsqu’elle est fermée, puis la laisser non connectée lorsqu’elle est ouverte. Pour les interrupteurs de fin de course, cela signifie qu’il faut connecter un fil à la terre et l’autre au port numérique. Un mauvais branchement (connexion de 3,3 V au port numérique) peut provoquer une instabilité et faire planter votre concentrateur d’expansion.
Entrée analogique¶
AnalogInput analogInput = hardwareMap.get(AnalogInput.class, "analog input");
Une AnalogInput a une méthode principale : getVoltage() qui est utilisée pour obtenir la tension d’entrée actuelle sur le port.
Note
Bien que getMaxVoltage() renvoie 3.3v, les ports d’entrée analogique des concentrateurs d’expansion et de contrôle peuvent en toute sécurité supporter jusqu’à 5v.
Note sur la vitesse d’appel du matériel¶
Chaque appel matériel que vous effectuez (qu’il s’agisse de régler la puissance d’un moteur, de définir une position servo, de lire une valeur d’encodeur, etc.) prendra environ 3 millisecondes pour être exécuté, à l’exception des appels I2C qui peuvent prendre jusqu’à 7 ms. Cela s’explique par le fait qu’en coulisses, le SDK peut avoir besoin de faire plusieurs appels au matériel pour effectuer l’opération I2C.
Note
Lorsque vous utilisez un concentrateur de contrôle, vous pouvez constater des temps d’appel matériel considérablement plus rapides car le concentrateur de contrôle utilise une connexion UART directe avec la carte Lynx au lieu de passer par USB et un intermédiaire FTDI comme c’est le cas lorsqu’on utilise un téléphone.
Ces temps peuvent sembler rapides, mais ils s’additionnent rapidement. Prenons l’exemple d’une boucle de contrôle permettant d’avancer pour N comptes codeur tout en maintenant le cap à l’aide de l’IMU. Cela nécessiterait 5 appels matériels normaux (4 réglages de puissance + 1 lecture de codeur) et un appel I2C (IMU), ce qui signifie que le cycle de la boucle prendrait environ 22 ms pour s’exécuter, et donc fonctionnerait à environ 45 Hz.
Cela signifie qu’il est essentiel de minimiser le nombre d’appels au matériel que vous effectuez afin que vos boucles de contrôle fonctionnent rapidement. Par exemple, ne lisez pas un capteur plus d’une fois par boucle. Au contraire, lisez-le une fois et stockez la valeur dans une variable si vous avez besoin de l’utiliser à nouveau à d’autres moments du même cycle de boucle.
L’utilisation d’un appel matériel de lecture en bloc peut résoudre ce problème. L’exécution d’une lecture en bloc prend les mêmes 3 ms que n’importe quel autre appel matériel normal, mais elle renvoie beaucoup plus de données. Pour pouvoir utiliser les lectures en bloc, vous devez utiliser le SDK v5.4 ou une version ultérieure. Voir Bulk Reads pour plus d’informations.