Surveillance des changements de paramètres (C++)

Objectif : Apprendre à utiliser la classe ParameterEventHandler pour surveiller et réagir aux modifications de paramètres.

Niveau du didacticiel : Intermédiaire

Durée : 20 minutes

Arrière-plan

Souvent, un nœud doit répondre aux modifications de ses propres paramètres ou des paramètres d’un autre nœud. La classe ParameterEventHandler facilite l’écoute des modifications de paramètres afin que votre code puisse y répondre. Ce didacticiel vous montrera comment utiliser la version C++ de la classe ParameterEventHandler pour surveiller les modifications apportées aux paramètres d’un nœud ainsi que les modifications apportées aux paramètres d’un autre nœud.

Conditions préalables

Avant de commencer ce didacticiel, vous devez d’abord suivre les didacticiels suivants :

Tâches

Dans ce didacticiel, vous allez créer un nouveau package contenant des exemples de code, écrire du code C++ pour utiliser la classe ParameterEventHandler et tester le code résultant.

1 Créer un package

Tout d’abord, 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. Alors, naviguez dans ros2_ws/src puis créez-y un nouveau paquet :

ros2 pkg create --build-type ament_cmake cpp_parameter_event_handler --dependencies rclcpp

Votre terminal renverra un message vérifiant la création de votre package cpp_parameter_event_handler 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 events client 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_parameter_event_handler/src, créez un nouveau fichier appelé parameter_event_handler.cpp et collez le code suivant à l’intérieur :

#include <memory>

#include "rclcpp/rclcpp.hpp"

class SampleNodeWithParameters : public rclcpp::Node
{
public:
  SampleNodeWithParameters()
  : Node("node_with_parameters")
  {
    this->declare_parameter("an_int_param", 0);

    // Create a parameter subscriber that can be used to monitor parameter changes
    // (for this node's parameters as well as other nodes' parameters)
    param_subscriber_ = std::make_shared<rclcpp::ParameterEventHandler>(this);

    // Set a callback for this node's integer parameter, "an_int_param"
    auto cb = [this](const rclcpp::Parameter & p) {
        RCLCPP_INFO(
          this->get_logger(), "cb: Received an update to parameter \"%s\" of type %s: \"%ld\"",
          p.get_name().c_str(),
          p.get_type_name().c_str(),
          p.as_int());
      };
    cb_handle_ = param_subscriber_->add_parameter_callback("an_int_param", cb);
  }

private:
  std::shared_ptr<rclcpp::ParameterEventHandler> param_subscriber_;
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle_;
};

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

  return 0;
}

2.1 Examiner le code

La première instruction, #include <memory> est incluse afin que le code puisse utiliser le modèle std::make_shared. Le suivant, #include "rclcpp/rclcpp.hpp" est inclus pour permettre au code de référencer les diverses fonctionnalités fournies par l’interface rclcpp, y compris la classe ParameterEventHandler.

Après la déclaration de classe, le code définit une classe, SampleNodeWithParameters. Le constructeur de la classe déclare un paramètre entier an_int_param, avec une valeur par défaut de 0. Ensuite, le code crée un ParameterEventHandler qui sera utilisé pour surveiller les modifications apportées aux paramètres. Enfin, le code crée une fonction lambda et la définit comme rappel à invoquer chaque fois que an_int_param est mis à jour.

Note

Il est très important de sauvegarder le handle renvoyé par add_parameter_callback ; sinon, le rappel ne sera pas correctement enregistré.

SampleNodeWithParameters()
: Node("node_with_parameters")
{
  this->declare_parameter("an_int_param", 0);

  // Create a parameter subscriber that can be used to monitor parameter changes
  // (for this node's parameters as well as other nodes' parameters)
  param_subscriber_ = std::make_shared<rclcpp::ParameterEventHandler>(this);

  // Set a callback for this node's integer parameter, "an_int_param"
  auto cb = [this](const rclcpp::Parameter & p) {
      RCLCPP_INFO(
        this->get_logger(), "cb: Received an update to parameter \"%s\" of type %s: \"%ld\"",
        p.get_name().c_str(),
        p.get_type_name().c_str(),
        p.as_int());
    };
  cb_handle_ = param_subscriber_->add_parameter_callback("an_int_param", cb);
}

À la suite de SampleNodeWithParameters se trouve une fonction main typique qui initialise ROS, fait tourner le nœud échantillon afin qu’il puisse envoyer et recevoir des messages, puis s’arrête après que l’utilisateur ait saisi ^C sur la console.

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

  return 0;
}

