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