Écrire un service et un client simples (Python)
Objectif : créer et exécuter des nœuds de service et de client à l’aide de Python.
Niveau du tutoriel : Débutant
Durée : 20 minutes
Contenu
Arrière-plan
Lorsque nodes communiquent en utilisant services, le nœud qui envoie une demande de données est appelé le nœud client, et celui qui répond à la demande est le nœud de service. La structure de la requête et de la réponse est déterminée par un fichier .srv
.
L’exemple utilisé ici est un simple système d’addition d’entiers ; un nœud demande la somme de deux entiers et l’autre répond avec le résultat.
Conditions préalables
Dans les tutoriels précédents, vous avez appris à créer un espace de travail et créer un package.
Tâches
1 Créer un package
Ouvrez un nouveau terminal et sourcez votre installation ROS 2 pour que les commandes ros2
fonctionnent.
Naviguez dans le répertoire ros2_ws
créé dans un tutoriel précédent.
Rappelez-vous que les packages doivent être créés dans le répertoire src
, et non à la racine de l’espace de travail. Accédez à ros2_ws/src
et créez un nouveau package :
ros2 pkg create --build-type ament_python py_srvcli --dependencies rclpy example_interfaces
Votre terminal renverra un message vérifiant la création de votre package py_srvcli
et de tous ses fichiers et dossiers nécessaires.
L’argument --dependencies
ajoutera automatiquement les lignes de dépendance nécessaires à package.xml
. example_interfaces
est le package qui inclut le fichier .srv dont vous aurez besoin pour structurer vos requêtes et réponses :
int64 a
int64 b
---
int64 sum
Les deux premières lignes sont les paramètres de la requête, et sous les tirets se trouve la réponse.
1.1 Mettre à jour package.xml
Comme vous avez utilisé l’option --dependencies
lors de la création du package, vous n’avez pas besoin d’ajouter manuellement des dépendances à package.xml
.
Comme toujours, assurez-vous d’ajouter la description, l’adresse e-mail et le nom du responsable, ainsi que les informations de licence à package.xml
.
<description>Python client server tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
1.2 Mettre à jour setup.py
Ajoutez les mêmes informations au fichier setup.py
pour les champs maintainer
, maintainer_email
, description
et license
:
maintainer='Your Name',
maintainer_email='you@email.com',
description='Python client server tutorial',
license='Apache License 2.0',
2 Écrivez le nœud de service
Dans le répertoire ros2_ws/src/py_srvcli/py_srvcli
, créez un nouveau fichier appelé service_member_function.py
et collez le code suivant à l’intérieur :
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
class MinimalService(Node):
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
return response
def main():
rclpy.init()
minimal_service = MinimalService()
rclpy.spin(minimal_service)
rclpy.shutdown()
if __name__ == '__main__':
main()
2.1 Examiner le code
La première instruction import
importe le type de service AddTwoInts
du paquet example_interfaces
. L’instruction import
suivante importe la bibliothèque client ROS 2 Python, et plus particulièrement la classe Node
.
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
Le constructeur de la classe MinimalService
initialise le nœud avec le nom minimal_service
. Ensuite, il crée un service et définit le type, le nom et le rappel.
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
La définition du rappel de service reçoit les données de la demande, les additionne et renvoie la somme en réponse.
def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
return response
Enfin, la classe principale initialise la bibliothèque client Python ROS 2, instancie la classe MinimalService
pour créer le nœud de service et fait tourner le nœud pour gérer les rappels.
2.2 Ajouter un point d’entrée
Pour permettre à la commande ros2 run
d’exécuter votre nœud, vous devez ajouter le point d’entrée à setup.py
(situé dans le répertoire ros2_ws/src/py_srvcli
).
Ajoutez la ligne suivante entre les crochets 'console_scripts':
:
'service = py_srvcli.service_member_function:main',
3 Écrivez le nœud client
Dans le répertoire ros2_ws/src/py_srvcli/py_srvcli
, créez un nouveau fichier appelé client_member_function.py
et collez le code suivant à l’intérieur :
import sys
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
class MinimalClientAsync(Node):
def __init__(self):
super().__init__('minimal_client_async')
self.cli = self.create_client(AddTwoInts, 'add_two_ints')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
self.req = AddTwoInts.Request()
def send_request(self, a, b):
self.req.a = a
self.req.b = b
self.future = self.cli.call_async(self.req)
rclpy.spin_until_future_complete(self, self.future)
return self.future.result()
def main():
rclpy.init()
minimal_client = MinimalClientAsync()
response = minimal_client.send_request(int(sys.argv[1]), int(sys.argv[2]))
minimal_client.get_logger().info(
'Result of add_two_ints: for %d + %d = %d' %
(int(sys.argv[1]), int(sys.argv[2]), response.sum))
minimal_client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
3.1 Examiner le code
La seule instruction import
différente pour le client est import sys
. Le code du nœud client utilise sys.argv pour accéder aux arguments d’entrée de la ligne de commande pour la requête.
La définition du constructeur crée un client avec le même type et le même nom que le nœud de service. Le type et le nom doivent correspondre pour que le client et le service puissent communiquer.
La boucle while
dans le constructeur vérifie si un service correspondant au type et au nom du client est disponible une fois par seconde.
Sous le constructeur se trouve la définition de la requête, suivie de main
.
La seule différence significative dans le main
du client est la boucle while
. La boucle vérifie le futur
pour voir s’il y a une réponse du service, tant que le système est en cours d’exécution. Si le service a envoyé une réponse, le résultat sera écrit dans un message de journal.
3.2 Ajouter un point d’entrée
Comme le nœud de service, vous devez également ajouter un point d’entrée pour pouvoir exécuter le nœud client.
Le champ entry_points
de votre fichier setup.py
devrait ressembler à ceci :
entry_points={
'console_scripts': [
'service = py_srvcli.service_member_function:main',
'client = py_srvcli.client_member_function:main',
],
},
4 Construire et exécuter
Il est recommandé d’exécuter rosdep
à la racine de votre espace de travail (ros2_ws
) pour vérifier les dépendances manquantes avant de compiler :
rosdep install -i --from-path src --rosdistro rolling -y
rosdep ne fonctionne que sous Linux, vous pouvez donc passer à l’étape suivante.
rosdep ne fonctionne que sous Linux, vous pouvez donc passer à l’étape suivante.
Revenez à la racine de votre espace de travail, ros2_ws
, et créez votre nouveau package :
colcon build --packages-select py_srvcli
Ouvrez un nouveau terminal, accédez à ros2_ws
et sourcez les fichiers d’installation :
. install/setup.bash
. install/setup.bash
call install/setup.bat
Exécutez maintenant le nœud de service :
ros2 run py_srvcli service
Le nœud attendra la demande du client.
Ouvrez un autre terminal et sourcez à nouveau les fichiers de configuration depuis ros2_ws
. Démarrez le nœud client, suivi de deux nombres entiers séparés par un espace :
ros2 run py_srvcli client 2 3
Si vous choisissez 2
et 3
, par exemple, le client recevra une réponse comme celle-ci :
[INFO] [minimal_client_async]: Result of add_two_ints: for 2 + 3 = 5
Revenez au terminal sur lequel votre nœud de service est en cours d’exécution. Vous verrez qu’il a publié des messages de journal lorsqu’il a reçu la demande :
[INFO] [minimal_service]: Incoming request
a: 2 b: 3
Entrez Ctrl+C
dans le terminal du serveur pour empêcher le nœud de tourner.
Résumé
Vous avez créé deux nœuds pour demander et répondre aux données sur un service. Vous avez ajouté leurs dépendances et leurs exécutables aux fichiers de configuration du package afin de pouvoir les créer et les exécuter, ce qui vous permet de voir un système service/client au travail.
Prochaines étapes
Dans les derniers didacticiels, vous avez utilisé des interfaces pour transmettre des données entre des sujets et des services. Ensuite, vous apprendrez à créer des interfaces personnalisées.