Ce tutoriel explique comment introduire des repères coordonnés supplémentaires et fixes dans le système tf de ROS.
Pourquoi ajouter des repères coordonnés ?
Dans de nombreux scénarios, il est plus intuitif de raisonner dans un référentiel local. Par exemple, une lecture de télémètre laser est plus facilement interprétable dans un système de coordonnées centré sur le capteur lui-même. Le système tf permet de définir un tel référentiel local pour chaque capteur, chaque maillon de chaîne cinématique, etc., tout en gérant automatiquement les transformations nécessaires entre tous ces référentiels.
Structure des Repères Coordonnés dans tf
tf organise les repères coordonnés sous forme d'arbre. Cela signfiie qu'un repère ne peut avoir qu'un seul parent, mais peut avoir plusieurs enfants. Dans notre exemple de base, nous avons les repères world, turtle1 et turtle2, où les deux tortues sont des enfants du repère world. Pour ajouter un nouveau repère, l'un des repères existants doit devenir son parent.
Implémentation de l'ajout de repères
Dans l'exemple des tortues, nous allons ajouter un nouveau repère nommé carrot1, dont le parent sera turtle1.
Créez un nouveau fichier nommé frame_tf_broadcaster.cpp dans le répertoire learning_tf/src :
$ roscd learning_tf
$ touch src/frame_tf_broadcaster.cpp
$ vim src/frame_tf_broadcaster.cpp
Voici le contenu du fichier :
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
int main(int argc, char** argv) {
ros::init(argc, argv, "my_tf_broadcaster");
ros::NodeHandle node;
tf::TransformBroadcaster broadcaster;
tf::Transform transform;
ros::Rate loop_rate(10.0); // Fréquence de publication de 10 Hz
while (node.ok()) {
// Définir l'origine du nouveau repère par rapport à son parent ("turtle1")
transform.setOrigin(tf::Vector3(0.0, 2.0, 0.0));
// Définir la rotation (ici, aucune rotation initiale)
transform.setRotation(tf::Quaternion(0, 0, 0, 1));
// Diffuser la transformation : (transform, temps, parent, enfant)
broadcaster.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "turtle1", "carrot1"));
loop_rate.sleep();
}
return 0;
}
Explication du code
transform.setOrigin(tf::Vector3(0.0, 2.0, 0.0));: Définit la position du repèrecarrot1à 2 unités sur l'axe Y par rapport àturtle1.transform.setRotation(tf::Quaternion(0, 0, 0, 1));: Définit l'orientation. Un quaternion (0,0,0,1) représente l'identité, c'est-à-dire aucune rotation.broadcaster.sendTransform(...): La fonction principale qui publie la transformation entre le repère parentturtle1et le repère enfantcarrot1.
Compilation et Exécution
Modifiez le fichier CMakeLists.txt du package learning_tf pour inclure le nouvel exécutable :
$ rosed learning_tf CMakeLists.txt
Ajoutez les lignes suivantes à la fin du fichier :
add_executable(frame_tf_broadcaster src/frame_tf_broadcaster.cpp)
target_link_libraries(frame_tf_broadcaster ${catkin_LIBRARIES})
Recompilez votre espace de travail Catkin :
$ cd ~/catkin_ws
$ catkin_make
Ajoutez le nouveau nœud au fichier de lancement start_demo.launch :
$ rosed learning_tf start_demo.launch
Insérez la ligne suivante dans la section <launch> :
<node pkg="learning_tf" type="frame_tf_broadcaster" name="broadcaster_frame" />
Lancez la démo :
$ roslaunch learning_tf start_demo.launch
Vérification et Adaptation du Listener
Si vous lancez la démo et déplacez turtle1, vous remarquerez que le comportement général n'a pas changé. Ceci est dû au fait que le listener (turtle_tf_listener.cpp) utilise toujours la transformation entre /turtle2 et /turtle1. Pour observer l'effet du nouveau repère, nous devons modifier le listener pour qu'il utilise /carrot1.
Modifiez les lignes 26-27 du fichier src/turtle_tf_listener.cpp pour remplacer /turtle1 par /carrot1 :
listener.lookupTransform("/turtle2", "/carrot1",
ros::Time(0), transform);
Recompilez et relancez la démo :
$ catkin_make
$ roslaunch learning_tf start_demo.launch
Vous devriez maintenant observer que turtle2 suit /carrot1 au lieu de directement turtle1.
Diffusion de Repères Coordonnés Mobiles
Jusqu'à présent, le repère carrot1 était fixe par rapport à turtle1. Pour créer un repère mobile, il suffit de modifier le calcul de sa position et de son orientation dans le temps. Modifions le fichier src/frame_tf_broadcaster.cpp pour que carrot1 bouge en cercle autour de turtle1.
Éditez le fichier src/frame_tf_broadcaster.cpp :
$ vim src/frame_tf_broadcaster.cpp
Remplacez la section définissant l'origine par le code suivant :
// Le repère se déplace en cercle autour de turtle1
double delta_x = 2.0 * sin(ros::Time::now().toSec());
double delta_y = 2.0 * cos(ros::Time::now().toSec());
transform.setOrigin(tf::Vector3(delta_x, delta_y, 0.0));
transform.setRotation(tf::Quaternion(0, 0, 0, 1));
Recompilez et relancez la démo pour observer le mouvement du repère carrot1 :
$ catkin_make
$ roslaunch learning_tf start_demo.launch