ROS: Einfachen Publisher/Subscriber in C++ Erstellen

Last Updated on 23. April 2023 by sfambach

Hier soll kurz beschrieben werden ein Publisher (Nachrichten Erzeuger) und ein Subscriber (Nachrichten Abbonent) in C++ erstellt werden.

Für eine ausführliche Anleitung sei auf die offizelle Website verwiesen.

http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29

Voraussetzung

Ihr benötigt einen Workspace und ein Paket, in meinem Fall is das Workspace test_ws und Paket beginner_tutorial. Weiterhin könnten auch helfen:

Vorbereitung

Ins Paket Verzeichnis wechseln

source ~/test_ws/devel/setup.bash
roscd beginner_tutorial

Falls noch nicht vorhanden ein Quellcode-Verzeichnis im Paket erstellen und in das Verzeichnis wechseln.

mkdir -p src
cd src

Dieses Verzeichnis wird alle Quelltext-Dateien des Paketes enthalten.

Publisher Knoten

Publisher Knoten Erstellen

Nun ein Beispiel für einen einfachen Publisher von unten Kopiern oder einfach mit folgenden Befehl vom original ROS Tutorial herunterladen.

 wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp

Die Datei sollte im Prinzip folgenden Code enthalten. Ich habe meinen Code nur an der original Datei orientiert und die Texte zur besseren Lesbarkeit weg gelassen bitte die Copyrights und Disclaimer der Original Datei beachten solltet ihr diese verwenden.

/* Simple Publisher
*  Based on the example of Morgan Quigley and Willow Garage, Inc. No Garanties for this code. 
*  Basis for this Code can be found:
*  https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp 
*/ 

#include "ros/ros.h"
#include "std_msgs/String.h"
#include  <sstream>

int main(int argc, char **argv)
{

  ros::init(argc, argv, "Publizierer");
  ros::NodeHandle n;
  ros::Publisher pub = n.advertise<std_msgs::String>("ChatTopic", 1000);

  ros::Rate loop_rate(10);

  int count = 0;
  while (ros::ok())
  {
    std_msgs::String msg;

    std::stringstream strStream;
    strStream << "Chat Message " << count;
    msg.data = strStream.str();

    ROS_INFO("%s", msg.data.c_str());

    pub.publish(msg);

    ros::spinOnce();

    loop_rate.sleep();
    ++count;
  }

  return 0;
}

Publisher Code Erklärt

Wir benötigen die Ros Bibliothek, eine einfache Textnachricht und die Stream Bibliothek für Ausgaben.

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>

Der restliche Code wird in der Main Funktion die nach Programmstart aufgerufen wird, platziert. Der folgende Befehl initialisiert ROS. Er bekommt als Argumentliste die Consolen Argumente des Knoten und einen eindeutigen Namen (hier Publizierer). Dieser darf kein / enthalten.

int main(int argc, char **argv)
{
   ros::init(argc, argv, "Publizierer"); // <--
...

Nun erstellen wir einen NodeHandle, dies ermöglicht uns den Zugriff auf den Knotenprozess. Mit der ersten Erstellung wird der Knoten initialisiert.

ros::NodeHandle n;

Mit dessen Hilfe registrieren wir uns nun als PublishDie Frequenz beschreibt die Anzahl der Schwingungen in einer Zeiteinheit. Frequenz und Wellenlänge sind miteinander verknüpft. Die Maßeinheit der Frequenz ist das Hertz (Hz): 1 Hz = 1 Schwingung pro Sekunde = 1/s.er. Die Argumente sind der Topic Name („ChatTopic“) und die Göße des Nachrichten-Puffers in Anzahl Nachrichten. Wird die Anzahl nicht abgeholter Nachrichten überschritten werden alte Nachrichten verworfen. Der Aufruf gibt ein Publisher-Objekt zurück mit hilfen dessen wir später Nachrichten veröffentlichen können.

ros::Publisher pub = n.advertise<std_msgs::String>("ChatTopic", 1000);

Der folgenden Befehl gibt an wie lange gewartet werden soll nachdem ein sleep aufgerufen wurden. In diesem Fall 10 Hz (10/Sekunde = 100 ms warten)

ros::Rate loop_rate(10);


// loop_rate.sleep(); // kommt später

Unser eigene Logik läuft in einer Schleife ab. Diese wird so lange durchlaufen bis eines der folgenden Ereignisse eintritt, mit diesem wir ros::ok() dann auf false gesetzt.

  • Knotenprogramm gestoppt (eg. Control +C )
  • Aus dem Netzwerk geworfen wegen eines anderen Knoten mit gleichem Namen.
  • ros::shutdown() wurde aufgerufen
  • Alle ros::NodeHandels wurden zerstört
  int count = 0;
  while (ros::ok())
  {
    // ...
  }

Nun erzeugen wir eine Nachricht und verschicken diese mit pub.publish(msg). ROS_INFO schreib die Nachricht noch auf dem Standardoutput.

  std_msgs::String msg;
  std::stringstream strStream;
  strStream << "Chat Message " << count;
  msg.data = strStream.str();

  ROS_INFO("%s", msg.data.c_str());
  pub.publish(msg);

SpinOnce wird hier eigentlich nicht benötigt und wird hier nur der Vollständigkeit halber angegeben. Es sieht für mich so aus als würde hier einmal die EventLoop durchaufen um Nachrichten/Events … zu aktualisieren.

 ros::spinOnce();

Nun warten wir 100 Millisekunden (10Hz) bis zum nächsten Durchlauf der Schleife

// ... ros::Rate loop_rate(10); // vorher angegeben
loop_rate.sleep();

Subscriber Knoten

Subscriber Knoten erstellen

Nun ein Beispiel für einen einfachen Subscriber von unten Kopiern oder einfach mit folgenden Befehl vom original ROS Tutorial herunterladen.

wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp

Die Datei sollte im Prinzip folgenden Code enthalten. Ich habe meinen Code an der original Datei orientiert und die Texte zur besseren Lesbarkeit weg gelassen bitte die Copyrights und Disclaimer der Original Datei beachten solltet ihr diese verwenden.

/* Simple subscriber 
* Based on the example of Morgan Quigley and Willow Garage, Inc. No Garanties for this code. 
*  Basis for this Code can be found:
*  https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp 
*/ 

#include "ros/ros.h"
#include "std_msgs/String.h"

void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "Abbonent");
  ros::NodeHandle n;

  ros::Subscriber sub = n.subscribe("ChatTopic", 1000, chatterCallback);
  ros::spin();

  return 0;
}

