Écrire un simple éditeur et abonné (C++)
Objectif : créer et exécuter un nœud d’éditeur et d’abonné à l’aide de C++.
Niveau du tutoriel : Débutant
Durée : 20 minutes
Contenu
Arrière-plan
Nodes sont des processus exécutables qui communiquent via le graphe ROS. Dans ce didacticiel, les nœuds se transmettront des informations sous forme de messages de chaîne via un topic. L’exemple utilisé ici est un simple système « locuteur » et « auditeur » ; un nœud publie des données et l’autre s’abonne au sujet afin de pouvoir recevoir ces données.
Le code utilisé dans ces exemples peut être trouvé ici.
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. Alors, naviguez dans ros2_ws/src
et exécutez la commande de création de package :
ros2 pkg create --build-type ament_cmake cpp_pubsub
Votre terminal renverra un message vérifiant la création de votre package cpp_pubsub
et tous ses fichiers et dossiers nécessaires.
Accédez à ros2_ws/src/cpp_pubsub/src
. Rappelez-vous qu’il s’agit du répertoire de tout package CMake auquel appartiennent les fichiers source contenant les exécutables.
2 Écrivez le nœud de l’éditeur
Téléchargez l’exemple de code Talker en saisissant la commande suivante :
wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_publisher/member_function.cpp
wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_publisher/member_function.cpp
Dans une invite de ligne de commande Windows :
curl -sk https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_publisher/member_function.cpp -o publisher_member_function.cpp
Ou en powershell :
curl https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_publisher/member_function.cpp -o publisher_member_function.cpp
Il y aura maintenant un nouveau fichier nommé publisher_member_function.cpp
. Ouvrez le fichier à l’aide de votre éditeur de texte préféré.
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
/* This example creates a subclass of Node and uses std::bind() to register a
* member function as a callback from the timer. */
class MinimalPublisher : public rclcpp::Node
{
public:
MinimalPublisher()
: Node("minimal_publisher"), count_(0)
{
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
timer_ = this->create_wall_timer(
500ms, std::bind(&MinimalPublisher::timer_callback, this));
}
private:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
2.1 Examiner le code
La partie supérieure du code inclut les en-têtes C++ standard que vous utiliserez. Après les en-têtes C++ standard se trouve l’inclusion rclcpp/rclcpp.hpp
qui vous permet d’utiliser les éléments les plus courants du système ROS 2. Le dernier est std_msgs/msg/string.hpp
, qui inclut le type de message intégré que vous utiliserez pour publier des données.
Ces lignes représentent les dépendances du nœud. Rappelez-vous que les dépendances doivent être ajoutées à package.xml
et CMakeLists.txt
, ce que vous ferez dans la section suivante.
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
La ligne suivante crée la classe de nœud MinimalPublisher
en héritant de rclcpp::Node
. Chaque this
dans le code fait référence au nœud.
class MinimalPublisher : public rclcpp::Node
Le constructeur public nomme le nœud minimal_publisher
et initialise count_
à 0. À l’intérieur du constructeur, l’éditeur est initialisé avec le type de message String
, le nom de sujet topic
et le taille de file d’attente requise pour limiter les messages en cas de sauvegarde. Ensuite, timer_
est initialisé, ce qui provoque l’exécution de la fonction timer_callback
deux fois par seconde.
public:
MinimalPublisher()
: Node("minimal_publisher"), count_(0)
{
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
timer_ = this->create_wall_timer(
500ms, std::bind(&MinimalPublisher::timer_callback, this));
}
La fonction timer_callback
est l’endroit où les données du message sont définies et les messages sont réellement publiés. La macro RCLCPP_INFO
garantit que chaque message publié est imprimé sur la console.
private:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
La dernière est la déclaration des champs timer, publisher et counter.
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
Après la classe MinimalPublisher
se trouve main
, où le nœud s’exécute réellement. rclcpp::init
initialise ROS 2 et rclcpp::spin
commence à traiter les données du nœud, y compris les rappels du temporisateur.
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
2.2 Ajouter des dépendances
Naviguez d’un niveau vers le répertoire ros2_ws/src/cpp_pubsub
, où les fichiers CMakeLists.txt
et package.xml
ont été créés pour vous.
Ouvrez package.xml
avec votre éditeur de texte.
Comme mentionné dans le tutoriel précédent, assurez-vous de remplir les champs <description>
, <maintainer>
et < licence>
balises :
<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
Ajoutez une nouvelle ligne après la dépendance buildtool ament_cmake
et collez les dépendances suivantes correspondant aux instructions d’inclusion de votre nœud :
<depend>rclcpp</depend>
<depend>std_msgs</depend>
Cela déclare que le paquet a besoin de rclcpp
et std_msgs
quand son code est exécuté.
Assurez-vous de sauvegarder le fichier.
2.3 CMakeLists.txt
Ouvrez maintenant le fichier CMakeLists.txt
. Sous la dépendance existante find_package(ament_cmake REQUIRED)
, ajoutez les lignes :
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
Après cela, ajoutez l’exécutable et nommez-le talker
afin que vous puissiez exécuter votre nœud en utilisant ros2 run
:
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
Enfin, ajoutez la section install(TARGETS…)
pour que ros2 run
puisse trouver votre exécutable :
install(TARGETS
talker
DESTINATION lib/${PROJECT_NAME})
Vous pouvez nettoyer votre CMakeLists.txt
en supprimant certaines sections et commentaires inutiles, il ressemble donc à ceci :
cmake_minimum_required(VERSION 3.5)
project(cpp_pubsub)
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
install(TARGETS
talker
DESTINATION lib/${PROJECT_NAME})
ament_package()
Vous pouvez créer votre package maintenant, sourcer les fichiers d’installation locaux et l’exécuter, mais créons d’abord le nœud d’abonné afin que vous puissiez voir le système complet au travail.
3 Écrivez le nœud de l’abonné
Retournez à ros2_ws/src/cpp_pubsub/src
pour créer le nœud suivant. Entrez le code suivant dans votre terminal :
wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_subscriber/member_function.cpp
wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_subscriber/member_function.cpp
Dans une invite de ligne de commande Windows :
curl -sk https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_subscriber/member_function.cpp -o subscriber_member_function.cpp
Ou en powershell :
curl https://raw.githubusercontent.com/ros2/examples/rolling/rclcpp/topics/minimal_subscriber/member_function.cpp -o subscriber_member_function.cpp
Saisir ls
dans la console renverra maintenant :
publisher_member_function.cpp subscriber_member_function.cpp
Ouvrez le subscriber_member_function.cpp
avec votre éditeur de texte.
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;
class MinimalSubscriber : public rclcpp::Node
{
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}
private:
void topic_callback(const std_msgs::msg::String & msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalSubscriber>());
rclcpp::shutdown();
return 0;
}
3.1 Examiner le code
Le code du nœud d’abonné est presque identique à celui de l’éditeur. Maintenant, le nœud est nommé minimal_subscriber
, et le constructeur utilise la classe create_subscription
du nœud pour exécuter le rappel.
Il n’y a pas de temporisateur car l’abonné répond simplement chaque fois que des données sont publiées dans le sujet sujet
.
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}
Rappelez-vous du tutoriel topic que le nom du sujet et le type de message utilisés par l’éditeur et l’abonné doivent correspondre pour leur permettre communiquer.
La fonction topic_callback
reçoit la chaîne de données du message publiée sur le sujet et l’écrit simplement dans la console à l’aide de la macro RCLCPP_INFO
.
La seule déclaration de champ dans cette classe est la souscription.
private:
void topic_callback(const std_msgs::msg::String & msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
La fonction main
est exactement la même, sauf qu’elle fait tourner le nœud MinimalSubscriber
. Pour le nœud de l’éditeur, tourner signifiait démarrer le minuteur, mais pour l’abonné, cela signifie simplement se préparer à recevoir des messages chaque fois qu’ils arrivent.
Étant donné que ce nœud a les mêmes dépendances que le nœud de l’éditeur, il n’y a rien de nouveau à ajouter à package.xml
.
3.2 CMakeLists.txt
Rouvrez CMakeLists.txt
et ajoutez l’exécutable et la cible du nœud d’abonné sous les entrées de l’éditeur.
add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
Assurez-vous d’enregistrer le fichier, puis votre système pub/sub devrait être prêt à l’emploi.
4 Construire et exécuter
Vous avez probablement déjà les packages rclcpp
et std_msgs
installés dans le cadre de votre système ROS 2. 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.
Toujours à la racine de votre espace de travail, ros2_ws
, construisez votre nouveau package :
colcon build --packages-select cpp_pubsub
colcon build --packages-select cpp_pubsub
colcon build --merge-install --packages-select cpp_pubsub
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 Talker :
ros2 run cpp_pubsub talker
Le terminal devrait commencer à publier des messages d’information toutes les 0,5 seconde, comme ceci :
[INFO] [minimal_publisher]: Publishing: "Hello World: 0"
[INFO] [minimal_publisher]: Publishing: "Hello World: 1"
[INFO] [minimal_publisher]: Publishing: "Hello World: 2"
[INFO] [minimal_publisher]: Publishing: "Hello World: 3"
[INFO] [minimal_publisher]: Publishing: "Hello World: 4"
Ouvrez un autre terminal, sourcez à nouveau les fichiers de configuration depuis ros2_ws
, puis démarrez le nœud d’écoute :
ros2 run cpp_pubsub listener
L’écouteur commencera à imprimer des messages sur la console, à partir du nombre de messages sur lequel l’éditeur se trouve à ce moment-là, comme ceci :
[INFO] [minimal_subscriber]: I heard: "Hello World: 10"
[INFO] [minimal_subscriber]: I heard: "Hello World: 11"
[INFO] [minimal_subscriber]: I heard: "Hello World: 12"
[INFO] [minimal_subscriber]: I heard: "Hello World: 13"
[INFO] [minimal_subscriber]: I heard: "Hello World: 14"
Entrez Ctrl+C
dans chaque terminal pour empêcher les nœuds de tourner.
Résumé
Vous avez créé deux nœuds pour publier et vous abonner à des données sur une rubrique. Avant de les compiler et de les exécuter, vous avez ajouté leurs dépendances et leurs exécutables aux fichiers de configuration du package.
Prochaines étapes
Ensuite, vous allez créer un autre package ROS 2 simple en utilisant le modèle service/client. Encore une fois, vous pouvez choisir de l’écrire en C++ ou Python.