2.2 Ajouter un exécutable

Pour construire ce code, ouvrez d’abord le fichier CMakeLists.txt et ajoutez les lignes de code suivantes sous la dépendance find_package(rclcpp REQUIRED)

add_executable(parameter_event_handler src/parameter_event_handler.cpp)
ament_target_dependencies(parameter_event_handler rclcpp)

install(TARGETS
  parameter_event_handler
  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 $ROS_DISTRO -y

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

colcon build --packages-select cpp_parameter_event_handler

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_parameter_event_handler parameter_event_handler

Le nœud est maintenant actif et a un seul paramètre et imprimera un message chaque fois que ce paramètre est mis à jour. Pour tester cela, ouvrez un autre terminal et sourcez le fichier d’installation ROS comme avant (. install/setup.bash) et exécutez la commande suivante :

ros2 param set node_with_parameters an_int_param 43

Le terminal exécutant le nœud affichera un message semblable au suivant :

[INFO] [1606950498.422461764] [node_with_parameters]: cb: Received an update to parameter "an_int_param" of type integer: "43"

Le rappel que nous avons défini précédemment dans le nœud a été appelé et a affiché la nouvelle valeur mise à jour. Vous pouvez maintenant mettre fin à l’exemple de parameter_event_handler en cours d’exécution en utilisant ^C dans le terminal.

3.1 Surveiller les modifications apportées aux paramètres d’un autre nœud

Vous pouvez également utiliser le ParameterEventHandler pour surveiller les modifications de paramètres apportées aux paramètres d’un autre nœud. Mettons à jour la classe SampleNodeWithParameters pour surveiller également les modifications apportées à un paramètre dans un autre nœud. Nous utiliserons l’application de démonstration parameter_blackboard pour héberger un double paramètre que nous surveillerons pour les mises à jour.

Commencez par mettre à jour le constructeur pour ajouter le code suivant après le code existant :

// Now, add a callback to monitor any changes to the remote node's parameter. In this
// case, we supply the remote node name.
auto cb2 = [this](const rclcpp::Parameter & p) {
    RCLCPP_INFO(
      this->get_logger(), "cb2: Received an update to parameter \"%s\" of type: %s: \"%.02lf\"",
      p.get_name().c_str(),
      p.get_type_name().c_str(),
      p.as_double());
  };
auto remote_node_name = std::string("parameter_blackboard");
auto remote_param_name = std::string("a_double_param");
cb_handle2_ = param_subscriber_->add_parameter_callback(remote_param_name, cb2, remote_node_name);

Ajoutez ensuite une autre variable membre, cb_handle2 pour le handle de rappel supplémentaire :

private:
  std::shared_ptr<rclcpp::ParameterEventHandler> param_subscriber_;
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle_;
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle2_;  // Add this
};

Dans un terminal, revenez à la racine de votre espace de travail, ros2_ws, et construisez votre package mis à jour comme avant :

colcon build --packages-select cpp_parameter_event_handler

Ensuite, sourcez les fichiers d’installation :

. install/setup.bash

Maintenant, pour tester la surveillance des paramètres distants, exécutez d’abord le nouveau code parameter_event_handler :

ros2 run cpp_parameter_event_handler parameter_event_handler

Ensuite, à partir d’un autre terminal (avec ROS initialisé), exécutez l’application de démonstration parameter_blackboard, comme suit :

ros2 run demo_nodes_cpp parameter_blackboard

Enfin, depuis un troisième terminal (avec ROS initialisé), définissons un paramètre sur le nœud parameter_blackboard :

ros2 param set parameter_blackboard a_double_param 3.45

Lors de l’exécution de cette commande, vous devriez voir une sortie dans la fenêtre parameter_event_handler, indiquant que la fonction de rappel a été invoquée lors de la mise à jour du paramètre :

[INFO] [1606952588.237531933] [node_with_parameters]: cb2: Received an update to parameter "a_double_param" of type: double: "3.45"

Résumé

Vous avez créé un nœud avec un paramètre et utilisé la classe ParameterEventHandler pour définir un rappel afin de surveiller les modifications apportées à ce paramètre. Vous avez également utilisé la même classe pour surveiller les modifications apportées à un nœud distant. Le ParameterEventHandler est un moyen pratique de surveiller les modifications de paramètres afin que vous puissiez ensuite répondre aux valeurs mises à jour.