Subscriber Code Erklärt

Wir brauchen wieder die ROS Bibliothek und auch die standard String Nachricht.

#include "ros/ros.h"
#include "std_msgs/String.h"

Für den Subscriber braucen wir eine sogenannte CallBack Funktion, dies wird bei jedem eintreffen einer Nachricht gerufen. In unserem Fall macht sie nichts anderes als die Nachricht auf der Standardausgabe auszugeben.

void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

Auch um einen Subriber zu erzeugen muss ROS initialisiert und ein Handle erstellt werden.

int main(int argc, char **argv)
{
  ros::init(argc, argv, "Abbonent");
  ros::NodeHandle n;
..

Nun erstellen wir noch den Subcriber, geben ihm den Topic Namen, die Größe des Nachrichtenpuffers, den Namen der CallBack Funktion mit und lassen ROS die Eventloop peitschen.

  ros::Subscriber sub = n.subscribe("ChatTopic", 1000, chatterCallback);
  ros::spin();

das wars.

Bauen

CMake Anpassen

Die erstellten Dateien dem CMakeList.txt mitteilen.

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)

Meine Datei sieht dann wie folgt aus

cmake_minimum_required(VERSION 3.0.2)
project(beginner_tutorial)

find_package(catkin REQUIRED)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg message_generation)

add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)

generate_messages(DEPENDENCIES std_msgs)

catkin_package()

include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorial_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorial_generate_messages_cpp)

Compilieren

In das Hauptverzeichnis des Workspaces wechseln und catkin_make ausführen

cd ~/test_ws
catkin_make

Due Programme sollten ohne Fehler compilieren.

Probieren

ROS Kern starten

Neue Konsole öffnen und den Kern starten

roscore

Konsolenausgabe

Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://ros1:36971/
ros_comm version 1.14.13


SUMMARY
========

PARAMETERS
 * /rosdistro: melodic
 * /rosversion: 1.14.13

NODES

auto-starting new master
process[master]: started with pid [21703]
ROS_MASTER_URI=http://ros1:11311/

setting /run_id to b388e65a-e1ed-11ed-9402-2cf05d21f9e7
process[rosout-1]: started with pid [21714]
started core service [/rosout]

Publisher starten

Neue Console öffnen und den Publisher starten

source devel/setup.bash 
rosrun beginner_tutorial talker

Konsolen Ausgabe

stefan@ros1:~/test_ws$ rosrun beginner_tutorial talker
[ INFO] [1682264871.725780407]: Chat Message 0
[ INFO] [1682264871.825829919]: Chat Message 1
[ INFO] [1682264871.926003834]: Chat Message 2
[ INFO] [1682264872.025829924]: Chat Message 3
[ INFO] [1682264872.125823473]: Chat Message 4
[ INFO] [1682264872.225830377]: Chat Message 5
[ INFO] [1682264872.325854816]: Chat Message 6
[ INFO] [1682264872.425830687]: Chat Message 7
[ INFO] [1682264872.526317126]: Chat Message 8
[ INFO] [1682264872.626305447]: Chat Message 9
[ INFO] [1682264872.725830634]: Chat Message 10
[ INFO] [1682264872.826306242]: Chat Message 11
[ INFO] [1682264872.925858434]: Chat Message 12
[ INFO] [1682264873.025830754]: Chat Message 13

Subscriber starten

Neue Console öffenen und den Subscriber starten

source devel/setup.bash 
rosrun beginner_tutorial listener

Konsolen Ausgabe

stefan@ros1:~/test_ws$ rosrun beginner_tutorial listener
[ INFO] [1682264872.026229599]: I heard: [Chat Message 3]
[ INFO] [1682264872.126169068]: I heard: [Chat Message 4]
[ INFO] [1682264872.226189402]: I heard: [Chat Message 5]
[ INFO] [1682264872.326278372]: I heard: [Chat Message 6]
[ INFO] [1682264872.426155092]: I heard: [Chat Message 7]
[ INFO] [1682264872.526503319]: I heard: [Chat Message 8]
[ INFO] [1682264872.626625331]: I heard: [Chat Message 9]
[ INFO] [1682264872.726196509]: I heard: [Chat Message 10]
[ INFO] [1682264872.826496384]: I heard: [Chat Message 11]
[ INFO] [1682264872.926120007]: I heard: [Chat Message 12]
[ INFO] [1682264873.026085367]: I heard: [Chat Message 13]

Fazit

Ein Publisher-/Subscriber-Gerüst ist schnell erstellt und dient als Basis für kommende Projekte.

Verwandte Beiträge

Quellen

http://wiki.ros.org/melodic/Installation/Ubuntu

http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert