Utiliser des paramètres dans une classe (C++)

Objectif : Créer et exécuter une classe avec des paramètres ROS à l’aide de C++.

Niveau du tutoriel : Débutant

Durée : 20 minutes

Arrière-plan

Lorsque vous créez vos propres nodes, vous devrez parfois ajouter des paramètres pouvant être définis à partir du fichier de lancement.

Ce didacticiel vous montrera comment créer ces paramètres dans une classe C++ et comment les définir dans un fichier de lancement.

Conditions préalables

Dans les tutoriels précédents, vous avez appris à créer un espace de travail et créer un package. Vous avez également découvert parameters et leur fonction dans un système ROS 2.

Tâches

1 Créer un package

Ouvrez un nouveau terminal et sourcez votre installation ROS 2 pour que les commandes ros2 fonctionnent.

Suivez ces instructions pour créer un nouvel espace de travail nommé ros2_ws.

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_cmake cpp_parameters --dependencies rclcpp

Votre terminal renverra un message vérifiant la création de votre package cpp_parameters et tous ses fichiers et dossiers nécessaires.

L’argument --dependencies ajoutera automatiquement les lignes de dépendance nécessaires à package.xml et CMakeLists.txt.

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 ou CMakeLists.txt.

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>C++ parameter tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>

2 Écrivez le nœud C++

Dans le répertoire ros2_ws/src/cpp_parameters/src, créez un nouveau fichier appelé cpp_parameters_node.cpp et collez le code suivant à l’intérieur :

#include <chrono>
#include <functional>
#include <string>

#include <rclcpp/rclcpp.hpp>

using namespace std::chrono_literals;

class MinimalParam : public rclcpp::Node
{
public:
  MinimalParam()
  : Node("minimal_param_node")
  {
    this->declare_parameter("my_parameter", "world");

    timer_ = this->create_wall_timer(
      1000ms, std::bind(&MinimalParam::timer_callback, this));
  }

  void timer_callback()
  {
    std::string my_param =
      this->get_parameter("my_parameter").get_parameter_value().get<std::string>();

    RCLCPP_INFO(this->get_logger(), "Hello %s!", my_param.c_str());

    std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")};
    this->set_parameters(all_new_parameters);
  }

private:
  rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalParam>());
  rclcpp::shutdown();
  return 0;
}

2.1 Examiner le code

Les déclarations #include en haut sont les dépendances du paquet.

Le morceau de code suivant crée la classe et le constructeur. La première ligne de ce constructeur crée un paramètre avec le nom my_parameter et une valeur par défaut de world. Le type de paramètre est déduit de la valeur par défaut, donc dans ce cas, il serait défini sur un type de chaîne. Ensuite, le timer_ est initialisé avec une période de 1000 ms, ce qui provoque l’exécution de la fonction timer_callback une fois par seconde.

class MinimalParam : public rclcpp::Node
{
public:
  MinimalParam()
  : Node("minimal_param_node")
  {
    this->declare_parameter("my_parameter", "world");

    timer_ = this->create_wall_timer(
      1000ms, std::bind(&MinimalParam::timer_callback, this));
  }

La première ligne de notre fonction timer_callback récupère le paramètre my_parameter du nœud et le stocke dans my_param. Ensuite, la fonction RCLCPP_INFO s’assure que le message est enregistré. La fonction set_parameters définit ensuite le paramètre my_parameter à la valeur de chaîne par défaut world. Dans le cas où l’utilisateur a modifié le paramètre de manière externe, cela garantit qu’il est toujours réinitialisé à l’original.

void timer_callback()
{
  std::string my_param =
    this->get_parameter("my_parameter").get_parameter_value().get<std::string>();

  RCLCPP_INFO(this->get_logger(), "Hello %s!", my_param.c_str());

  std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")};
  this->set_parameters(all_new_parameters);
}

La dernière est la déclaration de timer_

private:
  rclcpp::TimerBase::SharedPtr timer_;

Après notre MinimalParam est notre main. Ici, ROS 2 est initialisé, une instance de la classe MinimalParam est construite et rclcpp::spin commence à traiter les données du nœud.

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalParam>());
  rclcpp::shutdown();
  return 0;
}
2.1.1 (Facultatif) Ajouter un descripteur de paramètre

Vous pouvez éventuellement définir un descripteur pour le paramètre. Les descripteurs vous permettent de spécifier une description textuelle du paramètre et de ses contraintes, comme le rendre en lecture seule, spécifier une plage, etc. Pour que cela fonctionne, le code du constructeur doit être modifié en :

// ...

class MinimalParam : public rclcpp::Node
{
public:
  MinimalParam()
  : Node("minimal_param_node")
  {
    auto param_desc = rcl_interfaces::msg::ParameterDescriptor{};
    param_desc.description = "This parameter is mine!";

    this->declare_parameter("my_parameter", "world", param_desc);

    timer_ = this->create_wall_timer(
      1000ms, std::bind(&MinimalParam::timer_callback, this));
  }

Le reste du code reste le même. Une fois que vous avez exécuté le nœud, vous pouvez alors exécuter ros2 param describe /minimal_param_node my_parameter pour voir le type et la description.

2.2 Ajouter un exécutable

Ouvrez maintenant le fichier CMakeLists.txt. Sous la dépendance find_package(rclcpp REQUIRED) ajoutez les lignes de code suivantes.

add_executable(minimal_param_node src/cpp_parameters_node.cpp)
ament_target_dependencies(minimal_param_node rclcpp)

install(TARGETS
  minimal_param_node
  DESTINATION lib/${PROJECT_NAME}
)

3 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

Revenez à la racine de votre espace de travail, ros2_ws, et créez votre nouveau package :

colcon build --packages-select cpp_parameters

Ouvrez un nouveau terminal, accédez à ros2_ws et sourcez les fichiers d’installation :

. install/setup.bash

Exécutez maintenant le nœud :

ros2 run cpp_parameters minimal_param_node

Le terminal doit renvoyer le message suivant toutes les secondes :

[INFO] [minimal_param_node]: Hello world!

Vous pouvez maintenant voir la valeur par défaut de votre paramètre, mais vous souhaitez pouvoir la définir vous-même. Il existe deux façons d’y parvenir.

3.1 Modification via la console

Cette partie utilisera les connaissances que vous avez acquises grâce au tutoriel sur les paramètres et les appliquera au nœud que vous venez de créé.

Assurez-vous que le nœud est en cours d’exécution :

ros2 run cpp_parameters minimal_param_node

Ouvrez un autre terminal, sourcez à nouveau les fichiers d’installation depuis ros2_ws et entrez la ligne suivante :

ros2 param list

Vous y verrez le paramètre personnalisé my_parameter. Pour le changer, exécutez simplement la ligne suivante dans la console :

ros2 param set /minimal_param_node my_parameter earth

Vous savez que cela s’est bien passé si vous obtenez la sortie Set parameters réussi. Si vous regardez l’autre terminal, vous devriez voir la sortie changer en [INFO] [minimal_param_node] : Hello Earth !

3.2 Modification via un fichier de lancement

Vous pouvez également définir le paramètre dans un fichier de lancement, mais vous devrez d’abord ajouter le répertoire de lancement. Dans le répertoire ros2_ws/src/cpp_parameters/, créez un nouveau répertoire appelé launch. Là, créez un nouveau fichier appelé cpp_parameters_launch.py

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package="cpp_parameters",
            executable="minimal_param_node",
            name="custom_minimal_param_node",
            output="screen",
            emulate_tty=True,
            parameters=[
                {"my_parameter": "earth"}
            ]
        )
    ])

Ici, vous pouvez voir que nous définissons my_parameter sur earth lorsque nous lançons notre nœud minimal_param_node. En ajoutant les deux lignes ci-dessous, nous nous assurons que notre sortie est imprimée dans notre console.

output="screen",
emulate_tty=True,

Ouvrez maintenant le fichier CMakeLists.txt. Sous les lignes que vous avez ajoutées précédemment, ajoutez les lignes de code suivantes.

install(
  DIRECTORY launch
  DESTINATION share/${PROJECT_NAME}
)

Ouvrez une console et accédez à la racine de votre espace de travail, ros2_ws, et créez votre nouveau package :

colcon build --packages-select cpp_parameters

Sourcez ensuite les fichiers de configuration dans un nouveau terminal :

. install/setup.bash

Exécutez maintenant le nœud en utilisant le fichier de lancement que nous venons de créer :

ros2 launch cpp_parameters cpp_parameters_launch.py

Le terminal doit renvoyer le message suivant toutes les secondes :

[INFO] [custom_minimal_param_node]: Hello earth!

Résumé

Vous avez créé un nœud avec un paramètre personnalisé, qui peut être défini à partir d’un fichier de lancement ou de la ligne de commande. Vous avez ajouté les dépendances, les exécutables et un fichier de lancement aux fichiers de configuration du package afin de pouvoir les générer et les exécuter, et voir le paramètre en action.

Prochaines étapes

Maintenant que vous disposez de certains packages et de vos propres systèmes ROS 2, le next tutorial vous montrera comment examiner les problèmes de votre environnement et de vos systèmes au cas où vous auriez des problèmes .